about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr_parsing/src/validate_attr.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs54
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs71
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs48
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs33
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0608.md15
-rw-r--r--compiler/rustc_expand/src/base.rs17
-rw-r--r--compiler/rustc_expand/src/expand.rs9
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs58
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs27
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs40
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs58
-rw-r--r--compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs13
-rw-r--r--compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs3
-rw-r--r--library/alloc/src/boxed.rs31
-rw-r--r--library/std/src/sys/thread_local/mod.rs2
-rw-r--r--library/std/src/sys/thread_local/native/eager.rs4
-rw-r--r--library/std/src/sys/thread_local/native/lazy.rs4
-rw-r--r--library/std/src/sys/thread_local/native/mod.rs12
-rw-r--r--library/std/src/sys/thread_local/no_threads.rs69
-rw-r--r--library/std/src/sys/thread_local/os.rs155
-rw-r--r--library/std/src/thread/local.rs233
-rw-r--r--library/std/src/thread/mod.rs1
-rw-r--r--library/std/tests/thread.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs4
-rw-r--r--src/ci/citool/Cargo.toml2
-rw-r--r--src/ci/citool/src/main.rs4
-rw-r--r--src/ci/citool/src/utils.rs18
m---------src/doc/book0
m---------src/doc/edition-guide0
m---------src/doc/nomicon0
m---------src/doc/reference0
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md14
-rw-r--r--src/doc/rustc/src/SUMMARY.md2
-rw-r--r--src/doc/rustc/src/platform-support.md6
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md50
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-none.md5
-rw-r--r--src/doc/rustc/src/platform-support/arm-linux.md4
-rw-r--r--src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md51
-rw-r--r--src/doc/rustc/src/platform-support/armv7a-none-eabi.md5
-rw-r--r--src/doc/rustc/src/platform-support/armv7r-none-eabi.md7
-rw-r--r--src/doc/rustc/src/platform-support/armv8r-none-eabihf.md7
-rw-r--r--src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/unknown-uefi.md8
-rw-r--r--src/etc/rust_analyzer_eglot.el8
-rw-r--r--src/etc/rust_analyzer_helix.toml14
-rw-r--r--src/etc/rust_analyzer_settings.json8
-rw-r--r--src/etc/rust_analyzer_zed.json23
-rw-r--r--src/tools/compiletest/src/common.rs6
-rw-r--r--src/tools/compiletest/src/directives.rs107
-rw-r--r--src/tools/compiletest/src/directives/tests.rs142
-rw-r--r--src/tools/compiletest/src/edition.rs35
-rw-r--r--src/tools/compiletest/src/lib.rs4
-rw-r--r--src/tools/miri/tests/pass/static_align.rs60
-rw-r--r--src/tools/miri/tests/pass/thread_local-panic.rs8
-rw-r--r--src/tools/miri/tests/pass/thread_local-panic.stderr5
-rw-r--r--tests/ui/attributes/unsafe/double-unsafe-attributes.rs2
-rw-r--r--tests/ui/attributes/unsafe/double-unsafe-attributes.stderr6
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs2
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr6
-rw-r--r--tests/ui/compiletest-self-test/compile-flags-incremental.rs17
-rw-r--r--tests/ui/feature-gates/feature-gate-static_align-thread_local.rs11
-rw-r--r--tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs16
-rw-r--r--tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs8
-rw-r--r--tests/ui/imports/auxiliary/same-res-ambigious-extern.rs11
-rw-r--r--tests/ui/imports/same-res-ambigious.fail.stderr20
-rw-r--r--tests/ui/imports/same-res-ambigious.nightly-fail.stderr20
-rw-r--r--tests/ui/imports/same-res-ambigious.rs11
-rw-r--r--tests/ui/indexing/index_message.stderr4
-rw-r--r--tests/ui/issues/issue-27842.stderr12
-rw-r--r--tests/ui/macros/macro-local-data-key-priv.stderr2
-rw-r--r--tests/ui/macros/macro-rules-attr-error.rs19
-rw-r--r--tests/ui/macros/macro-rules-attr-error.stderr14
-rw-r--r--tests/ui/parser/issues/issue-87086-colon-path-sep.rs3
-rw-r--r--tests/ui/parser/issues/issue-87086-colon-path-sep.stderr20
-rw-r--r--tests/ui/parser/macro/bad-macro-definition.rs3
-rw-r--r--tests/ui/parser/macro/bad-macro-definition.stderr8
-rw-r--r--tests/ui/parser/macro/macro-attr-bad.rs6
-rw-r--r--tests/ui/parser/macro/macro-attr-bad.stderr26
-rw-r--r--tests/ui/parser/macro/macro-derive-bad.rs3
-rw-r--r--tests/ui/parser/macro/macro-derive-bad.stderr8
-rw-r--r--tests/ui/parser/type-ascription-in-pattern.rs17
-rw-r--r--tests/ui/parser/type-ascription-in-pattern.stderr80
-rw-r--r--tests/ui/span/suggestion-non-ascii.stderr4
-rw-r--r--tests/ui/static/static-align.rs84
-rw-r--r--tests/ui/thread-local/long-docs.rs266
-rw-r--r--tests/ui/thread-local/no-unstable.rs17
-rw-r--r--tests/ui/thread-local/no-unstable.stderr57
-rw-r--r--tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr2
-rw-r--r--triagebot.toml4
96 files changed, 1952 insertions, 470 deletions
diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs
index 7a7624893bd..927417f89f8 100644
--- a/compiler/rustc_attr_parsing/src/validate_attr.rs
+++ b/compiler/rustc_attr_parsing/src/validate_attr.rs
@@ -207,10 +207,9 @@ pub fn check_attribute_safety(
             }
         }
 
-        // - Normal builtin attribute, or any non-builtin attribute
-        // - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is
-        //   not permitted on non-builtin attributes or normal builtin attributes
-        (Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => {
+        // - Normal builtin attribute
+        // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
+        (Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
             psess.dcx().emit_err(errors::InvalidAttrUnsafe {
                 span: unsafe_span,
                 name: attr_item.path.clone(),
@@ -224,9 +223,8 @@ pub fn check_attribute_safety(
         }
 
         // - Non-builtin attribute
-        // - No explicit `#[unsafe(..)]` written.
-        (None, Safety::Default) => {
-            // OK
+        (None, Safety::Unsafe(_) | Safety::Default) => {
+            // OK (not checked here)
         }
 
         (
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index cc09fa5b69b..93b1cf272ab 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -538,9 +538,7 @@ pub(crate) fn inline_asm_call<'ll>(
             bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)),
         )
     }));
-    let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) };
-    let md = bx.get_metadata_value(md);
-    llvm::LLVMSetMetadata(call, kind, md);
+    bx.cx.set_metadata_node(call, kind, &srcloc);
 
     Some(call)
 }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 5271d0b4bb8..e7cb18ab22d 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -1,12 +1,12 @@
 use std::borrow::{Borrow, Cow};
+use std::iter;
 use std::ops::Deref;
-use std::{iter, ptr};
 
 use rustc_ast::expand::typetree::FncTree;
 pub(crate) mod autodiff;
 pub(crate) mod gpu_offload;
 
-use libc::{c_char, c_uint, size_t};
+use libc::{c_char, c_uint};
 use rustc_abi as abi;
 use rustc_abi::{Align, Size, WrappingRange};
 use rustc_codegen_ssa::MemFlags;
@@ -396,10 +396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             md.push(weight(is_cold));
         }
 
-        unsafe {
-            let md_node = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len() as size_t);
-            self.cx.set_metadata(switch, llvm::MD_prof, md_node);
-        }
+        self.cx.set_metadata_node(switch, llvm::MD_prof, &md);
     }
 
     fn invoke(
@@ -801,22 +798,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             return;
         }
 
-        unsafe {
-            let llty = self.cx.val_ty(load);
-            let md = [
-                llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
-                llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
-            ];
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
-            self.set_metadata(load, llvm::MD_range, md);
-        }
+        let llty = self.cx.val_ty(load);
+        let md = [
+            llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
+            llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
+        ];
+        self.set_metadata_node(load, llvm::MD_range, &md);
     }
 
     fn nonnull_metadata(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_nonnull, md);
-        }
+        self.set_metadata_node(load, llvm::MD_nonnull, &[]);
     }
 
     fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
@@ -865,8 +856,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                     //
                     // [1]: https://llvm.org/docs/LangRef.html#store-instruction
                     let one = llvm::LLVMValueAsMetadata(self.cx.const_i32(1));
-                    let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, &one, 1);
-                    self.set_metadata(store, llvm::MD_nontemporal, md);
+                    self.set_metadata_node(store, llvm::MD_nontemporal, &[one]);
                 }
             }
             store
@@ -1381,10 +1371,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn set_invariant_load(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_invariant_load, md);
-        }
+        self.set_metadata_node(load, llvm::MD_invariant_load, &[]);
     }
 
     fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) {
@@ -1528,25 +1515,16 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
 }
 impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
     fn align_metadata(&mut self, load: &'ll Value, align: Align) {
-        unsafe {
-            let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
-            self.set_metadata(load, llvm::MD_align, md);
-        }
+        let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
+        self.set_metadata_node(load, llvm::MD_align, &md);
     }
 
     fn noundef_metadata(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_noundef, md);
-        }
+        self.set_metadata_node(load, llvm::MD_noundef, &[]);
     }
 
     pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(inst, llvm::MD_unpredictable, md);
-        }
+        self.set_metadata_node(inst, llvm::MD_unpredictable, &[]);
     }
 }
 impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 40375ef6510..b4ca85a26c8 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -494,16 +494,7 @@ impl<'ll> CodegenCx<'ll, '_> {
                 let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
                 let alloc = self.create_metadata(bytes);
                 let data = [section, alloc];
-                let meta =
-                    unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) };
-                let val = self.get_metadata_value(meta);
-                unsafe {
-                    llvm::LLVMAddNamedMetadataOperand(
-                        self.llmod,
-                        c"wasm.custom_sections".as_ptr(),
-                        val,
-                    )
-                };
+                self.module_add_named_metadata_node(self.llmod(), c"wasm.custom_sections", &data);
             }
         } else {
             base::set_link_section(g, attrs);
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index b1da6f7c740..922575dd63c 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -34,7 +34,7 @@ use smallvec::SmallVec;
 use crate::back::write::to_llvm_code_model;
 use crate::callee::get_fn;
 use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
-use crate::llvm::{Metadata, MetadataKindId};
+use crate::llvm::{Metadata, MetadataKindId, Module};
 use crate::type_::Type;
 use crate::value::Value;
 use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
@@ -495,14 +495,7 @@ pub(crate) unsafe fn create_module<'ll>(
         format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"));
 
     let name_metadata = cx.create_metadata(rustc_producer.as_bytes());
-
-    unsafe {
-        llvm::LLVMAddNamedMetadataOperand(
-            llmod,
-            c"llvm.ident".as_ptr(),
-            &cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)),
-        );
-    }
+    cx.module_add_named_metadata_node(llmod, c"llvm.ident", &[name_metadata]);
 
     // Emit RISC-V specific target-abi metadata
     // to workaround lld as the LTO plugin not
@@ -1002,6 +995,11 @@ impl CodegenCx<'_, '_> {
 }
 
 impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
+    /// Wrapper for `LLVMMDNodeInContext2`, i.e. `llvm::MDNode::get`.
+    pub(crate) fn md_node_in_context(&self, md_list: &[&'ll Metadata]) -> &'ll Metadata {
+        unsafe { llvm::LLVMMDNodeInContext2(self.llcx(), md_list.as_ptr(), md_list.len()) }
+    }
+
     /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
     pub(crate) fn set_metadata<'a>(
         &self,
@@ -1012,6 +1010,61 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
         let node = self.get_metadata_value(md);
         llvm::LLVMSetMetadata(val, kind_id, node);
     }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
+    /// - `LLVMSetMetadata` (to set that node as metadata of `kind_id` for `instruction`)
+    pub(crate) fn set_metadata_node(
+        &self,
+        instruction: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        self.set_metadata(instruction, kind_id, md);
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
+    /// - `LLVMAddNamedMetadataOperand` (to set that node as metadata of `kind_name` for `module`)
+    pub(crate) fn module_add_named_metadata_node(
+        &self,
+        module: &'ll Module,
+        kind_name: &CStr,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        let md_as_val = self.get_metadata_value(md);
+        unsafe { llvm::LLVMAddNamedMetadataOperand(module, kind_name.as_ptr(), md_as_val) };
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMRustGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`)
+    pub(crate) fn global_add_metadata_node(
+        &self,
+        global: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) };
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMGlobalSetMetadata` (to set that node as metadata of `kind_id` for `global`)
+    pub(crate) fn global_set_metadata_node(
+        &self,
+        global: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        unsafe { llvm::LLVMGlobalSetMetadata(global, kind_id, md) };
+    }
 }
 
 impl HasDataLayout for CodegenCx<'_, '_> {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index bc20c759413..2f9e7cae54f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -1607,17 +1607,11 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
     let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref);
     let typeid = cx.create_metadata(trait_ref_typeid.as_bytes());
 
-    unsafe {
-        let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
-        llvm::LLVMRustGlobalAddMetadata(
-            vtable,
-            llvm::MD_type,
-            llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()),
-        );
-        let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64));
-        let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1);
-        llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata);
-    }
+    let type_ = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
+    cx.global_add_metadata_node(vtable, llvm::MD_type, &type_);
+
+    let vcall_visibility = [llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64))];
+    cx.global_set_metadata_node(vtable, llvm::MD_vcall_visibility, &vcall_visibility);
 }
 
 /// Creates debug information for the given vtable, which is for the
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index 5b97898a4b8..1e5cf8374e3 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -302,26 +302,14 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
         let typeid_metadata = self.create_metadata(typeid);
-        unsafe {
-            let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
-            llvm::LLVMRustGlobalAddMetadata(
-                function,
-                llvm::MD_type,
-                llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
-            )
-        }
+        let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
+        self.global_add_metadata_node(function, llvm::MD_type, &v);
     }
 
     fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
         let typeid_metadata = self.create_metadata(typeid);
-        unsafe {
-            let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_type,
-                llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
-            )
-        }
+        let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
+        self.global_set_metadata_node(function, llvm::MD_type, &v);
     }
 
     fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> {
@@ -329,32 +317,12 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
-        let kcfi_type_metadata = self.const_u32(kcfi_typeid);
-        unsafe {
-            llvm::LLVMRustGlobalAddMetadata(
-                function,
-                llvm::MD_kcfi_type,
-                llvm::LLVMMDNodeInContext2(
-                    self.llcx,
-                    &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
-                    1,
-                ),
-            )
-        }
+        let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
+        self.global_add_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
     }
 
     fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
-        let kcfi_type_metadata = self.const_u32(kcfi_typeid);
-        unsafe {
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_kcfi_type,
-                llvm::LLVMMDNodeInContext2(
-                    self.llcx,
-                    &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
-                    1,
-                ),
-            )
-        }
+        let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
+        self.global_set_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index e644a43f883..ac123143738 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -17,7 +17,6 @@ use rustc_middle::middle::exported_symbols::{
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
-use rustc_span::sym;
 use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld};
 use tracing::{debug, warn};
 
