about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock71
-rw-r--r--compiler/rustc_ast/src/ast.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs512
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs4
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_driver_impl/Cargo.toml2
-rw-r--r--compiler/rustc_errors/Cargo.toml2
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs7
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs8
-rw-r--r--compiler/rustc_llvm/build.rs1
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs31
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs19
-rw-r--r--compiler/rustc_mir_transform/src/validate.rs4
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs1
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs133
-rw-r--r--compiler/rustc_session/Cargo.toml2
-rw-r--r--compiler/rustc_session/src/filesearch.rs14
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs7
-rw-r--r--compiler/rustc_target/src/target_features.rs44
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs4
-rw-r--r--library/core/src/fmt/float.rs36
-rw-r--r--library/core/src/lib.rs4
-rw-r--r--library/core/src/num/dec2flt/float.rs57
-rw-r--r--library/core/src/num/dec2flt/mod.rs16
-rw-r--r--library/core/src/num/flt2dec/decoder.rs7
-rw-r--r--library/core/src/ptr/non_null.rs6
-rw-r--r--library/coretests/tests/num/dec2flt/decimal.rs14
-rw-r--r--library/coretests/tests/num/dec2flt/float.rs40
-rw-r--r--library/coretests/tests/num/dec2flt/lemire.rs133
-rw-r--r--library/coretests/tests/num/dec2flt/mod.rs65
-rw-r--r--library/coretests/tests/num/dec2flt/parse.rs23
-rw-r--r--library/coretests/tests/num/flt2dec/mod.rs234
-rw-r--r--library/coretests/tests/num/flt2dec/random.rs60
-rw-r--r--library/coretests/tests/num/flt2dec/strategy/dragon.rs5
-rw-r--r--library/coretests/tests/num/flt2dec/strategy/grisu.rs4
-rw-r--r--library/coretests/tests/num/mod.rs332
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/sys/pal/unix/stack_overflow.rs144
-rw-r--r--library/std/src/sys/pal/unix/stack_overflow/thread_info.rs129
m---------library/stdarch0
-rw-r--r--src/bootstrap/Cargo.lock71
-rw-r--r--src/bootstrap/Cargo.toml2
-rw-r--r--src/bootstrap/src/bin/rustc.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs6
-rw-r--r--src/bootstrap/src/core/builder/mod.rs2
-rw-r--r--src/bootstrap/src/utils/helpers.rs2
-rw-r--r--src/bootstrap/src/utils/job.rs1
-rw-r--r--src/bootstrap/src/utils/shared_helpers.rs2
-rw-r--r--src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md4
-rw-r--r--src/doc/rustc/book.toml2
-rw-r--r--src/doc/rustc/theme/pagetoc.css84
-rw-r--r--src/doc/rustc/theme/pagetoc.js104
-rw-r--r--src/etc/test-float-parse/Cargo.toml7
-rw-r--r--src/etc/test-float-parse/src/gen_/subnorm.rs9
-rw-r--r--src/etc/test-float-parse/src/lib.rs7
-rw-r--r--src/etc/test-float-parse/src/traits.rs5
-rw-r--r--src/librustdoc/lib.rs1
m---------src/llvm-project0
-rw-r--r--src/tools/compiletest/Cargo.toml2
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/shims/native_lib.rs7
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs2
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs1
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs1
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs1
-rw-r--r--src/tools/rustfmt/src/parse/macros/asm.rs4
-rw-r--r--src/tools/tidy/src/deps.rs3
-rw-r--r--tests/mir-opt/gvn_overlapping.overlapping.GVN.diff18
-rw-r--r--tests/mir-opt/gvn_overlapping.rs36
-rw-r--r--tests/run-make/core-no-oom-handling/rmake.rs2
-rw-r--r--tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs7
-rw-r--r--tests/ui/abi/homogenous-floats-target-feature-mixup.rs2
-rw-r--r--tests/ui/abi/simd-abi-checks-avx.rs1
-rw-r--r--tests/ui/abi/simd-abi-checks-avx.stderr24
-rw-r--r--tests/ui/asm/aarch64/parse-error.rs2
-rw-r--r--tests/ui/asm/aarch64/parse-error.stderr40
-rw-r--r--tests/ui/asm/parse-error.rs6
-rw-r--r--tests/ui/asm/parse-error.stderr54
-rw-r--r--tests/ui/asm/x86_64/evex512-implicit-feature.rs1
-rw-r--r--tests/ui/asm/x86_64/target-feature-attr.rs2
-rw-r--r--tests/ui/asm/x86_64/target-feature-attr.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-guard-patterns.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-guard-patterns.stderr31
-rw-r--r--tests/ui/imports/issue-99695-b.fixed2
-rw-r--r--tests/ui/imports/issue-99695-b.stderr2
-rw-r--r--tests/ui/imports/issue-99695.edition_2015.fixed (renamed from tests/ui/imports/issue-99695.fixed)6
-rw-r--r--tests/ui/imports/issue-99695.edition_2015.stderr (renamed from tests/ui/imports/issue-99695.stderr)4
-rw-r--r--tests/ui/imports/issue-99695.edition_2018.fixed21
-rw-r--r--tests/ui/imports/issue-99695.edition_2018.stderr16
-rw-r--r--tests/ui/imports/issue-99695.rs4
-rw-r--r--tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs81
-rw-r--r--tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr133
-rw-r--r--tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs15
-rw-r--r--tests/ui/simd/target-feature-mixup.rs1
-rw-r--r--tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs4
-rw-r--r--tests/ui/target-feature/gate.rs3
-rw-r--r--tests/ui/target-feature/gate.stderr10
-rw-r--r--tests/ui/target-feature/unstable-feature.rs4
-rw-r--r--tests/ui/target-feature/unstable-feature.stderr2
-rw-r--r--triagebot.toml5
105 files changed, 2195 insertions, 879 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f1fddefcb01..59f7f3dda8f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -738,7 +738,7 @@ dependencies = [
  "tracing-subscriber",
  "unified-diff",
  "walkdir",
- "windows 0.59.0",
+ "windows",
 ]
 
 [[package]]
@@ -1587,7 +1587,7 @@ dependencies = [
  "js-sys",
  "log",
  "wasm-bindgen",
- "windows-core 0.61.0",
+ "windows-core",
 ]
 
 [[package]]
@@ -3493,7 +3493,7 @@ dependencies = [
  "thorin-dwp",
  "tracing",
  "wasm-encoder 0.219.2",
- "windows 0.59.0",
+ "windows",
 ]
 
 [[package]]
@@ -3552,7 +3552,7 @@ dependencies = [
  "tempfile",
  "thin-vec",
  "tracing",
- "windows 0.59.0",
+ "windows",
 ]
 
 [[package]]
@@ -3615,7 +3615,7 @@ dependencies = [
  "shlex",
  "stable_mir",
  "tracing",
- "windows 0.59.0",
+ "windows",
 ]
 
 [[package]]
@@ -3670,7 +3670,7 @@ dependencies = [
  "termcolor",
  "termize",
  "tracing",
- "windows 0.59.0",
+ "windows",
 ]
 
 [[package]]
@@ -4415,7 +4415,7 @@ dependencies = [
  "smallvec",
  "termize",
  "tracing",
- "windows 0.59.0",
+ "windows",
 ]
 
 [[package]]
@@ -5102,7 +5102,7 @@ dependencies = [
  "libc",
  "objc2-core-foundation",
  "objc2-io-kit",
- "windows 0.61.1",
+ "windows",
 ]
 
 [[package]]
@@ -6004,22 +6004,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1"
-dependencies = [
- "windows-core 0.59.0",
- "windows-targets 0.53.0",
-]
-
-[[package]]
-name = "windows"
 version = "0.61.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
 dependencies = [
  "windows-collections",
- "windows-core 0.61.0",
+ "windows-core",
  "windows-future",
  "windows-link",
  "windows-numerics",
@@ -6042,20 +6032,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
 dependencies = [
- "windows-core 0.61.0",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce"
-dependencies = [
- "windows-implement 0.59.0",
- "windows-interface",
- "windows-result",
- "windows-strings 0.3.1",
- "windows-targets 0.53.0",
+ "windows-core",
 ]
 
 [[package]]
@@ -6064,11 +6041,11 @@ version = "0.61.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
 dependencies = [
- "windows-implement 0.60.0",
+ "windows-implement",
  "windows-interface",
  "windows-link",
  "windows-result",
- "windows-strings 0.4.0",
+ "windows-strings",
 ]
 
 [[package]]
@@ -6077,23 +6054,12 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
 dependencies = [
- "windows-core 0.61.0",
+ "windows-core",
  "windows-link",
 ]
 
 [[package]]
 name = "windows-implement"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.101",
-]
-
-[[package]]
-name = "windows-implement"
 version = "0.60.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
@@ -6126,7 +6092,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
 dependencies = [
- "windows-core 0.61.0",
+ "windows-core",
  "windows-link",
 ]
 
@@ -6141,15 +6107,6 @@ dependencies = [
 
 [[package]]
 name = "windows-strings"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "windows-strings"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4ace80a7344..a16219361c0 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -610,7 +610,7 @@ impl Pat {
     /// Walk top-down and call `it` in each place where a pattern occurs
     /// starting with the root pattern `walk` is called on. If `it` returns
     /// false then we will descend no further but siblings will be processed.
-    pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) {
+    pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) {
         if !it(self) {
             return;
         }
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 3e8ddb8abd4..62ee71fecc2 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -18,7 +18,31 @@ use {rustc_ast as ast, rustc_parse_format as parse};
 use crate::errors;
 use crate::util::{ExprToSpannedString, expr_to_spanned_string};
 
-pub struct AsmArgs {
+/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
+/// not validated at all.
+pub struct AsmArg {
+    pub kind: AsmArgKind,
+    pub span: Span,
+}
+
+pub enum AsmArgKind {
+    Template(P<ast::Expr>),
+    Operand(Option<Symbol>, ast::InlineAsmOperand),
+    Options(Vec<AsmOption>),
+    ClobberAbi(Vec<(Symbol, Span)>),
+}
+
+pub struct AsmOption {
+    pub symbol: Symbol,
+    pub span: Span,
+    // A bitset, with only the bit for this option's symbol set.
+    pub options: ast::InlineAsmOptions,
+    // Used when suggesting to remove an option.
+    pub span_with_comma: Span,
+}
+
+/// Validated assembly arguments, ready for macro expansion.
+struct ValidatedAsmArgs {
     pub templates: Vec<P<ast::Expr>>,
     pub operands: Vec<(ast::InlineAsmOperand, Span)>,
     named_args: FxIndexMap<Symbol, usize>,
@@ -59,41 +83,95 @@ fn eat_operand_keyword<'a>(
     }
 }
 
-fn parse_args<'a>(
-    ecx: &ExtCtxt<'a>,
-    sp: Span,
-    tts: TokenStream,
+fn parse_asm_operand<'a>(
+    p: &mut Parser<'a>,
     asm_macro: AsmMacro,
-) -> PResult<'a, AsmArgs> {
-    let mut p = ecx.new_parser_from_tts(tts);
-    parse_asm_args(&mut p, sp, asm_macro)
+) -> PResult<'a, Option<ast::InlineAsmOperand>> {
+    let dcx = p.dcx();
+
+    Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
+        let reg = parse_reg(p)?;
+        if p.eat_keyword(exp!(Underscore)) {
+            let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
+            return Err(err);
+        }
+        let expr = p.parse_expr()?;
+        ast::InlineAsmOperand::In { reg, expr }
+    } else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
+        let reg = parse_reg(p)?;
+        let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
+        ast::InlineAsmOperand::Out { reg, expr, late: false }
+    } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
+        let reg = parse_reg(p)?;
+        let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
+        ast::InlineAsmOperand::Out { reg, expr, late: true }
+    } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
+        let reg = parse_reg(p)?;
+        if p.eat_keyword(exp!(Underscore)) {
+            let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
+            return Err(err);
+        }
+        let expr = p.parse_expr()?;
+        if p.eat(exp!(FatArrow)) {
+            let out_expr =
+                if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
+            ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
+        } else {
+            ast::InlineAsmOperand::InOut { reg, expr, late: false }
+        }
+    } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
+        let reg = parse_reg(p)?;
+        if p.eat_keyword(exp!(Underscore)) {
+            let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
+            return Err(err);
+        }
+        let expr = p.parse_expr()?;
+        if p.eat(exp!(FatArrow)) {
+            let out_expr =
+                if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
+            ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
+        } else {
+            ast::InlineAsmOperand::InOut { reg, expr, late: true }
+        }
+    } else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
+        let block = p.parse_block()?;
+        ast::InlineAsmOperand::Label { block }
+    } else if p.eat_keyword(exp!(Const)) {
+        let anon_const = p.parse_expr_anon_const()?;
+        ast::InlineAsmOperand::Const { anon_const }
+    } else if p.eat_keyword(exp!(Sym)) {
+        let expr = p.parse_expr()?;
+        let ast::ExprKind::Path(qself, path) = &expr.kind else {
+            let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
+            return Err(err);
+        };
+        let sym =
+            ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
+        ast::InlineAsmOperand::Sym { sym }
+    } else {
+        return Ok(None);
+    }))
 }
 
-// Primarily public for rustfmt consumption.
-// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm`
+// Public for rustfmt.
 pub fn parse_asm_args<'a>(
     p: &mut Parser<'a>,
     sp: Span,
     asm_macro: AsmMacro,
-) -> PResult<'a, AsmArgs> {
+) -> PResult<'a, Vec<AsmArg>> {
     let dcx = p.dcx();
 
     if p.token == token::Eof {
         return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
     }
 
+    let mut args = Vec::new();
+
     let first_template = p.parse_expr()?;
-    let mut args = AsmArgs {
-        templates: vec![first_template],
-        operands: vec![],
-        named_args: Default::default(),
-        reg_args: Default::default(),
-        clobber_abis: Vec::new(),
-        options: ast::InlineAsmOptions::empty(),
-        options_spans: vec![],
-    };
+    args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
 
     let mut allow_templates = true;
+
     while p.token != token::Eof {
         if !p.eat(exp!(Comma)) {
             if allow_templates {
@@ -104,27 +182,39 @@ pub fn parse_asm_args<'a>(
                 return Err(p.expect(exp!(Comma)).err().unwrap());
             }
         }
+
+        // Accept trailing commas.
         if p.token == token::Eof {
             break;
-        } // accept trailing commas
+        }
 
-        // Parse clobber_abi
+        let span_start = p.token.span;
+
+        // Parse `clobber_abi`.
         if p.eat_keyword(exp!(ClobberAbi)) {
-            parse_clobber_abi(p, &mut args)?;
             allow_templates = false;
+
+            args.push(AsmArg {
+                kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
+                span: span_start.to(p.prev_token.span),
+            });
+
             continue;
         }
 
-        // Parse options
+        // Parse `options`.
         if p.eat_keyword(exp!(Options)) {
-            parse_options(p, &mut args, asm_macro)?;
             allow_templates = false;
+
+            args.push(AsmArg {
+                kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
+                span: span_start.to(p.prev_token.span),
+            });
+
             continue;
         }
 
-        let span_start = p.token.span;
-
-        // Parse operand names
+        // Parse operand names.
         let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
             let (ident, _) = p.token.ident().unwrap();
             p.bump();
@@ -135,69 +225,13 @@ pub fn parse_asm_args<'a>(
             None
         };
 
-        let mut explicit_reg = false;
-        let op = if eat_operand_keyword(p, exp!(In), asm_macro)? {
-            let reg = parse_reg(p, &mut explicit_reg)?;
-            if p.eat_keyword(exp!(Underscore)) {
-                let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
-                return Err(err);
-            }
-            let expr = p.parse_expr()?;
-            ast::InlineAsmOperand::In { reg, expr }
-        } else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
-            let reg = parse_reg(p, &mut explicit_reg)?;
-            let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
-            ast::InlineAsmOperand::Out { reg, expr, late: false }
-        } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
-            let reg = parse_reg(p, &mut explicit_reg)?;
-            let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
-            ast::InlineAsmOperand::Out { reg, expr, late: true }
-        } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
-            let reg = parse_reg(p, &mut explicit_reg)?;
-            if p.eat_keyword(exp!(Underscore)) {
-                let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
-                return Err(err);
-            }
-            let expr = p.parse_expr()?;
-            if p.eat(exp!(FatArrow)) {
-                let out_expr =
-                    if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
-                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
-            } else {
-                ast::InlineAsmOperand::InOut { reg, expr, late: false }
-            }
-        } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
-            let reg = parse_reg(p, &mut explicit_reg)?;
-            if p.eat_keyword(exp!(Underscore)) {
-                let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
-                return Err(err);
-            }
-            let expr = p.parse_expr()?;
-            if p.eat(exp!(FatArrow)) {
-                let out_expr =
-                    if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
-                ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
-            } else {
-                ast::InlineAsmOperand::InOut { reg, expr, late: true }
-            }
-        } else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
-            let block = p.parse_block()?;
-            ast::InlineAsmOperand::Label { block }
-        } else if p.eat_keyword(exp!(Const)) {
-            let anon_const = p.parse_expr_anon_const()?;
-            ast::InlineAsmOperand::Const { anon_const }
-        } else if p.eat_keyword(exp!(Sym)) {
-            let expr = p.parse_expr()?;
-            let ast::ExprKind::Path(qself, path) = &expr.kind else {
-                let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
-                return Err(err);
-            };
-            let sym = ast::InlineAsmSym {
-                id: ast::DUMMY_NODE_ID,
-                qself: qself.clone(),
-                path: path.clone(),
-            };
-            ast::InlineAsmOperand::Sym { sym }
+        if let Some(op) = parse_asm_operand(p, asm_macro)? {
+            allow_templates = false;
+
+            args.push(AsmArg {
+                span: span_start.to(p.prev_token.span),
+                kind: AsmArgKind::Operand(name, op),
+            });
         } else if allow_templates {
             let template = p.parse_expr()?;
             // If it can't possibly expand to a string, provide diagnostics here to include other
@@ -217,55 +251,164 @@ pub fn parse_asm_args<'a>(
                     return Err(err);
                 }
             }
-            args.templates.push(template);
-            continue;
+
+            args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
         } else {
             p.unexpected_any()?
-        };
+        }
+    }
+
+    Ok(args)
+}
 
-        allow_templates = false;
-        let span = span_start.to(p.prev_token.span);
-        let slot = args.operands.len();
-        args.operands.push((op, span));
-
-        // Validate the order of named, positional & explicit register operands and
-        // clobber_abi/options. We do this at the end once we have the full span
-        // of the argument available.
-        if explicit_reg {
-            if name.is_some() {
-                dcx.emit_err(errors::AsmExplicitRegisterName { span });
+fn parse_args<'a>(
+    ecx: &ExtCtxt<'a>,
+    sp: Span,
+    tts: TokenStream,
+    asm_macro: AsmMacro,
+) -> PResult<'a, ValidatedAsmArgs> {
+    let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?;
+    validate_asm_args(ecx, asm_macro, args)
+}
+
+fn validate_asm_args<'a>(
+    ecx: &ExtCtxt<'a>,
+    asm_macro: AsmMacro,
+    args: Vec<AsmArg>,
+) -> PResult<'a, ValidatedAsmArgs> {
+    let dcx = ecx.dcx();
+
+    let mut validated = ValidatedAsmArgs {
+        templates: vec![],
+        operands: vec![],
+        named_args: Default::default(),
+        reg_args: Default::default(),
+        clobber_abis: Vec::new(),
+        options: ast::InlineAsmOptions::empty(),
+        options_spans: vec![],
+    };
+
+    let mut allow_templates = true;
+
+    for arg in args {
+        match arg.kind {
+            AsmArgKind::Template(template) => {
+                // The error for the first template is delayed.
+                if !allow_templates {
+                    match template.kind {
+                        ast::ExprKind::Lit(token_lit)
+                            if matches!(
+                                token_lit.kind,
+                                token::LitKind::Str | token::LitKind::StrRaw(_)
+                            ) => {}
+                        ast::ExprKind::MacCall(..) => {}
+                        _ => {
+                            let err = dcx.create_err(errors::AsmExpectedOther {
+                                span: template.span,
+                                is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
+                            });
+                            return Err(err);
+                        }
+                    }
+                }
+
+                validated.templates.push(template);
             }
-            args.reg_args.insert(slot);
-        } else if let Some(name) = name {
-            if let Some(&prev) = args.named_args.get(&name) {
-                dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
-                continue;
+            AsmArgKind::Operand(name, op) => {
+                allow_templates = false;
+
+                let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
+                let span = arg.span;
+                let slot = validated.operands.len();
+                validated.operands.push((op, span));
+
+                // Validate the order of named, positional & explicit register operands and
+                // clobber_abi/options. We do this at the end once we have the full span
+                // of the argument available.
+
+                if explicit_reg {
+                    if name.is_some() {
+                        dcx.emit_err(errors::AsmExplicitRegisterName { span });
+                    }
+                    validated.reg_args.insert(slot);
+                } else if let Some(name) = name {
+                    if let Some(&prev) = validated.named_args.get(&name) {
+                        dcx.emit_err(errors::AsmDuplicateArg {
+                            span,
+                            name,
+                            prev: validated.operands[prev].1,
+                        });
+                        continue;
+                    }
+                    validated.named_args.insert(name, slot);
+                } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() {
+                    let named =
+                        validated.named_args.values().map(|p| validated.operands[*p].1).collect();
+                    let explicit =
+                        validated.reg_args.iter().map(|p| validated.operands[p].1).collect();
+
+                    dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
+                }
             }
-            args.named_args.insert(name, slot);
-        } else if !args.named_args.is_empty() || !args.reg_args.is_empty() {
-            let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
-            let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
+            AsmArgKind::Options(new_options) => {
+                allow_templates = false;
+
+                for asm_option in new_options {
+                    let AsmOption { span, symbol, span_with_comma, options } = asm_option;
 
-            dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
+                    if !asm_macro.is_supported_option(options) {
+                        // Tool-only output.
+                        dcx.emit_err(errors::AsmUnsupportedOption {
+                            span,
+                            symbol,
+                            span_with_comma,
+                            macro_name: asm_macro.macro_name(),
+                        });
+                    } else if validated.options.contains(options) {
+                        // Tool-only output.
+                        dcx.emit_err(errors::AsmOptAlreadyprovided {
+                            span,
+                            symbol,
+                            span_with_comma,
+                        });
+                    } else {
+                        validated.options |= asm_option.options;
+                    }
+                }
+
+                validated.options_spans.push(arg.span);
+            }
+            AsmArgKind::ClobberAbi(new_abis) => {
+                allow_templates = false;
+
+                match &new_abis[..] {
+                    // This should have errored above during parsing.
+                    [] => unreachable!(),
+                    [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)),
+                    _ => validated.clobber_abis.extend(new_abis),
+                }
+            }
         }
     }
 
-    if args.options.contains(ast::InlineAsmOptions::NOMEM)
-        && args.options.contains(ast::InlineAsmOptions::READONLY)
+    if validated.options.contains(ast::InlineAsmOptions::NOMEM)
+        && validated.options.contains(ast::InlineAsmOptions::READONLY)
     {
-        let spans = args.options_spans.clone();
+        let spans = validated.options_spans.clone();
         dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
     }
