diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9e324286fa176..b395aa8162c3b 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -259,6 +259,21 @@ fn typeck_with_inspect<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); + // Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`. + if let None = fcx.infcx.tainted_by_errors() { + for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() { + let obligation = fcx.resolve_vars_if_possible(obligation); + if obligation.has_non_region_infer() { + bug!("unexpected inference variable after writeback: {obligation:?}"); + } + fcx.register_predicate(obligation); + } + fcx.select_obligations_where_possible(|_| {}); + if let None = fcx.infcx.tainted_by_errors() { + fcx.report_ambiguity_errors(); + } + } + fcx.detect_opaque_types_added_during_writeback(); // Consistency check our TypeckResults instance can hold all ItemLocalIds diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 9f4ab8ca5d412..20844dee7932f 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -85,8 +85,11 @@ impl<'tcx> TypeckRootCtxt<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; - let infcx = - tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id)); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .in_hir_typeck() + .build(TypingMode::typeck_for_body(tcx, def_id)); let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); let fulfillment_cx = RefCell::new(>::new(&infcx)); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 5fe795bd23a15..ad19cdef4e752 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -71,6 +71,7 @@ impl<'tcx> InferCtxt<'tcx> { tcx: self.tcx, typing_mode: self.typing_mode, considering_regions: self.considering_regions, + in_hir_typeck: self.in_hir_typeck, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), @@ -95,6 +96,7 @@ impl<'tcx> InferCtxt<'tcx> { tcx: self.tcx, typing_mode, considering_regions: self.considering_regions, + in_hir_typeck: self.in_hir_typeck, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index bb9c88500936f..21e999b080d51 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,6 +22,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.next_trait_solver } + fn in_hir_typeck(&self) -> bool { + self.in_hir_typeck + } + fn typing_mode(&self) -> ty::TypingMode<'tcx> { self.typing_mode() } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2d269e320b64d..5bbd8a84b7f03 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -37,10 +37,11 @@ use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; -use crate::infer::region_constraints::UndoLog; +use crate::infer::snapshot::undo_log::UndoLog; use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::traits::{ - self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, + self, ObligationCause, ObligationInspector, PredicateObligation, PredicateObligations, + TraitEngine, }; pub mod at; @@ -156,6 +157,12 @@ pub struct InferCtxtInner<'tcx> { /// which may cause types to no longer be considered well-formed. region_assumptions: Vec>, + /// `-Znext-solver`: Successfully proven goals during HIR typeck which + /// reference inference variables and get reproven after writeback. + /// + /// See the documentation of `InferCtxt::in_hir_typeck` for more details. + hir_typeck_potentially_region_dependent_goals: Vec>, + /// Caches for opaque type inference. opaque_type_storage: OpaqueTypeStorage<'tcx>, } @@ -173,6 +180,7 @@ impl<'tcx> InferCtxtInner<'tcx> { region_constraint_storage: Some(Default::default()), region_obligations: Default::default(), region_assumptions: Default::default(), + hir_typeck_potentially_region_dependent_goals: Default::default(), opaque_type_storage: Default::default(), } } @@ -244,9 +252,29 @@ pub struct InferCtxt<'tcx> { typing_mode: TypingMode<'tcx>, /// Whether this inference context should care about region obligations in - /// the root universe. Most notably, this is used during hir typeck as region + /// the root universe. Most notably, this is used during HIR typeck as region /// solving is left to borrowck instead. pub considering_regions: bool, + /// `-Znext-solver`: Whether this inference context is used by HIR typeck. If so, we + /// need to make sure we don't rely on region identity in the trait solver or when + /// relating types. This is necessary as borrowck starts by replacing each occurrence of a + /// free region with a unique inference variable. If HIR typeck ends up depending on two + /// regions being equal we'd get unexpected mismatches between HIR typeck and MIR typeck, + /// resulting in an ICE. + /// + /// The trait solver sometimes depends on regions being identical. As a concrete example + /// the trait solver ignores other candidates if one candidate exists without any constraints. + /// The goal `&'a u32: Equals<&'a u32>` has no constraints right now. If we replace each + /// occurrence of `'a` with a unique region the goal now equates these regions. See + /// the tests in trait-system-refactor-initiative#27 for concrete examples. + /// + /// We handle this by *uniquifying* region when canonicalizing root goals during HIR typeck. + /// This is still insufficient as inference variables may *hide* region variables, so e.g. + /// `dyn TwoSuper: Super` may hold but MIR typeck could end up having to prove + /// `dyn TwoSuper<&'0 (), &'1 ()>: Super<&'2 ()>` which is now ambiguous. Because of this we + /// stash all successfully proven goals which reference inference variables and then reprove + /// them after writeback. + pub in_hir_typeck: bool, /// If set, this flag causes us to skip the 'leak check' during /// higher-ranked subtyping operations. This flag is a temporary one used @@ -506,6 +534,7 @@ pub struct TypeOutlivesConstraint<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, considering_regions: bool, + in_hir_typeck: bool, skip_leak_check: bool, /// Whether we should use the new trait solver in the local inference context, /// which affects things like which solver is used in `predicate_may_hold`. @@ -518,6 +547,7 @@ impl<'tcx> TyCtxt<'tcx> { InferCtxtBuilder { tcx: self, considering_regions: true, + in_hir_typeck: false, skip_leak_check: false, next_trait_solver: self.next_trait_solver_globally(), } @@ -535,6 +565,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } + pub fn in_hir_typeck(mut self) -> Self { + self.in_hir_typeck = true; + self + } + pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self { self.skip_leak_check = skip_leak_check; self @@ -568,12 +603,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> { - let InferCtxtBuilder { tcx, considering_regions, skip_leak_check, next_trait_solver } = - *self; + let InferCtxtBuilder { + tcx, + considering_regions, + in_hir_typeck, + skip_leak_check, + next_trait_solver, + } = *self; InferCtxt { tcx, typing_mode, considering_regions, + in_hir_typeck, skip_leak_check, inner: RefCell::new(InferCtxtInner::new()), lexical_region_resolutions: RefCell::new(None), @@ -978,6 +1019,22 @@ impl<'tcx> InferCtxt<'tcx> { } } + pub fn push_hir_typeck_potentially_region_dependent_goal( + &self, + goal: PredicateObligation<'tcx>, + ) { + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushHirTypeckPotentiallyRegionDependentGoal); + inner.hir_typeck_potentially_region_dependent_goals.push(goal); + } + + pub fn take_hir_typeck_potentially_region_dependent_goals( + &self, + ) -> Vec> { + assert!(!self.in_snapshot(), "cannot take goals in a snapshot"); + std::mem::take(&mut self.inner.borrow_mut().hir_typeck_potentially_region_dependent_goals) + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a8520c0e71dd1..bb3b51c0ab2c6 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -177,6 +177,7 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn take_registered_region_assumptions(&self) -> Vec> { + assert!(!self.in_snapshot(), "cannot take registered region assumptions in a snapshot"); std::mem::take(&mut self.inner.borrow_mut().region_assumptions) } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 40e4c329446da..fcc0ab3af410c 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -29,6 +29,7 @@ pub(crate) enum UndoLog<'tcx> { ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, PushRegionAssumption, + PushHirTypeckPotentiallyRegionDependentGoal, } macro_rules! impl_from { @@ -79,7 +80,12 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { assert_matches!(popped, Some(_), "pushed region constraint but could not pop it"); } UndoLog::PushRegionAssumption => { - self.region_assumptions.pop(); + let popped = self.region_assumptions.pop(); + assert_matches!(popped, Some(_), "pushed region assumption but could not pop it"); + } + UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => { + let popped = self.hir_typeck_potentially_region_dependent_goals.pop(); + assert_matches!(popped, Some(_), "pushed goal but could not pop it"); } } } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index a418aa82100c4..1bc35e599c706 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -19,6 +19,20 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( ) .unwrap(); +#[derive(Debug, Clone, Copy)] +enum CanonicalizeInputKind { + /// When canonicalizing the `param_env`, we keep `'static` as merging + /// trait candidates relies on it when deciding whether a where-bound + /// is trivial. + ParamEnv, + /// When canonicalizing predicates, we don't keep `'static`. If we're + /// currently outside of the trait solver and canonicalize the root goal + /// during HIR typeck, we replace each occurance of a region with a + /// unique region variable. See the comment on `InferCtxt::in_hir_typeck` + /// for more details. + Predicate { is_hir_typeck_root_goal: bool }, +} + /// Whether we're canonicalizing a query input or the query response. /// /// When canonicalizing an input we're in the context of the caller @@ -26,10 +40,7 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( /// query. #[derive(Debug, Clone, Copy)] enum CanonicalizeMode { - /// When canonicalizing the `param_env`, we keep `'static` as merging - /// trait candidates relies on it when deciding whether a where-bound - /// is trivial. - Input { keep_static: bool }, + Input(CanonicalizeInputKind), /// FIXME: We currently return region constraints referring to /// placeholders and inference variables from a binder instantiated /// inside of the query. @@ -122,7 +133,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let mut variables = Vec::new(); let mut env_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), variables: &mut variables, variable_lookup_table: Default::default(), @@ -154,7 +165,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { } else { let mut env_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), variables, variable_lookup_table: Default::default(), @@ -180,6 +191,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { pub fn canonicalize_input>( delegate: &'a D, variables: &'a mut Vec, + is_hir_typeck_root_goal: bool, input: QueryInput, ) -> ty::Canonical> { // First canonicalize the `param_env` while keeping `'static` @@ -189,7 +201,9 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // while *mostly* reusing the canonicalizer from above. let mut rest_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: false }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal, + }), variables, variable_lookup_table, @@ -296,7 +310,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { } } - fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty { + fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -413,10 +427,10 @@ impl, I: Interner> TypeFolder for Canonicaliz // We don't canonicalize `ReStatic` in the `param_env` as we use it // when checking whether a `ParamEnv` candidate is global. ty::ReStatic => match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: false } => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { CanonicalVarKind::Region(ty::UniverseIndex::ROOT) } - CanonicalizeMode::Input { keep_static: true } + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { .. } => return r, }, @@ -428,12 +442,12 @@ impl, I: Interner> TypeFolder for Canonicaliz // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { panic!("unexpected region in response: {r:?}") } @@ -441,7 +455,7 @@ impl, I: Interner> TypeFolder for Canonicaliz ty::RePlaceholder(placeholder) => match self.canonicalize_mode { // We canonicalize placeholder regions as existentials in query inputs. - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { max_input_universe } => { // If we have a placeholder region inside of a query, it must be from // a new universe. @@ -459,9 +473,7 @@ impl, I: Interner> TypeFolder for Canonicaliz "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: _ } => { - CanonicalVarKind::Region(ty::UniverseIndex::ROOT) - } + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) } @@ -469,16 +481,34 @@ impl, I: Interner> TypeFolder for Canonicaliz } }; - let var = self.get_or_insert_bound_var(r, kind); + let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal: true, + }) = self.canonicalize_mode + { + let var = ty::BoundVar::from(self.variables.len()); + self.variables.push(r.into()); + self.var_kinds.push(kind); + var + } else { + self.get_or_insert_bound_var(r, kind) + }; Region::new_anon_bound(self.cx(), self.binder_index, var) } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal: true, + }) = self.canonicalize_mode + { + // If we're canonicalizing a root goal during HIR typeck, we + // must not use the `cache` as we want to map each occurrence + // of a region to a unique existential variable. + self.inner_fold_ty(t) + } else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { ty } else { - let res = self.cached_fold_ty(t); + let res = self.inner_fold_ty(t); let old = self.cache.insert((self.binder_index, t), res); assert_eq!(old, None); res @@ -541,9 +571,9 @@ impl, I: Interner> TypeFolder for Canonicaliz fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: true } + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { max_input_universe: _ } => {} - CanonicalizeMode::Input { keep_static: false } => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { panic!("erasing 'static in env") } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 5ed316aa6b131..74c5b49ea92f7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -53,20 +53,19 @@ where { /// Canonicalizes the goal remembering the original values /// for each bound variable. + /// + /// This expects `goal` and `opaque_types` to be eager resolved. pub(super) fn canonicalize_goal( &self, + is_hir_typeck_root_goal: bool, goal: Goal, + opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, ) -> (Vec, CanonicalInput) { - // We only care about one entry per `OpaqueTypeKey` here, - // so we only canonicalize the lookup table and ignore - // duplicate entries. - let opaque_types = self.delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let mut orig_values = Default::default(); let canonical = Canonicalizer::canonicalize_input( self.delegate, &mut orig_values, + is_hir_typeck_root_goal, QueryInput { goal, predefined_opaques_in_body: self 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 ce9b794d40d30..8671cc7c3d309 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 @@ -20,6 +20,7 @@ use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; +use crate::resolve::eager_resolve_vars; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; @@ -440,6 +441,7 @@ where return Ok(( NestedNormalizationGoals::empty(), GoalEvaluation { + goal, certainty: Certainty::Maybe(stalled_on.stalled_cause), has_changed: HasChanged::No, stalled_on: Some(stalled_on), @@ -447,7 +449,16 @@ where )); } - let (orig_values, canonical_goal) = self.canonicalize_goal(goal); + // We only care about one entry per `OpaqueTypeKey` here, + // so we only canonicalize the lookup table and ignore + // duplicate entries. + let opaque_types = self.delegate.clone_opaque_types_lookup_table(); + let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); + + let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) + && self.delegate.in_hir_typeck(); + let (orig_values, canonical_goal) = + self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_result = self.search_graph.evaluate_goal( @@ -525,7 +536,10 @@ where }, }; - Ok((normalization_nested_goals, GoalEvaluation { certainty, has_changed, stalled_on })) + Ok(( + normalization_nested_goals, + GoalEvaluation { goal, certainty, has_changed, stalled_on }, + )) } pub(super) fn compute_goal(&mut self, goal: Goal) -> QueryResult { @@ -661,7 +675,7 @@ where let ( NestedNormalizationGoals(nested_goals), - GoalEvaluation { certainty, stalled_on, has_changed: _ }, + GoalEvaluation { goal, certainty, stalled_on, has_changed: _ }, ) = self.evaluate_goal_raw( GoalEvaluationKind::Nested, source, @@ -699,7 +713,15 @@ where // FIXME: Do we need to eagerly resolve here? Or should we check // if the cache key has any changed vars? let with_resolved_vars = self.resolve_vars_if_possible(goal); - if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + if pred.alias + != with_resolved_vars + .predicate + .as_normalizes_to() + .unwrap() + .no_bound_vars() + .unwrap() + .alias + { unchanged_certainty = None; } @@ -711,7 +733,7 @@ where } } } else { - let GoalEvaluation { certainty, has_changed, stalled_on } = + let GoalEvaluation { goal, certainty, has_changed, stalled_on } = self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; if has_changed == HasChanged::Yes { unchanged_certainty = None; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 5ea3f0d10617d..9d52dee3c6592 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -252,8 +252,6 @@ where return None; } - // FIXME(-Znext-solver): Add support to merge region constraints in - // responses to deal with trait-system-refactor-initiative#27. let one = responses[0]; if responses[1..].iter().all(|&resp| resp == one) { return Some(one); @@ -388,6 +386,10 @@ fn response_no_constraints_raw( /// The result of evaluating a goal. pub struct GoalEvaluation { + /// The goal we've evaluated. This is the input goal, but potentially with its + /// inference variables resolved. This never applies any inference constraints + /// from evaluating the goal. + pub goal: Goal, pub certainty: Certainty, pub has_changed: HasChanged, /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 3ce0f02551244..628d32ad55ae0 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -197,6 +197,12 @@ where delegate.compute_goal_fast_path(goal, obligation.cause.span) { match certainty { + // Even if the goal references two instances of the same infer var, + // our fast paths should not depend on region identity. + // + // Only goals proven via the trait solver should be region dependent. + // We never call `push_hir_typeck_potentially_region_dependent_goal` + // here. Certainty::Yes => {} Certainty::Maybe(_) => { self.obligations.register(obligation, None); @@ -207,7 +213,7 @@ where let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on); self.inspect_evaluated_obligation(infcx, &obligation, &result); - let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(E::from_solver_error( @@ -218,6 +224,10 @@ where } }; + // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work + // in the next iteration. This does not resolve the inference variables + // constrained by evaluating the goal. + obligation.predicate = goal.predicate; if has_changed == HasChanged::Yes { // We increment the recursion depth here to track the number of times // this goal has resulted in inference progress. This doesn't precisely @@ -230,7 +240,11 @@ where } match certainty { - Certainty::Yes => {} + Certainty::Yes => { + if infcx.in_hir_typeck && obligation.has_non_region_infer() { + infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); + } + } Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index e86a2305e2334..b4873c8c71cbb 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -148,6 +148,10 @@ pub trait InferCtxtLike: Sized { true } + fn in_hir_typeck(&self) -> bool { + false + } + fn typing_mode(&self) -> TypingMode; fn universe(&self) -> ty::UniverseIndex; diff --git a/tests/crashes/139409.rs b/tests/crashes/139409.rs deleted file mode 100644 index 68cbfa153deb0..0000000000000 --- a/tests/crashes/139409.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: #139409 -//@ compile-flags: -Znext-solver=globally - -fn main() { - trait B {} - impl B for () {} - trait D: B + B { - fn f(&self) {} - } - impl D for () {} - (&() as &dyn D<&(), &()>).f() -} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr new file mode 100644 index 0000000000000..141a07b4be7c0 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` + --> $DIR/ambiguity-due-to-uniquification-1.rs:15:31 + | +LL | (&() as &dyn D<&(), &()>).f() + | ^ + | + = note: cannot satisfy `dyn D<&(), &()>: B<&()>` + = help: the trait `B` is implemented for `()` +note: required by a bound in `D::f` + --> $DIR/ambiguity-due-to-uniquification-1.rs:10:16 + | +LL | trait D: B + B { + | ^^^^ required by this bound in `D::f` +LL | fn f(&self) {} + | - required by a bound in this associated function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs new file mode 100644 index 0000000000000..cfdf74046fbd6 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test for #139409 and trait-system-refactor-initiative#27. + +trait B {} +impl B for () {} +trait D: B + B { + fn f(&self) {} +} +impl D for () {} +fn main() { + (&() as &dyn D<&(), &()>).f() + //[next]~^ ERROR type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` +} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr new file mode 100644 index 0000000000000..3b47888999695 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr @@ -0,0 +1,17 @@ +error[E0283]: type annotations needed: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` + --> $DIR/ambiguity-due-to-uniquification-2.rs:16:23 + | +LL | impls_trait::<'y, _>(foo::<'x, 'y>()); + | ^ + | + = note: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` + = help: the trait `Trait<'t>` is implemented for `()` +note: required by a bound in `impls_trait` + --> $DIR/ambiguity-due-to-uniquification-2.rs:13:23 + | +LL | fn impls_trait<'x, T: Trait<'x>>(_: T) {} + | ^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs new file mode 100644 index 0000000000000..2a9a8b80cc069 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs @@ -0,0 +1,20 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test from trait-system-refactor-initiative#27. + +trait Trait<'t> {} +impl<'t> Trait<'t> for () {} + +fn foo<'x, 'y>() -> impl Trait<'x> + Trait<'y> {} + +fn impls_trait<'x, T: Trait<'x>>(_: T) {} + +fn bar<'x, 'y>() { + impls_trait::<'y, _>(foo::<'x, 'y>()); + //[next]~^ ERROR type annotations needed: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr new file mode 100644 index 0000000000000..e25f892b3657a --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` + --> $DIR/ambiguity-due-to-uniquification-3.rs:28:17 + | +LL | impls_trait(obj, t); + | ----------- ^^^ + | | + | required by a bound introduced by this call + | + = note: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` + = help: the trait `Trait` is implemented for `()` +note: required by a bound in `impls_trait` + --> $DIR/ambiguity-due-to-uniquification-3.rs:24:19 + | +LL | fn impls_trait, U>(_: Inv, _: Inv) {} + | ^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs new file mode 100644 index 0000000000000..6dcd9d5bdf4dd --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test from trait-system-refactor-initiative#27. +// +// Unlike in the previous two tests, `dyn Object: Trait` relies +// on structural identity of type inference variables. This inference variable +// gets constrained to a type containing a region later on. To prevent this +// from causing an ICE during MIR borrowck, we stash goals which depend on +// inference variables and then reprove them at the end of HIR typeck. + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] +trait Trait {} +impl Trait for () {} + +trait Object: Trait + Trait {} + +#[derive(Clone, Copy)] +struct Inv(*mut T); +fn foo() -> (Inv>, Inv) { todo!() } +fn impls_trait, U>(_: Inv, _: Inv) {} + +fn bar() { + let (obj, t) = foo(); + impls_trait(obj, t); + //[next]~^ ERROR type annotations needed + let _: Inv> = obj; +} + +fn main() {}