about summary refs log tree commit diff
path: root/compiler/rustc_infer/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-30 19:22:04 +0000
committerbors <bors@rust-lang.org>2024-01-30 19:22:04 +0000
commitcb4d9a1902b3ea17e93872dafb76d24aa6295c47 (patch)
treec4c69212254f84cc1f8245d06685904b0a1ee13e /compiler/rustc_infer/src
parentf3d71c9249072413f014b378bb5ea79c8a7dc9a7 (diff)
parent720d7a7a03b9997644fe28a12a80a910b2652760 (diff)
downloadrust-cb4d9a1902b3ea17e93872dafb76d24aa6295c47.tar.gz
rust-cb4d9a1902b3ea17e93872dafb76d24aa6295c47.zip
Auto merge of #119101 - compiler-errors:outlives, r=lcnr
Normalize region obligation in lexical region resolution with next-gen solver

This normalizes region obligations when we `resolve_regions`, since they may be unnormalized with deferred projection equality.

It's pretty hard to add tests that exercise this without also triggering MIR borrowck errors (because we don't normalize there yet). I've added one test with two revisions that should test that we both 1. normalize region obligations in the param env, and 2. normalize registered region obligations during lexical region resolution.
Diffstat (limited to 'compiler/rustc_infer/src')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs11
-rw-r--r--compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs5
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs18
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs41
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs29
-rw-r--r--compiler/rustc_infer/src/lib.rs1
6 files changed, 70 insertions, 35 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index db01b5bd707..006638b740e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -518,6 +518,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
                         self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
                     }
+
+                    RegionResolutionError::CannotNormalize(ty, origin) => {
+                        self.tcx
+                            .dcx()
+                            .struct_span_err(origin.span(), format!("cannot normalize `{ty}`"))
+                            .emit();
+                    }
                 }
             }
         }
@@ -559,7 +566,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             RegionResolutionError::GenericBoundFailure(..) => true,
             RegionResolutionError::ConcreteFailure(..)
             | RegionResolutionError::SubSupConflict(..)
-            | RegionResolutionError::UpperBoundUniverseConflict(..) => false,
+            | RegionResolutionError::UpperBoundUniverseConflict(..)
+            | RegionResolutionError::CannotNormalize(..) => false,
         };
 
         let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
@@ -574,6 +582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
             RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
             RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
+            RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
         });
         errors
     }
diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
index 0562c6ccfcf..4a1169e68e0 100644
--- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
+++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
@@ -98,6 +98,8 @@ pub enum RegionResolutionError<'tcx> {
         SubregionOrigin<'tcx>, // cause of the constraint
         Region<'tcx>,          // the placeholder `'b`
     ),
+
+    CannotNormalize(Ty<'tcx>, SubregionOrigin<'tcx>),
 }
 
 impl<'tcx> RegionResolutionError<'tcx> {
@@ -106,7 +108,8 @@ impl<'tcx> RegionResolutionError<'tcx> {
             RegionResolutionError::ConcreteFailure(origin, _, _)
             | RegionResolutionError::GenericBoundFailure(origin, _, _)
             | RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
-            | RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
+            | RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _)
+            | RegionResolutionError::CannotNormalize(_, origin) => origin,
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 6379f84aa25..926e198b219 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -1,11 +1,11 @@
 //! Various code related to computing outlives relations.
 use self::env::OutlivesEnvironment;
 use super::region_constraints::RegionConstraintData;
-use super::{InferCtxt, RegionResolutionError};
+use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
 use crate::infer::free_regions::RegionRelations;
 use crate::infer::lexical_region_resolve;
 use rustc_middle::traits::query::OutlivesBound;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
 
 pub mod components;
 pub mod env;
@@ -41,12 +41,22 @@ impl<'tcx> InferCtxt<'tcx> {
     /// result. After this, no more unification operations should be
     /// done -- or the compiler will panic -- but it is legal to use
     /// `resolve_vars_if_possible` as well as `fully_resolve`.
+    ///
+    /// If you are in a crate that has access to `rustc_trait_selection`,
+    /// then it's probably better to use `resolve_regions`,
+    /// which knows how to normalize registered region obligations.
     #[must_use]
-    pub fn resolve_regions(
+    pub fn resolve_regions_with_normalize(
         &self,
         outlives_env: &OutlivesEnvironment<'tcx>,
+        deeply_normalize_ty: impl Fn(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, Ty<'tcx>>,
     ) -> Vec<RegionResolutionError<'tcx>> {
-        self.process_registered_region_obligations(outlives_env);
+        match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) {
+            Ok(()) => {}
+            Err((ty, origin)) => {
+                return vec![RegionResolutionError::CannotNormalize(ty, origin)];
+            }
+        };
 
         let (var_infos, data) = {
             let mut inner = self.inner.borrow_mut();
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index d7a3bfcbc41..b10bf98e8b5 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -70,6 +70,7 @@ use rustc_data_structures::undo_log::UndoLogs;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::GenericArgKind;
 use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::DUMMY_SP;
 use smallvec::smallvec;
 
 use super::env::OutlivesEnvironment;
@@ -123,26 +124,54 @@ impl<'tcx> InferCtxt<'tcx> {
     /// flow of the inferencer. The key point is that it is
     /// invoked after all type-inference variables have been bound --
     /// right before lexical region resolution.
-    #[instrument(level = "debug", skip(self, outlives_env))]
-    pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
+    #[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))]
+    pub fn process_registered_region_obligations<E>(
+        &self,
+        outlives_env: &OutlivesEnvironment<'tcx>,
+        mut deeply_normalize_ty: impl FnMut(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, E>,
+    ) -> Result<(), (E, 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 bound_clause = clause.kind();
+                let ty::ClauseKind::TypeOutlives(outlives) = bound_clause.skip_binder() else {
+                    return None;
+                };
+                Some(
+                    deeply_normalize_ty(
+                        outlives.0,
+                        SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP),
+                    )
+                    .map(|ty| bound_clause.rebind(ty::OutlivesPredicate(ty, outlives.1))),
+                )
+            })
+            // FIXME(-Znext-solver): How do we accurately report an error here :(
+            .try_collect()
+            .map_err(|e| (e, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)))?;
+
         let my_region_obligations = self.take_registered_region_obligations();
 
         for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
+            let sup_type =
+                deeply_normalize_ty(sup_type, origin.clone()).map_err(|e| (e, origin.clone()))?;
             debug!(?sup_type, ?sub_region, ?origin);
-            let sup_type = self.resolve_vars_if_possible(sup_type);
 
             let outlives = &mut TypeOutlives::new(
                 self,
                 self.tcx,
                 outlives_env.region_bound_pairs(),
                 None,
-                outlives_env.param_env,
+                &normalized_caller_bounds,
             );
             let category = origin.to_constraint_category();
             outlives.type_must_outlive(origin, sup_type, sub_region, category);
         }
+
+        Ok(())
     }
 }
 
@@ -190,7 +219,7 @@ where
         tcx: TyCtxt<'tcx>,
         region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
         implicit_region_bound: Option<ty::Region<'tcx>>,
-        param_env: ty::ParamEnv<'tcx>,
+        caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
     ) -> Self {
         Self {
             delegate,
@@ -199,7 +228,7 @@ where
                 tcx,
                 region_bound_pairs,
                 implicit_region_bound,
-                param_env,
+                caller_bounds,
             ),
         }
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 7a85268492b..5d2f51c689b 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -23,7 +23,7 @@ pub struct VerifyBoundCx<'cx, 'tcx> {
     /// Outside of borrowck the only way to prove `T: '?0` is by
     /// setting  `'?0` to `'empty`.
     implicit_region_bound: Option<ty::Region<'tcx>>,
-    param_env: ty::ParamEnv<'tcx>,
+    caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
 }
 
 impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
@@ -31,9 +31,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         tcx: TyCtxt<'tcx>,
         region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
         implicit_region_bound: Option<ty::Region<'tcx>>,
-        param_env: ty::ParamEnv<'tcx>,
+        caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
     ) -> Self {
-        Self { tcx, region_bound_pairs, implicit_region_bound, param_env }
+        Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds }
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -219,8 +219,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         // To start, collect bounds from user environment. Note that
         // parameter environments are already elaborated, so we don't
         // have to worry about that.
-        let c_b = self.param_env.caller_bounds();
-        let param_bounds = self.collect_outlives_from_clause_list(erased_ty, c_b.into_iter());
+        let param_bounds = 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
@@ -307,22 +308,4 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
             .filter_map(|p| p.no_bound_vars())
             .map(|OutlivesPredicate(_, r)| r)
     }
-
-    /// Searches through a predicate list for a predicate `T: 'a`.
-    ///
-    /// Careful: does not elaborate predicates, and just uses `==`
-    /// when comparing `ty` for equality, so `ty` must be something
-    /// that does not involve inference variables and where you
-    /// otherwise want a precise match.
-    fn collect_outlives_from_clause_list(
-        &self,
-        erased_ty: Ty<'tcx>,
-        clauses: impl Iterator<Item = ty::Clause<'tcx>>,
-    ) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>>
-    {
-        let tcx = self.tcx;
-        clauses.filter_map(|p| p.as_type_outlives_clause()).filter(move |outlives_predicate| {
-            super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty)
-        })
-    }
 }
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 9e3de1825ed..e2dd4b49e1a 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -21,6 +21,7 @@
 #![feature(extend_one)]
 #![feature(let_chains)]
 #![feature(if_let_guard)]
+#![feature(iterator_try_collect)]
 #![feature(min_specialization)]
 #![feature(try_blocks)]
 #![recursion_limit = "512"] // For rustdoc