@@ -1324,37 +1323,7 @@ struct WasmLd<'a> {
 
 impl<'a> WasmLd<'a> {
     fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> {
-        // If the atomics feature is enabled for wasm then we need a whole bunch
-        // of flags:
-        //
-        // * `--shared-memory` - the link won't even succeed without this, flags
-        //   the one linear memory as `shared`
-        //
-        // * `--max-memory=1G` - when specifying a shared memory this must also
-        //   be specified. We conservatively choose 1GB but users should be able
-        //   to override this with `-C link-arg`.
-        //
-        // * `--import-memory` - it doesn't make much sense for memory to be
-        //   exported in a threaded module because typically you're
-        //   sharing memory and instantiating the module multiple times. As a
-        //   result if it were exported then we'd just have no sharing.
-        //
-        // On wasm32-unknown-unknown, we also export symbols for glue code to use:
-        //    * `--export=*tls*` - when `#[thread_local]` symbols are used these
-        //      symbols are how the TLS segments are initialized and configured.
-        let mut wasm_ld = WasmLd { cmd, sess };
-        if sess.target_features.contains(&sym::atomics) {
-            wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]);
-            if sess.target.os == "unknown" || sess.target.os == "none" {
-                wasm_ld.link_args(&[
-                    "--export=__wasm_init_tls",
-                    "--export=__tls_size",
-                    "--export=__tls_align",
-                    "--export=__tls_base",
-                ]);
-            }
-        }
-        wasm_ld
+        WasmLd { cmd, sess }
     }
 }
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0608.md b/compiler/rustc_error_codes/src/error_codes/E0608.md
index d0ebc3a26f0..3c29484f575 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0608.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0608.md
@@ -1,5 +1,5 @@
-An attempt to use index on a type which doesn't implement the `std::ops::Index`
-trait was performed.
+Attempted to index a value whose type doesn't implement the
+`std::ops::Index` trait.
 
 Erroneous code example:
 
@@ -7,8 +7,8 @@ Erroneous code example:
 0u8[2]; // error: cannot index into a value of type `u8`
 ```
 
-To be able to index into a type it needs to implement the `std::ops::Index`
-trait. Example:
+Only values with types that implement the `std::ops::Index` trait
+can be indexed with square brackets. Example:
 
 ```
 let v: Vec<u8> = vec![0, 1, 2, 3];
@@ -16,3 +16,10 @@ let v: Vec<u8> = vec![0, 1, 2, 3];
 // The `Vec` type implements the `Index` trait so you can do:
 println!("{}", v[2]);
 ```
+
+Tuples and structs are indexed with dot (`.`), not with brackets (`[]`),
+and tuple element names are their positions:
+```ignore(pseudo code)
+// this (pseudo code) expression is true for any tuple:
+tuple == (tuple.0, tuple.1, ...)
+```
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 33b712e3aed..810a5a21a05 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -10,7 +10,7 @@ use rustc_ast::attr::{AttributeExt, MarkedAttrs};
 use rustc_ast::token::MetaVarKind;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{AssocCtxt, Visitor};
-use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
+use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::sync;
 use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
@@ -345,6 +345,21 @@ pub trait AttrProcMacro {
         annotation: TokenStream,
         annotated: TokenStream,
     ) -> Result<TokenStream, ErrorGuaranteed>;
+
+    // Default implementation for safe attributes; override if the attribute can be unsafe.
+    fn expand_with_safety<'cx>(
+        &self,
+        ecx: &'cx mut ExtCtxt<'_>,
+        safety: Safety,
+        span: Span,
+        annotation: TokenStream,
+        annotated: TokenStream,
+    ) -> Result<TokenStream, ErrorGuaranteed> {
+        if let Safety::Unsafe(span) = safety {
+            ecx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
+        }
+        self.expand(ecx, span, annotation, annotated)
+    }
 }
 
 impl<F> AttrProcMacro for F
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 172bc3d1d9f..3dfa3cdcc35 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -812,11 +812,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         _ => item.to_tokens(),
                     };
                     let attr_item = attr.get_normal_item();
+                    let safety = attr_item.unsafety;
                     if let AttrArgs::Eq { .. } = attr_item.args {
                         self.cx.dcx().emit_err(UnsupportedKeyValue { span });
                     }
                     let inner_tokens = attr_item.args.inner_tokens();
-                    match expander.expand(self.cx, span, inner_tokens, tokens) {
+                    match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) {
                         Ok(tok_result) => {
                             let fragment = self.parse_ast_fragment(
                                 tok_result,
@@ -840,6 +841,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
                     }
                 } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext {
+                    // `LegacyAttr` is only used for builtin attribute macros, which have their
+                    // safety checked by `check_builtin_meta_item`, so we don't need to check
+                    // `unsafety` here.
                     match validate_attr::parse_meta(&self.cx.sess.psess, &attr) {
                         Ok(meta) => {
                             let item_clone = macro_stats.then(|| item.clone());
@@ -882,6 +886,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         }
                     }
                 } else if let SyntaxExtensionKind::NonMacroAttr = ext {
+                    if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety {
+                        self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
+                    }
                     // `-Zmacro-stats` ignores these because they don't do any real expansion.
                     self.cx.expanded_inert_attrs.mark(&attr);
                     item.visit_attrs(|attrs| attrs.insert(pos, attr));
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index d4504ba720e..c548cea537f 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -8,7 +8,7 @@ use rustc_ast::token::NtPatKind::*;
 use rustc_ast::token::TokenKind::*;
 use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
 use rustc_ast::tokenstream::{self, DelimSpan, TokenStream};
-use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
+use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId, Safety};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
@@ -131,6 +131,7 @@ pub(super) enum MacroRule {
     Func { lhs: Vec<MatcherLoc>, lhs_span: Span, rhs: mbe::TokenTree },
     /// An attr rule, for use with `#[m]`
     Attr {
+        unsafe_rule: bool,
         args: Vec<MatcherLoc>,
         args_span: Span,
         body: Vec<MatcherLoc>,
@@ -248,7 +249,18 @@ impl TTMacroExpander for MacroRulesMacroExpander {
 impl AttrProcMacro for MacroRulesMacroExpander {
     fn expand(
         &self,
+        _cx: &mut ExtCtxt<'_>,
+        _sp: Span,
+        _args: TokenStream,
+        _body: TokenStream,
+    ) -> Result<TokenStream, ErrorGuaranteed> {
+        unreachable!("`expand` called on `MacroRulesMacroExpander`, expected `expand_with_safety`")
+    }
+
+    fn expand_with_safety(
+        &self,
         cx: &mut ExtCtxt<'_>,
+        safety: Safety,
         sp: Span,
         args: TokenStream,
         body: TokenStream,
@@ -260,6 +272,7 @@ impl AttrProcMacro for MacroRulesMacroExpander {
             self.node_id,
             self.name,
             self.transparency,
+            safety,
             args,
             body,
             &self.rules,
@@ -408,6 +421,7 @@ fn expand_macro_attr(
     node_id: NodeId,
     name: Ident,
     transparency: Transparency,
+    safety: Safety,
     args: TokenStream,
     body: TokenStream,
     rules: &[MacroRule],
@@ -429,13 +443,26 @@ fn expand_macro_attr(
     // Track nothing for the best performance.
     match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) {
         Ok((i, rule, named_matches)) => {
-            let MacroRule::Attr { rhs, .. } = rule else {
+            let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else {
                 panic!("try_macro_match_attr returned non-attr rule");
             };
             let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
                 cx.dcx().span_bug(sp, "malformed macro rhs");
             };
 
+            match (safety, unsafe_rule) {
+                (Safety::Default, false) | (Safety::Unsafe(_), true) => {}
+                (Safety::Default, true) => {
+                    cx.dcx().span_err(sp, "unsafe attribute invocation requires `unsafe`");
+                }
+                (Safety::Unsafe(span), false) => {
+                    cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute invocation");
+                }
+                (Safety::Safe(span), _) => {
+                    cx.dcx().span_bug(span, "unexpected `safe` keyword");
+                }
+            }
+
             let id = cx.current_expansion.id;
             let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id)
                 .map_err(|e| e.emit())?;
@@ -681,6 +708,11 @@ pub fn compile_declarative_macro(
     let mut rules = Vec::new();
 
     while p.token != token::Eof {
+        let unsafe_rule = p.eat_keyword_noexpect(kw::Unsafe);
+        let unsafe_keyword_span = p.prev_token.span;
+        if unsafe_rule && let Some(guar) = check_no_eof(sess, &p, "expected `attr`") {
+            return dummy_syn_ext(guar);
+        }
         let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
             kinds |= MacroKinds::ATTR;
             if !features.macro_attr() {
@@ -705,6 +737,10 @@ pub fn compile_declarative_macro(
                 feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable")
                     .emit();
             }
+            if unsafe_rule {
+                sess.dcx()
+                    .span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
+            }
             if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
                 return dummy_syn_ext(guar);
             }
@@ -730,6 +766,10 @@ pub fn compile_declarative_macro(
             (None, true)
         } else {
             kinds |= MacroKinds::BANG;
+            if unsafe_rule {
+                sess.dcx()
+                    .span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
+            }
             (None, false)
         };
         let lhs_tt = p.parse_token_tree();
@@ -741,10 +781,10 @@ pub fn compile_declarative_macro(
         if let Some(guar) = check_no_eof(sess, &p, "expected right-hand side of macro rule") {
             return dummy_syn_ext(guar);
         }
-        let rhs_tt = p.parse_token_tree();
-        let rhs_tt = parse_one_tt(rhs_tt, RulePart::Body, sess, node_id, features, edition);
-        check_emission(check_rhs(sess, &rhs_tt));
-        check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs_tt));
+        let rhs = p.parse_token_tree();
+        let rhs = parse_one_tt(rhs, RulePart::Body, sess, node_id, features, edition);
+        check_emission(check_rhs(sess, &rhs));
+        check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs));
         let lhs_span = lhs_tt.span();
         // Convert the lhs into `MatcherLoc` form, which is better for doing the
         // actual matching.
@@ -760,11 +800,11 @@ pub fn compile_declarative_macro(
             };
             let args = mbe::macro_parser::compute_locs(&delimited.tts);
             let body_span = lhs_span;
-            rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
+            rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs });
         } else if is_derive {
-            rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
+            rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs });
         } else {
-            rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
+            rules.push(MacroRule::Func { lhs, lhs_span, rhs });
         }
         if p.token == token::Eof {
             break;
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index d1ce0afddf9..f9cdc923670 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -3551,35 +3551,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                     // Try to give some advice about indexing tuples.
                     if let ty::Tuple(types) = base_t.kind() {
-                        let mut needs_note = true;
-                        // If the index is an integer, we can show the actual
-                        // fixed expression:
+                        err.help(
+                            "tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.",
+                        );
+                        // If index is an unsuffixed integer, show the fixed expression:
                         if let ExprKind::Lit(lit) = idx.kind
                             && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node
-                            && i.get()
-                                < types
-                                    .len()
-                                    .try_into()
-                                    .expect("expected tuple index to be < usize length")
+                            && i.get() < types.len().try_into().expect("tuple length fits in u128")
                         {
                             err.span_suggestion(
                                 brackets_span,
-                                "to access tuple elements, use",
+                                format!("to access tuple element `{i}`, use"),
                                 format!(".{i}"),
                                 Applicability::MachineApplicable,
                             );
-                            needs_note = false;
-                        } else if let ExprKind::Path(..) = idx.peel_borrows().kind {
-                            err.span_label(
-                                idx.span,
-                                "cannot access tuple elements at a variable index",
-                            );
-                        }
-                        if needs_note {
-                            err.help(
-                                "to access tuple elements, use tuple indexing \
-                                        syntax (e.g., `tuple.0`)",
-                            );
                         }
                     }
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index a28af7833c3..a9fbd0fa33d 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -2748,28 +2748,7 @@ impl<'a> Parser<'a> {
         if token::Colon != self.token.kind {
             return first_pat;
         }
-        if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
-            || !self.look_ahead(1, |token| token.is_non_reserved_ident())
-        {
-            let mut snapshot_type = self.create_snapshot_for_diagnostic();
-            snapshot_type.bump(); // `:`
-            match snapshot_type.parse_ty() {
-                Err(inner_err) => {
-                    inner_err.cancel();
-                }
-                Ok(ty) => {
-                    let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
-                        return first_pat;
-                    };
-                    err.span_label(ty.span, "specifying the type of a pattern isn't supported");
-                    self.restore_snapshot(snapshot_type);
-                    let span = first_pat.span.to(ty.span);
-                    first_pat = self.mk_pat(span, PatKind::Wild);
-                    err.emit();
-                }
-            }
-            return first_pat;
-        }
+
         // The pattern looks like it might be a path with a `::` -> `:` typo:
         // `match foo { bar:baz => {} }`
         let colon_span = self.token.span;
@@ -2857,7 +2836,13 @@ impl<'a> Parser<'a> {
                                 Applicability::MaybeIncorrect,
                             );
                         } else {
-                            first_pat = self.mk_pat(new_span, PatKind::Wild);
+                            first_pat = self.mk_pat(
+                                new_span,
+                                PatKind::Err(
+                                    self.dcx()
+                                        .span_delayed_bug(colon_span, "recovered bad path pattern"),
+                                ),
+                            );
                         }
                         self.restore_snapshot(snapshot_pat);
                     }
@@ -2870,7 +2855,14 @@ impl<'a> Parser<'a> {
                         err.span_label(ty.span, "specifying the type of a pattern isn't supported");
                         self.restore_snapshot(snapshot_type);
                         let new_span = first_pat.span.to(ty.span);
-                        first_pat = self.mk_pat(new_span, PatKind::Wild);
+                        first_pat =
+                            self.mk_pat(
+                                new_span,
+                                PatKind::Err(self.dcx().span_delayed_bug(
+                                    colon_span,
+                                    "recovered bad pattern with type",
+                                )),
+                            );
                     }
                 }
                 err.emit();
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 81a5d48d94e..8046abcd70b 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3612,7 +3612,7 @@ impl<'a> Parser<'a> {
         self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1)
     }
 
