diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5e56ae80ff5da..4eea6d625df64 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -585,7 +585,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_type_tests( &self, infcx: &InferCtxt<'tcx>, - mut propagated_outlives_requirements: Option<&mut Vec>>, + propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, ) { let tcx = infcx.tcx; @@ -595,6 +595,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // the user. Avoid that. let mut deduplicate_errors = FxIndexSet::default(); + // Each type test introduces one or more OR-constraints (e.g. T: 'a OR T: 'b), + // where at least one option in each constraint must be satisfied. All such + // constraints must be satisfied simultaneously: i.e., they form a conjunction (AND). + // We'll use this conjunctive requirement later on. + let mut conjunctive_propagated_outlives_requirement = + propagated_outlives_requirements.is_some().then_some(vec![]); + for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -608,8 +615,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { continue; } - if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements - && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) + if let Some(conjunctive_propagated_outlives_requirements) = + &mut conjunctive_propagated_outlives_requirement + && self.try_promote_type_test( + infcx, + type_test, + conjunctive_propagated_outlives_requirements, + ) { continue; } @@ -633,6 +645,85 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::TypeTestError { type_test: type_test.clone() }); } } + + if let Some(mut conjunctive_requirement) = conjunctive_propagated_outlives_requirement + && !conjunctive_requirement.is_empty() + { + // We can simplify this list of list of requirements. + // + // Say we did some number of type tests and it results in following requirements: + // + // R1: (T: 'a OR T: 'b) + // R2: (T: 'a) + // + // * See `try_promote_type_test` below on why we obtain OR requirements implicitly. + // + // Full requirement is then: R1 AND R2. *BUT*, we can remove R1 entirely, because we already + // require `T: 'a`, which implies `T:'a OR T: 'b`, making R1 redundant. + // + // The requirements can be seen as a boolean conjunctive normal form expression: + // Treat a requirement `T: 'region` as a boolean value, then this problem is (almost) + // equivalent to "Unit Propagation". However, this problem we are trying to solve is much + // simpler: Unit Propagation considers any form of subexpression, even containing negation + // of values, making it a multi-pass algorithm. The only subexpressions we encounter are of + // the form (R1 OR ... OR RN), thus if even on R is required on their own (a unit), this + // whole subexpression can be removed. + // + // Because of the outlives relations, we can actually have a stronger redundancy check, + // say we have following requirements that create a conjunctive requirement: + // R1: T: 'a + // R2: T: 'b OR T: 'c + // R: R1 AND R2 + // + // And we have we an assumption in our environment that `'a: 'b`, we can thus remove R2 + // as well. `T: 'b` is implied by `T: 'a` because of the assumption `'a: 'b`: + // T -> 'a -> 'b + // + // So we can filter redundant OR requirements with the following algorithm: + // Collect every Unit requirement. Then for every OR requirement, loop over its + // individual requirements and if the region is outlived by the region of one of the + // units, remove the entire OR requirement. + + fn requirement_key<'a>(subject: ClosureOutlivesRequirement<'a>) -> (Ty<'a>, RegionVid) { + let ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy { inner: ty }) = + subject.subject + else { + unreachable!("ClosureOutliveSubject of a type test is always a Ty"); + }; + (ty, subject.outlived_free_region) + } + + let units: Vec<_> = conjunctive_requirement + .iter() + .filter_map(|r| { + let [r] = r.as_slice() else { return None }; + Some(requirement_key(*r)) + }) + .collect(); + + // Remove the `or_requirement`s that contain any of the unit requirements. + conjunctive_requirement.retain(|or_requirement| { + or_requirement.len() == 1 + || !or_requirement.iter().any(|r| { + let (ty, region) = requirement_key(*r); + units.iter().any(|&(unit_subj, unit_region)| { + // Same type, and the unit region outlives the disjunct region, + // meaning T: unit_region implies T: region. + unit_subj == ty + && self.universal_region_relations.outlives(unit_region, region) + }) + }) + }); + + assert!( + !conjunctive_requirement.is_empty(), + "It should not be possible to remove every requirement." + ); + // Propagate all requirements as is. + propagated_outlives_requirements + .expect("conjunctive_requirements is `Some`, so this should be as well") + .extend(conjunctive_requirement.into_iter().flatten()); + } } /// Invoked when we have some type-test (e.g., `T: 'X`) that we cannot @@ -664,7 +755,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'tcx>, type_test: &TypeTest<'tcx>, - propagated_outlives_requirements: &mut Vec>, + propagated_outlives_requirements: &mut Vec>>, ) -> bool { let tcx = infcx.tcx; let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test; @@ -690,24 +781,41 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() { debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p); let static_r = self.universal_regions().fr_static; - propagated_outlives_requirements.push(ClosureOutlivesRequirement { + propagated_outlives_requirements.push(vec![ClosureOutlivesRequirement { subject, outlived_free_region: static_r, blame_span, category: ConstraintCategory::Boring, - }); + }]); // we can return here -- the code below might push add'l constraints // but they would all be weaker than this one. return true; } - // For each region outlived by lower_bound find a non-local, - // universal region (it may be the same region) and add it to - // `ClosureOutlivesRequirement`. - let mut found_outlived_universal_region = false; - for ur in self.scc_values.universal_regions_outlived_by(r_scc) { - found_outlived_universal_region = true; + let universal_regions: Vec<_> = + self.scc_values.universal_regions_outlived_by(r_scc).collect(); + debug!(?universal_regions); + + // Filter to only the "minimal" universal regions: + // Drop any region `a` that strictly outlives another region `b`. + let minimal_universal_regions: Vec<_> = universal_regions + .iter() + .copied() + .filter(|&a| { + !universal_regions.iter().copied().any(|b| { + !self.universal_region_relations.outlives(a, b) + && self.universal_region_relations.outlives(b, a) + }) + }) + .collect(); + + assert!( + !minimal_universal_regions.is_empty(), + "There should always be at least 1 minimal region" + ); + + for ur in minimal_universal_regions { debug!("universal_region_outlived_by ur={:?}", ur); let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); debug!(?non_local_ub); @@ -716,6 +824,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to // avoid potential non-determinism we approximate this by requiring // T: '1 and T: '2. + let mut or_requirements = Vec::with_capacity(non_local_ub.len()); for upper_bound in non_local_ub { debug_assert!(self.universal_regions().is_universal_region(upper_bound)); debug_assert!(!self.universal_regions().is_local_free_region(upper_bound)); @@ -727,14 +836,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { category: ConstraintCategory::Boring, }; debug!(?requirement, "adding closure requirement"); - propagated_outlives_requirements.push(requirement); + or_requirements.push(requirement); } + propagated_outlives_requirements.push(or_requirements); } - // If we succeed to promote the subject, i.e. it only contains non-local regions, - // and fail to prove the type test inside of the closure, the `lower_bound` has to - // also be at least as large as some universal region, as the type test is otherwise - // trivial. - assert!(found_outlived_universal_region); true } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 56c800dcf7c7e..4aeff9401b7fc 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -3,7 +3,6 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::outlives::env::RegionBoundPairs; -use rustc_middle::bug; use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Unnormalized, Upcast}; use rustc_span::Span; @@ -259,53 +258,4 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .and(type_op::ascribe_user_type::AscribeUserType { mir_ty, user_ty }), ); } - - /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`. - /// - /// FIXME(#104478, #104477): This is a hack for backward-compatibility. - #[instrument(skip(self), level = "debug")] - pub(super) fn ascribe_user_type_skip_wf( - &mut self, - mir_ty: Ty<'tcx>, - user_ty: ty::UserType<'tcx>, - span: Span, - ) { - let ty::UserTypeKind::Ty(user_ty) = user_ty.kind else { bug!() }; - - // A fast path for a common case with closure input/output types. - if let ty::Infer(_) = user_ty.kind() { - self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring) - .unwrap(); - return; - } - - // This is a hack. `body.local_decls` are not necessarily normalized in the old - // solver due to not deeply normalizing in writeback. So we must re-normalize here. - // - // I am not sure of a test case where this actually matters. There is a similar - // hack in `equate_inputs_and_outputs` which does have associated test cases. - let mir_ty = match self.infcx.next_trait_solver() { - true => mir_ty, - false => self.normalize(Unnormalized::new_wip(mir_ty), Locations::All(span)), - }; - - let cause = ObligationCause::dummy_with_span(span); - let param_env = self.infcx.param_env; - let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( - Locations::All(span), - ConstraintCategory::Boring, - type_op::custom::CustomTypeOp::new( - |ocx| { - // The `AscribeUserType` query would normally emit a wf - // obligation for the unnormalized user_ty here. This is - // where the "incorrectly skips the WF checks we normally do" - // happens - let user_ty = ocx.normalize(&cause, param_env, Unnormalized::new_wip(user_ty)); - ocx.eq(&cause, param_env, user_ty, mir_ty)?; - Ok(()) - }, - "ascribe_user_type_skip_wf", - ), - ); - } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index fe40173b905d6..43a2e14b9872a 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 }) .map(|local| &self.body.local_decls[local]), ) { - self.ascribe_user_type_skip_wf( + self.ascribe_user_type( arg_decl.ty, ty::UserType::new(ty::UserTypeKind::Ty(user_ty)), arg_decl.source_info.span, @@ -127,7 +127,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // If the user explicitly annotated the output type, enforce it. let output_decl = &self.body.local_decls[RETURN_PLACE]; - self.ascribe_user_type_skip_wf( + self.ascribe_user_type( output_decl.ty, ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())), output_decl.source_info.span, diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs index 4fdf5470feac6..dcba2081cfb33 100644 --- a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs @@ -13,8 +13,6 @@ impl Foo { //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough //~| ERROR the parameter type `I` may not live long enough - //~| ERROR the parameter type `I` may not live long enough - //~| ERROR the parameter type `I` may not live long enough //~| ERROR `f` does not live long enough } } diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr index df86ce79f09c7..9befce0e94dc3 100644 --- a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr @@ -69,34 +69,6 @@ help: consider adding an explicit lifetime bound LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { | +++++++++ -error[E0310]: the parameter type `I` may not live long enough - --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 - | -LL | self.bar = Box::new(|baz| Box::new(f(baz))); - | ^^^^^^^^^^^^^^^^ - | | - | the parameter type `I` must be valid for the static lifetime... - | ...so that the type `I` will meet its required lifetime bounds - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider adding an explicit lifetime bound - | -LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | +++++++++ - -error[E0311]: the parameter type `I` may not live long enough - --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 - | -LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | --------- the parameter type `I` must be valid for the anonymous lifetime defined here... -LL | self.bar = Box::new(|baz| Box::new(f(baz))); - | ^^^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | pub fn ack<'a, I: 'a>(&'a mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | +++ ++++ ++ - error[E0597]: `f` does not live long enough --> $DIR/unconstrained-closure-lifetime-generic.rs:10:44 | @@ -113,7 +85,7 @@ LL | } | = note: due to object lifetime defaults, `Box Fn(&'a usize) -> Box<(dyn Any + 'a)>>` actually means `Box<(dyn for<'a> Fn(&'a usize) -> Box<(dyn Any + 'a)> + 'static)>` -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0310, E0311, E0597. +Some errors have detailed explanations: E0310, E0597. For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/consts/issue-102117.rs b/tests/ui/consts/issue-102117.rs index b7955283a8d81..141dbfb4e935e 100644 --- a/tests/ui/consts/issue-102117.rs +++ b/tests/ui/consts/issue-102117.rs @@ -16,7 +16,6 @@ impl VTable { layout: Layout::new::(), type_id: TypeId::of::(), //~^ ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough drop_in_place: unsafe { transmute::(drop_in_place::) }, diff --git a/tests/ui/consts/issue-102117.stderr b/tests/ui/consts/issue-102117.stderr index 4440680bd7f19..56bc72621fc16 100644 --- a/tests/ui/consts/issue-102117.stderr +++ b/tests/ui/consts/issue-102117.stderr @@ -12,21 +12,6 @@ help: consider adding an explicit lifetime bound LL | pub fn new() -> &'static Self { | +++++++++ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/issue-102117.rs:17:26 - | -LL | type_id: TypeId::of::(), - | ^^^^^^^^^^^^^^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider adding an explicit lifetime bound - | -LL | pub fn new() -> &'static Self { - | +++++++++ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs index 0963053f57806..6bc644f256424 100644 --- a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs +++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs @@ -1,4 +1,3 @@ -//@ check-pass struct Foo<'a>(&'a ()) where (): Trait<'a>; @@ -21,7 +20,7 @@ where } fn main() { - let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {}; + let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {}; //~ ERROR: lifetime may not live long enough // If `could_use_implied_bounds` were to use implied bounds, // keeping 'a late-bound, then we could assign that function diff --git a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.stderr b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.stderr new file mode 100644 index 0000000000000..dd99590877bca --- /dev/null +++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/hrlt-implied-trait-bounds-roundtrip.rs:23:49 + | +LL | let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {}; + | ^ + | | + | has type `Foo<'1>` + | requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr index 2742162c82119..a5b68f1d22fc6 100644 --- a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -20,7 +20,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | - - ^^^^^^ assignment requires that `'1` must outlive `'2` | | | | | has type `&'1 i32` - | has type `&'?1 mut &'2 i32` + | has type `&'2 mut &'?2 i32` note: no external requirements --> $DIR/escape-argument-callee.rs:20:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr index f5527eeb2cdba..964ea3079c924 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -19,6 +19,8 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y = note: late-bound region is '?4 = note: number of external vids: 5 = note: where '?1: '?2 + = note: where '?1: '?2 + = note: where '?1: '?2 note: no external requirements --> $DIR/propagate-approximated-ref.rs:42:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 9e9eae985973a..d06166e4704db 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -18,6 +18,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { = note: late-bound region is '?3 = note: number of external vids: 4 = note: where '?1: '?0 + = note: where '?1: '?0 note: no external requirements --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:31:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 303fcd4cdfcf3..5fbd37062702a 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -18,6 +18,9 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y = note: late-bound region is '?3 = note: late-bound region is '?4 = note: number of external vids: 5 + = note: where '?1: '?2 + = note: where '?1: '?2 + = note: where '?1: '?0 = note: where '?1: '?0 note: no external requirements diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 6b04e346c6971..8b5641293b7a0 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -23,7 +23,7 @@ error: lifetime may not live long enough LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | --------- - has type `&'?6 Cell<&'1 u32>` | | - | has type `&'?4 Cell<&'2 &'?1 u32>` + | has type `&'2 Cell<&'?5 &'?1 u32>` LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs index 2cb6ceb0c3b95..bda64151fba3d 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs @@ -36,7 +36,7 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { //~ ERROR: lifetime may not live long enough // Only works if 'x: 'y: demand_y(x, y, x.get()) //~^ ERROR diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index ae2129c65f2c0..88eb8fe342d12 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -1,4 +1,4 @@ -note: no external requirements +note: external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:39:47 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { @@ -17,12 +17,16 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y = note: late-bound region is '?10 = note: late-bound region is '?3 = note: late-bound region is '?4 + = note: number of external vids: 5 + = note: where '?1: '?2 + = note: where '?1: '?2 + = note: where '?1: '?2 error: lifetime may not live long enough --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:41:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ---------- ---------- has type `&'?7 Cell<&'2 &'?2 u32>` + | ---------- ---------- has type `&'2 Cell<&'?8 &'?2 u32>` | | | has type `&'?5 Cell<&'1 &'?1 u32>` LL | // Only works if 'x: 'y: @@ -41,5 +45,24 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { | = note: defining type: supply -error: aborting due to 1 previous error +error: lifetime may not live long enough + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:39:5 + | +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | +LL | | }); + | |______^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&'?11 u32>`, which makes the generic argument `&'?11 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + +error: aborting due to 2 previous errors diff --git a/tests/ui/nll/closure-requirements/type-test-issue-154267-minimized.rs b/tests/ui/nll/closure-requirements/type-test-issue-154267-minimized.rs new file mode 100644 index 0000000000000..44988db4998c9 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-issue-154267-minimized.rs @@ -0,0 +1,11 @@ +//@ check-pass + +struct Arg<'a: 'b, 'b, 'c> { + field: *mut (&'a (), &'b (), &'c ()), +} +fn foo<'a, 'b, T: for<'c> FnOnce(Arg<'a, 'b, 'c>)>(_: T) {} + +fn error<'a, 'b>() { + foo::<'a, 'b>(|arg| {}); +} +fn main() {} diff --git a/tests/ui/nll/closure-requirements/type-test-issue-154267-stronger.rs b/tests/ui/nll/closure-requirements/type-test-issue-154267-stronger.rs new file mode 100644 index 0000000000000..4533562150f16 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-issue-154267-stronger.rs @@ -0,0 +1,20 @@ +// Stronger version of `type-test-issue-154267` where `error` should fail because it does not have +// an explicit `'a: 'b` bound. + +struct Arg<'a: 'b, 'b: 'd, 'c: 'd, 'd, T> { + field: *mut (&'a (), &'b (), &'c (), &'d (), T), +} +impl<'a: 'b, 'b, 'c, 'd, T> Arg<'a, 'b, 'c, 'd, T> { + fn constrain(self) + where + T: 'a, + T: 'd, + { + } +} +fn takes_closure<'a: 'b, 'b, 'c, T>(_: impl for<'d> FnOnce(Arg<'a, 'b, 'c, 'd, T>)) {} + +fn error<'a, 'b, 'c, T: 'a>() { + takes_closure::<'a, 'b, 'c, T>(|arg| arg.constrain()); //~ ERROR: lifetime may not live long enough +} +fn main() {} diff --git a/tests/ui/nll/closure-requirements/type-test-issue-154267-stronger.stderr b/tests/ui/nll/closure-requirements/type-test-issue-154267-stronger.stderr new file mode 100644 index 0000000000000..814e1f4d3755d --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-issue-154267-stronger.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/type-test-issue-154267-stronger.rs:18:5 + | +LL | fn error<'a, 'b, 'c, T: 'a>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | takes_closure::<'a, 'b, 'c, T>(|arg| arg.constrain()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/closure-requirements/type-test-issue-154267.rs b/tests/ui/nll/closure-requirements/type-test-issue-154267.rs new file mode 100644 index 0000000000000..6137f38886437 --- /dev/null +++ b/tests/ui/nll/closure-requirements/type-test-issue-154267.rs @@ -0,0 +1,26 @@ +//@ check-pass +// This test checks that the compiler doesn't propagate `T: 'b` during the `T: 'a` type test. +// If it did, it would fail to compile, even though the program is sound. + +struct Arg<'a: 'c, 'b: 'c, 'c, T> { + field: *mut (&'a (), &'b (), &'c (), T), +} + +impl<'a, 'b, 'c, T> Arg<'a, 'b, 'c, T> { + fn constrain(self) + where + T: 'a, + T: 'c, + { + } +} + +fn takes_closure<'a, 'b, T>(_: impl for<'c> FnOnce(Arg<'a, 'b, 'c, T>)) {} + +// We have `'a: 'c` and `'b: 'c`, requiring `T: 'a` in `constrain` should not need +// `T: 'b` here. +fn error<'a, 'b, T: 'a>() { + takes_closure::<'a, 'b, T>(|arg| arg.constrain()); +} + +fn main() {} diff --git a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr index e2f48f37f0dad..c84efdcb8e3a6 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | doit(0, &|x, y| { | - - has type `&'1 i32` | | - | has type `&Cell<&'2 i32>` + | has type `&'2 Cell<&i32>` LL | x.set(y); | ^^^^^^^^ argument requires that `'1` must outlive `'2` | diff --git a/tests/ui/wf/check-wf-of-closure-args.rs b/tests/ui/wf/check-wf-of-closure-args.rs new file mode 100644 index 0000000000000..a4c4de7013c2f --- /dev/null +++ b/tests/ui/wf/check-wf-of-closure-args.rs @@ -0,0 +1,14 @@ +// Checks that we perform WF checks on closure args, regardless of wether they +// are used in the closure it self. +// related to issue #104478 + +struct MyTy(T); +trait Trait {} +impl Trait for &'static str {} +fn wf(_: T) {} + +fn main() { + let _: for<'x> fn(MyTy<&'x str>) = |_| {}; //~ ERROR: lifetime may not live long enough + + let _: for<'x> fn(MyTy<&'x str>) = |x| wf(x); //~ ERROR: lifetime may not live long enough +} diff --git a/tests/ui/wf/check-wf-of-closure-args.stderr b/tests/ui/wf/check-wf-of-closure-args.stderr new file mode 100644 index 0000000000000..a4881bc75d07b --- /dev/null +++ b/tests/ui/wf/check-wf-of-closure-args.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/check-wf-of-closure-args.rs:11:41 + | +LL | let _: for<'x> fn(MyTy<&'x str>) = |_| {}; + | ^ + | | + | has type `MyTy<&'1 str>` + | requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/check-wf-of-closure-args.rs:13:41 + | +LL | let _: for<'x> fn(MyTy<&'x str>) = |x| wf(x); + | ^ + | | + | has type `MyTy<&'1 str>` + | requires that `'1` must outlive `'static` + +error: aborting due to 2 previous errors +