diff options
| author | bors <bors@rust-lang.org> | 2023-07-04 23:51:52 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-07-04 23:51:52 +0000 |
| commit | b7bc6f88ac771e1197ac9e06bf20586eeb5d0bdf (patch) | |
| tree | 119a0ac94ef99e5baaa936c90d1687ade4fe8943 /compiler/rustc_trait_selection/src | |
| parent | f20afcc455bbcc5c0f7679450fb35fd0c9668880 (diff) | |
| parent | 7996908c4f8fbe2b99ab5225faa794fe02b3158b (diff) | |
| download | rust-b7bc6f88ac771e1197ac9e06bf20586eeb5d0bdf.tar.gz rust-b7bc6f88ac771e1197ac9e06bf20586eeb5d0bdf.zip | |
Auto merge of #113330 - matthiaskrgr:rollup-zm3owin, r=matthiaskrgr
Rollup of 5 pull requests Successful merges: - #113192 (`assemble_candidates_after_normalizing_self_ty` docs) - #113251 (Use scoped-tls for SMIR to map between TyCtxt and SMIR datastructures) - #113282 (Update platform-support.md to improve ARM target descriptions) - #113296 (add flag for enabling global cache usage for proof trees and printing proof trees on error) - #113324 (implement `ConstEvaluatable` goals in new solver) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_trait_selection/src')
7 files changed, 187 insertions, 42 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 15e08166a8b..28138054ae5 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -331,11 +331,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates } - /// If the self type of a goal is an alias, computing the relevant candidates is difficult. + /// If the self type of a goal is an alias we first try to normalize the self type + /// and compute the candidates for the normalized self type in case that succeeds. /// - /// To deal with this, we first try to normalize the self type and add the candidates for the normalized - /// self type to the list of candidates in case that succeeds. We also have to consider candidates with the - /// projection as a self type as well + /// These candidates are used in addition to the ones with the alias as a self type. + /// We do this to simplify both builtin candidates and for better performance. + /// + /// We generate the builtin candidates on the fly by looking at the self type, e.g. + /// add `FnPtr` candidates if the self type is a function pointer. Handling builtin + /// candidates while the self type is still an alias seems difficult. This is similar + /// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented). + /// + /// Looking at all impls for some trait goal is prohibitively expensive. We therefore + /// only look at implementations with a matching self type. Because of this function, + /// we can avoid looking at all existing impls if the self type is an alias. #[instrument(level = "debug", skip_all)] fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index dd0ae0b95c0..5dd6b439858 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -19,7 +19,9 @@ use rustc_middle::ty::{ self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; +use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; +use std::io::Write; use std::ops::ControlFlow; use crate::traits::specialization_graph; @@ -113,9 +115,23 @@ impl NestedGoals<'_> { #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] pub enum GenerateProofTree { + Yes(UseGlobalCache), + No, +} + +#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] +pub enum UseGlobalCache { Yes, No, } +impl UseGlobalCache { + pub fn from_bool(use_cache: bool) -> Self { + match use_cache { + true => UseGlobalCache::Yes, + false => UseGlobalCache::No, + } + } +} pub trait InferCtxtEvalExt<'tcx> { /// Evaluates a goal from **outside** of the trait solver. @@ -177,17 +193,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { var_values: CanonicalVarValues::dummy(), nested_goals: NestedGoals::new(), tainted: Ok(()), - 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), + inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), }; let result = f(&mut ecx); let tree = ecx.inspect.finalize(); - if let Some(tree) = &tree { - // module to allow more granular RUSTC_LOG filtering to just proof tree output - super::inspect::dump::print_tree(tree); + if let (Some(tree), DumpSolverProofTree::Always) = + (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree) + { + let mut lock = std::io::stdout().lock(); + let _ = lock.write_fmt(format_args!("{tree:?}")); + let _ = lock.flush(); } assert!( @@ -425,12 +441,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { self.compute_well_formed_goal(Goal { param_env, predicate: arg }) } - ty::PredicateKind::Ambiguous => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - // FIXME: implement this predicate :) - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(_)) => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { + self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) } ty::PredicateKind::ConstEquate(_, _) => { bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active") @@ -440,6 +452,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { param_env, predicate: (lhs, rhs, direction), }), + ty::PredicateKind::Ambiguous => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } } } else { let kind = self.infcx.instantiate_binder_with_placeholders(kind); diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs index 6d7804a8fad..2d6717fdad9 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -3,9 +3,11 @@ use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind}; use rustc_middle::traits::solve::{ CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult, }; -use rustc_middle::ty; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::config::DumpSolverProofTree; -pub mod dump; +use super::eval_ctxt::UseGlobalCache; +use super::GenerateProofTree; #[derive(Eq, PartialEq, Debug, Hash, HashStable)] pub struct WipGoalEvaluation<'tcx> { @@ -144,20 +146,42 @@ impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> { } pub struct ProofTreeBuilder<'tcx> { - state: Option<Box<DebugSolver<'tcx>>>, + state: Option<Box<BuilderData<'tcx>>>, +} + +struct BuilderData<'tcx> { + tree: DebugSolver<'tcx>, + use_global_cache: UseGlobalCache, } impl<'tcx> ProofTreeBuilder<'tcx> { - fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> { - ProofTreeBuilder { state: Some(Box::new(state.into())) } + fn new( + state: impl Into<DebugSolver<'tcx>>, + use_global_cache: UseGlobalCache, + ) -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder { + state: Some(Box::new(BuilderData { tree: state.into(), use_global_cache })), + } + } + + fn nested(&self, state: impl Into<DebugSolver<'tcx>>) -> Self { + match &self.state { + Some(prev_state) => Self { + state: Some(Box::new(BuilderData { + tree: state.into(), + use_global_cache: prev_state.use_global_cache, + })), + }, + None => Self { state: None }, + } } fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> { - self.state.as_mut().map(|boxed| &mut **boxed) + self.state.as_mut().map(|boxed| &mut boxed.tree) } pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> { - match *(self.state?) { + match self.state?.tree { DebugSolver::GoalEvaluation(wip_goal_evaluation) => { Some(wip_goal_evaluation.finalize()) } @@ -165,8 +189,46 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - pub fn new_root() -> ProofTreeBuilder<'tcx> { - ProofTreeBuilder::new(DebugSolver::Root) + pub fn use_global_cache(&self) -> bool { + self.state + .as_ref() + .map(|state| matches!(state.use_global_cache, UseGlobalCache::Yes)) + .unwrap_or(true) + } + + pub fn new_maybe_root( + tcx: TyCtxt<'tcx>, + generate_proof_tree: GenerateProofTree, + ) -> ProofTreeBuilder<'tcx> { + let generate_proof_tree = match ( + tcx.sess.opts.unstable_opts.dump_solver_proof_tree, + tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache, + generate_proof_tree, + ) { + (_, Some(use_cache), GenerateProofTree::Yes(_)) => { + GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache)) + } + + (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => { + let use_cache = use_cache.unwrap_or(true); + GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache)) + } + + (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree, + (DumpSolverProofTree::Never, _, _) => generate_proof_tree, + (DumpSolverProofTree::OnError, _, _) => generate_proof_tree, + }; + + match generate_proof_tree { + GenerateProofTree::No => ProofTreeBuilder::new_noop(), + GenerateProofTree::Yes(global_cache_disabled) => { + ProofTreeBuilder::new_root(global_cache_disabled) + } + } + } + + pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder::new(DebugSolver::Root, use_global_cache) } pub fn new_noop() -> ProofTreeBuilder<'tcx> { @@ -186,7 +248,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { return ProofTreeBuilder { state: None }; } - ProofTreeBuilder::new(WipGoalEvaluation { + self.nested(WipGoalEvaluation { uncanonicalized_goal: goal, canonicalized_goal: None, evaluation_steps: vec![], @@ -232,7 +294,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match (this, *goal_evaluation.state.unwrap()) { + match (this, goal_evaluation.state.unwrap().tree) { ( DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation { evaluations, .. @@ -253,7 +315,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { return ProofTreeBuilder { state: None }; } - ProofTreeBuilder::new(WipGoalEvaluationStep { + self.nested(WipGoalEvaluationStep { instantiated_goal, nested_goal_evaluations: vec![], candidates: vec![], @@ -262,7 +324,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match (this, *goal_eval_step.state.unwrap()) { + match (this, goal_eval_step.state.unwrap().tree) { (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => { goal_eval.evaluation_steps.push(step); } @@ -276,7 +338,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { return ProofTreeBuilder { state: None }; } - ProofTreeBuilder::new(WipGoalCandidate { + self.nested(WipGoalCandidate { nested_goal_evaluations: vec![], candidates: vec![], kind: None, @@ -296,7 +358,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match (this, *candidate.state.unwrap()) { + match (this, candidate.state.unwrap().tree) { ( DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. }) | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }), @@ -312,7 +374,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { return ProofTreeBuilder { state: None }; } - ProofTreeBuilder::new(WipAddedGoalsEvaluation { evaluations: vec![], result: None }) + self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } pub fn evaluate_added_goals_loop_start(&mut self) { @@ -339,7 +401,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { - match (this, *goals_evaluation.state.unwrap()) { + match (this, goals_evaluation.state.unwrap().tree) { ( DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { nested_goal_evaluations, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/dump.rs b/compiler/rustc_trait_selection/src/solve/inspect/dump.rs deleted file mode 100644 index b755ee86215..00000000000 --- a/compiler/rustc_trait_selection/src/solve/inspect/dump.rs +++ /dev/null @@ -1,5 +0,0 @@ -use rustc_middle::traits::solve::inspect::GoalEvaluation; - -pub fn print_tree(tree: &GoalEvaluation<'_>) { - debug!(?tree); -} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index f3f78cdf09d..77809d8d2ba 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -33,7 +33,9 @@ mod search_graph; mod trait_goals; mod weak_types; -pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt, InferCtxtSelectExt}; +pub use eval_ctxt::{ + EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache, +}; pub use fulfill::FulfillmentCtxt; pub(crate) use normalize::deeply_normalize; @@ -159,6 +161,43 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] + fn compute_const_evaluatable_goal( + &mut self, + Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>, + ) -> QueryResult<'tcx> { + match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + // We never return `NoSolution` here as `try_const_eval_resolve` emits an + // error itself when failing to evaluate, so emitting an additional fulfillment + // error in that case is unnecessary noise. This may change in the future once + // evaluation failures are allowed to impact selection, e.g. generic const + // expressions in impl headers or `where`-clauses. + + // FIXME(generic_const_exprs): Implement handling for generic + // const expressions here. + if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } + ty::ConstKind::Infer(_) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // We can freely ICE here as: + // - `Param` gets replaced with a placeholder during canonicalization + // - `Bound` cannot exist as we don't have a binder around the self Type + // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet + ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => { + bug!("unexpect const kind: {:?}", ct) + } + } + } + #[instrument(level = "debug", skip(self), ret)] fn compute_const_arg_has_type_goal( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index d167ee46b39..f00456e26df 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -213,7 +213,7 @@ impl<'tcx> SearchGraph<'tcx> { inspect: &mut ProofTreeBuilder<'tcx>, mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { - if self.should_use_global_cache() { + if self.should_use_global_cache() && inspect.use_global_cache() { if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) { debug!(?canonical_input, ?result, "cache hit"); inspect.cache_hit(CacheHit::Global); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index fd4c5adbde1..d5c13a9ccd1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -10,6 +10,7 @@ use super::{ use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{self, InferCtxt}; +use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::QueryNormalizeExt as _; use crate::traits::specialize::to_pretty_impl_header; @@ -28,6 +29,7 @@ use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; +use rustc_middle::traits::solve::Goal; use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -37,13 +39,14 @@ use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, }; -use rustc_session::config::TraitSolver; +use rustc_session::config::{DumpSolverProofTree, TraitSolver}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt; +use std::io::Write; use std::iter; use std::ops::ControlFlow; use suggestions::TypeErrCtxtExt as _; @@ -630,6 +633,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { error: &SelectionError<'tcx>, ) { let tcx = self.tcx; + + if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + dump_proof_tree(root_obligation, self.infcx); + } + let mut span = obligation.cause.span; // FIXME: statically guarantee this by tainting after the diagnostic is emitted self.set_tainted_by_errors( @@ -1522,6 +1530,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { + if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + dump_proof_tree(&error.root_obligation, self.infcx); + } + match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { self.report_selection_error( @@ -3491,3 +3503,16 @@ pub enum DefIdOrName { DefId(DefId), Name(&'static str), } + +pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) { + infcx.probe(|_| { + let goal = Goal { predicate: o.predicate, param_env: o.param_env }; + let tree = infcx + .evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No)) + .1 + .expect("proof tree should have been generated"); + let mut lock = std::io::stdout().lock(); + let _ = lock.write_fmt(format_args!("{tree:?}")); + let _ = lock.flush(); + }); +} |