-    fn is_certainly_not_a_block(&self) -> bool {
+    fn is_likely_struct_lit(&self) -> bool {
         // `{ ident, ` and `{ ident: ` cannot start a block.
         self.look_ahead(1, |t| t.is_ident())
             && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
@@ -3624,24 +3624,50 @@ impl<'a> Parser<'a> {
         path: &ast::Path,
     ) -> Option<PResult<'a, Box<Expr>>> {
         let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
-        if struct_allowed || self.is_certainly_not_a_block() {
-            if let Err(err) = self.expect(exp!(OpenBrace)) {
-                return Some(Err(err));
+        match (struct_allowed, self.is_likely_struct_lit()) {
+            // A struct literal isn't expected and one is pretty much assured not to be present. The
+            // only situation that isn't detected is when a struct with a single field was attempted
+            // in a place where a struct literal wasn't expected, but regular parser errors apply.
+            // Happy path.
+            (false, false) => None,
+            (true, _) => {
+                // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for
+                // any kind of recovery. Happy path.
+                if let Err(err) = self.expect(exp!(OpenBrace)) {
+                    return Some(Err(err));
+                }
+                Some(self.parse_expr_struct(qself.clone(), path.clone(), true))
             }
-            let expr = self.parse_expr_struct(qself.clone(), path.clone(), true);
-            if let (Ok(expr), false) = (&expr, struct_allowed) {
-                // This is a struct literal, but we don't can't accept them here.
-                self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
-                    span: expr.span,
-                    sub: errors::StructLiteralNotAllowedHereSugg {
-                        left: path.span.shrink_to_lo(),
-                        right: expr.span.shrink_to_hi(),
-                    },
-                });
+            (false, true) => {
+                // We have something like `match foo { bar,` or `match foo { bar:`, which means the
+                // user might have meant to write a struct literal as part of the `match`
+                // discriminant. This is done purely for error recovery.
+                let snapshot = self.create_snapshot_for_diagnostic();
+                if let Err(err) = self.expect(exp!(OpenBrace)) {
+                    return Some(Err(err));
+                }
+                match self.parse_expr_struct(qself.clone(), path.clone(), false) {
+                    Ok(expr) => {
+                        // This is a struct literal, but we don't accept them here.
+                        self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
+                            span: expr.span,
+                            sub: errors::StructLiteralNotAllowedHereSugg {
+                                left: path.span.shrink_to_lo(),
+                                right: expr.span.shrink_to_hi(),
+                            },
+                        });
+                        Some(Ok(expr))
+                    }
+                    Err(err) => {
+                        // We couldn't parse a valid struct, rollback and let the parser emit an
+                        // error elsewhere.
+                        err.cancel();
+                        self.restore_snapshot(snapshot);
+                        None
+                    }
+                }
             }
-            return Some(expr);
         }
-        None
     }
 
     pub(super) fn parse_struct_fields(
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs
index a0eb4a254fc..06e5cfaed92 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs
@@ -6,11 +6,18 @@ use crate::spec::{Cc, LinkerFlavor, Target, TargetMetadata, base};
 pub(crate) fn target() -> Target {
     let mut options = base::linux_wasm::opts();
 
-    options
-        .add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &["--export-memory", "--shared-memory"]);
+    options.add_pre_link_args(
+        LinkerFlavor::WasmLld(Cc::No),
+        &["--export-memory", "--shared-memory", "--max-memory=1073741824"],
+    );
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::Yes),
-        &["--target=wasm32-wasi-threads", "-Wl,--export-memory,", "-Wl,--shared-memory"],
+        &[
+            "--target=wasm32-wasi-threads",
+            "-Wl,--export-memory,",
+            "-Wl,--shared-memory",
+            "-Wl,--max-memory=1073741824",
+        ],
     );
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
index 44d906a507d..c735c72cb1c 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
@@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
 
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::No),
-        &["--import-memory", "--export-memory", "--shared-memory"],
+        &["--import-memory", "--export-memory", "--shared-memory", "--max-memory=1073741824"],
     );
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::Yes),
@@ -28,6 +28,7 @@ pub(crate) fn target() -> Target {
             "-Wl,--import-memory",
             "-Wl,--export-memory,",
             "-Wl,--shared-memory",
+            "-Wl,--max-memory=1073741824",
         ],
     );
 
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 5a63d90b95f..49ff768bed1 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -619,6 +619,37 @@ impl<T, A: Allocator> Box<T, A> {
     pub fn into_inner(boxed: Self) -> T {
         *boxed
     }
+
+    /// Consumes the `Box` without consuming its allocation, returning the wrapped value and a `Box`
+    /// to the uninitialized memory where the wrapped value used to live.
+    ///
+    /// This can be used together with [`write`](Box::write) to reuse the allocation for multiple
+    /// boxed values.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(box_take)]
+    ///
+    /// let c = Box::new(5);
+    ///
+    /// // take the value out of the box
+    /// let (value, uninit) = Box::take(c);
+    /// assert_eq!(value, 5);
+    ///
+    /// // reuse the box for a second value
+    /// let c = Box::write(uninit, 6);
+    /// assert_eq!(*c, 6);
+    /// ```
+    #[unstable(feature = "box_take", issue = "147212")]
+    pub fn take(boxed: Self) -> (T, Box<mem::MaybeUninit<T>, A>) {
+        unsafe {
+            let (raw, alloc) = Box::into_raw_with_allocator(boxed);
+            let value = raw.read();
+            let uninit = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc);
+            (value, uninit)
+        }
+    }
 }
 
 impl<T> Box<[T]> {
diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs
index d5c795093cf..f7f051b1add 100644
--- a/library/std/src/sys/thread_local/mod.rs
+++ b/library/std/src/sys/thread_local/mod.rs
@@ -42,7 +42,7 @@ cfg_select! {
     }
     _ => {
         mod os;
-        pub use os::{Storage, thread_local_inner};
+        pub use os::{Storage, thread_local_inner, value_align};
         pub(crate) use os::{LocalPointer, local_pointer};
     }
 }
diff --git a/library/std/src/sys/thread_local/native/eager.rs b/library/std/src/sys/thread_local/native/eager.rs
index fd48c4f7202..23abad645c1 100644
--- a/library/std/src/sys/thread_local/native/eager.rs
+++ b/library/std/src/sys/thread_local/native/eager.rs
@@ -10,9 +10,11 @@ enum State {
 }
 
 #[allow(missing_debug_implementations)]
+#[repr(C)]
 pub struct Storage<T> {
-    state: Cell<State>,
+    // This field must be first, for correctness of `#[rustc_align_static]`
     val: UnsafeCell<T>,
+    state: Cell<State>,
 }
 
 impl<T> Storage<T> {
diff --git a/library/std/src/sys/thread_local/native/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs
index b556dd9aa25..02939a74fc0 100644
--- a/library/std/src/sys/thread_local/native/lazy.rs
+++ b/library/std/src/sys/thread_local/native/lazy.rs
@@ -27,9 +27,11 @@ enum State<D> {
 }
 
 #[allow(missing_debug_implementations)]
+#[repr(C)]
 pub struct Storage<T, D> {
-    state: Cell<State<D>>,
+    // This field must be first, for correctness of `#[rustc_align_static]`
     value: UnsafeCell<MaybeUninit<T>>,
+    state: Cell<State<D>>,
 }
 
 impl<T, D> Storage<T, D>
diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs
index a5dffe3c458..5dc14240804 100644
--- a/library/std/src/sys/thread_local/native/mod.rs
+++ b/library/std/src/sys/thread_local/native/mod.rs
@@ -54,7 +54,7 @@ pub macro thread_local_inner {
     // test in `tests/thread.rs` if these types are renamed.
 
     // Used to generate the `LocalKey` value for const-initialized thread locals.
-    (@key $t:ty, const $init:expr) => {{
+    (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
         const __INIT: $t = $init;
 
         unsafe {
@@ -62,6 +62,7 @@ pub macro thread_local_inner {
                 if $crate::mem::needs_drop::<$t>() {
                     |_| {
                         #[thread_local]
+                        $(#[$align_attr])*
                         static VAL: $crate::thread::local_impl::EagerStorage<$t>
                             = $crate::thread::local_impl::EagerStorage::new(__INIT);
                         VAL.get()
@@ -69,6 +70,7 @@ pub macro thread_local_inner {
                 } else {
                     |_| {
                         #[thread_local]
+                        $(#[$align_attr])*
                         static VAL: $t = __INIT;
                         &VAL
                     }
@@ -78,7 +80,7 @@ pub macro thread_local_inner {
     }},
 
     // used to generate the `LocalKey` value for `thread_local!`
-    (@key $t:ty, $init:expr) => {{
+    (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
         #[inline]
         fn __init() -> $t {
             $init
@@ -89,6 +91,7 @@ pub macro thread_local_inner {
                 if $crate::mem::needs_drop::<$t>() {
                     |init| {
                         #[thread_local]
+                        $(#[$align_attr])*
                         static VAL: $crate::thread::local_impl::LazyStorage<$t, ()>
                             = $crate::thread::local_impl::LazyStorage::new();
                         VAL.get_or_init(init, __init)
@@ -96,6 +99,7 @@ pub macro thread_local_inner {
                 } else {
                     |init| {
                         #[thread_local]
+                        $(#[$align_attr])*
                         static VAL: $crate::thread::local_impl::LazyStorage<$t, !>
                             = $crate::thread::local_impl::LazyStorage::new();
                         VAL.get_or_init(init, __init)
@@ -104,10 +108,6 @@ pub macro thread_local_inner {
             })
         }
     }},
-    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
-        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
-            $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
-    },
 }
 
 #[rustc_macro_transparency = "semitransparent"]
diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs
index 4da01a84acf..409dfb19518 100644
--- a/library/std/src/sys/thread_local/no_threads.rs
+++ b/library/std/src/sys/thread_local/no_threads.rs
@@ -2,6 +2,7 @@
 //! thread locals and we can instead just use plain statics!
 
 use crate::cell::{Cell, UnsafeCell};
+use crate::mem::MaybeUninit;
 use crate::ptr;
 
 #[doc(hidden)]
@@ -11,12 +12,13 @@ use crate::ptr;
 #[rustc_macro_transparency = "semitransparent"]
 pub macro thread_local_inner {
     // used to generate the `LocalKey` value for const-initialized thread locals
-    (@key $t:ty, const $init:expr) => {{
+    (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
         const __INIT: $t = $init;
 
         // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
         unsafe {
             $crate::thread::LocalKey::new(|_| {
+                $(#[$align_attr])*
                 static VAL: $crate::thread::local_impl::EagerStorage<$t> =
                     $crate::thread::local_impl::EagerStorage { value: __INIT };
                 &VAL.value
@@ -25,27 +27,22 @@ pub macro thread_local_inner {
     }},
 
     // used to generate the `LocalKey` value for `thread_local!`
-    (@key $t:ty, $init:expr) => {{
+    (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
         #[inline]
         fn __init() -> $t { $init }
 
         unsafe {
-            use $crate::thread::LocalKey;
-            use $crate::thread::local_impl::LazyStorage;
-
-            LocalKey::new(|init| {
-                static VAL: LazyStorage<$t> = LazyStorage::new();
+            $crate::thread::LocalKey::new(|init| {
+                $(#[$align_attr])*
+                static VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new();
                 VAL.get(init, __init)
             })
         }
     }},
-    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
-        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
-            $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
-    },
 }
 
 #[allow(missing_debug_implementations)]
+#[repr(transparent)] // Required for correctness of `#[rustc_align_static]`
 pub struct EagerStorage<T> {
     pub value: T,
 }
@@ -53,14 +50,27 @@ pub struct EagerStorage<T> {
 // SAFETY: the target doesn't have threads.
 unsafe impl<T> Sync for EagerStorage<T> {}
 
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum State {
+    Initial,
+    Alive,
+    Destroying,
+}
+
 #[allow(missing_debug_implementations)]
+#[repr(C)]
 pub struct LazyStorage<T> {
-    value: UnsafeCell<Option<T>>,
+    // This field must be first, for correctness of `#[rustc_align_static]`
+    value: UnsafeCell<MaybeUninit<T>>,
+    state: Cell<State>,
 }
 
 impl<T> LazyStorage<T> {
     pub const fn new() -> LazyStorage<T> {
-        LazyStorage { value: UnsafeCell::new(None) }
+        LazyStorage {
+            value: UnsafeCell::new(MaybeUninit::uninit()),
+            state: Cell::new(State::Initial),
+        }
     }
 
     /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +80,39 @@ impl<T> LazyStorage<T> {
     /// has occurred.
     #[inline]
     pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
-        let value = unsafe { &*self.value.get() };
-        match value {
-            Some(v) => v,
-            None => self.initialize(i, f),
+        if self.state.get() == State::Alive {
+            self.value.get() as *const T
+        } else {
+            self.initialize(i, f)
         }
     }
 
     #[cold]
     fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
         let value = i.and_then(Option::take).unwrap_or_else(f);
-        // Destroy the old value, after updating the TLS variable as the
-        // destructor might reference it.
+
+        // Destroy the old value if it is initialized
         // FIXME(#110897): maybe panic on recursive initialization.
+        if self.state.get() == State::Alive {
+            self.state.set(State::Destroying);
+            // Safety: we check for no initialization during drop below
+            unsafe {
+                ptr::drop_in_place(self.value.get() as *mut T);
+            }
+            self.state.set(State::Initial);
+        }
+
+        // Guard against initialization during drop
+        if self.state.get() == State::Destroying {
+            panic!("Attempted to initialize thread-local while it is being dropped");
+        }
+
         unsafe {
-            self.value.get().replace(Some(value));
+            self.value.get().write(MaybeUninit::new(value));
         }
-        // SAFETY: we just set this to `Some`.
-        unsafe { (*self.value.get()).as_ref().unwrap_unchecked() }
+        self.state.set(State::Alive);
+
+        self.value.get() as *const T
     }
 }
 
diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs
index fe6af27db3a..88bb5ae7c65 100644
--- a/library/std/src/sys/thread_local/os.rs
+++ b/library/std/src/sys/thread_local/os.rs
@@ -1,8 +1,12 @@
 use super::key::{Key, LazyKey, get, set};
 use super::{abort_on_dtor_unwind, guard};
+use crate::alloc::{self, Layout};
 use crate::cell::Cell;
 use crate::marker::PhantomData;
-use crate::ptr;
+use crate::mem::ManuallyDrop;
+use crate::ops::Deref;
+use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
+use crate::ptr::{self, NonNull};
 
 #[doc(hidden)]
 #[allow_internal_unstable(thread_local_internals)]
@@ -10,17 +14,12 @@ use crate::ptr;
 #[unstable(feature = "thread_local_internals", issue = "none")]
 #[rustc_macro_transparency = "semitransparent"]
 pub macro thread_local_inner {
-    // used to generate the `LocalKey` value for const-initialized thread locals
-    (@key $t:ty, const $init:expr) => {
-        $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR })
-    },
-
     // NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user
     // provided type or type alias with a matching name. Please update the shadowing test in
     // `tests/thread.rs` if these types are renamed.
 
     // used to generate the `LocalKey` value for `thread_local!`.
-    (@key $t:ty, $init:expr) => {{
+    (@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{
         #[inline]
         fn __init() -> $t { $init }
 
@@ -29,37 +28,148 @@ pub macro thread_local_inner {
         // in `tests/thread.rs` if these types are renamed.
         unsafe {
             $crate::thread::LocalKey::new(|init| {
-                static VAL: $crate::thread::local_impl::Storage<$t>
+                static VAL: $crate::thread::local_impl::Storage<$t, {
+                    $({
+                        // Ensure that attributes have valid syntax
+                        // and that the proper feature gate is enabled
+                        $(#[$($align_attr)*])+
+                        #[allow(unused)]
+                        static DUMMY: () = ();
+                    })?
+
+                    #[allow(unused_mut)]
+                    let mut final_align = $crate::thread::local_impl::value_align::<$t>();
+                    $($($crate::thread::local_impl::thread_local_inner!(@align final_align, $($align_attr)*);)+)?
+                    final_align
+                }>
                     = $crate::thread::local_impl::Storage::new();
                 VAL.get(init, __init)
             })
         }
     }},
-    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
-        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
-            $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
+
+    // process a single `rustc_align_static` attribute
+    (@align $final_align:ident, rustc_align_static($($align:tt)*) $(, $($attr_rest:tt)+)?) => {
+        let new_align: $crate::primitive::usize = $($align)*;
+        if new_align > $final_align {
+            $final_align = new_align;
+        }
+
+        $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
+    },
+
+    // process a single `cfg_attr` attribute
+    // by translating it into a `cfg`ed block and recursing.
+    // https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate
+
+    (@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
+        #[cfg(true)]
+        {
+            $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
+        }
+
+        $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
+    },
+
+    (@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
+        #[cfg(false)]
+        {
+            $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
+        }
+
+        $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
+    },
+
+    (@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
+        #[cfg($cfg_pred)]
+        {
+            $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
+        }
+
+        $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
     },
 }
 
 /// Use a regular global static to store this key; the state provided will then be
 /// thread-local.
+/// INVARIANT: ALIGN must be a valid alignment, and no less than `value_align::<T>`.
 #[allow(missing_debug_implementations)]
-pub struct Storage<T> {
+pub struct Storage<T, const ALIGN: usize> {
     key: LazyKey,
     marker: PhantomData<Cell<T>>,
 }
 
-unsafe impl<T> Sync for Storage<T> {}
+unsafe impl<T, const ALIGN: usize> Sync for Storage<T, ALIGN> {}
 
+#[repr(C)]
 struct Value<T: 'static> {
+    // This field must be first, for correctness of `#[rustc_align_static]`
     value: T,
     // INVARIANT: if this value is stored under a TLS key, `key` must be that `key`.
     key: Key,
 }
 
-impl<T: 'static> Storage<T> {
-    pub const fn new() -> Storage<T> {
-        Storage { key: LazyKey::new(Some(destroy_value::<T>)), marker: PhantomData }
+pub const fn value_align<T: 'static>() -> usize {
+    crate::mem::align_of::<Value<T>>()
+}
+
+/// Equivalent to `Box<Value<T>>`, but potentially over-aligned.
+struct AlignedBox<T: 'static, const ALIGN: usize> {
+    ptr: NonNull<Value<T>>,
+}
+
+impl<T: 'static, const ALIGN: usize> AlignedBox<T, ALIGN> {
+    #[inline]
+    fn new(v: Value<T>) -> Self {
+        let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
+
+        let ptr: *mut Value<T> = (unsafe { alloc::alloc(layout) }).cast();
+        let Some(ptr) = NonNull::new(ptr) else {
+            alloc::handle_alloc_error(layout);
+        };
+        unsafe { ptr.write(v) };
+        Self { ptr }
+    }
+
+    #[inline]
+    fn into_raw(b: Self) -> *mut Value<T> {
+        let md = ManuallyDrop::new(b);
+        md.ptr.as_ptr()
+    }
+
+    #[inline]
+    unsafe fn from_raw(ptr: *mut Value<T>) -> Self {
+        Self { ptr: unsafe { NonNull::new_unchecked(ptr) } }
+    }
+}
+
+impl<T: 'static, const ALIGN: usize> Deref for AlignedBox<T, ALIGN> {
+    type Target = Value<T>;
+
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        unsafe { &*(self.ptr.as_ptr()) }
+    }
+}
+
+impl<T: 'static, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> {
+    #[inline]
+    fn drop(&mut self) {
+        let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
+
+        unsafe {
+            let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place()));
+            alloc::dealloc(self.ptr.as_ptr().cast(), layout);
+            if let Err(payload) = unwind_result {
+                resume_unwind(payload);
+            }
+        }
+    }
+}
+
+impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> {
+    pub const fn new() -> Storage<T, ALIGN> {
+        Storage { key: LazyKey::new(Some(destroy_value::<T, ALIGN>)), marker: PhantomData }
     }
 
     /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -95,8 +205,11 @@ impl<T: 'static> Storage<T> {
             return ptr::null();
         }
 
-        let value = Box::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key });
-        let ptr = Box::into_raw(value);
+        let value = AlignedBox::<T, ALIGN>::new(Value {
+            value: i.and_then(Option::take).unwrap_or_else(f),
+            key,
+        });
+        let ptr = AlignedBox::into_raw(value);
 
         // SAFETY:
         // * key came from a `LazyKey` and is thus correct.
@@ -114,7 +227,7 @@ impl<T: 'static> Storage<T> {
             // initializer has already returned and the next scope only starts
             // after we return the pointer. Therefore, there can be no references
             // to the old value.
-            drop(unsafe { Box::from_raw(old) });
+            drop(unsafe { AlignedBox::<T, ALIGN>::from_raw(old) });
         }
 
         // SAFETY: We just created this value above.
@@ -122,7 +235,7 @@ impl<T: 'static> Storage<T> {
     }
 }
 
-unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
+unsafe extern "C" fn destroy_value<T: 'static, const ALIGN: usize>(ptr: *mut u8) {
     // SAFETY:
     //
     // The OS TLS ensures that this key contains a null value when this
@@ -133,7 +246,7 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
     // Note that to prevent an infinite loop we reset it back to null right
     // before we return from the destructor ourselves.
     abort_on_dtor_unwind(|| {
-        let ptr = unsafe { Box::from_raw(ptr as *mut Value<T>) };
+        let ptr = unsafe { AlignedBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) };
         let key = ptr.key;
         // SAFETY: `key` is the TLS key `ptr` was stored under.
         unsafe { set(key, ptr::without_provenance_mut(1)) };
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index 0a6f2e5d508..4259a4d1f3b 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -132,6 +132,216 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
     }
 }
 
+#[doc(hidden)]
+#[allow_internal_unstable(thread_local_internals)]
+#[unstable(feature = "thread_local_internals", issue = "none")]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro thread_local_process_attrs {
+
+    // Parse `cfg_attr` to figure out whether it's a `rustc_align_static`.
+    // Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested.
+
+    // finished parsing the `cfg_attr`, it had no `rustc_align_static`
+    (
+        [] [$(#[$($prev_other_attrs:tt)*])*];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
+        [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]];
+            $($rest)*
+        );
+    ),
+
+    // finished parsing the `cfg_attr`, it had nothing but `rustc_align_static`
+    (
+        [$(#[$($prev_align_attrs:tt)*])+] [];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
+        [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs_ret)*  #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*];
+            $($rest)*
+        );
+    ),
+
+    // finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs
+    (
+        [$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
+        [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs_ret)*  #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]];
+            $($rest)*
+        );
+    ),
+
+    // it's a `rustc_align_static`
+    (
+        [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] };
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*];
+            @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
+            $($rest)*
+        );
+    ),
+
+    // it's a nested `cfg_attr(true, ...)`; recurse into RHS
+    (
+        [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [] [];
+            @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*];
+            @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
+            $($rest)*
+        );
+    ),
+
+    // it's a nested `cfg_attr(false, ...)`; recurse into RHS
+    (
+        [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [] [];
+            @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*];
+            @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
+            $($rest)*
+        );
+    ),
+
+
+    // it's a nested `cfg_attr(..., ...)`; recurse into RHS
+    (
+        [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [] [];
+            @processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] };
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*];
+            @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
+            $($rest)*
+        );
+    ),
+
+    // it's some other attribute
+    (
+        [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
+        @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] };
+        $($rest:tt)*
+    ) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]];
+            @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
+            $($rest)*
+        );
+    ),
+
+
+    // Separate attributes into `rustc_align_static` and everything else:
+
+    // `rustc_align_static` attribute
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*];
+            $($rest)*
+        );
+    ),
+
+    // `cfg_attr(true, ...)` attribute; parse it
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [] [];
+            @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*];
+            $($rest)*
+        );
+    ),
+
+    // `cfg_attr(false, ...)` attribute; parse it
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [] [];
+            @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*];
+            $($rest)*
+        );
+    ),
+
+    // `cfg_attr(..., ...)` attribute; parse it
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [] [];
+            @processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] };
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*];
+            $($rest)*
+        );
+    ),
+
+    // doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+];
+            $vis static $($rest)*
+        );
+    ),
+
+    // 8 lines of doc comment; process them all at once to avoid blowing recursion limit
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
+     #[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*]
+     #[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*]
+     $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs)*] [$($prev_other_attrs)*
+            #[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*]
+            #[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]];
+            $($rest)*
+        );
+    ),
+
+    // other attribute
+    ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => (
+        $crate::thread::local_impl::thread_local_process_attrs!(
+            [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]];
+            $($rest)*
+        );
+    ),
+
+
+    // Delegate to `thread_local_inner` once attributes are fully categorized:
+
+    // process `const` declaration and recurse
+    ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => (
+        $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init);
+
+        $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
+    ),
+
+    // process non-`const` declaration and recurse
+    ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => (
+        $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init);
+
+        $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
+    ),
+}
+
 /// Declare a new thread local storage key of type [`std::thread::LocalKey`].
 ///
 /// # Syntax
