about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs3
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs13
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs3
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs7
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs5
-rw-r--r--compiler/rustc_type_ir/src/search_graph/global_cache.rs17
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs168
-rw-r--r--compiler/rustc_type_ir/src/search_graph/stack.rs8
-rw-r--r--library/Cargo.toml10
-rw-r--r--src/bootstrap/src/bin/rustc.rs12
-rw-r--r--src/librustdoc/clean/types.rs14
-rw-r--r--src/librustdoc/html/render/write_shared.rs39
-rw-r--r--src/librustdoc/json/conversions.rs344
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs4
-rw-r--r--src/tools/run-make-support/src/run.rs25
-rw-r--r--tests/run-make/c-link-to-rust-va-list-fn/rmake.rs3
-rw-r--r--tests/ui/mir/unreachable-loop-jump-threading.rs (renamed from tests/crashes/131451.rs)3
-rw-r--r--tests/ui/mir/unreachable-loop-jump-threading.stderr10
21 files changed, 385 insertions, 309 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 066e3e9eceb..f0f5cc4db07 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -240,6 +240,9 @@ pub enum AttributeKind {
     /// Represents `#[optimize(size|speed)]`
     Optimize(OptimizeAttr, Span),
 
+    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
+    PubTransparent(Span),
+
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
     Repr(ThinVec<(ReprAttr, Span)>),
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
index d4c846de56e..4cfd9a82ce8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
@@ -19,3 +19,16 @@ impl<S: Stage> SingleAttributeParser<S> for AsPtrParser {
         Some(AttributeKind::AsPtr(cx.attr_span))
     }
 }
