about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-06-29 19:06:27 +0000
committerMichael Goulet <michael@errs.io>2023-07-03 15:58:33 +0000
commitf3f879326895658ca8b07726b8ba3eddab5b7c81 (patch)
tree5f93767aeb7215e324a886575f4067ed2f7023e4
parent298c0d1a6201630d9a9e741d3c9e41e96acafb6d (diff)
downloadrust-f3f879326895658ca8b07726b8ba3eddab5b7c81.tar.gz
rust-f3f879326895658ca8b07726b8ba3eddab5b7c81.zip
Helpers for creating EvalCtxts, some comments
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs150
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs177
-rw-r--r--tests/ui/for/issue-20605.next.stderr4
3 files changed, 174 insertions, 157 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 0db2ecb517a..3b0c5849099 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -142,15 +142,34 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
         Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
         Option<inspect::GoalEvaluation<'tcx>>,
     ) {
-        let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
-        let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
+        EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+            ecx.evaluate_goal(IsNormalizesToHack::No, goal)
+        })
+    }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+    pub(super) fn solver_mode(&self) -> SolverMode {
+        self.search_graph.solver_mode()
+    }
+
+    /// Creates a root evaluation context and search graph. This should only be
+    /// used from outside of any evaluation, and other methods should be preferred
+    /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
+    fn enter_root<R>(
+        infcx: &InferCtxt<'tcx>,
+        generate_proof_tree: GenerateProofTree,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
+    ) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
+        let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
+        let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
 
         let mut ecx = EvalCtxt {
             search_graph: &mut search_graph,
-            infcx: self,
+            infcx: infcx,
             // Only relevant when canonicalizing the response,
             // which we don't do within this evaluation context.
-            predefined_opaques_in_body: self
+            predefined_opaques_in_body: infcx
                 .tcx
                 .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
             // Only relevant when canonicalizing the response.
@@ -158,12 +177,12 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
             var_values: CanonicalVarValues::dummy(),
             nested_goals: NestedGoals::new(),
             tainted: Ok(()),
-            inspect: (self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
+            inspect: (infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
                 || matches!(generate_proof_tree, GenerateProofTree::Yes))
             .then(ProofTreeBuilder::new_root)
             .unwrap_or_else(ProofTreeBuilder::new_noop),
         };
-        let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
+        let result = f(&mut ecx);
 
         let tree = ecx.inspect.finalize();
         if let Some(tree) = &tree {
@@ -179,11 +198,66 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
         assert!(search_graph.is_empty());
         (result, tree)
     }
-}
 
-impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
-    pub(super) fn solver_mode(&self) -> SolverMode {
-        self.search_graph.solver_mode()
+    /// Creates a nested evaluation context that shares the same search graph as the
+    /// one passed in. This is suitable for evaluation, granted that the search graph
+    /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
+    /// but it's preferable to use other methods that call this one rather than this
+    /// method directly.
+    ///
+    /// This function takes care of setting up the inference context, setting the anchor,
+    /// and registering opaques from the canonicalized input.
+    fn enter_canonical<R>(
+        tcx: TyCtxt<'tcx>,
+        search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+        canonical_input: CanonicalInput<'tcx>,
+        goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+        f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+    ) -> R {
+        let intercrate = match search_graph.solver_mode() {
+            SolverMode::Normal => false,
+            SolverMode::Coherence => true,
+        };
+        let (ref infcx, input, var_values) = tcx
+            .infer_ctxt()
+            .intercrate(intercrate)
+            .with_next_trait_solver(true)
+            .with_opaque_type_inference(canonical_input.value.anchor)
+            .build_with_canonical(DUMMY_SP, &canonical_input);
+
+        let mut ecx = EvalCtxt {
+            infcx,
+            var_values,
+            predefined_opaques_in_body: input.predefined_opaques_in_body,
+            max_input_universe: canonical_input.max_universe,
+            search_graph,
+            nested_goals: NestedGoals::new(),
+            tainted: Ok(()),
+            inspect: goal_evaluation.new_goal_evaluation_step(input),
+        };
+
+        for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
+            ecx.insert_hidden_type(key, input.goal.param_env, ty)
+                .expect("failed to prepopulate opaque types");
+        }
+
+        if !ecx.nested_goals.is_empty() {
+            panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
+        }
+
+        let result = f(&mut ecx, input.goal);
+
+        goal_evaluation.goal_evaluation_step(ecx.inspect);
+
+        // When creating a query response we clone the opaque type constraints
+        // instead of taking them. This would cause an ICE here, since we have
+        // assertions against dropping an `InferCtxt` without taking opaques.
+        // FIXME: Once we remove support for the old impl we can remove this.
+        if input.anchor != DefiningAnchor::Error {
+            let _ = infcx.take_opaque_types();
+        }
+
+        result
     }
 
     /// The entry point of the solver.