@@ -182,28 +392,11 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
 #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")]
 #[allow_internal_unstable(thread_local_internals)]
 macro_rules! thread_local {
-    // empty (base case for the recursion)
     () => {};
 
-    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block; $($rest:tt)*) => (
-        $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
-        $crate::thread_local!($($rest)*);
-    );
-
-    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block) => (
-        $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
-    );
-
-    // process multiple declarations
-    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
-        $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
-        $crate::thread_local!($($rest)*);
-    );
-
-    // handle a single declaration
-    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
-        $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
-    );
+    ($($tt:tt)+) => {
+        $crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+);
+    };
 }
 
 /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 1768369792a..fd7cce3f97d 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -207,6 +207,7 @@ pub use self::local::{AccessError, LocalKey};
 #[doc(hidden)]
 #[unstable(feature = "thread_local_internals", issue = "none")]
 pub mod local_impl {
+    pub use super::local::thread_local_process_attrs;
     pub use crate::sys::thread_local::*;
 }
 
diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs
index 29f220d8a70..dc8eadd7514 100644
--- a/library/std/tests/thread.rs
+++ b/library/std/tests/thread.rs
@@ -66,6 +66,8 @@ fn thread_local_hygeiene() {
     type Storage = ();
     type LazyStorage = ();
     type EagerStorage = ();
+    #[allow(non_camel_case_types)]
+    type usize = ();
     thread_local! {
         static A: LocalKey = const { () };
         static B: Storage = const { () };
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 9f9af1d9abe..4b0080f1c80 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -587,6 +587,7 @@ Select which editor you would like to set up [default: None]: ";
                 "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9",
                 "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce",
                 "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1",
+                "e260553b71e4773c30a63c4b23b42b279fc73e72f95b775c47b7b7c511c51595",
             ],
             EditorKind::Helix => &[
                 "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233",
@@ -594,6 +595,7 @@ Select which editor you would like to set up [default: None]: ";
                 "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5",
                 "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5",
                 "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1",
+                "eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6",
             ],
             EditorKind::Vim | EditorKind::VsCode => &[
                 "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
@@ -610,6 +612,7 @@ Select which editor you would like to set up [default: None]: ";
                 "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893",
                 "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12",
                 "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054",
+                "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47",
             ],
             EditorKind::Zed => &[
                 "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c",
@@ -617,6 +620,7 @@ Select which editor you would like to set up [default: None]: ";
                 "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26",
                 "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6",
                 "f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382",
+                "5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f",
             ],
         }
     }
diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml
index f61243a4d71..078b877e44b 100644
--- a/src/ci/citool/Cargo.toml
+++ b/src/ci/citool/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "citool"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 anyhow = "1"
diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs
index fe1b36673a1..255d39846da 100644
--- a/src/ci/citool/src/main.rs
+++ b/src/ci/citool/src/main.rs
@@ -24,7 +24,7 @@ use crate::github::JobInfoResolver;
 use crate::jobs::RunType;
 use crate::metrics::{JobMetrics, download_auto_job_metrics, download_job_metrics, load_metrics};
 use crate::test_dashboard::generate_test_dashboard;
-use crate::utils::{load_env_var, output_details};
+use crate::utils::{init_submodule_if_needed, load_env_var, output_details};
 
 const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/..");
 pub const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker");
@@ -121,6 +121,8 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any
         (key.clone(), value)
     }));
 
+    init_submodule_if_needed("src/llvm-project/")?;
+
     let mut cmd = Command::new(Path::new(DOCKER_DIRECTORY).join("run.sh"));
     cmd.arg(job.image());
     cmd.envs(custom_env);
diff --git a/src/ci/citool/src/utils.rs b/src/ci/citool/src/utils.rs
index 3176cb62f60..43b220255dc 100644
--- a/src/ci/citool/src/utils.rs
+++ b/src/ci/citool/src/utils.rs
@@ -1,5 +1,7 @@
 use std::borrow::Cow;
+use std::convert::AsRef;
 use std::path::Path;
+use std::process::Command;
 
 use anyhow::Context;
 
@@ -34,3 +36,19 @@ where
 pub fn normalize_path_delimiters(name: &str) -> Cow<'_, str> {
     if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() }
 }
+
+pub fn init_submodule_if_needed<P: AsRef<Path>>(path_to_submodule: P) -> anyhow::Result<()> {
+    let path_to_submodule = path_to_submodule.as_ref();
+
+    if let Ok(mut iter) = path_to_submodule.read_dir()
+        && iter.any(|entry| entry.is_ok())
+    {
+        // Seems like the submodule is already initialized, nothing to be done here.
+        return Ok(());
+    }
+    let mut child = Command::new("git")
+        .args(&["submodule", "update", "--init"])
+        .arg(path_to_submodule)
+        .spawn()?;
+    if !child.wait()?.success() { Err(anyhow::anyhow!("git command failed")) } else { Ok(()) }
+}
diff --git a/src/doc/book b/src/doc/book
-Subproject 33f1af40cc44dde7e3e892f7a508e6f427d2cbc
+Subproject 1d7c3e6abec2d5a9bfac798b29b7855b9502542
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
-Subproject aa6ce337c0adf7a63e33960d184270f2a45ab9e
+Subproject e2ed891f00361efc26616d82590b1c85d7a8920
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject f17a018b9989430967d1c58e9a12c51169abc74
+Subproject 23fc2682f8fcb887f77d0eaabba708809f834c1
diff --git a/src/doc/reference b/src/doc/reference
-Subproject cc7247d8dfaef4c39000bb12c55c32ba5b5ba97
+Subproject e11adf6016a362766eea5a3f9832e193994dd0c
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 4be78fac4ec..3b6c8917464 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -262,6 +262,20 @@ Consider writing the test as a proper incremental test instead.
 
 </div>
 
+#### The edition directive
+
+The `//@ edition` directive can take an exact edition, a bounded half-open range of editions or a left-bounded half-open range of editions, this affects which edition is used by `./x test` to run the test. For example:
+
+- A test with the `//@ edition: 2018` directive will only run under the 2018 edition.
+- A test with the `//@ edition: 2015..2021` directive can be run under both the 2015 and 2018 editions. However, CI will only run the test with the lowest edition possible (2015 in this case).
+- A test with the `//@ edition: 2018..` directive will run under any edition greater or equal than 2018. However, CI will only run the test with the lowest edition possible (2018 in this case).
+
+You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`:
+
+- A test with the `//@ edition: 2018` will run with the 2018 edition. 
+- A test with the `//@ edition: 2015..2021` will be run with the 2015 edition. 
+- A test with the `//@ edition: 2018..` will run with the 2018 edition. 
+
 ### Rustdoc
 
 | Directive   | Explanation                                                  | Supported test suites                   | Possible values           |
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 67882bb3813..a9ce738a013 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -47,6 +47,7 @@
     - [\*-apple-watchos](platform-support/apple-watchos.md)
     - [\*-apple-visionos](platform-support/apple-visionos.md)
     - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
+    - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md)
     - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md)
     - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md)
     - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md)
@@ -67,6 +68,7 @@
     - [arm\*-unknown-linux-\*](./platform-support/arm-linux.md)
         - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
         - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
