borrowck: use implied bounds from impl header

This commit is contained in:
Ali MJ Al-Nasrawy 2022-11-07 12:39:54 +03:00 committed by lcnr
parent a3fe3bbb2c
commit 8d4693c0f8
6 changed files with 89 additions and 57 deletions

View file

@ -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;
@ -195,7 +196,9 @@ fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
#[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;
@ -275,6 +278,26 @@ pub(crate) fn create(mut self) -> CreateResult<'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);
}

View file

@ -3049,9 +3049,8 @@ fn extend_reserve(&mut self, additional: usize) {
#[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]

View file

@ -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() {}

View file

@ -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

View file

@ -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 {} }
@ -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
}
}
@ -25,7 +20,6 @@ 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).
}
}
@ -33,12 +27,11 @@ fn static_evil(u: &'b u32) -> &'a u32 {
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)

View file

@ -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
@ -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
@ -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
@ -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