diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 16842c8208f82..054772daab840 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -121,8 +121,6 @@ pub enum ProbeStep<'tcx> { /// used whenever there are multiple candidates to prove the /// current goalby . NestedProbe(Probe<'tcx>), - CommitIfOkStart, - CommitIfOkSuccess, } /// What kind of probe we're in. In case the probe represents a candidate, or @@ -132,6 +130,8 @@ pub enum ProbeStep<'tcx> { pub enum ProbeKind<'tcx> { /// The root inference context while proving a goal. Root { result: QueryResult<'tcx> }, + /// Trying to normalize an alias by at least one stpe in `NormalizesTo`. + TryNormalizeNonRigid { result: QueryResult<'tcx> }, /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, /// Some candidate to prove the current goal. @@ -143,9 +143,6 @@ pub enum ProbeKind<'tcx> { /// Used in the probe that wraps normalizing the non-self type for the unsize /// trait, which is also structurally matched on. UnsizeAssembly, - /// A call to `EvalCtxt::commit_if_ok` which failed, causing the work - /// to be discarded. - CommitIfOk, /// During upcasting from some source object to target object type, used to /// do a probe to find out what projection type(s) may be used to prove that /// the source type upholds all of the target type's object bounds. diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 4393120501725..98f01fe877244 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::Root { result } => { write!(self.f, "ROOT RESULT: {result:?}") } + ProbeKind::TryNormalizeNonRigid { result } => { + write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}") + } ProbeKind::NormalizedSelfTyAssembly => { write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } @@ -109,9 +112,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::UpcastProjectionCompatibility => { write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } - ProbeKind::CommitIfOk => { - write!(self.f, "COMMIT_IF_OK:") - } ProbeKind::MiscCandidate { name, result } => { write!(self.f, "CANDIDATE {name}: {result:?}") } @@ -132,8 +132,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { } ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?, ProbeStep::NestedProbe(probe) => this.format_probe(probe)?, - ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?, - ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?, } } Ok(()) diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index f36c2cd94ad09..f2c441dcbeda6 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -2,8 +2,8 @@ //! Doing this via a separate goal is called "deferred alias relation" and part //! of our more general approach to "lazy normalization". //! -//! This is done by first normalizing both sides of the goal, ending up in -//! either a concrete type, rigid alias, or an infer variable. +//! This is done by first structurally normalizing both sides of the goal, ending +//! up in either a concrete type, rigid alias, or an infer variable. //! These are related further according to the rules below: //! //! (1.) If we end up with two rigid aliases, then we relate them structurally. @@ -14,17 +14,10 @@ //! //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. -//! -//! Subtle: when relating an opaque to another type, we emit a -//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque. -//! This nested goal starts out as ambiguous and does not actually define the opaque. -//! However, if `?fresh_var` ends up geteting equated to another type, we retry the -//! `NormalizesTo` goal, at which point the opaque is actually defined. use super::EvalCtxt; -use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -35,21 +28,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let tcx = self.tcx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; - let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); + // Structurally normalize the lhs. + let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(lhs); + self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term })); + term + } else { + lhs }; - let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); + // Structurally normalize the rhs. + let rhs = if let Some(alias) = rhs.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(rhs); + self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term })); + term + } else { + rhs }; + // Apply the constraints. + self.try_evaluate_added_goals()?; + let lhs = self.resolve_vars_if_possible(lhs); + let rhs = self.resolve_vars_if_possible(rhs); + debug!(?lhs, ?rhs); + let variance = match direction { ty::AliasRelationDirection::Equate => ty::Variance::Invariant, ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, }; - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { (None, None) => { self.relate(param_env, lhs, variance, rhs)?; @@ -57,14 +63,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } (Some(alias), None) => { - self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs) + self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + (None, Some(alias)) => { + self.relate_rigid_alias_non_alias( + param_env, + alias, + variance.xform(ty::Variance::Contravariant), + lhs, + )?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - (None, Some(alias)) => self.relate_rigid_alias_non_alias( - param_env, - alias, - variance.xform(ty::Variance::Contravariant), - lhs, - ), (Some(alias_lhs), Some(alias_rhs)) => { self.relate(param_env, alias_lhs, variance, alias_rhs)?; @@ -72,97 +82,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } - - /// Relate a rigid alias with another type. This is the same as - /// an ordinary relate except that we treat the outer most alias - /// constructor as rigid. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn relate_rigid_alias_non_alias( - &mut self, - param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - variance: ty::Variance, - term: ty::Term<'tcx>, - ) -> QueryResult<'tcx> { - // NOTE: this check is purely an optimization, the structural eq would - // always fail if the term is not an inference variable. - if term.is_infer() { - let tcx = self.tcx(); - // We need to relate `alias` to `term` treating only the outermost - // constructor as rigid, relating any contained generic arguments as - // normal. We do this by first structurally equating the `term` - // with the alias constructor instantiated with unconstrained infer vars, - // and then relate this with the whole `alias`. - // - // Alternatively we could modify `Equate` for this case by adding another - // variant to `StructurallyRelateAliases`. - let identity_args = self.fresh_args_for_item(alias.def_id); - let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args); - self.eq_structurally_relating_aliases(param_env, term, rigid_ctor.to_ty(tcx).into())?; - self.eq(param_env, alias, rigid_ctor)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - Err(NoSolution) - } - } - - // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var. - /// Normalize the `term` to equate it later. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn try_normalize_term( - &mut self, - param_env: ty::ParamEnv<'tcx>, - term: ty::Term<'tcx>, - ) -> Result>, NoSolution> { - match term.unpack() { - ty::TermKind::Ty(ty) => { - Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into)) - } - ty::TermKind::Const(_) => { - if let Some(alias) = term.to_alias_ty(self.tcx()) { - let term = self.next_term_infer_of_kind(term); - self.add_normalizes_to_goal(Goal::new( - self.tcx(), - param_env, - ty::NormalizesTo { alias, term }, - )); - self.try_evaluate_added_goals()?; - Ok(Some(self.resolve_vars_if_possible(term))) - } else { - Ok(Some(term)) - } - } - } - } - - #[instrument(level = "debug", skip(self, param_env), ret)] - fn try_normalize_ty_recur( - &mut self, - param_env: ty::ParamEnv<'tcx>, - depth: usize, - ty: Ty<'tcx>, - ) -> Option> { - if !self.tcx().recursion_limit().value_within_limit(depth) { - return None; - } - - let ty::Alias(_, alias) = *ty.kind() else { - return Some(ty); - }; - - match self.commit_if_ok(|this| { - let tcx = this.tcx(); - let normalized_ty = this.next_ty_infer(); - this.add_normalizes_to_goal(Goal::new( - tcx, - param_env, - ty::NormalizesTo { alias, term: normalized_ty.into() }, - )); - this.try_evaluate_added_goals()?; - Ok(this.resolve_vars_if_possible(normalized_ty)) - }) { - Ok(ty) => self.try_normalize_ty_recur(param_env, depth + 1, ty), - Err(NoSolution) => Some(ty), - } - } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 619435d2e8d50..4a4efb6884fc5 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -332,7 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// whether an alias is rigid by using the trait solver. When instantiating a response /// from the solver we assume that the solver correctly handled aliases and therefore /// always relate them structurally here. - #[instrument(level = "debug", skip(infcx), ret)] + #[instrument(level = "debug", skip(infcx))] fn unify_query_var_values( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs deleted file mode 100644 index c8f9a461adf52..0000000000000 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::{EvalCtxt, NestedGoals}; -use crate::solve::inspect; -use rustc_middle::traits::query::NoSolution; - -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { - pub(in crate::solve) fn commit_if_ok( - &mut self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result, - ) -> Result { - let mut nested_ecx = EvalCtxt { - infcx: self.infcx, - variables: self.variables, - var_values: self.var_values, - is_normalizes_to_goal: self.is_normalizes_to_goal, - predefined_opaques_in_body: self.predefined_opaques_in_body, - max_input_universe: self.max_input_universe, - search_graph: self.search_graph, - nested_goals: NestedGoals::new(), - tainted: self.tainted, - inspect: self.inspect.new_probe(), - }; - - let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx)); - if result.is_ok() { - let EvalCtxt { - infcx: _, - variables: _, - var_values: _, - is_normalizes_to_goal: _, - predefined_opaques_in_body: _, - max_input_universe: _, - search_graph: _, - nested_goals, - tainted, - inspect, - } = nested_ecx; - self.nested_goals.extend(nested_goals); - self.tainted = tainted; - self.inspect.integrate_snapshot(inspect); - } else { - nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk); - self.inspect.finish_probe(nested_ecx.inspect); - } - - result - } -} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index a0fe6eca0fc8d..1c71fd4d3b765 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -24,7 +24,6 @@ use rustc_middle::ty::{ use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; use std::io::Write; -use std::iter; use std::ops::ControlFlow; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; @@ -36,7 +35,6 @@ use super::{GoalSource, SolverMode}; pub use select::InferCtxtSelectExt; mod canonical; -mod commit_if_ok; mod probe; mod select; @@ -124,11 +122,6 @@ impl<'tcx> NestedGoals<'tcx> { pub(super) fn is_empty(&self) -> bool { self.normalizes_to_goals.is_empty() && self.goals.is_empty() } - - pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) { - self.normalizes_to_goals.extend(other.normalizes_to_goals); - self.goals.extend(other.goals) - } } #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] @@ -511,12 +504,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.inspect.evaluate_added_goals_loop_start(); - fn with_misc_source<'tcx>( - it: impl IntoIterator>>, - ) -> impl Iterator>)> { - iter::zip(iter::repeat(GoalSource::Misc), it) - } - // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); for goal in goals.normalizes_to_goals { @@ -534,16 +521,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { unconstrained_goal, )?; // Add the nested goals from normalization to our own nested goals. + debug!(?nested_goals); goals.goals.extend(nested_goals); // Finally, equate the goal's RHS with the unconstrained var. - // We put the nested goals from this into goals instead of - // next_goals to avoid needing to process the loop one extra - // time if this goal returns something -- I don't think this - // matters in practice, though. - let eq_goals = - self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?; - goals.goals.extend(with_misc_source(eq_goals)); + // + // SUBTLE: + // We structurally relate aliases here. This is necessary + // as we otherwise emit a nested `AliasRelate` goal in case the + // returned term is a rigid alias, resulting in overflow. + // + // It is correct as both `goal.predicate.term` and `unconstrained_rhs` + // start out as an unconstrained inference variable so any aliases get + // fully normalized when instantiating it. + // + // FIXME: Strictly speaking this may be incomplete if the normalized-to + // type contains an ambiguous alias referencing bound regions. We should + // consider changing this to only use "shallow structural equality". + self.eq_structurally_relating_aliases( + goal.param_env, + goal.predicate.term, + unconstrained_rhs, + )?; // We only look at the `projection_ty` part here rather than // looking at the "has changed" return from evaluate_goal, @@ -730,6 +729,46 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + /// This should be used when relating a rigid alias with another type. + /// + /// Normally we emit a nested `AliasRelate` when equating an inference + /// variable and an alias. This causes us to instead constrain the inference + /// variable to the alias without emitting a nested alias relate goals. + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn relate_rigid_alias_non_alias( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + variance: ty::Variance, + term: ty::Term<'tcx>, + ) -> Result<(), NoSolution> { + // NOTE: this check is purely an optimization, the structural eq would + // always fail if the term is not an inference variable. + if term.is_infer() { + let tcx = self.tcx(); + // We need to relate `alias` to `term` treating only the outermost + // constructor as rigid, relating any contained generic arguments as + // normal. We do this by first structurally equating the `term` + // with the alias constructor instantiated with unconstrained infer vars, + // and then relate this with the whole `alias`. + // + // Alternatively we could modify `Equate` for this case by adding another + // variant to `StructurallyRelateAliases`. + let identity_args = self.fresh_args_for_item(alias.def_id); + let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args); + let ctor_ty = rigid_ctor.to_ty(tcx); + let InferOk { value: (), obligations } = self + .infcx + .at(&ObligationCause::dummy(), param_env) + .trace(term, ctor_ty.into()) + .eq_structurally_relating_aliases(term, ctor_ty.into())?; + debug_assert!(obligations.is_empty()); + self.relate(param_env, alias, variance, rigid_ctor) + } else { + Err(NoSolution) + } + } + /// This sohuld only be used when we're either instantiating a previously /// unconstrained "return value" or when we're sure that all aliases in /// the types are rigid. diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index cfec2e9bbf353..56c32d3d53916 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -130,17 +130,14 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.candidates_recur(candidates, nested_goals, probe); nested_goals.truncate(num_goals); } - inspect::ProbeStep::EvaluateGoals(_) - | inspect::ProbeStep::CommitIfOkStart - | inspect::ProbeStep::CommitIfOkSuccess => (), + inspect::ProbeStep::EvaluateGoals(_) => (), } } match probe.kind { inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly - | inspect::ProbeKind::UpcastProjectionCompatibility - | inspect::ProbeKind::CommitIfOk => (), + | inspect::ProbeKind::UpcastProjectionCompatibility => (), // We add a candidate for the root evaluation if there // is only one way to prove a given goal, e.g. for `WellFormed`. // @@ -157,7 +154,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { }); } } - inspect::ProbeKind::MiscCandidate { name: _, result } + inspect::ProbeKind::TryNormalizeNonRigid { result } + | inspect::ProbeKind::MiscCandidate { name: _, result } | inspect::ProbeKind::TraitCandidate { source: _, result } => { candidates.push(InspectCandidate { goal: self, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 4da999f2406b9..43c76cc5f4a00 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -220,8 +220,6 @@ enum WipProbeStep<'tcx> { AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), NestedProbe(WipProbe<'tcx>), - CommitIfOkStart, - CommitIfOkSuccess, } impl<'tcx> WipProbeStep<'tcx> { @@ -230,8 +228,6 @@ impl<'tcx> WipProbeStep<'tcx> { WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), - WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart, - WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess, } } } @@ -467,29 +463,6 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside - /// of the probe into the parent. - pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) { - if let Some(this) = self.as_mut() { - match (this, *probe.state.unwrap()) { - ( - DebugSolver::Probe(WipProbe { steps, .. }) - | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - evaluation: WipProbe { steps, .. }, - .. - }), - DebugSolver::Probe(probe), - ) => { - steps.push(WipProbeStep::CommitIfOkStart); - assert_eq!(probe.kind, None); - steps.extend(probe.steps); - steps.push(WipProbeStep::CommitIfOkSuccess); - } - _ => unreachable!(), - } - } - } - pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 9c7b9812a6e24..7a3a441b73356 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -7,6 +7,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::inspect::ProbeKind; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::{ @@ -32,6 +33,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { self.set_is_normalizes_to_goal(); debug_assert!(self.term_is_fully_unconstrained(goal)); + let normalize_result = self + .probe(|&result| ProbeKind::TryNormalizeNonRigid { result }) + .enter(|this| this.normalize_at_least_one_step(goal)); + + match normalize_result { + Ok(res) => Ok(res), + Err(NoSolution) => { + let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; + if alias.opt_kind(self.tcx()).is_some() { + self.relate_rigid_alias_non_alias( + param_env, + alias, + ty::Variance::Invariant, + term, + )?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + // FIXME(ǵeneric_const_exprs): we currently do not support rigid + // unevaluated constants. + Err(NoSolution) + } + } + } + } + + /// Normalize the given alias by at least one step. If the alias is rigid, this + /// returns `NoSolution`. + #[instrument(level = "debug", skip(self), ret)] + fn normalize_at_least_one_step( + &mut self, + goal: Goal<'tcx, NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { @@ -52,31 +85,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - /// When normalizing an associated item, constrain the result to `term`. - /// - /// While `NormalizesTo` goals have the normalized-to term as an argument, - /// this argument is always fully unconstrained for associated items. - /// It is therefore appropriate to instead think of these `NormalizesTo` goals - /// as function returning a term after normalizing. - /// - /// When equating an inference variable and an alias, we tend to emit `alias-relate` - /// goals and only actually instantiate the inference variable with an alias if the - /// alias is rigid. However, this means that constraining the expected term of - /// such goals ends up fully structurally normalizing the resulting type instead of - /// only by one step. To avoid this we instead use structural equality here, resulting - /// in each `NormalizesTo` only projects by a single step. + /// When normalizing an associated item, constrain the expected term to `term`. /// - /// Not doing so, currently causes issues because trying to normalize an opaque type - /// during alias-relate doesn't actually constrain the opaque if the concrete type - /// is an inference variable. This means that `NormalizesTo` for associated types - /// normalizing to an opaque type always resulted in ambiguity, breaking tests e.g. - /// tests/ui/type-alias-impl-trait/issue-78450.rs. + /// We know `term` to always be a fully unconstrained inference variable, so + /// `eq` should never fail here. However, in case `term` contains aliases, we + /// emit nested `AliasRelate` goals to structurally normalize the alias. pub fn instantiate_normalizes_to_term( &mut self, goal: Goal<'tcx, NormalizesTo<'tcx>>, term: ty::Term<'tcx>, ) { - self.eq_structurally_relating_aliases(goal.param_env, goal.predicate.term, term) + self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); } } diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr index 6aad0f91cf5cc..d913b2e91ca0e 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -1,12 +1,12 @@ error[E0283]: type annotations needed - --> $DIR/ambig-hr-projection-issue-93340.rs:17:5 + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 | LL | cmp_eq | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` | = note: cannot satisfy `_: Scalar` note: required by a bound in `cmp_eq` - --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 + --> $DIR/ambig-hr-projection-issue-93340.rs:10:22 | LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { | ^^^^^^ required by this bound in `cmp_eq` @@ -15,45 +15,6 @@ help: consider specifying the generic arguments LL | cmp_eq:: | +++++++++++ -error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` - --> $DIR/ambig-hr-projection-issue-93340.rs:17:5 - | -LL | cmp_eq - | ^^^^^^ - -error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` - --> $DIR/ambig-hr-projection-issue-93340.rs:17:5 - | -LL | cmp_eq - | ^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` - --> $DIR/ambig-hr-projection-issue-93340.rs:14:51 - | -LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - | ___________________________________________________^ -LL | | -LL | | -LL | | cmp_eq -... | -LL | | -LL | | } - | |_^ - -error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O: Sized` - --> $DIR/ambig-hr-projection-issue-93340.rs:14:6 - | -LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | cmp_eq - | ------ this returned value is of type `for<'a, 'b> fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::}` - | - = note: the return type of a function must have a statically known size - -error: aborting due to 5 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0275, E0283. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr index 941c8a65f152c..d913b2e91ca0e 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr @@ -1,12 +1,12 @@ error[E0283]: type annotations needed - --> $DIR/ambig-hr-projection-issue-93340.rs:17:5 + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 | LL | cmp_eq | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` | = note: cannot satisfy `_: Scalar` note: required by a bound in `cmp_eq` - --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 + --> $DIR/ambig-hr-projection-issue-93340.rs:10:22 | LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { | ^^^^^^ required by this bound in `cmp_eq` diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs index 2093f9b604be8..acfebad38db0c 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs @@ -1,4 +1,5 @@ //@ revisions: old next +//@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver pub trait Scalar: 'static { type RefType<'a>: ScalarRef<'a>; @@ -12,12 +13,8 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT fn build_expression( ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - //[next]~^ ERROR overflow evaluating the requirement - //[next]~| ERROR overflow evaluating the requirement cmp_eq //~^ ERROR type annotations needed - //[next]~| ERROR overflow evaluating the requirement - //[next]~| ERROR overflow evaluating the requirement } fn main() {}