-    if args.options.contains(ast::InlineAsmOptions::PURE)
-        && args.options.contains(ast::InlineAsmOptions::NORETURN)
+    if validated.options.contains(ast::InlineAsmOptions::PURE)
+        && validated.options.contains(ast::InlineAsmOptions::NORETURN)
     {
-        let spans = args.options_spans.clone();
+        let spans = validated.options_spans.clone();
         dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
     }
-    if args.options.contains(ast::InlineAsmOptions::PURE)
-        && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
+    if validated.options.contains(ast::InlineAsmOptions::PURE)
+        && !validated
+            .options
+            .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
     {
-        let spans = args.options_spans.clone();
+        let spans = validated.options_spans.clone();
         dcx.emit_err(errors::AsmPureCombine { spans });
     }
 
@@ -273,7 +416,7 @@ pub fn parse_asm_args<'a>(
     let mut outputs_sp = vec![];
     let mut regclass_outputs = vec![];
     let mut labels_sp = vec![];
-    for (op, op_sp) in &args.operands {
+    for (op, op_sp) in &validated.operands {
         match op {
             ast::InlineAsmOperand::Out { reg, expr, .. }
             | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
@@ -296,10 +439,10 @@ pub fn parse_asm_args<'a>(
             _ => {}
         }
     }
-    if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
-        dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
+    if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
+        dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() });
     }
-    if args.options.contains(ast::InlineAsmOptions::NORETURN)
+    if validated.options.contains(ast::InlineAsmOptions::NORETURN)
         && !outputs_sp.is_empty()
         && labels_sp.is_empty()
     {
@@ -307,15 +450,15 @@ pub fn parse_asm_args<'a>(
         // Bail out now since this is likely to confuse MIR
         return Err(err);
     }
-    if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
+    if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
         dcx.emit_err(errors::AsmMayUnwind { labels_sp });
     }
 
-    if !args.clobber_abis.is_empty() {
+    if !validated.clobber_abis.is_empty() {
         match asm_macro {
             AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
                 let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
-                    spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
+                    spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
                     macro_name: asm_macro.macro_name(),
                 });
 
@@ -326,71 +469,21 @@ pub fn parse_asm_args<'a>(
                 if !regclass_outputs.is_empty() {
                     dcx.emit_err(errors::AsmClobberNoReg {
                         spans: regclass_outputs,
-                        clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
+                        clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
                     });
                 }
             }
         }
     }
 
-    Ok(args)
+    Ok(validated)
 }
 
-/// Report a duplicate option error.
-///
-/// This function must be called immediately after the option token is parsed.
-/// Otherwise, the suggestion will be incorrect.
-fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
-    // Tool-only output
-    let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
-    p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
-}
-
-/// Report an invalid option error.
-///
-/// This function must be called immediately after the option token is parsed.
-/// Otherwise, the suggestion will be incorrect.
-fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) {
-    // Tool-only output
-    let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
-    p.dcx().emit_err(errors::AsmUnsupportedOption {
-        span,
-        symbol,
-        full_span,
-        macro_name: asm_macro.macro_name(),
-    });
-}
-
-/// Try to set the provided option in the provided `AsmArgs`.
-/// If it is already set, report a duplicate option error.
-///
-/// This function must be called immediately after the option token is parsed.
-/// Otherwise, the error will not point to the correct spot.
-fn try_set_option<'a>(
-    p: &Parser<'a>,
-    args: &mut AsmArgs,
-    asm_macro: AsmMacro,
-    symbol: Symbol,
-    option: ast::InlineAsmOptions,
-) {
-    if !asm_macro.is_supported_option(option) {
-        err_unsupported_option(p, asm_macro, symbol, p.prev_token.span);
-    } else if args.options.contains(option) {
-        err_duplicate_option(p, symbol, p.prev_token.span);
-    } else {
-        args.options |= option;
-    }
-}
-
-fn parse_options<'a>(
-    p: &mut Parser<'a>,
-    args: &mut AsmArgs,
-    asm_macro: AsmMacro,
-) -> PResult<'a, ()> {
-    let span_start = p.prev_token.span;
-
+fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
     p.expect(exp!(OpenParen))?;
 
+    let mut asm_options = Vec::new();
+
     while !p.eat(exp!(CloseParen)) {
         const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
             (exp!(Pure), ast::InlineAsmOptions::PURE),
@@ -405,38 +498,38 @@ fn parse_options<'a>(
         ];
 
         'blk: {
-            for (exp, option) in OPTIONS {
-                let kw_matched = if asm_macro.is_supported_option(option) {
+            for (exp, options) in OPTIONS {
+                // Gives a more accurate list of expected next tokens.
+                let kw_matched = if asm_macro.is_supported_option(options) {
                     p.eat_keyword(exp)
                 } else {
                     p.eat_keyword_noexpect(exp.kw)
                 };
 
                 if kw_matched {
-                    try_set_option(p, args, asm_macro, exp.kw, option);
+                    let span = p.prev_token.span;
+                    let span_with_comma =
+                        if p.token == token::Comma { span.to(p.token.span) } else { span };
+
+                    asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
                     break 'blk;
                 }
             }
 
-            return p.unexpected();
+            return p.unexpected_any();
         }
 
-        // Allow trailing commas
+        // Allow trailing commas.
         if p.eat(exp!(CloseParen)) {
             break;
         }
         p.expect(exp!(Comma))?;
     }
 
-    let new_span = span_start.to(p.prev_token.span);
-    args.options_spans.push(new_span);
-
-    Ok(())
+    Ok(asm_options)
 }
 
-fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
-    let span_start = p.prev_token.span;
-
+fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
     p.expect(exp!(OpenParen))?;
 
     if p.eat(exp!(CloseParen)) {
@@ -462,31 +555,14 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
         p.expect(exp!(Comma))?;
     }
 
-    let full_span = span_start.to(p.prev_token.span);
-
-    match &new_abis[..] {
-        // should have errored above during parsing
-        [] => unreachable!(),
-        [(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
-        abis => {
-            for (abi, span) in abis {
-                args.clobber_abis.push((*abi, *span));
-            }
-        }
-    }
-
-    Ok(())
+    Ok(new_abis)
 }
 
-fn parse_reg<'a>(
-    p: &mut Parser<'a>,
-    explicit_reg: &mut bool,
-) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
+fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
     p.expect(exp!(OpenParen))?;
     let result = match p.token.uninterpolate().kind {
         token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
         token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
-            *explicit_reg = true;
             ast::InlineAsmRegOrRegClass::Reg(symbol)
         }
         _ => {
@@ -503,7 +579,7 @@ fn parse_reg<'a>(
 fn expand_preparsed_asm(
     ecx: &mut ExtCtxt<'_>,
     asm_macro: AsmMacro,
-    args: AsmArgs,
+    args: ValidatedAsmArgs,
 ) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
     let mut template = vec![];
     // Register operands are implicitly used since they are not allowed to be
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index d14ad8f4014..b28f7d312d9 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -910,7 +910,7 @@ pub(crate) struct AsmOptAlreadyprovided {
     pub(crate) span: Span,
     pub(crate) symbol: Symbol,
     #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
-    pub(crate) full_span: Span,
+    pub(crate) span_with_comma: Span,
 }
 
 #[derive(Diagnostic)]
@@ -921,7 +921,7 @@ pub(crate) struct AsmUnsupportedOption {
     pub(crate) span: Span,
     pub(crate) symbol: Symbol,
     #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
-    pub(crate) full_span: Span,
+    pub(crate) span_with_comma: Span,
     pub(crate) macro_name: &'static str,
 }
 
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 97eebffd1fe..d4c8ab80a33 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -58,5 +58,5 @@ default-features = false
 features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"]
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.59.0"
+version = "0.61.0"
 features = ["Win32_Globalization"]
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index f48c73b13b9..f6a02011618 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -38,7 +38,7 @@ features = ["nightly"] # for may_dangle
 version = "0.12"
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.59.0"
+version = "0.61.0"
 features = [
     "Win32_Foundation",
     "Win32_Storage_FileSystem",
diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml
index 9da4f2dbc27..1971d06aad6 100644
--- a/compiler/rustc_driver_impl/Cargo.toml
+++ b/compiler/rustc_driver_impl/Cargo.toml
@@ -60,7 +60,7 @@ libc = "0.2"
 # tidy-alphabetical-end
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.59.0"
+version = "0.61.0"
 features = [
     "Win32_System_Diagnostics_Debug",
 ]
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index b11793c190a..82e7468211d 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -33,7 +33,7 @@ tracing = "0.1"
 # tidy-alphabetical-end
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.59.0"
+version = "0.61.0"
 features = [
     "Win32_Foundation",
     "Win32_Security",
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 809d1630dde..820af9ac84b 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -82,6 +82,8 @@ declare_features! (
     (accepted, attr_literals, "1.30.0", Some(34981)),
     /// Allows overloading augmented assignment operations like `a += b`.
     (accepted, augmented_assignments, "1.8.0", Some(28235)),
+    /// Allows using `avx512*` target features.
+    (accepted, avx512_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
     /// Allows mixing bind-by-move in patterns and references to those identifiers in guards.
     (accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287)),
     /// Allows bindings in the subpattern of a binding pattern.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 8fb10736539..6cdcf451f37 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -318,7 +318,6 @@ declare_features! (
     (unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)),
     (unstable, apx_target_feature, "1.88.0", Some(139284)),
     (unstable, arm_target_feature, "1.27.0", Some(44839)),
-    (unstable, avx512_target_feature, "1.27.0", Some(44839)),
     (unstable, bpf_target_feature, "1.54.0", Some(44839)),
     (unstable, csky_target_feature, "1.73.0", Some(44839)),
     (unstable, ermsb_target_feature, "1.49.0", Some(44839)),
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 956671fc66e..7d99b0e7869 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -218,7 +218,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
             );
         }
         let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
-        intravisit::walk_pat(self, p);
+        if let PatKind::Guard(subpat, _) = p.kind {
+            // We'll visit the guard when checking it. Don't gather its locals twice.
+            self.visit_pat(subpat);
+        } else {
+            intravisit::walk_pat(self, p);
+        }
         self.outermost_fn_param_pat = old_outermost_fn_param_pat;
     }
 
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 8dde99c45cf..5fd98e35e5c 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -98,6 +98,14 @@ impl<'tcx> InferCtxt<'tcx> {
         sub_region: Region<'tcx>,
         cause: &ObligationCause<'tcx>,
     ) {
+        // `is_global` means the type has no params, infer, placeholder, or non-`'static`
+        // free regions. If the type has none of these things, then we can skip registering
+        // this outlives obligation since it has no components which affect lifetime
+        // checking in an interesting way.
+        if sup_type.is_global() {
+            return;
+        }
+
         debug!(?sup_type, ?sub_region, ?cause);
         let origin = SubregionOrigin::from_obligation_cause(cause, || {
             infer::RelateParamBound(
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
index 6692ea73540..a662694ac38 100644
--- a/compiler/rustc_llvm/build.rs
+++ b/compiler/rustc_llvm/build.rs
@@ -255,6 +255,7 @@ fn main() {
     } else if target.contains("haiku")
         || target.contains("darwin")
         || (is_crossed && (target.contains("dragonfly") || target.contains("solaris")))
+        || target.contains("cygwin")
     {
         println!("cargo:rustc-link-lib=z");
     } else if target.contains("netbsd") {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 657bda23bc4..b2a58897c31 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -121,6 +121,7 @@ use crate::ty;
 use crate::ty::codec::{TyDecoder, TyEncoder};
 pub use crate::ty::diagnostics::*;
 use crate::ty::fast_reject::SimplifiedType;
+use crate::ty::layout::LayoutError;
 use crate::ty::util::Discr;
 use crate::ty::walk::TypeWalker;
 
@@ -1878,6 +1879,11 @@ impl<'tcx> TyCtxt<'tcx> {
         self.def_kind(trait_def_id) == DefKind::TraitAlias
     }
 
+    /// Arena-alloc of LayoutError for coroutine layout
+    fn layout_error(self, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> {
+        self.arena.alloc(err)
+    }
+
     /// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the
     /// coroutine is tainted by errors.
     ///
@@ -1886,12 +1892,14 @@ impl<'tcx> TyCtxt<'tcx> {
     fn ordinary_coroutine_layout(
         self,
         def_id: DefId,
-        coroutine_kind_ty: Ty<'tcx>,
-    ) -> Option<&'tcx CoroutineLayout<'tcx>> {
+        args: GenericArgsRef<'tcx>,
+    ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> {
+        let coroutine_kind_ty = args.as_coroutine().kind_ty();
         let mir = self.optimized_mir(def_id);
+        let ty = || Ty::new_coroutine(self, def_id, args);
         // Regular coroutine
         if coroutine_kind_ty.is_unit() {
-            mir.coroutine_layout_raw()
+            mir.coroutine_layout_raw().ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
         } else {
             // If we have a `Coroutine` that comes from an coroutine-closure,
             // then it may be a by-move or by-ref body.
@@ -1905,6 +1913,7 @@ impl<'tcx> TyCtxt<'tcx> {
             // a by-ref coroutine.
             if identity_kind_ty == coroutine_kind_ty {
                 mir.coroutine_layout_raw()
+                    .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
             } else {
                 assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce));
                 assert_matches!(
@@ -1913,6 +1922,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 );
                 self.optimized_mir(self.coroutine_by_move_body_def_id(def_id))
                     .coroutine_layout_raw()
+                    .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
             }
         }
     }
@@ -1924,12 +1934,15 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
-    ) -> Option<&'tcx CoroutineLayout<'tcx>> {
+    ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> {
+        let ty = || Ty::new_coroutine(self, def_id, args);
         if args[0].has_placeholders() || args[0].has_non_region_param() {
-            return None;
+            return Err(self.layout_error(LayoutError::TooGeneric(ty())));
         }
         let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args));
-        self.mir_shims(instance).coroutine_layout_raw()
+        self.mir_shims(instance)
+            .coroutine_layout_raw()
+            .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
     }
 
     /// Returns layout of a coroutine. Layout might be unavailable if the
@@ -1938,7 +1951,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
-    ) -> Option<&'tcx CoroutineLayout<'tcx>> {
+    ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> {
         if self.is_async_drop_in_place_coroutine(def_id) {
             // layout of `async_drop_in_place<T>::{closure}` in case,
             // when T is a coroutine, contains this internal coroutine's ptr in upvars
@@ -1960,12 +1973,12 @@ impl<'tcx> TyCtxt<'tcx> {
                     variant_source_info,
                     storage_conflicts: BitMatrix::new(0, 0),
                 };
-                return Some(self.arena.alloc(proxy_layout));
+                return Ok(self.arena.alloc(proxy_layout));
             } else {
                 self.async_drop_coroutine_layout(def_id, args)
             }
         } else {
-            self.ordinary_coroutine_layout(def_id, args.as_coroutine().kind_ty())
+            self.ordinary_coroutine_layout(def_id, args)
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 8b8d1efbbd2..209e818e9e3 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -836,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     #[instrument(level = "trace", skip(self), ret)]
     fn simplify_rvalue(
         &mut self,
+        lhs: &Place<'tcx>,
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
     ) -> Option<VnIndex> {
@@ -855,7 +856,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 Value::Repeat(op, amount)
             }
             Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty),
-            Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
+            Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
             Rvalue::Ref(_, borrow_kind, ref mut place) => {
                 self.simplify_place_projection(place, location);
                 return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
@@ -943,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
     fn simplify_aggregate_to_copy(
         &mut self,
+        lhs: &Place<'tcx>,
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
         fields: &[VnIndex],
@@ -982,12 +984,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         // Allow introducing places with non-constant offsets, as those are still better than
         // reconstructing an aggregate.
-        if let Some(place) = self.try_as_place(copy_from_local_value, location, true) {
-            if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty {
+        if let Some(place) = self.try_as_place(copy_from_local_value, location, true)
+            && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty
+        {
+            // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments.
+            // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections.
+            if lhs.as_local().is_some() {
                 self.reused_locals.insert(place.local);
                 *rvalue = Rvalue::Use(Operand::Copy(place));
-                return Some(copy_from_local_value);
             }
+            return Some(copy_from_local_value);
         }
 
         None
@@ -995,6 +1001,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
     fn simplify_aggregate(
         &mut self,
+        lhs: &Place<'tcx>,
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
     ) -> Option<VnIndex> {
@@ -1090,7 +1097,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         if let AggregateTy::Def(_, _) = ty
             && let Some(value) =
-                self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
+                self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
         {
             return Some(value);
         }
@@ -1765,7 +1772,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
         if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
             self.simplify_place_projection(lhs, location);
 
-            let value = self.simplify_rvalue(rvalue, location);
+            let value = self.simplify_rvalue(lhs, rvalue, location);
             let value = if let Some(local) = lhs.as_local()
                 && self.ssa.is_ssa(local)
                 // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index f541a32cd26..f8d1629b0e2 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -752,7 +752,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             let layout = if def_id == self.caller_body.source.def_id() {
                                 self.caller_body
                                     .coroutine_layout_raw()
-                                    .or_else(|| self.tcx.coroutine_layout(def_id, args))
+                                    .or_else(|| self.tcx.coroutine_layout(def_id, args).ok())
                             } else if self.tcx.needs_coroutine_by_move_body_def_id(def_id)
                                 && let ty::ClosureKind::FnOnce =
                                     args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap()
@@ -762,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                                 // Same if this is the by-move body of a coroutine-closure.
                                 self.caller_body.coroutine_layout_raw()
                             } else {
-                                self.tcx.coroutine_layout(def_id, args)
+                                self.tcx.coroutine_layout(def_id, args).ok()
                             };
 
                             let Some(layout) = layout else {
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index f63d8b2d79f..2b85d7b26ce 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -6,7 +6,6 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 #![allow(unused_crate_dependencies)]
-#![cfg_attr(all(feature = "rustc", bootstrap), feature(let_chains))]
 // tidy-alphabetical-end
 
 pub mod constructor;
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 0b16983c2c7..d09750fa281 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -2493,7 +2493,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else {
             return None;
         };
-        let module_name = crate_module.kind.name().unwrap_or(kw::Empty);
+        let module_name = crate_module.kind.name().unwrap_or(kw::Crate);
         let import_snippet = match import.kind {
             ImportKind::Single { source, target, .. } if source != target => {
                 format!("{source} as {target}")
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index faee0e7dd5f..1b682d0cf8a 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -111,6 +111,19 @@ enum PatBoundCtx {
     Or,
 }
 
+/// Tracks bindings resolved within a pattern. This serves two purposes:
+///
+/// - This tracks when identifiers are bound multiple times within a pattern. In a product context,
+///   this is an error. In an or-pattern, this lets us reuse the same resolution for each instance.
+///   See `fresh_binding` and `resolve_pattern_inner` for more information.
+///
+/// - The guard expression of a guard pattern may use bindings from within the guard pattern, but
+///   not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the
+///   subpattern to construct the scope for the guard.
+///
+/// Each identifier must map to at most one distinct [`Res`].
+type PatternBindings = SmallVec<[(PatBoundCtx, FxIndexMap<Ident, Res>); 1]>;
+
 /// Does this the item (from the item rib scope) allow generic parameters?
 #[derive(Copy, Clone, Debug)]
 pub(crate) enum HasGenericParams {
@@ -786,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
     fn visit_pat(&mut self, p: &'ast Pat) {
         let prev = self.diag_metadata.current_pat;
         self.diag_metadata.current_pat = Some(p);
-        visit::walk_pat(self, p);
+
+        if let PatKind::Guard(subpat, _) = &p.kind {
+            // We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice.
+            self.visit_pat(subpat);
+        } else {
+            visit::walk_pat(self, p);
+        }
+
         self.diag_metadata.current_pat = prev;
     }
     fn visit_local(&mut self, local: &'ast Local) {
@@ -2297,7 +2317,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
     fn resolve_fn_params(
         &mut self,
         has_self: bool,
-        inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
+        inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
     ) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
         enum Elision {
             /// We have not found any candidate.
@@ -2319,15 +2339,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         let mut parameter_info = Vec::new();
         let mut all_candidates = Vec::new();
 
+        // Resolve and apply bindings first so diagnostics can see if they're used in types.
         let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
-        for (index, (pat, ty)) in inputs.enumerate() {
-            debug!(?pat, ?ty);
+        for (pat, _) in inputs.clone() {
+            debug!("resolving bindings in pat = {pat:?}");
             self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
                 if let Some(pat) = pat {
                     this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
                 }
             });
+        }
+        self.apply_pattern_bindings(bindings);
 
+        for (index, (pat, ty)) in inputs.enumerate() {
+            debug!("resolving type for pat = {pat:?}, ty = {ty:?}");
             // Record elision candidates only for this parameter.
             debug_assert_matches!(self.lifetime_elision_candidates, None);
             self.lifetime_elision_candidates = Some(Default::default());
@@ -3615,16 +3640,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         self.visit_path(&delegation.path, delegation.id);
         let Some(body) = &delegation.body else { return };
         self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
-            // `PatBoundCtx` is not necessary in this context
-            let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
-
             let span = delegation.path.segments.last().unwrap().ident.span;
-            this.fresh_binding(
-                Ident::new(kw::SelfLower, span),
-                delegation.id,
-                PatternSource::FnParam,
-                &mut bindings,
-            );
+            let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules());
+            let res = Res::Local(delegation.id);
+            this.innermost_rib_bindings(ValueNS).insert(ident, res);
             this.visit_block(body);
         });
     }
@@ -3635,6 +3654,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             for Param { pat, .. } in params {
                 this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
             }
+            this.apply_pattern_bindings(bindings);
         });
         for Param { ty, .. } in params {
             self.visit_ty(ty);
@@ -3851,13 +3871,32 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
     fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) {
         let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
         self.resolve_pattern(pat, pat_src, &mut bindings);
+        self.apply_pattern_bindings(bindings);
+    }
+
+    /// Apply the bindings from a pattern to the innermost rib of the current scope.
+    fn apply_pattern_bindings(&mut self, mut pat_bindings: PatternBindings) {
+        let rib_bindings = self.innermost_rib_bindings(ValueNS);
+        let Some((_, pat_bindings)) = pat_bindings.pop() else {
+            bug!("tried applying nonexistent bindings from pattern");
+        };
+
+        if rib_bindings.is_empty() {
+            // Often, such as for match arms, the bindings are introduced into a new rib.
+            // In this case, we can move the bindings over directly.
+            *rib_bindings = pat_bindings;
+        } else {
+            rib_bindings.extend(pat_bindings);
+        }
     }
 
+    /// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce
+    /// the bindings into scope.
     fn resolve_pattern(
         &mut self,
         pat: &'ast Pat,
         pat_src: PatternSource,
-        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+        bindings: &mut PatternBindings,
     ) {
         // We walk the pattern before declaring the pattern's inner bindings,
         // so that we avoid resolving a literal expression to a binding defined
@@ -3890,9 +3929,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
     #[tracing::instrument(skip(self, bindings), level = "debug")]
     fn resolve_pattern_inner(
         &mut self,
-        pat: &Pat,
+        pat: &'ast Pat,
         pat_src: PatternSource,
-        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+        bindings: &mut PatternBindings,
     ) {
         // Visit all direct subpatterns of this pattern.
         pat.walk(&mut |pat| {
@@ -3950,6 +3989,31 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     // Prevent visiting `ps` as we've already done so above.
                     return false;
                 }
+                PatKind::Guard(ref subpat, ref guard) => {
+                    // Add a new set of bindings to the stack to collect bindings in `subpat`.
+                    bindings.push((PatBoundCtx::Product, Default::default()));
+                    // Resolving `subpat` adds bindings onto the newly-pushed context. After, the
+                    // total number of contexts on the stack should be the same as before.
+                    let binding_ctx_stack_len = bindings.len();
+                    self.resolve_pattern_inner(subpat, pat_src, bindings);
+                    assert_eq!(bindings.len(), binding_ctx_stack_len);
+                    // These bindings, but none from the surrounding pattern, are visible in the
+                    // guard; put them in scope and resolve `guard`.
+                    let subpat_bindings = bindings.pop().unwrap().1;
+                    self.with_rib(ValueNS, RibKind::Normal, |this| {
+                        *this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone();
+                        this.resolve_expr(guard, None);
+                    });
+                    // Propagate the subpattern's bindings upwards.
+                    // FIXME(guard_patterns): For `if let` guards, we'll also need to get the
+                    // bindings introduced by the guard from its rib and propagate them upwards.
+                    // This will require checking the identifiers for overlaps with `bindings`, like
+                    // what `fresh_binding` does (ideally sharing its logic). To keep them separate
+                    // from `subpat_bindings`, we can introduce a fresh rib for the guard.
+                    bindings.last_mut().unwrap().1.extend(subpat_bindings);
+                    // Prevent visiting `subpat` as we've already done so above.
+                    return false;
+                }
                 _ => {}
             }
             true
@@ -3988,20 +4052,17 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         ident: Ident,
         pat_id: NodeId,
         pat_src: PatternSource,
-        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+        bindings: &mut PatternBindings,
     ) -> Res {
-        // Add the binding to the local ribs, if it doesn't already exist in the bindings map.
+        // Add the binding to the bindings map, if it doesn't already exist.
         // (We must not add it if it's in the bindings map because that breaks the assumptions
         // later passes make about or-patterns.)
         let ident = ident.normalize_to_macro_rules();
 
-        let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident));
         // Already bound in a product pattern? e.g. `(a, a)` which is not allowed.
-        let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product);
-        // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
-        // This is *required* for consistency which is checked later.
-        let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or);
-
+        let already_bound_and = bindings
+            .iter()
+            .any(|(ctx, map)| *ctx == PatBoundCtx::Product && map.contains_key(&ident));
         if already_bound_and {
             // Overlap in a product pattern somewhere; report an error.
             use ResolutionError::*;
@@ -4014,19 +4075,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             self.report_error(ident.span, error(ident));
         }
 
-        // Record as bound.
-        bindings.last_mut().unwrap().1.insert(ident);
-
-        if already_bound_or {
+        // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
+        // This is *required* for consistency which is checked later.
+        let already_bound_or = bindings
+            .iter()
+            .find_map(|(ctx, map)| if *ctx == PatBoundCtx::Or { map.get(&ident) } else { None });
+        let res = if let Some(&res) = already_bound_or {
             // `Variant1(a) | Variant2(a)`, ok
             // Reuse definition from the first `a`.
-            self.innermost_rib_bindings(ValueNS)[&ident]
-        } else {
-            // A completely fresh binding is added to the set.
-            let res = Res::Local(pat_id);
-            self.innermost_rib_bindings(ValueNS).insert(ident, res);
             res
-        }
+        } else {
+            // A completely fresh binding is added to the map.
+            Res::Local(pat_id)
+        };
+
+        // Record as bound.
+        bindings.last_mut().unwrap().1.insert(ident, res);
+        res
     }
 
     fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut FxIndexMap<Ident, Res> {
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 63772a32222..f0ee19e3c67 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -33,7 +33,7 @@ libc = "0.2"
 # tidy-alphabetical-end
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.59.0"
+version = "0.61.0"
 features = [
     "Win32_Foundation",
     "Win32_System_LibraryLoader",
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index bdeca91eb64..207ba5157bd 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -78,10 +78,16 @@ fn current_dll_path() -> Result<PathBuf, String> {
                 if libc::dladdr(addr, &mut info) == 0 {
                     return Err("dladdr failed".into());
                 }
-                if info.dli_fname.is_null() {
-                    return Err("dladdr returned null pointer".into());
-                }
-                let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
+                #[cfg(target_os = "cygwin")]
+                let fname_ptr = info.dli_fname.as_ptr();
+                #[cfg(not(target_os = "cygwin"))]
+                let fname_ptr = {
+                    if info.dli_fname.is_null() {
+                        return Err("dladdr returned null pointer".into());
+                    }
+                    info.dli_fname
+                };
+                let bytes = CStr::from_ptr(fname_ptr).to_bytes();
                 let os = OsStr::from_bytes(bytes);
                 Ok(PathBuf::from(os))
             }
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs
index c5704c57448..f1b6fa123de 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs
@@ -1,4 +1,4 @@
-use crate::spec::{FramePointer, LinkerFlavor, Lld, Target, TargetMetadata, base};
+use crate::spec::{FramePointer, Target, TargetMetadata, base};
 
 pub(crate) fn target() -> Target {
     let mut base = base::windows_msvc::opts();
@@ -11,11 +11,6 @@ pub(crate) fn target() -> Target {
     // and other services. It must point to the previous {x29, x30} pair on the stack."
     base.frame_pointer = FramePointer::NonLeaf;
 
-    // MSVC emits a warning about code that may trip "Cortex-A53 MPCore processor bug #843419" (see
-    // https://developer.arm.com/documentation/epm048406/latest) which is sometimes emitted by LLVM.
-    // Since Arm64 Windows 10+ isn't supported on that processor, it's safe to disable the warning.
-    base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &["/arm64hazardfree"]);
-
     Target {
         llvm_target: "aarch64-pc-windows-msvc".into(),
         metadata: TargetMetadata {
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 5428aa4cf70..99b04ac2720 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -416,25 +416,25 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ),
     ("avx10.2", Unstable(sym::avx10_target_feature), &["avx10.1"]),
     ("avx2", Stable, &["avx"]),
-    ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
-    ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]),
-    ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]),
-    ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
-    ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]),
-    ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]),
-    ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]),
-    ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]),
-    ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]),
-    ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]),
-    ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]),
-    ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]),
+    ("avx512bf16", Stable, &["avx512bw"]),
+    ("avx512bitalg", Stable, &["avx512bw"]),
+    ("avx512bw", Stable, &["avx512f"]),
+    ("avx512cd", Stable, &["avx512f"]),
+    ("avx512dq", Stable, &["avx512f"]),
+    ("avx512f", Stable, &["avx2", "fma", "f16c"]),
+    ("avx512fp16", Stable, &["avx512bw"]),
+    ("avx512ifma", Stable, &["avx512f"]),
+    ("avx512vbmi", Stable, &["avx512bw"]),
+    ("avx512vbmi2", Stable, &["avx512bw"]),
+    ("avx512vl", Stable, &["avx512f"]),
+    ("avx512vnni", Stable, &["avx512f"]),
+    ("avx512vp2intersect", Stable, &["avx512f"]),
+    ("avx512vpopcntdq", Stable, &["avx512f"]),
+    ("avxifma", Stable, &["avx2"]),
+    ("avxneconvert", Stable, &["avx2"]),
+    ("avxvnni", Stable, &["avx2"]),
+    ("avxvnniint16", Stable, &["avx2"]),
+    ("avxvnniint8", Stable, &["avx2"]),
     ("bmi1", Stable, &[]),
     ("bmi2", Stable, &[]),
     ("cmpxchg16b", Stable, &[]),