@@ -212,53 +286,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             canonical_input,
             goal_evaluation,
             |search_graph, goal_evaluation| {
-                let intercrate = match search_graph.solver_mode() {
-                    SolverMode::Normal => false,
-                    SolverMode::Coherence => true,
-                };
-                let (ref infcx, input, var_values) = tcx
-                    .infer_ctxt()
-                    .intercrate(intercrate)
-                    .with_next_trait_solver(true)
-                    .with_opaque_type_inference(canonical_input.value.anchor)
-                    .build_with_canonical(DUMMY_SP, &canonical_input);
-
-                let mut ecx = EvalCtxt {
-                    infcx,
-                    var_values,
-                    predefined_opaques_in_body: input.predefined_opaques_in_body,
-                    max_input_universe: canonical_input.max_universe,
+                EvalCtxt::enter_canonical(
+                    tcx,
                     search_graph,
-                    nested_goals: NestedGoals::new(),
-                    tainted: Ok(()),
-                    inspect: goal_evaluation.new_goal_evaluation_step(input),
-                };
-
-                for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-                    ecx.insert_hidden_type(key, input.goal.param_env, ty)
-                        .expect("failed to prepopulate opaque types");
-                }
-
-                if !ecx.nested_goals.is_empty() {
-                    panic!(
-                        "prepopulating opaque types shouldn't add goals: {:?}",
-                        ecx.nested_goals
-                    );
-                }
-
-                let result = ecx.compute_goal(input.goal);
-                ecx.inspect.query_result(result);
-                goal_evaluation.goal_evaluation_step(ecx.inspect);
-
-                // When creating a query response we clone the opaque type constraints
-                // instead of taking them. This would cause an ICE here, since we have
-                // assertions against dropping an `InferCtxt` without taking opaques.
-                // FIXME: Once we remove support for the old impl we can remove this.
-                if input.anchor != DefiningAnchor::Error {
-                    let _ = infcx.take_opaque_types();
-                }
-
-                result
+                    canonical_input,
+                    goal_evaluation,
+                    |ecx, goal| {
+                        let result = ecx.compute_goal(goal);
+                        ecx.inspect.query_result(result);
+                        result
+                    },
+                )
             },
         )
     }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index 7680df13802..466a03e2163 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -1,25 +1,23 @@
 use std::ops::ControlFlow;
 
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt};
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
 use rustc_infer::traits::util::supertraits;
 use rustc_infer::traits::{
     Obligation, PredicateObligation, Selection, SelectionResult, TraitObligation,
 };
-use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
-use rustc_middle::traits::solve::{Certainty, Goal, PredefinedOpaquesData, QueryInput};
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
 use rustc_middle::traits::{
-    DefiningAnchor, ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData,
-    ImplSourceUserDefinedData, ObligationCause, SelectionError,
+    ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
+    ObligationCause, SelectionError,
 };
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::DUMMY_SP;
 
 use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
-use crate::solve::eval_ctxt::{EvalCtxt, NestedGoals};
+use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
 use crate::solve::inspect::ProofTreeBuilder;
