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 4644b145b18a3..ac9a686eb0ec3 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 @@ -14,9 +14,10 @@ use std::iter; use rustc_index::IndexVec; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{ - self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, + self as ty, Canonical, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, TypeFoldable, }; use tracing::{debug, instrument, trace}; @@ -133,6 +134,43 @@ where ), ) } + (_, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); + } + if goals.iter().all(|(_, g, _)| { + let Some(clause) = g.predicate.as_clause() else { + return false; + }; + match clause.kind().skip_binder() { + ClauseKind::Trait(pred) => { + let def_id = pred.def_id(); + (self.cx().is_lang_item(def_id, TraitSolverLangItem::Sized) + || self + .cx() + .is_lang_item(def_id, TraitSolverLangItem::MetaSized)) + && self.resolve_vars_if_possible(pred).self_ty().is_ty_var() + } + ClauseKind::WellFormed(arg) => { + self.resolve_vars_if_possible(arg).is_infer() + } + _ => false, + } + }) { + ( + Certainty::Yes, + NestedNormalizationGoals( + goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + ), + ) + } else { + let certainty = shallow_certainty.and(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + } _ => { let certainty = shallow_certainty.and(goals_certainty); (certainty, NestedNormalizationGoals::empty()) 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 4f87902e46e95..5936923bc4232 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 @@ -171,13 +171,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate { &self, goal: Goal::Predicate>, span: ::Span, - ) -> ( - Result< - (NestedNormalizationGoals, GoalEvaluation), - NoSolution, - >, - inspect::GoalEvaluation, - ); + ) -> (Result, NoSolution>, inspect::GoalEvaluation); } impl SolverDelegateEvalExt for D @@ -221,16 +215,13 @@ where &self, goal: Goal, span: I::Span, - ) -> ( - Result<(NestedNormalizationGoals, GoalEvaluation), NoSolution>, - inspect::GoalEvaluation, - ) { + ) -> (Result, NoSolution>, inspect::GoalEvaluation) { let (result, proof_tree) = EvalCtxt::enter_root( self, self.cx().recursion_limit(), GenerateProofTree::Yes, span, - |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal, None), + |ecx| ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, None), ); (result, proof_tree.unwrap()) } @@ -411,26 +402,6 @@ where goal: Goal, stalled_on: Option>, ) -> Result, NoSolution> { - let (normalization_nested_goals, goal_evaluation) = - self.evaluate_goal_raw(goal_evaluation_kind, source, goal, stalled_on)?; - assert!(normalization_nested_goals.is_empty()); - Ok(goal_evaluation) - } - - /// Recursively evaluates `goal`, returning the nested goals in case - /// the nested goal is a `NormalizesTo` goal. - /// - /// As all other goal kinds do not return any nested goals and - /// `NormalizesTo` is only used by `AliasRelate`, all other callsites - /// should use [`EvalCtxt::evaluate_goal`] which discards that empty - /// storage. - pub(super) fn evaluate_goal_raw( - &mut self, - goal_evaluation_kind: GoalEvaluationKind, - source: GoalSource, - goal: Goal, - stalled_on: Option>, - ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolution> { // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. @@ -441,15 +412,13 @@ where .opaque_types_storage_num_entries() .needs_reevaluation(stalled_on.num_opaques) { - return Ok(( - NestedNormalizationGoals::empty(), - GoalEvaluation { - goal, - certainty: Certainty::Maybe(stalled_on.stalled_cause), - has_changed: HasChanged::No, - stalled_on: Some(stalled_on), - }, - )); + return Ok(GoalEvaluation { + goal, + certainty: Certainty::Maybe(stalled_on.stalled_cause), + has_changed: HasChanged::No, + stalled_on: Some(stalled_on), + nested_goals: NestedNormalizationGoals::empty(), + }); } // We only care about one entry per `OpaqueTypeKey` here, @@ -477,7 +446,7 @@ where let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; - let (normalization_nested_goals, certainty) = + let (nested_goals, certainty) = self.instantiate_and_apply_query_response(goal.param_env, &orig_values, response); // FIXME: We previously had an assert here that checked that recomputing @@ -536,10 +505,7 @@ where }, }; - Ok(( - normalization_nested_goals, - GoalEvaluation { goal, certainty, has_changed, stalled_on }, - )) + Ok(GoalEvaluation { goal, certainty, has_changed, stalled_on, nested_goals }) } pub(super) fn compute_goal(&mut self, goal: Goal) -> QueryResult { @@ -673,10 +639,13 @@ where let unconstrained_goal = goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs }); - let ( - NestedNormalizationGoals(nested_goals), - GoalEvaluation { goal, certainty, stalled_on, has_changed: _ }, - ) = self.evaluate_goal_raw( + let GoalEvaluation { + goal, + certainty, + stalled_on, + has_changed: _, + nested_goals: NestedNormalizationGoals(nested_goals), + } = self.evaluate_goal( GoalEvaluationKind::Nested, source, unconstrained_goal, @@ -733,12 +702,18 @@ where } } } else { - let GoalEvaluation { goal, certainty, has_changed, stalled_on } = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; + let GoalEvaluation { + goal, + certainty, + has_changed, + stalled_on, + nested_goals: NestedNormalizationGoals(nested_goals), + } = self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; + trace!(?nested_goals); + self.nested_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None))); if has_changed == HasChanged::Yes { unchanged_certainty = None; } - match certainty { Certainty::Yes => {} Certainty::Maybe(_) => { diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index c2745c878dc12..5f73f75084232 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -413,6 +413,7 @@ pub struct GoalEvaluation { /// then reevaluating this goal now only needs to resolve `?y` while it would otherwise /// have to resolve both `?x` and `?y`, pub goal: Goal, + pub nested_goals: NestedNormalizationGoals, 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 575e0472e0e38..f9b8d22c044e5 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -7,7 +7,8 @@ use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ - FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, + FromSolverError, Obligation, ObligationCause, PredicateObligation, PredicateObligations, + TraitEngine, }; use rustc_middle::ty::{ self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -15,7 +16,7 @@ use rustc_middle::ty::{ }; use rustc_next_trait_solver::delegate::SolverDelegate as _; use rustc_next_trait_solver::solve::{ - GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt as _, + GoalEvaluation, GoalStalledOn, HasChanged, NestedNormalizationGoals, SolverDelegateEvalExt as _, }; use rustc_span::Span; use thin_vec::ThinVec; @@ -213,7 +214,13 @@ where let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on); self.inspect_evaluated_obligation(infcx, &obligation, &result); - let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result { + let GoalEvaluation { + goal, + certainty, + has_changed, + stalled_on, + nested_goals: NestedNormalizationGoals(nested_goals), + } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(E::from_solver_error( @@ -223,6 +230,18 @@ where continue; } }; + for (_, nested_goal) in nested_goals { + self.obligations.register( + Obligation::with_depth( + infcx.tcx, + ObligationCause::misc(obligation.cause.span, obligation.cause.body_id), + obligation.recursion_depth + 1, + nested_goal.param_env, + nested_goal.predicate, + ), + None, + ); + } // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work // in the next iteration. This does not resolve the inference variables diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 308486811e6e3..757975724faeb 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -20,7 +20,7 @@ use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; use rustc_next_trait_solver::resolve::eager_resolve_vars; use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; -use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::solve::{GoalEvaluation, MaybeCause, SolverDelegateEvalExt as _}; use rustc_span::Span; use tracing::instrument; @@ -249,23 +249,26 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { // `InspectGoal::new` so that the goal has the right result (and maintains // the impression that we don't do this normalizes-to infer hack at all). let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span); - let nested_goals_result = nested.and_then(|(nested, _)| { - normalizes_to_term_hack.constrain_and( - infcx, - span, - proof_tree.uncanonicalized_goal.param_env, - |ocx| { - ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| { - Obligation::new( - infcx.tcx, - ObligationCause::dummy_with_span(span), - goal.param_env, - goal.predicate, - ) - })); - }, - ) - }); + let nested_goals_result = + nested.and_then(|GoalEvaluation { nested_goals, .. }| { + normalizes_to_term_hack.constrain_and( + infcx, + span, + proof_tree.uncanonicalized_goal.param_env, + |ocx| { + ocx.register_obligations(nested_goals.0.into_iter().map( + |(_, goal)| { + Obligation::new( + infcx.tcx, + ObligationCause::dummy_with_span(span), + goal.param_env, + goal.predicate, + ) + }, + )); + }, + ) + }); (proof_tree, nested_goals_result) }); InspectGoal::new(