about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-11-02 14:46:23 +0000
committerbors <bors@rust-lang.org>2023-11-02 14:46:23 +0000
commitc5afe0a61e39bdd912803eae8d1887a513bdd172 (patch)
tree2fcc0f3372d393cbca91c01e0c58ccc61a675633
parentb800c303525f74023e02e01eb2c5c578439ef053 (diff)
parent6268598dfbd224b7f98045d58116cb4beec9a615 (diff)
downloadrust-c5afe0a61e39bdd912803eae8d1887a513bdd172.tar.gz
rust-c5afe0a61e39bdd912803eae8d1887a513bdd172.zip
Auto merge of #117513 - matthiaskrgr:rollup-jvl6y84, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - #117394 (use global cache when computing proof trees)
 - #117495 (Clarify `Unsize` documentation)
 - #117509 (Remove support for alias `-Z symbol-mangling-version`)
 - #117512 (Expand mem::offset_of! docs)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_middle/src/arena.rs1
-rw-r--r--compiler/rustc_middle/src/traits/solve/cache.rs47
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs10
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs9
-rw-r--r--compiler/rustc_session/src/config.rs33
-rw-r--r--compiler/rustc_session/src/options.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs16
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs121
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph.rs38
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs4
-rw-r--r--library/core/src/marker.rs18
-rw-r--r--library/core/src/mem/mod.rs65
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--tests/run-make/crate-hash-rustc-version/Makefile2
-rw-r--r--tests/ui/symbol-mangling-version/bad-value.bad.stderr2
-rw-r--r--tests/ui/symbol-mangling-version/bad-value.blank.stderr2
-rw-r--r--tests/ui/symbol-mangling-version/bad-value.no-value.stderr2
-rw-r--r--tests/ui/symbol-mangling-version/bad-value.rs6
-rw-r--r--tests/ui/symbol-mangling-version/stable.rs5
-rw-r--r--tests/ui/symbol-mangling-version/unstable.legacy.stderr2
-rw-r--r--tests/ui/symbol-mangling-version/unstable.rs6
-rw-r--r--tests/ui/unsized/unsize-coerce-multiple-adt-params.rs29
25 files changed, 281 insertions, 161 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 594283168c9..8e66083a390 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -817,7 +817,6 @@ fn test_unstable_options_tracking_hash() {
     tracked!(split_lto_unit, Some(true));
     tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
     tracked!(stack_protector, StackProtector::All);
-    tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
     tracked!(teach, true);
     tracked!(thinlto, Some(true));
     tracked!(thir_unsafeck, true);
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 1d573a746b9..dd761b4e312 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -69,6 +69,7 @@ macro_rules! arena_types {
             [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>,
             [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
             [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
+            [] canonical_goal_evaluation: rustc_middle::traits::solve::inspect::GoalEvaluationStep<'tcx>,
             [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
             [] type_op_subtype:
                 rustc_middle::infer::canonical::Canonical<'tcx,
diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs
index 9898b0019bb..e9e9cc418a6 100644
--- a/compiler/rustc_middle/src/traits/solve/cache.rs
+++ b/compiler/rustc_middle/src/traits/solve/cache.rs
@@ -1,4 +1,4 @@
-use super::{CanonicalInput, QueryResult};
+use super::{inspect, CanonicalInput, QueryResult};
 use crate::ty::TyCtxt;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lock;
@@ -14,8 +14,10 @@ pub struct EvaluationCache<'tcx> {
     map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
 }
 
+#[derive(PartialEq, Eq)]
 pub struct CacheData<'tcx> {
     pub result: QueryResult<'tcx>,
+    pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
     pub reached_depth: usize,
     pub encountered_overflow: bool,
 }
@@ -24,22 +26,33 @@ impl<'tcx> EvaluationCache<'tcx> {
     /// Insert a final result into the global cache.
     pub fn insert(
         &self,
+        tcx: TyCtxt<'tcx>,
         key: CanonicalInput<'tcx>,
+        proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
         reached_depth: usize,
-        did_overflow: bool,
+        encountered_overflow: bool,
         cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
         dep_node: DepNodeIndex,
         result: QueryResult<'tcx>,
     ) {
         let mut map = self.map.borrow_mut();
         let entry = map.entry(key).or_default();
-        let data = WithDepNode::new(dep_node, result);
+        let data = WithDepNode::new(dep_node, QueryData { result, proof_tree });
         entry.cycle_participants.extend(cycle_participants);
-        if did_overflow {
+        if encountered_overflow {
             entry.with_overflow.insert(reached_depth, data);
         } else {
             entry.success = Some(Success { data, reached_depth });
         }
+
+        if cfg!(debug_assertions) {
+            drop(map);
+            if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow })
+                != self.get(tcx, key, |_| false, Limit(reached_depth))
+            {
+                bug!("unable to retrieve inserted element from cache: {key:?}");
+            }
+        }
     }
 
     /// Try to fetch a cached result, checking the recursion limit
@@ -62,27 +75,39 @@ impl<'tcx> EvaluationCache<'tcx> {
 
         if let Some(ref success) = entry.success {
             if available_depth.value_within_limit(success.reached_depth) {
+                let QueryData { result, proof_tree } = success.data.get(tcx);
                 return Some(CacheData {
-                    result: success.data.get(tcx),
+                    result,
+                    proof_tree,
                     reached_depth: success.reached_depth,
                     encountered_overflow: false,
                 });
             }
         }
 
-        entry.with_overflow.get(&available_depth.0).map(|e| CacheData {
-            result: e.get(tcx),
-            reached_depth: available_depth.0,
-            encountered_overflow: true,
+        entry.with_overflow.get(&available_depth.0).map(|e| {
+            let QueryData { result, proof_tree } = e.get(tcx);
+            CacheData {
+                result,
+                proof_tree,
+                reached_depth: available_depth.0,
+                encountered_overflow: true,
+            }
         })
     }
 }
 
 struct Success<'tcx> {
-    data: WithDepNode<QueryResult<'tcx>>,
+    data: WithDepNode<QueryData<'tcx>>,
     reached_depth: usize,
 }
 
+#[derive(Clone, Copy)]
+pub struct QueryData<'tcx> {
+    pub result: QueryResult<'tcx>,
+    pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
+}
+
 /// The cache entry for a goal `CanonicalInput`.
 ///
 /// This contains results whose computation never hit the
@@ -96,5 +121,5 @@ struct CacheEntry<'tcx> {
     /// See the doc comment of `StackEntry::cycle_participants` for more
     /// details.
     cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
-    with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>,
+    with_overflow: FxHashMap<usize, WithDepNode<QueryData<'tcx>>>,
 }
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index e7e40bee6b9..a5916c4ab85 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -42,12 +42,6 @@ pub struct State<'tcx, T> {
 
 pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
 
-#[derive(Debug, Eq, PartialEq)]
-pub enum CacheHit {
-    Provisional,
-    Global,
-}
-
 /// When evaluating the root goals we also store the
 /// original values for the `CanonicalVarValues` of the
 /// canonicalized goal. We use this to map any [CanonicalState]
@@ -78,8 +72,8 @@ pub struct CanonicalGoalEvaluation<'tcx> {
 #[derive(Eq, PartialEq)]
 pub enum CanonicalGoalEvaluationKind<'tcx> {
     Overflow,
-    CacheHit(CacheHit),
-    Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
+    CycleInStack,
+    Evaluation { revisions: &'tcx [GoalEvaluationStep<'tcx>] },
 }
 impl Debug for GoalEvaluation<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index 5733be00adf..4b73d8e41a1 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -74,13 +74,10 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
             CanonicalGoalEvaluationKind::Overflow => {
                 writeln!(self.f, "OVERFLOW: {:?}", eval.result)
             }