@@ -442,7 +442,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("f16c", Stable, &["avx"]),
     ("fma", Stable, &["avx"]),
     ("fxsr", Stable, &[]),
-    ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]),
+    ("gfni", Stable, &["sse2"]),
     ("kl", Unstable(sym::keylocker_x86), &["sse2"]),
     ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]),
     ("lzcnt", Stable, &[]),
@@ -469,8 +469,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]),
     ("ssse3", Stable, &["sse3"]),
     ("tbm", Unstable(sym::tbm_target_feature), &[]),
-    ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]),
-    ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]),
+    ("vaes", Stable, &["avx2", "aes"]),
+    ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]),
     ("widekl", Unstable(sym::keylocker_x86), &["kl"]),
     ("x87", Unstable(sym::x87_target_feature), &[]),
     ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]),
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 908fcb14cb2..ad57555bd24 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -492,9 +492,7 @@ fn layout_of_uncached<'tcx>(
         ty::Coroutine(def_id, args) => {
             use rustc_middle::ty::layout::PrimitiveExt as _;
 
-            let Some(info) = tcx.coroutine_layout(def_id, args) else {
-                return Err(error(cx, LayoutError::Unknown(ty)));
-            };
+            let info = tcx.coroutine_layout(def_id, args)?;
 
             let local_layouts = info
                 .field_tys
diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs
index 870ad9df4fd..556db239f24 100644
--- a/library/core/src/fmt/float.rs
+++ b/library/core/src/fmt/float.rs
@@ -20,6 +20,8 @@ macro_rules! impl_general_format {
     }
 }
 
+#[cfg(target_has_reliable_f16)]
+impl_general_format! { f16 }
 impl_general_format! { f32 f64 }
 
 // Don't inline this so callers don't use the stack space this function
@@ -231,6 +233,13 @@ macro_rules! floating {
 
 floating! { f32 f64 }
 
+#[cfg(target_has_reliable_f16)]
+floating! { f16 }
+
+// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order
+// to avoid ICEs.
+
+#[cfg(not(target_has_reliable_f16))]
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Debug for f16 {
     #[inline]
@@ -239,6 +248,33 @@ impl Debug for f16 {
     }
 }
 
+#[cfg(not(target_has_reliable_f16))]
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Display for f16 {
+    #[inline]
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
+        Debug::fmt(self, fmt)
+    }
+}
+
+#[cfg(not(target_has_reliable_f16))]
+#[stable(feature = "rust1", since = "1.0.0")]
+impl LowerExp for f16 {
+    #[inline]
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
+        Debug::fmt(self, fmt)
+    }
+}
+
+#[cfg(not(target_has_reliable_f16))]
+#[stable(feature = "rust1", since = "1.0.0")]
+impl UpperExp for f16 {
+    #[inline]
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
+        Debug::fmt(self, fmt)
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Debug for f128 {
     #[inline]
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 64a7ec8906b..e605d7e0d78 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -101,6 +101,7 @@
 #![feature(bstr)]
 #![feature(bstr_internals)]
 #![feature(cfg_match)]
+#![feature(cfg_target_has_reliable_f16_f128)]
 #![feature(const_carrying_mul_add)]
 #![feature(const_eval_select)]
 #![feature(core_intrinsics)]
@@ -111,7 +112,6 @@
 #![feature(is_ascii_octdigit)]
 #![feature(lazy_get)]
 #![feature(link_cfg)]
-#![feature(non_null_from_ref)]
 #![feature(offset_of_enum)]
 #![feature(panic_internals)]
 #![feature(ptr_alignment_type)]
@@ -188,9 +188,9 @@
 //
 // Target features:
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(avx512_target_feature))]
 #![feature(aarch64_unstable_target_feature)]
 #![feature(arm_target_feature)]
-#![feature(avx512_target_feature)]
 #![feature(hexagon_target_feature)]
 #![feature(keylocker_x86)]
 #![feature(loongarch_target_feature)]
diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs
index b8a28a67569..5bf0faf0bc9 100644
--- a/library/core/src/num/dec2flt/float.rs
+++ b/library/core/src/num/dec2flt/float.rs
@@ -45,7 +45,7 @@ macro_rules! int {
     }
 }
 
-int!(u32, u64);
+int!(u16, u32, u64);
 
 /// A helper trait to avoid duplicating basically all the conversion code for IEEE floats.
 ///
@@ -189,9 +189,14 @@ pub trait RawFloat:
 
     /// Returns the mantissa, exponent and sign as integers.
     ///
-    /// That is, this returns `(m, p, s)` such that `s * m * 2^p` represents the original float.
-    /// For 0, the exponent will be `-(EXP_BIAS + SIG_BITS`, which is the
-    /// minimum subnormal power.
+    /// This returns `(m, p, s)` such that `s * m * 2^p` represents the original float. For 0, the
+    /// exponent will be `-(EXP_BIAS + SIG_BITS)`, which is the minimum subnormal power. For
+    /// infinity or NaN, the exponent will be `EXP_SAT - EXP_BIAS - SIG_BITS`.
+    ///
+    /// If subnormal, the mantissa will be shifted one bit to the left. Otherwise, it is returned
+    /// with the explicit bit set but otherwise unshifted
+    ///
+    /// `s` is only ever +/-1.
     fn integer_decode(self) -> (u64, i16, i8) {
         let bits = self.to_bits();
         let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 };
@@ -213,6 +218,50 @@ const fn pow2_to_pow10(a: i64) -> i64 {
     res as i64
 }
 
+#[cfg(target_has_reliable_f16)]
+impl RawFloat for f16 {
+    type Int = u16;
+
+    const INFINITY: Self = Self::INFINITY;
+    const NEG_INFINITY: Self = Self::NEG_INFINITY;
+    const NAN: Self = Self::NAN;
+    const NEG_NAN: Self = -Self::NAN;
+
+    const BITS: u32 = 16;
+    const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS;
+    const EXP_MASK: Self::Int = Self::EXP_MASK;
+    const SIG_MASK: Self::Int = Self::MAN_MASK;
+
+    const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22;
+    const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5;
+    const SMALLEST_POWER_OF_TEN: i32 = -27;
+
+    #[inline]
+    fn from_u64(v: u64) -> Self {
+        debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
+        v as _
+    }
+
+    #[inline]
+    fn from_u64_bits(v: u64) -> Self {
+        Self::from_bits((v & 0xFFFF) as u16)
+    }
+
+    fn pow10_fast_path(exponent: usize) -> Self {
+        #[allow(clippy::use_self)]
+        const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.];
+        TABLE[exponent & 7]
+    }
+
+    fn to_bits(self) -> Self::Int {
+        self.to_bits()
+    }
+
+    fn classify(self) -> FpCategory {
+        self.classify()
+    }
+}
+
 impl RawFloat for f32 {
     type Int = u32;
 
diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs
index d1a0e1db313..abad7acb104 100644
--- a/library/core/src/num/dec2flt/mod.rs
+++ b/library/core/src/num/dec2flt/mod.rs
@@ -171,9 +171,25 @@ macro_rules! from_str_float_impl {
         }
     };
 }
+
+#[cfg(target_has_reliable_f16)]
+from_str_float_impl!(f16);
 from_str_float_impl!(f32);
 from_str_float_impl!(f64);
 
+// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order
+// to avoid ICEs.
+
+#[cfg(not(target_has_reliable_f16))]
+impl FromStr for f16 {
+    type Err = ParseFloatError;
+
+    #[inline]
+    fn from_str(_src: &str) -> Result<Self, ParseFloatError> {
+        unimplemented!("requires target_has_reliable_f16")
+    }
+}
+
 /// An error which can be returned when parsing a float.
 ///
 /// This error is used as the error type for the [`FromStr`] implementation
diff --git a/library/core/src/num/flt2dec/decoder.rs b/library/core/src/num/flt2dec/decoder.rs
index 40b3aae24a5..bd6e2cdbafe 100644
--- a/library/core/src/num/flt2dec/decoder.rs
+++ b/library/core/src/num/flt2dec/decoder.rs
@@ -45,6 +45,13 @@ pub trait DecodableFloat: RawFloat + Copy {
     fn min_pos_norm_value() -> Self;
 }
 
+#[cfg(target_has_reliable_f16)]
+impl DecodableFloat for f16 {
+    fn min_pos_norm_value() -> Self {
+        f16::MIN_POSITIVE
+    }
+}
+
 impl DecodableFloat for f32 {
     fn min_pos_norm_value() -> Self {
         f32::MIN_POSITIVE
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index d05fb6a6d31..8b31328de04 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -262,7 +262,8 @@ impl<T: ?Sized> NonNull<T> {
     }
 
     /// Converts a reference to a `NonNull` pointer.
-    #[unstable(feature = "non_null_from_ref", issue = "130823")]
+    #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn from_ref(r: &T) -> Self {
         // SAFETY: A reference cannot be null.
@@ -270,7 +271,8 @@ impl<T: ?Sized> NonNull<T> {
     }
 
     /// Converts a mutable reference to a `NonNull` pointer.
-    #[unstable(feature = "non_null_from_ref", issue = "130823")]
+    #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
     #[inline]
     pub const fn from_mut(r: &mut T) -> Self {
         // SAFETY: A mutable reference cannot be null.
diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs
index 1fa06de692e..f759e1dbde6 100644
--- a/library/coretests/tests/num/dec2flt/decimal.rs
+++ b/library/coretests/tests/num/dec2flt/decimal.rs
@@ -7,6 +7,20 @@ const FPATHS_F32: &[FPath<f32>] =
 const FPATHS_F64: &[FPath<f64>] =
     &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))];
 
+// FIXME(f16_f128): enable on all targets once possible.
+#[test]
+#[cfg(target_has_reliable_f16)]
+fn check_fast_path_f16() {
+    const FPATHS_F16: &[FPath<f16>] =
+        &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))];
+    for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() {
+        let dec = Decimal { exponent, mantissa, negative, many_digits };
+        let actual = dec.try_fast_path::<f16>();
+
+        assert_eq!(actual, expected);
+    }
+}
+
 #[test]
 fn check_fast_path_f32() {
     for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() {
diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs
index b5afd3e3b24..264de061be9 100644
--- a/library/coretests/tests/num/dec2flt/float.rs
+++ b/library/coretests/tests/num/dec2flt/float.rs
@@ -1,5 +1,24 @@
 use core::num::dec2flt::float::RawFloat;
 
+// FIXME(f16_f128): enable on all targets once possible.
+#[test]
+#[cfg(target_has_reliable_f16)]
+fn test_f16_integer_decode() {
+    assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1));
+    assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1));
+    #[cfg(not(miri))] // miri doesn't have powf16
+    assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1));
+    assert_eq!(0f16.integer_decode(), (0, -25, 1));
+    assert_eq!((-0f16).integer_decode(), (0, -25, -1));
+    assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1));
+    assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1));
+
+    // Ignore the "sign" (quiet / signalling flag) of NAN.
+    // It can vary between runtime operations and LLVM folding.
+    let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode();
+    assert_eq!((nan_m, nan_p), (1536, 6));
+}
+
 #[test]
 fn test_f32_integer_decode() {
     assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
@@ -34,6 +53,27 @@ fn test_f64_integer_decode() {
 
 /* Sanity checks of computed magic numbers */
 
+// FIXME(f16_f128): enable on all targets once possible.
+#[test]
+#[cfg(target_has_reliable_f16)]
+fn test_f16_consts() {
+    assert_eq!(<f16 as RawFloat>::INFINITY, f16::INFINITY);
+    assert_eq!(<f16 as RawFloat>::NEG_INFINITY, -f16::INFINITY);
+    assert_eq!(<f16 as RawFloat>::NAN.to_bits(), f16::NAN.to_bits());
+    assert_eq!(<f16 as RawFloat>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
+    assert_eq!(<f16 as RawFloat>::SIG_BITS, 10);
+    assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
+    assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
+    assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_FAST_PATH, -4);
+    assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_FAST_PATH, 4);
+    assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
+    assert_eq!(<f16 as RawFloat>::EXP_MIN, -14);
+    assert_eq!(<f16 as RawFloat>::EXP_SAT, 0x1f);
+    assert_eq!(<f16 as RawFloat>::SMALLEST_POWER_OF_TEN, -27);
+    assert_eq!(<f16 as RawFloat>::LARGEST_POWER_OF_TEN, 4);
+    assert_eq!(<f16 as RawFloat>::MAX_MANTISSA_FAST_PATH, 2048);
+}
+
 #[test]
 fn test_f32_consts() {
     assert_eq!(<f32 as RawFloat>::INFINITY, f32::INFINITY);
diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs
index 0db80fbd525..6d49d85170e 100644
--- a/library/coretests/tests/num/dec2flt/lemire.rs
+++ b/library/coretests/tests/num/dec2flt/lemire.rs
@@ -1,6 +1,12 @@
 use core::num::dec2flt::float::RawFloat;
 use core::num::dec2flt::lemire::compute_float;
 
+#[cfg(target_has_reliable_f16)]
+fn compute_float16(q: i64, w: u64) -> (i32, u64) {
+    let fp = compute_float::<f16>(q, w);
+    (fp.p_biased, fp.m)
+}
+
 fn compute_float32(q: i64, w: u64) -> (i32, u64) {
     let fp = compute_float::<f32>(q, w);
     (fp.p_biased, fp.m)
@@ -11,23 +17,73 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) {
     (fp.p_biased, fp.m)
 }
 
+// FIXME(f16_f128): enable on all targets once possible.
+#[test]
+#[cfg(target_has_reliable_f16)]
+fn compute_float_f16_rounding() {
+    // The maximum integer that cna be converted to a `f16` without lost precision.
+    let val = 1 << 11;
+    let scale = 10_u64.pow(10);
+
+    // These test near-halfway cases for half-precision floats.
+    assert_eq!(compute_float16(0, val), (26, 0));
+    assert_eq!(compute_float16(0, val + 1), (26, 0));
+    assert_eq!(compute_float16(0, val + 2), (26, 1));
+    assert_eq!(compute_float16(0, val + 3), (26, 2));
+    assert_eq!(compute_float16(0, val + 4), (26, 2));
+
+    // For the next power up, the two nearest representable numbers are twice as far apart.
+    let val2 = 1 << 12;
+    assert_eq!(compute_float16(0, val2), (27, 0));
+    assert_eq!(compute_float16(0, val2 + 2), (27, 0));
+    assert_eq!(compute_float16(0, val2 + 4), (27, 1));
+    assert_eq!(compute_float16(0, val2 + 6), (27, 2));
+    assert_eq!(compute_float16(0, val2 + 8), (27, 2));
+
+    // These are examples of the above tests, with digits from the exponent shifted
+    // to the mantissa.
+    assert_eq!(compute_float16(-10, val * scale), (26, 0));
+    assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0));
+    assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1));
+    // Let's check the lines to see if anything is different in table...
+    assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2));
+    assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2));
+
+    // Check the rounding point between infinity and the next representable number down
+    assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851));
+    assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity
+    assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023));
+}
+
 #[test]
 fn compute_float_f32_rounding() {
+    // the maximum integer that cna be converted to a `f32` without lost precision.
+    let val = 1 << 24;
+    let scale = 10_u64.pow(10);
+
     // These test near-halfway cases for single-precision floats.
-    assert_eq!(compute_float32(0, 16777216), (151, 0));
-    assert_eq!(compute_float32(0, 16777217), (151, 0));
-    assert_eq!(compute_float32(0, 16777218), (151, 1));
-    assert_eq!(compute_float32(0, 16777219), (151, 2));
-    assert_eq!(compute_float32(0, 16777220), (151, 2));
-
-    // These are examples of the above tests, with
-    // digits from the exponent shifted to the mantissa.
-    assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
-    assert_eq!(compute_float32(-10, 167772170000000000), (151, 0));
-    assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
+    assert_eq!(compute_float32(0, val), (151, 0));
+    assert_eq!(compute_float32(0, val + 1), (151, 0));
+    assert_eq!(compute_float32(0, val + 2), (151, 1));
+    assert_eq!(compute_float32(0, val + 3), (151, 2));
+    assert_eq!(compute_float32(0, val + 4), (151, 2));
+
+    // For the next power up, the two nearest representable numbers are twice as far apart.
+    let val2 = 1 << 25;
+    assert_eq!(compute_float32(0, val2), (152, 0));
+    assert_eq!(compute_float32(0, val2 + 2), (152, 0));
+    assert_eq!(compute_float32(0, val2 + 4), (152, 1));
+    assert_eq!(compute_float32(0, val2 + 6), (152, 2));
+    assert_eq!(compute_float32(0, val2 + 8), (152, 2));
+
+    // These are examples of the above tests, with digits from the exponent shifted
+    // to the mantissa.
+    assert_eq!(compute_float32(-10, val * scale), (151, 0));
+    assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0));
+    assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1));
     // Let's check the lines to see if anything is different in table...
