diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 24055d6cd832..c65c5851e9b4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -48,12 +48,20 @@ enum GoalEvaluationKind { Nested, } +// FIXME(trait-system-refactor-initiative#117): we don't detect whether a response +// ended up pulling down any universes. fn has_no_inference_or_external_constraints( response: ty::Canonical>, ) -> bool { - response.value.external_constraints.region_constraints.is_empty() - && response.value.var_values.is_identity() - && response.value.external_constraints.opaque_types.is_empty() + let ExternalConstraintsData { + ref region_constraints, + ref opaque_types, + ref normalization_nested_goals, + } = *response.value.external_constraints; + response.value.var_values.is_identity() + && region_constraints.is_empty() + && opaque_types.is_empty() + && normalization_nested_goals.is_empty() } impl<'a, D, I> EvalCtxt<'a, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 2cd3b10f56ad..69d52dcad7a5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -71,7 +71,7 @@ struct StackEntry { /// C :- D /// D :- C /// ``` - cycle_participants: HashSet>, + nested_goals: HashSet>, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. provisional_result: Option>, @@ -139,18 +139,11 @@ impl SearchGraph { self.mode } - /// Pops the highest goal from the stack, lazily updating the - /// the next goal in the stack. - /// - /// Directly popping from the stack instead of using this method - /// would cause us to not track overflow and recursion depth correctly. - fn pop_stack(&mut self) -> StackEntry { - let elem = self.stack.pop().unwrap(); - if let Some(last) = self.stack.raw.last_mut() { - last.reached_depth = last.reached_depth.max(elem.reached_depth); - last.encountered_overflow |= elem.encountered_overflow; + fn update_parent_goal(&mut self, reached_depth: StackDepth, encountered_overflow: bool) { + if let Some(parent) = self.stack.raw.last_mut() { + parent.reached_depth = parent.reached_depth.max(reached_depth); + parent.encountered_overflow |= encountered_overflow; } - elem } pub(super) fn is_empty(&self) -> bool { @@ -222,8 +215,8 @@ impl SearchGraph { let current_cycle_root = &mut stack[current_root.as_usize()]; for entry in cycle_participants { entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); - current_cycle_root.cycle_participants.insert(entry.input); - current_cycle_root.cycle_participants.extend(mem::take(&mut entry.cycle_participants)); + current_cycle_root.nested_goals.insert(entry.input); + current_cycle_root.nested_goals.extend(mem::take(&mut entry.nested_goals)); } } @@ -342,7 +335,7 @@ impl SearchGraph { non_root_cycle_participant: None, encountered_overflow: false, has_been_used: HasBeenUsed::empty(), - cycle_participants: Default::default(), + nested_goals: Default::default(), provisional_result: None, }; assert_eq!(self.stack.push(entry), depth); @@ -364,7 +357,7 @@ impl SearchGraph { } debug!("canonical cycle overflow"); - let current_entry = self.pop_stack(); + let current_entry = self.stack.pop().unwrap(); debug_assert!(current_entry.has_been_used.is_empty()); let result = Self::response_no_constraints(cx, input, Certainty::overflow(false)); (current_entry, result) @@ -372,6 +365,8 @@ impl SearchGraph { let proof_tree = inspect.finalize_canonical_goal_evaluation(cx); + self.update_parent_goal(final_entry.reached_depth, final_entry.encountered_overflow); + // We're now done with this goal. In case this goal is involved in a larger cycle // do not remove it from the provisional cache and update its provisional result. // We only add the root of cycles to the global cache. @@ -394,7 +389,7 @@ impl SearchGraph { // // We must not use the global cache entry of a root goal if a cycle // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `StackEntry::cycle_participants` for + // results. See the comment of `StackEntry::nested_goals` for // more details. self.global_cache(cx).insert( cx, @@ -402,7 +397,7 @@ impl SearchGraph { proof_tree, reached_depth, final_entry.encountered_overflow, - final_entry.cycle_participants, + final_entry.nested_goals, dep_node, result, ) @@ -441,14 +436,9 @@ impl SearchGraph { } } - // Update the reached depth of the current goal to make sure - // its state is the same regardless of whether we've used the - // global cache or not. + // Adjust the parent goal as if we actually computed this goal. let reached_depth = self.stack.next_index().plus(additional_depth); - if let Some(last) = self.stack.raw.last_mut() { - last.reached_depth = last.reached_depth.max(reached_depth); - last.encountered_overflow |= encountered_overflow; - } + self.update_parent_goal(reached_depth, encountered_overflow); Some(result) } @@ -477,7 +467,7 @@ impl SearchGraph { F: FnMut(&mut Self, &mut ProofTreeBuilder) -> QueryResult, { let result = prove_goal(self, inspect); - let stack_entry = self.pop_stack(); + let stack_entry = self.stack.pop().unwrap(); debug_assert_eq!(stack_entry.input, input); // If the current goal is not the root of a cycle, we are done. @@ -554,27 +544,27 @@ impl SearchGraph { non_root_cycle_participant, encountered_overflow: _, has_been_used, - ref cycle_participants, + ref nested_goals, provisional_result, } = *entry; let cache_entry = provisional_cache.get(&entry.input).unwrap(); assert_eq!(cache_entry.stack_depth, Some(depth)); if let Some(head) = non_root_cycle_participant { assert!(head < depth); - assert!(cycle_participants.is_empty()); + assert!(nested_goals.is_empty()); assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); let mut current_root = head; while let Some(parent) = stack[current_root].non_root_cycle_participant { current_root = parent; } - assert!(stack[current_root].cycle_participants.contains(&input)); + assert!(stack[current_root].nested_goals.contains(&input)); } - if !cycle_participants.is_empty() { + if !nested_goals.is_empty() { assert!(provisional_result.is_some() || !has_been_used.is_empty()); for entry in stack.iter().take(depth.as_usize()) { - assert_eq!(cycle_participants.get(&entry.input), None); + assert_eq!(nested_goals.get(&entry.input), None); } } }