-            CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
-                writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
+            CanonicalGoalEvaluationKind::CycleInStack => {
+                writeln!(self.f, "CYCLE IN STACK: {:?}", eval.result)
             }
-            CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
-                writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
-            }
-            CanonicalGoalEvaluationKind::Uncached { revisions } => {
+            CanonicalGoalEvaluationKind::Evaluation { revisions } => {
                 for (n, step) in revisions.iter().enumerate() {
                     writeln!(self.f, "REVISION {n}")?;
                     self.nested(|this| this.format_evaluation_step(step))?;
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a8ebab4ae33..715a7f9cb63 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2674,28 +2674,19 @@ pub fn build_session_options(
         );
     }
 
-    // Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes
-    // precedence.
-    match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
-        (Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
-            handler.early_error(
-                "incompatible values passed for `-C symbol-mangling-version` \
-                and `-Z symbol-mangling-version`",
-            );
-        }
-        (Some(SymbolManglingVersion::V0), _) => {}
-        (Some(_), _) if !unstable_opts.unstable_options => {
-            handler
-                .early_error("`-C symbol-mangling-version=legacy` requires `-Z unstable-options`");
-        }
-        (None, None) => {}
-        (None, smv) => {
-            handler.early_warn(
-                "`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`",
-            );
-            cg.symbol_mangling_version = smv;
+    // Check for unstable values of `-C symbol-mangling-version`.
+    // This is what prevents them from being used on stable compilers.
+    match cg.symbol_mangling_version {
+        // Stable values:
+        None | Some(SymbolManglingVersion::V0) => {}
+        // Unstable values:
+        Some(SymbolManglingVersion::Legacy) => {
+            if !unstable_opts.unstable_options {
+                handler.early_error(
+                    "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
+                );
+            }
         }
-        _ => {}
     }
 
     // Check for unstable values of `-C instrument-coverage`.
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 30c8b9d6700..7510a41485a 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1529,8 +1529,6 @@ options! {
     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. 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],
         "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
     dylib_lto: bool = (false, parse_bool, [UNTRACKED],
@@ -1823,9 +1821,6 @@ written to standard error output)"),
         "control if mem::uninitialized and mem::zeroed panic on more UB"),
     strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
         "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
-    symbol_mangling_version: Option<SymbolManglingVersion> = (None,
-        parse_symbol_mangling_version, [TRACKED],
-        "which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
     #[rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field")]
     teach: bool = (false, parse_bool, [TRACKED],
         "show extended diagnostic help (default: no)"),
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 066129d8e47..70235b710e2 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -119,25 +119,11 @@ impl NestedGoals<'_> {
 
 #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
 pub enum GenerateProofTree {
-    Yes(UseGlobalCache),
+    Yes,
     IfEnabled,
     Never,
 }
 
-#[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.
     ///
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 15c8d9e5bcb..69bfdd4688c 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -17,7 +17,7 @@ use rustc_middle::traits::solve::{Certainty, Goal};
 use rustc_middle::ty;
 
 use crate::solve::inspect::ProofTreeBuilder;
-use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
 
 pub struct InspectGoal<'a, 'tcx> {
     infcx: &'a InferCtxt<'tcx>,
@@ -82,8 +82,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
                 }
 
                 for &goal in &instantiated_goals {
-                    let (_, proof_tree) =
-                        infcx.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
+                    let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
                     let proof_tree = proof_tree.unwrap();
                     visitor.visit_goal(&InspectGoal::new(
                         infcx,
@@ -169,11 +168,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         let mut candidates = vec![];
         let last_eval_step = match self.evaluation.evaluation.kind {
             inspect::CanonicalGoalEvaluationKind::Overflow
-            | inspect::CanonicalGoalEvaluationKind::CacheHit(_) => {
+            | inspect::CanonicalGoalEvaluationKind::CycleInStack => {
                 warn!("unexpected root evaluation: {:?}", self.evaluation);
                 return vec![];
             }
-            inspect::CanonicalGoalEvaluationKind::Uncached { ref revisions } => {
+            inspect::CanonicalGoalEvaluationKind::Evaluation { ref revisions } => {
                 if let Some(last) = revisions.last() {
                     last
                 } else {
@@ -227,8 +226,7 @@ impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> {
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
         visitor: &mut V,
     ) -> ControlFlow<V::BreakTy> {
-        let (_, proof_tree) =
-            self.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
+        let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
         let proof_tree = proof_tree.unwrap();
         visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
     }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
index 2eba98b02e9..088455b38cb 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -3,6 +3,8 @@
 //! This code is *a bit* of a mess and can hopefully be
 //! mostly ignored. For a general overview of how it works,
 //! see the comment on [ProofTreeBuilder].
+use std::mem;
+
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{
     CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
@@ -10,7 +12,6 @@ use rustc_middle::traits::solve::{
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::DumpSolverProofTree;
 
-use crate::solve::eval_ctxt::UseGlobalCache;
 use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree};
 
 /// The core data structure when building proof trees.
@@ -34,12 +35,7 @@ use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree};
 /// is called to recursively convert the whole structure to a
 /// finished proof tree.
 pub(in crate::solve) struct ProofTreeBuilder<'tcx> {
-    state: Option<Box<BuilderData<'tcx>>>,
-}
-
-struct BuilderData<'tcx> {
-    tree: DebugSolver<'tcx>,
-    use_global_cache: UseGlobalCache,
+    state: Option<Box<DebugSolver<'tcx>>>,
 }
 
 /// The current state of the proof tree builder, at most places
@@ -118,36 +114,46 @@ pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> {
     Nested { is_normalizes_to_hack: IsNormalizesToHack },
 }
 
-#[derive(Eq, PartialEq, Debug)]
-pub(in crate::solve) enum WipCanonicalGoalEvaluationKind {
+#[derive(Eq, PartialEq)]
+pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<'tcx> {
     Overflow,
-    CacheHit(inspect::CacheHit),
+    CycleInStack,
+    Interned { revisions: &'tcx [inspect::GoalEvaluationStep<'tcx>] },
+}
+
+impl std::fmt::Debug for WipCanonicalGoalEvaluationKind<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Overflow => write!(f, "Overflow"),
+            Self::CycleInStack => write!(f, "CycleInStack"),
+            Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(),
+        }
+    }
 }
 
 #[derive(Eq, PartialEq, Debug)]
 struct WipCanonicalGoalEvaluation<'tcx> {
     goal: CanonicalInput<'tcx>,
-    kind: Option<WipCanonicalGoalEvaluationKind>,
+    kind: Option<WipCanonicalGoalEvaluationKind<'tcx>>,
+    /// Only used for uncached goals. After we finished evaluating
+    /// the goal, this is interned and moved into `kind`.
     revisions: Vec<WipGoalEvaluationStep<'tcx>>,
     result: Option<QueryResult<'tcx>>,
 }
 
 impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
     fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
-        let kind = match self.kind {
-            Some(WipCanonicalGoalEvaluationKind::Overflow) => {
+        assert!(self.revisions.is_empty());
+        let kind = match self.kind.unwrap() {
+            WipCanonicalGoalEvaluationKind::Overflow => {
                 inspect::CanonicalGoalEvaluationKind::Overflow
             }
-            Some(WipCanonicalGoalEvaluationKind::CacheHit(hit)) => {
-                inspect::CanonicalGoalEvaluationKind::CacheHit(hit)
+            WipCanonicalGoalEvaluationKind::CycleInStack => {
+                inspect::CanonicalGoalEvaluationKind::CycleInStack
+            }
+            WipCanonicalGoalEvaluationKind::Interned { revisions } => {
+                inspect::CanonicalGoalEvaluationKind::Evaluation { revisions }
             }
-            None => inspect::CanonicalGoalEvaluationKind::Uncached {
-                revisions: self
-                    .revisions
-                    .into_iter()
-                    .map(WipGoalEvaluationStep::finalize)
-                    .collect(),
-            },
         };
 
         inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() }
@@ -226,33 +232,20 @@ impl<'tcx> WipProbeStep<'tcx> {
 }
 
 impl<'tcx> ProofTreeBuilder<'tcx> {
-    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 new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder { state: Some(Box::new(state.into())) }
     }
 
     fn nested<T: Into<DebugSolver<'tcx>>>(&self, state: impl FnOnce() -> T) -> 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 },
-        }
+        ProofTreeBuilder { state: self.state.as_ref().map(|_| Box::new(state().into())) }
     }
 
     fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
-        self.state.as_mut().map(|boxed| &mut boxed.tree)
+        self.state.as_deref_mut()
     }
 
     pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
-        match self.state?.tree {
+        match *self.state? {
             DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
                 Some(wip_goal_evaluation.finalize())
             }
@@ -260,13 +253,6 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    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,
@@ -276,10 +262,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             GenerateProofTree::IfEnabled => {
                 let opts = &tcx.sess.opts.unstable_opts;
                 match opts.dump_solver_proof_tree {
-                    DumpSolverProofTree::Always => {
-                        let use_cache = opts.dump_solver_proof_tree_use_cache.unwrap_or(true);
-                        ProofTreeBuilder::new_root(UseGlobalCache::from_bool(use_cache))
-                    }
+                    DumpSolverProofTree::Always => ProofTreeBuilder::new_root(),
                     // `OnError` is handled by reevaluating goals in error
                     // reporting with `GenerateProofTree::Yes`.
                     DumpSolverProofTree::OnError | DumpSolverProofTree::Never => {
@@ -287,12 +270,12 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
                     }
                 }
             }
-            GenerateProofTree::Yes(use_cache) => ProofTreeBuilder::new_root(use_cache),
+            GenerateProofTree::Yes => ProofTreeBuilder::new_root(),
         }
     }
 
-    pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> {
-        ProofTreeBuilder::new(DebugSolver::Root, use_global_cache)
+    pub fn new_root() -> ProofTreeBuilder<'tcx> {
+        ProofTreeBuilder::new(DebugSolver::Root)
     }
 
     pub fn new_noop() -> ProofTreeBuilder<'tcx> {
@@ -336,9 +319,27 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         })
     }
 