-use crate::solve::search_graph::SearchGraph;
-use crate::solve::SolverMode;
+use crate::solve::search_graph::OverflowHandler;
 use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
 
 pub trait InferCtxtSelectExt<'tcx> {
@@ -36,59 +34,54 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
     ) -> SelectionResult<'tcx, Selection<'tcx>> {
         assert!(self.next_trait_solver());
 
-        let goal = Goal::new(
+        let trait_goal = Goal::new(
             self.tcx,
             obligation.param_env,
             self.instantiate_binder_with_placeholders(obligation.predicate),
         );
 
-        let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
-        let mut search_graph = SearchGraph::new(self.tcx, mode);
-        let mut ecx = EvalCtxt {
-            search_graph: &mut search_graph,
-            infcx: self,
-            // Only relevant when canonicalizing the response,
-            // which we don't do within this evaluation context.
-            predefined_opaques_in_body: self
-                .tcx
-                .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
-            // Only relevant when canonicalizing the response.
-            max_input_universe: ty::UniverseIndex::ROOT,
-            var_values: CanonicalVarValues::dummy(),
-            nested_goals: NestedGoals::new(),
-            tainted: Ok(()),
-            inspect: ProofTreeBuilder::new_noop(),
-        };
-
-        let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
-        let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
-
-        // pseudo-winnow
-        if candidates.len() == 0 {
-            return Err(SelectionError::Unimplemented);
-        } else if candidates.len() > 1 {
-            let mut i = 0;
-            while i < candidates.len() {
-                let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
-                    candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
-                });
-                if should_drop_i {
-                    candidates.swap_remove(i);
-                } else {
-                    i += 1;
-                    if i > 1 {
-                        return Ok(None);
+        let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
+            let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
+            let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
+            let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
+
+            // pseudo-winnow
+            if candidates.len() == 0 {
+                return Err(SelectionError::Unimplemented);
+            } else if candidates.len() > 1 {
+                let mut i = 0;
+                while i < candidates.len() {
+                    let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+                        candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
+                    });
+                    if should_drop_i {
+                        candidates.swap_remove(i);
+                    } else {
+                        i += 1;
+                        if i > 1 {
+                            return Ok(None);
+                        }
                     }
                 }
             }
-        }
 
-        let candidate = candidates.pop().unwrap();
-        let (certainty, nested_goals) = ecx
-            .instantiate_and_apply_query_response(goal.param_env, orig_values, candidate.result)
-            .map_err(|_| SelectionError::Unimplemented)?;
+            let candidate = candidates.pop().unwrap();
+            let (certainty, nested_goals) = ecx
+                .instantiate_and_apply_query_response(
+                    trait_goal.param_env,
+                    orig_values,
+                    candidate.result,
+                )
+                .map_err(|_| SelectionError::Unimplemented)?;
 
-        let goal = self.resolve_vars_if_possible(goal);
+            Ok(Some((candidate, certainty, nested_goals)))
+        });
+
+        let (candidate, certainty, nested_goals) = match result {
+            Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals),
+            Ok(None) => return Ok(None),
+            Err(e) => return Err(e),
+        };
 
         let nested_obligations: Vec<_> = nested_goals
             .into_iter()
@@ -97,15 +90,18 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
             })
             .collect();
 
