about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-01-25 16:20:23 +0000
committerMichael Goulet <michael@errs.io>2025-01-28 19:11:05 +0000
commit8e0909d98a8e467f79d80000fe57eee6417ac02e (patch)
treee8ade4aa95da4ef2436539f0ac53d49336b303c4
parent48b7e38c0607e856dbbb932e60ecc85e45a1427d (diff)
downloadrust-8e0909d98a8e467f79d80000fe57eee6417ac02e.tar.gz
rust-8e0909d98a8e467f79d80000fe57eee6417ac02e.zip
Move param env bound deep normalization to OutlivesEnvironment building
-rw-r--r--compiler/rustc_infer/src/infer/outlives/env.rs38
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs22
-rw-r--r--compiler/rustc_trait_selection/src/regions.rs22
3 files changed, 37 insertions, 45 deletions
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
index f8ab0b53a00..e924c974a02 100644
--- a/compiler/rustc_infer/src/infer/outlives/env.rs
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -31,26 +31,14 @@ use crate::traits::query::OutlivesBound;
 pub struct OutlivesEnvironment<'tcx> {
     pub param_env: ty::ParamEnv<'tcx>,
     free_region_map: FreeRegionMap<'tcx>,
-
-    // Contains the implied region bounds in scope for our current body.
-    //
-    // Example:
-    //
-    // ```
-    // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) {
-    //   bar(x, y, |y: &'b T| { .. } // body B1)
-    // } // body B0
-    // ```
-    //
-    // Here, when checking the body B0, the list would be `[T: 'a]`, because we
-    // infer that `T` must outlive `'a` from the implied bounds on the
-    // fn declaration.
-    //
-    // For the body B1 however, the list would be `[T: 'a, T: 'b]`, because we
-    // also can see that -- within the closure body! -- `T` must
-    // outlive `'b`. This is not necessarily true outside the closure
-    // body, since the closure may never be called.
+    /// FIXME: Your first reaction may be that this is a bit strange. `RegionBoundPairs`
+    /// does not contain lifetimes, which are instead in the `FreeRegionMap`, and other
+    /// known type outlives are stored in the `known_type_outlives` set. So why do we
+    /// have these at all? It turns out that removing these and using `known_type_outlives`
+    /// everywhere is just enough of a perf regression to matter. This can/should be
+    /// optimized in the future, though.
     region_bound_pairs: RegionBoundPairs<'tcx>,
+    known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
 }
 
 /// "Region-bound pairs" tracks outlives relations that are known to
@@ -59,9 +47,10 @@ pub struct OutlivesEnvironment<'tcx> {
 pub type RegionBoundPairs<'tcx> = FxIndexSet<ty::OutlivesPredicate<'tcx, GenericKind<'tcx>>>;
 
 impl<'tcx> OutlivesEnvironment<'tcx> {
-    /// Create a new `OutlivesEnvironment` with extra outlives bounds.
-    pub fn with_bounds(
+    /// Create a new `OutlivesEnvironment` from normalized outlives bounds.
+    pub fn from_normalized_bounds(
         param_env: ty::ParamEnv<'tcx>,
+        known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
         extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
     ) -> Self {
         let mut region_relation = TransitiveRelationBuilder::default();
@@ -96,18 +85,21 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
 
         OutlivesEnvironment {
             param_env,
+            known_type_outlives,
             free_region_map: FreeRegionMap { relation: region_relation.freeze() },
             region_bound_pairs,
         }
     }
 
-    /// Borrows current value of the `free_region_map`.
     pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
         &self.free_region_map
     }
 
-    /// Borrows current `region_bound_pairs`.
     pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> {
         &self.region_bound_pairs
     }
+
+    pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
+        &self.known_type_outlives
+    }
 }
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 0383c81f2af..84e51b18dc5 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -67,7 +67,6 @@ use rustc_middle::ty::{
     self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt,
     TypeFoldable as _, TypeVisitableExt,
 };
-use rustc_span::DUMMY_SP;
 use rustc_type_ir::outlives::{Component, push_outlives_components};
 use smallvec::smallvec;
 use tracing::{debug, instrument};
@@ -142,25 +141,6 @@ impl<'tcx> InferCtxt<'tcx> {
     ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> {
         assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
 
-        let normalized_caller_bounds: Vec<_> = outlives_env
-            .param_env
-            .caller_bounds()
-            .iter()
-            .filter_map(|clause| {
-                let outlives = clause.as_type_outlives_clause()?;
-                Some(
-                    deeply_normalize_ty(
-                        outlives,
-                        SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP),
-                    )
-                    // FIXME(-Znext-solver): How do we accurately report an error span here :(
-                    .map_err(|NoSolution| {
-                        (outlives, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP))
-                    }),
-                )
-            })
-            .try_collect()?;
-
         // Must loop since the process of normalizing may itself register region obligations.
         for iteration in 0.. {
             let my_region_obligations = self.take_registered_region_obligations();
@@ -194,7 +174,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     self.tcx,
                     outlives_env.region_bound_pairs(),
                     None,
-                    &normalized_caller_bounds,
+                    outlives_env.known_type_outlives(),
                 );
                 let category = origin.to_constraint_category();
                 outlives.type_must_outlive(origin, sup_type, sub_region, category);
diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs
index fc9fa44b4c6..55171754618 100644
--- a/compiler/rustc_trait_selection/src/regions.rs
+++ b/compiler/rustc_trait_selection/src/regions.rs
@@ -33,12 +33,32 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
         assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
         implied_bounds_compat: bool,
     ) -> Self {
+        let mut bounds = vec![];
+
+        for bound in param_env.caller_bounds() {
+            if let Some(mut type_outlives) = bound.as_type_outlives_clause() {
+                if infcx.next_trait_solver() {
+                    match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>(
+                        infcx.at(&ObligationCause::dummy(), param_env),
+                        type_outlives,
+                    ) {
+                        Ok(new) => type_outlives = new,
+                        Err(_) => {
+                            infcx.dcx().delayed_bug(format!("could not normalize `{bound}`"));
+                        }
+                    }
+                }
+                bounds.push(type_outlives);
+            }
+        }
+
         // FIXME: This needs to be modified so that we normalize the known type
         // outlives obligations then elaborate them into their region/type components.
         // Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
         // if we can normalize `'a`.
-        OutlivesEnvironment::with_bounds(
+        OutlivesEnvironment::from_normalized_bounds(
             param_env,
+            bounds,
             infcx.implied_bounds_tys_with_compat(
                 body_id,
                 param_env,