+    pub fn finalize_evaluation(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+    ) -> Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]> {
+        self.as_mut().map(|this| match this {
+            DebugSolver::CanonicalGoalEvaluation(evaluation) => {
+                let revisions = mem::take(&mut evaluation.revisions)
+                    .into_iter()
+                    .map(WipGoalEvaluationStep::finalize);
+                let revisions = &*tcx.arena.alloc_from_iter(revisions);
+                let kind = WipCanonicalGoalEvaluationKind::Interned { revisions };
+                assert_eq!(evaluation.kind.replace(kind), None);
+                revisions
+            }
+            _ => unreachable!(),
+        })
+    }
+
     pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, canonical_goal_evaluation.state.unwrap().tree) {
+            match (this, *canonical_goal_evaluation.state.unwrap()) {
                 (
                     DebugSolver::GoalEvaluation(goal_evaluation),
                     DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation),
@@ -348,7 +349,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) {
+    pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<'tcx>) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
@@ -372,7 +373,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().tree) {
+            match (this, *goal_evaluation.state.unwrap()) {
                 (
                     DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
                         evaluations, ..
@@ -396,7 +397,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     }
     pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, goal_evaluation_step.state.unwrap().tree) {
+            match (this, *goal_evaluation_step.state.unwrap()) {
                 (
                     DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations),
                     DebugSolver::GoalEvaluationStep(goal_evaluation_step),
@@ -444,7 +445,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
 
     pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, probe.state.unwrap().tree) {
+            match (this, *probe.state.unwrap()) {
                 (
                     DebugSolver::Probe(WipProbe { steps, .. })
                     | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
@@ -486,7 +487,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
 
     pub fn added_goals_evaluation(&mut self, added_goals_evaluation: ProofTreeBuilder<'tcx>) {
         if let Some(this) = self.as_mut() {
-            match (this, added_goals_evaluation.state.unwrap().tree) {
+            match (this, *added_goals_evaluation.state.unwrap()) {
                 (
                     DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
                         evaluation: WipProbe { steps, .. },
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index d45fe102805..dba5369fa0f 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -38,9 +38,7 @@ mod project_goals;
 mod search_graph;
 mod trait_goals;
 
-pub use eval_ctxt::{
-    EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
-};
+pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt};
 pub use fulfill::FulfillmentCtxt;
 pub(crate) use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
 
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs
index 33513f6bd43..7ffa1d7d319 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs
@@ -6,7 +6,6 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_index::Idx;
 use rustc_index::IndexVec;
 use rustc_middle::dep_graph::dep_kinds;
-use rustc_middle::traits::solve::inspect::CacheHit;
 use rustc_middle::traits::solve::CacheData;
 use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
 use rustc_middle::ty::TyCtxt;
@@ -191,8 +190,8 @@ impl<'tcx> SearchGraph<'tcx> {
         };
 
         // Try to fetch the goal from the global cache.
-        if inspect.use_global_cache() {
-            if let Some(CacheData { result, reached_depth, encountered_overflow }) =
+        'global: {
+            let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) =
                 self.global_cache(tcx).get(
                     tcx,
                     input,
@@ -201,13 +200,26 @@ impl<'tcx> SearchGraph<'tcx> {
                     },
                     available_depth,
                 )
-            {
-                inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
-                    CacheHit::Global,
-                ));
-                self.on_cache_hit(reached_depth, encountered_overflow);
-                return result;
+            else {
+                break 'global;
+            };
+
+            // If we're building a proof tree and the current cache entry does not
+            // contain a proof tree, we do not use the entry but instead recompute
+            // the goal. We simply overwrite the existing entry once we're done,
+            // caching the proof tree.
+            if !inspect.is_noop() {
+                if let Some(revisions) = proof_tree {
+                    inspect.goal_evaluation_kind(
+                        inspect::WipCanonicalGoalEvaluationKind::Interned { revisions },
+                    );
+                } else {
+                    break 'global;
+                }
             }
+
+            self.on_cache_hit(reached_depth, encountered_overflow);
+            return result;
         }
 
         // Check whether we're in a cycle.
@@ -238,9 +250,7 @@ impl<'tcx> SearchGraph<'tcx> {
             // Finally we can return either the provisional response for that goal if we have a
             // coinductive cycle or an ambiguous result if the cycle is inductive.
             Entry::Occupied(entry) => {
-                inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
-                    CacheHit::Provisional,
-                ));
+                inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack);
 
                 let stack_depth = *entry.get();
                 debug!("encountered cycle with depth {stack_depth:?}");
@@ -329,6 +339,8 @@ impl<'tcx> SearchGraph<'tcx> {
                 (current_entry, result)
             });
 
+        let proof_tree = inspect.finalize_evaluation(tcx);
+
         // We're now done with this goal. In case this goal is involved in a larger cycle
         // do not remove it from the provisional cache and update its provisional result.
         // We only add the root of cycles to the global cache.
@@ -346,7 +358,9 @@ impl<'tcx> SearchGraph<'tcx> {
             // more details.
             let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len();
             self.global_cache(tcx).insert(
+                tcx,
                 input,
+                proof_tree,
                 reached_depth,
                 final_entry.encountered_overflow,
                 final_entry.cycle_participants,
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 79b09db2268..0796cb57d97 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -8,7 +8,7 @@ mod type_err_ctxt_ext;
 
 use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
 use crate::infer::InferCtxt;
-use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
@@ -173,7 +173,7 @@ pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &
     infcx.probe(|_| {
         let goal = Goal { predicate: o.predicate, param_env: o.param_env };
         let tree = infcx
-            .evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No))
+            .evaluate_root_goal(goal, GenerateProofTree::Yes)
             .1
             .expect("proof tree should have been generated");
         let mut lock = std::io::stdout().lock();
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index d396a707b55..13437d8f961 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -155,12 +155,18 @@ pub trait Sized {
 /// Those implementations are:
 ///
 /// - Arrays `[T; N]` implement `Unsize<[T]>`.
-/// - Types implementing a trait `Trait` also implement `Unsize<dyn Trait>`.
-/// - Structs `Foo<..., T, ...>` implement `Unsize<Foo<..., U, ...>>` if all of these conditions
-///   are met:
-///   - `T: Unsize<U>`.
-///   - Only the last field of `Foo` has a type involving `T`.
-///   - `Bar<T>: Unsize<Bar<U>>`, where `Bar<T>` stands for the actual type of that last field.
+/// - A type implements `Unsize<dyn Trait + 'a>` if all of these conditions are met:
+///   - The type implements `Trait`.
+///   - `Trait` is object safe.
+///   - The type is sized.
+///   - The type outlives `'a`.
+/// - Structs `Foo<..., T1, ..., Tn, ...>` implement `Unsize<Foo<..., U1, ..., Un, ...>>`
+/// where any number of (type and const) parameters may be changed if all of these conditions
+/// are met:
+///   - Only the last field of `Foo` has a type involving the parameters `T1`, ..., `Tn`.
+///   - All other parameters of the struct are equal.
+///   - `Field<T1, ..., Tn>: Unsize<Field<U1, ..., Un>>`, where `Field<...>` stands for the actual
+///     type of the struct's last field.
 ///
 /// `Unsize` is used along with [`ops::CoerceUnsized`] to allow
 /// "user-defined" containers such as [`Rc`] to contain dynamically-sized
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 8792134cdc8..c88d71f8e54 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -1294,13 +1294,63 @@ impl<T> SizedTypeProperties for T {}
 ///
 /// Structs, enums, unions and tuples are supported.
 ///
-/// Nested field accesses may be used, but not array indexes like in `C`'s `offsetof`.
+/// Nested field accesses may be used, but not array indexes.
 ///
 /// Enum variants may be traversed as if they were fields. Variants themselves do
 /// not have an offset.
 ///
+/// Visibility is respected - all types and fields must be visible to the call site:
+///
+/// ```
+/// #![feature(offset_of)]
+///
+/// mod nested {
+///     #[repr(C)]
+///     pub struct Struct {
+///         private: u8,
+///     }
+/// }
+///
+/// // assert_eq!(mem::offset_of!(nested::Struct, private), 0);
+/// // ^^^ error[E0616]: field `private` of struct `Struct` is private
+/// ```
+///
 /// Note that type layout is, in general, [subject to change and
-/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html).
+/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If
+/// layout stability is required, consider using an [explicit `repr` attribute].
+///
+/// Rust guarantees that the offset of a given field within a given type will not
+/// change over the lifetime of the program. However, two different compilations of
+/// the same program may result in different layouts. Also, even within a single
+/// program execution, no guarantees are made about types which are *similar* but
+/// not *identical*, e.g.:
+///
+/// ```
+/// #![feature(offset_of)]
+///
+/// struct Wrapper<T, U>(T, U);
+///
+/// type A = Wrapper<u8, u8>;
+/// type B = Wrapper<u8, i8>;
+///
+/// // Not necessarily identical even though `u8` and `i8` have the same layout!
+/// // assert!(mem::offset_of!(A, 1), mem::offset_of!(B, 1));
+///
+/// #[repr(transparent)]
+/// struct U8(u8);
+///
+/// type C = Wrapper<u8, U8>;
+///
+/// // Not necessarily identical even though `u8` and `U8` have the same layout!
+/// // assert!(mem::offset_of!(A, 1), mem::offset_of!(C, 1));
+///
+/// struct Empty<T>(core::marker::PhantomData<T>);
+///
+/// // Not necessarily identical even though `PhantomData` always has the same layout!
+/// // assert!(mem::offset_of!(Empty<u8>, 0), mem::offset_of!(Empty<i8>, 0));
+/// ```
+///
+/// [explicit `repr` attribute]: https://doc.rust-lang.org/reference/type-layout.html#representations
 ///
 /// # Examples
 ///
@@ -1329,6 +1379,17 @@ impl<T> SizedTypeProperties for T {}
 ///
 /// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
 ///
+/// #[repr(u8)]
+/// enum Enum {
+///     A(u8, u16),
+///     B { one: u8, two: u16 },
+/// }
+///
+/// # #[cfg(not(bootstrap))]
+/// assert_eq!(mem::offset_of!(Enum, A.0), 1);
+/// # #[cfg(not(bootstrap))]
+/// assert_eq!(mem::offset_of!(Enum, B.two), 2);
+///
 /// # #[cfg(not(bootstrap))]
 /// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);
 /// ```
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index dcf218bc189..7e24793adee 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
 const ISSUES_ENTRY_LIMIT: usize = 1854;
-const ROOT_ENTRY_LIMIT: usize = 866;
+const ROOT_ENTRY_LIMIT: usize = 867;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/tests/run-make/crate-hash-rustc-version/Makefile b/tests/run-make/crate-hash-rustc-version/Makefile
index f1d2a360410..6bf504bf01b 100644
--- a/tests/run-make/crate-hash-rustc-version/Makefile
+++ b/tests/run-make/crate-hash-rustc-version/Makefile
@@ -4,7 +4,7 @@ include ../tools.mk
 # Ensure that crates compiled with different rustc versions cannot
 # be dynamically linked.
 
-FLAGS := -Cprefer-dynamic -Zsymbol-mangling-version=v0
+FLAGS := -Cprefer-dynamic -Csymbol-mangling-version=v0
 UNAME := $(shell uname)
 ifeq ($(UNAME),Linux)
   EXT=".so"
diff --git a/tests/ui/symbol-mangling-version/bad-value.bad.stderr b/tests/ui/symbol-mangling-version/bad-value.bad.stderr
new file mode 100644
index 00000000000..c36c73c6069
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/bad-value.bad.stderr
@@ -0,0 +1,2 @@
+error: incorrect value `bad-value` for codegen option `symbol-mangling-version` - either `legacy` or `v0` (RFC 2603) was expected
+
diff --git a/tests/ui/symbol-mangling-version/bad-value.blank.stderr b/tests/ui/symbol-mangling-version/bad-value.blank.stderr
new file mode 100644
index 00000000000..0e70af5b8ff
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/bad-value.blank.stderr
@@ -0,0 +1,2 @@
+error: incorrect value `` for codegen option `symbol-mangling-version` - either `legacy` or `v0` (RFC 2603) was expected
+
diff --git a/tests/ui/symbol-mangling-version/bad-value.no-value.stderr b/tests/ui/symbol-mangling-version/bad-value.no-value.stderr
new file mode 100644
index 00000000000..77013b72b6c
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/bad-value.no-value.stderr
@@ -0,0 +1,2 @@
+error: codegen option `symbol-mangling-version` requires either `legacy` or `v0` (RFC 2603) (C symbol-mangling-version=<value>)
+
diff --git a/tests/ui/symbol-mangling-version/bad-value.rs b/tests/ui/symbol-mangling-version/bad-value.rs
new file mode 100644
index 00000000000..7623857d49e
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/bad-value.rs
@@ -0,0 +1,6 @@
+// revisions: no-value blank bad
+// [no-value] compile-flags: -Csymbol-mangling-version
+// [blank] compile-flags: -Csymbol-mangling-version=
+// [bad] compile-flags: -Csymbol-mangling-version=bad-value
+
+fn main() {}
diff --git a/tests/ui/symbol-mangling-version/stable.rs b/tests/ui/symbol-mangling-version/stable.rs
new file mode 100644
index 00000000000..dac9bb18d1c
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/stable.rs
@@ -0,0 +1,5 @@
+// check-pass
+// revisions: v0
+// [v0] compile-flags: -Csymbol-mangling-version=v0
+
+fn main() {}
diff --git a/tests/ui/symbol-mangling-version/unstable.legacy.stderr b/tests/ui/symbol-mangling-version/unstable.legacy.stderr
new file mode 100644
index 00000000000..c5b359b41bd
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/unstable.legacy.stderr
@@ -0,0 +1,2 @@
+error: `-C symbol-mangling-version=legacy` requires `-Z unstable-options`
+
diff --git a/tests/ui/symbol-mangling-version/unstable.rs b/tests/ui/symbol-mangling-version/unstable.rs
new file mode 100644
index 00000000000..df87a39cdfb
--- /dev/null
+++ b/tests/ui/symbol-mangling-version/unstable.rs
@@ -0,0 +1,6 @@
+// revisions: legacy legacy-ok
+// [legacy] compile-flags: -Csymbol-mangling-version=legacy
+// [legacy-ok] check-pass
+// [legacy-ok] compile-flags: -Zunstable-options -Csymbol-mangling-version=legacy
+
+fn main() {}
diff --git a/tests/ui/unsized/unsize-coerce-multiple-adt-params.rs b/tests/ui/unsized/unsize-coerce-multiple-adt-params.rs
new file mode 100644
index 00000000000..eba341ff284
--- /dev/null
+++ b/tests/ui/unsized/unsize-coerce-multiple-adt-params.rs
@@ -0,0 +1,29 @@
+// check-pass
+
+struct Foo<T, U>
+where
+    (T, U): Trait,
+{
+    f: <(T, U) as Trait>::Assoc,
+}
+
+trait Trait {
+    type Assoc: ?Sized;
+}
+
+struct Count<const N: usize>;
+
+impl<const N: usize> Trait for (i32, Count<N>) {
+    type Assoc = [(); N];
+}
+
+impl<'a> Trait for (u32, ()) {
+    type Assoc = [()];
+}
+
+// Test that we can unsize several trait params in creative ways.
+fn unsize<const N: usize>(x: &Foo<i32, Count<N>>) -> &Foo<u32, ()> {
+    x
+}
+
+fn main() {}