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 97aaf076a9d9a..66a316a310173 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 @@ -8,7 +8,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; +use rustc_type_ir::search_graph::{CandidateHeadUsages, IncreaseDepthForNested, PathKind}; use rustc_type_ir::solve::{ AccessedOpaques, ExternalRegionConstraints, FetchEligibleAssocItemResponse, MaybeInfo, NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunCondition, @@ -483,7 +483,7 @@ where stalled_on: Option>, ) -> Result, NoSolutionOrRerunNonErased> { let (normalization_nested_goals, goal_evaluation) = - self.evaluate_goal_raw(source, goal, stalled_on)?; + self.evaluate_goal_raw(source, goal, stalled_on, IncreaseDepthForNested::Yes)?; assert!(normalization_nested_goals.is_empty()); Ok(goal_evaluation) } @@ -575,6 +575,7 @@ where source: GoalSource, goal: Goal, stalled_on: Option>, + increase_depth_for_nested: IncreaseDepthForNested, ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { if let RerunStalled::WontMakeProgress(stalled_certainty) = self.rerunning_stalled_goal_may_make_progress(stalled_on.as_ref()) @@ -652,6 +653,7 @@ where self.cx(), canonical_goal, step_kind, + increase_depth_for_nested, &mut inspect::ProofTreeBuilder::new_noop(), ); @@ -691,6 +693,7 @@ where self.cx(), canonical_goal, step_kind, + increase_depth_for_nested, &mut inspect::ProofTreeBuilder::new_noop(), ); assert!( diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index 81a035e8a9980..2101010bf238b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -3,6 +3,7 @@ mod free_alias; mod inherent; mod opaque_types; +use rustc_type_ir::search_graph::IncreaseDepthForNested; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, ProjectionPredicate}; use tracing::{instrument, trace}; @@ -67,7 +68,19 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ }, - ) = self.evaluate_goal_raw(GoalSource::TypeRelating, normalizes_to, None)?; + ) = self.evaluate_goal_raw( + GoalSource::TypeRelating, + normalizes_to, + None, + // We don't increase depth for nested goals for this `NormalizesTo` goal, as + // evaluating `NormalizesTo` is an extra step only exists in the new solver + // that behaves like a function call rather than an independent nested goal + // evaluation, so increasing the depth may end up regressions which hit the + // recursion limits for crates compiled well with the old solver. Furthermore, + // those nested goals from `NormalizesTo` will be evaluated again as the + // caller's nested goals with increased depths anyway. + IncreaseDepthForNested::No, + )?; trace!(?nested_goals); diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 3a962b3832aa3..3283c0ccc170a 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -262,6 +262,12 @@ impl CandidateHeadUsages { } } +#[derive(Debug, Clone, Copy)] +pub enum IncreaseDepthForNested { + Yes, + No, +} + #[derive(Debug, Clone, Copy)] struct AvailableDepth(usize); impl AvailableDepth { @@ -276,6 +282,13 @@ impl AvailableDepth { stack: &Stack, ) -> Option { if let Some(last) = stack.last() { + match last.increase_depth_for_nested { + IncreaseDepthForNested::Yes => {} + IncreaseDepthForNested::No => { + return Some(last.available_depth); + } + } + if last.available_depth.0 == 0 { return None; } @@ -566,7 +579,7 @@ impl EvaluationResult { encountered_overflow, // Unlike `encountered_overflow`, we share `heads`, `required_depth`, // and `nested_goals` between evaluations. - required_depth: final_entry.required_depth, + required_depth: final_entry.required_depth(), heads: final_entry.heads, nested_goals: final_entry.nested_goals, // We only care about the final result. @@ -619,13 +632,17 @@ impl, X: Cx> SearchGraph { fn update_parent_goal( stack: &mut Stack, step_kind_from_parent: PathKind, - required_depth_for_nested: usize, + min_reachable_for_nested: Option, heads: impl Iterator, encountered_overflow: bool, context: UpdateParentGoalCtxt<'_, X>, ) { if let Some((parent_index, parent)) = stack.last_mut_with_index() { - parent.required_depth = parent.required_depth.max(required_depth_for_nested + 1); + if let Some(min_reachable_for_nested) = min_reachable_for_nested { + parent.min_reached_available_depth = AvailableDepth( + parent.min_reached_available_depth.0.min(min_reachable_for_nested.0), + ); + } parent.encountered_overflow |= encountered_overflow; for (head_index, head) in heads { @@ -739,8 +756,9 @@ impl, X: Cx> SearchGraph { input, step_kind_from_parent, available_depth, + min_reached_available_depth: available_depth, provisional_result: None, - required_depth: 0, + increase_depth_for_nested: IncreaseDepthForNested::Yes, heads: Default::default(), encountered_overflow: false, usages: None, @@ -761,6 +779,7 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, step_kind_from_parent: PathKind, + increase_depth_for_nested: IncreaseDepthForNested, inspect: &mut D::ProofTreeBuilder, ) -> X::Result { let Some(available_depth) = @@ -816,7 +835,8 @@ impl, X: Cx> SearchGraph { step_kind_from_parent, available_depth, provisional_result: None, - required_depth: 0, + min_reached_available_depth: available_depth, + increase_depth_for_nested, heads: Default::default(), encountered_overflow: false, usages: None, @@ -838,7 +858,7 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - evaluation_result.required_depth, + Some(AvailableDepth(available_depth.0 - evaluation_result.required_depth)), evaluation_result.heads.iter(), evaluation_result.encountered_overflow, UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals), @@ -1116,7 +1136,7 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - 0, + None, heads.iter(), encountered_overflow, UpdateParentGoalCtxt::ProvisionalCacheHit, @@ -1239,7 +1259,7 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - required_depth, + Some(AvailableDepth(available_depth.0 - required_depth)), heads, encountered_overflow, UpdateParentGoalCtxt::Ordinary(nested_goals), @@ -1271,7 +1291,7 @@ impl, X: Cx> SearchGraph { Self::update_parent_goal( &mut self.stack, step_kind_from_parent, - 0, + None, iter::once((head_index, head)), false, UpdateParentGoalCtxt::CycleOnStack(input), @@ -1407,7 +1427,8 @@ impl, X: Cx> SearchGraph { provisional_result: Some(result), // We can keep these goals from previous iterations as they are only // ever read after finalizing this evaluation. - required_depth: stack_entry.required_depth, + min_reached_available_depth: stack_entry.min_reached_available_depth, + increase_depth_for_nested: stack_entry.increase_depth_for_nested, heads: stack_entry.heads, nested_goals: stack_entry.nested_goals, // We reset these two fields when rerunning this goal. We could diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index 8348666be412d..776ab28f9fcdc 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -4,7 +4,8 @@ use derive_where::derive_where; use rustc_index::IndexVec; use crate::search_graph::{ - AvailableDepth, CandidateHeadUsages, Cx, CycleHeads, HeadUsages, NestedGoals, PathKind, + AvailableDepth, CandidateHeadUsages, Cx, CycleHeads, HeadUsages, IncreaseDepthForNested, + NestedGoals, PathKind, }; rustc_index::newtype_index! { @@ -28,8 +29,20 @@ pub(super) struct StackEntry { /// The available depth of a given goal, immutable. pub available_depth: AvailableDepth, - /// The maximum depth required while evaluating this goal. - pub required_depth: usize, + /// The minimum available depth encountered while evaluating this goal's nested goals. + /// If there's no nested goal, this is equal to the `available_depth`. + pub min_reached_available_depth: AvailableDepth, + + /// Whether evaluating nested goals of a given goal should increase the depth. + /// + /// Normally, it should be `Yes`, but among rustc's predicate goals, `normalizes-to` + /// goals are exceptions. They act like functions that used for normalizing associated + /// terms while evaluating projection goals and since their expected terms are always fully + /// unconstrained intentionally, they often return ambiguous nested goals to the caller's + /// context. As these nested goals are evaluated again in the caller's context, we don't + /// want to increase depths when they are evaluated as nested goals for `normalizes-to` + /// goals, otherwise we will encounter recursion limit overflows more often. + pub increase_depth_for_nested: IncreaseDepthForNested, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. @@ -57,6 +70,12 @@ pub(super) struct StackEntry { pub nested_goals: NestedGoals, } +impl StackEntry { + pub(super) fn required_depth(&self) -> usize { + self.available_depth.0 - self.min_reached_available_depth.0 + } +} + /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*.