about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_session/src/config.rs8
-rw-r--r--compiler/rustc_session/src/options.rs20
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs60
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs92
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/dump.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs29
8 files changed, 175 insertions, 48 deletions
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 2fe7a6f511b..996e106cf85 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -745,6 +745,14 @@ pub enum TraitSolver {
     NextCoherence,
 }
 
+#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum SolverProofTreeCondition {
+    #[default]
+    Never,
+    Always,
+    OnError,
+}
+
 pub enum Input {
     /// Load source code from a file.
     File(PathBuf),
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 16a4c2a8b3d..e04e1d75287 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -418,6 +418,7 @@ mod desc {
         "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
     pub const parse_proc_macro_execution_strategy: &str =
         "one of supported execution strategies (`same-thread`, or `cross-thread`)";
+    pub const parse_solver_proof_tree_condition: &str = "one of: `always`, `never`, `on_error`";
 }
 
 mod parse {
@@ -1238,6 +1239,19 @@ mod parse {
         };
         true
     }
+
+    pub(crate) fn parse_solver_proof_tree_condition(
+        slot: &mut SolverProofTreeCondition,
+        v: Option<&str>,
+    ) -> bool {
+        match v {
+            None | Some("always") => *slot = SolverProofTreeCondition::Always,
+            Some("never") => *slot = SolverProofTreeCondition::Never,
+            Some("on-error") => *slot = SolverProofTreeCondition::OnError,
+            _ => return false,
+        };
+        true
+    }
 }
 
 options! {
@@ -1463,8 +1477,10 @@ options! {
         "output statistics about monomorphization collection"),
     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`)"),
-    dump_solver_proof_tree: bool = (false, parse_bool, [UNTRACKED],
-        "dump a proof tree for every goal evaluated by the new trait solver"),
+    dump_solver_proof_tree: SolverProofTreeCondition = (SolverProofTreeCondition::Never, parse_solver_proof_tree_condition, [UNTRACKED],
+        "dump a proof tree for every goal evaluated by the new trait solver. The default is `always`"),
+    dump_solver_proof_tree_uses_cache: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+        "determines whether proof tree generation uses the global cache"),
     dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
         "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
     dylib_lto: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 3b0c5849099..9fe860fe114 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::SolverProofTreeCondition;
 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(DisableGlobalCache),
+    No,
+}
+
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum DisableGlobalCache {
     Yes,
     No,
 }
+impl DisableGlobalCache {
+    pub fn from_bool(disable_cache: bool) -> Self {
+        match disable_cache {
+            true => DisableGlobalCache::Yes,
+            false => DisableGlobalCache::No,
+        }
+    }
+}
 
 pub trait InferCtxtEvalExt<'tcx> {
     /// Evaluates a goal from **outside** of the trait solver.
@@ -164,6 +180,36 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
         let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
 
+        let inspect = {
+            let generate_proof_tree = match (
+                infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
+                infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree_uses_cache,
+                generate_proof_tree,
+            ) {
+                (_, Some(use_cache), GenerateProofTree::Yes(_)) => {
+                    GenerateProofTree::Yes(DisableGlobalCache::from_bool(!use_cache))
+                }
+
+                (SolverProofTreeCondition::Always, use_cache, GenerateProofTree::No) => {
+                    let use_cache = use_cache.unwrap_or(true);
+                    GenerateProofTree::Yes(DisableGlobalCache::from_bool(!use_cache))
+                }
+
+                (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
+                // `Never` is kind of weird- it doesn't actually force us to not generate proof trees
+                // its just the default setting for rustflags forced proof tree generation.
+                (SolverProofTreeCondition::Never, _, _) => generate_proof_tree,
+                (SolverProofTreeCondition::OnError, _, _) => generate_proof_tree,
+            };
+
+            match generate_proof_tree {
+                GenerateProofTree::No => ProofTreeBuilder::new_noop(),
+                GenerateProofTree::Yes(global_cache_disabled) => {
+                    ProofTreeBuilder::new_root(global_cache_disabled)
+                }
+            }
+        };
+
         let mut ecx = EvalCtxt {
             search_graph: &mut search_graph,
             infcx: infcx,
@@ -177,17 +223,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,
         };
         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), SolverProofTreeCondition::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!(
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index 6d7804a8fad..1872c0c92d8 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -5,7 +5,7 @@ use rustc_middle::traits::solve::{
 };
 use rustc_middle::ty;
 
-pub mod dump;
+use super::eval_ctxt::DisableGlobalCache;
 
 #[derive(Eq, PartialEq, Debug, Hash, HashStable)]
 pub struct WipGoalEvaluation<'tcx> {
@@ -145,11 +145,15 @@ impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
 
 pub struct ProofTreeBuilder<'tcx> {
     state: Option<Box<DebugSolver<'tcx>>>,
+    disable_global_cache: DisableGlobalCache,
 }
 
 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>>,
+        disable_global_cache: DisableGlobalCache,
+    ) -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder { state: Some(Box::new(state.into())), disable_global_cache }
     }
 
     fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
@@ -165,12 +169,16 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn new_root() -> ProofTreeBuilder<'tcx> {
-        ProofTreeBuilder::new(DebugSolver::Root)
+    pub fn disable_global_cache(&self) -> DisableGlobalCache {
+        self.disable_global_cache
+    }
+
+    pub fn new_root(disable_global_cache: DisableGlobalCache) -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder::new(DebugSolver::Root, disable_global_cache)
     }
 
     pub fn new_noop() -> ProofTreeBuilder<'tcx> {
-        ProofTreeBuilder { state: None }
+        ProofTreeBuilder { state: None, disable_global_cache: DisableGlobalCache::No }
     }
 
     pub fn is_noop(&self) -> bool {
@@ -183,18 +191,24 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         is_normalizes_to_hack: IsNormalizesToHack,
     ) -> ProofTreeBuilder<'tcx> {
         if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
+            return ProofTreeBuilder {
+                state: None,
+                disable_global_cache: self.disable_global_cache,
+            };
         }
 
-        ProofTreeBuilder::new(WipGoalEvaluation {
-            uncanonicalized_goal: goal,
-            canonicalized_goal: None,
-            evaluation_steps: vec![],
-            is_normalizes_to_hack,
-            cache_hit: None,
-            returned_goals: vec![],
-            result: None,
-        })
+        ProofTreeBuilder::new(
+            WipGoalEvaluation {
+                uncanonicalized_goal: goal,
+                canonicalized_goal: None,
+                evaluation_steps: vec![],
+                is_normalizes_to_hack,
+                cache_hit: None,
+                returned_goals: vec![],
+                result: None,
+            },
+            self.disable_global_cache,
+        )
     }
 
     pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
@@ -250,15 +264,21 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
     ) -> ProofTreeBuilder<'tcx> {
         if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
+            return ProofTreeBuilder {
+                state: None,
+                disable_global_cache: self.disable_global_cache,
+            };
         }
 
-        ProofTreeBuilder::new(WipGoalEvaluationStep {
-            instantiated_goal,
-            nested_goal_evaluations: vec![],
-            candidates: vec![],
-            result: None,
-        })
+        ProofTreeBuilder::new(
+            WipGoalEvaluationStep {
+                instantiated_goal,
+                nested_goal_evaluations: vec![],
+                candidates: vec![],
+                result: None,
+            },
+            self.disable_global_cache,
+        )
     }
     pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
@@ -273,14 +293,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
 
     pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
         if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
+            return ProofTreeBuilder {
+                state: None,
+
+                disable_global_cache: self.disable_global_cache,
+            };
         }
 
-        ProofTreeBuilder::new(WipGoalCandidate {
-            nested_goal_evaluations: vec![],
-            candidates: vec![],
-            kind: None,
-        })
+        ProofTreeBuilder::new(
+            WipGoalCandidate { nested_goal_evaluations: vec![], candidates: vec![], kind: None },
+            self.disable_global_cache,
+        )
     }
 
     pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) {
@@ -309,10 +332,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
 
     pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
         if self.state.is_none() {
-            return ProofTreeBuilder { state: None };
+            return ProofTreeBuilder {
+                state: None,
+
+                disable_global_cache: self.disable_global_cache,
+            };
         }
 
-        ProofTreeBuilder::new(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
+        ProofTreeBuilder::new(
+            WipAddedGoalsEvaluation { evaluations: vec![], result: None },
+            self.disable_global_cache,
+        )
     }
 
     pub fn evaluate_added_goals_loop_start(&mut self) {
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..c0f71fe70a6 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::{
+    DisableGlobalCache, EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt,
+};
 pub use fulfill::FulfillmentCtxt;
 pub(crate) use normalize::deeply_normalize;
 
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..da41ed01acd 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -13,6 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe
 use rustc_middle::ty::TyCtxt;
 use std::{collections::hash_map::Entry, mem};
 
+use super::eval_ctxt::DisableGlobalCache;
 use super::inspect::ProofTreeBuilder;
 use super::SolverMode;
 
@@ -213,7 +214,9 @@ 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.disable_global_cache() == DisableGlobalCache::No
+        {
             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 f7670d51bdc..0b91a5fc70a 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::{DisableGlobalCache, GenerateProofTree, InferCtxtEvalExt};
 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::{SolverProofTreeCondition, 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 == SolverProofTreeCondition::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(
@@ -1529,6 +1537,12 @@ 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
+            == SolverProofTreeCondition::OnError
+        {
+            dump_proof_tree(&error.root_obligation, self.infcx);
+        }
+
         match error.code {
             FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
                 self.report_selection_error(
@@ -3499,3 +3513,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(DisableGlobalCache::Yes))
+            .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();
+    });
+}