about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-05-07 01:16:08 +0000
committerbors <bors@rust-lang.org>2021-05-07 01:16:08 +0000
commit1773f14a24c49356b384e45ebb45643bc9bef2c4 (patch)
treee6ad409e4410a72f43101974b5a824e15fba28a5
parent777bb2f6129e71a88ba030251eb370ef12fe28af (diff)
parent01e9d09d3bbb35a1fbb0a2a353cfcf90a4feb050 (diff)
downloadrust-1773f14a24c49356b384e45ebb45643bc9bef2c4.tar.gz
rust-1773f14a24c49356b384e45ebb45643bc9bef2c4.zip
Auto merge of #85014 - Dylan-DPC:rollup-jzpbkdu, r=Dylan-DPC
Rollup of 11 pull requests

Successful merges:

 - #84409 (Ensure TLS destructors run before thread joins in SGX)
 - #84500 (Add --run flag to compiletest)
 - #84728 (Add test for suggestion to borrow unsized function parameters)
 - #84734 (Add `needs-unwind` and beginning of support for testing `panic=abort` std to compiletest)
 - #84755 (Allow using `core::` in intra-doc links within core itself)
 - #84871 (Disallows `#![feature(no_coverage)]` on stable and beta (using standard crate-level gating))
 - #84872 (Wire up tidy dependency checks for cg_clif)
 - #84896 (Handle incorrect placement of parentheses in trait bounds more gracefully)
 - #84905 (CTFE engine: rename copy → copy_intrinsic, move to intrinsics.rs)
 - #84953 (Remove unneeded call to with_default_session_globals in rustdoc highlight)
 - #84987 (small nits)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs14
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs8
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs3
-rw-r--r--compiler/rustc_mir/src/interpret/intrinsics.rs34
-rw-r--r--compiler/rustc_mir/src/interpret/step.rs34
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs39
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs2
-rw-r--r--compiler/rustc_typeck/src/collect.rs28
-rw-r--r--library/core/src/cmp.rs5
-rw-r--r--library/core/src/intrinsics.rs20
-rw-r--r--library/core/src/lib.rs5
-rw-r--r--library/std/src/sys/sgx/abi/mod.rs6
-rw-r--r--library/std/src/sys/sgx/thread.rs69
-rw-r--r--library/std/src/thread/local/tests.rs108
-rw-r--r--src/bootstrap/builder/tests.rs3
-rw-r--r--src/bootstrap/flags.rs14
-rw-r--r--src/bootstrap/test.rs5
-rw-r--r--src/librustdoc/html/highlight.rs37
-rw-r--r--src/librustdoc/html/highlight/tests.rs27
-rw-r--r--src/test/debuginfo/should-fail.rs1
-rw-r--r--src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt19
-rw-r--r--src/test/run-make-fulldeps/coverage/no_cov_func.rs18
-rw-r--r--src/test/ui/cfg/cfg-panic.rs1
-rw-r--r--src/test/ui/feature-gates/feature-gate-no_coverage.rs13
-rw-r--r--src/test/ui/feature-gates/feature-gate-no_coverage.stderr3
-rw-r--r--src/test/ui/intrinsics/intrinsic-alignment.rs1
-rw-r--r--src/test/ui/issues/issue-70093.rs1
-rw-r--r--src/test/ui/meta/revision-bad.rs1
-rw-r--r--src/test/ui/panic-handler/weak-lang-item.rs1
-rw-r--r--src/test/ui/panic-handler/weak-lang-item.stderr2
-rw-r--r--src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs1
-rw-r--r--src/test/ui/panic-runtime/lto-unwind.rs1
-rw-r--r--src/test/ui/panic-runtime/transitive-link-a-bunch.rs1
-rw-r--r--src/test/ui/panic-runtime/want-unwind-got-abort.rs1
-rw-r--r--src/test/ui/panic-runtime/want-unwind-got-abort2.rs1
-rw-r--r--src/test/ui/parser/trait-object-delimiters.rs17
-rw-r--r--src/test/ui/parser/trait-object-delimiters.stderr77
-rw-r--r--src/test/ui/structs-enums/rec-align-u64.rs1
-rw-r--r--src/test/ui/suggestions/unsized-function-parameter.fixed23
-rw-r--r--src/test/ui/suggestions/unsized-function-parameter.rs23
-rw-r--r--src/test/ui/suggestions/unsized-function-parameter.stderr42
-rw-r--r--src/test/ui/test-panic-abort-disabled.rs2
-rw-r--r--src/test/ui/unwind-no-uwtable.rs1
-rw-r--r--src/test/ui/x86stdcall.rs1
-rw-r--r--src/tools/compiletest/src/common.rs22
-rw-r--r--src/tools/compiletest/src/header.rs12
-rw-r--r--src/tools/compiletest/src/main.rs25
-rw-r--r--src/tools/compiletest/src/runtest.rs62
-rw-r--r--src/tools/tidy/src/deps.rs183
50 files changed, 763 insertions, 258 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index 5a4e7fd9d07..54ab88dc3ff 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -15,20 +15,12 @@ pub fn expand_deriving_eq(
     item: &Annotatable,
     push: &mut dyn FnMut(Annotatable),
 ) {
+    let span = cx.with_def_site_ctxt(span);
     let inline = cx.meta_word(span, sym::inline);
-    let no_coverage_ident =
-        rustc_ast::attr::mk_nested_word_item(Ident::new(sym::no_coverage, span));
-    let no_coverage_feature =
-        rustc_ast::attr::mk_list_item(Ident::new(sym::feature, span), vec![no_coverage_ident]);
-    let no_coverage = cx.meta_word(span, sym::no_coverage);
     let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
     let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
-    let attrs = vec![
-        cx.attribute(inline),
-        cx.attribute(no_coverage_feature),
-        cx.attribute(no_coverage),
-        cx.attribute(doc),
-    ];
+    let no_coverage = cx.meta_word(span, sym::no_coverage);
+    let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5474fea9c78..a8719be84c2 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -273,13 +273,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: "address, memory, thread"),
         experimental!(no_sanitize)
     ),
-    ungated!(
-        // Not exclusively gated at the crate level (though crate-level is
-        // supported). The feature can alternatively be enabled on individual
-        // functions.
-        no_coverage, AssumedUsed,
-        template!(Word),
-    ),
+    gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
 
     // FIXME: #14408 assume docs are used since rustdoc looks at them.
     ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index a91bd9ce2ff..1cafb2fe1a2 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -2398,9 +2398,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 self.tcx.associated_item(def_id).ident
             ),
             infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name),
-            infer::BoundRegionInCoherence(name) => {
-                format!(" for lifetime parameter `{}` in coherence check", name)
-            }
             infer::UpvarRegion(ref upvar_id, _) => {
                 let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                 format!(" for capture of `{}` by closure", var_name)
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index eaec6b46bcd..f39431f2494 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -453,8 +453,6 @@ pub enum RegionVariableOrigin {
 
     UpvarRegion(ty::UpvarId, Span),
 
-    BoundRegionInCoherence(Symbol),
-
     /// This origin is used for the inference variables that we create
     /// during NLL region processing.
     Nll(NllRegionVariableOrigin),
@@ -1749,7 +1747,6 @@ impl RegionVariableOrigin {
             | EarlyBoundRegion(a, ..)
             | LateBoundRegion(a, ..)
             | UpvarRegion(_, a) => a,
-            BoundRegionInCoherence(_) => rustc_span::DUMMY_SP,
             Nll(..) => bug!("NLL variable used with `span`"),
         }
     }
diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs
index dea1b113315..292306f6cde 100644
--- a/compiler/rustc_mir/src/interpret/intrinsics.rs
+++ b/compiler/rustc_mir/src/interpret/intrinsics.rs
@@ -323,7 +323,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 self.write_scalar(result, dest)?;
             }
             sym::copy => {
-                self.copy(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
+                self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
             }
             sym::offset => {
                 let ptr = self.read_scalar(&args[0])?.check_init()?;
@@ -530,4 +530,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         )?;
         Ok(offset_ptr)
     }
+
+    /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
+    pub(crate) fn copy_intrinsic(
+        &mut self,
+        src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+        nonoverlapping: bool,
+    ) -> InterpResult<'tcx> {
+        let count = self.read_scalar(&count)?.to_machine_usize(self)?;
+        let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
+        let (size, align) = (layout.size, layout.align.abi);
+        let size = size.checked_mul(count, self).ok_or_else(|| {
+            err_ub_format!(
+                "overflow computing total size of `{}`",
+                if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
+            )
+        })?;
+
+        // Make sure we check both pointers for an access of the total size and aligment,
+        // *even if* the total size is 0.
+        let src =
+            self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?;
+
+        let dst =
+            self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?;
+
+        if let (Some(src), Some(dst)) = (src, dst) {
+            self.memory.copy(src, dst, size, nonoverlapping)?;
+        }
+        Ok(())
+    }
 }
diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs
index 6084f67abd7..5a10ffe6d61 100644
--- a/compiler/rustc_mir/src/interpret/step.rs
+++ b/compiler/rustc_mir/src/interpret/step.rs
@@ -2,7 +2,6 @@
 //!
 //! The main entry point is the `step` method.
 
-use crate::interpret::OpTy;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_target::abi::LayoutOf;
@@ -119,7 +118,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let src = self.eval_operand(src, None)?;
                 let dst = self.eval_operand(dst, None)?;
                 let count = self.eval_operand(count, None)?;
-                self.copy(&src, &dst, &count, /* nonoverlapping */ true)?;
+                self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?;
             }
 
             // Statements we do not track.
@@ -149,37 +148,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(())
     }
 
-    pub(crate) fn copy(
-        &mut self,
-        src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
-        nonoverlapping: bool,
-    ) -> InterpResult<'tcx> {
-        let count = self.read_scalar(&count)?.to_machine_usize(self)?;
-        let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
-        let (size, align) = (layout.size, layout.align.abi);
-        let size = size.checked_mul(count, self).ok_or_else(|| {
-            err_ub_format!(
-                "overflow computing total size of `{}`",
-                if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
-            )
-        })?;
-
-        // Make sure we check both pointers for an access of the total size and aligment,
-        // *even if* the total size is 0.
-        let src =
-            self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?;
-
-        let dst =
-            self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?;
-
-        if let (Some(src), Some(dst)) = (src, dst) {
-            self.memory.copy(src, dst, size, nonoverlapping)?;
-        }
-        Ok(())
-    }
-
     /// Evaluate an assignment statement.
     ///
     /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 0f7b8ebd376..d537741c749 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -470,7 +470,7 @@ impl<'a> Parser<'a> {
     /// Is a `dyn B0 + ... + Bn` type allowed here?
     fn is_explicit_dyn_type(&mut self) -> bool {
         self.check_keyword(kw::Dyn)
-            && (self.token.uninterpolated_span().rust_2018()
+            && (!self.token.uninterpolated_span().rust_2015()
                 || self.look_ahead(1, |t| {
                     t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t)
                 }))
@@ -539,7 +539,21 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, GenericBounds> {
         let mut bounds = Vec::new();
         let mut negative_bounds = Vec::new();
-        while self.can_begin_bound() {
+
+        while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
+            if self.token.is_keyword(kw::Dyn) {
+                // Account for `&dyn Trait + dyn Other`.
+                self.struct_span_err(self.token.span, "invalid `dyn` keyword")
+                    .help("`dyn` is only needed at the start of a trait `+`-separated list")
+                    .span_suggestion(
+                        self.token.span,
+                        "remove this keyword",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
+                self.bump();
+            }
             match self.parse_generic_bound()? {
                 Ok(bound) => bounds.push(bound),
                 Err(neg_sp) => negative_bounds.push(neg_sp),
@@ -721,7 +735,26 @@ impl<'a> Parser<'a> {
         let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
         let path = self.parse_path(PathStyle::Type)?;
         if has_parens {
-            self.expect(&token::CloseDelim(token::Paren))?;
+            if self.token.is_like_plus() {
+                // Someone has written something like `&dyn (Trait + Other)`. The correct code
+                // would be `&(dyn Trait + Other)`, but we don't have access to the appropriate
+                // span to suggest that. When written as `&dyn Trait + Other`, an appropriate
+                // suggestion is given.
+                let bounds = vec![];
+                self.parse_remaining_bounds(bounds, true)?;
+                self.expect(&token::CloseDelim(token::Paren))?;
+                let sp = vec![lo, self.prev_token.span];
+                let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
+                self.struct_span_err(sp, "incorrect braces around trait bounds")
+                    .multipart_suggestion(
+                        "remove the parentheses",
+                        sugg,
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
+            } else {
+                self.expect(&token::CloseDelim(token::Paren))?;
+            }
         }
 
         let modifier = modifiers.to_trait_bound_modifier();
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 727285e4927..08d452900c8 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1044,8 +1044,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 
     /// Returns `true` if the global caches can be used.
-    /// Do note that if the type itself is not in the
-    /// global tcx, the local caches will be used.
     fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
         // If there are any inference variables in the `ParamEnv`, then we
         // always use a cache local to this particular scope. Otherwise, we
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 190c9d35934..0528f8812f9 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -2661,8 +2661,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     let mut inline_span = None;
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
-    let mut no_coverage_feature_enabled = false;
-    let mut no_coverage_attr = None;
     for attr in attrs.iter() {
         if tcx.sess.check_name(attr, sym::cold) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
@@ -2726,15 +2724,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
         } else if tcx.sess.check_name(attr, sym::no_mangle) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
-        } else if attr.has_name(sym::feature) {
-            if let Some(list) = attr.meta_item_list() {
-                if list.iter().any(|nested_meta_item| nested_meta_item.has_name(sym::no_coverage)) {
-                    tcx.sess.mark_attr_used(attr);
-                    no_coverage_feature_enabled = true;
-                }
-            }
         } else if tcx.sess.check_name(attr, sym::no_coverage) {
-            no_coverage_attr = Some(attr);
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
         } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
         } else if tcx.sess.check_name(attr, sym::used) {
@@ -2945,23 +2936,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
         }
     }
 
-    if let Some(no_coverage_attr) = no_coverage_attr {
-        if tcx.sess.features_untracked().no_coverage || no_coverage_feature_enabled {
-            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE
-        } else {
-            let mut err = feature_err(
-                &tcx.sess.parse_sess,
-                sym::no_coverage,
-                no_coverage_attr.span,
-                "the `#[no_coverage]` attribute is an experimental feature",
-            );
-            if tcx.sess.parse_sess.unstable_features.is_nightly_build() {
-                err.help("or, alternatively, add `#[feature(no_coverage)]` to the function");
-            }
-            err.emit();
-        }
-    }
-
     codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
         if !attr.has_name(sym::inline) {
             return ia;
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 0a3e5789e8b..f8b16b6f927 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -274,8 +274,7 @@ pub trait Eq: PartialEq<Self> {
     //
     // This should never be implemented by hand.
     #[doc(hidden)]
-    #[cfg_attr(not(bootstrap), feature(no_coverage))]
-    #[cfg_attr(not(bootstrap), no_coverage)]
+    #[cfg_attr(not(bootstrap), no_coverage)] // rust-lang/rust#84605
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn assert_receiver_is_total_eq(&self) {}
@@ -284,7 +283,7 @@ pub trait Eq: PartialEq<Self> {
 /// Derive macro generating an impl of the trait `Eq`.
 #[rustc_builtin_macro]
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)]
+#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match, no_coverage)]
 pub macro Eq($item:item) {
     /* compiler built-in */
 }
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d7dd7ee02c1..0034de9ad1b 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -723,7 +723,7 @@ extern "rust-intrinsic" {
     /// macro, which panics when it is executed, it is *undefined behavior* to
     /// reach code marked with this function.
     ///
-    /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`](crate::hint::unreachable_unchecked).
+    /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`].
     #[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")]
     pub fn unreachable() -> !;
 
@@ -768,13 +768,13 @@ extern "rust-intrinsic" {
     /// More specifically, this is the offset in bytes between successive
     /// items of the same type, including alignment padding.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::size_of`](crate::mem::size_of).
+    /// The stabilized version of this intrinsic is [`core::mem::size_of`].
     #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
     pub fn size_of<T>() -> usize;
 
     /// The minimum alignment of a type.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::align_of`](crate::mem::align_of).
+    /// The stabilized version of this intrinsic is [`core::mem::align_of`].
     #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")]
     pub fn min_align_of<T>() -> usize;
     /// The preferred alignment of a type.
@@ -790,13 +790,13 @@ extern "rust-intrinsic" {
     pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
     /// The required alignment of the referenced value.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::align_of_val`](crate::mem::align_of_val).
+    /// The stabilized version of this intrinsic is [`core::mem::align_of_val`].
     #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")]
     pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
 
     /// Gets a static string slice containing the name of a type.
     ///
-    /// The stabilized version of this intrinsic is [`core::any::type_name`](crate::any::type_name).
+    /// The stabilized version of this intrinsic is [`core::any::type_name`].
     #[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
     pub fn type_name<T: ?Sized>() -> &'static str;
 
@@ -804,7 +804,7 @@ extern "rust-intrinsic" {
     /// function will return the same value for a type regardless of whichever
     /// crate it is invoked in.
     ///
-    /// The stabilized version of this intrinsic is [`core::any::TypeId::of`](crate::any::TypeId::of).
+    /// The stabilized version of this intrinsic is [`core::any::TypeId::of`].
     #[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
     pub fn type_id<T: ?Sized + 'static>() -> u64;
 
@@ -829,7 +829,7 @@ extern "rust-intrinsic" {
 
     /// Gets a reference to a static `Location` indicating where it was called.
     ///
-    /// Consider using [`core::panic::Location::caller`](crate::panic::Location::caller) instead.
+    /// Consider using [`core::panic::Location::caller`] instead.
     #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
     pub fn caller_location() -> &'static crate::panic::Location<'static>;
 
@@ -1158,11 +1158,11 @@ extern "rust-intrinsic" {
 
     /// Performs a volatile load from the `src` pointer.
     ///
-    /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`](crate::ptr::read_volatile).
+    /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`].
     pub fn volatile_load<T>(src: *const T) -> T;
     /// Performs a volatile store to the `dst` pointer.
     ///
-    /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`](crate::ptr::write_volatile).
+    /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`].
     pub fn volatile_store<T>(dst: *mut T, val: T);
 
     /// Performs a volatile load from the `src` pointer
@@ -1703,7 +1703,7 @@ extern "rust-intrinsic" {
     /// Returns the value of the discriminant for the variant in 'v';
     /// if `T` has no discriminant, returns `0`.
     ///
-    /// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant).
+    /// The stabilized version of this intrinsic is [`core::mem::discriminant`].
     #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
     pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
 
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 0e2c140c367..07bf47b9c6f 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -166,9 +166,14 @@
 #![feature(const_caller_location)]
 #![feature(slice_ptr_get)]
 #![feature(no_niche)] // rust-lang/rust#68303
+#![cfg_attr(not(bootstrap), feature(no_coverage))] // rust-lang/rust#84605
 #![feature(int_error_matching)]
 #![deny(unsafe_op_in_unsafe_fn)]
 
+// allow using `core::` in intra-doc links
+#[allow(unused_extern_crates)]
+extern crate self as core;
+
 #[prelude_import]
 #[allow(unused)]
 use prelude::v1::*;
diff --git a/library/std/src/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs
index a5e45303476..f9536c4203d 100644
--- a/library/std/src/sys/sgx/abi/mod.rs
+++ b/library/std/src/sys/sgx/abi/mod.rs
@@ -62,10 +62,12 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
 extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
     // FIXME: how to support TLS in library mode?
     let tls = Box::new(tls::Tls::new());
-    let _tls_guard = unsafe { tls.activate() };
+    let tls_guard = unsafe { tls.activate() };
 
     if secondary {
-        super::thread::Thread::entry();
+        let join_notifier = super::thread::Thread::entry();
+        drop(tls_guard);
+        drop(join_notifier);
 
         EntryReturn(0, 0)
     } else {
diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs
index 55ef460cc90..67e2e8b59d3 100644
--- a/library/std/src/sys/sgx/thread.rs
+++ b/library/std/src/sys/sgx/thread.rs
@@ -9,26 +9,37 @@ pub struct Thread(task_queue::JoinHandle);
 
 pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
 
+pub use self::task_queue::JoinNotifier;
+
 mod task_queue {
-    use crate::sync::mpsc;
+    use super::wait_notify;
     use crate::sync::{Mutex, MutexGuard, Once};
 
-    pub type JoinHandle = mpsc::Receiver<()>;
+    pub type JoinHandle = wait_notify::Waiter;
+
+    pub struct JoinNotifier(Option<wait_notify::Notifier>);
+
+    impl Drop for JoinNotifier {
+        fn drop(&mut self) {
+            self.0.take().unwrap().notify();
+        }
+    }
 
     pub(super) struct Task {
         p: Box<dyn FnOnce()>,
-        done: mpsc::Sender<()>,
+        done: JoinNotifier,
     }
 
     impl Task {
         pub(super) fn new(p: Box<dyn FnOnce()>) -> (Task, JoinHandle) {
-            let (done, recv) = mpsc::channel();
+            let (done, recv) = wait_notify::new();
+            let done = JoinNotifier(Some(done));
             (Task { p, done }, recv)
         }
 
-        pub(super) fn run(self) {
+        pub(super) fn run(self) -> JoinNotifier {
             (self.p)();
-            let _ = self.done.send(());
+            self.done
         }
     }
 
@@ -47,6 +58,48 @@ mod task_queue {
     }
 }
 
+/// This module provides a synchronization primitive that does not use thread
+/// local variables. This is needed for signaling that a thread has finished
+/// execution. The signal is sent once all TLS destructors have finished at
+/// which point no new thread locals should be created.
+pub mod wait_notify {
+    use super::super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+    use crate::sync::Arc;
+
+    pub struct Notifier(Arc<SpinMutex<WaitVariable<bool>>>);
+
+    impl Notifier {
+        /// Notify the waiter. The waiter is either notified right away (if
+        /// currently blocked in `Waiter::wait()`) or later when it calls the
+        /// `Waiter::wait()` method.
+        pub fn notify(self) {
+            let mut guard = self.0.lock();
+            *guard.lock_var_mut() = true;
+            let _ = WaitQueue::notify_one(guard);
+        }
+    }
+
+    pub struct Waiter(Arc<SpinMutex<WaitVariable<bool>>>);
+
+    impl Waiter {
+        /// Wait for a notification. If `Notifier::notify()` has already been
+        /// called, this will return immediately, otherwise the current thread
+        /// is blocked until notified.
+        pub fn wait(self) {
+            let guard = self.0.lock();
+            if *guard.lock_var() {
+                return;
+            }
+            WaitQueue::wait(guard, || {});
+        }
+    }
+
+    pub fn new() -> (Notifier, Waiter) {
+        let inner = Arc::new(SpinMutex::new(WaitVariable::new(false)));
+        (Notifier(inner.clone()), Waiter(inner))
+    }
+}
+
 impl Thread {
     // unsafe: see thread::Builder::spawn_unchecked for safety requirements
     pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
@@ -57,7 +110,7 @@ impl Thread {
         Ok(Thread(handle))
     }
 
-    pub(super) fn entry() {
+    pub(super) fn entry() -> JoinNotifier {
         let mut pending_tasks = task_queue::lock();
         let task = rtunwrap!(Some, pending_tasks.pop());
         drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
@@ -78,7 +131,7 @@ impl Thread {
     }
 
     pub fn join(self) {
-        let _ = self.0.recv();
+        self.0.wait();
     }
 }
 
diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs
index 80e6798d847..f33d6129619 100644
--- a/library/std/src/thread/local/tests.rs
+++ b/library/std/src/thread/local/tests.rs
@@ -1,4 +1,5 @@
 use crate::cell::{Cell, UnsafeCell};
+use crate::sync::atomic::{AtomicU8, Ordering};
 use crate::sync::mpsc::{channel, Sender};
 use crate::thread::{self, LocalKey};
 use crate::thread_local;
@@ -207,3 +208,110 @@ fn dtors_in_dtors_in_dtors_const_init() {
     });
     rx.recv().unwrap();
 }
+
+// This test tests that TLS destructors have run before the thread joins. The
+// test has no false positives (meaning: if the test fails, there's actually
+// an ordering problem). It may have false negatives, where the test passes but
+// join is not guaranteed to be after the TLS destructors. However, false
+// negatives should be exceedingly rare due to judicious use of
+// thread::yield_now and running the test several times.
+#[test]
+fn join_orders_after_tls_destructors() {
+    // We emulate a synchronous MPSC rendezvous channel using only atomics and
+    // thread::yield_now. We can't use std::mpsc as the implementation itself
+    // may rely on thread locals.
+    //
+    // The basic state machine for an SPSC rendezvous channel is:
+    //           FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS
+    // where the first transition is done by the “receiving” thread and the 2nd
+    // transition is done by the “sending” thread.
+    //
+    // We add an additional state `THREAD2_LAUNCHED` between `FRESH` and
+    // `THREAD1_WAITING` to block until all threads are actually running.
+    //
+    // A thread that joins on the “receiving” thread completion should never
+    // observe the channel in the `THREAD1_WAITING` state. If this does occur,
+    // we switch to the “poison” state `THREAD2_JOINED` and panic all around.
+    // (This is equivalent to “sending” from an alternate producer thread.)
+    const FRESH: u8 = 0;
+    const THREAD2_LAUNCHED: u8 = 1;
+    const THREAD1_WAITING: u8 = 2;
+    const MAIN_THREAD_RENDEZVOUS: u8 = 3;
+    const THREAD2_JOINED: u8 = 4;
+    static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH);
+
+    for _ in 0..10 {
+        SYNC_STATE.store(FRESH, Ordering::SeqCst);
+
+        let jh = thread::Builder::new()
+            .name("thread1".into())
+            .spawn(move || {
+                struct TlDrop;
+
+                impl Drop for TlDrop {
+                    fn drop(&mut self) {
+                        let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst);
+                        loop {
+                            match sync_state {
+                                THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(),
+                                MAIN_THREAD_RENDEZVOUS => break,
+                                THREAD2_JOINED => panic!(
+                                    "Thread 1 still running after thread 2 joined on thread 1"
+                                ),
+                                v => unreachable!("sync state: {}", v),
+                            }
+                            sync_state = SYNC_STATE.load(Ordering::SeqCst);
+                        }
+                    }
+                }
+
+                thread_local! {
+                    static TL_DROP: TlDrop = TlDrop;
+                }
+
+                TL_DROP.with(|_| {});
+
+                loop {
+                    match SYNC_STATE.load(Ordering::SeqCst) {
+                        FRESH => thread::yield_now(),
+                        THREAD2_LAUNCHED => break,
+                        v => unreachable!("sync state: {}", v),
+                    }
+                }
+            })
+            .unwrap();
+
+        let jh2 = thread::Builder::new()
+            .name("thread2".into())
+            .spawn(move || {
+                assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH);
+                jh.join().unwrap();
+                match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) {
+                    MAIN_THREAD_RENDEZVOUS => return,
+                    THREAD2_LAUNCHED | THREAD1_WAITING => {
+                        panic!("Thread 2 running after thread 1 join before main thread rendezvous")
+                    }
+                    v => unreachable!("sync state: {:?}", v),
+                }
+            })
+            .unwrap();
+
+        loop {
+            match SYNC_STATE.compare_exchange_weak(
+                THREAD1_WAITING,
+                MAIN_THREAD_RENDEZVOUS,
+                Ordering::SeqCst,
+                Ordering::SeqCst,
+            ) {
+                Ok(_) => break,
+                Err(FRESH) => thread::yield_now(),
+                Err(THREAD2_LAUNCHED) => thread::yield_now(),
+                Err(THREAD2_JOINED) => {
+                    panic!("Main thread rendezvous after thread 2 joined thread 1")
+                }
+                v => unreachable!("sync state: {:?}", v),
+            }
+        }
+        jh2.join().unwrap();
+    }
+}
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index a881512e988..4d7c207e3ab 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -489,6 +489,7 @@ mod dist {
             compare_mode: None,
             rustfix_coverage: false,
             pass: None,
+            run: None,
         };
 
         let build = Build::new(config);
@@ -529,6 +530,7 @@ mod dist {
             compare_mode: None,
             rustfix_coverage: false,
             pass: None,
+            run: None,
         };
 
         let build = Build::new(config);
@@ -584,6 +586,7 @@ mod dist {
             compare_mode: None,
             rustfix_coverage: false,
             pass: None,
+            run: None,
         };
         // Make sure rustfmt binary not being found isn't an error.
         config.channel = "beta".to_string();
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 6044899c237..d961e067db3 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -103,6 +103,7 @@ pub enum Subcommand {
         bless: bool,
         compare_mode: Option<String>,
         pass: Option<String>,
+        run: Option<String>,
         test_args: Vec<String>,
         rustc_args: Vec<String>,
         fail_fast: bool,
@@ -222,8 +223,8 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
              VALUE overrides the skip-rebuild option in config.toml.",
             "VALUE",
         );
-        opts.optopt("", "rust-profile-generate", "rustc error format", "FORMAT");
-        opts.optopt("", "rust-profile-use", "rustc error format", "FORMAT");
+        opts.optopt("", "rust-profile-generate", "generate PGO profile with rustc build", "FORMAT");
+        opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "FORMAT");
 
         // We can't use getopt to parse the options until we have completed specifying which
         // options are valid, but under the current implementation, some options are conditional on
@@ -293,6 +294,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
                     "force {check,build,run}-pass tests to this mode.",
                     "check | build | run",
                 );
+                opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never");
                 opts.optflag(
                     "",
                     "rustfix-coverage",
@@ -556,6 +558,7 @@ Arguments:
                 bless: matches.opt_present("bless"),
                 compare_mode: matches.opt_str("compare-mode"),
                 pass: matches.opt_str("pass"),
+                run: matches.opt_str("run"),
                 test_args: matches.opt_strs("test-args"),
                 rustc_args: matches.opt_strs("rustc-args"),
                 fail_fast: !matches.opt_present("no-fail-fast"),
@@ -742,6 +745,13 @@ impl Subcommand {
         }
     }
 
+    pub fn run(&self) -> Option<&str> {
+        match *self {
+            Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
+            _ => None,
+        }
+    }
+
     pub fn open(&self) -> bool {
         match *self {
             Subcommand::Doc { open, .. } => open,
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index db443756de3..fbce2f03da0 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1240,6 +1240,11 @@ note: if you're sure you want to do this, please open an issue as to why. In the
             cmd.arg(pass);
         }
 
+        if let Some(ref run) = builder.config.cmd.run() {
+            cmd.arg("--run");
+            cmd.arg(run);
+        }
+
         if let Some(ref nodejs) = builder.config.nodejs {
             cmd.arg("--nodejs").arg(nodejs);
         }
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 7130a6bc1e8..f631f627fc2 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -13,7 +13,6 @@ use std::iter::Peekable;
 use rustc_lexer::{LiteralKind, TokenKind};
 use rustc_span::edition::Edition;
 use rustc_span::symbol::Symbol;
-use rustc_span::with_default_session_globals;
 
 use super::format::Buffer;
 
@@ -238,28 +237,26 @@ impl<'a> Classifier<'a> {
     /// possibly giving it an HTML span with a class specifying what flavor of
     /// token is used.
     fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) {
-        with_default_session_globals(|| {
-            loop {
-                if self
-                    .tokens
-                    .peek()
-                    .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
-                    .unwrap_or(false)
-                {
-                    let tokens = self.get_full_ident_path();
-                    for (token, start, end) in tokens {
-                        let text = &self.src[start..end];
-                        self.advance(token, text, sink);
-                        self.byte_pos += text.len() as u32;
-                    }
-                }
-                if let Some((token, text)) = self.next() {
+        loop {
+            if self
+                .tokens
+                .peek()
+                .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
+                .unwrap_or(false)
+            {
+                let tokens = self.get_full_ident_path();
+                for (token, start, end) in tokens {
+                    let text = &self.src[start..end];
                     self.advance(token, text, sink);
-                } else {
-                    break;
+                    self.byte_pos += text.len() as u32;
                 }
             }
-        })
+            if let Some((token, text)) = self.next() {
+                self.advance(token, text, sink);
+            } else {
+                break;
+            }
+        }
     }
 
     /// Single step of highlighting. This will classify `token`, but maybe also
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index 305cf61091d..a0da2c963d1 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -2,6 +2,7 @@ use super::write_code;
 use crate::html::format::Buffer;
 use expect_test::expect_file;
 use rustc_span::edition::Edition;
+use rustc_span::with_default_session_globals;
 
 const STYLE: &str = r#"
 <style>
@@ -17,21 +18,25 @@ const STYLE: &str = r#"
 
 #[test]
 fn test_html_highlighting() {
-    let src = include_str!("fixtures/sample.rs");
-    let html = {
-        let mut out = Buffer::new();
-        write_code(&mut out, src, Edition::Edition2018);
-        format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
-    };
-    expect_file!["fixtures/sample.html"].assert_eq(&html);
+    with_default_session_globals(|| {
+        let src = include_str!("fixtures/sample.rs");
+        let html = {
+            let mut out = Buffer::new();
+            write_code(&mut out, src, Edition::Edition2018);
+            format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
+        };
+        expect_file!["fixtures/sample.html"].assert_eq(&html);
+    });
 }
 
 #[test]
 fn test_dos_backline() {
-    let src = "pub fn foo() {\r\n\
+    with_default_session_globals(|| {
+        let src = "pub fn foo() {\r\n\
     println!(\"foo\");\r\n\
 }\r\n";
-    let mut html = Buffer::new();
-    write_code(&mut html, src, Edition::Edition2018);
-    expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
+        let mut html = Buffer::new();
+        write_code(&mut html, src, Edition::Edition2018);
+        expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
+    });
 }
diff --git a/src/test/debuginfo/should-fail.rs b/src/test/debuginfo/should-fail.rs
index 1e0d22cbce4..eef6d99d2a9 100644
--- a/src/test/debuginfo/should-fail.rs
+++ b/src/test/debuginfo/should-fail.rs
@@ -2,6 +2,7 @@
 
 // == Test [gdb|lldb]-[command|check] are parsed correctly ===
 // should-fail
+// needs-run-enabled
 // compile-flags:-g
 
 // === GDB TESTS ===================================================================================
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt
deleted file mode 100644
index 16eaf7c858c..00000000000
--- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-    1|       |// Enables `no_coverage` on individual functions
-    2|       |
-    3|       |#[feature(no_coverage)]
-    4|       |#[no_coverage]
-    5|       |fn do_not_add_coverage_1() {
-    6|       |    println!("called but not covered");
-    7|       |}
-    8|       |
-    9|       |#[no_coverage]
-   10|       |#[feature(no_coverage)]
-   11|       |fn do_not_add_coverage_2() {
-   12|       |    println!("called but not covered");
-   13|       |}
-   14|       |
-   15|      1|fn main() {
-   16|      1|    do_not_add_coverage_1();
-   17|      1|    do_not_add_coverage_2();
-   18|      1|}
-
diff --git a/src/test/run-make-fulldeps/coverage/no_cov_func.rs b/src/test/run-make-fulldeps/coverage/no_cov_func.rs
deleted file mode 100644
index e19a2c4a872..00000000000
--- a/src/test/run-make-fulldeps/coverage/no_cov_func.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Enables `no_coverage` on individual functions
-
-#[feature(no_coverage)]
-#[no_coverage]
-fn do_not_add_coverage_1() {
-    println!("called but not covered");
-}
-
-#[no_coverage]
-#[feature(no_coverage)]
-fn do_not_add_coverage_2() {
-    println!("called but not covered");
-}
-
-fn main() {
-    do_not_add_coverage_1();
-    do_not_add_coverage_2();
-}
diff --git a/src/test/ui/cfg/cfg-panic.rs b/src/test/ui/cfg/cfg-panic.rs
index dbb5932a9bb..d2113e4f5ec 100644
--- a/src/test/ui/cfg/cfg-panic.rs
+++ b/src/test/ui/cfg/cfg-panic.rs
@@ -1,5 +1,6 @@
 // build-pass
 // compile-flags: -C panic=unwind
+// needs-unwind
 // ignore-emscripten no panic_unwind implementation
 // ignore-wasm32     no panic_unwind implementation
 // ignore-wasm64     no panic_unwind implementation
diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.rs b/src/test/ui/feature-gates/feature-gate-no_coverage.rs
index c6b79f9a431..fd4c6f76059 100644
--- a/src/test/ui/feature-gates/feature-gate-no_coverage.rs
+++ b/src/test/ui/feature-gates/feature-gate-no_coverage.rs
@@ -1,8 +1,13 @@
 #![crate_type = "lib"]
 
-#[no_coverage]
-#[feature(no_coverage)] // does not have to be enabled before `#[no_coverage]`
-fn no_coverage_is_enabled_on_this_function() {}
+#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(no_coverage)`
+struct Foo {
+    a: u8,
+    b: u32,
+}
 
 #[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
-fn requires_feature_no_coverage() {}
+fn requires_feature_no_coverage() -> bool {
+    let bar = Foo { a: 0, b: 0 };
+    bar == Foo { a: 0, b: 0 }
+}
diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
index 04627be4aaf..f7167e0b771 100644
--- a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
+++ b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
@@ -1,12 +1,11 @@
 error[E0658]: the `#[no_coverage]` attribute is an experimental feature
-  --> $DIR/feature-gate-no_coverage.rs:7:1
+  --> $DIR/feature-gate-no_coverage.rs:9:1
    |
 LL | #[no_coverage]
    | ^^^^^^^^^^^^^^
    |
    = note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
    = help: add `#![feature(no_coverage)]` to the crate attributes to enable
-   = help: or, alternatively, add `#[feature(no_coverage)]` to the function
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/intrinsics/intrinsic-alignment.rs b/src/test/ui/intrinsics/intrinsic-alignment.rs
index 592409ba89f..5a27ea8783a 100644
--- a/src/test/ui/intrinsics/intrinsic-alignment.rs
+++ b/src/test/ui/intrinsics/intrinsic-alignment.rs
@@ -14,6 +14,7 @@ mod rusti {
           target_os = "dragonfly",
           target_os = "emscripten",
           target_os = "freebsd",
+          target_os = "fuchsia",
           target_os = "linux",
           target_os = "macos",
           target_os = "netbsd",
diff --git a/src/test/ui/issues/issue-70093.rs b/src/test/ui/issues/issue-70093.rs
index 95ab86ebcb1..fbe68fb9379 100644
--- a/src/test/ui/issues/issue-70093.rs
+++ b/src/test/ui/issues/issue-70093.rs
@@ -1,6 +1,7 @@
 // run-pass
 // compile-flags: -Zlink-native-libraries=no -Cdefault-linker-libraries=yes
 // ignore-windows - this will probably only work on unixish systems
+// ignore-fuchsia - missing __libc_start_main for some reason (#84733)
 
 #[link(name = "some-random-non-existent-library", kind = "static")]
 extern "C" {}
diff --git a/src/test/ui/meta/revision-bad.rs b/src/test/ui/meta/revision-bad.rs
index 01f1518c1c6..37ddbe99a9f 100644
--- a/src/test/ui/meta/revision-bad.rs
+++ b/src/test/ui/meta/revision-bad.rs
@@ -4,6 +4,7 @@
 // run-fail
 // revisions: foo bar
 // should-fail
+// needs-run-enabled
 //[foo] error-pattern:bar
 //[bar] error-pattern:foo
 
diff --git a/src/test/ui/panic-handler/weak-lang-item.rs b/src/test/ui/panic-handler/weak-lang-item.rs
index 3fa3822831b..df31e614cf8 100644
--- a/src/test/ui/panic-handler/weak-lang-item.rs
+++ b/src/test/ui/panic-handler/weak-lang-item.rs
@@ -1,6 +1,7 @@
 // aux-build:weak-lang-items.rs
 // error-pattern: `#[panic_handler]` function required, but not found
 // error-pattern: language item required, but not found: `eh_personality`
+// needs-unwind since it affects the error output
 // ignore-emscripten compiled with panic=abort, personality not required
 
 #![no_std]
diff --git a/src/test/ui/panic-handler/weak-lang-item.stderr b/src/test/ui/panic-handler/weak-lang-item.stderr
index 68e3e21df3e..1f14b20e451 100644
--- a/src/test/ui/panic-handler/weak-lang-item.stderr
+++ b/src/test/ui/panic-handler/weak-lang-item.stderr
@@ -1,5 +1,5 @@
 error[E0259]: the name `core` is defined multiple times
-  --> $DIR/weak-lang-item.rs:8:1
+  --> $DIR/weak-lang-item.rs:9:1
    |
 LL | extern crate core;
    | ^^^^^^^^^^^^^^^^^^ `core` reimported here
diff --git a/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs b/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs
index f8368ff6900..58a90a592c4 100644
--- a/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs
+++ b/src/test/ui/panic-runtime/abort-link-to-unwind-dylib.rs
@@ -1,5 +1,6 @@
 // build-fail
 // compile-flags:-C panic=abort -C prefer-dynamic
+// needs-unwind
 // ignore-musl - no dylibs here
 // ignore-emscripten
 // ignore-sgx no dynamic lib support
diff --git a/src/test/ui/panic-runtime/lto-unwind.rs b/src/test/ui/panic-runtime/lto-unwind.rs
index 6f39b76526b..24048ebe008 100644
--- a/src/test/ui/panic-runtime/lto-unwind.rs
+++ b/src/test/ui/panic-runtime/lto-unwind.rs
@@ -2,6 +2,7 @@
 #![allow(unused_variables)]
 
 // compile-flags:-C lto -C panic=unwind
+// needs-unwind
 // no-prefer-dynamic
 // ignore-emscripten no processes
 // ignore-sgx no processes
diff --git a/src/test/ui/panic-runtime/transitive-link-a-bunch.rs b/src/test/ui/panic-runtime/transitive-link-a-bunch.rs
index 1848c986e36..622535a75af 100644
--- a/src/test/ui/panic-runtime/transitive-link-a-bunch.rs
+++ b/src/test/ui/panic-runtime/transitive-link-a-bunch.rs
@@ -1,4 +1,5 @@
 // build-fail
+// needs-unwind
 // aux-build:panic-runtime-unwind.rs
 // aux-build:panic-runtime-abort.rs
 // aux-build:wants-panic-runtime-unwind.rs
diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort.rs b/src/test/ui/panic-runtime/want-unwind-got-abort.rs
index 894a5eb38b8..c48caaf0790 100644
--- a/src/test/ui/panic-runtime/want-unwind-got-abort.rs
+++ b/src/test/ui/panic-runtime/want-unwind-got-abort.rs
@@ -1,4 +1,5 @@
 // build-fail
+// needs-unwind
 // error-pattern:is incompatible with this crate's strategy of `unwind`
 // aux-build:panic-runtime-abort.rs
 // aux-build:panic-runtime-lang-items.rs
diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort2.rs b/src/test/ui/panic-runtime/want-unwind-got-abort2.rs
index 5955075bae5..7a2e48e2f10 100644
--- a/src/test/ui/panic-runtime/want-unwind-got-abort2.rs
+++ b/src/test/ui/panic-runtime/want-unwind-got-abort2.rs
@@ -1,4 +1,5 @@
 // build-fail
+// needs-unwind
 // error-pattern:is incompatible with this crate's strategy of `unwind`
 // aux-build:panic-runtime-abort.rs
 // aux-build:wants-panic-runtime-abort.rs
diff --git a/src/test/ui/parser/trait-object-delimiters.rs b/src/test/ui/parser/trait-object-delimiters.rs
new file mode 100644
index 00000000000..650ab572261
--- /dev/null
+++ b/src/test/ui/parser/trait-object-delimiters.rs
@@ -0,0 +1,17 @@
+// edition:2018
+
+fn foo1(_: &dyn Drop + AsRef<str>) {} //~ ERROR ambiguous `+` in a type
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+
+fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds
+
+fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{`
+//~^ ERROR expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
+//~| ERROR at least one trait is required for an object type
+
+fn foo4(_: &dyn <Drop + AsRef<str>>) {} //~ ERROR expected identifier, found `<`
+
+fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {} //~ ERROR invalid `dyn` keyword
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+
+fn main() {}
diff --git a/src/test/ui/parser/trait-object-delimiters.stderr b/src/test/ui/parser/trait-object-delimiters.stderr
new file mode 100644
index 00000000000..18b1b24122e
--- /dev/null
+++ b/src/test/ui/parser/trait-object-delimiters.stderr
@@ -0,0 +1,77 @@
+error: ambiguous `+` in a type
+  --> $DIR/trait-object-delimiters.rs:3:13
+   |
+LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(dyn Drop + AsRef<str>)`
+
+error: incorrect braces around trait bounds
+  --> $DIR/trait-object-delimiters.rs:6:17
+   |
+LL | fn foo2(_: &dyn (Drop + AsRef<str>)) {}
+   |                 ^                 ^
+   |
+help: remove the parentheses
+   |
+LL | fn foo2(_: &dyn Drop + AsRef<str>) {}
+   |                --               --
+
+error: expected parameter name, found `{`
+  --> $DIR/trait-object-delimiters.rs:8:17
+   |
+LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
+   |                 ^ expected parameter name
+
+error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
+  --> $DIR/trait-object-delimiters.rs:8:17
+   |
+LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
+   |                -^ expected one of 8 possible tokens
+   |                |
+   |                help: missing `,`
+
+error: expected identifier, found `<`
+  --> $DIR/trait-object-delimiters.rs:12:17
+   |
+LL | fn foo4(_: &dyn <Drop + AsRef<str>>) {}
+   |                 ^ expected identifier
+
+error: invalid `dyn` keyword
+  --> $DIR/trait-object-delimiters.rs:14:25
+   |
+LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
+   |                         ^^^ help: remove this keyword
+   |
+   = help: `dyn` is only needed at the start of a trait `+`-separated list
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-delimiters.rs:3:24
+   |
+LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
+   |                 ----   ^^^^^^^^^^ additional non-auto trait
+   |                 |
+   |                 first non-auto trait
+   |
+   = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
+   = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/trait-object-delimiters.rs:8:13
+   |
+LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
+   |             ^^^
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-delimiters.rs:14:29
+   |
+LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
+   |                  ----       ^^^^^^^^^^ additional non-auto trait
+   |                  |
+   |                  first non-auto trait
+   |
+   = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
+   = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0224, E0225.
+For more information about an error, try `rustc --explain E0224`.
diff --git a/src/test/ui/structs-enums/rec-align-u64.rs b/src/test/ui/structs-enums/rec-align-u64.rs
index 69544b1c060..cc6412e271a 100644
--- a/src/test/ui/structs-enums/rec-align-u64.rs
+++ b/src/test/ui/structs-enums/rec-align-u64.rs
@@ -35,6 +35,7 @@ struct Outer {
           target_os = "dragonfly",
           target_os = "emscripten",
           target_os = "freebsd",
+          target_os = "fuchsia",
           target_os = "linux",
           target_os = "macos",
           target_os = "netbsd",
diff --git a/src/test/ui/suggestions/unsized-function-parameter.fixed b/src/test/ui/suggestions/unsized-function-parameter.fixed
new file mode 100644
index 00000000000..18e93cb96cd
--- /dev/null
+++ b/src/test/ui/suggestions/unsized-function-parameter.fixed
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+
+fn foo1(bar: &str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| HELP unsized fn params are gated as an unstable feature
+//~| HELP function arguments must have a statically known size, borrowed types always have a known size
+
+fn foo2(_bar: &str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| HELP unsized fn params are gated as an unstable feature
+//~| HELP function arguments must have a statically known size, borrowed types always have a known size
+
+fn foo3(_: &str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| HELP unsized fn params are gated as an unstable feature
+//~| HELP function arguments must have a statically known size, borrowed types always have a known size
+
+fn main() {}
diff --git a/src/test/ui/suggestions/unsized-function-parameter.rs b/src/test/ui/suggestions/unsized-function-parameter.rs
new file mode 100644
index 00000000000..344ee71c1bc
--- /dev/null
+++ b/src/test/ui/suggestions/unsized-function-parameter.rs
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![allow(dead_code, unused_variables)]
+
+fn foo1(bar: str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| HELP unsized fn params are gated as an unstable feature
+//~| HELP function arguments must have a statically known size, borrowed types always have a known size
+
+fn foo2(_bar: str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| HELP unsized fn params are gated as an unstable feature
+//~| HELP function arguments must have a statically known size, borrowed types always have a known size
+
+fn foo3(_: str) {}
+//~^ ERROR the size for values of type `str` cannot be known at compilation time
+//~| HELP the trait `Sized` is not implemented for `str`
+//~| HELP unsized fn params are gated as an unstable feature
+//~| HELP function arguments must have a statically known size, borrowed types always have a known size
+
+fn main() {}
diff --git a/src/test/ui/suggestions/unsized-function-parameter.stderr b/src/test/ui/suggestions/unsized-function-parameter.stderr
new file mode 100644
index 00000000000..8cbd8bf3f34
--- /dev/null
+++ b/src/test/ui/suggestions/unsized-function-parameter.stderr
@@ -0,0 +1,42 @@
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-function-parameter.rs:5:9
+   |
+LL | fn foo1(bar: str) {}
+   |         ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn foo1(bar: &str) {}
+   |              ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-function-parameter.rs:11:9
+   |
+LL | fn foo2(_bar: str) {}
+   |         ^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn foo2(_bar: &str) {}
+   |               ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-function-parameter.rs:17:9
+   |
+LL | fn foo3(_: str) {}
+   |         ^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn foo3(_: &str) {}
+   |            ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/test-panic-abort-disabled.rs b/src/test/ui/test-panic-abort-disabled.rs
index 4adb161d9ee..874dbdb42c3 100644
--- a/src/test/ui/test-panic-abort-disabled.rs
+++ b/src/test/ui/test-panic-abort-disabled.rs
@@ -1,6 +1,6 @@
 // error-pattern:building tests with panic=abort is not supported
 // no-prefer-dynamic
-// compile-flags: --test -Cpanic=abort
+// compile-flags: --test -Cpanic=abort -Zpanic-abort-tests=no
 // run-flags: --test-threads=1
 
 // ignore-wasm no panic or subprocess support
diff --git a/src/test/ui/unwind-no-uwtable.rs b/src/test/ui/unwind-no-uwtable.rs
index f249d3f4574..0440cf488e8 100644
--- a/src/test/ui/unwind-no-uwtable.rs
+++ b/src/test/ui/unwind-no-uwtable.rs
@@ -1,4 +1,5 @@
 // run-pass
+// needs-unwind
 // ignore-windows target requires uwtable
 // ignore-wasm32-bare no proper panic=unwind support
 // compile-flags: -C panic=unwind -C force-unwind-tables=n
diff --git a/src/test/ui/x86stdcall.rs b/src/test/ui/x86stdcall.rs
index e1136807b3c..2bf4cfc5003 100644
--- a/src/test/ui/x86stdcall.rs
+++ b/src/test/ui/x86stdcall.rs
@@ -27,6 +27,7 @@ pub fn main() {
           target_os = "dragonfly",
           target_os = "emscripten",
           target_os = "freebsd",
+          target_os = "fuchsia",
           target_os = "linux",
           target_os = "macos",
           target_os = "netbsd",
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index b7693a3cb14..408c0b8da0b 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -171,6 +171,12 @@ impl fmt::Display for Debugger {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum PanicStrategy {
+    Unwind,
+    Abort,
+}
+
 /// Configuration for compiletest
 #[derive(Debug, Clone)]
 pub struct Config {
@@ -249,6 +255,9 @@ pub struct Config {
     /// Force the pass mode of a check/build/run-pass test to this mode.
     pub force_pass_mode: Option<PassMode>,
 
+    /// Explicitly enable or disable running.
+    pub run: Option<bool>,
+
     /// Write out a parseable log of tests that were run
     pub logfile: Option<PathBuf>,
 
@@ -262,6 +271,10 @@ pub struct Config {
     /// Flags to pass to the compiler when building for the target
     pub target_rustcflags: Option<String>,
 
+    /// What panic strategy the target is built with.  Unwind supports Abort, but
+    /// not vice versa.
+    pub target_panic: PanicStrategy,
+
     /// Target system to be tested
     pub target: String,
 
@@ -348,6 +361,15 @@ pub struct Config {
     pub npm: Option<String>,
 }
 
+impl Config {
+    pub fn run_enabled(&self) -> bool {
+        self.run.unwrap_or_else(|| {
+            // Auto-detect whether to run based on the platform.
+            !self.target.ends_with("-fuchsia")
+        })
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct TestPaths {
     pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index f31a24738df..983934d129a 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
 
 use tracing::*;
 
-use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode};
+use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode};
 use crate::util;
 use crate::{extract_cdb_version, extract_gdb_version};
 
@@ -85,6 +85,10 @@ impl EarlyProps {
                     props.ignore = true;
                 }
 
+                if !config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled") {
+                    props.ignore = true;
+                }
+
                 if !rustc_has_sanitizer_support
                     && config.parse_name_directive(ln, "needs-sanitizer-support")
                 {
@@ -111,6 +115,12 @@ impl EarlyProps {
                     props.ignore = true;
                 }
 
+                if config.target_panic == PanicStrategy::Abort
+                    && config.parse_name_directive(ln, "needs-unwind")
+                {
+                    props.ignore = true;
+                }
+
                 if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
                     props.ignore = true;
                 }
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index d1798a52df7..d53e19f2908 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -5,7 +5,9 @@
 
 extern crate test;
 
-use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
+use crate::common::{
+    expected_output_path, output_base_dir, output_relative_path, PanicStrategy, UI_EXTENSIONS,
+};
 use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths};
 use crate::util::logv;
 use getopts::Options;
@@ -87,6 +89,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "force {check,build,run}-pass tests to this mode.",
             "check | build | run",
         )
+        .optopt("", "run", "whether to execute run-* tests", "auto | always | never")
         .optflag("", "ignored", "run tests marked as ignored")
         .optflag("", "exact", "filters match exactly")
         .optopt(
@@ -96,8 +99,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
              (eg. emulator, valgrind)",
             "PROGRAM",
         )
-        .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
-        .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
+        .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
+        .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
+        .optopt("", "target-panic", "what panic strategy the target supports", "unwind | abort")
         .optflag("", "verbose", "run tests verbosely, showing all output")
         .optflag(
             "",
@@ -234,10 +238,21 @@ pub fn parse_config(args: Vec<String>) -> Config {
             mode.parse::<PassMode>()
                 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
         }),
+        run: matches.opt_str("run").and_then(|mode| match mode.as_str() {
+            "auto" => None,
+            "always" => Some(true),
+            "never" => Some(false),
+            _ => panic!("unknown `--run` option `{}` given", mode),
+        }),
         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
         runtool: matches.opt_str("runtool"),
-        host_rustcflags: matches.opt_str("host-rustcflags"),
-        target_rustcflags: matches.opt_str("target-rustcflags"),
+        host_rustcflags: Some(matches.opt_strs("host-rustcflags").join(" ")),
+        target_rustcflags: Some(matches.opt_strs("target-rustcflags").join(" ")),
+        target_panic: match matches.opt_str("target-panic").as_deref() {
+            Some("unwind") | None => PanicStrategy::Unwind,
+            Some("abort") => PanicStrategy::Abort,
+            _ => panic!("unknown `--target-panic` option `{}` given", mode),
+        },
         target,
         host: opt_str2(matches.opt_str("host")),
         cdb,
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index ecbaccf744d..c606aa1dfbf 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -259,6 +259,7 @@ pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
 pub fn compute_stamp_hash(config: &Config) -> String {
     let mut hash = DefaultHasher::new();
     config.stage_id.hash(&mut hash);
+    config.run.hash(&mut hash);
 
     match config.debugger {
         Some(Debugger::Cdb) => {
@@ -317,6 +318,7 @@ enum TestOutput {
 enum WillExecute {
     Yes,
     No,
+    Disabled,
 }
 
 /// Should `--emit metadata` be used?
@@ -357,14 +359,17 @@ impl<'test> TestCx<'test> {
     }
 
     fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
-        match self.config.mode {
-            Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => {
-                WillExecute::Yes
-            }
-            MirOpt if pm == Some(PassMode::Run) => WillExecute::Yes,
-            Ui | MirOpt => WillExecute::No,
+        let test_should_run = match self.config.mode {
+            Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => true,
+            MirOpt if pm == Some(PassMode::Run) => true,
+            Ui | MirOpt => false,
             mode => panic!("unimplemented for mode {:?}", mode),
-        }
+        };
+        if test_should_run { self.run_if_enabled() } else { WillExecute::No }
+    }
+
+    fn run_if_enabled(&self) -> WillExecute {
+        if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled }
     }
 
     fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
@@ -439,12 +444,17 @@ impl<'test> TestCx<'test> {
 
     fn run_rfail_test(&self) {
         let pm = self.pass_mode();
-        let proc_res = self.compile_test(WillExecute::Yes, self.should_emit_metadata(pm));
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
 
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
         let proc_res = self.exec_compiled_test();
 
         // The value our Makefile configures valgrind to return on failure
@@ -483,12 +493,17 @@ impl<'test> TestCx<'test> {
 
     fn run_rpass_test(&self) {
         let emit_metadata = self.should_emit_metadata(self.pass_mode());
-        let proc_res = self.compile_test(WillExecute::Yes, emit_metadata);
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, emit_metadata);
 
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
         // FIXME(#41968): Move this check to tidy?
         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
         assert!(
@@ -510,12 +525,17 @@ impl<'test> TestCx<'test> {
             return self.run_rpass_test();
         }
 
-        let mut proc_res = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let mut proc_res = self.compile_test(should_run, EmitMetadata::No);
 
         if !proc_res.status.success() {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
         let mut new_config = self.config.clone();
         new_config.runtool = new_config.valgrind_path.clone();
         let new_cx = TestCx { config: &new_config, ..*self };
@@ -732,10 +752,14 @@ impl<'test> TestCx<'test> {
 
     fn run_debuginfo_cdb_test_no_opt(&self) {
         // compile test file (it should have 'compile-flags:-g' in the header)
-        let compile_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let compile_result = self.compile_test(should_run, EmitMetadata::No);
         if !compile_result.status.success() {
             self.fatal_proc_rec("compilation failed!", &compile_result);
         }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
 
         let exe_file = self.make_exe_name();
 
@@ -826,10 +850,14 @@ impl<'test> TestCx<'test> {
         let mut cmds = commands.join("\n");
 
         // compile test file (it should have 'compile-flags:-g' in the header)
-        let compiler_run_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let compiler_run_result = self.compile_test(should_run, EmitMetadata::No);
         if !compiler_run_result.status.success() {
             self.fatal_proc_rec("compilation failed!", &compiler_run_result);
         }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
 
         let exe_file = self.make_exe_name();
 
@@ -1044,10 +1072,14 @@ impl<'test> TestCx<'test> {
 
     fn run_debuginfo_lldb_test_no_opt(&self) {
         // compile test file (it should have 'compile-flags:-g' in the header)
-        let compile_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+        let should_run = self.run_if_enabled();
+        let compile_result = self.compile_test(should_run, EmitMetadata::No);
         if !compile_result.status.success() {
             self.fatal_proc_rec("compilation failed!", &compile_result);
         }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
 
         let exe_file = self.make_exe_name();
 
@@ -1531,7 +1563,9 @@ impl<'test> TestCx<'test> {
         // Only use `make_exe_name` when the test ends up being executed.
         let output_file = match will_execute {
             WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
-            WillExecute::No => TargetLocation::ThisDirectory(self.output_base_dir()),
+            WillExecute::No | WillExecute::Disabled => {
+                TargetLocation::ThisDirectory(self.output_base_dir())
+            }
         };
 
         let allow_unused = match self.config.mode {
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index b604b39967e..064dd716521 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -44,12 +44,29 @@ const EXCEPTIONS: &[(&str, &str)] = &[
     ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
 ];
 
+const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
+    ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-jit", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
+    ("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
+    ("libloading", "ISC"),
+    ("mach", "BSD-2-Clause"),
+    ("regalloc", "Apache-2.0 WITH LLVM-exception"),
+    ("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
+];
+
 /// These are the root crates that are part of the runtime. The licenses for
 /// these and all their dependencies *must not* be in the exception list.
 const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
 
 /// Crates whose dependencies must be explicitly permitted.
-const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_middle", "rustc_codegen_llvm"];
+const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_driver", "rustc_codegen_llvm"];
 
 /// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
 ///
@@ -72,7 +89,10 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "cc",
     "cfg-if",
     "chalk-derive",
+    "chalk-engine",
     "chalk-ir",
+    "chalk-solve",
+    "chrono",
     "cmake",
     "compiler_builtins",
     "cpuid-bool",
@@ -92,6 +112,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "expect-test",
     "fake-simd",
     "filetime",
+    "fixedbitset",
     "flate2",
     "fortanix-sgx-abi",
     "fuchsia-zircon",
@@ -107,6 +128,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "indexmap",
     "instant",
     "itertools",
+    "itoa",
     "jobserver",
     "kernel32-sys",
     "lazy_static",
@@ -114,6 +136,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "libz-sys",
     "lock_api",
     "log",
+    "matchers",
     "maybe-uninit",
     "md-5",
     "measureme",
@@ -123,6 +146,8 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "memoffset",
     "miniz_oxide",
     "num_cpus",
+    "num-integer",
+    "num-traits",
     "object",
     "once_cell",
     "opaque-debug",
@@ -130,6 +155,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "parking_lot_core",
     "pathdiff",
     "perf-event-open-sys",
+    "petgraph",
     "pin-project-lite",
     "pkg-config",
     "polonius-engine",
@@ -147,22 +173,28 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "rand_xorshift",
     "redox_syscall",
     "regex",
+    "regex-automata",
     "regex-syntax",
     "remove_dir_all",
+    "rls-data",
+    "rls-span",
     "rustc-demangle",
     "rustc-hash",
     "rustc-rayon",
     "rustc-rayon-core",
     "rustc_version",
+    "ryu",
     "scoped-tls",
     "scopeguard",
     "semver",
     "semver-parser",
     "serde",
     "serde_derive",
+    "serde_json",
     "sha-1",
     "sha2",
     "smallvec",
+    "sharded-slab",
     "snap",
     "stable_deref_trait",
     "stacker",
@@ -172,9 +204,15 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "termcolor",
     "termize",
     "thread_local",
+    "time",
+    "tinyvec",
     "tracing",
     "tracing-attributes",
     "tracing-core",
+    "tracing-log",
+    "tracing-serde",
+    "tracing-subscriber",
+    "tracing-tree",
     "typenum",
     "unicode-normalization",
     "unicode-script",
@@ -193,6 +231,59 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "yansi-term",
 ];
 
+const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
+    "anyhow",
+    "ar",
+    "autocfg",
+    "bitflags",
+    "byteorder",
+    "cfg-if",
+    "cranelift-bforest",
+    "cranelift-codegen",
+    "cranelift-codegen-meta",
+    "cranelift-codegen-shared",
+    "cranelift-entity",
+    "cranelift-frontend",
+    "cranelift-jit",
+    "cranelift-module",
+    "cranelift-native",
+    "cranelift-object",
+    "crc32fast",
+    "errno",
+    "errno-dragonfly",
+    "gcc",
+    "gimli",
+    "hashbrown",
+    "indexmap",
+    "libc",
+    "libloading",
+    "log",
+    "mach",
+    "object",
+    "proc-macro2",
+    "quote",
+    "regalloc",
+    "region",
+    "rustc-hash",
+    "smallvec",
+    "syn",
+    "target-lexicon",
+    "thiserror",
+    "thiserror-impl",
+    "unicode-xid",
+    "winapi",
+    "winapi-i686-pc-windows-gnu",
+    "winapi-x86_64-pc-windows-gnu",
+];
+
+const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
+    // These two crates take quite a long time to build, so don't allow two versions of them
+    // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
+    // under control.
+    "cargo",
+    "rustc-ap-rustc_ast",
+];
+
 /// Dependency checks.
 ///
 /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
@@ -203,17 +294,39 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
         .manifest_path(root.join("Cargo.toml"))
         .features(cargo_metadata::CargoOpt::AllFeatures);
     let metadata = t!(cmd.exec());
-    check_exceptions(&metadata, bad);
-    check_dependencies(&metadata, bad);
-    check_crate_duplicate(&metadata, bad);
+    let runtime_ids = compute_runtime_crates(&metadata);
+    check_exceptions(&metadata, EXCEPTIONS, runtime_ids, bad);
+    check_dependencies(&metadata, PERMITTED_DEPENDENCIES, RESTRICTED_DEPENDENCY_CRATES, bad);
+    check_crate_duplicate(&metadata, FORBIDDEN_TO_HAVE_DUPLICATES, bad);
+
+    // Check rustc_codegen_cranelift independently as it has it's own workspace.
+    let mut cmd = cargo_metadata::MetadataCommand::new();
+    cmd.cargo_path(cargo)
+        .manifest_path(root.join("compiler/rustc_codegen_cranelift/Cargo.toml"))
+        .features(cargo_metadata::CargoOpt::AllFeatures);
+    let metadata = t!(cmd.exec());
+    let runtime_ids = HashSet::new();
+    check_exceptions(&metadata, EXCEPTIONS_CRANELIFT, runtime_ids, bad);
+    check_dependencies(
+        &metadata,
+        PERMITTED_CRANELIFT_DEPENDENCIES,
+        &["rustc_codegen_cranelift"],
+        bad,
+    );
+    check_crate_duplicate(&metadata, &[], bad);
 }
 
 /// Check that all licenses are in the valid list in `LICENSES`.
 ///
 /// Packages listed in `EXCEPTIONS` are allowed for tools.
-fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
+fn check_exceptions(
+    metadata: &Metadata,
+    exceptions: &[(&str, &str)],
+    runtime_ids: HashSet<&PackageId>,
+    bad: &mut bool,
+) {
     // Validate the EXCEPTIONS list hasn't changed.
-    for (name, license) in EXCEPTIONS {
+    for (name, license) in exceptions {
         // Check that the package actually exists.
         if !metadata.packages.iter().any(|p| p.name == *name) {
             tidy_error!(
@@ -225,13 +338,6 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
         }
         // Check that the license hasn't changed.
         for pkg in metadata.packages.iter().filter(|p| p.name == *name) {
-            if pkg.name == "fuchsia-cprng" {
-                // This package doesn't declare a license expression. Manual
-                // inspection of the license file is necessary, which appears
-                // to be BSD-3-Clause.
-                assert!(pkg.license.is_none());
-                continue;
-            }
             match &pkg.license {
                 None => {
                     tidy_error!(
@@ -242,14 +348,6 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
                 }
                 Some(pkg_license) => {
                     if pkg_license.as_str() != *license {
-                        if *name == "crossbeam-queue"
-                            && *license == "MIT/Apache-2.0 AND BSD-2-Clause"
-                        {
-                            // We have two versions of crossbeam-queue and both
-                            // are fine.
-                            continue;
-                        }
-
                         println!("dependency exception `{}` license has changed", name);
                         println!("    previously `{}` now `{}`", license, pkg_license);
                         println!("    update EXCEPTIONS for the new license");
@@ -260,8 +358,7 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
         }
     }
 
-    let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect();
-    let runtime_ids = compute_runtime_crates(metadata);
+    let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
 
     // Check if any package does not have a valid license.
     for pkg in &metadata.packages {
@@ -296,9 +393,14 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
 /// `true` if a check failed.
 ///
 /// Specifically, this checks that the dependencies are on the `PERMITTED_DEPENDENCIES`.
-fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
+fn check_dependencies(
+    metadata: &Metadata,
+    permitted_dependencies: &[&'static str],
+    restricted_dependency_crates: &[&'static str],
+    bad: &mut bool,
+) {
     // Check that the PERMITTED_DEPENDENCIES does not have unused entries.
-    for name in PERMITTED_DEPENDENCIES {
+    for name in permitted_dependencies {
         if !metadata.packages.iter().any(|p| p.name == *name) {
             tidy_error!(
                 bad,
@@ -309,12 +411,12 @@ fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
         }
     }
     // Get the list in a convenient form.
-    let permitted_dependencies: HashSet<_> = PERMITTED_DEPENDENCIES.iter().cloned().collect();
+    let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
 
     // Check dependencies.
     let mut visited = BTreeSet::new();
     let mut unapproved = BTreeSet::new();
-    for &krate in RESTRICTED_DEPENDENCY_CRATES.iter() {
+    for &krate in restricted_dependency_crates.iter() {
         let pkg = pkg_from_name(metadata, krate);
         let mut bad =
             check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
@@ -367,16 +469,12 @@ fn check_crate_dependencies<'a>(
 }
 
 /// Prevents multiple versions of some expensive crates.
-fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) {
-    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
-        // These two crates take quite a long time to build, so don't allow two versions of them
-        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
-        // under control.
-        "cargo",
-        "rustc-ap-rustc_ast",
-    ];
-
-    for &name in FORBIDDEN_TO_HAVE_DUPLICATES {
+fn check_crate_duplicate(
+    metadata: &Metadata,
+    forbidden_to_have_duplicates: &[&str],
+    bad: &mut bool,
+) {
+    for &name in forbidden_to_have_duplicates {
         let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect();
         match matches.len() {
             0 => {
@@ -456,16 +554,7 @@ fn normal_deps_of_r<'a>(
         .iter()
         .find(|n| &n.id == pkg_id)
         .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id));
-    // Don't care about dev-dependencies.
-    // Build dependencies *shouldn't* matter unless they do some kind of
-    // codegen. For now we'll assume they don't.
-    let deps = node.deps.iter().filter(|node_dep| {
-        node_dep
-            .dep_kinds
-            .iter()
-            .any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal)
-    });
-    for dep in deps {
+    for dep in &node.deps {
         normal_deps_of_r(resolve, &dep.pkg, result);
     }
 }