-    assert_eq!(compute_float32(-10, 167772190000000000), (151, 2));
-    assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
+    assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2));
+    assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2));
 
     // Check the rounding point between infinity and the next representable number down
     assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534));
@@ -37,23 +93,38 @@ fn compute_float_f32_rounding() {
 
 #[test]
 fn compute_float_f64_rounding() {
+    // The maximum integer that cna be converted to a `f64` without lost precision.
+    let val = 1 << 53;
+    let scale = 1000;
+
     // These test near-halfway cases for double-precision floats.
-    assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
-    assert_eq!(compute_float64(0, 9007199254740993), (1076, 0));
-    assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
-    assert_eq!(compute_float64(0, 9007199254740995), (1076, 2));
-    assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
-    assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
-    assert_eq!(compute_float64(0, 18014398509481986), (1077, 0));
-    assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
-    assert_eq!(compute_float64(0, 18014398509481990), (1077, 2));
-    assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
-
-    // These are examples of the above tests, with
-    // digits from the exponent shifted to the mantissa.
-    assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
-    assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0));
-    assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
-    assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2));
-    assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
+    assert_eq!(compute_float64(0, val), (1076, 0));
+    assert_eq!(compute_float64(0, val + 1), (1076, 0));
+    assert_eq!(compute_float64(0, val + 2), (1076, 1));
+    assert_eq!(compute_float64(0, val + 3), (1076, 2));
+    assert_eq!(compute_float64(0, val + 4), (1076, 2));
+
+    // For the next power up, the two nearest representable numbers are twice as far apart.
+    let val2 = 1 << 54;
+    assert_eq!(compute_float64(0, val2), (1077, 0));
+    assert_eq!(compute_float64(0, val2 + 2), (1077, 0));
+    assert_eq!(compute_float64(0, val2 + 4), (1077, 1));
+    assert_eq!(compute_float64(0, val2 + 6), (1077, 2));
+    assert_eq!(compute_float64(0, val2 + 8), (1077, 2));
+
+    // These are examples of the above tests, with digits from the exponent shifted
+    // to the mantissa.
+    assert_eq!(compute_float64(-3, val * scale), (1076, 0));
+    assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0));
+    assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1));
+    assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2));
+    assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2));
+
+    // Check the rounding point between infinity and the next representable number down
+    assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936));
+    assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity
+    assert_eq!(
+        compute_float64(292, 17976931348623157),
+        (f64::INFINITE_POWER - 1, 4503599627370495)
+    );
 }
diff --git a/library/coretests/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs
index a9025be5ca7..b8ca220847c 100644
--- a/library/coretests/tests/num/dec2flt/mod.rs
+++ b/library/coretests/tests/num/dec2flt/mod.rs
@@ -11,15 +11,23 @@ mod parse;
 // Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
 macro_rules! test_literal {
     ($x: expr) => {{
+        #[cfg(target_has_reliable_f16)]
+        let x16: f16 = $x;
         let x32: f32 = $x;
         let x64: f64 = $x;
         let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
+
         for input in inputs {
-            assert_eq!(input.parse(), Ok(x64));
-            assert_eq!(input.parse(), Ok(x32));
+            assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
+            assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
+            #[cfg(target_has_reliable_f16)]
+            assert_eq!(input.parse(), Ok(x16), "failed f16 {input}");
+
             let neg_input = format!("-{input}");
-            assert_eq!(neg_input.parse(), Ok(-x64));
-            assert_eq!(neg_input.parse(), Ok(-x32));
+            assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
+            assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
+            #[cfg(target_has_reliable_f16)]
+            assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}");
         }
     }};
 }
@@ -84,48 +92,87 @@ fn fast_path_correct() {
     test_literal!(1.448997445238699);
 }
 
+// FIXME(f16_f128): remove gates once tests work on all targets
+
 #[test]
 fn lonely_dot() {
+    #[cfg(target_has_reliable_f16)]
+    assert!(".".parse::<f16>().is_err());
     assert!(".".parse::<f32>().is_err());
     assert!(".".parse::<f64>().is_err());
 }
 
 #[test]
 fn exponentiated_dot() {
+    #[cfg(target_has_reliable_f16)]
+    assert!(".e0".parse::<f16>().is_err());
     assert!(".e0".parse::<f32>().is_err());
     assert!(".e0".parse::<f64>().is_err());
 }
 
 #[test]
 fn lonely_sign() {
-    assert!("+".parse::<f32>().is_err());
-    assert!("-".parse::<f64>().is_err());
+    #[cfg(target_has_reliable_f16)]
+    assert!("+".parse::<f16>().is_err());
+    assert!("-".parse::<f32>().is_err());
+    assert!("+".parse::<f64>().is_err());
 }
 
 #[test]
 fn whitespace() {
+    #[cfg(target_has_reliable_f16)]
+    assert!("1.0 ".parse::<f16>().is_err());
     assert!(" 1.0".parse::<f32>().is_err());
     assert!("1.0 ".parse::<f64>().is_err());
 }
 
 #[test]
 fn nan() {
+    #[cfg(target_has_reliable_f16)]
+    {
+        assert!("NaN".parse::<f16>().unwrap().is_nan());
+        assert!("-NaN".parse::<f16>().unwrap().is_nan());
+    }
+
     assert!("NaN".parse::<f32>().unwrap().is_nan());
+    assert!("-NaN".parse::<f32>().unwrap().is_nan());
+
     assert!("NaN".parse::<f64>().unwrap().is_nan());
+    assert!("-NaN".parse::<f64>().unwrap().is_nan());
 }
 
 #[test]
 fn inf() {
-    assert_eq!("inf".parse(), Ok(f64::INFINITY));
-    assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
+    #[cfg(target_has_reliable_f16)]
+    {
+        assert_eq!("inf".parse(), Ok(f16::INFINITY));
+        assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY));
+    }
+
     assert_eq!("inf".parse(), Ok(f32::INFINITY));
     assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY));
+
+    assert_eq!("inf".parse(), Ok(f64::INFINITY));
+    assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
 }
 
 #[test]
 fn massive_exponent() {
+    #[cfg(target_has_reliable_f16)]
+    {
+        let max = i16::MAX;
+        assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
+        assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16));
+        assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
+    }
+
+    let max = i32::MAX;
+    assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
+    assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32));
+    assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
+
     let max = i64::MAX;
     assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
-    assert_eq!(format!("1e-{max}000").parse(), Ok(0.0));
+    assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64));
     assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
 }
diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs
index 59be3915052..dccb6b5528d 100644
--- a/library/coretests/tests/num/dec2flt/parse.rs
+++ b/library/coretests/tests/num/dec2flt/parse.rs
@@ -10,6 +10,9 @@ fn new_dec(e: i64, m: u64) -> Decimal {
 fn missing_pieces() {
     let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"];
     for &s in permutations {
+        #[cfg(target_has_reliable_f16)]
+        assert_eq!(dec2flt::<f16>(s), Err(pfe_invalid()));
+        assert_eq!(dec2flt::<f32>(s), Err(pfe_invalid()));
         assert_eq!(dec2flt::<f64>(s), Err(pfe_invalid()));
     }
 }
@@ -17,15 +20,31 @@ fn missing_pieces() {
 #[test]
 fn invalid_chars() {
     let invalid = "r,?<j";
-    let error = Err(pfe_invalid());
     let valid_strings = &["123", "666.", ".1", "5e1", "7e-3", "0.0e+1"];
+
     for c in invalid.chars() {
         for s in valid_strings {
             for i in 0..s.len() {
                 let mut input = String::new();
                 input.push_str(s);
                 input.insert(i, c);
-                assert!(dec2flt::<f64>(&input) == error, "did not reject invalid {:?}", input);
+
+                #[cfg(target_has_reliable_f16)]
+                assert_eq!(
+                    dec2flt::<f16>(&input),
+                    Err(pfe_invalid()),
+                    "f16 did not reject invalid {input:?}",
+                );
+                assert_eq!(
+                    dec2flt::<f32>(&input),
+                    Err(pfe_invalid()),
+                    "f32 did not reject invalid {input:?}",
+                );
+                assert_eq!(
+                    dec2flt::<f64>(&input),
+                    Err(pfe_invalid()),
+                    "f64 did not reject invalid {input:?}",
+                );
             }
         }
     }
diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs
index c64bb0a3072..ce36db33d05 100644
--- a/library/coretests/tests/num/flt2dec/mod.rs
+++ b/library/coretests/tests/num/flt2dec/mod.rs
@@ -16,7 +16,7 @@ mod random;
 pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
     match decode(v).1 {
         FullDecoded::Finite(decoded) => decoded,
-        full_decoded => panic!("expected finite, got {full_decoded:?} instead"),
+        full_decoded => panic!("expected finite, got {full_decoded:?} instead for {v:?}"),
     }
 }
 
@@ -75,6 +75,11 @@ macro_rules! try_fixed {
     })
 }
 
+#[cfg(target_has_reliable_f16)]
+fn ldexp_f16(a: f16, b: i32) -> f16 {
+    ldexp_f64(a as f64, b) as f16
+}
+
 fn ldexp_f32(a: f32, b: i32) -> f32 {
     ldexp_f64(a as f64, b) as f32
 }
@@ -176,6 +181,13 @@ trait TestableFloat: DecodableFloat + fmt::Display {
     fn ldexpi(f: i64, exp: isize) -> Self;
 }
 
+#[cfg(target_has_reliable_f16)]
+impl TestableFloat for f16 {
+    fn ldexpi(f: i64, exp: isize) -> Self {
+        f as Self * (exp as Self).exp2()
+    }
+}
+
 impl TestableFloat for f32 {
     fn ldexpi(f: i64, exp: isize) -> Self {
         f as Self * (exp as Self).exp2()
@@ -225,6 +237,76 @@ macro_rules! check_exact_one {
 //
 // [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion
 //     ftp://ftp.ee.lbl.gov/testbase-report.ps.Z
+//  or https://www.icir.org/vern/papers/testbase-report.pdf
+
+#[cfg(target_has_reliable_f16)]
+pub fn f16_shortest_sanity_test<F>(mut f: F)
+where
+    F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+    // 0.0999145507813
+    // 0.0999755859375
+    // 0.100036621094
+    check_shortest!(f(0.1f16) => b"1", 0);
+
+    // 0.3330078125
+    // 0.333251953125 (1/3 in the default rounding)
+    // 0.33349609375
+    check_shortest!(f(1.0f16/3.0) => b"3333", 0);
+
+    // 10^1 * 0.3138671875
+    // 10^1 * 0.3140625
+    // 10^1 * 0.3142578125
+    check_shortest!(f(3.14f16) => b"314", 1);
+
+    // 10^18 * 0.31415916243714048
+    // 10^18 * 0.314159196796878848
+    // 10^18 * 0.314159231156617216
+    check_shortest!(f(3.1415e4f16) => b"3141", 5);
+
+    // regression test for decoders
+    // 10^2 * 0.31984375
+    // 10^2 * 0.32
+    // 10^2 * 0.3203125
+    check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2);
+
+    // 10^5 * 0.65472
+    // 10^5 * 0.65504
+    // 10^5 * 0.65536
+    check_shortest!(f(f16::MAX) => b"655", 5);
+
+    // 10^-4 * 0.60975551605224609375
+    // 10^-4 * 0.6103515625
+    // 10^-4 * 0.61094760894775390625
+    check_shortest!(f(f16::MIN_POSITIVE) => b"6104", -4);
+
+    // 10^-9 * 0
+    // 10^-9 * 0.59604644775390625
+    // 10^-8 * 0.11920928955078125
+    let minf16 = ldexp_f16(1.0, -24);
+    check_shortest!(f(minf16) => b"6", -7);
+}
+
+#[cfg(target_has_reliable_f16)]
+pub fn f16_exact_sanity_test<F>(mut f: F)
+where
+    F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+    let minf16 = ldexp_f16(1.0, -24);
+
+    check_exact!(f(0.1f16)            => b"999755859375     ", -1);
+    check_exact!(f(0.5f16)            => b"5                ", 0);
+    check_exact!(f(1.0f16/3.0)        => b"333251953125     ", 0);
+    check_exact!(f(3.141f16)          => b"3140625          ", 1);
+    check_exact!(f(3.141e4f16)        => b"31408            ", 5);
+    check_exact!(f(f16::MAX)          => b"65504            ", 5);
+    check_exact!(f(f16::MIN_POSITIVE) => b"6103515625       ", -4);
+    check_exact!(f(minf16)            => b"59604644775390625", -7);
+
+    // FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have,
+    // but these values are not easy to generate. The algorithm from the Paxon paper [1] needs
+    // to be adapted to binary16.
+}
 
 pub fn f32_shortest_sanity_test<F>(mut f: F)
 where
@@ -553,23 +635,45 @@ where
     assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0");
     assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000");
 
-    assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", ""));
-    assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", ""));
-    assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", ""));
-
-    let minf32 = ldexp_f32(1.0, -149);
-    assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", ""));
-    assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", ""));
-    assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", ""));
+    #[cfg(target_has_reliable_f16)]
+    {
+        // f16
+        assert_eq!(to_string(f, f16::MAX, Minus, 0), "65500");
+        assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0");
+        assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000");
+
+        let minf16 = ldexp_f16(1.0, -24);
+        assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006");
+        assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006");
+        assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060");
+    }
 
-    assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", ""));
-    assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", ""));
-    assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", ""));
+    {
+        // f32
+        assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", ""));
+        assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", ""));
+        assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", ""));
+
+        let minf32 = ldexp_f32(1.0, -149);
+        assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", ""));
+        assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", ""));
+        assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", ""));
+    }
 
-    let minf64 = ldexp_f64(1.0, -1074);
-    assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", ""));
-    assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", ""));
-    assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", ""));
+    {
+        // f64
+        assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", ""));
+        assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", ""));
+        assert_eq!(
+            to_string(f, f64::MAX, Minus, 8),
+            format!("17976931348623157{:0>292}.00000000", "")
+        );
+
+        let minf64 = ldexp_f64(1.0, -1074);
+        assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", ""));
+        assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", ""));
+        assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", ""));
+    }
 
     if cfg!(miri) {
         // Miri is too slow
@@ -655,27 +759,45 @@ where
     assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000");
     assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23");
 
-    assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38");
-    assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
-    assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
-
-    let minf32 = ldexp_f32(1.0, -149);
-    assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45");
-    assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
-    assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
-
-    assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308");
-    assert_eq!(
-        to_string(f, f64::MAX, Minus, (-308, 309), false),
-        format!("17976931348623157{:0>292}", "")
-    );
-    assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308");
+    #[cfg(target_has_reliable_f16)]
+    {
+        // f16
+        assert_eq!(to_string(f, f16::MAX, Minus, (-2, 2), false), "6.55e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500");
+
+        let minf16 = ldexp_f16(1.0, -24);
+        assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8");
+        assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8");
+        assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006");
+    }
 
-    let minf64 = ldexp_f64(1.0, -1074);
-    assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324");
-    assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
-    assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
+    {
+        // f32
+        assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38");
+        assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
+        assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
+
+        let minf32 = ldexp_f32(1.0, -149);
+        assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45");
+        assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
+        assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
+    }
 
+    {
+        // f64
+        assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308");
+        assert_eq!(
+            to_string(f, f64::MAX, Minus, (-308, 309), false),
+            format!("17976931348623157{:0>292}", "")
+        );
+        assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308");
+
+        let minf64 = ldexp_f64(1.0, -1074);
+        assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324");
+        assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
+        assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
+    }
     assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1");
 }
 
@@ -791,6 +913,26 @@ where
         "9.999999999999999547481118258862586856139387236908078193664550781250000e-7"
     );
 
+    #[cfg(target_has_reliable_f16)]
+    {
+        assert_eq!(to_string(f, f16::MAX, Minus, 1, false), "7e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, 2, false), "6.6e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, 4, false), "6.550e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, 5, false), "6.5504e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4");
+        assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4");
+
+        let minf16 = ldexp_f16(1.0, -24);
+        assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8");
+        assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8");
+        assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8");
+        assert_eq!(to_string(f, minf16, Minus, 8, false), "5.9604645e-8");
+        assert_eq!(to_string(f, minf16, Minus, 16, false), "5.960464477539062e-8");
+        assert_eq!(to_string(f, minf16, Minus, 17, false), "5.9604644775390625e-8");
+        assert_eq!(to_string(f, minf16, Minus, 18, false), "5.96046447753906250e-8");
+        assert_eq!(to_string(f, minf16, Minus, 24, false), "5.96046447753906250000000e-8");
+    }
+
     assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38");
     assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38");
     assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38");
@@ -1069,6 +1211,13 @@ where
         "0.000000999999999999999954748111825886258685613938723690807819366455078125000"
     );
 
+    #[cfg(target_has_reliable_f16)]
+    {
+        assert_eq!(to_string(f, f16::MAX, Minus, 0), "65504");
+        assert_eq!(to_string(f, f16::MAX, Minus, 1), "65504.0");
+        assert_eq!(to_string(f, f16::MAX, Minus, 2), "65504.00");
+    }
+
     assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440");
     assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0");
     assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00");
@@ -1078,6 +1227,21 @@ where
         return;
     }
 
+    #[cfg(target_has_reliable_f16)]
+    {
+        let minf16 = ldexp_f16(1.0, -24);
+        assert_eq!(to_string(f, minf16, Minus, 0), "0");
+        assert_eq!(to_string(f, minf16, Minus, 1), "0.0");
+        assert_eq!(to_string(f, minf16, Minus, 2), "0.00");
+        assert_eq!(to_string(f, minf16, Minus, 4), "0.0000");
+        assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006");
+        assert_eq!(to_string(f, minf16, Minus, 10), "0.0000000596");
+        assert_eq!(to_string(f, minf16, Minus, 15), "0.000000059604645");
+        assert_eq!(to_string(f, minf16, Minus, 20), "0.00000005960464477539");
+        assert_eq!(to_string(f, minf16, Minus, 24), "0.000000059604644775390625");
+        assert_eq!(to_string(f, minf16, Minus, 32), "0.00000005960464477539062500000000");
+    }
+
     let minf32 = ldexp_f32(1.0, -149);
     assert_eq!(to_string(f, minf32, Minus, 0), "0");
     assert_eq!(to_string(f, minf32, Minus, 1), "0.0");
diff --git a/library/coretests/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs
index 586b49df7d9..7386139aace 100644
--- a/library/coretests/tests/num/flt2dec/random.rs
+++ b/library/coretests/tests/num/flt2dec/random.rs
@@ -79,6 +79,20 @@ where
     (npassed, nignored)
 }
 
+#[cfg(target_has_reliable_f16)]
+pub fn f16_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
+where
+    F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
+    G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+    let mut rng = crate::test_rng();
+    let f16_range = Uniform::new(0x0001u16, 0x7c00).unwrap();
+    iterate("f16_random_equivalence_test", k, n, f, g, |_| {
+        let x = f16::from_bits(f16_range.sample(&mut rng));
+        decode_finite(x)
+    });
+}
+
 pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
 where
     F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
@@ -105,6 +119,24 @@ where
     });
 }
 
+#[cfg(target_has_reliable_f16)]
+pub fn f16_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
+where
+    F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
+    G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+    // Unlike the other float types, `f16` is small enough that these exhaustive tests
+    // can run in less than a second so we don't need to ignore it.
+
+    // iterate from 0x0001 to 0x7bff, i.e., all finite ranges
+    let (npassed, nignored) =
+        iterate("f16_exhaustive_equivalence_test", k, 0x7bff, f, g, |i: usize| {
+            let x = f16::from_bits(i as u16 + 1);
+            decode_finite(x)
+        });
+    assert_eq!((npassed, nignored), (29735, 2008));
+}
+
 pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
 where
     F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
@@ -133,6 +165,17 @@ fn shortest_random_equivalence_test() {
 
     f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
     f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
+    #[cfg(target_has_reliable_f16)]
+    f16_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
+}
+
+#[test]
+#[cfg_attr(miri, ignore)] // Miri is to slow
+#[cfg(target_has_reliable_f16)]
+fn shortest_f16_exhaustive_equivalence_test() {
+    // see the f32 version
+    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
+    f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
 }
 
 #[test]
@@ -159,6 +202,23 @@ fn shortest_f64_hard_random_equivalence_test() {
 }
 
 #[test]