-        if let Certainty::Maybe(_) = certainty {
-            return Ok(None);
-        }
-
+        let goal = self.resolve_vars_if_possible(trait_goal);
         match (certainty, candidate.source) {
+            // Rematching the implementation will instantiate the same nested goals that
+            // would have caused the ambiguity, so we can still make progress here regardless.
             (_, CandidateSource::Impl(def_id)) => {
                 rematch_impl(self, goal, def_id, nested_obligations)
             }
 
+            // Rematching the dyn upcast or object goal will instantiate the same nested
+            // goals that would have caused the ambiguity, so we can still make progress here
+            // regardless.
+            // FIXME: This doesn't actually check the object bounds hold here.
             (
                 _,
                 CandidateSource::BuiltinImpl(
@@ -113,16 +109,16 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
                 ),
             ) => rematch_object(self, goal, nested_obligations),
 
+            // Technically some builtin impls have nested obligations, but if
+            // `Certainty::Yes`, then they should've all been verified and don't
+            // need re-checking.
             (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
-                // technically some builtin impls have nested obligations, but if
-                // `Certainty::Yes`, then they should've all been verified by the
-                // evaluation above.
                 Ok(Some(ImplSource::Builtin(nested_obligations)))
             }
 
+            // It's fine not to do anything to rematch these, since there are no
+            // nested obligations.
             (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
-                // It's fine not to do anything to rematch these, since there are no
-                // nested obligations.
                 Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
             }
 
@@ -135,44 +131,31 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     fn compute_canonical_trait_candidates(
         &mut self,
-        canonical_input: Canonical<'tcx, QueryInput<'tcx, ty::TraitPredicate<'tcx>>>,
+        canonical_input: CanonicalInput<'tcx>,
     ) -> Vec<Candidate<'tcx>> {
-        let intercrate = match self.search_graph.solver_mode() {
-            SolverMode::Normal => false,
-            SolverMode::Coherence => true,
-        };
-        let (canonical_infcx, input, var_values) = self
-            .tcx()
-            .infer_ctxt()
-            .intercrate(intercrate)
-            .with_next_trait_solver(true)
-            .with_opaque_type_inference(canonical_input.value.anchor)
-            .build_with_canonical(DUMMY_SP, &canonical_input);
-
-        let mut ecx = EvalCtxt {
-            infcx: &canonical_infcx,
-            var_values,
-            predefined_opaques_in_body: input.predefined_opaques_in_body,
-            max_input_universe: canonical_input.max_universe,
-            search_graph: &mut self.search_graph,
-            nested_goals: NestedGoals::new(),
-            tainted: Ok(()),
-            inspect: ProofTreeBuilder::new_noop(),
-        };
-
-        for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-            ecx.insert_hidden_type(key, input.goal.param_env, ty)
-                .expect("failed to prepopulate opaque types");
-        }
-
-        let candidates = ecx.assemble_and_evaluate_candidates(input.goal);
-
-        // We don't need the canonicalized context anymore
-        if input.anchor != DefiningAnchor::Error {
-            let _ = canonical_infcx.take_opaque_types();
-        }
-
-        candidates
+        // This doesn't record the canonical goal on the stack during the
+        // candidate assembly step, but that's fine. Selection is conceptually
+        // outside of the solver, and if there were any cycles, we'd encounter
+        // the cycle anyways one step later.
+        EvalCtxt::enter_canonical(
+            self.tcx(),
+            self.search_graph(),
+            canonical_input,
+            // FIXME: This is wrong, idk if we even want to track stuff here.
+            &mut ProofTreeBuilder::new_noop(),
+            |ecx, goal| {
+                let trait_goal = Goal {
+                    param_env: goal.param_env,
+                    predicate: goal
+                        .predicate
+                        .to_opt_poly_trait_pred()
+                        .expect("we canonicalized a trait goal")
+                        .no_bound_vars()
+                        .expect("we instantiated all bound vars"),
+                };
+                ecx.assemble_and_evaluate_candidates(trait_goal)
+            },
+        )
     }
 }
 
diff --git a/tests/ui/for/issue-20605.next.stderr b/tests/ui/for/issue-20605.next.stderr
index de60555ae3c..d55efedfcbe 100644
--- a/tests/ui/for/issue-20605.next.stderr
+++ b/tests/ui/for/issue-20605.next.stderr
@@ -13,10 +13,6 @@ LL |     for item in *things { *item = 0 }
    = help: the trait `Sized` is not implemented for `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter`
    = note: all local variables must have a statically known size
    = help: unsized locals are gated as an unstable feature
-help: consider further restricting the associated type
-   |
-LL | fn changer<'a>(mut things: Box<dyn Iterator<Item=&'a mut u8>>) where <dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter: Sized {
-   |                                                                ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 error: the type `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter` is not well-formed
   --> $DIR/issue-20605.rs:5:17