From 8d4693c0f803d87c1ea127093dab5f0499879e5c Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Mon, 7 Nov 2022 12:39:54 +0300 Subject: [PATCH] borrowck: use implied bounds from impl header --- .../src/type_check/free_region_relations.rs | 25 +++++++++- library/std/src/collections/hash/map.rs | 5 +- tests/ui/wf/wf-associated-const.rs | 42 +++++++++++++++++ tests/ui/wf/wf-associated-const.stderr | 14 ++++++ tests/ui/wf/wf-static-method.rs | 13 ++--- tests/ui/wf/wf-static-method.stderr | 47 ++----------------- 6 files changed, 89 insertions(+), 57 deletions(-) create mode 100644 tests/ui/wf/wf-associated-const.rs create mode 100644 tests/ui/wf/wf-associated-const.stderr diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 4a76d877af0f5..011b5b760c23a 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -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 @@ 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; @@ -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); } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 1a416a4d10bdd..39e94902cfe5f 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -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 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] diff --git a/tests/ui/wf/wf-associated-const.rs b/tests/ui/wf/wf-associated-const.rs new file mode 100644 index 0000000000000..dffa1713eff8f --- /dev/null +++ b/tests/ui/wf/wf-associated-const.rs @@ -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 { + ::EVIL(b) // FIXME: should be an error +} + +fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { + ::INHERENT_EVIL(b) + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/wf/wf-associated-const.stderr b/tests/ui/wf/wf-associated-const.stderr new file mode 100644 index 0000000000000..e025d5d468a14 --- /dev/null +++ b/tests/ui/wf/wf-associated-const.stderr @@ -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 | ::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 + diff --git a/tests/ui/wf/wf-static-method.rs b/tests/ui/wf/wf-static-method.rs index 7ff195230bfed..66546ac489df7 100644 --- a/tests/ui/wf/wf-static-method.rs +++ b/tests/ui/wf/wf-static-method.rs @@ -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 @@ impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> { 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) diff --git a/tests/ui/wf/wf-static-method.stderr b/tests/ui/wf/wf-static-method.stderr index 161609a5f86a1..6c49098aad398 100644 --- a/tests/ui/wf/wf-static-method.stderr +++ b/tests/ui/wf/wf-static-method.stderr @@ -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 | ::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 | ::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