Rollup merge of #113296 - BoxyUwU:proof_trees_on_error, r=lcnr
add flag for enabling global cache usage for proof trees and printing proof trees on error This adds a few new things: - `-Zdump-solver-proof-tree=always/never/on-error` - `always`/`never` were previosuly specifiable by whether the flag exists or not, th new flag is `on_error` which reruns obligations of fulfillment and selection errors with proof tree generation enabled and prints them out - `-Zdump-solver-proof-tree-uses-cache` - allows forcing global cache to be used or unused for all generated proof trees, global cache is enabled by default for `always` so that it accurately represents what happend. This flag currently would affect misc uses of `GenerateProofTree::Yes` which will be added in the future for things like diagnostics logic and rustdoc's auto_trait file. We can fix this when we start using proof tree generation for those use cases if it's desirable. I also changed the output to go straight to stdout instead of going through `debug!` so that `-Zdump-solver-proof-tree` can be adequately used on `nightly` not just a locally built toolchain. The idea for `on-error` is that it should hopefully make it easier to quickly figure out "why doesnt this code compile"- you just pass in `-Zdump-solver-proof-tree=on-error` and you'll only get proof trees you care about. --- r? `@lcnr` `@compiler-errors`
This commit is contained in:
commit
494e67c63c
@ -743,6 +743,14 @@ pub enum TraitSolver {
|
|||||||
NextCoherence,
|
NextCoherence,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub enum DumpSolverProofTree {
|
||||||
|
Always,
|
||||||
|
OnError,
|
||||||
|
#[default]
|
||||||
|
Never,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Input {
|
pub enum Input {
|
||||||
/// Load source code from a file.
|
/// Load source code from a file.
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
|
@ -418,6 +418,7 @@ mod desc {
|
|||||||
"a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
|
"a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
|
||||||
pub const parse_proc_macro_execution_strategy: &str =
|
pub const parse_proc_macro_execution_strategy: &str =
|
||||||
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
|
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
|
||||||
|
pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
|
||||||
}
|
}
|
||||||
|
|
||||||
mod parse {
|
mod parse {
|
||||||
@ -1237,6 +1238,19 @@ mod parse {
|
|||||||
};
|
};
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_dump_solver_proof_tree(
|
||||||
|
slot: &mut DumpSolverProofTree,
|
||||||
|
v: Option<&str>,
|
||||||
|
) -> bool {
|
||||||
|
match v {
|
||||||
|
None | Some("always") => *slot = DumpSolverProofTree::Always,
|
||||||
|
Some("never") => *slot = DumpSolverProofTree::Never,
|
||||||
|
Some("on-error") => *slot = DumpSolverProofTree::OnError,
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options! {
|
options! {
|
||||||
@ -1462,8 +1476,11 @@ options! {
|
|||||||
"output statistics about monomorphization collection"),
|
"output statistics about monomorphization collection"),
|
||||||
dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
|
dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
|
||||||
"the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
|
"the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
|
||||||
dump_solver_proof_tree: bool = (false, parse_bool, [UNTRACKED],
|
dump_solver_proof_tree: DumpSolverProofTree = (DumpSolverProofTree::Never, parse_dump_solver_proof_tree, [UNTRACKED],
|
||||||
"dump a proof tree for every goal evaluated by the new trait solver"),
|
"dump a proof tree for every goal evaluated by the new trait solver. If the flag is specified without any options after it
|
||||||
|
then it defaults to `always`. If the flag is not specified at all it defaults to `on-request`."),
|
||||||
|
dump_solver_proof_tree_use_cache: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
|
||||||
|
"determines whether dumped proof trees use the global cache"),
|
||||||
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
|
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
|
||||||
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
|
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
|
||||||
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
|
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
|
||||||
|
@ -19,7 +19,9 @@ use rustc_middle::ty::{
|
|||||||
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
||||||
TypeVisitableExt, TypeVisitor,
|
TypeVisitableExt, TypeVisitor,
|
||||||
};
|
};
|
||||||
|
use rustc_session::config::DumpSolverProofTree;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
use std::io::Write;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use crate::traits::specialization_graph;
|
use crate::traits::specialization_graph;
|
||||||
@ -113,9 +115,23 @@ impl NestedGoals<'_> {
|
|||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||||
pub enum GenerateProofTree {
|
pub enum GenerateProofTree {
|
||||||
|
Yes(UseGlobalCache),
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||||
|
pub enum UseGlobalCache {
|
||||||
Yes,
|
Yes,
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
impl UseGlobalCache {
|
||||||
|
pub fn from_bool(use_cache: bool) -> Self {
|
||||||
|
match use_cache {
|
||||||
|
true => UseGlobalCache::Yes,
|
||||||
|
false => UseGlobalCache::No,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait InferCtxtEvalExt<'tcx> {
|
pub trait InferCtxtEvalExt<'tcx> {
|
||||||
/// Evaluates a goal from **outside** of the trait solver.
|
/// Evaluates a goal from **outside** of the trait solver.
|
||||||
@ -177,17 +193,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
|||||||
var_values: CanonicalVarValues::dummy(),
|
var_values: CanonicalVarValues::dummy(),
|
||||||
nested_goals: NestedGoals::new(),
|
nested_goals: NestedGoals::new(),
|
||||||
tainted: Ok(()),
|
tainted: Ok(()),
|
||||||
inspect: (infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
|
inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
|
||||||
|| matches!(generate_proof_tree, GenerateProofTree::Yes))
|
|
||||||
.then(ProofTreeBuilder::new_root)
|
|
||||||
.unwrap_or_else(ProofTreeBuilder::new_noop),
|
|
||||||
};
|
};
|
||||||
let result = f(&mut ecx);
|
let result = f(&mut ecx);
|
||||||
|
|
||||||
let tree = ecx.inspect.finalize();
|
let tree = ecx.inspect.finalize();
|
||||||
if let Some(tree) = &tree {
|
if let (Some(tree), DumpSolverProofTree::Always) =
|
||||||
// module to allow more granular RUSTC_LOG filtering to just proof tree output
|
(&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
|
||||||
super::inspect::dump::print_tree(tree);
|
{
|
||||||
|
let mut lock = std::io::stdout().lock();
|
||||||
|
let _ = lock.write_fmt(format_args!("{tree:?}"));
|
||||||
|
let _ = lock.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -3,9 +3,11 @@ use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind};
|
|||||||
use rustc_middle::traits::solve::{
|
use rustc_middle::traits::solve::{
|
||||||
CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
|
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)]
|
#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
|
||||||
pub struct WipGoalEvaluation<'tcx> {
|
pub struct WipGoalEvaluation<'tcx> {
|
||||||
@ -144,20 +146,42 @@ impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProofTreeBuilder<'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> {
|
impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||||
fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> {
|
fn new(
|
||||||
ProofTreeBuilder { state: Some(Box::new(state.into())) }
|
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>> {
|
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>> {
|
pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
|
||||||
match *(self.state?) {
|
match self.state?.tree {
|
||||||
DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
|
DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
|
||||||
Some(wip_goal_evaluation.finalize())
|
Some(wip_goal_evaluation.finalize())
|
||||||
}
|
}
|
||||||
@ -165,8 +189,46 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_root() -> ProofTreeBuilder<'tcx> {
|
pub fn use_global_cache(&self) -> bool {
|
||||||
ProofTreeBuilder::new(DebugSolver::Root)
|
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> {
|
pub fn new_noop() -> ProofTreeBuilder<'tcx> {
|
||||||
@ -186,7 +248,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
return ProofTreeBuilder { state: None };
|
return ProofTreeBuilder { state: None };
|
||||||
}
|
}
|
||||||
|
|
||||||
ProofTreeBuilder::new(WipGoalEvaluation {
|
self.nested(WipGoalEvaluation {
|
||||||
uncanonicalized_goal: goal,
|
uncanonicalized_goal: goal,
|
||||||
canonicalized_goal: None,
|
canonicalized_goal: None,
|
||||||
evaluation_steps: vec![],
|
evaluation_steps: vec![],
|
||||||
@ -232,7 +294,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
}
|
}
|
||||||
pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
|
pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
|
||||||
if let Some(this) = self.as_mut() {
|
if let Some(this) = self.as_mut() {
|
||||||
match (this, *goal_evaluation.state.unwrap()) {
|
match (this, goal_evaluation.state.unwrap().tree) {
|
||||||
(
|
(
|
||||||
DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
|
DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
|
||||||
evaluations, ..
|
evaluations, ..
|
||||||
@ -253,7 +315,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
return ProofTreeBuilder { state: None };
|
return ProofTreeBuilder { state: None };
|
||||||
}
|
}
|
||||||
|
|
||||||
ProofTreeBuilder::new(WipGoalEvaluationStep {
|
self.nested(WipGoalEvaluationStep {
|
||||||
instantiated_goal,
|
instantiated_goal,
|
||||||
nested_goal_evaluations: vec![],
|
nested_goal_evaluations: vec![],
|
||||||
candidates: vec![],
|
candidates: vec![],
|
||||||
@ -262,7 +324,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
}
|
}
|
||||||
pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
|
pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
|
||||||
if let Some(this) = self.as_mut() {
|
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)) => {
|
(DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
|
||||||
goal_eval.evaluation_steps.push(step);
|
goal_eval.evaluation_steps.push(step);
|
||||||
}
|
}
|
||||||
@ -276,7 +338,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
return ProofTreeBuilder { state: None };
|
return ProofTreeBuilder { state: None };
|
||||||
}
|
}
|
||||||
|
|
||||||
ProofTreeBuilder::new(WipGoalCandidate {
|
self.nested(WipGoalCandidate {
|
||||||
nested_goal_evaluations: vec![],
|
nested_goal_evaluations: vec![],
|
||||||
candidates: vec![],
|
candidates: vec![],
|
||||||
kind: None,
|
kind: None,
|
||||||
@ -296,7 +358,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
|
|
||||||
pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
|
pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
|
||||||
if let Some(this) = self.as_mut() {
|
if let Some(this) = self.as_mut() {
|
||||||
match (this, *candidate.state.unwrap()) {
|
match (this, candidate.state.unwrap().tree) {
|
||||||
(
|
(
|
||||||
DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
|
DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
|
||||||
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
|
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
|
||||||
@ -312,7 +374,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
|||||||
return ProofTreeBuilder { state: None };
|
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) {
|
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>) {
|
pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) {
|
||||||
if let Some(this) = self.as_mut() {
|
if let Some(this) = self.as_mut() {
|
||||||
match (this, *goals_evaluation.state.unwrap()) {
|
match (this, goals_evaluation.state.unwrap().tree) {
|
||||||
(
|
(
|
||||||
DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
|
DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
|
||||||
nested_goal_evaluations,
|
nested_goal_evaluations,
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
use rustc_middle::traits::solve::inspect::GoalEvaluation;
|
|
||||||
|
|
||||||
pub fn print_tree(tree: &GoalEvaluation<'_>) {
|
|
||||||
debug!(?tree);
|
|
||||||
}
|
|
@ -33,7 +33,9 @@ mod search_graph;
|
|||||||
mod trait_goals;
|
mod trait_goals;
|
||||||
mod weak_types;
|
mod weak_types;
|
||||||
|
|
||||||
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt, InferCtxtSelectExt};
|
pub use eval_ctxt::{
|
||||||
|
EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
|
||||||
|
};
|
||||||
pub use fulfill::FulfillmentCtxt;
|
pub use fulfill::FulfillmentCtxt;
|
||||||
pub(crate) use normalize::deeply_normalize;
|
pub(crate) use normalize::deeply_normalize;
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
|||||||
inspect: &mut ProofTreeBuilder<'tcx>,
|
inspect: &mut ProofTreeBuilder<'tcx>,
|
||||||
mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
|
mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'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) {
|
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
|
||||||
debug!(?canonical_input, ?result, "cache hit");
|
debug!(?canonical_input, ?result, "cache hit");
|
||||||
inspect.cache_hit(CacheHit::Global);
|
inspect.cache_hit(CacheHit::Global);
|
||||||
|
@ -10,6 +10,7 @@ use super::{
|
|||||||
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
||||||
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use crate::infer::{self, InferCtxt};
|
use crate::infer::{self, InferCtxt};
|
||||||
|
use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
use crate::traits::query::normalize::QueryNormalizeExt as _;
|
use crate::traits::query::normalize::QueryNormalizeExt as _;
|
||||||
use crate::traits::specialize::to_pretty_impl_header;
|
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::error_reporting::TypeErrCtxt;
|
||||||
use rustc_infer::infer::{InferOk, TypeTrace};
|
use rustc_infer::infer::{InferOk, TypeTrace};
|
||||||
use rustc_middle::traits::select::OverflowError;
|
use rustc_middle::traits::select::OverflowError;
|
||||||
|
use rustc_middle::traits::solve::Goal;
|
||||||
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
|
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
|
||||||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||||
@ -37,13 +39,14 @@ use rustc_middle::ty::{
|
|||||||
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
|
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
|
||||||
TypeVisitable, TypeVisitableExt,
|
TypeVisitable, TypeVisitableExt,
|
||||||
};
|
};
|
||||||
use rustc_session::config::TraitSolver;
|
use rustc_session::config::{DumpSolverProofTree, TraitSolver};
|
||||||
use rustc_session::Limit;
|
use rustc_session::Limit;
|
||||||
use rustc_span::def_id::LOCAL_CRATE;
|
use rustc_span::def_id::LOCAL_CRATE;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{ExpnKind, Span, DUMMY_SP};
|
use rustc_span::{ExpnKind, Span, DUMMY_SP};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::io::Write;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use suggestions::TypeErrCtxtExt as _;
|
use suggestions::TypeErrCtxtExt as _;
|
||||||
@ -630,6 +633,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||||||
error: &SelectionError<'tcx>,
|
error: &SelectionError<'tcx>,
|
||||||
) {
|
) {
|
||||||
let tcx = self.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;
|
let mut span = obligation.cause.span;
|
||||||
// FIXME: statically guarantee this by tainting after the diagnostic is emitted
|
// FIXME: statically guarantee this by tainting after the diagnostic is emitted
|
||||||
self.set_tainted_by_errors(
|
self.set_tainted_by_errors(
|
||||||
@ -1522,6 +1530,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||||||
|
|
||||||
#[instrument(skip(self), level = "debug")]
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) {
|
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 {
|
match error.code {
|
||||||
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
|
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
|
||||||
self.report_selection_error(
|
self.report_selection_error(
|
||||||
@ -3492,3 +3504,16 @@ pub enum DefIdOrName {
|
|||||||
DefId(DefId),
|
DefId(DefId),
|
||||||
Name(&'static str),
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user