+#[cfg(target_has_reliable_f16)]
+fn exact_f16_random_equivalence_test() {
+    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
+    // Miri is too slow
+    let n = if cfg!(miri) { 3 } else { 1_000 };
+
+    for k in 1..21 {
+        f16_random_equivalence_test(
+            |d, buf| format_exact_opt(d, buf, i16::MIN),
+            |d, buf| fallback(d, buf, i16::MIN),
+            k,
+            n,
+        );
+    }
+}
+
+#[test]
 fn exact_f32_random_equivalence_test() {
     use core::num::flt2dec::strategy::dragon::format_exact as fallback;
     // Miri is too slow
diff --git a/library/coretests/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs
index be25fee3f6c..43bb6024f9c 100644
--- a/library/coretests/tests/num/flt2dec/strategy/dragon.rs
+++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs
@@ -18,6 +18,8 @@ fn test_mul_pow10() {
 fn shortest_sanity_test() {
     f64_shortest_sanity_test(format_shortest);
     f32_shortest_sanity_test(format_shortest);
+    #[cfg(target_has_reliable_f16)]
+    f16_shortest_sanity_test(format_shortest);
     more_shortest_sanity_test(format_shortest);
 }
 
@@ -41,6 +43,9 @@ fn exact_sanity_test() {
         f64_exact_sanity_test(format_exact);
     }
     f32_exact_sanity_test(format_exact);
+
+    #[cfg(target_has_reliable_f16)]
+    f16_exact_sanity_test(format_exact);
 }
 
 #[test]
diff --git a/library/coretests/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs
index 9b2f0453de7..117191e0c8f 100644
--- a/library/coretests/tests/num/flt2dec/strategy/grisu.rs
+++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs
@@ -38,6 +38,8 @@ fn test_max_pow10_no_more_than() {
 fn shortest_sanity_test() {
     f64_shortest_sanity_test(format_shortest);
     f32_shortest_sanity_test(format_shortest);
+    #[cfg(target_has_reliable_f16)]
+    f16_shortest_sanity_test(format_shortest);
     more_shortest_sanity_test(format_shortest);
 }
 
@@ -50,6 +52,8 @@ fn exact_sanity_test() {
         f64_exact_sanity_test(format_exact);
     }
     f32_exact_sanity_test(format_exact);
+    #[cfg(target_has_reliable_f16)]
+    f16_exact_sanity_test(format_exact);
 }
 
 #[test]
diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs
index 0add9a01e68..a6b75f70266 100644
--- a/library/coretests/tests/num/mod.rs
+++ b/library/coretests/tests/num/mod.rs
@@ -732,157 +732,157 @@ assume_usize_width! {
 }
 
 macro_rules! test_float {
-    ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => {
+    ($modname: ident, $fassert: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => {
         mod $modname {
             #[test]
             fn min() {
-                assert_eq!((0.0 as $fty).min(0.0), 0.0);
-                assert!((0.0 as $fty).min(0.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).min(-0.0), -0.0);
-                assert!((-0.0 as $fty).min(-0.0).is_sign_negative());
-                assert_eq!((9.0 as $fty).min(9.0), 9.0);
-                assert_eq!((-9.0 as $fty).min(0.0), -9.0);
-                assert_eq!((0.0 as $fty).min(9.0), 0.0);
-                assert!((0.0 as $fty).min(9.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).min(9.0), -0.0);
-                assert!((-0.0 as $fty).min(9.0).is_sign_negative());
-                assert_eq!((-0.0 as $fty).min(-9.0), -9.0);
-                assert_eq!(($inf as $fty).min(9.0), 9.0);
-                assert_eq!((9.0 as $fty).min($inf), 9.0);
-                assert_eq!(($inf as $fty).min(-9.0), -9.0);
-                assert_eq!((-9.0 as $fty).min($inf), -9.0);
-                assert_eq!(($neginf as $fty).min(9.0), $neginf);
-                assert_eq!((9.0 as $fty).min($neginf), $neginf);
-                assert_eq!(($neginf as $fty).min(-9.0), $neginf);
-                assert_eq!((-9.0 as $fty).min($neginf), $neginf);
-                assert_eq!(($nan as $fty).min(9.0), 9.0);
-                assert_eq!(($nan as $fty).min(-9.0), -9.0);
-                assert_eq!((9.0 as $fty).min($nan), 9.0);
-                assert_eq!((-9.0 as $fty).min($nan), -9.0);
-                assert!(($nan as $fty).min($nan).is_nan());
+                $fassert!((0.0 as $fty).min(0.0), 0.0);
+                $fassert!((0.0 as $fty).min(0.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).min(-0.0), -0.0);
+                $fassert!((-0.0 as $fty).min(-0.0).is_sign_negative());
+                $fassert!((9.0 as $fty).min(9.0), 9.0);
+                $fassert!((-9.0 as $fty).min(0.0), -9.0);
+                $fassert!((0.0 as $fty).min(9.0), 0.0);
+                $fassert!((0.0 as $fty).min(9.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).min(9.0), -0.0);
+                $fassert!((-0.0 as $fty).min(9.0).is_sign_negative());
+                $fassert!((-0.0 as $fty).min(-9.0), -9.0);
+                $fassert!(($inf as $fty).min(9.0), 9.0);
+                $fassert!((9.0 as $fty).min($inf), 9.0);
+                $fassert!(($inf as $fty).min(-9.0), -9.0);
+                $fassert!((-9.0 as $fty).min($inf), -9.0);
+                $fassert!(($neginf as $fty).min(9.0), $neginf);
+                $fassert!((9.0 as $fty).min($neginf), $neginf);
+                $fassert!(($neginf as $fty).min(-9.0), $neginf);
+                $fassert!((-9.0 as $fty).min($neginf), $neginf);
+                $fassert!(($nan as $fty).min(9.0), 9.0);
+                $fassert!(($nan as $fty).min(-9.0), -9.0);
+                $fassert!((9.0 as $fty).min($nan), 9.0);
+                $fassert!((-9.0 as $fty).min($nan), -9.0);
+                $fassert!(($nan as $fty).min($nan).is_nan());
             }
             #[test]
             fn max() {
-                assert_eq!((0.0 as $fty).max(0.0), 0.0);
-                assert!((0.0 as $fty).max(0.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).max(-0.0), -0.0);
-                assert!((-0.0 as $fty).max(-0.0).is_sign_negative());
-                assert_eq!((9.0 as $fty).max(9.0), 9.0);
-                assert_eq!((-9.0 as $fty).max(0.0), 0.0);
-                assert!((-9.0 as $fty).max(0.0).is_sign_positive());
-                assert_eq!((-9.0 as $fty).max(-0.0), -0.0);
-                assert!((-9.0 as $fty).max(-0.0).is_sign_negative());
-                assert_eq!((0.0 as $fty).max(9.0), 9.0);
-                assert_eq!((0.0 as $fty).max(-9.0), 0.0);
-                assert!((0.0 as $fty).max(-9.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).max(-9.0), -0.0);
-                assert!((-0.0 as $fty).max(-9.0).is_sign_negative());
-                assert_eq!(($inf as $fty).max(9.0), $inf);
-                assert_eq!((9.0 as $fty).max($inf), $inf);
-                assert_eq!(($inf as $fty).max(-9.0), $inf);
-                assert_eq!((-9.0 as $fty).max($inf), $inf);
-                assert_eq!(($neginf as $fty).max(9.0), 9.0);
-                assert_eq!((9.0 as $fty).max($neginf), 9.0);
-                assert_eq!(($neginf as $fty).max(-9.0), -9.0);
-                assert_eq!((-9.0 as $fty).max($neginf), -9.0);
-                assert_eq!(($nan as $fty).max(9.0), 9.0);
-                assert_eq!(($nan as $fty).max(-9.0), -9.0);
-                assert_eq!((9.0 as $fty).max($nan), 9.0);
-                assert_eq!((-9.0 as $fty).max($nan), -9.0);
-                assert!(($nan as $fty).max($nan).is_nan());
+                $fassert!((0.0 as $fty).max(0.0), 0.0);
+                $fassert!((0.0 as $fty).max(0.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).max(-0.0), -0.0);
+                $fassert!((-0.0 as $fty).max(-0.0).is_sign_negative());
+                $fassert!((9.0 as $fty).max(9.0), 9.0);
+                $fassert!((-9.0 as $fty).max(0.0), 0.0);
+                $fassert!((-9.0 as $fty).max(0.0).is_sign_positive());
+                $fassert!((-9.0 as $fty).max(-0.0), -0.0);
+                $fassert!((-9.0 as $fty).max(-0.0).is_sign_negative());
+                $fassert!((0.0 as $fty).max(9.0), 9.0);
+                $fassert!((0.0 as $fty).max(-9.0), 0.0);
+                $fassert!((0.0 as $fty).max(-9.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).max(-9.0), -0.0);
+                $fassert!((-0.0 as $fty).max(-9.0).is_sign_negative());
+                $fassert!(($inf as $fty).max(9.0), $inf);
+                $fassert!((9.0 as $fty).max($inf), $inf);
+                $fassert!(($inf as $fty).max(-9.0), $inf);
+                $fassert!((-9.0 as $fty).max($inf), $inf);
+                $fassert!(($neginf as $fty).max(9.0), 9.0);
+                $fassert!((9.0 as $fty).max($neginf), 9.0);
+                $fassert!(($neginf as $fty).max(-9.0), -9.0);
+                $fassert!((-9.0 as $fty).max($neginf), -9.0);
+                $fassert!(($nan as $fty).max(9.0), 9.0);
+                $fassert!(($nan as $fty).max(-9.0), -9.0);
+                $fassert!((9.0 as $fty).max($nan), 9.0);
+                $fassert!((-9.0 as $fty).max($nan), -9.0);
+                $fassert!(($nan as $fty).max($nan).is_nan());
             }
             #[test]
             fn minimum() {
-                assert_eq!((0.0 as $fty).minimum(0.0), 0.0);
-                assert!((0.0 as $fty).minimum(0.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).minimum(0.0), -0.0);
-                assert!((-0.0 as $fty).minimum(0.0).is_sign_negative());
-                assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0);
-                assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative());
-                assert_eq!((9.0 as $fty).minimum(9.0), 9.0);
-                assert_eq!((-9.0 as $fty).minimum(0.0), -9.0);
-                assert_eq!((0.0 as $fty).minimum(9.0), 0.0);
-                assert!((0.0 as $fty).minimum(9.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).minimum(9.0), -0.0);
-                assert!((-0.0 as $fty).minimum(9.0).is_sign_negative());
-                assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0);
-                assert_eq!(($inf as $fty).minimum(9.0), 9.0);
-                assert_eq!((9.0 as $fty).minimum($inf), 9.0);
-                assert_eq!(($inf as $fty).minimum(-9.0), -9.0);
-                assert_eq!((-9.0 as $fty).minimum($inf), -9.0);
-                assert_eq!(($neginf as $fty).minimum(9.0), $neginf);
-                assert_eq!((9.0 as $fty).minimum($neginf), $neginf);
-                assert_eq!(($neginf as $fty).minimum(-9.0), $neginf);
-                assert_eq!((-9.0 as $fty).minimum($neginf), $neginf);
-                assert!(($nan as $fty).minimum(9.0).is_nan());
-                assert!(($nan as $fty).minimum(-9.0).is_nan());
-                assert!((9.0 as $fty).minimum($nan).is_nan());
-                assert!((-9.0 as $fty).minimum($nan).is_nan());
-                assert!(($nan as $fty).minimum($nan).is_nan());
+                $fassert!((0.0 as $fty).minimum(0.0), 0.0);
+                $fassert!((0.0 as $fty).minimum(0.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).minimum(0.0), -0.0);
+                $fassert!((-0.0 as $fty).minimum(0.0).is_sign_negative());
+                $fassert!((-0.0 as $fty).minimum(-0.0), -0.0);
+                $fassert!((-0.0 as $fty).minimum(-0.0).is_sign_negative());
+                $fassert!((9.0 as $fty).minimum(9.0), 9.0);
+                $fassert!((-9.0 as $fty).minimum(0.0), -9.0);
+                $fassert!((0.0 as $fty).minimum(9.0), 0.0);
+                $fassert!((0.0 as $fty).minimum(9.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).minimum(9.0), -0.0);
+                $fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative());
+                $fassert!((-0.0 as $fty).minimum(-9.0), -9.0);
+                $fassert!(($inf as $fty).minimum(9.0), 9.0);
+                $fassert!((9.0 as $fty).minimum($inf), 9.0);
+                $fassert!(($inf as $fty).minimum(-9.0), -9.0);
+                $fassert!((-9.0 as $fty).minimum($inf), -9.0);
+                $fassert!(($neginf as $fty).minimum(9.0), $neginf);
+                $fassert!((9.0 as $fty).minimum($neginf), $neginf);
+                $fassert!(($neginf as $fty).minimum(-9.0), $neginf);
+                $fassert!((-9.0 as $fty).minimum($neginf), $neginf);
+                $fassert!(($nan as $fty).minimum(9.0).is_nan());
+                $fassert!(($nan as $fty).minimum(-9.0).is_nan());
+                $fassert!((9.0 as $fty).minimum($nan).is_nan());
+                $fassert!((-9.0 as $fty).minimum($nan).is_nan());
+                $fassert!(($nan as $fty).minimum($nan).is_nan());
             }
             #[test]
             fn maximum() {
-                assert_eq!((0.0 as $fty).maximum(0.0), 0.0);
-                assert!((0.0 as $fty).maximum(0.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).maximum(0.0), 0.0);
-                assert!((-0.0 as $fty).maximum(0.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0);
-                assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative());
-                assert_eq!((9.0 as $fty).maximum(9.0), 9.0);
-                assert_eq!((-9.0 as $fty).maximum(0.0), 0.0);
-                assert!((-9.0 as $fty).maximum(0.0).is_sign_positive());
-                assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0);
-                assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative());
-                assert_eq!((0.0 as $fty).maximum(9.0), 9.0);
-                assert_eq!((0.0 as $fty).maximum(-9.0), 0.0);
-                assert!((0.0 as $fty).maximum(-9.0).is_sign_positive());
-                assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0);
-                assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative());
-                assert_eq!(($inf as $fty).maximum(9.0), $inf);
-                assert_eq!((9.0 as $fty).maximum($inf), $inf);
-                assert_eq!(($inf as $fty).maximum(-9.0), $inf);
-                assert_eq!((-9.0 as $fty).maximum($inf), $inf);
-                assert_eq!(($neginf as $fty).maximum(9.0), 9.0);
-                assert_eq!((9.0 as $fty).maximum($neginf), 9.0);
-                assert_eq!(($neginf as $fty).maximum(-9.0), -9.0);
-                assert_eq!((-9.0 as $fty).maximum($neginf), -9.0);
-                assert!(($nan as $fty).maximum(9.0).is_nan());
-                assert!(($nan as $fty).maximum(-9.0).is_nan());
-                assert!((9.0 as $fty).maximum($nan).is_nan());
-                assert!((-9.0 as $fty).maximum($nan).is_nan());
-                assert!(($nan as $fty).maximum($nan).is_nan());
+                $fassert!((0.0 as $fty).maximum(0.0), 0.0);
+                $fassert!((0.0 as $fty).maximum(0.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).maximum(0.0), 0.0);
+                $fassert!((-0.0 as $fty).maximum(0.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).maximum(-0.0), -0.0);
+                $fassert!((-0.0 as $fty).maximum(-0.0).is_sign_negative());
+                $fassert!((9.0 as $fty).maximum(9.0), 9.0);
+                $fassert!((-9.0 as $fty).maximum(0.0), 0.0);
+                $fassert!((-9.0 as $fty).maximum(0.0).is_sign_positive());
+                $fassert!((-9.0 as $fty).maximum(-0.0), -0.0);
+                $fassert!((-9.0 as $fty).maximum(-0.0).is_sign_negative());
+                $fassert!((0.0 as $fty).maximum(9.0), 9.0);
+                $fassert!((0.0 as $fty).maximum(-9.0), 0.0);
+                $fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive());
+                $fassert!((-0.0 as $fty).maximum(-9.0), -0.0);
+                $fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative());
+                $fassert!(($inf as $fty).maximum(9.0), $inf);
+                $fassert!((9.0 as $fty).maximum($inf), $inf);
+                $fassert!(($inf as $fty).maximum(-9.0), $inf);
+                $fassert!((-9.0 as $fty).maximum($inf), $inf);
+                $fassert!(($neginf as $fty).maximum(9.0), 9.0);
+                $fassert!((9.0 as $fty).maximum($neginf), 9.0);
+                $fassert!(($neginf as $fty).maximum(-9.0), -9.0);
+                $fassert!((-9.0 as $fty).maximum($neginf), -9.0);
+                $fassert!(($nan as $fty).maximum(9.0).is_nan());
+                $fassert!(($nan as $fty).maximum(-9.0).is_nan());
+                $fassert!((9.0 as $fty).maximum($nan).is_nan());
+                $fassert!((-9.0 as $fty).maximum($nan).is_nan());
+                $fassert!(($nan as $fty).maximum($nan).is_nan());
             }
             #[test]
             fn midpoint() {
-                assert_eq!((0.5 as $fty).midpoint(0.5), 0.5);
-                assert_eq!((0.5 as $fty).midpoint(2.5), 1.5);
-                assert_eq!((3.0 as $fty).midpoint(4.0), 3.5);
-                assert_eq!((-3.0 as $fty).midpoint(4.0), 0.5);
-                assert_eq!((3.0 as $fty).midpoint(-4.0), -0.5);
-                assert_eq!((-3.0 as $fty).midpoint(-4.0), -3.5);
-                assert_eq!((0.0 as $fty).midpoint(0.0), 0.0);
-                assert_eq!((-0.0 as $fty).midpoint(-0.0), -0.0);
-                assert_eq!((-5.0 as $fty).midpoint(5.0), 0.0);
-                assert_eq!(($max as $fty).midpoint($min), 0.0);
-                assert_eq!(($min as $fty).midpoint($max), -0.0);
-                assert_eq!(($max as $fty).midpoint($min_pos), $max / 2.);
-                assert_eq!((-$max as $fty).midpoint($min_pos), -$max / 2.);
-                assert_eq!(($max as $fty).midpoint(-$min_pos), $max / 2.);
-                assert_eq!((-$max as $fty).midpoint(-$min_pos), -$max / 2.);
-                assert_eq!(($min_pos as $fty).midpoint($max), $max / 2.);
-                assert_eq!(($min_pos as $fty).midpoint(-$max), -$max / 2.);
-                assert_eq!((-$min_pos as $fty).midpoint($max), $max / 2.);
-                assert_eq!((-$min_pos as $fty).midpoint(-$max), -$max / 2.);
-                assert_eq!(($max as $fty).midpoint($max), $max);
-                assert_eq!(($min_pos as $fty).midpoint($min_pos), $min_pos);
-                assert_eq!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos);
-                assert_eq!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5);
-                assert_eq!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5);
-                assert_eq!(($inf as $fty).midpoint($inf), $inf);
-                assert_eq!(($neginf as $fty).midpoint($neginf), $neginf);
-                assert!(($nan as $fty).midpoint(1.0).is_nan());
-                assert!((1.0 as $fty).midpoint($nan).is_nan());
-                assert!(($nan as $fty).midpoint($nan).is_nan());
+                $fassert!((0.5 as $fty).midpoint(0.5), 0.5);
+                $fassert!((0.5 as $fty).midpoint(2.5), 1.5);
+                $fassert!((3.0 as $fty).midpoint(4.0), 3.5);
+                $fassert!((-3.0 as $fty).midpoint(4.0), 0.5);
+                $fassert!((3.0 as $fty).midpoint(-4.0), -0.5);
+                $fassert!((-3.0 as $fty).midpoint(-4.0), -3.5);
+                $fassert!((0.0 as $fty).midpoint(0.0), 0.0);
+                $fassert!((-0.0 as $fty).midpoint(-0.0), -0.0);
+                $fassert!((-5.0 as $fty).midpoint(5.0), 0.0);
+                $fassert!(($max as $fty).midpoint($min), 0.0);
+                $fassert!(($min as $fty).midpoint($max), -0.0);
+                $fassert!(($max as $fty).midpoint($min_pos), $max / 2.);
+                $fassert!((-$max as $fty).midpoint($min_pos), -$max / 2.);
+                $fassert!(($max as $fty).midpoint(-$min_pos), $max / 2.);
+                $fassert!((-$max as $fty).midpoint(-$min_pos), -$max / 2.);
+                $fassert!(($min_pos as $fty).midpoint($max), $max / 2.);
+                $fassert!(($min_pos as $fty).midpoint(-$max), -$max / 2.);
+                $fassert!((-$min_pos as $fty).midpoint($max), $max / 2.);
+                $fassert!((-$min_pos as $fty).midpoint(-$max), -$max / 2.);
+                $fassert!(($max as $fty).midpoint($max), $max);
+                $fassert!(($min_pos as $fty).midpoint($min_pos), $min_pos);
+                $fassert!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos);
+                $fassert!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5);
+                $fassert!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5);
+                $fassert!(($inf as $fty).midpoint($inf), $inf);
+                $fassert!(($neginf as $fty).midpoint($neginf), $neginf);
+                $fassert!(($nan as $fty).midpoint(1.0).is_nan());
+                $fassert!((1.0 as $fty).midpoint($nan).is_nan());
+                $fassert!(($nan as $fty).midpoint($nan).is_nan());
 
                 // test if large differences in magnitude are still correctly computed.
                 // NOTE: that because of how small x and y are, x + y can never overflow
@@ -907,19 +907,19 @@ macro_rules! test_float {
             }
             #[test]
             fn rem_euclid() {
-                let a: $fty = 42.0;
-                assert!($inf.rem_euclid(a).is_nan());
-                assert_eq!(a.rem_euclid($inf), a);
-                assert!(a.rem_euclid($nan).is_nan());
+                // FIXME: Use $fassert when rem_euclid becomes const
+                assert!($inf.rem_euclid((42.0 as $fty)).is_nan());
+                assert_eq!((42.0 as $fty).rem_euclid($inf), (42.0 as $fty));
+                assert!((42.0 as $fty).rem_euclid($nan).is_nan());
                 assert!($inf.rem_euclid($inf).is_nan());
                 assert!($inf.rem_euclid($nan).is_nan());
                 assert!($nan.rem_euclid($inf).is_nan());
             }
             #[test]
             fn div_euclid() {
-                let a: $fty = 42.0;
-                assert_eq!(a.div_euclid($inf), 0.0);
-                assert!(a.div_euclid($nan).is_nan());
+                // FIXME: Use $fassert when div_euclid becomes const
+                assert_eq!((42.0 as $fty).div_euclid($inf), 0.0);
+                assert!((42.0 as $fty).div_euclid($nan).is_nan());
                 assert!($inf.div_euclid($inf).is_nan());
                 assert!($inf.div_euclid($nan).is_nan());
                 assert!($nan.div_euclid($inf).is_nan());
@@ -928,8 +928,41 @@ macro_rules! test_float {
     };
 }
 