+
+pub(crate) struct PubTransparentParser;
+impl<S: Stage> SingleAttributeParser<S> for PubTransparentParser {
+    const PATH: &[Symbol] = &[sym::rustc_pub_transparent];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        // FIXME: check that there's no args (this is currently checked elsewhere)
+        Some(AttributeKind::PubTransparent(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 1bcf500459d..b95ea598e72 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -19,7 +19,7 @@ use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser};
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
-use crate::attributes::lint_helpers::AsPtrParser;
+use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
 use crate::attributes::repr::{AlignParser, ReprParser};
 use crate::attributes::semantics::MayDangleParser;
 use crate::attributes::stability::{
@@ -113,6 +113,7 @@ attribute_parsers!(
         Single<InlineParser>,
         Single<MayDangleParser>,
         Single<OptimizeParser>,
+        Single<PubTransparentParser>,
         Single<RustcForceInlineParser>,
         Single<TransparencyParser>,
         // tidy-alphabetical-end
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5b1f1684d54..280b33f0723 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -710,7 +710,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_pub_transparent, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes,
+        ErrorFollowing, EncodeCrossCrate::Yes,
         "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
     ),
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 32fec0604c0..752cc2eff97 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -2,6 +2,7 @@ use std::cell::LazyCell;
 use std::ops::ControlFlow;
 
 use rustc_abi::FieldIdx;
+use rustc_attr_data_structures::AttributeKind;
 use rustc_attr_data_structures::ReprAttr::ReprPacked;
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::codes::*;
@@ -1384,7 +1385,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
                 ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
                 ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
                 ty::Adt(def, args) => {
-                    if !def.did().is_local() && !tcx.has_attr(def.did(), sym::rustc_pub_transparent)
+                    if !def.did().is_local()
+                        && !attrs::find_attr!(
+                            tcx.get_all_attrs(def.did()),
+                            AttributeKind::PubTransparent(_)
+                        )
                     {
                         let non_exhaustive = def.is_variant_list_non_exhaustive()
                             || def
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 48db536c122..b45bff2af44 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -89,7 +89,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
             opportunities: Vec::new(),
         };
 
-        for bb in body.basic_blocks.indices() {
+        for (bb, _) in traversal::preorder(body) {
             finder.start_from_switch(bb);
         }
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 7ead0a6d6b7..00fd3ba8046 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -430,7 +430,7 @@ where
                 canonical_input,
                 step_kind_from_parent,
                 &mut canonical_goal_evaluation,
-                |search_graph, canonical_goal_evaluation| {
+                |search_graph, cx, canonical_input, canonical_goal_evaluation| {
                     EvalCtxt::enter_canonical(
                         cx,
                         search_graph,
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 30ac1bbe94b..d802bf4df19 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -150,6 +150,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 }
                 Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
                 }
+
+                &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => {
+                    self.check_rustc_pub_transparent(attr_span, span, attrs)
+                }
                 Attribute::Parsed(AttributeKind::Cold(attr_span)) => {
                     self.check_cold(hir_id, *attr_span, span, target)
                 }
@@ -291,7 +295,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             self.check_type_const(hir_id,attr, target);
                         }
                         [sym::linkage, ..] => self.check_linkage(attr, span, target),
-                        [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs),
                         [
                             // ok
                             sym::allow
diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
index a2442660259..1b99cc820f1 100644
--- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs
+++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
@@ -2,6 +2,7 @@ use derive_where::derive_where;
 
 use super::{AvailableDepth, Cx, NestedGoals};
 use crate::data_structures::HashMap;
+use crate::search_graph::EvaluationResult;
 
 struct Success<X: Cx> {
     required_depth: usize,
@@ -43,28 +44,26 @@ impl<X: Cx> GlobalCache<X> {
         &mut self,
         cx: X,
         input: X::Input,
-
-        origin_result: X::Result,
+        evaluation_result: EvaluationResult<X>,
         dep_node: X::DepNodeIndex,
-
-        required_depth: usize,
-        encountered_overflow: bool,
-        nested_goals: NestedGoals<X>,
     ) {
-        let result = cx.mk_tracked(origin_result, dep_node);
+        let EvaluationResult { encountered_overflow, required_depth, heads, nested_goals, result } =
+            evaluation_result;
+        debug_assert!(heads.is_empty());
+        let result = cx.mk_tracked(result, dep_node);
         let entry = self.map.entry(input).or_default();
         if encountered_overflow {
             let with_overflow = WithOverflow { nested_goals, result };
             let prev = entry.with_overflow.insert(required_depth, with_overflow);
             if let Some(prev) = &prev {
                 assert!(cx.evaluation_is_concurrent());
-                assert_eq!(cx.get_tracked(&prev.result), origin_result);
+                assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result);
             }
         } else {
             let prev = entry.success.replace(Success { required_depth, nested_goals, result });
             if let Some(prev) = &prev {
                 assert!(cx.evaluation_is_concurrent());
-                assert_eq!(cx.get_tracked(&prev.result), origin_result);
+                assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result);
             }
         }
     }
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index b59b4f92854..a857da2fcd5 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -1,16 +1,16 @@
-/// The search graph is responsible for caching and cycle detection in the trait
-/// solver. Making sure that caching doesn't result in soundness bugs or unstable
-/// query results is very challenging and makes this one of the most-involved
-/// self-contained components of the compiler.
-///
-/// We added fuzzing support to test its correctness. The fuzzers used to verify
-/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz.
-///
-/// This is just a quick overview of the general design, please check out the relevant
-/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for
-/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`.
-/// The global cache has to be completely unobservable, while the per-cycle cache may impact
-/// behavior as long as the resulting behavior is still correct.
+//! The search graph is responsible for caching and cycle detection in the trait
+//! solver. Making sure that caching doesn't result in soundness bugs or unstable
+//! query results is very challenging and makes this one of the most-involved
+//! self-contained components of the compiler.
+//!
+//! We added fuzzing support to test its correctness. The fuzzers used to verify
+//! the current implementation can be found in <https://github.com/lcnr/search_graph_fuzz>.
+//!
+//! This is just a quick overview of the general design, please check out the relevant
+//! [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for
+//! more details. Caching is split between a global cache and the per-cycle `provisional_cache`.
+//! The global cache has to be completely unobservable, while the per-cycle cache may impact
+//! behavior as long as the resulting behavior is still correct.
 use std::cmp::Ordering;
 use std::collections::BTreeMap;
 use std::collections::hash_map::Entry;
@@ -381,18 +381,16 @@ impl PathsToNested {
 /// The nested goals of each stack entry and the path from the
 /// stack entry to that nested goal.
 ///
+/// They are used when checking whether reevaluating a global cache
+/// would encounter a cycle or use a provisional cache entry given the
+/// currentl search graph state. We need to disable the global cache
+/// in this case as it could otherwise result in behaviorial differences.
+/// Cycles can impact behavior. The cycle ABA may have different final
+/// results from a the cycle BAB depending on the cycle root.
+///
 /// We only start tracking nested goals once we've either encountered
 /// overflow or a solver cycle. This is a performance optimization to
 /// avoid tracking nested goals on the happy path.
-///
-/// We use nested goals for two reasons:
-/// - when rebasing provisional cache entries
-/// - when checking whether we have to ignore a global cache entry as reevaluating
-///   it would encounter a cycle or use a provisional cache entry.
-///
-/// We need to disable the global cache if using it would hide a cycle, as
-/// cycles can impact behavior. The cycle ABA may have different final
-/// results from a the cycle BAB depending on the cycle root.
 #[derive_where(Debug, Default, Clone; X: Cx)]
 struct NestedGoals<X: Cx> {
     nested_goals: HashMap<X::Input, PathsToNested>,
@@ -450,6 +448,43 @@ struct ProvisionalCacheEntry<X: Cx> {
     result: X::Result,
 }
 
+/// The final result of evaluating a goal.
+///
+/// We reset `encountered_overflow` when reevaluating a goal,
+/// but need to track whether we've hit the recursion limit at
+/// all for correctness.
+///
+/// We've previously simply returned the final `StackEntry` but this
+/// made it easy to accidentally drop information from the previous
+/// evaluation.
+#[derive_where(Debug; X: Cx)]
+struct EvaluationResult<X: Cx> {
+    encountered_overflow: bool,
+    required_depth: usize,
+    heads: CycleHeads,
+    nested_goals: NestedGoals<X>,
+    result: X::Result,
+}
+
+impl<X: Cx> EvaluationResult<X> {
+    fn finalize(
+        final_entry: StackEntry<X>,
+        encountered_overflow: bool,
+        result: X::Result,
+    ) -> EvaluationResult<X> {
+        EvaluationResult {
+            encountered_overflow,
+            // Unlike `encountered_overflow`, we share `heads`, `required_depth`,
+            // and `nested_goals` between evaluations.
+            required_depth: final_entry.required_depth,
+            heads: final_entry.heads,
+            nested_goals: final_entry.nested_goals,
+            // We only care about the final result.
+            result,
+        }
+    }
+}
+
 pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
     root_depth: AvailableDepth,
     /// The stack of goals currently being computed.
@@ -562,7 +597,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         input: X::Input,
         step_kind_from_parent: PathKind,
         inspect: &mut D::ProofTreeBuilder,
-        mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
+        evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy,
     ) -> X::Result {
         let Some(available_depth) =
             AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack)
@@ -616,12 +651,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             input,
             step_kind_from_parent,
             available_depth,
+            provisional_result: None,
             required_depth: 0,
             heads: Default::default(),
             encountered_overflow: false,
             has_been_used: None,
             nested_goals: Default::default(),
-            provisional_result: None,
         });
 
         // This is for global caching, so we properly track query dependencies.
@@ -630,35 +665,41 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         // not tracked by the cache key and from outside of this anon task, it
         // must not be added to the global cache. Notably, this is the case for
         // trait solver cycles participants.
-        let ((final_entry, result), dep_node) = cx.with_cached_task(|| {
-            self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal)
-        });
+        let (evaluation_result, dep_node) =
+            cx.with_cached_task(|| self.evaluate_goal_in_task(cx, input, inspect, evaluate_goal));
 
         // We've finished computing the goal and have popped it from the stack,
         // lazily update its parent goal.
         Self::update_parent_goal(
             &mut self.stack,
-            final_entry.step_kind_from_parent,
-            final_entry.required_depth,
-            &final_entry.heads,
-            final_entry.encountered_overflow,
-            UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals),
+            step_kind_from_parent,
+            evaluation_result.required_depth,
+            &evaluation_result.heads,
+            evaluation_result.encountered_overflow,
+            UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals),
         );
+        let result = evaluation_result.result;
 
         // We're now done with this goal. We only add the root of cycles to the global cache.
         // In case this goal is involved in a larger cycle add it to the provisional cache.
-        if final_entry.heads.is_empty() {
+        if evaluation_result.heads.is_empty() {
             if let Some((_scope, expected)) = validate_cache {
                 // Do not try to move a goal into the cache again if we're testing
                 // the global cache.
-                assert_eq!(result, expected, "input={input:?}");
+                assert_eq!(evaluation_result.result, expected, "input={input:?}");
             } else if D::inspect_is_noop(inspect) {
-                self.insert_global_cache(cx, final_entry, result, dep_node)
+                self.insert_global_cache(cx, input, evaluation_result, dep_node)
             }
         } else if D::ENABLE_PROVISIONAL_CACHE {
             debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}");
             let entry = self.provisional_cache.entry(input).or_default();
-            let StackEntry { heads, encountered_overflow, .. } = final_entry;
+            let EvaluationResult {
+                encountered_overflow,
+                required_depth: _,
+                heads,
+                nested_goals: _,
+                result,
+            } = evaluation_result;
             let path_from_head = Self::cycle_path_kind(
                 &self.stack,
                 step_kind_from_parent,
@@ -1023,19 +1064,25 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         cx: X,
         input: X::Input,
         inspect: &mut D::ProofTreeBuilder,
-        mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
-    ) -> (StackEntry<X>, X::Result) {
+        evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy,
+    ) -> EvaluationResult<X> {
+        // We reset `encountered_overflow` each time we rerun this goal
+        // but need to make sure we currently propagate it to the global
+        // cache even if only some of the evaluations actually reach the
+        // recursion limit.
+        let mut encountered_overflow = false;
         let mut i = 0;
         loop {
-            let result = evaluate_goal(self, inspect);
+            let result = evaluate_goal(self, cx, input, inspect);
             let stack_entry = self.stack.pop();
+            encountered_overflow |= stack_entry.encountered_overflow;
             debug_assert_eq!(stack_entry.input, input);
 
             // If the current goal is not the root of a cycle, we are done.
             //
             // There are no provisional cache entries which depend on this goal.
             let Some(usage_kind) = stack_entry.has_been_used else {
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             };
 
             // If it is a cycle head, we have to keep trying to prove it until
@@ -1051,7 +1098,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             // final result is equal to the initial response for that case.
             if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) {
                 self.rebase_provisional_cache_entries(&stack_entry, |_, result| result);
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             }
 
             // If computing this goal results in ambiguity with no constraints,
@@ -1070,7 +1117,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 self.rebase_provisional_cache_entries(&stack_entry, |input, _| {
                     D::propagate_ambiguity(cx, input, result)
                 });
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             };
 
             // If we've reached the fixpoint step limit, we bail with overflow and taint all
@@ -1082,7 +1129,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 self.rebase_provisional_cache_entries(&stack_entry, |input, _| {
                     D::on_fixpoint_overflow(cx, input)
                 });
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             }
 
             // Clear all provisional cache entries which depend on a previous provisional
@@ -1091,9 +1138,22 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
 
             debug!(?result, "fixpoint changed provisional results");
             self.stack.push(StackEntry {
-                has_been_used: None,
+                input,
+                step_kind_from_parent: stack_entry.step_kind_from_parent,
+                available_depth: stack_entry.available_depth,
                 provisional_result: Some(result),
-                ..stack_entry
+                // We can keep these goals from previous iterations as they are only
+                // ever read after finalizing this evaluation.
+                required_depth: stack_entry.required_depth,
+                heads: stack_entry.heads,
+                nested_goals: stack_entry.nested_goals,
+                // We reset these two fields when rerunning this goal. We could
+                // keep `encountered_overflow` as it's only used as a performance
+                // optimization. However, given that the proof tree will likely look
+                // similar to the previous iterations when reevaluating, it's better
+                // for caching if the reevaluation also starts out with `false`.
+                encountered_overflow: false,
+                has_been_used: None,
             });
         }
     }
@@ -1109,21 +1169,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     fn insert_global_cache(
         &mut self,
         cx: X,
-        final_entry: StackEntry<X>,
-        result: X::Result,
+        input: X::Input,
+        evaluation_result: EvaluationResult<X>,
         dep_node: X::DepNodeIndex,
     ) {
-        debug!(?final_entry, ?result, "insert global cache");
-        cx.with_global_cache(|cache| {
-            cache.insert(
-                cx,
-                final_entry.input,
-                result,
-                dep_node,
-                final_entry.required_depth,
-                final_entry.encountered_overflow,
-                final_entry.nested_goals,
-            )
-        })
+        debug!(?evaluation_result, "insert global cache");
+        cx.with_global_cache(|cache| cache.insert(cx, input, evaluation_result, dep_node))
     }
 }
diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs
index 8bb247bf055..e0fd934df69 100644
--- a/compiler/rustc_type_ir/src/search_graph/stack.rs
+++ b/compiler/rustc_type_ir/src/search_graph/stack.rs
@@ -26,6 +26,10 @@ pub(super) struct StackEntry<X: Cx> {
     /// The available depth of a given goal, immutable.
     pub available_depth: AvailableDepth,
 
+    /// Starts out as `None` and gets set when rerunning this
+    /// goal in case we encounter a cycle.
+    pub provisional_result: Option<X::Result>,
+
     /// The maximum depth required while evaluating this goal.
     pub required_depth: usize,
 
@@ -42,10 +46,6 @@ pub(super) struct StackEntry<X: Cx> {
 
     /// The nested goals of this goal, see the doc comment of the type.
     pub nested_goals: NestedGoals<X>,
-
-    /// Starts out as `None` and gets set when rerunning this
-    /// goal in case we encounter a cycle.
-    pub provisional_result: Option<X::Result>,
 }
 
 #[derive_where(Default; X: Cx)]
diff --git a/library/Cargo.toml b/library/Cargo.toml
index 35480b9319d..c66f621ffde 100644
--- a/library/Cargo.toml
+++ b/library/Cargo.toml
@@ -1,3 +1,5 @@
+cargo-features = ["profile-rustflags"]
+
 [workspace]
 resolver = "1"
 members = [
@@ -44,6 +46,14 @@ object.debug = 0
 rustc-demangle.debug = 0
 rustc-demangle.opt-level = "s"
 
+# panic_abort must always be compiled with panic=abort, even when the rest of the
+# sysroot is panic=unwind.
+[profile.dev.package.panic_abort]
+rustflags = ["-Cpanic=abort"]
+
+[profile.release.package.panic_abort]
+rustflags = ["-Cpanic=abort"]
+
 [patch.crates-io]
 # See comments in `library/rustc-std-workspace-core/README.md` for what's going on
 # here
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 0671a8467e8..0364c664ba5 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -151,18 +151,6 @@ fn main() {
             cmd.arg("--sysroot").arg(&sysroot);
         }
 
-        // If we're compiling specifically the `panic_abort` crate then we pass
-        // the `-C panic=abort` option. Note that we do not do this for any
-        // other crate intentionally as this is the only crate for now that we
-        // ship with panic=abort.
-        //
-        // This... is a bit of a hack how we detect this. Ideally this
-        // information should be encoded in the crate I guess? Would likely
-        // require an RFC amendment to RFC 1513, however.
-        if crate_name == Some("panic_abort") {
-            cmd.arg("-C").arg("panic=abort");
-        }
-
         let crate_type = parse_value_from_args(&orig_args, "--crate-type");
         // `-Ztls-model=initial-exec` must not be applied to proc-macros, see
         // issue https://github.com/rust-lang/rust/issues/100530
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 408ef611ee5..c0a9d8c84f6 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -2432,20 +2432,6 @@ pub(crate) enum ConstantKind {
     Infer,
 }
 
-impl Constant {
-    pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
-        self.kind.expr(tcx)
-    }
-
-    pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option<String> {
-        self.kind.value(tcx)
-    }
-
-    pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool {
-        self.kind.is_literal(tcx)
-    }
-}
-
 impl ConstantKind {
     pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
         match *self {
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index fb2b45802a6..606a9113908 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -439,24 +439,20 @@ impl CratesIndexPart {
         let content =
             format!("<h1>List of all crates</h1><ul class=\"all-items\">{DELIMITER}</ul>");
         let template = layout::render(layout, &page, "", content, style_files);
-        match SortedTemplate::from_template(&template, DELIMITER) {
-            Ok(template) => template,
-            Err(e) => panic!(
-                "Object Replacement Character (U+FFFC) should not appear in the --index-page: {e}"
-            ),
-        }
+        SortedTemplate::from_template(&template, DELIMITER)
+            .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page")
     }
 
     /// Might return parts that are duplicate with ones in prexisting index.html
     fn get(crate_name: &str, external_crates: &[String]) -> Result<PartsAndLocations<Self>, Error> {
         let mut ret = PartsAndLocations::default();
-        let path = PathBuf::from("index.html");
+        let path = Path::new("index.html");
         for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) {
             let part = format!(
                 "<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
                 trailing_slash = ensure_trailing_slash(crate_name),
             );
-            ret.push(path.clone(), part);
+            ret.push(path.to_path_buf(), part);
         }
         Ok(ret)
     }
@@ -737,7 +733,7 @@ impl TraitAliasPart {
                 },
             };
 
-            let implementors = imps
+            let mut implementors = imps
                 .iter()
                 .filter_map(|imp| {
                     // If the trait and implementation are in the same crate, then
@@ -759,12 +755,12 @@ impl TraitAliasPart {
                         })
                     }
                 })
-                .collect::<Vec<_>>();
+                .peekable();
 
             // Only create a js file if we have impls to add to it. If the trait is
             // documented locally though we always create the file to avoid dead
             // links.
-            if implementors.is_empty() && !cache.paths.contains_key(&did) {
+            if implementors.peek().is_none() && !cache.paths.contains_key(&did) {
                 continue;
             }
 
@@ -775,11 +771,7 @@ impl TraitAliasPart {
             path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
 
             let part = OrderedJson::array_sorted(
-                implementors
-                    .iter()
-                    .map(OrderedJson::serialize)
-                    .collect::<Result<Vec<_>, _>>()
-                    .unwrap(),
+                implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
             );
             path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
         }
@@ -874,9 +866,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
             let impl_ = cache
                 .impls
                 .get(&target_did)
-                .map(|v| &v[..])
-                .unwrap_or_default()
-                .iter()
+                .into_iter()
+                .flatten()
                 .map(|impl_| {
                     (impl_.impl_item.item_id, AliasedTypeImpl { impl_, type_aliases: Vec::new() })
                 })
@@ -891,14 +882,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
         // Exclude impls that are directly on this type. They're already in the HTML.
         // Some inlining scenarios can cause there to be two versions of the same
         // impl: one on the type alias and one on the underlying target type.
-        let mut seen_impls: FxHashSet<ItemId> = cache
-            .impls
-            .get(&self_did)
-            .map(|s| &s[..])
-            .unwrap_or_default()
-            .iter()
-            .map(|i| i.impl_item.item_id)
-            .collect();
+        let mut seen_impls: FxHashSet<ItemId> =
+            cache.impls.get(&self_did).into_iter().flatten().map(|i| i.impl_item.item_id).collect();
         for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
             // Only include this impl if it actually unifies with this alias.
             // Synthetic impls are not included; those are also included in the HTML.
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index cb4c1f7fbc0..abad6e48029 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -11,7 +11,7 @@ use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::DefId;
 use rustc_metadata::rendered_const;
 use rustc_middle::{bug, ty};
-use rustc_span::{Pos, Symbol, kw};
+use rustc_span::{Pos, kw, sym};
 use rustdoc_json_types::*;
 use thin_vec::ThinVec;
 
@@ -66,47 +66,16 @@ impl JsonRenderer<'_> {
             id,
             crate_id: item_id.krate().as_u32(),
             name: name.map(|sym| sym.to_string()),
-            span: span.and_then(|span| self.convert_span(span)),
-            visibility: self.convert_visibility(visibility),
+            span: span.and_then(|span| span.into_json(self)),
+            visibility: visibility.into_json(self),
             docs,
             attrs,
-            deprecation: deprecation.map(from_deprecation),
+            deprecation: deprecation.into_json(self),
             inner,
             links,
         })
     }
 
-    fn convert_span(&self, span: clean::Span) -> Option<Span> {
-        match span.filename(self.sess()) {
-            rustc_span::FileName::Real(name) => {
-                if let Some(local_path) = name.into_local_path() {
-                    let hi = span.hi(self.sess());
-                    let lo = span.lo(self.sess());
-                    Some(Span {
-                        filename: local_path,
-                        begin: (lo.line, lo.col.to_usize() + 1),
-                        end: (hi.line, hi.col.to_usize() + 1),
-                    })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        }
-    }
-
-    fn convert_visibility(&self, v: Option<ty::Visibility<DefId>>) -> Visibility {
-        match v {
-            None => Visibility::Default,
-            Some(ty::Visibility::Public) => Visibility::Public,
-            Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate,
-            Some(ty::Visibility::Restricted(did)) => Visibility::Restricted {
-                parent: self.id_from_item_default(did.into()),
-                path: self.tcx.def_path(did).to_string_no_crate_verbose(),
-            },
-        }
-    }
-
     fn ids(&self, items: &[clean::Item]) -> Vec<Id> {
         items
             .iter()
@@ -140,11 +109,29 @@ where
     }
 }
 
+impl<T, U> FromClean<Box<T>> for U
+where
+    U: FromClean<T>,
+{
+    fn from_clean(opt: &Box<T>, renderer: &JsonRenderer<'_>) -> Self {
+        opt.as_ref().into_json(renderer)
+    }
+}
+
+impl<T, U> FromClean<Option<T>> for Option<U>
+where
+    U: FromClean<T>,
+{
+    fn from_clean(opt: &Option<T>, renderer: &JsonRenderer<'_>) -> Self {
+        opt.as_ref().map(|x| x.into_json(renderer))
+    }
+}
+
 impl<T, U> FromClean<Vec<T>> for Vec<U>
 where
     U: FromClean<T>,
 {
-    fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> {
+    fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Self {
         items.iter().map(|i| i.into_json(renderer)).collect()
     }
 }
@@ -153,20 +140,57 @@ impl<T, U> FromClean<ThinVec<T>> for Vec<U>
 where
     U: FromClean<T>,
 {
-    fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> {
+    fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Self {
         items.iter().map(|i| i.into_json(renderer)).collect()
     }
 }
 
-pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation {
-    let attrs::Deprecation { since, note, suggestion: _ } = deprecation;
-    let since = match since {
-        DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
-        DeprecatedSince::Future => Some("TBD".to_owned()),
-        DeprecatedSince::NonStandard(since) => Some(since.to_string()),
-        DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
-    };
-    Deprecation { since, note: note.map(|s| s.to_string()) }
+impl FromClean<clean::Span> for Option<Span> {
+    fn from_clean(span: &clean::Span, renderer: &JsonRenderer<'_>) -> Self {
+        match span.filename(renderer.sess()) {
+            rustc_span::FileName::Real(name) => {
+                if let Some(local_path) = name.into_local_path() {
+                    let hi = span.hi(renderer.sess());
+                    let lo = span.lo(renderer.sess());
+                    Some(Span {
+                        filename: local_path,
+                        begin: (lo.line, lo.col.to_usize() + 1),
+                        end: (hi.line, hi.col.to_usize() + 1),
+                    })
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    }
+}
+
+impl FromClean<Option<ty::Visibility<DefId>>> for Visibility {
+    fn from_clean(v: &Option<ty::Visibility<DefId>>, renderer: &JsonRenderer<'_>) -> Self {
+        match v {
+            None => Visibility::Default,
+            Some(ty::Visibility::Public) => Visibility::Public,
+            Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate,
+            Some(ty::Visibility::Restricted(did)) => Visibility::Restricted {
+                parent: renderer.id_from_item_default((*did).into()),
+                path: renderer.tcx.def_path(*did).to_string_no_crate_verbose(),
+            },
+        }
+    }
+}
+
+impl FromClean<attrs::Deprecation> for Deprecation {
+    fn from_clean(deprecation: &attrs::Deprecation, _renderer: &JsonRenderer<'_>) -> Self {
+        let attrs::Deprecation { since, note, suggestion: _ } = deprecation;
+        let since = match since {
+            DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
+            DeprecatedSince::Future => Some("TBD".to_string()),
+            DeprecatedSince::NonStandard(since) => Some(since.to_string()),
+            DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
+        };
+        Deprecation { since, note: note.map(|sym| sym.to_string()) }
+    }
 }
 
 impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> {
@@ -182,7 +206,7 @@ impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> {
             },
             Parenthesized { inputs, output } => GenericArgs::Parenthesized {
                 inputs: inputs.into_json(renderer),
-                output: output.as_ref().map(|a| a.as_ref().into_json(renderer)),
+                output: output.into_json(renderer),
             },
             ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
         }))
@@ -193,7 +217,7 @@ impl FromClean<clean::GenericArg> for GenericArg {
     fn from_clean(arg: &clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArg::*;
         match arg {
-            Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)),
+            Lifetime(l) => GenericArg::Lifetime(l.into_json(renderer)),
             Type(t) => GenericArg::Type(t.into_json(renderer)),
             Const(box c) => GenericArg::Const(c.into_json(renderer)),
             Infer => GenericArg::Infer,
@@ -201,17 +225,6 @@ impl FromClean<clean::GenericArg> for GenericArg {
     }
 }
 
-impl FromClean<clean::Constant> for Constant {
-    // FIXME(generic_const_items): Add support for generic const items.
-    fn from_clean(constant: &clean::Constant, renderer: &JsonRenderer<'_>) -> Self {
-        let tcx = renderer.tcx;
-        let expr = constant.expr(tcx);
-        let value = constant.value(tcx);
-        let is_literal = constant.is_literal(tcx);
-        Constant { expr, value, is_literal }
-    }
-}
-
 impl FromClean<clean::ConstantKind> for Constant {
     // FIXME(generic_const_items): Add support for generic const items.
     fn from_clean(constant: &clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self {
@@ -259,21 +272,25 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum
         StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)),
         EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)),
         VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)),
-        FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), renderer)),
+        FunctionItem(f) => {
+            ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer))
+        }
         ForeignFunctionItem(f, _) => {
-            ItemEnum::Function(from_function(f, false, header.unwrap(), renderer))
+            ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer))
         }
-        TraitItem(t) => ItemEnum::Trait(t.as_ref().into_json(renderer)),
+        TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)),
         TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)),
-        MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
+        MethodItem(m, _) => {
+            ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer))
+        }
         RequiredMethodItem(m) => {
-            ItemEnum::Function(from_function(m, false, header.unwrap(), renderer))
+            ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer))
         }
-        ImplItem(i) => ItemEnum::Impl(i.as_ref().into_json(renderer)),
-        StaticItem(s) => ItemEnum::Static(convert_static(s, &rustc_hir::Safety::Safe, renderer)),
-        ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)),
+        ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)),
+        StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)),
+        ForeignStaticItem(s, safety) => ItemEnum::Static(from_clean_static(s, *safety, renderer)),
         ForeignTypeItem => ItemEnum::ExternType,
-        TypeAliasItem(t) => ItemEnum::TypeAlias(t.as_ref().into_json(renderer)),
+        TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
         // FIXME(generic_const_items): Add support for generic free consts
         ConstantItem(ci) => ItemEnum::Constant {
             type_: ci.type_.into_json(renderer),
@@ -289,7 +306,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         RequiredAssocConstItem(_generics, ty) => {
-            ItemEnum::AssocConst { type_: ty.as_ref().into_json(renderer), value: None }
+            ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None }
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst {
@@ -361,32 +378,38 @@ impl FromClean<clean::Union> for Union {
     }
 }
 
-pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader {
-    FunctionHeader {
-        is_async: header.is_async(),
-        is_const: header.is_const(),
-        is_unsafe: header.is_unsafe(),
-        abi: convert_abi(header.abi),
+impl FromClean<rustc_hir::FnHeader> for FunctionHeader {
+    fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self {
+        FunctionHeader {
+            is_async: header.is_async(),
+            is_const: header.is_const(),
+            is_unsafe: header.is_unsafe(),
+            abi: header.abi.into_json(renderer),
+        }
     }
 }
 
-fn convert_abi(a: ExternAbi) -> Abi {
-    match a {
-        ExternAbi::Rust => Abi::Rust,
-        ExternAbi::C { unwind } => Abi::C { unwind },
-        ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
-        ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
-        ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
-        ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
-        ExternAbi::Win64 { unwind } => Abi::Win64 { unwind },
-        ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
-        ExternAbi::System { unwind } => Abi::System { unwind },
-        _ => Abi::Other(a.to_string()),
+impl FromClean<ExternAbi> for Abi {
+    fn from_clean(a: &ExternAbi, _renderer: &JsonRenderer<'_>) -> Self {
+        match *a {
+            ExternAbi::Rust => Abi::Rust,
+            ExternAbi::C { unwind } => Abi::C { unwind },
+            ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
+            ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
+            ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
+            ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
+            ExternAbi::Win64 { unwind } => Abi::Win64 { unwind },
+            ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
+            ExternAbi::System { unwind } => Abi::System { unwind },
+            _ => Abi::Other(a.to_string()),
+        }
     }
 }
 
-fn convert_lifetime(l: &clean::Lifetime) -> String {
-    l.0.to_string()
+impl FromClean<clean::Lifetime> for String {
+    fn from_clean(l: &clean::Lifetime, _renderer: &JsonRenderer<'_>) -> String {
+        l.0.to_string()
+    }
 }
 
 impl FromClean<clean::Generics> for Generics {
@@ -411,16 +434,16 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
     fn from_clean(kind: &clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericParamDefKind::*;
         match kind {
-            Lifetime { outlives } => GenericParamDefKind::Lifetime {
-                outlives: outlives.into_iter().map(convert_lifetime).collect(),
-            },
+            Lifetime { outlives } => {
+                GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) }
+            }
             Type { bounds, default, synthetic } => GenericParamDefKind::Type {
                 bounds: bounds.into_json(renderer),
-                default: default.as_ref().map(|x| x.as_ref().into_json(renderer)),
+                default: default.into_json(renderer),
                 is_synthetic: *synthetic,
             },
             Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
-                type_: ty.as_ref().into_json(renderer),
+                type_: ty.into_json(renderer),
                 default: default.as_ref().map(|x| x.as_ref().clone()),
             },
         }
@@ -434,45 +457,14 @@ impl FromClean<clean::WherePredicate> for WherePredicate {
             BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
                 type_: ty.into_json(renderer),
                 bounds: bounds.into_json(renderer),
-                generic_params: bound_params
-                    .iter()
-                    .map(|x| {
-                        let name = x.name.to_string();
-                        let kind = match &x.kind {
-                            clean::GenericParamDefKind::Lifetime { outlives } => {
-                                GenericParamDefKind::Lifetime {
-                                    outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
-                                }
-                            }
-                            clean::GenericParamDefKind::Type { bounds, default, synthetic } => {
-                                GenericParamDefKind::Type {
-                                    bounds: bounds
-                                        .into_iter()
-                                        .map(|bound| bound.into_json(renderer))
-                                        .collect(),
-                                    default: default
-                                        .as_ref()
-                                        .map(|ty| ty.as_ref().into_json(renderer)),
-                                    is_synthetic: *synthetic,
-                                }
-                            }
-                            clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
-                                GenericParamDefKind::Const {
-                                    type_: ty.as_ref().into_json(renderer),
-                                    default: default.as_ref().map(|d| d.as_ref().clone()),
-                                }
-                            }
-                        };
-                        GenericParamDef { name, kind }
-                    })
-                    .collect(),
+                generic_params: bound_params.into_json(renderer),
             },
             RegionPredicate { lifetime, bounds } => WherePredicate::LifetimePredicate {
-                lifetime: convert_lifetime(lifetime),
+                lifetime: lifetime.into_json(renderer),
                 outlives: bounds
                     .iter()
                     .map(|bound| match bound {
-                        clean::GenericBound::Outlives(lt) => convert_lifetime(lt),
+                        clean::GenericBound::Outlives(lt) => lt.into_json(renderer),
                         _ => bug!("found non-outlives-bound on lifetime predicate"),
                     })
                     .collect(),
@@ -496,15 +488,15 @@ impl FromClean<clean::GenericBound> for GenericBound {
                 GenericBound::TraitBound {
                     trait_: trait_.into_json(renderer),
                     generic_params: generic_params.into_json(renderer),
-                    modifier: from_trait_bound_modifier(modifier),
+                    modifier: modifier.into_json(renderer),
                 }
             }
-            Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)),
+            Outlives(lifetime) => GenericBound::Outlives(lifetime.into_json(renderer)),
             Use(args) => GenericBound::Use(
                 args.iter()
                     .map(|arg| match arg {
                         clean::PreciseCapturingArg::Lifetime(lt) => {
-                            PreciseCapturingArg::Lifetime(convert_lifetime(lt))
+                            PreciseCapturingArg::Lifetime(lt.into_json(renderer))
                         }
                         clean::PreciseCapturingArg::Param(param) => {
                             PreciseCapturingArg::Param(param.to_string())
@@ -516,19 +508,22 @@ impl FromClean<clean::GenericBound> for GenericBound {
     }
 }
 
-pub(crate) fn from_trait_bound_modifier(
-    modifiers: &rustc_hir::TraitBoundModifiers,
-) -> TraitBoundModifier {
-    use rustc_hir as hir;
-    let hir::TraitBoundModifiers { constness, polarity } = modifiers;
-    match (constness, polarity) {
-        (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None,
-        (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe,
-        (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => {
-            TraitBoundModifier::MaybeConst
+impl FromClean<rustc_hir::TraitBoundModifiers> for TraitBoundModifier {
+    fn from_clean(
+        modifiers: &rustc_hir::TraitBoundModifiers,
+        _renderer: &JsonRenderer<'_>,
+    ) -> Self {
+        use rustc_hir as hir;
+        let hir::TraitBoundModifiers { constness, polarity } = modifiers;
+        match (constness, polarity) {
+            (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None,
+            (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe,
+            (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => {
+                TraitBoundModifier::MaybeConst
+            }
+            // FIXME: Fill out the rest of this matrix.
+            _ => TraitBoundModifier::None,
         }
-        // FIXME: Fill out the rest of this matrix.
-        _ => TraitBoundModifier::None,
     }
 }
 
@@ -542,35 +537,35 @@ impl FromClean<clean::Type> for Type {
         match ty {
             clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)),
             clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
-                lifetime: lt.as_ref().map(convert_lifetime),
+                lifetime: lt.into_json(renderer),
                 traits: bounds.into_json(renderer),
             }),
             Generic(s) => Type::Generic(s.to_string()),
             // FIXME: add dedicated variant to json Type?
             SelfTy => Type::Generic("Self".to_owned()),
             Primitive(p) => Type::Primitive(p.as_sym().to_string()),
-            BareFunction(f) => Type::FunctionPointer(Box::new(f.as_ref().into_json(renderer))),
+            BareFunction(f) => Type::FunctionPointer(Box::new(f.into_json(renderer))),
             Tuple(t) => Type::Tuple(t.into_json(renderer)),
-            Slice(t) => Type::Slice(Box::new(t.as_ref().into_json(renderer))),
+            Slice(t) => Type::Slice(Box::new(t.into_json(renderer))),
             Array(t, s) => {
-                Type::Array { type_: Box::new(t.as_ref().into_json(renderer)), len: s.to_string() }
+                Type::Array { type_: Box::new(t.into_json(renderer)), len: s.to_string() }
             }
             clean::Type::Pat(t, p) => Type::Pat {
-                type_: Box::new(t.as_ref().into_json(renderer)),
+                type_: Box::new(t.into_json(renderer)),
                 __pat_unstable_do_not_use: p.to_string(),
             },
             ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)),
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
                 is_mutable: *mutability == ast::Mutability::Mut,
-                type_: Box::new(type_.as_ref().into_json(renderer)),
+                type_: Box::new(type_.into_json(renderer)),
             },
             BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
-                lifetime: lifetime.as_ref().map(convert_lifetime),
+                lifetime: lifetime.into_json(renderer),
                 is_mutable: *mutability == ast::Mutability::Mut,
-                type_: Box::new(type_.as_ref().into_json(renderer)),
+                type_: Box::new(type_.into_json(renderer)),
             },
-            QPath(qpath) => qpath.as_ref().into_json(renderer),
+            QPath(qpath) => qpath.into_json(renderer),
             // FIXME(unsafe_binder): Implement rustdoc-json.
             UnsafeBinder(_) => todo!(),
         }
@@ -578,7 +573,7 @@ impl FromClean<clean::Type> for Type {
 }
 
 impl FromClean<clean::Path> for Path {
-    fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Path {
+    fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Self {
         Path {
             path: path.whole_name(),
             id: renderer.id_from_item_default(path.def_id().into()),
@@ -608,13 +603,13 @@ impl FromClean<clean::QPathData> for Type {
             name: assoc.name.to_string(),
             args: assoc.args.into_json(renderer),
             self_type: Box::new(self_type.into_json(renderer)),
-            trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)),
+            trait_: trait_.into_json(renderer),
         }
     }
 }
 
 impl FromClean<clean::Term> for Term {
-    fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Term {
+    fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Self {
         match term {
             clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)),
             clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)),
@@ -630,7 +625,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
                 is_unsafe: safety.is_unsafe(),
                 is_const: false,
                 is_async: false,
-                abi: convert_abi(*abi),
+                abi: abi.into_json(renderer),
             },
             generic_params: generic_params.into_json(renderer),
             sig: decl.into_json(renderer),
@@ -709,17 +704,17 @@ impl FromClean<clean::Impl> for Impl {
                 .into_iter()
                 .map(|x| x.to_string())
                 .collect(),
-            trait_: trait_.as_ref().map(|path| path.into_json(renderer)),
+            trait_: trait_.into_json(renderer),
             for_: for_.into_json(renderer),
             items: renderer.ids(&items),
             is_negative,
             is_synthetic,
-            blanket_impl: blanket_impl.map(|x| x.as_ref().into_json(renderer)),
+            blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
         }
     }
 }
 
-pub(crate) fn from_function(
+pub(crate) fn from_clean_function(
     clean::Function { decl, generics }: &clean::Function,
     has_body: bool,
     header: rustc_hir::FnHeader,
@@ -728,7 +723,7 @@ pub(crate) fn from_function(
     Function {
         sig: decl.into_json(renderer),
         generics: generics.into_json(renderer),
-        header: from_fn_header(&header),
+        header: header.into_json(renderer),
         has_body,
     }
 }
@@ -750,7 +745,7 @@ impl FromClean<clean::Variant> for Variant {
     fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self {
         use clean::VariantKind::*;
 
-        let discriminant = variant.discriminant.as_ref().map(|d| d.into_json(renderer));
+        let discriminant = variant.discriminant.into_json(renderer);
 
         let kind = match &variant.kind {
             CLike => VariantKind::Plain,
@@ -783,10 +778,7 @@ impl FromClean<clean::Import> for Use {
         use clean::ImportKind::*;
         let (name, is_glob) = match import.kind {
             Simple(s) => (s.to_string(), false),
-            Glob => (
-                import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
-                true,
-            ),
+            Glob => (import.source.path.last_opt().unwrap_or(sym::asterisk).to_string(), true),
         };
         Use {
             source: import.source.path.whole_name(),
@@ -798,20 +790,22 @@ impl FromClean<clean::Import> for Use {
 }
 
 impl FromClean<clean::ProcMacro> for ProcMacro {
-    fn from_clean(mac: &clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(mac: &clean::ProcMacro, renderer: &JsonRenderer<'_>) -> Self {
         ProcMacro {
-            kind: from_macro_kind(mac.kind),
+            kind: mac.kind.into_json(renderer),
             helpers: mac.helpers.iter().map(|x| x.to_string()).collect(),
         }
     }
 }
 
-pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind {
-    use rustc_span::hygiene::MacroKind::*;
-    match kind {
-        Bang => MacroKind::Bang,
-        Attr => MacroKind::Attr,
-        Derive => MacroKind::Derive,
+impl FromClean<rustc_span::hygiene::MacroKind> for MacroKind {
+    fn from_clean(kind: &rustc_span::hygiene::MacroKind, _renderer: &JsonRenderer<'_>) -> Self {
+        use rustc_span::hygiene::MacroKind::*;
+        match kind {
+            Bang => MacroKind::Bang,
+            Attr => MacroKind::Attr,
+            Derive => MacroKind::Derive,
+        }
     }
 }
 
@@ -822,9 +816,9 @@ impl FromClean<clean::TypeAlias> for TypeAlias {
     }
 }
 
-fn convert_static(
+fn from_clean_static(
     stat: &clean::Static,
-    safety: &rustc_hir::Safety,
+    safety: rustc_hir::Safety,
     renderer: &JsonRenderer<'_>,
 ) -> Static {
     let tcx = renderer.tcx;
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 029da1c1898..60e8e16e25e 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -221,6 +221,10 @@ impl TestCx<'_> {
             cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
         }
 
+        if let Some(runner) = &self.config.runner {
+            cmd.env("RUNNER", runner);
+        }
+
         // We don't want RUSTFLAGS set from the outside to interfere with
         // compiler flags set in the test cases:
         cmd.env_remove("RUSTFLAGS");
diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs
index 60e711d3402..b95f3a5cfe5 100644
--- a/src/tools/run-make-support/src/run.rs
+++ b/src/tools/run-make-support/src/run.rs
@@ -1,4 +1,4 @@
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
 use std::path::PathBuf;
 use std::{env, panic};
 
@@ -22,6 +22,20 @@ fn run_common(name: &str, args: Option<&[&str]>) -> Command {
         cmd.arg("0");
         cmd.arg(bin_path);
         cmd
+    } else if let Ok(runner) = std::env::var("RUNNER") {
+        let mut args = split_maybe_args(&runner);
+
+        let prog = args.remove(0);
+        let mut cmd = Command::new(prog);
+
+        for arg in args {
+            cmd.arg(arg);
+        }
+
+        cmd.arg("--");
+        cmd.arg(bin_path);
+
+        cmd
     } else {
         Command::new(bin_path)
     };
@@ -92,3 +106,12 @@ pub fn cmd<S: AsRef<OsStr>>(program: S) -> Command {
     command.env("LC_ALL", "C"); // force english locale
     command
 }
+
+fn split_maybe_args(s: &str) -> Vec<OsString> {
+    // FIXME(132599): implement proper env var/shell argument splitting.
+    s.split(' ')
+        .filter_map(|s| {
+            if s.chars().all(|c| c.is_whitespace()) { None } else { Some(OsString::from(s)) }
+        })
+        .collect()
+}
diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
index 63904bea622..426d65b7af3 100644
--- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
+++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
@@ -3,7 +3,8 @@
 // prevent the creation of a functional binary.
 // See https://github.com/rust-lang/rust/pull/49878
 
-//@ ignore-cross-compile
+//@ needs-target-std
+//@ ignore-android: FIXME(#142855)
 
 use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name};
 
diff --git a/tests/crashes/131451.rs b/tests/ui/mir/unreachable-loop-jump-threading.rs
index cd5b44bad8a..8403906bb5c 100644
--- a/tests/crashes/131451.rs
+++ b/tests/ui/mir/unreachable-loop-jump-threading.rs
@@ -1,9 +1,10 @@
-//@ known-bug: #131451
+//@ build-pass
 //@ needs-rustc-debug-assertions
 //@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+JumpThreading --crate-type=lib
 
 pub fn fun(terminate: bool) {
     while true {}
+    //~^ WARN denote infinite loops with `loop { ... }`
 
     while !terminate {}
 }
diff --git a/tests/ui/mir/unreachable-loop-jump-threading.stderr b/tests/ui/mir/unreachable-loop-jump-threading.stderr
new file mode 100644
index 00000000000..21b174c8021
--- /dev/null
+++ b/tests/ui/mir/unreachable-loop-jump-threading.stderr
@@ -0,0 +1,10 @@
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/unreachable-loop-jump-threading.rs:6:5
+   |
+LL |     while true {}
+   |     ^^^^^^^^^^ help: use `loop`
+   |
+   = note: `#[warn(while_true)]` on by default
+
+warning: 1 warning emitted
+