+        - [armv7-unknown-linux-gnueabi](platform-support/armv7-unknown-linux-gnueabi.md)
         - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
         - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
     - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index d0b6ed51bc1..263ea1ddb42 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -34,7 +34,7 @@ target | notes
 -------|-------
 [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
 [`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC
-`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+)
+[`aarch64-unknown-linux-gnu`](platform-support/aarch64-unknown-linux-gnu.md) | ARM64 Linux (kernel 4.1+, glibc 2.17+)
 [`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
 `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI]
 [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+)
@@ -93,7 +93,7 @@ target | notes
 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
 `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17)
-`armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17)
+[`armv7-unknown-linux-gnueabihf`](platform-support/armv7-unknown-linux-gnueabi.md) | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17)
 [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony
 [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36), LSX required
 [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5), LSX required
@@ -159,7 +159,7 @@ target | std | notes
 [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23)
 `armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3
 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android
-`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
+[`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
 `armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3
 `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat
 [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md
new file mode 100644
index 00000000000..2003a3cb9ea
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md
@@ -0,0 +1,50 @@
+# `aarch64-unknown-linux-gnu`
+
+**Tier: 1 (with Host Tools)**
+
+Target for 64-bit little endian ARMv8-A Linux 4.1+ programs using glibc 2.17+.
+
+## Target maintainers
+
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
+
+## Requirements
+
+Building the target itself requires a 64-bit little endian ARMv8-A compiler that is supported by
+`cc-rs`.
+
+## Building the target
+
+The target can be built by enabling it for a `rustc` build:
+
+```toml
+[build]
+target = ["aarch64-unknown-linux-gnu"]
+```
+
+If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the
+`bootstrap.toml`:
+
+```toml
+[target.aarch64-unknown-linux-musl]
+cc = "aarch64-linux-gnu-gcc"
+cxx = "aarch64-linux-gnu-g++"
+ar = "aarch64-linux-gnu-ar"
+linker = "aarch64-linux-gnu-gcc"
+```
+
+## Building Rust programs
+
+This target is distributed through `rustup`, and otherwise requires no special configuration.
+
+## Cross-compilation
+
+This target can be cross-compiled from any host.
+
+## Testing
+
+This target can be tested as normal with `x.py` on a 64-bit little endian ARMv8-A host or via QEMU
+emulation.
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md
index 7e18e8c157f..3d776677d23 100644
--- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md
@@ -14,9 +14,12 @@ Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team]
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/arm-linux.md b/src/doc/rustc/src/platform-support/arm-linux.md
index 5f40743f3d0..c461a1a3403 100644
--- a/src/doc/rustc/src/platform-support/arm-linux.md
+++ b/src/doc/rustc/src/platform-support/arm-linux.md
@@ -14,8 +14,8 @@ Linux (but not Android). Those targets are:
 * [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md)
 * `armv5te-unknown-linux-musleabi`
 * `armv5te-unknown-linux-uclibceabi`
-* `armv7-unknown-linux-gnueabi`
-* `armv7-unknown-linux-gnueabihf`
+* [`armv7-unknown-linux-gnueabi`](armv7-unknown-linux-gnueabi.md)
+* [`armv7-unknown-linux-gnueabihf`](armv7-unknown-linux-gnueabi.md)
 * `armv7-unknown-linux-musleabi`
 * `armv7-unknown-linux-musleabihf`
 * `armv7-unknown-linux-ohos`
diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md
new file mode 100644
index 00000000000..c2fe63a4908
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md
@@ -0,0 +1,51 @@
+# `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-gnueabihf`
+
+* **Tier: 2 (with Host Tools)** for `armv7-unknown-linux-gnueabihf`
+* **Tier: 2** for `armv7-unknown-linux-gnueabi`
+
+Target for 32-bit little endian ARMv7-A Linux 3.2+ programs using glibc 2.17+.
+
+## Target maintainers
+
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
+
+## Requirements
+
+Building the targets themselves requires a 32-bit little endian ARMv7-A compiler that is supported
+by `cc-rs`.
+
+## Building the target
+
+These targets can be built by enabling it for a `rustc` build:
+
+```toml
+[build]
+target = ["armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabi"]
+```
+
+If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the
+`bootstrap.toml`:
+
+```toml
+[target.aarch64-unknown-linux-musl]
+cc = "arm-linux-gnu-gcc"
+cxx = "arm-linux-gnu-g++"
+ar = "arm-linux-gnu-ar"
+linker = "arm-linux-gnu-gcc"
+```
+
+## Building Rust programs
+
+These targets is distributed through `rustup`, and otherwise requires no special configuration.
+
+## Cross-compilation
+
+These targets can be cross-compiled from any host.
+
+## Testing
+
+These targets can be tested as normal with `x.py` on a 32-bit little endian ARMv7-A host or via
+QEMU emulation.
diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md
index 3dadda86a5f..22278a0a7dc 100644
--- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md
@@ -19,9 +19,12 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team]
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md
index c1252b4a4bf..9429eb6ab8a 100644
--- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md
@@ -15,10 +15,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[@chrisnc](https://github.com/chrisnc)
-[Rust Embedded Devices Working Group Arm Team]
+- [@chrisnc](https://github.com/chrisnc)
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
index 0d5a36c3ee2..e465eb79f49 100644
--- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
+++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
@@ -17,10 +17,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[@chrisnc](https://github.com/chrisnc)
-[Rust Embedded Devices Working Group Arm Team]
+- [@chrisnc](https://github.com/chrisnc)
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
index 98dcf9bd396..192e013d3a4 100644
--- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
@@ -22,7 +22,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md
index d8f3970c8bf..b04cb7bfacf 100644
--- a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md
@@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv7-M].
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md
index b16d450275d..104520854b4 100644
--- a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md
@@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv8-M] Baseline.
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
index a2d515d07ea..5cc535ce376 100644
--- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
@@ -26,7 +26,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md
index 9587590d12d..e8989616b84 100644
--- a/src/doc/rustc/src/platform-support/unknown-uefi.md
+++ b/src/doc/rustc/src/platform-support/unknown-uefi.md
@@ -13,8 +13,12 @@ Available targets:
 
 ## Target maintainers
 
-[@dvdhrm](https://github.com/dvdhrm)
-[@nicholasbishop](https://github.com/nicholasbishop)
+- [@dvdhrm](https://github.com/dvdhrm)
+- [@nicholasbishop](https://github.com/nicholasbishop)
+- (for `aarch64-unknown-uefi` only) [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el
index 3151cb1a6e7..e5abf67235a 100644
--- a/src/etc/rust_analyzer_eglot.el
+++ b/src/etc/rust_analyzer_eglot.el
@@ -6,6 +6,8 @@
                                        :overrideCommand ["python3"
                                                          "x.py"
                                                          "check"
+                                                         "--build-dir"
+                                                         "build-rust-analyzer"
                                                          "--json-output"])
                  :linkedProjects ["Cargo.toml"
                                   "compiler/rustc_codegen_cranelift/Cargo.toml"
@@ -13,9 +15,9 @@
                                   "library/Cargo.toml"
                                   "src/bootstrap/Cargo.toml"
                                   "src/tools/rust-analyzer/Cargo.toml"]
-                 :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
+                 :rustfmt ( :overrideCommand ["build-rust-analyzer/host/rustfmt/bin/rustfmt"
                                               "--edition=2024"])
-                 :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+                 :procMacro ( :server "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
                                       :enable t)
                  :cargo ( :buildScripts ( :enable t
                                                   :invocationLocation "root"
@@ -23,6 +25,8 @@
                                                   :overrideCommand ["python3"
                                                                     "x.py"
                                                                     "check"
+                                                                    "--build-dir"
+                                                                    "build-rust-analyzer"
                                                                     "--json-output"
                                                                     "--compile-time-deps"])]
                                         :sysrootSrc "./library"
diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml
index 8c1782a1abc..e2de2a374cb 100644
--- a/src/etc/rust_analyzer_helix.toml
+++ b/src/etc/rust_analyzer_helix.toml
@@ -1,10 +1,10 @@
 # This config uses a separate build directory for rust-analyzer,
 # so that r-a's checks don't block user `x` commands and vice-verse.
-# R-a's build directory is located in `build/rust-analyzer`.
+# R-a's build directory is located in `build-rust-analyzer`.
 #
 # To build rustfmt and proc macro server for r-a run the following command:
 # ```
-# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build/rust-analyzer
+# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build-rust-analyzer
 # ```
 
 [language-server.rust-analyzer.config]
@@ -26,17 +26,17 @@ overrideCommand = [
     "check",
     "--json-output",
     "--build-dir",
-    "build/rust-analyzer",
+    "build-rust-analyzer",
 ]
 
 [language-server.rust-analyzer.config.rustfmt]
 overrideCommand = [
-    "build/rust-analyzer/host/rustfmt/bin/rustfmt",
+    "build-rust-analyzer/host/rustfmt/bin/rustfmt",
     "--edition=2024"
 ]
 
 [language-server.rust-analyzer.config.procMacro]
-server = "build/rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+server = "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
 enable = true
 
 [language-server.rust-analyzer.config.rustc]
@@ -58,6 +58,6 @@ overrideCommand = [
     "check",
     "--json-output",
     "--build-dir",
-    "build/rust-analyzer",
-    "--compile-time-deps"
+    "build-rust-analyzer",
+    "--compile-time-deps",
 ]
diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json
index b31169857c5..f89e8a2df18 100644
--- a/src/etc/rust_analyzer_settings.json
+++ b/src/etc/rust_analyzer_settings.json
@@ -5,6 +5,8 @@
         "python3",
         "x.py",
         "check",
+        "--build-dir",
+        "build-rust-analyzer",
         "--json-output"
     ],
     "rust-analyzer.linkedProjects": [
@@ -16,10 +18,10 @@
         "src/tools/rust-analyzer/Cargo.toml"
     ],
     "rust-analyzer.rustfmt.overrideCommand": [
-        "${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
+        "${workspaceFolder}/build-rust-analyzer/host/rustfmt/bin/rustfmt",
         "--edition=2024"
     ],
-    "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
+    "rust-analyzer.procMacro.server": "${workspaceFolder}/build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv",
     "rust-analyzer.procMacro.enable": true,
     "rust-analyzer.cargo.buildScripts.enable": true,
     "rust-analyzer.cargo.buildScripts.invocationStrategy": "once",
@@ -27,6 +29,8 @@
         "python3",
         "x.py",
         "check",
+        "--build-dir",
+        "build-rust-analyzer",
         "--json-output",
         "--compile-time-deps"
     ],
diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json
index 7eace92500e..d98a082a9b8 100644
--- a/src/etc/rust_analyzer_zed.json
+++ b/src/etc/rust_analyzer_zed.json
@@ -7,7 +7,15 @@
             "enable": true,
             "invocationLocation": "root",
             "invocationStrategy": "once",
-            "overrideCommand": ["python3", "x.py", "check", "--json-output", "--compile-time-deps"]
+            "overrideCommand": [
+              "python3",
+              "x.py",
+              "check",
+              "--build-dir",
+              "build-rust-analyzer",
+              "--compile-time-deps",
+              "--json-output"
+            ]
           },
           "extraEnv": {
             "RUSTC_BOOTSTRAP": "1"
@@ -17,7 +25,14 @@
         "check": {
           "invocationLocation": "root",
           "invocationStrategy": "once",
-          "overrideCommand": ["python3", "x.py", "check", "--json-output"]
+          "overrideCommand": [
+            "python3",
+            "x.py",
+            "check",
+            "--json-output",
+            "--build-dir",
+            "build-rust-analyzer"
+          ]
         },
         "linkedProjects": [
           "Cargo.toml",
@@ -29,14 +44,14 @@
         ],
         "procMacro": {
           "enable": true,
-          "server": "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+          "server": "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
         },
         "rustc": {
           "source": "./Cargo.toml"
         },
         "rustfmt": {
           "overrideCommand": [
-            "build/host/rustfmt/bin/rustfmt",
+            "build-rust-analyzer/host/rustfmt/bin/rustfmt",
             "--edition=2024"
           ]
         },
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 6d959948918..65db816ad1a 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -7,6 +7,7 @@ use build_helper::git::GitConfig;
 use camino::{Utf8Path, Utf8PathBuf};
 use semver::Version;
 
+use crate::edition::Edition;
 use crate::executor::ColorConfig;
 use crate::fatal;
 use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum};