+// Custom assert macro that distribute between assert! and assert_eq! in a non-const context
+macro_rules! float_assert {
+    ($b:expr) => {
+        assert!($b);
+    };
+    ($left:expr, $right:expr) => {
+        assert_eq!($left, $right);
+    };
+}
+
+// Custom assert macro that only uses assert! in a const context
+macro_rules! float_const_assert {
+    ($b:expr) => {
+        assert!(const { $b });
+    };
+    ($left:expr, $right:expr) => {
+        assert!(const { $left == $right });
+    };
+}
+
 test_float!(
     f32,
+    float_assert,
+    f32,
+    f32::INFINITY,
+    f32::NEG_INFINITY,
+    f32::NAN,
+    f32::MIN,
+    f32::MAX,
+    f32::MIN_POSITIVE,
+    f32::MAX_EXP
+);
+test_float!(
+    f32_const,
+    float_const_assert,
     f32,
     f32::INFINITY,
     f32::NEG_INFINITY,
@@ -941,6 +974,19 @@ test_float!(
 );
 test_float!(
     f64,
+    float_assert,
+    f64,
+    f64::INFINITY,
+    f64::NEG_INFINITY,
+    f64::NAN,
+    f64::MIN,
+    f64::MAX,
+    f64::MIN_POSITIVE,
+    f64::MAX_EXP
+);
+test_float!(
+    f64_const,
+    float_const_assert,
     f64,
     f64::INFINITY,
     f64::NEG_INFINITY,
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 5c1d2deb481..ca04a381271 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -305,7 +305,6 @@
 #![feature(iter_advance_by)]
 #![feature(iter_next_chunk)]
 #![feature(lang_items)]
-#![feature(let_chains)]
 #![feature(link_cfg)]
 #![feature(linkage)]
 #![feature(macro_metavar_expr_concat)]
diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs
index 8bf6d833515..a3be2cdf738 100644
--- a/library/std/src/sys/pal/unix/stack_overflow.rs
+++ b/library/std/src/sys/pal/unix/stack_overflow.rs
@@ -25,15 +25,36 @@ impl Drop for Handler {
     }
 }
 
-#[cfg(any(
-    target_os = "linux",
-    target_os = "freebsd",
-    target_os = "hurd",
-    target_os = "macos",
-    target_os = "netbsd",
-    target_os = "openbsd",
-    target_os = "solaris",
-    target_os = "illumos",
+#[cfg(all(
+    not(miri),
+    any(
+        target_os = "linux",
+        target_os = "freebsd",
+        target_os = "hurd",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "solaris",
+        target_os = "illumos",
+    ),
+))]
+mod thread_info;
+
+// miri doesn't model signals nor stack overflows and this code has some
+// synchronization properties that we don't want to expose to user code,
+// hence we disable it on miri.
+#[cfg(all(
+    not(miri),
+    any(
+        target_os = "linux",
+        target_os = "freebsd",
+        target_os = "hurd",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "solaris",
+        target_os = "illumos",
+    )
 ))]
 mod imp {
     use libc::{
@@ -46,22 +67,13 @@ mod imp {
     use libc::{mmap64, mprotect, munmap};
 
     use super::Handler;
-    use crate::cell::Cell;
+    use super::thread_info::{delete_current_info, set_current_info, with_current_info};
     use crate::ops::Range;
     use crate::sync::OnceLock;
     use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
     use crate::sys::pal::unix::os;
-    use crate::{io, mem, ptr, thread};
-
-    // We use a TLS variable to store the address of the guard page. While TLS
-    // variables are not guaranteed to be signal-safe, this works out in practice
-    // since we make sure to write to the variable before the signal stack is
-    // installed, thereby ensuring that the variable is always allocated when
-    // the signal handler is called.
-    thread_local! {
-        // FIXME: use `Range` once that implements `Copy`.
-        static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) };
-    }
+    use crate::thread::with_current_name;
+    use crate::{io, mem, panic, ptr};
 
     // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
     // (unmapped pages) at the end of every thread's stack, so if a thread ends
@@ -93,29 +105,35 @@ mod imp {
         info: *mut libc::siginfo_t,
         _data: *mut libc::c_void,
     ) {
-        let (start, end) = GUARD.get();
         // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`.
-        let addr = unsafe { (*info).si_addr().addr() };
+        let fault_addr = unsafe { (*info).si_addr().addr() };
+
+        // `with_current_info` expects that the process aborts after it is
+        // called. If the signal was not caused by a memory access, this might
+        // not be true. We detect this by noticing that the `si_addr` field is
+        // zero if the signal is synthetic.
+        if fault_addr != 0 {
+            with_current_info(|thread_info| {
+                // If the faulting address is within the guard page, then we print a
+                // message saying so and abort.
+                if let Some(thread_info) = thread_info
+                    && thread_info.guard_page_range.contains(&fault_addr)
+                {
+                    let name = thread_info.thread_name.as_deref().unwrap_or("<unknown>");
+                    rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
+                    rtabort!("stack overflow");
+                }
+            })
+        }
 
-        // If the faulting address is within the guard page, then we print a
-        // message saying so and abort.
-        if start <= addr && addr < end {
-            thread::with_current_name(|name| {
-                let name = name.unwrap_or("<unknown>");
-                rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
-            });
+        // Unregister ourselves by reverting back to the default behavior.
+        // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
+        let mut action: sigaction = unsafe { mem::zeroed() };
+        action.sa_sigaction = SIG_DFL;
+        // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
+        unsafe { sigaction(signum, &action, ptr::null_mut()) };
 
-            rtabort!("stack overflow");
-        } else {
-            // Unregister ourselves by reverting back to the default behavior.
-            // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
-            let mut action: sigaction = unsafe { mem::zeroed() };
-            action.sa_sigaction = SIG_DFL;
-            // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
-            unsafe { sigaction(signum, &action, ptr::null_mut()) };
-
-            // See comment above for why this function returns.
-        }
+        // See comment above for why this function returns.
     }
 
     static PAGE_SIZE: Atomic<usize> = AtomicUsize::new(0);
@@ -128,9 +146,7 @@ mod imp {
     pub unsafe fn init() {
         PAGE_SIZE.store(os::page_size(), Ordering::Relaxed);
 
-        // Always write to GUARD to ensure the TLS variable is allocated.
-        let guard = unsafe { install_main_guard().unwrap_or(0..0) };
-        GUARD.set((guard.start, guard.end));
+        let mut guard_page_range = unsafe { install_main_guard() };
 
         // SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
         let mut action: sigaction = unsafe { mem::zeroed() };
@@ -145,7 +161,13 @@ mod imp {
                     let handler = unsafe { make_handler(true) };
                     MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
                     mem::forget(handler);
+
+                    if let Some(guard_page_range) = guard_page_range.take() {
+                        let thread_name = with_current_name(|name| name.map(Box::from));
+                        set_current_info(guard_page_range, thread_name);
+                    }
                 }
+
                 action.sa_flags = SA_SIGINFO | SA_ONSTACK;
                 action.sa_sigaction = signal_handler as sighandler_t;
                 // SAFETY: only overriding signals if the default is set
@@ -214,9 +236,10 @@ mod imp {
         }
 
         if !main_thread {
-            // Always write to GUARD to ensure the TLS variable is allocated.
-            let guard = unsafe { current_guard() }.unwrap_or(0..0);
-            GUARD.set((guard.start, guard.end));
+            if let Some(guard_page_range) = unsafe { current_guard() } {
+                let thread_name = with_current_name(|name| name.map(Box::from));
+                set_current_info(guard_page_range, thread_name);
+            }
         }
 
         // SAFETY: assuming stack_t is zero-initializable
@@ -261,6 +284,8 @@ mod imp {
             // a mapping that started one page earlier, so walk back a page and unmap from there.
             unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
         }
+
+        delete_current_info();
     }
 
     /// Modern kernels on modern hardware can have dynamic signal stack sizes.
@@ -590,17 +615,20 @@ mod imp {
 // usually have fewer qualms about forwards compatibility, since the runtime
 // is shipped with the OS):
 // <https://github.com/apple/swift/blob/swift-5.10-RELEASE/stdlib/public/runtime/CrashHandlerMacOS.cpp>
-#[cfg(not(any(
-    target_os = "linux",
-    target_os = "freebsd",
-    target_os = "hurd",
-    target_os = "macos",
-    target_os = "netbsd",
-    target_os = "openbsd",
-    target_os = "solaris",
-    target_os = "illumos",
-    target_os = "cygwin",
-)))]
+#[cfg(any(
+    miri,
+    not(any(
+        target_os = "linux",
+        target_os = "freebsd",
+        target_os = "hurd",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "cygwin",
+    ))
+))]
 mod imp {
     pub unsafe fn init() {}
 
diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs
new file mode 100644
index 00000000000..e81429b98a6
--- /dev/null
+++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs
@@ -0,0 +1,129 @@
+//! TLS, but async-signal-safe.
+//!
+//! Unfortunately, because thread local storage isn't async-signal-safe, we
+//! cannot soundly use it in our stack overflow handler. While this works
+//! without problems on most platforms, it can lead to undefined behaviour
+//! on others (such as GNU/Linux). Luckily, the POSIX specification documents
+//! two thread-specific values that can be accessed in asynchronous signal
+//! handlers: the value of `pthread_self()` and the address of `errno`. As
+//! `pthread_t` is an opaque platform-specific type, we use the address of
+//! `errno` here. As it is thread-specific and does not change over the
+//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap`
+//! that stores thread-specific data.
+//!
+//! Concurrent access to this map is synchronized by two locks – an outer
+//! [`Mutex`] and an inner spin lock that also remembers the identity of
+//! the lock owner:
+//! * The spin lock is the primary means of synchronization: since it only
+//!   uses native atomics, it can be soundly used inside the signal handle
+//!   as opposed to [`Mutex`], which might not be async-signal-safe.
+//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses
+//!   there are performed with the [`Mutex`] held, which makes the spin-lock
+//!   redundant in the common case.
+//! * Finally, by using the `errno` address as the locked value of the spin
+//!   lock, we can detect cases where a SIGSEGV occurred while the thread
+//!   info is being modified.
+
+use crate::collections::BTreeMap;
+use crate::hint::spin_loop;
+use crate::ops::Range;
+use crate::sync::Mutex;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sys::os::errno_location;
+
+pub struct ThreadInfo {
+    pub guard_page_range: Range<usize>,
+    pub thread_name: Option<Box<str>>,
+}
+
+static LOCK: Mutex<()> = Mutex::new(());
+static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0);
+// This uses a `BTreeMap` instead of a hashmap since it supports constant
+// initialization and automatically reduces the amount of memory used when
+// items are removed.
+static mut THREAD_INFO: BTreeMap<usize, ThreadInfo> = BTreeMap::new();
+
+struct UnlockOnDrop;
+
+impl Drop for UnlockOnDrop {
+    fn drop(&mut self) {
+        SPIN_LOCK.store(0, Ordering::Release);
+    }
+}
+
+/// Get the current thread's information, if available.
+///
+/// Calling this function might freeze other threads if they attempt to modify
+/// their thread information. Thus, the caller should ensure that the process
+/// is aborted shortly after this function is called.
+///
+/// This function is guaranteed to be async-signal-safe if `f` is too.
+pub fn with_current_info<R>(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R {
+    let this = errno_location().addr();
+    let mut attempt = 0;
+    let _guard = loop {
+        // If we are just spinning endlessly, it's very likely that the thread
+        // modifying the thread info map has a lower priority than us and will
+        // not continue until we stop running. Just give up in that case.
+        if attempt == 10_000_000 {
+            rtprintpanic!("deadlock in SIGSEGV handler");
+            return f(None);
+        }
+
+        match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) {
+            Ok(_) => break UnlockOnDrop,
+            Err(owner) if owner == this => {
+                rtabort!("a thread received SIGSEGV while modifying its stack overflow information")
+            }
+            // Spin until the lock can be acquired – there is nothing better to
+            // do. This is unfortunately a priority hole, but a stack overflow
+            // is a fatal error anyway.
+            Err(_) => {
+                spin_loop();
+                attempt += 1;
+            }
+        }
+    };
+
+    // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased.
+    let thread_info = unsafe { &*(&raw const THREAD_INFO) };
+    f(thread_info.get(&this))
+}
+
+fn spin_lock_in_setup(this: usize) -> UnlockOnDrop {
+    loop {
+        match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) {
+            Ok(_) => return UnlockOnDrop,
+            Err(owner) if owner == this => {
+                unreachable!("the thread info setup logic isn't recursive")
+            }
+            // This function is always called with the outer lock held,
+            // meaning the only time locking can fail is if another thread has
+            // encountered a stack overflow. Since that will abort the process,
+            // we just stop the current thread until that time. We use `pause`
+            // instead of spinning to avoid priority inversion.
+            // SAFETY: this doesn't have any safety preconditions.
+            Err(_) => drop(unsafe { libc::pause() }),
+        }
+    }
+}
+
+pub fn set_current_info(guard_page_range: Range<usize>, thread_name: Option<Box<str>>) {
+    let this = errno_location().addr();
+    let _lock_guard = LOCK.lock();
+    let _spin_guard = spin_lock_in_setup(this);
+
+    // SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased.
+    let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) };
+    thread_info.insert(this, ThreadInfo { guard_page_range, thread_name });
+}
+
+pub fn delete_current_info() {
+    let this = errno_location().addr();
+    let _lock_guard = LOCK.lock();
+    let _spin_guard = spin_lock_in_setup(this);
+
+    // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased.
+    let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) };
+    thread_info.remove(&this);
+}
diff --git a/library/stdarch b/library/stdarch
-Subproject f1c1839c0deb985a9f98cbd6b38a6d43f2df615
+Subproject 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index ff63b8c62d3..d10d2d9bf8c 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -64,7 +64,7 @@ dependencies = [
  "tracing-subscriber",
  "tracing-tree",
  "walkdir",
- "windows 0.57.0",
+ "windows",
  "xz2",
 ]
 
@@ -703,7 +703,7 @@ dependencies = [
  "ntapi",
  "objc2-core-foundation",
  "objc2-io-kit",
- "windows 0.61.1",
+ "windows",
 ]
 
 [[package]]
@@ -918,22 +918,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
-dependencies = [
- "windows-core 0.57.0",
- "windows-targets",
-]
-
-[[package]]
-name = "windows"
 version = "0.61.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
 dependencies = [
  "windows-collections",
- "windows-core 0.61.0",
+ "windows-core",
  "windows-future",
  "windows-link",
  "windows-numerics",
@@ -945,19 +935,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
 dependencies = [
- "windows-core 0.61.0",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
-dependencies = [
- "windows-implement 0.57.0",
- "windows-interface 0.57.0",
- "windows-result 0.1.2",
- "windows-targets",
+ "windows-core",
 ]
 
 [[package]]
@@ -966,10 +944,10 @@ version = "0.61.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
 dependencies = [
- "windows-implement 0.60.0",
- "windows-interface 0.59.1",
+ "windows-implement",
+ "windows-interface",
  "windows-link",
- "windows-result 0.3.2",
+ "windows-result",
  "windows-strings",
 ]
 
@@ -979,23 +957,12 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
 dependencies = [
- "windows-core 0.61.0",
+ "windows-core",
  "windows-link",
 ]
 
 [[package]]
 name = "windows-implement"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "windows-implement"
 version = "0.60.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
@@ -1007,17 +974,6 @@ dependencies = [
 
 [[package]]
 name = "windows-interface"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "windows-interface"
 version = "0.59.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
@@ -1039,21 +995,12 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
 dependencies = [
- "windows-core 0.61.0",
+ "windows-core",
  "windows-link",
 ]
 
 [[package]]
 name = "windows-result"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
-dependencies = [
- "windows-targets",
-]
-
-[[package]]
-name = "windows-result"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index e34de924cc1..9652d18f1a6 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -70,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true }
 version = "1.0.0"
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.57"
+version = "0.61"
 features = [
     "Win32_Foundation",
     "Win32_Security",
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 85c682a46c5..374884d8a9a 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -342,7 +342,7 @@ fn format_rusage_data(child: Child) -> Option<String> {
     use windows::Win32::System::Threading::GetProcessTimes;
     use windows::Win32::System::Time::FileTimeToSystemTime;
 
-    let handle = HANDLE(child.as_raw_handle() as isize);
+    let handle = HANDLE(child.as_raw_handle());
 
     let mut user_filetime = Default::default();
     let mut user_time = Default::default();
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index b2dc509ddca..27791825aa0 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -3554,7 +3554,7 @@ impl Step for TestFloatParse {
         builder.ensure(tool::TestFloatParse { host: self.host });
 
         // Run any unit tests in the crate
-        let cargo_test = tool::prepare_tool_cargo(
+        let mut cargo_test = tool::prepare_tool_cargo(
             builder,
             compiler,
             Mode::ToolStd,
@@ -3564,6 +3564,7 @@ impl Step for TestFloatParse {
             SourceType::InTree,
             &[],
         );
+        cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
 
         run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder);
 
@@ -3578,6 +3579,7 @@ impl Step for TestFloatParse {
             SourceType::InTree,
             &[],
         );
+        cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
 
         if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) {
             cargo_run.args(["--", "--skip-huge"]);
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index ac568eab2e8..678aa9b01e4 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1259,6 +1259,10 @@ pub struct TestFloatParse {
     pub host: TargetSelection,
 }
 
+impl TestFloatParse {
+    pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128";
+}
+
 impl Step for TestFloatParse {
     type Output = ToolBuildResult;
     const ONLY_HOSTS: bool = true;
@@ -1280,7 +1284,7 @@ impl Step for TestFloatParse {
             path: "src/etc/test-float-parse",
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
-            allow_features: "",
+            allow_features: Self::ALLOW_FEATURES,
             cargo_args: Vec::new(),
             artifact_kind: ToolArtifactKind::Binary,
         })
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index cc4fa953ddc..af3e3cc37b9 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -1388,7 +1388,7 @@ impl<'a> Builder<'a> {
         // Windows doesn't need dylib path munging because the dlls for the
         // compiler live next to the compiler and the system will find them
         // automatically.
-        if cfg!(windows) {
+        if cfg!(any(windows, target_os = "cygwin")) {
             return;
         }
 
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index b29c1fb3889..f2c3e8c0df4 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -130,7 +130,7 @@ pub fn is_debug_info(name: &str) -> bool {
 /// Returns the corresponding relative library directory that the compiler's
 /// dylibs will be found in.
 pub fn libdir(target: TargetSelection) -> &'static str {
-    if target.is_windows() { "bin" } else { "lib" }
+    if target.is_windows() || target.contains("cygwin") { "bin" } else { "lib" }
 }
 
 /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs
index 4949518de79..887deb41ca8 100644
--- a/src/bootstrap/src/utils/job.rs
+++ b/src/bootstrap/src/utils/job.rs
@@ -66,7 +66,6 @@ mod for_windows {
             // Enable the Windows Error Reporting dialog which msys disables,
             // so we can JIT debug rustc
             let mode = SetErrorMode(THREAD_ERROR_MODE::default());
-            let mode = THREAD_ERROR_MODE(mode);
             SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
 
             // Create a new job object for us to use
diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs
index 1297a53d488..08e1c21e58e 100644
--- a/src/bootstrap/src/utils/shared_helpers.rs
+++ b/src/bootstrap/src/utils/shared_helpers.rs
@@ -20,7 +20,7 @@ use std::str::FromStr;
 /// Returns the environment variable which the dynamic library lookup path
 /// resides in for this platform.
 pub fn dylib_path_var() -> &'static str {
-    if cfg!(target_os = "windows") {
+    if cfg!(any(target_os = "windows", target_os = "cygwin")) {
         "PATH"
     } else if cfg!(target_vendor = "apple") {
         "DYLD_LIBRARY_PATH"
diff --git a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md
index 9ba4eff629e..696f2038e1a 100644
--- a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md
+++ b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md
@@ -1,9 +1,9 @@
 # Rust for Linux notification group
 
-**Github Label:** [O-rfl] <br>
+**Github Label:** [A-rust-for-linux] <br>
 **Ping command:** `@rustbot ping rfl`
 
-[O-rfl]: https://github.com/rust-lang/rust/labels/O-rfl
+[A-rust-for-linux]: https://github.com/rust-lang/rust/labels/A-rust-for-linux
 
 This list will be used to notify [Rust for Linux (RfL)][rfl] maintainers
 when the compiler or the standard library changes in a way that would
diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml
index 167aece0ed6..01f127ad390 100644
--- a/src/doc/rustc/book.toml
+++ b/src/doc/rustc/book.toml
@@ -6,6 +6,8 @@ title = "The rustc book"
 [output.html]
 git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc"
 edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}"
+additional-css = ["theme/pagetoc.css"]
+additional-js = ["theme/pagetoc.js"]
 
 [output.html.search]
 use-boolean-and = true
diff --git a/src/doc/rustc/theme/pagetoc.css b/src/doc/rustc/theme/pagetoc.css
new file mode 100644
index 00000000000..58ca1f8b26f
--- /dev/null
+++ b/src/doc/rustc/theme/pagetoc.css
@@ -0,0 +1,84 @@
+/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */
+
+:root {
+    --toc-width: 270px;
+    --center-content-toc-shift: calc(-1 * var(--toc-width) / 2);
+}
+
+.nav-chapters {
+    /* adjust width of buttons that bring to the previous or the next page */
+    min-width: 50px;
+}
+
+@media only screen {
+    @media (max-width: 1179px) {
+        .sidebar-hidden #sidetoc {
+            display: none;
+        }
+    }
+
+    @media (max-width: 1439px) {
+        .sidebar-visible #sidetoc {
+            display: none;
+        }
+    }
+
+    @media (1180px <= width <= 1439px) {
+        .sidebar-hidden main {
+            position: relative;
+            left: var(--center-content-toc-shift);
+        }
+    }
+
+    @media (1440px <= width <= 1700px) {
+        .sidebar-visible main {
+            position: relative;
+            left: var(--center-content-toc-shift);
+        }
+    }
+
+    #sidetoc {
+        margin-left: calc(100% + 20px);
+    }
+    #pagetoc {
+        position: fixed;
+        /* adjust TOC width */
+        width: var(--toc-width);
+        height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
+        overflow: auto;
+    }
+    #pagetoc a {
+        border-left: 1px solid var(--sidebar-bg);
+        color: var(--sidebar-fg) !important;
+        display: block;
+        padding-bottom: 5px;
+        padding-top: 5px;
+        padding-left: 10px;
+        text-align: left;
+        text-decoration: none;
+    }
+    #pagetoc a:hover,
+    #pagetoc a.active {
+        background: var(--sidebar-bg);
+        color: var(--sidebar-active) !important;
+    }
+    #pagetoc .active {
+        background: var(--sidebar-bg);
+        color: var(--sidebar-active);
+    }
+    #pagetoc .pagetoc-H2 {
+        padding-left: 20px;
+    }
+    #pagetoc .pagetoc-H3 {
+        padding-left: 40px;
+    }
+    #pagetoc .pagetoc-H4 {
+        padding-left: 60px;
+    }
+}
+
+@media print {
+    #sidetoc {
+        display: none;
+    }
+}
diff --git a/src/doc/rustc/theme/pagetoc.js b/src/doc/rustc/theme/pagetoc.js
new file mode 100644
index 00000000000..927a5b10749
--- /dev/null
+++ b/src/doc/rustc/theme/pagetoc.js
@@ -0,0 +1,104 @@
+// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL)
+
+let activeHref = location.href;
+function updatePageToc(elem = undefined) {
+    let selectedPageTocElem = elem;
+    const pagetoc = document.getElementById("pagetoc");
+
+    function getRect(element) {
+        return element.getBoundingClientRect();
+    }
+
+    function overflowTop(container, element) {
+        return getRect(container).top - getRect(element).top;
+    }
+
+    function overflowBottom(container, element) {
+        return getRect(container).bottom - getRect(element).bottom;
+    }
+
+    // We've not selected a heading to highlight, and the URL needs updating
+    // so we need to find a heading based on the URL
+    if (selectedPageTocElem === undefined && location.href !== activeHref) {
+        activeHref = location.href;
+        for (const pageTocElement of pagetoc.children) {
+            if (pageTocElement.href === activeHref) {
+                selectedPageTocElem = pageTocElement;
+            }
+        }
+    }
+
+    // We still don't have a selected heading, let's try and find the most
+    // suitable heading based on the scroll position
+    if (selectedPageTocElem === undefined) {
+        const margin = window.innerHeight / 3;
+
+        const headers = document.getElementsByClassName("header");
+        for (let i = 0; i < headers.length; i++) {
+            const header = headers[i];
+            if (selectedPageTocElem === undefined && getRect(header).top >= 0) {
+                if (getRect(header).top < margin) {
+                    selectedPageTocElem = header;
+                } else {
+                    selectedPageTocElem = headers[Math.max(0, i - 1)];
+                }
+            }
+            // a very long last section's heading is over the screen
+            if (selectedPageTocElem === undefined && i === headers.length - 1) {
+                selectedPageTocElem = header;
+            }
+        }
+    }
+
+    // Remove the active flag from all pagetoc elements
+    for (const pageTocElement of pagetoc.children) {
+        pageTocElement.classList.remove("active");
+    }
+
+    // If we have a selected heading, set it to active and scroll to it
+    if (selectedPageTocElem !== undefined) {
+        for (const pageTocElement of pagetoc.children) {
+            if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) {
+                pageTocElement.classList.add("active");
+                if (overflowTop(pagetoc, pageTocElement) > 0) {
+                    pagetoc.scrollTop = pageTocElement.offsetTop;
+                }
+                if (overflowBottom(pagetoc, pageTocElement) < 0) {
+                    pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement);
+                }
+            }
+        }
+    }
+}
+
+if (document.getElementById("sidetoc") === null &&
+    document.getElementsByClassName("header").length > 0) {
+    // The sidetoc element doesn't exist yet, let's create it
+
+    // Create the empty sidetoc and pagetoc elements
+    const sidetoc = document.createElement("div");
+    const pagetoc = document.createElement("div");
+    sidetoc.id = "sidetoc";
+    pagetoc.id = "pagetoc";
+    sidetoc.appendChild(pagetoc);
+
+    // And append them to the current DOM
+    const main = document.querySelector('main');
+    main.insertBefore(sidetoc, main.firstChild);
+
+    // Populate sidebar on load
+    window.addEventListener("load", () => {
+        for (const header of document.getElementsByClassName("header")) {
+            const link = document.createElement("a");
+            link.innerHTML = header.innerHTML;
+            link.href = header.hash;
+            link.classList.add("pagetoc-" + header.parentElement.tagName);
+            document.getElementById("pagetoc").appendChild(link);
+            link.onclick = () => updatePageToc(link);
+        }
+        updatePageToc();
+    });
+
+    // Update page table of contents selected heading on scroll
+    window.addEventListener("scroll", () => updatePageToc());
+}
diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml
index 8a9c5322ef7..e407e322f9e 100644
--- a/src/etc/test-float-parse/Cargo.toml
+++ b/src/etc/test-float-parse/Cargo.toml
@@ -13,3 +13,10 @@ rayon = "1"
 
 [lib]
 name = "test_float_parse"
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = [
+    # Internal features aren't marked known config by default
+    'cfg(target_has_reliable_f16)',
+]
diff --git a/src/etc/test-float-parse/src/gen_/subnorm.rs b/src/etc/test-float-parse/src/gen_/subnorm.rs
index 4fe3b90a3dd..654f324b9b0 100644
--- a/src/etc/test-float-parse/src/gen_/subnorm.rs
+++ b/src/etc/test-float-parse/src/gen_/subnorm.rs
@@ -1,4 +1,3 @@
-use std::cmp::min;
 use std::fmt::Write;
 use std::ops::RangeInclusive;
 
