From f3646748cd9d3cf5a6834f299acd10af1fe99c1b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 2 Jan 2025 00:38:11 +0000 Subject: [PATCH 1/3] Remove hack for filtering out param-env outlives that match item-bound outlives --- .../src/infer/outlives/obligations.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index b1d5d688295d9..3512b2a5657eb 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -388,26 +388,9 @@ where // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. - let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty); + let approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty); debug!(?approx_env_bounds); - // Remove outlives bounds that we get from the environment but - // which are also deducible from the trait. This arises (cc - // #55756) in cases where you have e.g., `>::Item: - // 'a` in the environment but `trait Foo<'b> { type Item: 'b - // }` in the trait definition. - approx_env_bounds.retain(|bound_outlives| { - // OK to skip binder because we only manipulate and compare against other values from - // the same binder. e.g. if we have (e.g.) `for<'a> >::Item: 'a` in - // `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region. If the - // declaration is `trait Trait<'b> { type Item: 'b; }`, then - // `projection_declared_bounds_from_trait` will be invoked with `['b => ^1]` and so we - // will get `^1` returned. - let bound = bound_outlives.skip_binder(); - let ty::Alias(_, alias_ty) = bound.0.kind() else { bug!("expected AliasTy") }; - self.verify_bound.declared_bounds_from_definition(*alias_ty).all(|r| r != bound.1) - }); - // If declared bounds list is empty, the only applicable rule is // OutlivesProjectionComponent. If there are inference variables, // then, we can break down the outlives into more primitive From 2a373d7dd38a4bc3b04402d7cf84bd167727d1c8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 2 Jan 2025 00:47:43 +0000 Subject: [PATCH 2/3] Make it clearer that the only infers we expect to see when processing outlives are regions --- compiler/rustc_infer/src/infer/outlives/obligations.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 3512b2a5657eb..0383c81f2af0d 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -363,6 +363,13 @@ where return; } + if alias_ty.has_non_region_infer() { + self.tcx + .dcx() + .span_delayed_bug(origin.span(), "an alias has infers during region solving"); + return; + } + // This case is thorny for inference. The fundamental problem is // that there are many cases where we have choice, and inference // doesn't like choice (the current region inference in @@ -408,7 +415,7 @@ where let is_opaque = alias_ty.kind(self.tcx) == ty::Opaque; if approx_env_bounds.is_empty() && trait_bounds.is_empty() - && (alias_ty.has_infer() || is_opaque) + && (alias_ty.has_infer_regions() || is_opaque) { debug!("no declared bounds"); let opt_variances = is_opaque.then(|| self.tcx.variances_of(alias_ty.def_id)); From dd210eca43bbada8835789f85f83fb106d70c637 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 2 Jan 2025 01:30:55 +0000 Subject: [PATCH 3/3] Simplify declared_generic_bounds_from_env --- .../rustc_infer/src/infer/outlives/verify.rs | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 247fbc259652e..7a21c2883d1ac 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -192,7 +192,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// Obviously these must be approximate -- they are in fact both *over* and /// and *under* approximated: /// - /// * Over-approximated because we erase regions, so + /// * Over-approximated because we don't consider equality of regions. /// * Under-approximated because we look for syntactic equality and so for complex types /// like `>::Item` or whatever we may fail to figure out /// all the subtleties. @@ -205,13 +205,14 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { erased_ty: Ty<'tcx>, ) -> Vec> { let tcx = self.tcx; + let mut bounds = vec![]; // To start, collect bounds from user environment. Note that // parameter environments are already elaborated, so we don't // have to worry about that. - let param_bounds = self.caller_bounds.iter().copied().filter(move |outlives_predicate| { + bounds.extend(self.caller_bounds.iter().copied().filter(move |outlives_predicate| { super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty) - }); + })); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -224,37 +225,27 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must outlive `'a`, but we don't know that // this holds from first principles. - let from_region_bound_pairs = - self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { - debug!( - "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", - (r, p) - ); - // Fast path for the common case. - match (&p, erased_ty.kind()) { - // In outlive routines, all types are expected to be fully normalized. - // And therefore we can safely use structural equality for alias types. - (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} - (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} - (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {} - _ => return None, - } + bounds.extend(self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", + (r, p) + ); + // Fast path for the common case. + match (&p, erased_ty.kind()) { + // In outlive routines, all types are expected to be fully normalized. + // And therefore we can safely use structural equality for alias types. + (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} + (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} + (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {} + _ => return None, + } - let p_ty = p.to_ty(tcx); - let erased_p_ty = self.tcx.erase_regions(p_ty); - (erased_p_ty == erased_ty) - .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) - }); + let p_ty = p.to_ty(tcx); + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) + })); - param_bounds - .chain(from_region_bound_pairs) - .inspect(|bound| { - debug!( - "declared_generic_bounds_from_env_for_erased_ty: result predicate = {:?}", - bound - ) - }) - .collect() + bounds } /// Given a projection like `>::Bar`, returns any bounds