@@ -612,10 +613,7 @@ pub struct Config {
     pub git_hash: bool,
 
     /// The default Rust edition.
-    ///
-    /// FIXME: perform stronger validation for this. There are editions that *definitely* exists,
-    /// but there might also be "future" edition.
-    pub edition: Option<String>,
+    pub edition: Option<Edition>,
 
     // Configuration for various run-make tests frobbing things like C compilers or querying about
     // various LLVM component information.
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index e6916610190..a79978d036c 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -16,10 +16,11 @@ use crate::directives::directive_names::{
     KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES,
 };
 use crate::directives::needs::CachedNeedsConditions;
+use crate::edition::{Edition, parse_edition};
 use crate::errors::ErrorKind;
 use crate::executor::{CollectedTestDesc, ShouldPanic};
-use crate::help;
 use crate::util::static_regex;
+use crate::{fatal, help};
 
 pub(crate) mod auxiliary;
 mod cfg;
@@ -415,10 +416,18 @@ impl TestProps {
                         config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile)
                     {
                         let flags = split_flags(&flags);
-                        for flag in &flags {
+                        for (i, flag) in flags.iter().enumerate() {
                             if flag == "--edition" || flag.starts_with("--edition=") {
                                 panic!("you must use `//@ edition` to configure the edition");
                             }
+                            if (flag == "-C"
+                                && flags.get(i + 1).is_some_and(|v| v.starts_with("incremental=")))
+                                || flag.starts_with("-Cincremental=")
+                            {
+                                panic!(
+                                    "you must use `//@ incremental` to enable incremental compilation"
+                                );
+                            }
                         }
                         self.compile_flags.extend(flags);
                     }
@@ -429,10 +438,13 @@ impl TestProps {
                         panic!("`compiler-flags` directive should be spelled `compile-flags`");
                     }
 
-                    if let Some(edition) = config.parse_edition(ln, testfile) {
+                    if let Some(range) = parse_edition_range(config, ln, testfile) {
                         // The edition is added at the start, since flags from //@compile-flags must
                         // be passed to rustc last.
-                        self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
+                        self.compile_flags.insert(
+                            0,
+                            format!("--edition={}", range.edition_to_test(config.edition)),
+                        );
                         has_edition = true;
                     }
 
@@ -1116,10 +1128,6 @@ impl Config {
         }
     }
 
-    fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<String> {
-        self.parse_name_value_directive(line, "edition", testfile)
-    }
-
     fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) {
         // If the flag is already true, don't bother looking at the directive.
         *value = *value || self.parse_name_directive(line, directive);
@@ -1761,3 +1769,86 @@ enum IgnoreDecision {
     Continue,
     Error { message: String },
 }
+
+fn parse_edition_range(
+    config: &Config,
+    line: &DirectiveLine<'_>,
+    testfile: &Utf8Path,
+) -> Option<EditionRange> {
+    let raw = config.parse_name_value_directive(line, "edition", testfile)?;
+    let line_number = line.line_number;
+
+    // Edition range is half-open: `[lower_bound, upper_bound)`
+    if let Some((lower_bound, upper_bound)) = raw.split_once("..") {
+        Some(match (maybe_parse_edition(lower_bound), maybe_parse_edition(upper_bound)) {
+            (Some(lower_bound), Some(upper_bound)) if upper_bound <= lower_bound => {
+                fatal!(
+                    "{testfile}:{line_number}: the left side of `//@ edition` cannot be greater than or equal to the right side"
+                );
+            }
+            (Some(lower_bound), Some(upper_bound)) => {
+                EditionRange::Range { lower_bound, upper_bound }
+            }
+            (Some(lower_bound), None) => EditionRange::RangeFrom(lower_bound),
+            (None, Some(_)) => {
+                fatal!(
+                    "{testfile}:{line_number}: `..edition` is not a supported range in `//@ edition`"
+                );
+            }
+            (None, None) => {
+                fatal!("{testfile}:{line_number}: `..` is not a supported range in `//@ edition`");
+            }
+        })
+    } else {
+        match maybe_parse_edition(&raw) {
+            Some(edition) => Some(EditionRange::Exact(edition)),
+            None => {
+                fatal!("{testfile}:{line_number}: empty value for `//@ edition`");
+            }
+        }
+    }
+}
+
+fn maybe_parse_edition(mut input: &str) -> Option<Edition> {
+    input = input.trim();
+    if input.is_empty() {
+        return None;
+    }
+    Some(parse_edition(input))
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum EditionRange {
+    Exact(Edition),
+    RangeFrom(Edition),
+    /// Half-open range: `[lower_bound, upper_bound)`
+    Range {
+        lower_bound: Edition,
+        upper_bound: Edition,
+    },
+}
+
+impl EditionRange {
+    fn edition_to_test(&self, requested: impl Into<Option<Edition>>) -> Edition {
+        let min_edition = Edition::Year(2015);
+        let requested = requested.into().unwrap_or(min_edition);
+
+        match *self {
+            EditionRange::Exact(exact) => exact,
+            EditionRange::RangeFrom(lower_bound) => {
+                if requested >= lower_bound {
+                    requested
+                } else {
+                    lower_bound
+                }
+            }
+            EditionRange::Range { lower_bound, upper_bound } => {
+                if requested >= lower_bound && requested < upper_bound {
+                    requested
+                } else {
+                    lower_bound
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs
index 16cf76be9a5..93621192d4b 100644
--- a/src/tools/compiletest/src/directives/tests.rs
+++ b/src/tools/compiletest/src/directives/tests.rs
@@ -4,10 +4,11 @@ use camino::Utf8Path;
 use semver::Version;
 
 use super::{
-    DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives,
-    parse_normalize_rule,
+    DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version,
+    extract_version_range, iter_directives, parse_normalize_rule,
 };
 use crate::common::{Config, Debugger, TestMode};
+use crate::directives::parse_edition;
 use crate::executor::{CollectedTestDesc, ShouldPanic};
 
 fn make_test_description<R: Read>(
@@ -73,6 +74,7 @@ fn test_parse_normalize_rule() {
 struct ConfigBuilder {
     mode: Option<String>,
     channel: Option<String>,
+    edition: Option<Edition>,
     host: Option<String>,
     target: Option<String>,
     stage: Option<u32>,
@@ -96,6 +98,11 @@ impl ConfigBuilder {
         self
     }
 
+    fn edition(&mut self, e: Edition) -> &mut Self {
+        self.edition = Some(e);
+        self
+    }
+
     fn host(&mut self, s: &str) -> &mut Self {
         self.host = Some(s.to_owned());
         self
@@ -183,6 +190,10 @@ impl ConfigBuilder {
         ];
         let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
 
+        if let Some(edition) = &self.edition {
+            args.push(format!("--edition={edition}"));
+        }
+
         if let Some(ref llvm_version) = self.llvm_version {
             args.push("--llvm-version".to_owned());
             args.push(llvm_version.clone());
@@ -941,3 +952,130 @@ fn test_needs_target_std() {
     let config = cfg().target("x86_64-unknown-linux-gnu").build();
     assert!(!check_ignore(&config, "//@ needs-target-std"));
 }
+
+fn parse_edition_range(line: &str) -> Option<EditionRange> {
+    let config = cfg().build();
+    let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line };
+
+    super::parse_edition_range(&config, &line, "tmp.rs".into())
+}
+
+#[test]
+fn test_parse_edition_range() {
+    assert_eq!(None, parse_edition_range("hello-world"));
+    assert_eq!(None, parse_edition_range("edition"));
+
+    assert_eq!(Some(EditionRange::Exact(2018.into())), parse_edition_range("edition: 2018"));
+    assert_eq!(Some(EditionRange::Exact(2021.into())), parse_edition_range("edition:2021"));
+    assert_eq!(Some(EditionRange::Exact(2024.into())), parse_edition_range("edition: 2024 "));
+    assert_eq!(Some(EditionRange::Exact(Edition::Future)), parse_edition_range("edition: future"));
+
+    assert_eq!(Some(EditionRange::RangeFrom(2018.into())), parse_edition_range("edition: 2018.."));
+    assert_eq!(Some(EditionRange::RangeFrom(2021.into())), parse_edition_range("edition:2021 .."));
+    assert_eq!(
+        Some(EditionRange::RangeFrom(2024.into())),
+        parse_edition_range("edition: 2024 .. ")
+    );
+    assert_eq!(
+        Some(EditionRange::RangeFrom(Edition::Future)),
+        parse_edition_range("edition: future.. ")
+    );
+
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }),
+        parse_edition_range("edition: 2018..2024")
+    );
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2015.into(), upper_bound: 2021.into() }),
+        parse_edition_range("edition:2015 .. 2021 ")
+    );
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: 2027.into() }),
+        parse_edition_range("edition: 2021 .. 2027 ")
+    );
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: Edition::Future }),
+        parse_edition_range("edition: 2021..future")
+    );
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_empty() {
+    parse_edition_range("edition:");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_invalid_edition() {
+    parse_edition_range("edition: hello");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_double_dots() {
+    parse_edition_range("edition: ..");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_inverted_range() {
+    parse_edition_range("edition: 2021..2015");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_inverted_range_future() {
+    parse_edition_range("edition: future..2015");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_empty_range() {
+    parse_edition_range("edition: 2021..2021");
+}
+
+#[track_caller]
+fn assert_edition_to_test(
+    expected: impl Into<Edition>,
+    range: EditionRange,
+    default: Option<Edition>,
+) {
+    let mut cfg = cfg();
+    if let Some(default) = default {
+        cfg.edition(default);
+    }
+    assert_eq!(expected.into(), range.edition_to_test(cfg.build().edition));
+}
+
+#[test]
+fn test_edition_range_edition_to_test() {
+    let e2015 = parse_edition("2015");
+    let e2018 = parse_edition("2018");
+    let e2021 = parse_edition("2021");
+    let e2024 = parse_edition("2024");
+    let efuture = parse_edition("future");
+
+    let exact = EditionRange::Exact(2021.into());
+    assert_edition_to_test(2021, exact, None);
+    assert_edition_to_test(2021, exact, Some(e2018));
+    assert_edition_to_test(2021, exact, Some(efuture));
+
+    assert_edition_to_test(Edition::Future, EditionRange::Exact(Edition::Future), None);
+
+    let greater_equal_than = EditionRange::RangeFrom(2021.into());
+    assert_edition_to_test(2021, greater_equal_than, None);
+    assert_edition_to_test(2021, greater_equal_than, Some(e2015));
+    assert_edition_to_test(2021, greater_equal_than, Some(e2018));
+    assert_edition_to_test(2021, greater_equal_than, Some(e2021));
+    assert_edition_to_test(2024, greater_equal_than, Some(e2024));
+    assert_edition_to_test(Edition::Future, greater_equal_than, Some(efuture));
+
+    let range = EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() };
+    assert_edition_to_test(2018, range, None);
+    assert_edition_to_test(2018, range, Some(e2015));
+    assert_edition_to_test(2018, range, Some(e2018));
+    assert_edition_to_test(2021, range, Some(e2021));
+    assert_edition_to_test(2018, range, Some(e2024));
+    assert_edition_to_test(2018, range, Some(efuture));
+}
diff --git a/src/tools/compiletest/src/edition.rs b/src/tools/compiletest/src/edition.rs
new file mode 100644
index 00000000000..36550cf5b2b
--- /dev/null
+++ b/src/tools/compiletest/src/edition.rs
@@ -0,0 +1,35 @@
+use crate::fatal;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Edition {
+    // Note that the ordering here is load-bearing, as we want the future edition to be greater than
+    // any year-based edition.
+    Year(u32),
+    Future,
+}
+
+impl std::fmt::Display for Edition {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Edition::Year(year) => write!(f, "{year}"),
+            Edition::Future => f.write_str("future"),
+        }
+    }
+}
+
+impl From<u32> for Edition {
+    fn from(value: u32) -> Self {
+        Edition::Year(value)
+    }
+}
+
+pub fn parse_edition(mut input: &str) -> Edition {
+    input = input.trim();
+    if input == "future" {
+        Edition::Future
+    } else {
+        Edition::Year(input.parse().unwrap_or_else(|_| {
+            fatal!("`{input}` doesn't look like an edition");
+        }))
+    }
+}
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 15e31dadf97..2d759279f34 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -7,6 +7,7 @@ pub mod common;
 mod debuggers;
 pub mod diagnostics;
 pub mod directives;
+pub mod edition;
 pub mod errors;
 mod executor;
 mod json;
@@ -39,6 +40,7 @@ use crate::common::{
     expected_output_path, output_base_dir, output_relative_path,
 };
 use crate::directives::DirectivesCache;
+use crate::edition::parse_edition;
 use crate::executor::{CollectedTest, ColorConfig};
 
 /// Creates the `Config` instance for this invocation of compiletest.
@@ -449,7 +451,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         has_enzyme,
         channel: matches.opt_str("channel").unwrap(),
         git_hash: matches.opt_present("git-hash"),
-        edition: matches.opt_str("edition"),
+        edition: matches.opt_str("edition").as_deref().map(parse_edition),
 
         cc: matches.opt_str("cc").unwrap(),
         cxx: matches.opt_str("cxx").unwrap(),
diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs
index f292f028568..bc6a9bf8af7 100644
--- a/src/tools/miri/tests/pass/static_align.rs
+++ b/src/tools/miri/tests/pass/static_align.rs
@@ -1,4 +1,7 @@
 #![feature(static_align)]
+#![deny(non_upper_case_globals)]
+
+use std::cell::Cell;
 
 // When a static uses `align(N)`, its address should be a multiple of `N`.
 
@@ -8,7 +11,64 @@ static FOO: u64 = 0;
 #[rustc_align_static(512)]
 static BAR: u64 = 0;
 
+struct HasDrop(*const HasDrop);
+
+impl Drop for HasDrop {
+    fn drop(&mut self) {
+        assert_eq!(core::ptr::from_mut(self).cast_const(), self.0);
+    }
+}
+
+thread_local! {
+    #[rustc_align_static(4096)]
+    static LOCAL: u64 = 0;
+
+    #[allow(unused_mut, reason = "test attribute handling")]
+    #[cfg_attr(true, rustc_align_static(4096))]
+    static CONST_LOCAL: u64 = const { 0 };
+
+    #[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))]
+    #[allow(unused_mut, reason = "test attribute handling")]
+    static HASDROP_LOCAL: Cell<HasDrop> = Cell::new(HasDrop(core::ptr::null()));
+
+    /// I love doc comments.
+    #[allow(unused_mut, reason = "test attribute handling")]
+    #[cfg_attr(all(),
+      cfg_attr(any(true),
+      cfg_attr(true, rustc_align_static(4096))))]
+    #[allow(unused_mut, reason = "test attribute handling")]
+    /// I love doc comments.
+    static HASDROP_CONST_LOCAL: Cell<HasDrop> = const { Cell::new(HasDrop(core::ptr::null())) };
+
+    #[cfg_attr(true,)]
+    #[cfg_attr(false,)]
+    #[cfg_attr(
+        true,
+        rustc_align_static(32),
+        cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")),
+        cfg_attr(false,)
+    )]
+    #[cfg_attr(false, rustc_align_static(0))]
+    static more_attr_testing: u64 = 0;
+}
+
+fn thread_local_ptr<T>(key: &'static std::thread::LocalKey<T>) -> *const T {
+    key.with(|local| core::ptr::from_ref::<T>(local))
+}
+
 fn main() {
     assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256));
     assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512));
+
+    assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32));
+
+    // Test that address (and therefore alignment) is maintained during drop
+    let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL);
+    core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast())));
+    let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL);
+    core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast())));
 }
diff --git a/src/tools/miri/tests/pass/thread_local-panic.rs b/src/tools/miri/tests/pass/thread_local-panic.rs
new file mode 100644
index 00000000000..549acd23987
--- /dev/null
+++ b/src/tools/miri/tests/pass/thread_local-panic.rs
@@ -0,0 +1,8 @@
+thread_local! {
+    static LOCAL: u64 = panic!();
+
+}
+
+fn main() {
+    let _ = std::panic::catch_unwind(|| LOCAL.with(|_| {}));
+}
diff --git a/src/tools/miri/tests/pass/thread_local-panic.stderr b/src/tools/miri/tests/pass/thread_local-panic.stderr
new file mode 100644
index 00000000000..e69340a8102
--- /dev/null
+++ b/src/tools/miri/tests/pass/thread_local-panic.stderr
@@ -0,0 +1,5 @@
+
+thread 'main' ($TID) panicked at tests/pass/thread_local-panic.rs:LL:CC:
+explicit panic
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
index 894d1327da7..c0181d96053 100644
--- a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
@@ -1,7 +1,7 @@
 #[unsafe(unsafe(no_mangle))]
 //~^ ERROR expected identifier, found keyword `unsafe`
 //~| ERROR cannot find attribute `r#unsafe` in this scope
-//~| ERROR `r#unsafe` is not an unsafe attribute
+//~| ERROR unnecessary `unsafe`
 fn a() {}
 
 fn main() {}
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
index 0825cf79408..846800daa54 100644
--- a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
@@ -9,13 +9,11 @@ help: escape `unsafe` to use it as an identifier
 LL | #[unsafe(r#unsafe(no_mangle))]
    |          ++
 
-error: `r#unsafe` is not an unsafe attribute
+error: unnecessary `unsafe` on safe attribute
   --> $DIR/double-unsafe-attributes.rs:1:3
    |
 LL | #[unsafe(unsafe(no_mangle))]
