diff options
| author | Michael Goulet <michael@errs.io> | 2023-06-29 19:06:27 +0000 |
|---|---|---|
| committer | Michael Goulet <michael@errs.io> | 2023-07-03 15:58:33 +0000 |
| commit | f3f879326895658ca8b07726b8ba3eddab5b7c81 (patch) | |
| tree | 5f93767aeb7215e324a886575f4067ed2f7023e4 | |
| parent | 298c0d1a6201630d9a9e741d3c9e41e96acafb6d (diff) | |
| download | rust-f3f879326895658ca8b07726b8ba3eddab5b7c81.tar.gz rust-f3f879326895658ca8b07726b8ba3eddab5b7c81.zip | |
Helpers for creating EvalCtxts, some comments
| -rw-r--r-- | compiler/rustc_trait_selection/src/solve/eval_ctxt.rs | 150 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs | 177 | ||||
| -rw-r--r-- | tests/ui/for/issue-20605.next.stderr | 4 |
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 |