@@ -83,7 +82,13 @@ where
     }
 
     fn new() -> Self {
-        Self { iter: F::Int::ZERO..=min(F::Int::ONE << 22, F::MAN_BITS.try_into().unwrap()) }
+        let upper_lim = if F::MAN_BITS >= 22 {
+            F::Int::ONE << 22
+        } else {
+            (F::Int::ONE << F::MAN_BITS) - F::Int::ONE
+        };
+
+        Self { iter: F::Int::ZERO..=upper_lim }
     }
 
     fn write_string(s: &mut String, ctx: Self::WriteCtx) {
diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs
index 3c3ef5802b6..0bd4878f9a6 100644
--- a/src/etc/test-float-parse/src/lib.rs
+++ b/src/etc/test-float-parse/src/lib.rs
@@ -1,3 +1,7 @@
+#![feature(f16)]
+#![feature(cfg_target_has_reliable_f16_f128)]
+#![expect(internal_features)] // reliable_f16_f128
+
 mod traits;
 mod ui;
 mod validate;
@@ -114,6 +118,9 @@ pub fn register_tests(cfg: &Config) -> Vec<TestInfo> {
     let mut tests = Vec::new();
 
     // Register normal generators for all floats.
+
+    #[cfg(target_has_reliable_f16)]
+    register_float::<f16>(&mut tests, cfg);
     register_float::<f32>(&mut tests, cfg);
     register_float::<f64>(&mut tests, cfg);
 
diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs
index 57e702b7d09..65a8721bfa5 100644
--- a/src/etc/test-float-parse/src/traits.rs
+++ b/src/etc/test-float-parse/src/traits.rs
@@ -98,7 +98,7 @@ macro_rules! impl_int {
     }
 }
 
-impl_int!(u32, i32; u64, i64);
+impl_int!(u16, i16; u32, i32; u64, i64);
 
 /// Floating point types.
 pub trait Float:
@@ -170,6 +170,9 @@ macro_rules! impl_float {
 
 impl_float!(f32, u32; f64, u64);
 
+#[cfg(target_has_reliable_f16)]
+impl_float!(f16, u16);
+
 /// A test generator. Should provide an iterator that produces unique patterns to parse.
 ///
 /// The iterator needs to provide a `WriteCtx` (could be anything), which is then used to
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index b4210e7b518..b4003044e20 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -11,7 +11,6 @@
 #![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(iter_intersperse)]
-#![feature(let_chains)]
 #![feature(never_type)]
 #![feature(round_char_boundary)]
 #![feature(test)]
diff --git a/src/llvm-project b/src/llvm-project
-Subproject 8448283b4bd34ea00d76fd4f18ec730b549d6e1
+Subproject c1118fdbb3024157df7f4cfe765f2b0b4339e8a
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index 93f7b1cb7cf..3b544d8b828 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -37,7 +37,7 @@ libc = "0.2"
 miow = "0.6"
 
 [target.'cfg(windows)'.dependencies.windows]
-version = "0.59.0"
+version = "0.61.0"
 features = [
     "Win32_Foundation",
     "Win32_System_Diagnostics_Debug",
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 8b98fe3c4fc..0d889a5d5b9 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-ac17c3486c6fdfbb0c3c18b99f3d8dfbff625d29
+e42bbfe1f7c26f8760a99c4b1f27d33aba1040bb
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
index 0258a76c3e7..837e1b31cac 100644
--- a/src/tools/miri/src/shims/native_lib.rs
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -117,7 +117,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
         unsafe {
             if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
-                if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
+                let info = info.assume_init();
+                #[cfg(target_os = "cygwin")]
+                let fname_ptr = info.dli_fname.as_ptr();
+                #[cfg(not(target_os = "cygwin"))]
+                let fname_ptr = info.dli_fname;
+                if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap()
                     != _lib_path.to_str().unwrap()
                 {
                     return None;
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs
index 47f086f7340..48633c0a7fe 100644
--- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs
@@ -2,7 +2,7 @@
 //@only-target: x86_64 i686
 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f
 
-#![feature(avx512_target_feature, stdarch_x86_avx512)]
+#![feature(stdarch_x86_avx512)]
 
 use core::mem::transmute;
 #[cfg(target_arch = "x86")]
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs
index db593063890..0ec2f679d80 100644
--- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs
@@ -2,7 +2,6 @@
 //@only-target: x86_64 i686
 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq
 
-#![feature(avx512_target_feature)]
 #![feature(stdarch_x86_avx512)]
 
 #[cfg(target_arch = "x86")]
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs
index 882b5e3f795..b58d68e2ef9 100644
--- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs
@@ -6,7 +6,6 @@
 // be interpreted as integers; signedness does not make sense for them, but
 // __mXXXi happens to be defined in terms of signed integers.
 #![allow(overflowing_literals)]
-#![feature(avx512_target_feature)]
 #![feature(stdarch_x86_avx512)]
 
 #[cfg(target_arch = "x86")]
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
index 68964728e4e..c7c9eb5e395 100644
--- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
@@ -8,7 +8,6 @@
 // be interpreted as integers; signedness does not make sense for them, but
 // __mXXXi happens to be defined in terms of signed integers.
 #![allow(overflowing_literals)]
-#![feature(avx512_target_feature)]
 #![feature(stdarch_x86_avx512)]
 
 #[cfg(target_arch = "x86")]
diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs
index 58c8d21bd7a..1a9614bacec 100644
--- a/src/tools/rustfmt/src/parse/macros/asm.rs
+++ b/src/tools/rustfmt/src/parse/macros/asm.rs
@@ -1,10 +1,10 @@
 use rustc_ast::ast;
-use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args};
+use rustc_builtin_macros::asm::{AsmArg, parse_asm_args};
 
 use crate::rewrite::RewriteContext;
 
 #[allow(dead_code)]
-pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
+pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<Vec<AsmArg>> {
     let ts = mac.args.tokens.clone();
     let mut parser = super::build_parser(context, ts);
     parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok()
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 4195258af88..9bb06c31c5c 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -429,10 +429,13 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "winapi-util",
     "winapi-x86_64-pc-windows-gnu",
     "windows",
+    "windows-collections",
     "windows-core",
+    "windows-future",
     "windows-implement",
     "windows-interface",
     "windows-link",
+    "windows-numerics",
     "windows-result",
     "windows-strings",
     "windows-sys",
diff --git a/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff
new file mode 100644
index 00000000000..fcabcdbcfef
--- /dev/null
+++ b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff
@@ -0,0 +1,18 @@
+- // MIR for `overlapping` before GVN
++ // MIR for `overlapping` after GVN
+  
+  fn overlapping(_1: Adt) -> () {
+      let mut _0: ();
+      let mut _2: *mut Adt;
+      let mut _3: u32;
+      let mut _4: &Adt;
+  
+      bb0: {
+          _2 = &raw mut _1;
+          _4 = &(*_2);
+          _3 = copy (((*_4) as variant#1).0: u32);
+          (*_2) = Adt::Some(copy _3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs
new file mode 100644
index 00000000000..99113445e68
--- /dev/null
+++ b/tests/mir-opt/gvn_overlapping.rs
@@ -0,0 +1,36 @@
+//@ test-mir-pass: GVN
+
+#![feature(custom_mir, core_intrinsics)]
+
+// Check that we do not create overlapping assignments.
+
+use std::intrinsics::mir::*;
+
+// EMIT_MIR gvn_overlapping.overlapping.GVN.diff
+#[custom_mir(dialect = "runtime")]
+fn overlapping(_17: Adt) {
+    // CHECK-LABEL: fn overlapping(
+    // CHECK: let mut [[PTR:.*]]: *mut Adt;
+    // CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}});
+    mir! {
+        let _33: *mut Adt;
+        let _48: u32;
+        let _73: &Adt;
+        {
+            _33 = core::ptr::addr_of_mut!(_17);
+            _73 = &(*_33);
+            _48 = Field(Variant((*_73), 1), 0);
+            (*_33) = Adt::Some(_48);
+            Return()
+        }
+    }
+}
+
+fn main() {
+    overlapping(Adt::Some(0));
+}
+
+enum Adt {
+    None,
+    Some(u32),
+}
diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs
index a9e2b33e210..5194d773114 100644
--- a/tests/run-make/core-no-oom-handling/rmake.rs
+++ b/tests/run-make/core-no-oom-handling/rmake.rs
@@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root};
 
 fn main() {
     rustc()
-        .edition("2021")
+        .edition("2024")
         .arg("-Dwarnings")
         .crate_type("rlib")
         .input(source_root().join("library/core/src/lib.rs"))
diff --git a/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs b/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs
index 2727effe818..d28c8463016 100644
--- a/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs
+++ b/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs
@@ -11,6 +11,13 @@
 //@ needs-dynamic-linking
 //@ only-nightly (requires unstable rustc flag)
 
+// This test trips a check in the MSVC linker for an outdated processor:
+// "LNK1322: cannot avoid potential ARM hazard (Cortex-A53 MPCore processor bug #843419)"
+// Until MSVC removes this check:
+// https://developercommunity.microsoft.com/t/Remove-checking-for-and-fixing-Cortex-A/10905134
+// we'll need to disable this test on Arm64 Windows.
+//@ ignore-aarch64-pc-windows-msvc
+
 #![deny(warnings)]
 
 use run_make_support::{dynamic_lib_name, rfs, rust_lib_name, rustc};
diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
index 22b9b029a40..2c78b794a8d 100644
--- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
+++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
@@ -7,8 +7,6 @@
 //@ run-pass
 //@ needs-subprocess
 
-#![feature(avx512_target_feature)]
-
 #![allow(overflowing_literals)]
 #![allow(unused_variables)]
 
diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs
index 772512702ec..7432381d15b 100644
--- a/tests/ui/abi/simd-abi-checks-avx.rs
+++ b/tests/ui/abi/simd-abi-checks-avx.rs
@@ -2,7 +2,6 @@
 //@ build-fail
 //@ compile-flags: -C target-feature=-avx
 
-#![feature(avx512_target_feature)]
 #![feature(portable_simd)]
 #![feature(simd_ffi)]
 #![allow(improper_ctypes_definitions)]
diff --git a/tests/ui/abi/simd-abi-checks-avx.stderr b/tests/ui/abi/simd-abi-checks-avx.stderr
index 48db30bf453..7489ca01946 100644
--- a/tests/ui/abi/simd-abi-checks-avx.stderr
+++ b/tests/ui/abi/simd-abi-checks-avx.stderr
@@ -1,5 +1,5 @@
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:60:11
+  --> $DIR/simd-abi-checks-avx.rs:59:11
    |
 LL |         f(g());
    |           ^^^ function called here
@@ -7,7 +7,7 @@ LL |         f(g());
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:60:9
+  --> $DIR/simd-abi-checks-avx.rs:59:9
    |
 LL |         f(g());
    |         ^^^^^^ function called here
@@ -15,7 +15,7 @@ LL |         f(g());
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:66:14
+  --> $DIR/simd-abi-checks-avx.rs:65:14
    |
 LL |         gavx(favx());
    |              ^^^^^^ function called here
@@ -23,7 +23,7 @@ LL |         gavx(favx());
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:66:9
+  --> $DIR/simd-abi-checks-avx.rs:65:9
    |
 LL |         gavx(favx());
    |         ^^^^^^^^^^^^ function called here
@@ -31,7 +31,7 @@ LL |         gavx(favx());
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:76:19
+  --> $DIR/simd-abi-checks-avx.rs:75:19
    |
 LL |         w(Wrapper(g()));
    |                   ^^^ function called here
@@ -39,7 +39,7 @@ LL |         w(Wrapper(g()));
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:76:9
+  --> $DIR/simd-abi-checks-avx.rs:75:9
    |
 LL |         w(Wrapper(g()));
    |         ^^^^^^^^^^^^^^^ function called here
@@ -47,7 +47,7 @@ LL |         w(Wrapper(g()));
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:90:9
+  --> $DIR/simd-abi-checks-avx.rs:89:9
    |
 LL |         some_extern();
    |         ^^^^^^^^^^^^^ function called here
@@ -55,7 +55,7 @@ LL |         some_extern();
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled
-  --> $DIR/simd-abi-checks-avx.rs:25:1
+  --> $DIR/simd-abi-checks-avx.rs:24:1
    |
 LL | unsafe extern "C" fn g() -> __m256 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
@@ -63,7 +63,7 @@ LL | unsafe extern "C" fn g() -> __m256 {
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled
-  --> $DIR/simd-abi-checks-avx.rs:20:1
+  --> $DIR/simd-abi-checks-avx.rs:19:1
    |
 LL | unsafe extern "C" fn f(_: __m256) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
@@ -71,7 +71,7 @@ LL | unsafe extern "C" fn f(_: __m256) {
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled
-  --> $DIR/simd-abi-checks-avx.rs:15:1
+  --> $DIR/simd-abi-checks-avx.rs:14:1
    |
 LL | unsafe extern "C" fn w(_: Wrapper) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
@@ -79,7 +79,7 @@ LL | unsafe extern "C" fn w(_: Wrapper) {
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
-  --> $DIR/simd-abi-checks-avx.rs:54:8
+  --> $DIR/simd-abi-checks-avx.rs:53:8
    |
 LL |     || g()
    |        ^^^ function called here
@@ -87,7 +87,7 @@ LL |     || g()
    = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
 
 note: the above error was encountered while instantiating `fn in_closure::{closure#0}`
-  --> $DIR/simd-abi-checks-avx.rs:82:9
+  --> $DIR/simd-abi-checks-avx.rs:81:9
    |
 LL |         in_closure()();
    |         ^^^^^^^^^^^^^^
diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs
index aa731c35dda..35e1d037f38 100644
--- a/tests/ui/asm/aarch64/parse-error.rs
+++ b/tests/ui/asm/aarch64/parse-error.rs
@@ -96,10 +96,8 @@ global_asm!("", options(FOO));
 //~^ ERROR expected one of
 global_asm!("", options(nomem FOO));
 //~^ ERROR expected one of
-//~| ERROR the `nomem` option cannot be used with `global_asm!`
 global_asm!("", options(nomem, FOO));
 //~^ ERROR expected one of
-//~| ERROR the `nomem` option cannot be used with `global_asm!`
 global_asm!("{}", options(), const FOO);
 global_asm!("", clobber_abi(FOO));
 //~^ ERROR expected string literal
diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr
index b5e1169e5f6..45f9e7989c2 100644
--- a/tests/ui/asm/aarch64/parse-error.stderr
+++ b/tests/ui/asm/aarch64/parse-error.stderr
@@ -218,68 +218,56 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
 LL | global_asm!("", options(FOO));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
-error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:97:25
-   |
-LL | global_asm!("", options(nomem FOO));
-   |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
-
 error: expected one of `)` or `,`, found `FOO`
   --> $DIR/parse-error.rs:97:31
    |
 LL | global_asm!("", options(nomem FOO));
    |                               ^^^ expected one of `)` or `,`
 
-error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:100:25
-   |
-LL | global_asm!("", options(nomem, FOO));
-   |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
-
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:100:32
+  --> $DIR/parse-error.rs:99:32
    |
 LL | global_asm!("", options(nomem, FOO));
    |                                ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:104:29
+  --> $DIR/parse-error.rs:102:29
    |
 LL | global_asm!("", clobber_abi(FOO));
    |                             ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:106:33
+  --> $DIR/parse-error.rs:104:33
    |
 LL | global_asm!("", clobber_abi("C" FOO));
    |                                 ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:108:34
+  --> $DIR/parse-error.rs:106:34
    |
 LL | global_asm!("", clobber_abi("C", FOO));
    |                                  ^^^ not a string literal
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:110:19
+  --> $DIR/parse-error.rs:108:19
    |
 LL | global_asm!("{}", clobber_abi("C"), const FOO);
    |                   ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:112:28
+  --> $DIR/parse-error.rs:110:28
    |
 LL | global_asm!("", options(), clobber_abi("C"));
    |                            ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:114:30
+  --> $DIR/parse-error.rs:112:30
    |
 LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
    |                              ^^^^^^^^^^^^^^^^
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:116:35
+  --> $DIR/parse-error.rs:114:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -287,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:116:35
+  --> $DIR/parse-error.rs:114:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
@@ -295,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:119:28
+  --> $DIR/parse-error.rs:117:28
    |
 LL | global_asm!("", options(), "");
    |                            ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:121:30
+  --> $DIR/parse-error.rs:119:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
    |                              ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:123:13
+  --> $DIR/parse-error.rs:121:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -315,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:125:20
+  --> $DIR/parse-error.rs:123:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -418,6 +406,6 @@ LL -     let mut bar = 0;
 LL +     const bar: /* Type */ = 0;
    |
 
-error: aborting due to 59 previous errors
+error: aborting due to 57 previous errors
 
 For more information about this error, try `rustc --explain E0435`.
diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs
index 4d7b522f5fc..d135ccae128 100644
--- a/tests/ui/asm/parse-error.rs
+++ b/tests/ui/asm/parse-error.rs
@@ -113,11 +113,9 @@ global_asm!("", options(FOO));
 global_asm!("", options(FOO,));
 //~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
 global_asm!("", options(nomem FOO));
-//~^ ERROR the `nomem` option cannot be used with `global_asm!`
-//~| ERROR expected one of `)` or `,`, found `FOO`
+//~^ ERROR expected one of `)` or `,`, found `FOO`
 global_asm!("", options(nomem, FOO));
-//~^ ERROR the `nomem` option cannot be used with `global_asm!`
-//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
+//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
 global_asm!("{}", options(), const FOO);
 global_asm!("", clobber_abi(FOO));
 //~^ ERROR expected string literal
diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr
index 74647372a35..0bba1fd8d9b 100644
--- a/tests/ui/asm/parse-error.stderr
+++ b/tests/ui/asm/parse-error.stderr
@@ -270,74 +270,62 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
 LL | global_asm!("", options(FOO,));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
-error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:115:25
-   |
-LL | global_asm!("", options(nomem FOO));
-   |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
-
 error: expected one of `)` or `,`, found `FOO`
   --> $DIR/parse-error.rs:115:31
    |
 LL | global_asm!("", options(nomem FOO));
    |                               ^^^ expected one of `)` or `,`
 
-error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:118:25
-   |
-LL | global_asm!("", options(nomem, FOO));
-   |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
-
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:118:32
+  --> $DIR/parse-error.rs:117:32
    |
 LL | global_asm!("", options(nomem, FOO));
    |                                ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:122:29
+  --> $DIR/parse-error.rs:120:29
    |
 LL | global_asm!("", clobber_abi(FOO));
    |                             ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:124:33
+  --> $DIR/parse-error.rs:122:33
    |
 LL | global_asm!("", clobber_abi("C" FOO));
    |                                 ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:126:34
+  --> $DIR/parse-error.rs:124:34
    |
 LL | global_asm!("", clobber_abi("C", FOO));
    |                                  ^^^ not a string literal
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:128:19
+  --> $DIR/parse-error.rs:126:19
    |
 LL | global_asm!("{}", clobber_abi("C"), const FOO);
    |                   ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:130:28
+  --> $DIR/parse-error.rs:128:28
    |
 LL | global_asm!("", options(), clobber_abi("C"));
    |                            ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:132:30
+  --> $DIR/parse-error.rs:130:30
    |
 LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
    |                              ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:134:17
+  --> $DIR/parse-error.rs:132:17
    |
 LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
    |                 ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:136:35
+  --> $DIR/parse-error.rs:134:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -345,7 +333,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:136:35
+  --> $DIR/parse-error.rs:134:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
@@ -353,19 +341,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:139:28
+  --> $DIR/parse-error.rs:137:28
    |
 LL | global_asm!("", options(), "");
    |                            ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:141:30
+  --> $DIR/parse-error.rs:139:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
    |                              ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:143:13
+  --> $DIR/parse-error.rs:141:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -373,7 +361,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:145:20
+  --> $DIR/parse-error.rs:143:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -381,37 +369,37 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: the `in` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:148:19
+  --> $DIR/parse-error.rs:146:19
    |
 LL | global_asm!("{}", in(reg));
    |                   ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `out` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:150:19
+  --> $DIR/parse-error.rs:148:19
    |
 LL | global_asm!("{}", out(reg));
    |                   ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `lateout` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:152:19
+  --> $DIR/parse-error.rs:150:19
    |
 LL | global_asm!("{}", lateout(reg));
    |                   ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `inout` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:154:19
+  --> $DIR/parse-error.rs:152:19
    |
 LL | global_asm!("{}", inout(reg));
    |                   ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `inlateout` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:156:19
+  --> $DIR/parse-error.rs:154:19
    |
 LL | global_asm!("{}", inlateout(reg));
    |                   ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `label` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:158:19
+  --> $DIR/parse-error.rs:156:19
    |
 LL | global_asm!("{}", label(reg));
    |                   ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it
@@ -476,6 +464,6 @@ LL -     let mut bar = 0;
 LL +     const bar: /* Type */ = 0;
    |
 
-error: aborting due to 72 previous errors
+error: aborting due to 70 previous errors
 
 For more information about this error, try `rustc --explain E0435`.
diff --git a/tests/ui/asm/x86_64/evex512-implicit-feature.rs b/tests/ui/asm/x86_64/evex512-implicit-feature.rs
index ea2acd424e2..ec5da7c7fa4 100644
--- a/tests/ui/asm/x86_64/evex512-implicit-feature.rs
+++ b/tests/ui/asm/x86_64/evex512-implicit-feature.rs
@@ -2,7 +2,6 @@
 //@ only-x86_64
 //@ compile-flags: --crate-type=lib -C target-cpu=skylake
 
-#![feature(avx512_target_feature)]
 #![feature(stdarch_x86_avx512)]
 
 use std::arch::x86_64::*;
diff --git a/tests/ui/asm/x86_64/target-feature-attr.rs b/tests/ui/asm/x86_64/target-feature-attr.rs
index 6bb277ac165..2193117caeb 100644
--- a/tests/ui/asm/x86_64/target-feature-attr.rs
+++ b/tests/ui/asm/x86_64/target-feature-attr.rs
@@ -2,8 +2,6 @@
 // Set the base cpu explicitly, in case the default has been changed.
 //@ compile-flags: -C target-cpu=x86-64
 
-#![feature(avx512_target_feature)]
-
 use std::arch::asm;
 
 #[target_feature(enable = "avx")]
diff --git a/tests/ui/asm/x86_64/target-feature-attr.stderr b/tests/ui/asm/x86_64/target-feature-attr.stderr
index 0cd571ac8cc..c852726ee7f 100644
--- a/tests/ui/asm/x86_64/target-feature-attr.stderr
+++ b/tests/ui/asm/x86_64/target-feature-attr.stderr
@@ -1,23 +1,23 @@
 error: register class `ymm_reg` requires the `avx` target feature
-  --> $DIR/target-feature-attr.rs:20:40
+  --> $DIR/target-feature-attr.rs:18:40
    |
 LL |     asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x);
    |                                        ^^^^^^^^^^^^^
 
 error: register class `ymm_reg` requires the `avx` target feature
-  --> $DIR/target-feature-attr.rs:20:55
+  --> $DIR/target-feature-attr.rs:18:55
    |
 LL |     asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x);
    |                                                       ^^^^^^^^^^^^^
 
 error: register class `ymm_reg` requires the `avx` target feature
-  --> $DIR/target-feature-attr.rs:20:70
+  --> $DIR/target-feature-attr.rs:18:70
    |
 LL |     asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x);
    |                                                                      ^^^^^^^^^^^^^^^^^^
 
 error: register class `kreg` requires at least one of the following target features: avx512bw, avx512f
-  --> $DIR/target-feature-attr.rs:35:23
+  --> $DIR/target-feature-attr.rs:33:23
    |
 LL |     asm!("/* {0} */", in(kreg) x);
    |                       ^^^^^^^^^^
diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs
index 74fb5817081..095f66eeb90 100644
--- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs
+++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs
@@ -22,7 +22,6 @@ fn other_guards_dont() {
 
     let ((x if guard(x)) | x) = 0;
     //~^ ERROR: guard patterns are experimental
-    //~| ERROR: cannot find value `x`
 
     if let (x if guard(x)) = 0 {}
     //~^ ERROR: guard patterns are experimental
@@ -37,7 +36,6 @@ fn other_guards_dont() {
 
 fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
 //~^ ERROR: guard patterns are experimental
-//~| ERROR: cannot find value `x`
 
 fn guard<T>(x: T) -> bool {
     unimplemented!()
diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
index 8b85b663889..b0bf302f3cb 100644
--- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
@@ -10,24 +10,6 @@ LL -         (0 if guard(0)) => {},
 LL +         0 if guard(0) => {},
    |
 
-error[E0425]: cannot find value `x` in this scope
-  --> $DIR/feature-gate-guard-patterns.rs:23:22
-   |
-LL |     let ((x if guard(x)) | x) = 0;
-   |                      ^ not found in this scope
-
-error[E0425]: cannot find value `x` in this scope
-  --> $DIR/feature-gate-guard-patterns.rs:38:45
-   |
-LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
-   |                                             ^
-   |
-help: the binding `x` is available in a different scope in the same function
-  --> $DIR/feature-gate-guard-patterns.rs:23:11
-   |
-LL |     let ((x if guard(x)) | x) = 0;
-   |           ^
-
 error[E0658]: guard patterns are experimental
   --> $DIR/feature-gate-guard-patterns.rs:18:15
    |
@@ -51,7 +33,7 @@ LL |     let ((x if guard(x)) | x) = 0;
    = help: consider using match arm guards
 
 error[E0658]: guard patterns are experimental
-  --> $DIR/feature-gate-guard-patterns.rs:27:18
+  --> $DIR/feature-gate-guard-patterns.rs:26:18
    |
 LL |     if let (x if guard(x)) = 0 {}
    |                  ^^^^^^^^
@@ -62,7 +44,7 @@ LL |     if let (x if guard(x)) = 0 {}
    = help: consider using match arm guards
 
 error[E0658]: guard patterns are experimental
-  --> $DIR/feature-gate-guard-patterns.rs:30:21
+  --> $DIR/feature-gate-guard-patterns.rs:29:21
    |
 LL |     while let (x if guard(x)) = 0 {}
    |                     ^^^^^^^^
@@ -73,7 +55,7 @@ LL |     while let (x if guard(x)) = 0 {}
    = help: consider using match arm guards
 
 error[E0658]: guard patterns are experimental
-  --> $DIR/feature-gate-guard-patterns.rs:34:21
+  --> $DIR/feature-gate-guard-patterns.rs:33:21
    |
 LL |     while let (x if guard(x)) = 0 {}
    |                     ^^^^^^^^
@@ -84,7 +66,7 @@ LL |     while let (x if guard(x)) = 0 {}
    = help: consider using match arm guards
 
 error[E0658]: guard patterns are experimental
-  --> $DIR/feature-gate-guard-patterns.rs:38:39
+  --> $DIR/feature-gate-guard-patterns.rs:37:39
    |
 LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
    |                                       ^^^^^^^^
@@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
    = help: consider using match arm guards
 
-error: aborting due to 9 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed
index 0108f762400..ae63b0c4627 100644
--- a/tests/ui/imports/issue-99695-b.fixed
+++ b/tests/ui/imports/issue-99695-b.fixed
@@ -11,7 +11,7 @@ mod m {
         pub struct other_item;
     }
 
-    use ::nu;
+    use crate::nu;
 pub use self::p::{other_item as _};
     //~^ ERROR unresolved import `self::p::nu` [E0432]
     //~| HELP a macro with this name exists at the root of the crate
diff --git a/tests/ui/imports/issue-99695-b.stderr b/tests/ui/imports/issue-99695-b.stderr
index d58d2798746..ad752d5c45a 100644
--- a/tests/ui/imports/issue-99695-b.stderr
+++ b/tests/ui/imports/issue-99695-b.stderr
@@ -7,7 +7,7 @@ LL |     pub use self::p::{nu, other_item as _};
    = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
 help: a macro with this name exists at the root of the crate
    |
-LL ~     use ::nu;
+LL ~     use crate::nu;
 LL ~ pub use self::p::{other_item as _};
    |
 
diff --git a/tests/ui/imports/issue-99695.fixed b/tests/ui/imports/issue-99695.edition_2015.fixed
index 51ccd3f8c48..798acfd5874 100644
--- a/tests/ui/imports/issue-99695.fixed
+++ b/tests/ui/imports/issue-99695.edition_2015.fixed
@@ -1,4 +1,8 @@
 //@ run-rustfix
+//@ revisions: edition_2015 edition_2018
+//@ [edition_2015] edition: 2015
+//@ [edition_2018] edition: 2018
+
 #![allow(unused, nonstandard_style)]
 mod m {
     #[macro_export]
@@ -8,7 +12,7 @@ mod m {
 
     pub struct other_item;
 
-    use ::nu;
+    use crate::nu;
 pub use self::{other_item as _};
     //~^ ERROR unresolved import `self::nu` [E0432]
     //~| HELP a macro with this name exists at the root of the crate
diff --git a/tests/ui/imports/issue-99695.stderr b/tests/ui/imports/issue-99695.edition_2015.stderr
index 536f51dcb3b..4ef8e6426fb 100644
--- a/tests/ui/imports/issue-99695.stderr
+++ b/tests/ui/imports/issue-99695.edition_2015.stderr
@@ -1,5 +1,5 @@
 error[E0432]: unresolved import `self::nu`
-  --> $DIR/issue-99695.rs:11:20
+  --> $DIR/issue-99695.rs:15:20
    |
 LL |     pub use self::{nu, other_item as _};
    |                    ^^ no `nu` in `m`
@@ -7,7 +7,7 @@ LL |     pub use self::{nu, other_item as _};
    = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
 help: a macro with this name exists at the root of the crate
    |
-LL ~     use ::nu;
+LL ~     use crate::nu;
 LL ~ pub use self::{other_item as _};
    |
 
diff --git a/tests/ui/imports/issue-99695.edition_2018.fixed b/tests/ui/imports/issue-99695.edition_2018.fixed
new file mode 100644
index 00000000000..798acfd5874
--- /dev/null
+++ b/tests/ui/imports/issue-99695.edition_2018.fixed
@@ -0,0 +1,21 @@
+//@ run-rustfix
+//@ revisions: edition_2015 edition_2018
+//@ [edition_2015] edition: 2015
+//@ [edition_2018] edition: 2018
+
+#![allow(unused, nonstandard_style)]
+mod m {
+    #[macro_export]
+    macro_rules! nu {
+        {} => {};
+    }
+
+    pub struct other_item;
+
+    use crate::nu;
+pub use self::{other_item as _};
+    //~^ ERROR unresolved import `self::nu` [E0432]
+    //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
diff --git a/tests/ui/imports/issue-99695.edition_2018.stderr b/tests/ui/imports/issue-99695.edition_2018.stderr
new file mode 100644
index 00000000000..4ef8e6426fb
--- /dev/null
+++ b/tests/ui/imports/issue-99695.edition_2018.stderr
@@ -0,0 +1,16 @@
+error[E0432]: unresolved import `self::nu`
+  --> $DIR/issue-99695.rs:15:20
+   |
+LL |     pub use self::{nu, other_item as _};
+   |                    ^^ no `nu` in `m`
+   |
+   = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
+help: a macro with this name exists at the root of the crate
+   |
+LL ~     use crate::nu;
+LL ~ pub use self::{other_item as _};
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/imports/issue-99695.rs b/tests/ui/imports/issue-99695.rs
index a52370e0eb0..dc0a8f438e0 100644
--- a/tests/ui/imports/issue-99695.rs
+++ b/tests/ui/imports/issue-99695.rs
@@ -1,4 +1,8 @@
 //@ run-rustfix
+//@ revisions: edition_2015 edition_2018
+//@ [edition_2015] edition: 2015
+//@ [edition_2018] edition: 2018
+
 #![allow(unused, nonstandard_style)]
 mod m {
     #[macro_export]
diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs
new file mode 100644
index 00000000000..83ad8c76bb1
--- /dev/null
+++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs
@@ -0,0 +1,81 @@
+//! Test that guard patterns can see bindings already in scope and bindings introduced in their
+//! subpattern, but no other bindings from the containing pattern. Also make sure bindings
+//! introduced in guard patterns are visible in fn/arm/loop/etc bodies.
+
+#![feature(guard_patterns)]
+#![expect(incomplete_features)]
+
+fn good_fn_item(((x if x) | x): bool) -> bool { x }
+
+fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {}
+//~^ ERROR cannot find value `x` in this scope
+fn bad_fn_item_2(((x if y) | x): bool, y: bool) {}
+//~^ ERROR cannot find value `y` in this scope
+
+fn main() {
+    let ((local if local) if local) = false;
+
+    match (true, true) {
+        (x if local, y if good_fn_item(y)) => x && y,
+        (x, y if x) => x && y,
+        //~^ ERROR cannot find value `x` in this scope
+        (x if y, y) => x && y,
+        //~^ ERROR cannot find value `y` in this scope
+    };
+
+    match (true,) {
+        (x @ y if x && y,) => x && y,
+        (x @ (y if y),) => x && y,
+        (x @ (y if x),) => x && y,
+        //~^ ERROR cannot find value `x` in this scope
+    };
+
+    match (Ok(true),) {
+        ((Ok(x) | Err(x)) if good_fn_item(x),) => x,
+        ((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x,
+        ((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x,
+        ((Ok(x) if y) | (Err(y) if x),) => x && y,
+        //~^ ERROR variable `x` is not bound in all patterns
+        //~| ERROR variable `y` is not bound in all patterns
+        //~| ERROR cannot find value `x` in this scope
+        //~| ERROR cannot find value `y` in this scope
+    };
+
+    let (_ if nonexistent) = true;
+    //~^ ERROR cannot find value `nonexistent` in this scope
+    if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+    //~^ ERROR cannot find value `x` in this scope
+    //~| ERROR cannot find value `y` in this scope
+    while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+    //~^ ERROR cannot find value `x` in this scope
+    //~| ERROR cannot find value `y` in this scope
+    for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
+    //~^ ERROR cannot find value `x` in this scope
+    //~| ERROR cannot find value `y` in this scope
+
+    (|(x if x), (y if y)| x && y)(true, true);
+    (|(x if y), (y if x)| x && y)(true, true);
+    //~^ ERROR cannot find value `x` in this scope
+    //~| ERROR cannot find value `y` in this scope
+
+    // FIXME(guard_patterns): mismatched bindings are not yet allowed
+    match Some(0) {
+        Some(x if x > 0) | None => {}
+        //~^ ERROR variable `x` is not bound in all patterns
+    }
+}
+
+/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier,
+/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in
+/// its subpattern.
+fn test_shadowing(local: bool) -> u8 {
+    match (0, 0) {
+        // The `local` binding here shadows the `bool` definition, so we get a type error.
+        //~v ERROR mismatched types
+        local if local => 0,
+        // The guards here should see the `bool` definition of `local`, not the new `u8` binding.
+        // The body should see the new binding.
+        (local, _ if local) => local,
+        (_ if local, local) => local,
+    }
+}
diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr
new file mode 100644
index 00000000000..d76e60478a1
--- /dev/null
+++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr
@@ -0,0 +1,133 @@
+error[E0408]: variable `y` is not bound in all patterns
+  --> $DIR/name-resolution.rs:37:10
+   |
+LL |         ((Ok(x) if y) | (Err(y) if x),) => x && y,
+   |          ^^^^^^^^^^^^        - variable not in all patterns
+   |          |
+   |          pattern doesn't bind `y`
+
+error[E0408]: variable `x` is not bound in all patterns
+  --> $DIR/name-resolution.rs:37:25
+   |
+LL |         ((Ok(x) if y) | (Err(y) if x),) => x && y,
+   |              -          ^^^^^^^^^^^^^ pattern doesn't bind `x`
+   |              |
+   |              variable not in all patterns
+
+error[E0408]: variable `x` is not bound in all patterns
+  --> $DIR/name-resolution.rs:63:28
+   |
+LL |         Some(x if x > 0) | None => {}
+   |              -             ^^^^ pattern doesn't bind `x`
+   |              |
+   |              variable not in all patterns
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:10:34
+   |
+LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {}
+   |                                  ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:12:25
+   |
+LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {}
+   |                         ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:20:18
+   |
+LL |         (x, y if x) => x && y,
+   |                  ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:22:15
+   |
+LL |         (x if y, y) => x && y,
+   |               ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:29:20
+   |
+LL |         (x @ (y if x),) => x && y,
+   |                    ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:37:20
+   |
+LL |         ((Ok(x) if y) | (Err(y) if x),) => x && y,
+   |                    ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:37:36
+   |
+LL |         ((Ok(x) if y) | (Err(y) if x),) => x && y,
+   |                                    ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `nonexistent` in this scope
+  --> $DIR/name-resolution.rs:44:15
+   |
+LL |     let (_ if nonexistent) = true;
+   |               ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:46:22
+   |
+LL |     if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+   |                      ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:46:33
+   |
+LL |     if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+   |                                 ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:49:25
+   |
+LL |     while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+   |                         ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:49:36
+   |
+LL |     while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+   |                                    ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:52:19
+   |
+LL |     for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
+   |                   ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:52:30
+   |
+LL |     for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
+   |                              ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `y` in this scope
+  --> $DIR/name-resolution.rs:57:13
+   |
+LL |     (|(x if y), (y if x)| x && y)(true, true);
+   |             ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/name-resolution.rs:57:23
+   |
+LL |     (|(x if y), (y if x)| x && y)(true, true);
+   |                       ^ help: a local variable with a similar name exists: `y`
+
+error[E0308]: mismatched types
+  --> $DIR/name-resolution.rs:75:18
+   |
+LL |         local if local => 0,
+   |                  ^^^^^ expected `bool`, found `({integer}, {integer})`
+   |
+   = note: expected type `bool`
+             found tuple `({integer}, {integer})`
+
+error: aborting due to 20 previous errors
+
+Some errors have detailed explanations: E0308, E0408, E0425.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs
new file mode 100644
index 00000000000..7bb39ca7bb9
--- /dev/null
+++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs
@@ -0,0 +1,15 @@
+//@ check-pass
+//! Test that `GatherLocalsVisitor` only visits expressions in guard patterns when checking the
+//! expressions, and not a second time when visiting the pattern. If locals are declared inside the
+//! the guard expression, it would ICE if visited twice ("evaluated expression more than once").
+
+#![feature(guard_patterns)]
+#![expect(incomplete_features)]
+
+fn main() {
+    match (0,) {
+        // FIXME(guard_patterns): liveness lints don't work yet; this will ICE without the `_`.
+        (_ if { let _x = false; _x },) => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs
index 2786251c795..77f18615248 100644
--- a/tests/ui/simd/target-feature-mixup.rs
+++ b/tests/ui/simd/target-feature-mixup.rs
@@ -7,7 +7,6 @@
 //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590)
 
 #![feature(repr_simd, target_feature, cfg_target_feature)]
-#![feature(avx512_target_feature)]
 
 use std::process::{Command, ExitStatus};
 use std::env;
diff --git a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs
index 2682028936c..15bcfdd9076 100644
--- a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs
+++ b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs
@@ -1,5 +1,5 @@
-#![feature(avx512_target_feature)]
+#![feature(x87_target_feature)]
 
 #[inline]
-#[target_feature(enable = "avx512ifma")]
+#[target_feature(enable = "x87")]
 pub unsafe fn foo() {}
diff --git a/tests/ui/target-feature/gate.rs b/tests/ui/target-feature/gate.rs
index 14fdad02f56..9244a98d82f 100644
--- a/tests/ui/target-feature/gate.rs
+++ b/tests/ui/target-feature/gate.rs
@@ -2,7 +2,6 @@
 //
 // gate-test-sse4a_target_feature
 // gate-test-powerpc_target_feature
-// gate-test-avx512_target_feature
 // gate-test-tbm_target_feature
 // gate-test-arm_target_feature
 // gate-test-hexagon_target_feature
@@ -27,7 +26,7 @@
 // gate-test-x87_target_feature
 // gate-test-m68k_target_feature
 
-#[target_feature(enable = "avx512bw")]
+#[target_feature(enable = "x87")]
 //~^ ERROR: currently unstable
 unsafe fn foo() {}
 
diff --git a/tests/ui/target-feature/gate.stderr b/tests/ui/target-feature/gate.stderr
index fa876893848..32d60ce4382 100644
--- a/tests/ui/target-feature/gate.stderr
+++ b/tests/ui/target-feature/gate.stderr
@@ -1,11 +1,11 @@
-error[E0658]: the target feature `avx512bw` is currently unstable
-  --> $DIR/gate.rs:30:18
+error[E0658]: the target feature `x87` is currently unstable
+  --> $DIR/gate.rs:29:18
    |
-LL | #[target_feature(enable = "avx512bw")]
-   |                  ^^^^^^^^^^^^^^^^^^^
+LL | #[target_feature(enable = "x87")]
+   |                  ^^^^^^^^^^^^^^
    |
    = note: see issue #44839 <https://github.com/rust-lang/rust/issues/44839> for more information
-   = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable
+   = help: add `#![feature(x87_target_feature)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/target-feature/unstable-feature.rs b/tests/ui/target-feature/unstable-feature.rs
index f62c4dd938a..a79ad469603 100644
--- a/tests/ui/target-feature/unstable-feature.rs
+++ b/tests/ui/target-feature/unstable-feature.rs
@@ -1,8 +1,8 @@
-//@ compile-flags: -Ctarget-feature=+vaes --crate-type=rlib --target=x86_64-unknown-linux-gnu
+//@ compile-flags: -Ctarget-feature=+x87 --crate-type=rlib --target=x86_64-unknown-linux-gnu
 //@ build-pass
 //@ needs-llvm-components: x86
 
 #![feature(no_core)]
 #![no_core]
 
-//~? WARN unstable feature specified for `-Ctarget-feature`: `vaes`
+//~? WARN unstable feature specified for `-Ctarget-feature`: `x87`
diff --git a/tests/ui/target-feature/unstable-feature.stderr b/tests/ui/target-feature/unstable-feature.stderr
index d34544c5c27..309b64afd92 100644
--- a/tests/ui/target-feature/unstable-feature.stderr
+++ b/tests/ui/target-feature/unstable-feature.stderr
@@ -1,4 +1,4 @@
-warning: unstable feature specified for `-Ctarget-feature`: `vaes`
+warning: unstable feature specified for `-Ctarget-feature`: `x87`
    |
    = note: this feature is not stably supported; its behavior can change in the future
 
diff --git a/triagebot.toml b/triagebot.toml
index 9dcdbcecbec..3afa2d36410 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -133,7 +133,7 @@ In case it's useful, here are some [instructions] for tackling these sorts of is
 
 [instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/rust-for-linux.html
 """
-label = "O-rfl"
+label = "A-rust-for-linux"
 
 [ping.wasm]
 alias = ["webassembly"]
@@ -1424,3 +1424,6 @@ compiletest = [
 # Enable `@rustbot note` functionality
 # Documentation at: https://forge.rust-lang.org/triagebot/note.html
 [note]
+
+[behind-upstream]
+days-threshold = 14