-   |   ^^^^^^ this is not an unsafe attribute
-   |
-   = note: extraneous unsafe is not allowed in attributes
+   |   ^^^^^^
 
 error: cannot find attribute `r#unsafe` in this scope
   --> $DIR/double-unsafe-attributes.rs:1:10
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
index 0f241cc439f..d9054248a29 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
@@ -1,4 +1,4 @@
-#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute
+#[unsafe(diagnostic::on_unimplemented( //~ ERROR: unnecessary `unsafe`
     message = "testing",
 ))]
 trait Foo {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
index 3bc291db5ac..a7662f5ee6c 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
@@ -1,10 +1,8 @@
-error: `diagnostic::on_unimplemented` is not an unsafe attribute
+error: unnecessary `unsafe` on safe attribute
   --> $DIR/unsafe-safe-attribute_diagnostic.rs:1:3
    |
 LL | #[unsafe(diagnostic::on_unimplemented(
-   |   ^^^^^^ this is not an unsafe attribute
-   |
-   = note: extraneous unsafe is not allowed in attributes
+   |   ^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/compiletest-self-test/compile-flags-incremental.rs b/tests/ui/compiletest-self-test/compile-flags-incremental.rs
new file mode 100644
index 00000000000..62a1ad84d8f
--- /dev/null
+++ b/tests/ui/compiletest-self-test/compile-flags-incremental.rs
@@ -0,0 +1,17 @@
+//@ revisions: good bad bad-space
+//@ check-pass
+
+//@[bad] compile-flags: -Cincremental=true
+//@[bad] should-fail
+
+//@[bad-space] compile-flags:  -C  incremental=dir
+//@[bad-space] should-fail
+
+fn main() {}
+
+// Tests should not try to manually enable incremental compilation with
+// `-Cincremental`, because that typically results in stray directories being
+// created in the repository root.
+//
+// Instead, use the `//@ incremental` directive, which instructs compiletest
+// to handle the details of passing `-Cincremental` with a fresh directory.
diff --git a/tests/ui/feature-gates/feature-gate-static_align-thread_local.rs b/tests/ui/feature-gates/feature-gate-static_align-thread_local.rs
new file mode 100644
index 00000000000..29d4facffce
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-static_align-thread_local.rs
@@ -0,0 +1,11 @@
+// The feature gate error may be emitted twice, but only on certain targets
+//@ dont-require-annotations: ERROR
+//@ dont-check-compiler-stderr
+
+#![crate_type = "lib"]
+
+thread_local! {
+    //~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature
+    #[rustc_align_static(16)]
+    static THREAD_LOCAL: u16 = 0;
+}
diff --git a/tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs b/tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs
new file mode 100644
index 00000000000..61a8d8f0054
--- /dev/null
+++ b/tests/ui/imports/auxiliary/same-res-ambigious-extern-fail.rs
@@ -0,0 +1,16 @@
+//@ edition:2018
+//@ proc-macro: same-res-ambigious-extern-macro.rs
+
+macro_rules! globbing{
+    () => {
+        pub use same_res_ambigious_extern_macro::*;
+    }
+}
+
+#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility
+extern crate same_res_ambigious_extern_macro;
+globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility
+
+pub trait RustEmbed {}
+
+pub use RustEmbed as Embed;
diff --git a/tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs b/tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs
new file mode 100644
index 00000000000..4e9b8427092
--- /dev/null
+++ b/tests/ui/imports/auxiliary/same-res-ambigious-extern-macro.rs
@@ -0,0 +1,8 @@
+//@ edition: 2018
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(RustEmbed)]
+pub fn rust_embed_derive(_input: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
diff --git a/tests/ui/imports/auxiliary/same-res-ambigious-extern.rs b/tests/ui/imports/auxiliary/same-res-ambigious-extern.rs
new file mode 100644
index 00000000000..5269dcd0b17
--- /dev/null
+++ b/tests/ui/imports/auxiliary/same-res-ambigious-extern.rs
@@ -0,0 +1,11 @@
+//@ edition:2018
+//@ proc-macro: same-res-ambigious-extern-macro.rs
+
+#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility
+extern crate same_res_ambigious_extern_macro;
+// this imports the same `RustEmbed` macro with `pub` visibility
+pub use same_res_ambigious_extern_macro::*;
+
+pub trait RustEmbed {}
+
+pub use RustEmbed as Embed;
diff --git a/tests/ui/imports/same-res-ambigious.fail.stderr b/tests/ui/imports/same-res-ambigious.fail.stderr
new file mode 100644
index 00000000000..dfd7c5a5f94
--- /dev/null
+++ b/tests/ui/imports/same-res-ambigious.fail.stderr
@@ -0,0 +1,20 @@
+error[E0603]: derive macro `Embed` is private
+  --> $DIR/same-res-ambigious.rs:8:28
+   |
+LL | #[derive(ambigious_extern::Embed)]
+   |                            ^^^^^ private derive macro
+   |
+note: the derive macro `Embed` is defined here
+  --> $DIR/auxiliary/same-res-ambigious-extern-fail.rs:16:9
+   |
+LL | pub use RustEmbed as Embed;
+   |         ^^^^^^^^^
+help: import `Embed` directly
+   |
+LL - #[derive(ambigious_extern::Embed)]
+LL + #[derive(same_res_ambigious_extern_macro::RustEmbed)]
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/tests/ui/imports/same-res-ambigious.nightly-fail.stderr b/tests/ui/imports/same-res-ambigious.nightly-fail.stderr
new file mode 100644
index 00000000000..dfd7c5a5f94
--- /dev/null
+++ b/tests/ui/imports/same-res-ambigious.nightly-fail.stderr
@@ -0,0 +1,20 @@
+error[E0603]: derive macro `Embed` is private
+  --> $DIR/same-res-ambigious.rs:8:28
+   |
+LL | #[derive(ambigious_extern::Embed)]
+   |                            ^^^^^ private derive macro
+   |
+note: the derive macro `Embed` is defined here
+  --> $DIR/auxiliary/same-res-ambigious-extern-fail.rs:16:9
+   |
+LL | pub use RustEmbed as Embed;
+   |         ^^^^^^^^^
+help: import `Embed` directly
+   |
+LL - #[derive(ambigious_extern::Embed)]
+LL + #[derive(same_res_ambigious_extern_macro::RustEmbed)]
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/tests/ui/imports/same-res-ambigious.rs b/tests/ui/imports/same-res-ambigious.rs
new file mode 100644
index 00000000000..b5c13a15b7c
--- /dev/null
+++ b/tests/ui/imports/same-res-ambigious.rs
@@ -0,0 +1,11 @@
+//@ edition: 2018
+//@ revisions: fail pass
+//@[pass] check-pass
+//@[pass] aux-crate: ambigious_extern=same-res-ambigious-extern.rs
+//@[fail] aux-crate: ambigious_extern=same-res-ambigious-extern-fail.rs
+// see https://github.com/rust-lang/rust/pull/147196
+
+#[derive(ambigious_extern::Embed)] //[fail]~ ERROR: derive macro `Embed` is private
+struct Foo{}
+
+fn main(){}
diff --git a/tests/ui/indexing/index_message.stderr b/tests/ui/indexing/index_message.stderr
index 6affb1ed962..b6f61379f2a 100644
--- a/tests/ui/indexing/index_message.stderr
+++ b/tests/ui/indexing/index_message.stderr
@@ -2,7 +2,9 @@ error[E0608]: cannot index into a value of type `({integer},)`
   --> $DIR/index_message.rs:3:14
    |
 LL |     let _ = z[0];
-   |              ^^^ help: to access tuple elements, use: `.0`
+   |              ^^^ help: to access tuple element `0`, use: `.0`
+   |
+   = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/issues/issue-27842.stderr
index b18fe1512b5..f388fdf85cd 100644
--- a/tests/ui/issues/issue-27842.stderr
+++ b/tests/ui/issues/issue-27842.stderr
@@ -2,17 +2,17 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer
   --> $DIR/issue-27842.rs:4:16
    |
 LL |     let _ = tup[0];
-   |                ^^^ help: to access tuple elements, use: `.0`
+   |                ^^^ help: to access tuple element `0`, use: `.0`
+   |
+   = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
 
 error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})`
   --> $DIR/issue-27842.rs:9:16
    |
 LL |     let _ = tup[i];
-   |                ^-^
-   |                 |
-   |                 cannot access tuple elements at a variable index
+   |                ^^^
    |
-   = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
+   = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
 
 error[E0608]: cannot index into a value of type `({integer},)`
   --> $DIR/issue-27842.rs:14:16
@@ -20,7 +20,7 @@ error[E0608]: cannot index into a value of type `({integer},)`
 LL |     let _ = tup[3];
    |                ^^^
    |
-   = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
+   = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/macros/macro-local-data-key-priv.stderr b/tests/ui/macros/macro-local-data-key-priv.stderr
index e93bd11046d..8df1aec140d 100644
--- a/tests/ui/macros/macro-local-data-key-priv.stderr
+++ b/tests/ui/macros/macro-local-data-key-priv.stderr
@@ -9,7 +9,7 @@ note: the constant `baz` is defined here
    |
 LL |     thread_local!(static baz: f64 = 0.0);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/macros/macro-rules-attr-error.rs b/tests/ui/macros/macro-rules-attr-error.rs
index 81eadb6692f..60290b883cb 100644
--- a/tests/ui/macros/macro-rules-attr-error.rs
+++ b/tests/ui/macros/macro-rules-attr-error.rs
@@ -50,3 +50,22 @@ macro_rules! forward_referenced_attr {
 macro_rules! cyclic_attr {
     attr() {} => {}
 }
+
+macro_rules! attr_with_safety {
+    unsafe attr() { struct RequiresUnsafe; } => {};
+    attr() { struct SafeInvocation; } => {};
+}
+
+#[attr_with_safety]
+struct SafeInvocation;
+
+//~v ERROR: unnecessary `unsafe` on safe attribute invocation
+#[unsafe(attr_with_safety)]
+struct SafeInvocation;
+
+//~v ERROR: unsafe attribute invocation requires `unsafe`
+#[attr_with_safety]
+struct RequiresUnsafe;
+
+#[unsafe(attr_with_safety)]
+struct RequiresUnsafe;
diff --git a/tests/ui/macros/macro-rules-attr-error.stderr b/tests/ui/macros/macro-rules-attr-error.stderr
index 674d35091b6..27527a2da7e 100644
--- a/tests/ui/macros/macro-rules-attr-error.stderr
+++ b/tests/ui/macros/macro-rules-attr-error.stderr
@@ -9,6 +9,18 @@ LL |     #[local_attr]
    |
    = note: this error originates in the attribute macro `local_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+error: unnecessary `unsafe` on safe attribute invocation
+  --> $DIR/macro-rules-attr-error.rs:63:3
+   |
+LL | #[unsafe(attr_with_safety)]
+   |   ^^^^^^
+
+error: unsafe attribute invocation requires `unsafe`
+  --> $DIR/macro-rules-attr-error.rs:67:1
+   |
+LL | #[attr_with_safety]
+   | ^^^^^^^^^^^^^^^^^^^
+
 error: cannot find macro `local_attr` in this scope
   --> $DIR/macro-rules-attr-error.rs:27:5
    |
