From 1fb0965feeeeb10a5d4abcf5898d4a2b86cf4a51 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Wed, 22 Jul 2026 18:20:49 +0800 Subject: [PATCH 1/3] move `eager_resolve_vars` to `rustc_type_ir` --- .../src/canonical/mod.rs | 3 +- compiler/rustc_next_trait_solver/src/lib.rs | 1 - .../rustc_next_trait_solver/src/normalize.rs | 10 +- .../rustc_next_trait_solver/src/resolve.rs | 107 ------------------ .../src/solve/eval_ctxt/mod.rs | 3 +- .../src/solve/inspect/analyse.rs | 3 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 105 ++++++++++++++++- 7 files changed, 111 insertions(+), 121 deletions(-) delete mode 100644 compiler/rustc_next_trait_solver/src/resolve.rs diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index f325862102318..5d54065260888 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -19,12 +19,11 @@ use rustc_type_ir::relate::{ }; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, Region, - TypeFoldable, TypingMode, TypingModeEqWrapper, + TypeFoldable, TypingMode, TypingModeEqWrapper, eager_resolve_vars, }; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::resolve::eager_resolve_vars; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, ExternalRegionConstraints, Goal, NestedNormalizationGoals, QueryInput, Response, diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 57bbf8772d322..6396c0e7f2b57 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -15,5 +15,4 @@ pub mod coherence; pub mod delegate; pub mod normalize; pub mod placeholder; -pub mod resolve; pub mod solve; diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 959e18071bb57..e2036f0ab0cc3 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -4,7 +4,7 @@ use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; use rustc_type_ir::{ self as ty, AliasTerm, Binder, FallibleTypeFolder, InferCtxtLike, Interner, TypeFoldable, - TypeSuperFoldable, TypeVisitableExt, UniverseIndex, + TypeSuperFoldable, TypeVisitableExt, UniverseIndex, eager_resolve_vars, }; use tracing::instrument; @@ -143,8 +143,8 @@ where if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { // find out missing typing env change. - let original = crate::resolve::eager_resolve_vars(infcx, original); - let normalized = crate::resolve::eager_resolve_vars(infcx, normalized); + let original = eager_resolve_vars(infcx, original); + let normalized = eager_resolve_vars(infcx, normalized); assert_eq!(original, normalized, "rigid alias is further normalized"); } Ok(normalized) @@ -196,8 +196,8 @@ where if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { // find out missing typing env change. - let original = crate::resolve::eager_resolve_vars(infcx, original); - let normalized = crate::resolve::eager_resolve_vars(infcx, normalized); + let original = eager_resolve_vars(infcx, original); + let normalized = eager_resolve_vars(infcx, normalized); assert_eq!(original, normalized, "rigid alias is further normalized"); } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs deleted file mode 100644 index 3a927e2a92506..0000000000000 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ /dev/null @@ -1,107 +0,0 @@ -use rustc_type_ir::data_structures::DelayedMap; -use rustc_type_ir::inherent::*; -use rustc_type_ir::{ - self as ty, InferCtxtLike, Interner, Region, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, -}; - -/////////////////////////////////////////////////////////////////////////// -// EAGER RESOLUTION - -/// Resolves ty, region, and const vars to their inferred values or their root vars. -struct EagerResolver<'a, D, I = ::Interner> -where - D: InferCtxtLike, - I: Interner, -{ - delegate: &'a D, - /// We're able to use a cache here as the folder does not have any - /// mutable state. - cache: DelayedMap, -} - -pub fn eager_resolve_vars>( - infcx: &Infcx, - value: T, -) -> T { - if value.has_infer() { - let mut folder = EagerResolver::new(infcx); - value.fold_with(&mut folder) - } else { - value - } -} - -impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> { - fn new(delegate: &'a Infcx) -> Self { - EagerResolver { delegate, cache: Default::default() } - } -} - -impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { - fn cx(&self) -> I { - self.delegate.cx() - } - - fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - match t.kind() { - ty::Infer(ty::TyVar(vid)) => { - let resolved = self.delegate.opportunistic_resolve_ty_var(vid); - if t != resolved && resolved.has_infer() { - resolved.fold_with(self) - } else { - resolved - } - } - ty::Infer(ty::IntVar(vid)) => self.delegate.opportunistic_resolve_int_var(vid), - ty::Infer(ty::FloatVar(vid)) => self.delegate.opportunistic_resolve_float_var(vid), - _ => { - if t.has_infer() { - if let Some(&ty) = self.cache.get(&t) { - return ty; - } - let res = t.super_fold_with(self); - assert!(self.cache.insert(t, res)); - res - } else { - t - } - } - } - } - - fn fold_region(&mut self, r: Region) -> Region { - match r.kind() { - ty::ReVar(vid) => self.delegate.opportunistic_resolve_lt_var(vid), - _ => r, - } - } - - fn fold_const(&mut self, c: I::Const) -> I::Const { - match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - let resolved = self.delegate.opportunistic_resolve_ct_var(vid); - if c != resolved && resolved.has_infer() { - resolved.fold_with(self) - } else { - resolved - } - } - _ => { - if c.has_infer() { - c.super_fold_with(self) - } else { - c - } - } - } - } - - fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { - if p.has_infer() { p.super_fold_with(self) } else { p } - } - - fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { - if c.has_infer() { c.super_fold_with(self) } else { c } - } -} diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c3ccb46069063..cfb3b6b85f7cc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,7 +17,7 @@ use rustc_type_ir::solve::{ use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, Region, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, + TypeVisitableExt, TypeVisitor, TypingMode, eager_resolve_vars, }; use tracing::{Level, debug, instrument, trace, warn}; @@ -30,7 +30,6 @@ use crate::coherence; use crate::delegate::SolverDelegate; use crate::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; use crate::placeholder::BoundVarReplacer; -use crate::resolve::eager_resolve_vars; use crate::solve::eval_ctxt::fast_path::{ RerunStalled, compute_goal_fast_path, rerunning_stalled_goal_may_make_progress, }; diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 7bf41d343ecf4..37cbdfb66f505 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -14,10 +14,9 @@ use std::assert_matches; use rustc_infer::infer::InferCtxt; use rustc_macros::extension; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; -use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; +use rustc_middle::ty::{TyCtxt, VisitorResult, eager_resolve_vars, try_visit}; use rustc_middle::{bug, ty}; use rustc_next_trait_solver::canonical::instantiate_canonical_state; -use rustc_next_trait_solver::resolve::eager_resolve_vars; use rustc_next_trait_solver::solve::{MaybeCause, MaybeInfo, SolverDelegateEvalExt as _, inspect}; use rustc_span::Span; use tracing::instrument; diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 75f906e498eda..2f37ab94c152b 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -5,12 +5,15 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext}; -use crate::fold::TypeFoldable; +use crate::data_structures::DelayedMap; use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; use crate::solve::VisibleForLeakCheck; -use crate::{self as ty, Interner, Region, TyVid}; +use crate::{ + self as ty, Interner, Region, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, +}; mod private { pub trait Sealed {} @@ -568,3 +571,101 @@ where TypingMode::Codegen => true, } } + +/// Resolves ty, region, and const vars to their inferred values or their root vars. +pub fn eager_resolve_vars>( + infcx: &Infcx, + value: T, +) -> T { + if value.has_infer() { + let mut folder = EagerResolver::new(infcx); + value.fold_with(&mut folder) + } else { + value + } +} + +struct EagerResolver<'a, D, I = ::Interner> +where + D: InferCtxtLike, + I: Interner, +{ + delegate: &'a D, + /// We're able to use a cache here as the folder does not have any + /// mutable state. + cache: DelayedMap, +} + +impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> { + fn new(delegate: &'a Infcx) -> Self { + EagerResolver { delegate, cache: Default::default() } + } +} + +impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { + fn cx(&self) -> I { + self.delegate.cx() + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + match t.kind() { + ty::Infer(ty::TyVar(vid)) => { + let resolved = self.delegate.opportunistic_resolve_ty_var(vid); + if t != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + ty::Infer(ty::IntVar(vid)) => self.delegate.opportunistic_resolve_int_var(vid), + ty::Infer(ty::FloatVar(vid)) => self.delegate.opportunistic_resolve_float_var(vid), + _ => { + if t.has_infer() { + if let Some(&ty) = self.cache.get(&t) { + return ty; + } + let res = t.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res + } else { + t + } + } + } + } + + fn fold_region(&mut self, r: Region) -> Region { + match r.kind() { + ty::ReVar(vid) => self.delegate.opportunistic_resolve_lt_var(vid), + _ => r, + } + } + + fn fold_const(&mut self, c: I::Const) -> I::Const { + match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + let resolved = self.delegate.opportunistic_resolve_ct_var(vid); + if c != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + _ => { + if c.has_infer() { + c.super_fold_with(self) + } else { + c + } + } + } + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_infer() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_infer() { c.super_fold_with(self) } else { c } + } +} From f5b6af2ee7bad7439e91069a7f4c0720af564db2 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Mon, 20 Jul 2026 16:32:06 +0800 Subject: [PATCH 2/3] normalize before regionck --- .../src/type_check/constraint_conversion.rs | 129 +++++------------- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../rustc_infer/src/infer/outlives/mod.rs | 22 +-- .../src/infer/outlives/obligations.rs | 26 +--- .../rustc_next_trait_solver/src/solve/mod.rs | 15 +- compiler/rustc_trait_selection/src/regions.rs | 59 ++------ .../src/solve/delegate.rs | 47 ++++++- .../src/traits/auto_trait.rs | 3 +- .../cycles/cycle-modulo-ambig-aliases.rs | 1 + .../cycles/cycle-modulo-ambig-aliases.stderr | 11 +- 10 files changed, 122 insertions(+), 193 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index d1e2f2437ae2f..e0e5252ef1ed1 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -5,14 +5,11 @@ use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraint use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; -use rustc_infer::traits::query::type_op::Normalize; -use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArgKind, RegionExt, RegionUtilitiesExt, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, elaborate, fold_regions, + self, GenericArgKind, RegionExt, RegionUtilitiesExt, TyCtxt, TypeFoldable, TypeVisitableExt, + elaborate, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::traits::query::type_op::TypeOpOutput; use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; @@ -137,83 +134,49 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // Extract out various useful fields we'll need below. let ConstraintConversion { - infcx, + infcx: _, universal_regions, region_bound_pairs, known_type_outlives_obligations, .. } = *self; - let mut outlives_predicates = vec![(predicate, constraint_category)]; - for iteration in 0.. { - if outlives_predicates.is_empty() { - break; - } + let pred = predicate; + // Constraint is implied by a coroutine's well-formedness. + if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions + && higher_ranked_assumptions.contains(&pred) + { + return; + } - if !tcx.recursion_limit().value_within_limit(iteration) { - // This may actually be reachable. If so, we should convert - // this to a proper error/consider whether we should detect - // this somewhere else. - bug!( - "unexpected overflowed when processing region obligations: {outlives_predicates:#?}" - ); + let ty::OutlivesPredicate(k1, r2) = pred; + match k1.kind() { + GenericArgKind::Lifetime(r1) => { + let r1_vid = self.to_region_vid(r1); + let r2_vid = self.to_region_vid(r2); + self.add_outlives(r1_vid, r2_vid, constraint_category); } - let mut next_outlives_predicates = vec![]; - for (pred, constraint_category) in outlives_predicates { - // Constraint is implied by a coroutine's well-formedness. - if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions - && higher_ranked_assumptions.contains(&pred) - { - continue; - } - - let ty::OutlivesPredicate(k1, r2) = pred; - match k1.kind() { - GenericArgKind::Lifetime(r1) => { - let r1_vid = self.to_region_vid(r1); - let r2_vid = self.to_region_vid(r2); - self.add_outlives(r1_vid, r2_vid, constraint_category); - } - - GenericArgKind::Type(mut t1) => { - // Scraped constraints may have had inference vars. - t1 = self.infcx.resolve_vars_if_possible(t1); - - // Normalize the type we receive from a `TypeOutlives` obligation - // in the new trait solver. - if infcx.next_trait_solver() { - t1 = self.normalize_and_add_type_outlives_constraints( - ty::Unnormalized::new_wip(t1), - &mut next_outlives_predicates, - ); - } + GenericArgKind::Type(mut t1) => { + // Scraped constraints may have had inference vars. + t1 = self.infcx.resolve_vars_if_possible(t1); - let implicit_region_bound = - ty::Region::new_var(tcx, universal_regions.implicit_region_bound()); - // we don't actually use this for anything, but - // the `TypeOutlives` code needs an origin. - let origin = SubregionOrigin::RelateParamBound(self.span, t1, None); - TypeOutlives::new( - &mut *self, - tcx, - region_bound_pairs, - Some(implicit_region_bound), - known_type_outlives_obligations, - ) - .type_must_outlive( - origin, - t1, - r2, - constraint_category, - ); - } - - GenericArgKind::Const(_) => unreachable!(), - } + let implicit_region_bound = + ty::Region::new_var(tcx, universal_regions.implicit_region_bound()); + // we don't actually use this for anything, but + // the `TypeOutlives` code needs an origin. + let origin = SubregionOrigin::RelateParamBound(self.span, t1, None); + TypeOutlives::new( + &mut *self, + tcx, + region_bound_pairs, + Some(implicit_region_bound), + known_type_outlives_obligations, + ) + .type_must_outlive(origin, t1, r2, constraint_category); } - outlives_predicates = next_outlives_predicates; + GenericArgKind::Const(_) => unreachable!(), } } @@ -279,32 +242,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { debug!("add_type_test(type_test={:?})", type_test); self.constraints.type_tests.push(type_test); } - - // FIXME(trait-refactor-initiative#260): This function should be - // removed. - fn normalize_and_add_type_outlives_constraints( - &self, - ty: ty::Unnormalized<'tcx, Ty<'tcx>>, - next_outlives_predicates: &mut Vec<( - ty::ArgOutlivesPredicate<'tcx>, - ConstraintCategory<'tcx>, - )>, - ) -> Ty<'tcx> { - match self.infcx.fully_perform(Normalize { value: ty }, self.span) { - Ok(TypeOpOutput { output: ty, constraints, .. }) => { - // FIXME(higher_ranked_auto): What should we do with the assumptions here? - if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { - next_outlives_predicates.extend(constraints.iter().flat_map( - |QueryRegionConstraint { constraint, category, .. }| { - constraint.iter_outlives().map(|outlives| (outlives, *category)) - }, - )); - } - ty - } - Err(_) => ty.skip_norm_wip(), - } - } } impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'tcx> { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f59345cd970eb..fd2944a122f03 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -31,7 +31,7 @@ use rustc_session::diagnostics::feature_err; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::{ - InferCtxtRegionExt, OutlivesEnvironmentBuildExt, region_known_to_outlive, ty_known_to_outlive, + OutlivesEnvironmentBuildExt, region_known_to_outlive, ty_known_to_outlive, }; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty, diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 76db3830d3962..ce2650dff9f18 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -3,14 +3,14 @@ use std::iter; use rustc_data_structures::undo_log::UndoLogs; -use rustc_middle::traits::query::{NoSolution, OutlivesBound}; +use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; use rustc_span::Span; use tracing::instrument; use self::env::OutlivesEnvironment; use super::region_constraints::{RegionConstraintData, UndoLog}; -use super::{InferCtxt, RegionResolutionError, SubregionOrigin}; +use super::{InferCtxt, RegionResolutionError}; use crate::infer::free_regions::RegionRelations; use crate::infer::lexical_region_resolve; use crate::infer::region_constraints::ConstraintKind; @@ -38,25 +38,15 @@ impl<'tcx> InferCtxt<'tcx> { /// 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. + /// Don't call this directly unless you know what you're doing. + /// You probably want to use `resolve_regions` instead. #[must_use] - pub fn resolve_regions_with_normalize( + pub fn resolve_regions_with_outlives_env( &self, outlives_env: &OutlivesEnvironment<'tcx>, - deeply_normalize_ty: impl Fn( - ty::PolyTypeOutlivesPredicate<'tcx>, - SubregionOrigin<'tcx>, - ) -> Result, NoSolution>, span: Span, ) -> Vec> { - match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty, span) { - Ok(()) => {} - Err((clause, origin)) => { - return vec![RegionResolutionError::CannotNormalize(clause, origin)]; - } - }; + self.process_registered_region_obligations(outlives_env, span); let mut storage = { 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 c8a94d26133ea..c2753d7499556 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -63,11 +63,10 @@ use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, RegionExt, RegionVid, - Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, + Ty, TyCtxt, TypeVisitableExt, eager_resolve_vars, }; use rustc_span::Span; use smallvec::smallvec; @@ -76,7 +75,6 @@ use tracing::{debug, instrument}; use super::env::OutlivesEnvironment; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; -use crate::infer::resolve::OpportunisticRegionResolver; use crate::infer::snapshot::undo_log::UndoLog; use crate::infer::{ self, GenericKind, InferCtxt, SubregionOrigin, TypeOutlivesConstraint, VerifyBound, @@ -288,17 +286,12 @@ 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, deeply_normalize_ty))] + #[instrument(level = "debug", skip(self, outlives_env))] pub fn process_registered_region_obligations( &self, outlives_env: &OutlivesEnvironment<'tcx>, - mut deeply_normalize_ty: impl FnMut( - PolyTypeOutlivesPredicate<'tcx>, - SubregionOrigin<'tcx>, - ) - -> Result, NoSolution>, span: Span, - ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { + ) { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); if self.tcx.assumptions_on_binders() { @@ -322,17 +315,10 @@ impl<'tcx> InferCtxt<'tcx> { } for TypeOutlivesConstraint { sup_type, sub_region, origin } in my_region_obligations { - let outlives = ty::Binder::dummy(ty::OutlivesPredicate(sup_type, sub_region)); - let ty::OutlivesPredicate(sup_type, sub_region) = - deeply_normalize_ty(outlives, origin.clone()) - .map_err(|NoSolution| (outlives, origin.clone()))? - .no_bound_vars() - .expect("started with no bound vars, should end with no bound vars"); // `TypeOutlives` is structural, so we should try to opportunistically resolve all // region vids before processing regions, so we have a better chance to match clauses // in our param-env. - let (sup_type, sub_region) = - (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self)); + let (sup_type, sub_region) = eager_resolve_vars(self, (sup_type, sub_region)); if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions && outlives_env @@ -355,8 +341,6 @@ impl<'tcx> InferCtxt<'tcx> { outlives.type_must_outlive(origin, sup_type, sub_region, category); } } - - Ok(()) } } @@ -435,6 +419,8 @@ where category: ConstraintCategory<'tcx>, ) { assert!(!ty.has_escaping_bound_vars()); + debug_assert!(!ty.has_non_region_infer()); + debug_assert!(!self.tcx.next_trait_solver_globally() || !ty.has_non_rigid_aliases()); let mut components = smallvec![]; push_outlives_components(self.tcx, ty, &mut components); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 195f08dfafd59..8a869df067301 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -23,7 +23,7 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, Region}; +use rustc_type_ir::{self as ty, Interner, Region, TypeVisitableExt}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -90,16 +90,25 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let ty::OutlivesPredicate(ty, lt) = goal.predicate; + let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; if self.cx().assumptions_on_binders() { - // FIXME(-Zassumptions-on-binders): we need to normalize `ty` let constraint = self.destructure_type_outlives(ty, lt); self.register_solver_region_constraint(constraint); } else { self.register_ty_outlives(ty, lt); } - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + // The normalized type can still contain non-rigid higher ranked aliases if their + // normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones. + // Infer vars may be resolved to types/consts containing non-rigid aliases later. + // Thus we should stall this goal to avoid registering non-rigid type outlives into + // the outer infcx. + if ty.has_non_region_infer() || ty.has_non_rigid_aliases() { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } } #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 5ff2c904e94c5..c63b7773739d0 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -5,12 +5,9 @@ use rustc_infer::infer::{ InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt, TypeOutlivesConstraint, }; use rustc_macros::extension; -use rustc_middle::traits::ObligationCause; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode, Unnormalized, elaborate}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, elaborate}; +use rustc_span::DUMMY_SP; -use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; #[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] @@ -34,18 +31,8 @@ impl<'tcx> OutlivesEnvironment<'tcx> { 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), - Unnormalized::new_wip(type_outlives), - ) { - Ok(new) => type_outlives = new, - Err(_) => { - infcx.dcx().delayed_bug(format!("could not normalize `{bound}`")); - } - } - } + if let Some(type_outlives) = bound.as_type_outlives_clause() { + debug_assert!(!infcx.next_trait_solver() || !type_outlives.has_non_rigid_aliases()); bounds.push(type_outlives); } } @@ -75,14 +62,12 @@ impl<'tcx> OutlivesEnvironment<'tcx> { #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { - /// Resolve regions, using the deep normalizer to normalize any type-outlives - /// obligations in the process. This is in `rustc_trait_selection` because - /// we need to normalize. - /// - /// Prefer this method over `resolve_regions_with_normalize`, unless you are - /// doing something specific for normalization. + /// Resolve regions lexically. /// /// This function assumes that all infer variables are already constrained. + /// + /// FIXME(#155345): this can probably be moved back to `rustc_infer` now that normalization is + /// no longer required. These two extension traits won't be needed then. fn resolve_regions( &self, body_def_id: LocalDefId, @@ -94,34 +79,6 @@ impl<'tcx> InferCtxt<'tcx> { self.tcx.def_span(body_def_id), ) } - - /// Don't call this directly unless you know what you're doing. - fn resolve_regions_with_outlives_env( - &self, - outlives_env: &OutlivesEnvironment<'tcx>, - span: Span, - ) -> Vec> { - self.resolve_regions_with_normalize( - &outlives_env, - |ty, origin| { - let ty = self.resolve_vars_if_possible(ty); - - if self.next_trait_solver() { - crate::solve::deeply_normalize( - self.at( - &ObligationCause::dummy_with_span(origin.span()), - outlives_env.param_env, - ), - Unnormalized::new_wip(ty), - ) - .map_err(|_: Vec>| NoSolution) - } else { - Ok(ty) - } - }, - span, - ) - } } /// Given a known `param_env` and a set of well formed types, can we prove that diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 605f4d6758a5a..47a147b595a1d 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -1,7 +1,7 @@ use std::collections::hash_map::Entry; use std::ops::Deref; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; @@ -16,7 +16,8 @@ use rustc_infer::traits::solve::{ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; use rustc_middle::ty::{ - self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt, TypingMode, + self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques}; use rustc_span::{DUMMY_SP, Span}; @@ -87,6 +88,33 @@ fn goal_stalled_on_args_or_nonempty_opaques<'tcx>( } } +struct CollectNonRegionInfer<'tcx> { + infers: Vec>, + visited: FxHashSet>, +} + +impl<'tcx> TypeVisitor> for CollectNonRegionInfer<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if self.visited.contains(&ty) { + return; + } + + match ty.kind() { + ty::Infer(_) => self.infers.push(ty.into()), + _ => ty.super_visit_with(self), + } + + self.visited.insert(ty); + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) { + match ct.kind() { + ty::ConstKind::Infer(_) => self.infers.push(ct.into()), + _ => ct.super_visit_with(self), + } + } +} + impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> { type Infcx = InferCtxt<'tcx>; type Interner = TyCtxt<'tcx>; @@ -189,6 +217,21 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< return Outcome::NoFastPath; } + let ty = self.resolve_vars_if_possible(outlives.0); + let mut infer_collector = CollectNonRegionInfer { + infers: Default::default(), + visited: Default::default(), + }; + ty.visit_with(&mut infer_collector); + let infers = infer_collector.infers; + if !infers.is_empty() { + return goal_stalled_on_args(infers); + } + + if ty.has_non_rigid_aliases() { + return Outcome::NoFastPath; + } + self.0.register_type_outlives_constraint( outlives.0, outlives.1, diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 691290f75495b..c959b6856ca70 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -173,8 +173,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); - let _ = - infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty), DUMMY_SP); + let _ = infcx.process_registered_region_obligations(&outlives_env, DUMMY_SP); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs index 5c13a871a7b8d..70c1d9713d0a9 100644 --- a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs @@ -86,4 +86,5 @@ fn foo() {} fn main() { foo::<&_>(); //~^ ERROR overflow evaluating the requirement `&_: Typed` + //~| ERROR: type annotations needed: cannot satisfy `_: '_` } diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr index d350eb0f7795c..133bfd7140c9c 100644 --- a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr @@ -1,3 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `_: '_` + --> $DIR/cycle-modulo-ambig-aliases.rs:87:11 + | +LL | foo::<&_>(); + | ^^ cannot satisfy `_: '_` + error[E0275]: overflow evaluating the requirement `&_: Typed` --> $DIR/cycle-modulo-ambig-aliases.rs:87:11 | @@ -10,6 +16,7 @@ note: required by a bound in `foo` LL | fn foo() {} | ^^^^^ required by this bound in `foo` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0275`. +Some errors have detailed explanations: E0275, E0284. +For more information about an error, try `rustc --explain E0275`. From 7c1c57179de7152fddf0b61a37a94c7659b76815 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Thu, 23 Jul 2026 13:51:02 +0800 Subject: [PATCH 3/3] fix building std with next solver --- .../src/check/always_applicable.rs | 2 +- .../rustc_hir_analysis/src/outlives/utils.rs | 4 +-- .../src/infer/outlives/for_liveness.rs | 8 ++---- .../src/infer/outlives/obligations.rs | 10 ++++++-- .../src/infer/outlives/test_type_match.rs | 17 +++---------- .../rustc_infer/src/infer/outlives/verify.rs | 25 ++++++------------- .../src/infer/region_constraints/mod.rs | 8 +++--- .../eval_ctxt/solver_region_constraints.rs | 3 ++- .../query/type_op/implied_outlives_bounds.rs | 17 +++++++++---- compiler/rustc_type_ir/src/binder.rs | 8 ++++++ compiler/rustc_type_ir/src/elaborate.rs | 8 +++--- compiler/rustc_type_ir/src/outlives.rs | 9 ++++--- 12 files changed, 59 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index b94a8cd5e6d63..9a8008214ed04 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -282,7 +282,7 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( // reference the params from the ADT instead of from the impl which is bad UX. To resolve // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); - let adt_env = ty::EarlyBinder::bind(tcx, tcx.param_env(adt_def_id)) + let adt_env = ty::EarlyBinder::bind_unchecked(tcx.param_env(adt_def_id)) .instantiate(tcx, adt_to_impl_args) .skip_norm_wip(); diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index ac58a37cc8e81..0157703532268 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -83,7 +83,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( span_bug!(span, "Should not deduce placeholder outlives component"); } - Component::Alias(alias_ty) => { + Component::Alias(is_rigid, alias_ty) => { // This would either arise from something like: // // ``` @@ -102,7 +102,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // // Here we want to add an explicit `where ::Item: 'a` // or `Opaque: 'a` depending on the alias kind. - let ty = alias_ty.to_ty(tcx, ty::IsRigid::No); + let ty = alias_ty.to_ty(tcx, is_rigid); required_predicates .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) .or_insert(span); diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index 15756f03f25f9..1ad0c5d192cf1 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -56,7 +56,7 @@ where // either `'static` or a unique outlives region, and if one is // found, we just need to prove that that region is still live. // If one is not found, then we continue to walk through the alias. - ty::Alias(_, alias_ty @ ty::AliasTy { kind, args, .. }) => { + ty::Alias(is_rigid, alias_ty @ ty::AliasTy { kind, args, .. }) => { let tcx = self.tcx; let param_env = self.param_env; let def_id = match kind { @@ -82,11 +82,7 @@ where &outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| { VerifyIfEq { ty, bound } }), - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - alias_ty.to_ty(tcx, ty::IsRigid::No), + alias_ty.to_ty(tcx, is_rigid), ) } }) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index c2753d7499556..058aaa017cad4 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -420,7 +420,10 @@ where ) { assert!(!ty.has_escaping_bound_vars()); debug_assert!(!ty.has_non_region_infer()); - debug_assert!(!self.tcx.next_trait_solver_globally() || !ty.has_non_rigid_aliases()); + debug_assert!( + !self.tcx.next_trait_solver_globally() || !ty.has_non_rigid_aliases(), + "{ty:?} has non-rigid aliases" + ); let mut components = smallvec![]; push_outlives_components(self.tcx, ty, &mut components); @@ -446,7 +449,10 @@ where Component::Placeholder(placeholder_ty) => { self.placeholder_ty_must_outlive(origin, region, *placeholder_ty); } - Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty), + Component::Alias(is_rigid, alias_ty) => { + debug_assert_eq!(*is_rigid, ty::IsRigid::yes_if_next_solver(self.tcx)); + self.alias_ty_must_outlive(origin, region, *alias_ty); + } Component::EscapingAlias(subcomponents) => { self.components_must_outlive(origin, subcomponents, region, category); } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index b84c6d0aed42d..a90f7d58847e3 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -44,13 +44,10 @@ pub fn extract_verify_if_eq<'tcx>( assert!(!verify_if_eq_b.has_escaping_bound_vars()); let mut m = MatchAgainstHigherRankedOutlives::new(tcx); let verify_if_eq = verify_if_eq_b.skip_binder(); - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - let (verify_ty, test_ty) = - ty::set_aliases_to_non_rigid(tcx, (verify_if_eq.ty, test_ty)).skip_norm_wip(); - m.relate(verify_ty, test_ty).ok()?; + debug_assert!( + !tcx.next_trait_solver_globally() || !(verify_if_eq.ty, test_ty).has_non_rigid_aliases() + ); + m.relate(verify_if_eq.ty, test_ty).ok()?; if let ty::RegionKind::ReBound(index_kind, br) = verify_if_eq.bound.kind() { assert!(matches!(index_kind, ty::BoundVarIndexKind::Bound(ty::INNERMOST))); @@ -86,12 +83,6 @@ pub(super) fn can_match_erased_ty<'tcx>( assert!(!outlives_predicate.has_escaping_bound_vars()); let erased_outlives_predicate = tcx.erase_and_anonymize_regions(outlives_predicate); let outlives_ty = erased_outlives_predicate.skip_binder().0; - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - let (outlives_ty, erased_ty) = - ty::set_aliases_to_non_rigid(tcx, (outlives_ty, erased_ty)).skip_normalization(); if outlives_ty == erased_ty { // pointless micro-optimization true diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 3c07a32471bdb..f1e927912ef51 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -96,14 +96,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { &self, alias_ty: ty::AliasTy<'tcx>, ) -> Vec> { - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. let erased_alias_ty = self.tcx.erase_and_anonymize_regions( - ty::set_aliases_to_non_rigid(self.tcx, alias_ty) - .skip_norm_wip() - .to_ty(self.tcx, ty::IsRigid::No), + alias_ty.to_ty(self.tcx, ty::IsRigid::yes_if_next_solver(self.tcx)), ); self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } @@ -168,7 +162,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { Component::Placeholder(placeholder_ty) => { self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty)) } - Component::Alias(alias_ty) => self.alias_bound(alias_ty), + // `type_must_outlive` already asserted that it's rigid in the next solver. + Component::Alias(_, alias_ty) => self.alias_bound(alias_ty), Component::EscapingAlias(ref components) => self.bound_from_components(components), Component::UnresolvedInferenceVariable(v) => { // Ignore this, we presume it will yield an error later, since @@ -245,20 +240,14 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // 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 => {} - // FIXME(#155345): We probably want to assert that the rhs is rigid. - (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.kind == a2.kind => {} + (GenericKind::Alias(a1), ty::Alias(is_rigid, a2)) if a1.kind == a2.kind => { + debug_assert_eq!(*is_rigid, ty::IsRigid::yes_if_next_solver(self.tcx)); + } _ => return None, } let p_ty = p.to_ty(tcx); - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - let erased_p_ty = self.tcx.erase_and_anonymize_regions( - ty::set_aliases_to_non_rigid(self.tcx, p_ty).skip_norm_wip(), - ); - let erased_ty = ty::set_aliases_to_non_rigid(self.tcx, erased_ty).skip_norm_wip(); + let erased_p_ty = self.tcx.erase_and_anonymize_regions(p_ty); (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) })); diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 32ed93564bd37..ef77c9b17bba8 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -190,6 +190,8 @@ pub struct Verify<'tcx> { pub enum GenericKind<'tcx> { Param(ty::ParamTy), Placeholder(ty::PlaceholderType<'tcx>), + // FIXME: we expect this alias to be rigid in the next solver. + // But we can't assert that in construction since this enum is public. Alias(ty::AliasTy<'tcx>), } @@ -807,11 +809,7 @@ impl<'tcx> GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => p.to_ty(tcx), GenericKind::Placeholder(ref p) => Ty::new_placeholder(tcx, *p), - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - GenericKind::Alias(ref p) => p.to_ty(tcx, ty::IsRigid::No), + GenericKind::Alias(ref p) => p.to_ty(tcx, ty::IsRigid::yes_if_next_solver(tcx)), } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index 129a7b0f0de78..f31f292240a56 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -179,7 +179,8 @@ where Placeholder(p) => { RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r) } - Alias(alias) => self.destructure_alias_outlives(*alias, r), + // The alias is either rigid or ambiguous in which case we'll return with ambiguity. + Alias(_, alias) => self.destructure_alias_outlives(*alias, r), UnresolvedInferenceVariable(_) => RegionConstraint::Ambiguity, Param(_) => panic!("Params should have been canonicalized to placeholders"), EscapingAlias(components) => self.destructure_components(components, r), diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index a98f8b9a9af88..c9de97ea82c11 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -61,6 +61,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( "compute_implied_outlives_bounds assumes region obligations are empty before starting" ); + let tcx = ocx.infcx.tcx; + // FIXME: This doesn't seem right. All call sites already normalize `ty`: // - `Ty`s from the `DefiningTy` in Borrowck: we have to normalize in the caller // in order to get implied bounds involving any unconstrained region vars @@ -141,8 +143,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( r_b, ))) => { let mut components = smallvec![]; - push_outlives_components(ocx.infcx.tcx, ty_a, &mut components); - outlives_bounds.extend(implied_bounds_from_components(r_b, components)) + push_outlives_components(tcx, ty_a, &mut components); + outlives_bounds.extend(implied_bounds_from_components(tcx, r_b, components)) } } } @@ -159,8 +161,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx.infcx.clone_registered_region_obligations() { let mut components = smallvec![]; - push_outlives_components(ocx.infcx.tcx, sup_type, &mut components); - outlives_bounds.extend(implied_bounds_from_components(sub_region, components)); + push_outlives_components(tcx, sup_type, &mut components); + outlives_bounds.extend(implied_bounds_from_components(tcx, sub_region, components)); } } @@ -197,6 +199,7 @@ impl<'tcx> TypeVisitor> for ContainsBevyParamSet<'tcx> { /// `T: 'a` to hold. We get to assume that the caller has validated /// those relationships. fn implied_bounds_from_components<'tcx>( + tcx: TyCtxt<'tcx>, sub_region: ty::Region<'tcx>, sup_components: SmallVec<[Component>; 4]>, ) -> Vec> { @@ -206,7 +209,11 @@ fn implied_bounds_from_components<'tcx>( match component { Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), - Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + Component::Alias(is_rigid, p) => { + // We expect them to be already deeply normalized. + debug_assert_eq!(is_rigid, ty::IsRigid::yes_if_next_solver(tcx)); + Some(OutlivesBound::RegionSubAlias(sub_region, p)) + } Component::Placeholder(_p) => { // FIXME(non_lifetime_binders): Placeholders don't currently // imply anything for outlives, though they could easily. diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index a3ba407b4d7c7..c11532c798a42 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -354,6 +354,14 @@ impl> EarlyBinder { } } +impl EarlyBinder { + /// Use `bind/bind_iter/bind_no_rigid_aliases` instead. + /// Don't use this unless you know what you're doing. + pub fn bind_unchecked(value: T) -> EarlyBinder { + EarlyBinder { value, _tcx: PhantomData } + } +} + impl EarlyBinder { pub fn as_ref(&self) -> EarlyBinder { EarlyBinder { value: &self.value, _tcx: PhantomData } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index a22f558fe5b54..03d5fc1ff8330 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -274,11 +274,11 @@ fn elaborate_component_to_clause( Component::UnresolvedInferenceVariable(_) => None, - Component::Alias(alias_ty) => { + Component::Alias(is_rigid, alias_ty) => { // We might end up here if we have `Foo<::Assoc>: 'a`. // With this, we can deduce that `::Assoc: 'a`. Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - alias_ty.to_ty(cx, ty::IsRigid::No), + alias_ty.to_ty(cx, is_rigid), outlives_region, ))) } @@ -415,9 +415,9 @@ pub fn elaborate_outlives_assumptions( collected.insert(ty::OutlivesPredicate(ty.into(), r2)); } - Component::Alias(alias_ty) => { + Component::Alias(is_rigid, alias_ty) => { collected.insert(ty::OutlivesPredicate( - alias_ty.to_ty(cx, ty::IsRigid::No).into(), + alias_ty.to_ty(cx, is_rigid).into(), r2, )); } diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 1474e65a9ccbf..1494da29527c4 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -26,7 +26,10 @@ pub enum Component { // is not in a position to judge which is the best technique, so // we just product the projection as a component and leave it to // the consumer to decide (but see `EscapingProjection` below). - Alias(ty::AliasTy), + // + // We have to track rigidness because it's also used in param env + // elaboration where things are not normalized yet. + Alias(ty::IsRigid, ty::AliasTy), // In the case where a projection has escaping regions -- meaning // regions bound within the type itself -- we always use @@ -149,7 +152,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // trait-ref. Therefore, if we see any higher-ranked regions, // we simply fallback to the most restrictive rule, which // requires that `Pi: 'a` for all `i`. - ty::Alias(_, alias_ty) => { + ty::Alias(is_rigid, alias_ty) => { if !alias_ty.has_escaping_bound_vars() { // best case: no escaping regions, so push the // projection and skip the subtree (thus generating no @@ -157,7 +160,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // the rules OutlivesProjectionEnv, // OutlivesProjectionTraitDef, and // OutlivesProjectionComponents to regionck. - self.out.push(Component::Alias(alias_ty)); + self.out.push(Component::Alias(is_rigid, alias_ty)); } else { // fallback case: hard code // OutlivesProjectionComponents. Continue walking