Skip to content

Commit

Permalink
borrowck: use implied bounds from impl header
Browse files Browse the repository at this point in the history
  • Loading branch information
aliemjay authored and lcnr committed Jan 16, 2024
1 parent a3fe3bb commit 8d4693c
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 57 deletions.
25 changes: 24 additions & 1 deletion compiler/rustc_borrowck/src/type_check/free_region_relations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
use rustc_hir::def::DefKind;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
Expand Down Expand Up @@ -195,7 +196,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {

#[instrument(level = "debug", skip(self))]
pub(crate) fn create(mut self) -> CreateResult<'tcx> {
let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id());
let tcx = self.infcx.tcx;
let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local();
let span = tcx.def_span(defining_ty_def_id);

// Insert the facts we know from the predicates. Why? Why not.
let param_env = self.param_env;
Expand Down Expand Up @@ -275,6 +278,26 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
normalized_inputs_and_output.push(norm_ty);
}

// Add implied bounds from impl header.
if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) {
for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = self
.param_env
.and(type_op::normalize::Normalize::new(ty))
.fully_perform(self.infcx, span)
else {
tcx.dcx().span_delayed_bug(span, format!("failed to normalize {ty:?}"));
continue;
};
constraints.extend(c);

// We currently add implied bounds from the normalized ty only.
// This is more conservative and matches wfcheck behavior.
let c = self.add_implied_bounds(norm_ty);
constraints.extend(c);
}
}

for c in constraints {
self.push_region_constraints(c, span);
}
Expand Down
5 changes: 2 additions & 3 deletions library/std/src/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3049,9 +3049,8 @@ where
#[stable(feature = "hash_extend_copy", since = "1.4.0")]
impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
where
// FIXME(aliemjay): the bound `+ 'a` should not be necessary.
K: Eq + Hash + Copy + 'a,
V: Copy + 'a,
K: Eq + Hash + Copy,
V: Copy,
S: BuildHasher,
{
#[inline]
Expand Down
42 changes: 42 additions & 0 deletions tests/ui/wf/wf-associated-const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// check that associated consts can assume the impl header is well-formed.

// FIXME(aliemjay): we should check the impl header is WF at the use site
// but we currently don't in some cases. This is *unsound*.

trait Foo<'a, 'b, T>: Sized {
const EVIL: fn(u: &'b u32) -> &'a u32;
}

struct Evil<'a, 'b: 'a>(Option<&'a &'b ()>);

impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
const EVIL: fn(&'b u32) -> &'a u32 = { |u| u };
}

struct IndirectEvil<'a, 'b: 'a>(Option<&'a &'b ()>);

impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
const EVIL: fn(&'b u32) -> &'a u32 = { |u| u };
}

impl<'a, 'b> Evil<'a, 'b> {
const INHERENT_EVIL: fn(&'b u32) -> &'a u32 = { |u| u };
}

// while static methods can *assume* this, we should still
// *check* that it holds at the use site.

fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
<()>::EVIL(b) // FIXME: should be an error
}

fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
<IndirectEvil>::EVIL(b) // FIXME: should be an error
}

fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
<Evil>::INHERENT_EVIL(b)
//~^ ERROR lifetime may not live long enough
}

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/wf/wf-associated-const.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: lifetime may not live long enough
--> $DIR/wf-associated-const.rs:38:5
|
LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | <Evil>::INHERENT_EVIL(b)
| ^^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`

error: aborting due to previous error

13 changes: 3 additions & 10 deletions tests/ui/wf/wf-static-method.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
// check that static methods don't get to assume their trait-ref
// is well-formed.
// FIXME(#27579): this is just a bug. However, our checking with
// static inherent methods isn't quite working - need to
// fix that before removing the check.
// check that static methods can assume their trait-ref is well-formed.

trait Foo<'a, 'b, T>: Sized {
fn make_me() -> Self { loop {} }
Expand All @@ -15,7 +11,6 @@ impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
fn make_me() -> Self { }
fn static_evil(u: &'b u32) -> &'a u32 {
u
//~^ ERROR lifetime may not live long enough
}
}

Expand All @@ -25,20 +20,18 @@ impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
fn make_me() -> Self { IndirectEvil(None) }
fn static_evil(u: &'b u32) -> &'a u32 {
let me = Self::make_me();
//~^ ERROR lifetime may not live long enough
loop {} // (`me` could be used for the lifetime transmute).
}
}

impl<'a, 'b> Evil<'a, 'b> {
fn inherent_evil(u: &'b u32) -> &'a u32 {
u
//~^ ERROR lifetime may not live long enough
}
}

// while static methods don't get to *assume* this, we still
// *check* that they hold.
// while static methods can *assume* this, we should still
// *check* that it holds at the use site.

fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
<()>::static_evil(b)
Expand Down
47 changes: 4 additions & 43 deletions tests/ui/wf/wf-static-method.stderr
Original file line number Diff line number Diff line change
@@ -1,44 +1,5 @@
error: lifetime may not live long enough
--> $DIR/wf-static-method.rs:17:9
|
LL | impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | u
| ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`

error: lifetime may not live long enough
--> $DIR/wf-static-method.rs:27:18
|
LL | impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | let me = Self::make_me();
| ^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`

error: lifetime may not live long enough
--> $DIR/wf-static-method.rs:35:9
|
LL | impl<'a, 'b> Evil<'a, 'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | fn inherent_evil(u: &'b u32) -> &'a u32 {
LL | u
| ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`

error: lifetime may not live long enough
--> $DIR/wf-static-method.rs:44:5
--> $DIR/wf-static-method.rs:37:5
|
LL | fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
Expand All @@ -50,7 +11,7 @@ LL | <()>::static_evil(b)
= help: consider adding the following bound: `'b: 'a`

error: lifetime may not live long enough
--> $DIR/wf-static-method.rs:49:5
--> $DIR/wf-static-method.rs:42:5
|
LL | fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
Expand All @@ -62,7 +23,7 @@ LL | <IndirectEvil>::static_evil(b)
= help: consider adding the following bound: `'b: 'a`

error: lifetime may not live long enough
--> $DIR/wf-static-method.rs:54:5
--> $DIR/wf-static-method.rs:47:5
|
LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
Expand All @@ -73,5 +34,5 @@ LL | <Evil>::inherent_evil(b)
|
= help: consider adding the following bound: `'b: 'a`

error: aborting due to 6 previous errors
error: aborting due to 3 previous errors

0 comments on commit 8d4693c

Please sign in to comment.