@@ -59,5 +71,5 @@ note: a macro with the same name exists, but it appears later
 LL | macro_rules! cyclic_attr {
    |              ^^^^^^^^^^^
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs
index d081c06044f..e1ea38f2795 100644
--- a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs
+++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs
@@ -37,10 +37,9 @@ fn g1() {
         //~| HELP: maybe write a path separator here
         _ => {}
     }
-    if let Foo:Bar = f() { //~ WARN: irrefutable `if let` pattern
+    if let Foo:Bar = f() {
     //~^ ERROR: expected one of
     //~| HELP: maybe write a path separator here
-    //~| HELP: consider replacing the `if let` with a `let`
     }
 }
 
diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr
index a9bad96f9af..061586882e0 100644
--- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr
+++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr
@@ -64,7 +64,7 @@ LL |     if let Foo::Bar = f() {
    |                +
 
 error: expected one of `@` or `|`, found `:`
-  --> $DIR/issue-87086-colon-path-sep.rs:49:16
+  --> $DIR/issue-87086-colon-path-sep.rs:48:16
    |
 LL |         ref qux: Foo::Baz => {}
    |                ^ -------- specifying the type of a pattern isn't supported
@@ -77,7 +77,7 @@ LL |         ref qux::Foo::Baz => {}
    |                ~~
 
 error: expected one of `@` or `|`, found `:`
-  --> $DIR/issue-87086-colon-path-sep.rs:58:16
+  --> $DIR/issue-87086-colon-path-sep.rs:57:16
    |
 LL |         mut qux: Foo::Baz => {}
    |                ^ -------- specifying the type of a pattern isn't supported
@@ -90,7 +90,7 @@ LL |         mut qux::Foo::Baz => {}
    |                ~~
 
 error: expected one of `@` or `|`, found `:`
-  --> $DIR/issue-87086-colon-path-sep.rs:69:12
+  --> $DIR/issue-87086-colon-path-sep.rs:68:12
    |
 LL |         Foo:Bar::Baz => {}
    |            ^-------- specifying the type of a pattern isn't supported
@@ -103,7 +103,7 @@ LL |         Foo::Bar::Baz => {}
    |             +
 
 error: expected one of `@` or `|`, found `:`
-  --> $DIR/issue-87086-colon-path-sep.rs:75:12
+  --> $DIR/issue-87086-colon-path-sep.rs:74:12
    |
 LL |         Foo:Bar => {}
    |            ^--- specifying the type of a pattern isn't supported
@@ -115,15 +115,5 @@ help: maybe write a path separator here
 LL |         Foo::Bar => {}
    |             +
 
-warning: irrefutable `if let` pattern
-  --> $DIR/issue-87086-colon-path-sep.rs:40:8
-   |
-LL |     if let Foo:Bar = f() {
-   |        ^^^^^^^^^^^^^^^^^
-   |
-   = note: this pattern will always match, so the `if let` is useless
-   = help: consider replacing the `if let` with a `let`
-   = note: `#[warn(irrefutable_let_patterns)]` on by default
-
-error: aborting due to 9 previous errors; 1 warning emitted
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/parser/macro/bad-macro-definition.rs b/tests/ui/parser/macro/bad-macro-definition.rs
index 3c5c93ea3b3..12df6e64bd2 100644
--- a/tests/ui/parser/macro/bad-macro-definition.rs
+++ b/tests/ui/parser/macro/bad-macro-definition.rs
@@ -20,3 +20,6 @@ macro_rules! e { {} }
 
 macro_rules! f {}
 //~^ ERROR: macros must contain at least one rule
+
+macro_rules! g { unsafe {} => {} }
+//~^ ERROR: `unsafe` is only supported on `attr` rules
diff --git a/tests/ui/parser/macro/bad-macro-definition.stderr b/tests/ui/parser/macro/bad-macro-definition.stderr
index de6d9d6a38b..d15f33f708d 100644
--- a/tests/ui/parser/macro/bad-macro-definition.stderr
+++ b/tests/ui/parser/macro/bad-macro-definition.stderr
@@ -52,5 +52,11 @@ error: macros must contain at least one rule
 LL | macro_rules! f {}
    | ^^^^^^^^^^^^^^^^^
 
-error: aborting due to 9 previous errors
+error: `unsafe` is only supported on `attr` rules
+  --> $DIR/bad-macro-definition.rs:24:18
+   |
+LL | macro_rules! g { unsafe {} => {} }
+   |                  ^^^^^^
+
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/parser/macro/macro-attr-bad.rs b/tests/ui/parser/macro/macro-attr-bad.rs
index 9f50b057a7a..0ac46c8b768 100644
--- a/tests/ui/parser/macro/macro-attr-bad.rs
+++ b/tests/ui/parser/macro/macro-attr-bad.rs
@@ -13,6 +13,12 @@ macro_rules! attr_incomplete_3 { attr() {} }
 macro_rules! attr_incomplete_4 { attr() {} => }
 //~^ ERROR macro definition ended unexpectedly
 
+macro_rules! attr_incomplete_5 { unsafe }
+//~^ ERROR macro definition ended unexpectedly
+
+macro_rules! attr_incomplete_6 { unsafe attr }
+//~^ ERROR macro definition ended unexpectedly
+
 macro_rules! attr_noparens_1 { attr{} {} => {} }
 //~^ ERROR `attr` rule argument matchers require parentheses
 
diff --git a/tests/ui/parser/macro/macro-attr-bad.stderr b/tests/ui/parser/macro/macro-attr-bad.stderr
index bf0ed13cd55..481ef8118ae 100644
--- a/tests/ui/parser/macro/macro-attr-bad.stderr
+++ b/tests/ui/parser/macro/macro-attr-bad.stderr
@@ -22,8 +22,20 @@ error: macro definition ended unexpectedly
 LL | macro_rules! attr_incomplete_4 { attr() {} => }
    |                                              ^ expected right-hand side of macro rule
 
+error: macro definition ended unexpectedly
+  --> $DIR/macro-attr-bad.rs:16:40
+   |
+LL | macro_rules! attr_incomplete_5 { unsafe }
+   |                                        ^ expected `attr`
+
+error: macro definition ended unexpectedly
+  --> $DIR/macro-attr-bad.rs:19:45
+   |
+LL | macro_rules! attr_incomplete_6 { unsafe attr }
+   |                                             ^ expected macro attr args
+
 error: `attr` rule argument matchers require parentheses
-  --> $DIR/macro-attr-bad.rs:16:36
+  --> $DIR/macro-attr-bad.rs:22:36
    |
 LL | macro_rules! attr_noparens_1 { attr{} {} => {} }
    |                                    ^^
@@ -35,7 +47,7 @@ LL + macro_rules! attr_noparens_1 { attr() {} => {} }
    |
 
 error: `attr` rule argument matchers require parentheses
-  --> $DIR/macro-attr-bad.rs:19:36
+  --> $DIR/macro-attr-bad.rs:25:36
    |
 LL | macro_rules! attr_noparens_2 { attr[] {} => {} }
    |                                    ^^
@@ -47,13 +59,13 @@ LL + macro_rules! attr_noparens_2 { attr() {} => {} }
    |
 
 error: invalid macro matcher; matchers must be contained in balanced delimiters
-  --> $DIR/macro-attr-bad.rs:22:37
+  --> $DIR/macro-attr-bad.rs:28:37
    |
 LL | macro_rules! attr_noparens_3 { attr _ {} => {} }
    |                                     ^
 
 error: duplicate matcher binding
-  --> $DIR/macro-attr-bad.rs:25:52
+  --> $DIR/macro-attr-bad.rs:31:52
    |
 LL | macro_rules! attr_dup_matcher_1 { attr() {$x:ident $x:ident} => {} }
    |                                           -------- ^^^^^^^^ duplicate binding
@@ -61,7 +73,7 @@ LL | macro_rules! attr_dup_matcher_1 { attr() {$x:ident $x:ident} => {} }
    |                                           previous binding
 
 error: duplicate matcher binding
-  --> $DIR/macro-attr-bad.rs:28:49
+  --> $DIR/macro-attr-bad.rs:34:49
    |
 LL | macro_rules! attr_dup_matcher_2 { attr($x:ident $x:ident) {} => {} }
    |                                        -------- ^^^^^^^^ duplicate binding
@@ -69,12 +81,12 @@ LL | macro_rules! attr_dup_matcher_2 { attr($x:ident $x:ident) {} => {} }
    |                                        previous binding
 
 error: duplicate matcher binding
-  --> $DIR/macro-attr-bad.rs:31:51
+  --> $DIR/macro-attr-bad.rs:37:51
    |
 LL | macro_rules! attr_dup_matcher_3 { attr($x:ident) {$x:ident} => {} }
    |                                        --------   ^^^^^^^^ duplicate binding
    |                                        |
    |                                        previous binding
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/parser/macro/macro-derive-bad.rs b/tests/ui/parser/macro/macro-derive-bad.rs
index 79b9eb8c113..74e7d9acdaf 100644
--- a/tests/ui/parser/macro/macro-derive-bad.rs
+++ b/tests/ui/parser/macro/macro-derive-bad.rs
@@ -41,3 +41,6 @@ macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
 //~^ ERROR duplicate matcher binding
 //~| NOTE duplicate binding
 //~| NOTE previous binding
+
+macro_rules! derive_unsafe { unsafe derive() {} => {} }
+//~^ ERROR `unsafe` is only supported on `attr` rules
diff --git a/tests/ui/parser/macro/macro-derive-bad.stderr b/tests/ui/parser/macro/macro-derive-bad.stderr
index ec750c9ac82..c98535f4031 100644
--- a/tests/ui/parser/macro/macro-derive-bad.stderr
+++ b/tests/ui/parser/macro/macro-derive-bad.stderr
@@ -86,5 +86,11 @@ LL | macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
    |                                             |
    |                                             previous binding
 
-error: aborting due to 12 previous errors
+error: `unsafe` is only supported on `attr` rules
+  --> $DIR/macro-derive-bad.rs:45:30
+   |
+LL | macro_rules! derive_unsafe { unsafe derive() {} => {} }
+   |                              ^^^^^^
+
+error: aborting due to 13 previous errors
 
diff --git a/tests/ui/parser/type-ascription-in-pattern.rs b/tests/ui/parser/type-ascription-in-pattern.rs
index 18d7061d69c..75059d33db6 100644
--- a/tests/ui/parser/type-ascription-in-pattern.rs
+++ b/tests/ui/parser/type-ascription-in-pattern.rs
@@ -1,15 +1,16 @@
 fn foo(x: bool) -> i32 {
-    match x { //~ ERROR struct literals are not allowed here
-        x: i32 => x, //~ ERROR expected
-        true => 42., //~ ERROR expected identifier
-        false => 0.333, //~ ERROR expected identifier
+    match x {
+        x: i32 => x, //~ ERROR: expected
+        //~^ ERROR: mismatched types
+        true => 42.,
+        false => 0.333,
     }
-} //~ ERROR expected one of
+}
 
 fn main() {
     match foo(true) {
-        42: i32 => (), //~ ERROR expected
-        _: f64 => (), //~ ERROR expected
-        x: i32 => (), //~ ERROR expected
+        42: i32 => (), //~ ERROR: expected
+        _: f64 => (), //~ ERROR: expected
+        x: i32 => (), //~ ERROR: expected
     }
 }
diff --git a/tests/ui/parser/type-ascription-in-pattern.stderr b/tests/ui/parser/type-ascription-in-pattern.stderr
index 135879f208b..09190754993 100644
--- a/tests/ui/parser/type-ascription-in-pattern.stderr
+++ b/tests/ui/parser/type-ascription-in-pattern.stderr
@@ -1,64 +1,18 @@
-error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>`
-  --> $DIR/type-ascription-in-pattern.rs:3:16
-   |
-LL |     match x {
-   |           - while parsing this struct
-LL |         x: i32 => x,
-   |               -^^ expected one of 8 possible tokens
-   |               |
-   |               help: try adding a comma: `,`
-
-error: expected identifier, found keyword `true`
-  --> $DIR/type-ascription-in-pattern.rs:4:9
-   |
-LL |     match x {
-   |           - while parsing this struct
-LL |         x: i32 => x,
-LL |         true => 42.,
-   |         ^^^^ expected identifier, found keyword
-
-error: expected identifier, found keyword `false`
-  --> $DIR/type-ascription-in-pattern.rs:5:9
-   |
-LL |     match x {
-   |           - while parsing this struct
-...
-LL |         false => 0.333,
-   |         ^^^^^ expected identifier, found keyword
-
-error: struct literals are not allowed here
-  --> $DIR/type-ascription-in-pattern.rs:2:11
-   |
-LL |       match x {
-   |  ___________^
-LL | |         x: i32 => x,
-LL | |         true => 42.,
-LL | |         false => 0.333,
-LL | |     }
-   | |_____^
-   |
-help: surround the struct literal with parentheses
+error: expected one of `@` or `|`, found `:`
+  --> $DIR/type-ascription-in-pattern.rs:3:10
    |
-LL ~     match (x {
 LL |         x: i32 => x,
-LL |         true => 42.,
-LL |         false => 0.333,
-LL ~     })
+   |          ^ --- specifying the type of a pattern isn't supported
+   |          |
+   |          expected one of `@` or `|`
    |
-
-error: expected one of `.`, `?`, `{`, or an operator, found `}`
-  --> $DIR/type-ascription-in-pattern.rs:7:1
+help: maybe write a path separator here
    |
-LL |     match x {
-   |     ----- while parsing this `match` expression
-...
-LL |     }
-   |      - expected one of `.`, `?`, `{`, or an operator
-LL | }
-   | ^ unexpected token
+LL |         x::i32 => x,
+   |          ~~
 
 error: expected one of `...`, `..=`, `..`, or `|`, found `:`
-  --> $DIR/type-ascription-in-pattern.rs:11:11
+  --> $DIR/type-ascription-in-pattern.rs:12:11
    |
 LL |         42: i32 => (),
    |           ^ --- specifying the type of a pattern isn't supported
@@ -66,7 +20,7 @@ LL |         42: i32 => (),
    |           expected one of `...`, `..=`, `..`, or `|`
 
 error: expected `|`, found `:`
-  --> $DIR/type-ascription-in-pattern.rs:12:10
+  --> $DIR/type-ascription-in-pattern.rs:13:10
    |
 LL |         _: f64 => (),
    |          ^ --- specifying the type of a pattern isn't supported
@@ -74,7 +28,7 @@ LL |         _: f64 => (),
    |          expected `|`
 
 error: expected one of `@` or `|`, found `:`
-  --> $DIR/type-ascription-in-pattern.rs:13:10
+  --> $DIR/type-ascription-in-pattern.rs:14:10
    |
 LL |         x: i32 => (),
    |          ^ --- specifying the type of a pattern isn't supported
@@ -86,5 +40,15 @@ help: maybe write a path separator here
 LL |         x::i32 => (),
    |          ~~
 
-error: aborting due to 8 previous errors
+error[E0308]: mismatched types
+  --> $DIR/type-ascription-in-pattern.rs:3:19
+   |
+LL | fn foo(x: bool) -> i32 {
+   |                    --- expected `i32` because of return type
+LL |     match x {
+LL |         x: i32 => x,
+   |                   ^ expected `i32`, found `bool`
+
+error: aborting due to 5 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/span/suggestion-non-ascii.stderr b/tests/ui/span/suggestion-non-ascii.stderr
index 6e6e31a5698..361f744ee8e 100644
--- a/tests/ui/span/suggestion-non-ascii.stderr
+++ b/tests/ui/span/suggestion-non-ascii.stderr
@@ -2,7 +2,9 @@ error[E0608]: cannot index into a value of type `({integer},)`
   --> $DIR/suggestion-non-ascii.rs:3:24
    |
 LL |     println!("☃{}", tup[0]);
-   |                        ^^^ help: to access tuple elements, use: `.0`
+   |                        ^^^ help: to access tuple element `0`, use: `.0`
+   |
+   = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/static/static-align.rs b/tests/ui/static/static-align.rs
index 93241db09f9..e2db7c01adf 100644
--- a/tests/ui/static/static-align.rs
+++ b/tests/ui/static/static-align.rs
@@ -1,10 +1,14 @@
 //@ run-pass
+//@ compile-flags: --cfg FOURTY_TWO="42" --cfg TRUE --check-cfg=cfg(FOURTY_TWO,values("42")) --check-cfg=cfg(TRUE)
 #![feature(static_align)]
+#![deny(non_upper_case_globals)]
+
+use std::cell::Cell;
 
 #[rustc_align_static(64)]
 static A: u8 = 0;
 
-#[rustc_align_static(64)]
+#[rustc_align_static(4096)]
 static B: u8 = 0;
 
 #[rustc_align_static(128)]
@@ -17,10 +21,86 @@ unsafe extern "C" {
     static C: u64;
 }
 
+struct HasDrop(*const HasDrop);
+
+impl Drop for HasDrop {
+    fn drop(&mut self) {
+        assert_eq!(core::ptr::from_mut(self).cast_const(), self.0);
+    }
+}
+
+thread_local! {
+    #[rustc_align_static(4096)]
+    static LOCAL: u64 = 0;
+
+    #[allow(unused_mut, reason = "test attribute handling")]
+    #[cfg_attr(true, rustc_align_static(4096))]
+    static CONST_LOCAL: u64 = const { 0 };
+
+    #[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))]
+    #[allow(unused_mut, reason = "test attribute handling")]
+    static HASDROP_LOCAL: Cell<HasDrop> = Cell::new(HasDrop(core::ptr::null()));
+
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    #[allow(unused_mut, reason = "test attribute handling")]
+    #[cfg_attr(TRUE,
+      cfg_attr(FOURTY_TWO = "42",
+      cfg_attr(all(),
+      cfg_attr(any(true),
+      cfg_attr(true, rustc_align_static(4096))))))]
+    #[allow(unused_mut, reason = "test attribute handling")]
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    static HASDROP_CONST_LOCAL: Cell<HasDrop> = const { Cell::new(HasDrop(core::ptr::null())) };
+
+    #[cfg_attr(TRUE,)]
+    #[cfg_attr(true,)]
+    #[cfg_attr(false,)]
+    #[cfg_attr(
+        TRUE,
+        rustc_align_static(32),
+        cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")),
+        cfg_attr(false,)
+    )]
+    #[cfg_attr(false, rustc_align_static(0))]
+    static more_attr_testing: u64 = 0;
+}
+
+fn thread_local_ptr<T>(key: &'static std::thread::LocalKey<T>) -> *const T {
+    key.with(|local| core::ptr::from_ref::<T>(local))
+}
+
 fn main() {
     assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64));
-    assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64));
+    assert!(core::ptr::from_ref(&B).addr().is_multiple_of(4096));
 
     assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128));
     unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) };
+
+    assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096));
+    assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32));
+
+    // Test that address (and therefore alignment) is maintained during drop
+    let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL);
+    core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast())));
+    let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL);
+    core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast())));
 }
diff --git a/tests/ui/thread-local/long-docs.rs b/tests/ui/thread-local/long-docs.rs
new file mode 100644
index 00000000000..0577d0b27c2
--- /dev/null
+++ b/tests/ui/thread-local/long-docs.rs
@@ -0,0 +1,266 @@
+//@ check-pass
+
+thread_local! {
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    pub static LONG_DOCS: () = ();
+
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    /// I love doc comments.
+    #[allow(unused_mut, reason = "testing")]
+    pub static LONG_DOCS_2: () = ();
+}
+
+fn main() {}
diff --git a/tests/ui/thread-local/no-unstable.rs b/tests/ui/thread-local/no-unstable.rs
new file mode 100644
index 00000000000..3de7985e62d
--- /dev/null
+++ b/tests/ui/thread-local/no-unstable.rs
@@ -0,0 +1,17 @@
+thread_local! {
+    //~^ ERROR: use of an internal attribute [E0658]
+    //~| ERROR: use of an internal attribute [E0658]
+    //~| ERROR: `#[used(linker)]` is currently unstable [E0658]
+    //~| ERROR: `#[used]` attribute cannot be used on constants
+
+    #[rustc_dummy = 17]
+    pub static FOO: () = ();
+
+    #[cfg_attr(true, rustc_dummy = 17)]
+    pub static BAR: () = ();
+
+    #[used(linker)]
+    pub static BAZ: () = ();
+}
+
+fn main() {}
diff --git a/tests/ui/thread-local/no-unstable.stderr b/tests/ui/thread-local/no-unstable.stderr
new file mode 100644
index 00000000000..fbcd804d917
--- /dev/null
+++ b/tests/ui/thread-local/no-unstable.stderr
@@ -0,0 +1,57 @@
+error[E0658]: use of an internal attribute
+  --> $DIR/no-unstable.rs:1:1
+   |
+LL | / thread_local! {
+...  |
+LL | |     pub static BAZ: () = ();
+LL | | }
+   | |_^
+   |
+   = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
+   = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_dummy]` attribute is used for rustc unit tests
+   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0658]: use of an internal attribute
+  --> $DIR/no-unstable.rs:1:1
+   |
+LL | / thread_local! {
+...  |
+LL | |     pub static BAZ: () = ();
+LL | | }
+   | |_^
+   |
+   = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
+   = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
+   = note: the `#[rustc_dummy]` attribute is used for rustc unit tests
+   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0658]: `#[used(linker)]` is currently unstable
+  --> $DIR/no-unstable.rs:1:1
+   |
+LL | / thread_local! {
+...  |
+LL | |     pub static BAZ: () = ();
+LL | | }
+   | |_^
+   |
+   = note: see issue #93798 <https://github.com/rust-lang/rust/issues/93798> for more information
+   = help: add `#![feature(used_with_arg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `#[used]` attribute cannot be used on constants
+  --> $DIR/no-unstable.rs:1:1
+   |
+LL | / thread_local! {
+...  |
+LL | |     pub static BAZ: () = ();
+LL | | }
+   | |_^
+   |
+   = help: `#[used]` can only be applied to statics
+   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr
index 13bc0cd94f3..ef5f1786801 100644
--- a/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr
+++ b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr
@@ -4,7 +4,7 @@ error[E0608]: cannot index into a value of type `()`
 LL |     ()[f(&[1.0])];
    |       ^^^^^^^^^^^
    |
-   = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
+   = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
 
 error: aborting due to 1 previous error
 
diff --git a/triagebot.toml b/triagebot.toml
index a04f8d28072..79b5c2d1b72 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -61,10 +61,6 @@ required-issue-label = "regression-from-stable-to-beta"
 # if the above conditions matches, the PR will receive these labels
 add-labels = ["beta-nominated"]
 
-[backport.t-compiler-stable-backport]
-required-pr-labels = ["T-compiler"]
-required-issue-label = "regression-from-stable-to-stable"
-add-labels = ["stable-nominated"]
 
 # ------------------------------------------------------------------------------
 # Ping groups