Auto merge of #149784 - fereidani:retain_mut, r=joboet

Improve alloc `Vec::retain_mut` performance

Hi,

While reading the rustc source code, I noticed it uses `smallvec` and `thin-vec` in many places. I started reviewing those crates, optimized their `retain_mut` implementation, and then realized they were using the exact same algorithm as `alloc::vec::Vec` with less unsafe  So now I’m back here with a PR for the standard library 😂.

In my benchmarks, this version is noticeably faster when `retain_mut` actually removes elements (thanks to fewer pointer operations, it just advances `write_index`), while performing identically to the current implementation when nothing is removed.

Let’s see if bors likes this change or not.
diff --git a/Cargo.lock b/Cargo.lock
index d3637d1..816bb1a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -445,22 +445,21 @@
 
 [[package]]
 name = "capstone"
-version = "0.13.0"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a"
+checksum = "f442ae0f2f3f1b923334b4a5386c95c69c1cfa072bafa23d6fae6d9682eb1dd4"
 dependencies = [
  "capstone-sys",
- "libc",
+ "static_assertions",
 ]
 
 [[package]]
 name = "capstone-sys"
-version = "0.17.0"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0"
+checksum = "a4e8087cab6731295f5a2a2bd82989ba4f41d3a428aab2e7c98d8f4db38aac05"
 dependencies = [
  "cc",
- "libc",
 ]
 
 [[package]]
@@ -2232,9 +2231,9 @@
 
 [[package]]
 name = "libffi"
-version = "5.0.0"
+version = "5.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0444124f3ffd67e1b0b0c661a7f81a278a135eb54aaad4078e79fbc8be50c8a5"
+checksum = "0498fe5655f857803e156523e644dcdcdc3b3c7edda42ea2afdae2e09b2db87b"
 dependencies = [
  "libc",
  "libffi-sys",
@@ -2242,9 +2241,9 @@
 
 [[package]]
 name = "libffi-sys"
-version = "4.0.0"
+version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d722da8817ea580d0669da6babe2262d7b86a1af1103da24102b8bb9c101ce7"
+checksum = "71d4f1d4ce15091955144350b75db16a96d4a63728500122706fb4d29a26afbb"
 dependencies = [
  "cc",
 ]
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 416fef8..9a45915 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2396,6 +2396,35 @@ fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir>
         };
 
         match &expr.kind {
+            ExprKind::Call(func, args) if let ExprKind::Path(qself, path) = &func.kind => {
+                let qpath = self.lower_qpath(
+                    func.id,
+                    qself,
+                    path,
+                    ParamMode::Explicit,
+                    AllowReturnTypeNotation::No,
+                    ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+                    None,
+                );
+
+                let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| {
+                    let const_arg = if let ExprKind::ConstBlock(anon_const) = &arg.kind {
+                        let def_id = self.local_def_id(anon_const.id);
+                        let def_kind = self.tcx.def_kind(def_id);
+                        assert_eq!(DefKind::AnonConst, def_kind);
+                        self.lower_anon_const_to_const_arg_direct(anon_const)
+                    } else {
+                        self.lower_expr_to_const_arg_direct(arg)
+                    };
+
+                    &*self.arena.alloc(const_arg)
+                }));
+
+                ConstArg {
+                    hir_id: self.next_id(),
+                    kind: hir::ConstArgKind::TupleCall(qpath, lowered_args),
+                }
+            }
             ExprKind::Path(qself, path) => {
                 let qpath = self.lower_qpath(
                     expr.id,
@@ -2460,7 +2489,10 @@ fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir>
                     && let StmtKind::Expr(expr) = &stmt.kind
                     && matches!(
                         expr.kind,
-                        ExprKind::Block(..) | ExprKind::Path(..) | ExprKind::Struct(..)
+                        ExprKind::Block(..)
+                            | ExprKind::Path(..)
+                            | ExprKind::Struct(..)
+                            | ExprKind::Call(..)
                     )
                 {
                     return self.lower_expr_to_const_arg_direct(expr);
diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs
index 388118f..d00e706 100644
--- a/compiler/rustc_codegen_llvm/src/base.rs
+++ b/compiler/rustc_codegen_llvm/src/base.rs
@@ -93,8 +93,13 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<ModuleLlvm
             // They are necessary for correct offload execution. We do this here to simplify the
             // `offload` intrinsic, avoiding the need for tracking whether it's the first
             // intrinsic call or not.
-            let has_host_offload =
-                cx.sess().opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Host(_)));
+            let has_host_offload = cx
+                .sess()
+                .opts
+                .unstable_opts
+                .offload
+                .iter()
+                .any(|o| matches!(o, Offload::Host(_) | Offload::Test));
             if has_host_offload && !cx.sess().target.is_like_gpu {
                 cx.offload_globals.replace(Some(OffloadGlobals::declare(&cx)));
             }
diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs
index fba92f9..b8eb4f0 100644
--- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs
+++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs
@@ -49,8 +49,9 @@ pub(crate) fn declare(cx: &CodegenCx<'ll, '_>) -> Self {
         let bin_desc = cx.type_named_struct("struct.__tgt_bin_desc");
         cx.set_struct_body(bin_desc, &tgt_bin_desc_ty, false);
 
-        let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", mapper_fn_ty);
-        let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", mapper_fn_ty);
+        let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
+        let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", reg_lib_decl);
+        let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
         let init_ty = cx.type_func(&[], cx.type_void());
         let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
 
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index ca2ce2f..f64702f 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -239,22 +239,16 @@ pub fn internal(&self, feature: Symbol) -> bool {
     (internal, negative_bounds, "1.71.0", None),
     /// Set the maximum pattern complexity allowed (not limited by default).
     (internal, pattern_complexity_limit, "1.78.0", None),
-    /// Allows using pattern types.
-    (internal, pattern_types, "1.79.0", Some(123646)),
     /// Allows using `#[prelude_import]` on glob `use` items.
     (internal, prelude_import, "1.2.0", None),
     /// Used to identify crates that contain the profiler runtime.
     (internal, profiler_runtime, "1.18.0", None),
     /// Allows using `rustc_*` attributes (RFC 572).
     (internal, rustc_attrs, "1.0.0", None),
-    /// Introduces a hierarchy of `Sized` traits (RFC 3729).
-    (unstable, sized_hierarchy, "1.89.0", None),
     /// Allows using the `#[stable]` and `#[unstable]` attributes.
     (internal, staged_api, "1.0.0", None),
     /// Added for testing unstable lints; perma-unstable.
     (internal, test_unstable_lint, "1.60.0", None),
-    /// Helps with formatting for `group_imports = "StdExternalCrate"`.
-    (unstable, unqualified_local_imports, "1.83.0", Some(138299)),
     /// Use for stable + negative coherence and strict coherence depending on trait's
     /// rustc_strict_coherence value.
     (unstable, with_negative_coherence, "1.60.0", None),
@@ -295,6 +289,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
     (internal, needs_panic_runtime, "1.10.0", Some(32837)),
     /// Allows using the `#![panic_runtime]` attribute.
     (internal, panic_runtime, "1.10.0", Some(32837)),
+    /// Allows using pattern types.
+    (internal, pattern_types, "1.79.0", Some(123646)),
     /// Allows using `#[rustc_allow_const_fn_unstable]`.
     /// This is an attribute on `const fn` for the same
     /// purpose as `#[allow_internal_unstable]`.
@@ -305,12 +301,16 @@ pub fn internal(&self, feature: Symbol) -> bool {
     (internal, rustdoc_internals, "1.58.0", Some(90418)),
     /// Allows using the `rustdoc::missing_doc_code_examples` lint
     (unstable, rustdoc_missing_doc_code_examples, "1.31.0", Some(101730)),
+    /// Introduces a hierarchy of `Sized` traits (RFC 3729).
+    (unstable, sized_hierarchy, "1.89.0", Some(144404)),
     /// Allows using `#[structural_match]` which indicates that a type is structurally matchable.
     /// FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library
     /// feature with the same name exists.
     (unstable, structural_match, "1.8.0", Some(31434)),
     /// Allows using the `rust-call` ABI.
     (unstable, unboxed_closures, "1.0.0", Some(29625)),
+    /// Helps with formatting for `group_imports = "StdExternalCrate"`.
+    (unstable, unqualified_local_imports, "1.83.0", Some(138299)),
     // !!!!    !!!!    !!!!    !!!!   !!!!    !!!!    !!!!    !!!!    !!!!    !!!!    !!!!
     // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
     // !!!!    !!!!    !!!!    !!!!   !!!!    !!!!    !!!!    !!!!    !!!!    !!!!    !!!!
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index da3e79e..da11d1b 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -440,7 +440,7 @@ pub fn span<'tcx>(&self, tcx: impl crate::intravisit::HirTyCtxt<'tcx>) -> Span {
 /// versus const args that are literals or have arbitrary computations (e.g., `{ 1 + 3 }`).
 ///
 /// For an explanation of the `Unambig` generic parameter see the dev-guide:
-/// <https://rustc-dev-guide.rust-lang.org/hir/ambig-unambig-ty-and-consts.html>
+/// <https://rustc-dev-guide.rust-lang.org/ambig-unambig-ty-and-consts.html>
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
 #[repr(C)]
 pub struct ConstArg<'hir, Unambig = ()> {
@@ -499,6 +499,7 @@ pub fn span(&self) -> Span {
         match self.kind {
             ConstArgKind::Struct(path, _) => path.span(),
             ConstArgKind::Path(path) => path.span(),
+            ConstArgKind::TupleCall(path, _) => path.span(),
             ConstArgKind::Anon(anon) => anon.span,
             ConstArgKind::Error(span, _) => span,
             ConstArgKind::Infer(span, _) => span,
@@ -519,6 +520,8 @@ pub enum ConstArgKind<'hir, Unambig = ()> {
     Anon(&'hir AnonConst),
     /// Represents construction of struct/struct variants
     Struct(QPath<'hir>, &'hir [&'hir ConstArgExprField<'hir>]),
+    /// Tuple constructor variant
+    TupleCall(QPath<'hir>, &'hir [&'hir ConstArg<'hir>]),
     /// Error const
     Error(Span, ErrorGuaranteed),
     /// This variant is not always used to represent inference consts, sometimes
@@ -3374,7 +3377,7 @@ pub enum AmbigArg {}
 /// Represents a type in the `HIR`.
 ///
 /// For an explanation of the `Unambig` generic parameter see the dev-guide:
-/// <https://rustc-dev-guide.rust-lang.org/hir/ambig-unambig-ty-and-consts.html>
+/// <https://rustc-dev-guide.rust-lang.org/ambig-unambig-ty-and-consts.html>
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 #[repr(C)]
 pub struct Ty<'hir, Unambig = ()> {
@@ -3713,7 +3716,7 @@ pub enum InferDelegationKind {
 /// The various kinds of types recognized by the compiler.
 ///
 /// For an explanation of the `Unambig` generic parameter see the dev-guide:
-/// <https://rustc-dev-guide.rust-lang.org/hir/ambig-unambig-ty-and-consts.html>
+/// <https://rustc-dev-guide.rust-lang.org/ambig-unambig-ty-and-consts.html>
 // SAFETY: `repr(u8)` is required so that `TyKind<()>` and `TyKind<!>` are layout compatible
 #[repr(u8, C)]
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index e636b3f..853e7db 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -1090,6 +1090,13 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>(
 
             V::Result::output()
         }
+        ConstArgKind::TupleCall(qpath, args) => {
+            try_visit!(visitor.visit_qpath(qpath, *hir_id, qpath.span()));
+            for arg in *args {
+                try_visit!(visitor.visit_const_arg_unambig(*arg));
+            }
+            V::Result::output()
+        }
         ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, qpath.span()),
         ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
         ConstArgKind::Error(_, _) => V::Result::output(), // errors and spans are not important
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 7a11a80..d0f25e8 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -314,6 +314,7 @@ pub enum PermitVariants {
 enum TypeRelativePath<'tcx> {
     AssocItem(DefId, GenericArgsRef<'tcx>),
     Variant { adt: Ty<'tcx>, variant_did: DefId },
+    Ctor { ctor_def_id: DefId, args: GenericArgsRef<'tcx> },
 }
 
 /// New-typed boolean indicating whether explicit late-bound lifetimes
@@ -1375,6 +1376,10 @@ pub fn lower_type_relative_ty_path(
                 let adt = self.check_param_uses_if_mcg(adt, span, false);
                 Ok((adt, DefKind::Variant, variant_did))
             }
+            TypeRelativePath::Ctor { .. } => {
+                let e = tcx.dcx().span_err(span, "expected type, found tuple constructor");
+                Err(e)
+            }
         }
     }
 
@@ -1410,6 +1415,9 @@ fn lower_type_relative_const_path(
                 let ct = self.check_param_uses_if_mcg(ct, span, false);
                 Ok(ct)
             }
+            TypeRelativePath::Ctor { ctor_def_id, args } => {
+                return Ok(ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, ctor_def_id, args)));
+            }
             // FIXME(mgca): implement support for this once ready to support all adt ctor expressions,
             // not just const ctors
             TypeRelativePath::Variant { .. } => {
@@ -1441,6 +1449,23 @@ fn lower_type_relative_path(
                     .iter()
                     .find(|vd| tcx.hygienic_eq(segment.ident, vd.ident(tcx), adt_def.did()));
                 if let Some(variant_def) = variant_def {
+                    // FIXME(mgca): do we want constructor resolutions to take priority over
+                    // other possible resolutions?
+                    if matches!(mode, LowerTypeRelativePathMode::Const)
+                        && let Some((CtorKind::Fn, ctor_def_id)) = variant_def.ctor
+                    {
+                        tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None);
+                        let _ = self.prohibit_generic_args(
+                            slice::from_ref(segment).iter(),
+                            GenericsArgsErrExtend::EnumVariant {
+                                qself: hir_self_ty,
+                                assoc_segment: segment,
+                                adt_def,
+                            },
+                        );
+                        let ty::Adt(_, enum_args) = self_ty.kind() else { unreachable!() };
+                        return Ok(TypeRelativePath::Ctor { ctor_def_id, args: enum_args });
+                    }
                     if let PermitVariants::Yes = mode.permit_variants() {
                         tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None);
                         let _ = self.prohibit_generic_args(
@@ -2349,12 +2374,106 @@ pub fn lower_const_arg(
             hir::ConstArgKind::Struct(qpath, inits) => {
                 self.lower_const_arg_struct(hir_id, qpath, inits, const_arg.span())
             }
+            hir::ConstArgKind::TupleCall(qpath, args) => {
+                self.lower_const_arg_tuple_call(hir_id, qpath, args, const_arg.span())
+            }
             hir::ConstArgKind::Anon(anon) => self.lower_const_arg_anon(anon),
             hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
             hir::ConstArgKind::Error(_, e) => ty::Const::new_error(tcx, e),
         }
     }
 
+    fn lower_const_arg_tuple_call(
+        &self,
+        hir_id: HirId,
+        qpath: hir::QPath<'tcx>,
+        args: &'tcx [&'tcx hir::ConstArg<'tcx>],
+        span: Span,
+    ) -> Const<'tcx> {
+        let tcx = self.tcx();
+
+        let non_adt_or_variant_res = || {
+            let e = tcx.dcx().span_err(span, "tuple constructor with invalid base path");
+            ty::Const::new_error(tcx, e)
+        };
+
+        let ctor_const = match qpath {
+            hir::QPath::Resolved(maybe_qself, path) => {
+                let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
+                self.lower_resolved_const_path(opt_self_ty, path, hir_id)
+            }
+            hir::QPath::TypeRelative(hir_self_ty, segment) => {
+                let self_ty = self.lower_ty(hir_self_ty);
+                match self.lower_type_relative_const_path(
+                    self_ty,
+                    hir_self_ty,
+                    segment,
+                    hir_id,
+                    span,
+                ) {
+                    Ok(c) => c,
+                    Err(_) => return non_adt_or_variant_res(),
+                }
+            }
+        };
+
+        let Some(value) = ctor_const.try_to_value() else {
+            return non_adt_or_variant_res();
+        };
+
+        let (adt_def, adt_args, variant_did) = match value.ty.kind() {
+            ty::FnDef(def_id, fn_args)
+                if let DefKind::Ctor(CtorOf::Variant, _) = tcx.def_kind(*def_id) =>
+            {
+                let parent_did = tcx.parent(*def_id);
+                let enum_did = tcx.parent(parent_did);
+                (tcx.adt_def(enum_did), fn_args, parent_did)
+            }
+            ty::FnDef(def_id, fn_args)
+                if let DefKind::Ctor(CtorOf::Struct, _) = tcx.def_kind(*def_id) =>
+            {
+                let parent_did = tcx.parent(*def_id);
+                (tcx.adt_def(parent_did), fn_args, parent_did)
+            }
+            _ => return non_adt_or_variant_res(),
+        };
+
+        let variant_def = adt_def.variant_with_id(variant_did);
+        let variant_idx = adt_def.variant_index_with_id(variant_did).as_u32();
+
+        if args.len() != variant_def.fields.len() {
+            let e = tcx.dcx().span_err(
+                span,
+                format!(
+                    "tuple constructor has {} arguments but {} were provided",
+                    variant_def.fields.len(),
+                    args.len()
+                ),
+            );
+            return ty::Const::new_error(tcx, e);
+        }
+
+        let fields = variant_def
+            .fields
+            .iter()
+            .zip(args)
+            .map(|(field_def, arg)| {
+                self.lower_const_arg(arg, FeedConstTy::Param(field_def.did, adt_args))
+            })
+            .collect::<Vec<_>>();
+
+        let opt_discr_const = if adt_def.is_enum() {
+            let valtree = ty::ValTree::from_scalar_int(tcx, variant_idx.into());
+            Some(ty::Const::new_value(tcx, valtree, tcx.types.u32))
+        } else {
+            None
+        };
+
+        let valtree = ty::ValTree::from_branches(tcx, opt_discr_const.into_iter().chain(fields));
+        let adt_ty = Ty::new_adt(tcx, adt_def, adt_args);
+        ty::Const::new_value(tcx, valtree, adt_ty)
+    }
+
     fn lower_const_arg_struct(
         &self,
         hir_id: HirId,
@@ -2486,6 +2605,20 @@ fn lower_resolved_const_path(
                 let args = self.lower_generic_args_of_path_segment(span, did, segment);
                 ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args))
             }
+            Res::Def(DefKind::Ctor(_, CtorKind::Fn), did) => {
+                assert_eq!(opt_self_ty, None);
+                let [leading_segments @ .., segment] = path.segments else { bug!() };
+                let _ = self
+                    .prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None);
+                let parent_did = tcx.parent(did);
+                let generics_did = if let DefKind::Ctor(CtorOf::Variant, _) = tcx.def_kind(did) {
+                    tcx.parent(parent_did)
+                } else {
+                    parent_did
+                };
+                let args = self.lower_generic_args_of_path_segment(span, generics_did, segment);
+                ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args))
+            }
             Res::Def(DefKind::AssocConst, did) => {
                 let trait_segment = if let [modules @ .., trait_, _item] = path.segments {
                     let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None);
@@ -2521,9 +2654,7 @@ fn lower_resolved_const_path(
                 DefKind::Mod
                 | DefKind::Enum
                 | DefKind::Variant
-                | DefKind::Ctor(CtorOf::Variant, CtorKind::Fn)
                 | DefKind::Struct
-                | DefKind::Ctor(CtorOf::Struct, CtorKind::Fn)
                 | DefKind::OpaqueTy
                 | DefKind::TyAlias
                 | DefKind::TraitAlias
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 5c03135..533afc3 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1139,6 +1139,7 @@ fn print_const_arg(&mut self, const_arg: &hir::ConstArg<'_>) {
         match &const_arg.kind {
             // FIXME(mgca): proper printing for struct exprs
             ConstArgKind::Struct(..) => self.word("/* STRUCT EXPR */"),
+            ConstArgKind::TupleCall(..) => self.word("/* TUPLE CALL */"),
             ConstArgKind::Path(qpath) => self.print_qpath(qpath, true),
             ConstArgKind::Anon(anon) => self.print_anon_const(anon),
             ConstArgKind::Error(_, _) => self.word("/*ERROR*/"),
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index c0f8f33..b2c4a91 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -55,7 +55,11 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
     cfgs.into_iter()
         .map(|s| {
             let psess = ParseSess::emitter_with_note(
-                vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+                vec![
+                    crate::DEFAULT_LOCALE_RESOURCE,
+                    rustc_parse::DEFAULT_LOCALE_RESOURCE,
+                    rustc_session::DEFAULT_LOCALE_RESOURCE,
+                ],
                 format!("this occurred on the command line: `--cfg={s}`"),
             );
             let filename = FileName::cfg_spec_source_code(&s);
@@ -129,7 +133,11 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
 
     for s in specs {
         let psess = ParseSess::emitter_with_note(
-            vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+            vec![
+                crate::DEFAULT_LOCALE_RESOURCE,
+                rustc_parse::DEFAULT_LOCALE_RESOURCE,
+                rustc_session::DEFAULT_LOCALE_RESOURCE,
+            ],
             format!("this occurred on the command line: `--check-cfg={s}`"),
         );
         let filename = FileName::cfg_spec_source_code(&s);
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 920c896..d79ab9e 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1441,6 +1441,7 @@ fn encode_def_ids(&mut self) {
                         // Skip encoding defs for these as they should not have had a `DefId` created
                         hir::ConstArgKind::Error(..)
                         | hir::ConstArgKind::Struct(..)
+                        | hir::ConstArgKind::TupleCall(..)
                         | hir::ConstArgKind::Path(..)
                         | hir::ConstArgKind::Infer(..) => true,
                         hir::ConstArgKind::Anon(..) => false,
diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs
index 2b0be98..b7848bc 100644
--- a/compiler/rustc_middle/src/metadata.rs
+++ b/compiler/rustc_middle/src/metadata.rs
@@ -45,16 +45,9 @@ pub struct ModChild {
     pub reexport_chain: SmallVec<[Reexport; 2]>,
 }
 
-#[derive(Debug, TyEncodable, TyDecodable, HashStable)]
-pub enum AmbigModChildKind {
-    GlobVsGlob,
-    GlobVsExpanded,
-}
-
 /// Same as `ModChild`, however, it includes ambiguity error.
 #[derive(Debug, TyEncodable, TyDecodable, HashStable)]
 pub struct AmbigModChild {
     pub main: ModChild,
     pub second: ModChild,
-    pub kind: AmbigModChildKind,
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 0310003..988baae 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -4,6 +4,7 @@
 mod const_to_pat;
 mod migration;
 
+use std::assert_matches::assert_matches;
 use std::cmp::Ordering;
 use std::sync::Arc;
 
@@ -21,7 +22,7 @@
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::ErrorGuaranteed;
 use tracing::{debug, instrument};
 
 pub(crate) use self::check_match::check_match;
@@ -129,15 +130,20 @@ fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
 
     fn lower_pattern_range_endpoint(
         &mut self,
+        pat: &'tcx hir::Pat<'tcx>, // Range pattern containing the endpoint
         expr: Option<&'tcx hir::PatExpr<'tcx>>,
         // Out-parameter collecting extra data to be reapplied by the caller
         ascriptions: &mut Vec<Ascription<'tcx>>,
     ) -> Result<Option<PatRangeBoundary<'tcx>>, ErrorGuaranteed> {
+        assert_matches!(pat.kind, hir::PatKind::Range(..));
+
+        // For partly-bounded ranges like `X..` or `..X`, an endpoint will be absent.
+        // Return None in that case; the caller will use NegInfinity or PosInfinity instead.
         let Some(expr) = expr else { return Ok(None) };
 
         // Lower the endpoint into a temporary `PatKind` that will then be
         // deconstructed to obtain the constant value and other data.
-        let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr, None);
+        let mut kind: PatKind<'tcx> = self.lower_pat_expr(pat, expr);
 
         // Unpeel any ascription or inline-const wrapper nodes.
         loop {
@@ -214,12 +220,14 @@ fn error_on_literal_overflow(
 
     fn lower_pattern_range(
         &mut self,
+        pat: &'tcx hir::Pat<'tcx>,
         lo_expr: Option<&'tcx hir::PatExpr<'tcx>>,
         hi_expr: Option<&'tcx hir::PatExpr<'tcx>>,
         end: RangeEnd,
-        ty: Ty<'tcx>,
-        span: Span,
     ) -> Result<PatKind<'tcx>, ErrorGuaranteed> {
+        let ty = self.typeck_results.node_type(pat.hir_id);
+        let span = pat.span;
+
         if lo_expr.is_none() && hi_expr.is_none() {
             let msg = "found twice-open range pattern (`..`) outside of error recovery";
             self.tcx.dcx().span_bug(span, msg);
@@ -227,7 +235,8 @@ fn lower_pattern_range(
 
         // Collect extra data while lowering the endpoints, to be reapplied later.
         let mut ascriptions = vec![];
-        let mut lower_endpoint = |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions);
+        let mut lower_endpoint =
+            |expr| self.lower_pattern_range_endpoint(pat, expr, &mut ascriptions);
 
         let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity);
         let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity);
@@ -299,12 +308,10 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
 
             hir::PatKind::Never => PatKind::Never,
 
-            hir::PatKind::Expr(value) => self.lower_pat_expr(value, Some(ty)),
+            hir::PatKind::Expr(value) => self.lower_pat_expr(pat, value),
 
-            hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
-                let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
-                self.lower_pattern_range(lo_expr, hi_expr, end, ty, span)
-                    .unwrap_or_else(PatKind::Error)
+            hir::PatKind::Range(lo_expr, hi_expr, end) => {
+                self.lower_pattern_range(pat, lo_expr, hi_expr, end).unwrap_or_else(PatKind::Error)
             }
 
             hir::PatKind::Deref(subpattern) => {
@@ -327,7 +334,7 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
             },
 
             hir::PatKind::Slice(prefix, slice, suffix) => {
-                self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
+                self.slice_or_array_pattern(pat, prefix, slice, suffix)
             }
 
             hir::PatKind::Tuple(pats, ddpos) => {
@@ -389,7 +396,7 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
                 };
                 let variant_def = adt_def.variant_of_res(res);
                 let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
-                self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
+                self.lower_variant_or_leaf(pat, None, res, subpatterns)
             }
 
             hir::PatKind::Struct(ref qpath, fields, _) => {
@@ -406,7 +413,7 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
                     })
                     .collect();
 
-                self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
+                self.lower_variant_or_leaf(pat, None, res, subpatterns)
             }
 
             hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) },
@@ -445,12 +452,13 @@ fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option<Box
 
     fn slice_or_array_pattern(
         &mut self,
-        span: Span,
-        ty: Ty<'tcx>,
+        pat: &'tcx hir::Pat<'tcx>,
         prefix: &'tcx [hir::Pat<'tcx>],
         slice: Option<&'tcx hir::Pat<'tcx>>,
         suffix: &'tcx [hir::Pat<'tcx>],
     ) -> PatKind<'tcx> {
+        let ty = self.typeck_results.node_type(pat.hir_id);
+
         let prefix = self.lower_patterns(prefix);
         let slice = self.lower_opt_pattern(slice);
         let suffix = self.lower_patterns(suffix);
@@ -465,18 +473,32 @@ fn slice_or_array_pattern(
                 assert!(len >= prefix.len() as u64 + suffix.len() as u64);
                 PatKind::Array { prefix, slice, suffix }
             }
-            _ => span_bug!(span, "bad slice pattern type {:?}", ty),
+            _ => span_bug!(pat.span, "bad slice pattern type {ty:?}"),
         }
     }
 
     fn lower_variant_or_leaf(
         &mut self,
+        pat: &'tcx hir::Pat<'tcx>,
+        expr: Option<&'tcx hir::PatExpr<'tcx>>,
         res: Res,
-        hir_id: hir::HirId,
-        span: Span,
-        ty: Ty<'tcx>,
         subpatterns: Vec<FieldPat<'tcx>>,
     ) -> PatKind<'tcx> {
+        // Check whether the caller should have provided an `expr` for this pattern kind.
+        assert_matches!(
+            (pat.kind, expr),
+            (hir::PatKind::Expr(..) | hir::PatKind::Range(..), Some(_))
+                | (hir::PatKind::Struct(..) | hir::PatKind::TupleStruct(..), None)
+        );
+
+        // Use the id/span of the `hir::PatExpr`, if provided.
+        // Otherwise, use the id/span of the `hir::Pat`.
+        let (hir_id, span) = match expr {
+            Some(expr) => (expr.hir_id, expr.span),
+            None => (pat.hir_id, pat.span),
+        };
+        let ty = self.typeck_results.node_type(hir_id);
+
         let res = match res {
             Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
                 let variant_id = self.tcx.parent(variant_ctor_id);
@@ -563,7 +585,16 @@ fn user_args_applied_to_ty_of_hir_id(
     /// it to `const_to_pat`. Any other path (like enum variants without fields)
     /// is converted to the corresponding pattern via `lower_variant_or_leaf`.
     #[instrument(skip(self), level = "debug")]
-    fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Box<Pat<'tcx>> {
+    fn lower_path(
+        &mut self,
+        pat: &'tcx hir::Pat<'tcx>, // Pattern that directly contains `expr`
+        expr: &'tcx hir::PatExpr<'tcx>,
+        qpath: &hir::QPath<'_>,
+    ) -> Box<Pat<'tcx>> {
+        assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..));
+
+        let id = expr.hir_id;
+        let span = expr.span;
         let ty = self.typeck_results.node_type(id);
         let res = self.typeck_results.qpath_res(qpath, id);
 
@@ -575,7 +606,7 @@ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) ->
             _ => {
                 // The path isn't the name of a constant, so it must actually
                 // be a unit struct or unit variant (e.g. `Option::None`).
-                let kind = self.lower_variant_or_leaf(res, id, span, ty, vec![]);
+                let kind = self.lower_variant_or_leaf(pat, Some(expr), res, vec![]);
                 return Box::new(Pat { span, ty, kind });
             }
         };
@@ -615,24 +646,26 @@ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) ->
     /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`)
     fn lower_pat_expr(
         &mut self,
+        pat: &'tcx hir::Pat<'tcx>, // Pattern that directly contains `expr`
         expr: &'tcx hir::PatExpr<'tcx>,
-        pat_ty: Option<Ty<'tcx>>,
     ) -> PatKind<'tcx> {
+        assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..));
         match &expr.kind {
-            hir::PatExprKind::Path(qpath) => self.lower_path(qpath, expr.hir_id, expr.span).kind,
+            hir::PatExprKind::Path(qpath) => self.lower_path(pat, expr, qpath).kind,
             hir::PatExprKind::Lit { lit, negated } => {
                 // We handle byte string literal patterns by using the pattern's type instead of the
                 // literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference,
                 // the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the
                 // pattern's type means we'll properly translate it to a slice reference pattern. This works
                 // because slices and arrays have the same valtree representation.
-                let ct_ty = match pat_ty {
-                    Some(pat_ty) => pat_ty,
-                    None => self.typeck_results.node_type(expr.hir_id),
-                };
-                let lit_input = LitToConstInput { lit: lit.node, ty: ct_ty, neg: *negated };
+                //
+                // Under `feature(deref_patterns)`, this adjustment can also convert string literal
+                // patterns to `str`, and byte-string literal patterns to `[u8; N]` or `[u8]`.
+
+                let pat_ty = self.typeck_results.node_type(pat.hir_id);
+                let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated };
                 let constant = self.tcx.at(expr.span).lit_to_const(lit_input);
-                self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind
+                self.const_to_pat(constant, pat_ty, expr.hir_id, lit.span).kind
             }
         }
     }
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index 971616b..bf236b7 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -169,7 +169,7 @@
 //! on pattern-tuples.
 //!
 //! Let `pt_1, .., pt_n` and `qt` be length-m tuples of patterns for the same type `(T_1, .., T_m)`.
-//! We compute `usefulness(tp_1, .., tp_n, tq)` as follows:
+//! We compute `usefulness(pt_1, .., pt_n, qt)` as follows:
 //!
 //! - Base case: `m == 0`.
 //!     The pattern-tuples are all empty, i.e. they're all `()`. Thus `tq` is useful iff there are
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index b9c945a..241c136 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -22,7 +22,7 @@
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
 use rustc_index::bit_set::DenseBitSet;
 use rustc_metadata::creader::LoadedMacro;
-use rustc_middle::metadata::{AmbigModChildKind, ModChild, Reexport};
+use rustc_middle::metadata::{ModChild, Reexport};
 use rustc_middle::ty::{Feed, Visibility};
 use rustc_middle::{bug, span_bug};
 use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
@@ -36,9 +36,9 @@
 use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 use crate::ref_mut::CmCell;
 use crate::{
-    AmbiguityKind, BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind,
-    ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult,
-    ResolutionError, Resolver, Segment, Used, VisResolutionError, errors,
+    BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot,
+    NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, ResolutionError,
+    Resolver, Segment, Used, VisResolutionError, errors,
 };
 
 type Res = def::Res<NodeId>;
@@ -82,7 +82,7 @@ fn define_extern(
         vis: Visibility<DefId>,
         span: Span,
         expansion: LocalExpnId,
-        ambiguity: Option<(NameBinding<'ra>, AmbiguityKind)>,
+        ambiguity: Option<NameBinding<'ra>>,
     ) {
         let binding = self.arenas.alloc_name_binding(NameBindingData {
             kind: NameBindingKind::Res(res),
@@ -254,7 +254,7 @@ pub(crate) fn build_reduced_graph_external(&self, module: Module<'ra>) {
                 &child.main,
                 parent_scope,
                 children.len() + i,
-                Some((&child.second, child.kind)),
+                Some(&child.second),
             )
         }
     }
@@ -265,7 +265,7 @@ fn build_reduced_graph_for_external_crate_res(
         child: &ModChild,
         parent_scope: ParentScope<'ra>,
         child_index: usize,
-        ambig_child: Option<(&ModChild, AmbigModChildKind)>,
+        ambig_child: Option<&ModChild>,
     ) {
         let parent = parent_scope.module;
         let child_span = |this: &Self, reexport_chain: &[Reexport], res: def::Res<_>| {
@@ -280,15 +280,11 @@ fn build_reduced_graph_for_external_crate_res(
         let span = child_span(self, reexport_chain, res);
         let res = res.expect_non_local();
         let expansion = parent_scope.expansion;
-        let ambig = ambig_child.map(|(ambig_child, ambig_kind)| {
+        let ambig = ambig_child.map(|ambig_child| {
             let ModChild { ident: _, res, vis, ref reexport_chain } = *ambig_child;
             let span = child_span(self, reexport_chain, res);
             let res = res.expect_non_local();
-            let ambig_kind = match ambig_kind {
-                AmbigModChildKind::GlobVsGlob => AmbiguityKind::GlobVsGlob,
-                AmbigModChildKind::GlobVsExpanded => AmbiguityKind::GlobVsExpanded,
-            };
-            (self.arenas.new_res_binding(res, vis, span, expansion), ambig_kind)
+            self.arenas.new_res_binding(res, vis, span, expansion)
         });
 
         // Record primary definitions.
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index b50fc20..f7b8545 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -419,7 +419,7 @@ fn visit_expr(&mut self, expr: &'a Expr) {
 
             // Avoid overwriting `const_arg_context` as we may want to treat const blocks
             // as being anon consts if we are inside a const argument.
-            ExprKind::Struct(_) => return visit::walk_expr(self, expr),
+            ExprKind::Struct(_) | ExprKind::Call(..) => return visit::walk_expr(self, expr),
             // FIXME(mgca): we may want to handle block labels in some manner
             ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind {
                 // FIXME(mgca): this probably means that mac calls that expand
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 8f20b5f..716e1d5 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1206,9 +1206,12 @@ pub(crate) fn add_scope_set_candidates(
                         }
                     }
                 }
-                Scope::Module(module, _) => {
+                Scope::ModuleNonGlobs(module, _) => {
                     this.add_module_candidates(module, suggestions, filter_fn, None);
                 }
+                Scope::ModuleGlobs(..) => {
+                    // Already handled in `ModuleNonGlobs`.
+                }
                 Scope::MacroUsePrelude => {
                     suggestions.extend(this.macro_use_prelude.iter().filter_map(
                         |(name, binding)| {
@@ -2033,7 +2036,7 @@ fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors:
                 help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously"))
             }
 
-            if let Scope::Module(module, _) = scope {
+            if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope {
                 if module == self.graph_root {
                     help_msgs.push(format!(
                         "use `crate::{ident}` to refer to this {thing} unambiguously"
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index fad92f0..b6ec26b 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -103,22 +103,25 @@ pub(crate) fn visit_scopes<'r, T>(
 
         let rust_2015 = ctxt.edition().is_rust_2015();
         let (ns, macro_kind) = match scope_set {
-            ScopeSet::All(ns) | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None),
+            ScopeSet::All(ns)
+            | ScopeSet::Module(ns, _)
+            | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None),
             ScopeSet::ExternPrelude => (TypeNS, None),
             ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
         };
         let module = match scope_set {
             // Start with the specified module.
-            ScopeSet::ModuleAndExternPrelude(_, module) => module,
+            ScopeSet::Module(_, module) | ScopeSet::ModuleAndExternPrelude(_, module) => module,
             // Jump out of trait or enum modules, they do not act as scopes.
             _ => parent_scope.module.nearest_item_scope(),
         };
+        let module_only = matches!(scope_set, ScopeSet::Module(..));
         let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..));
         let extern_prelude = matches!(scope_set, ScopeSet::ExternPrelude);
         let mut scope = match ns {
-            _ if module_and_extern_prelude => Scope::Module(module, None),
+            _ if module_only || module_and_extern_prelude => Scope::ModuleNonGlobs(module, None),
             _ if extern_prelude => Scope::ExternPreludeItems,
-            TypeNS | ValueNS => Scope::Module(module, None),
+            TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None),
             MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
         };
         let mut ctxt = ctxt.normalize_to_macros_2_0();
@@ -145,7 +148,7 @@ pub(crate) fn visit_scopes<'r, T>(
                     }
                     true
                 }
-                Scope::Module(..) => true,
+                Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true,
                 Scope::MacroUsePrelude => use_prelude || rust_2015,
                 Scope::BuiltinAttrs => true,
                 Scope::ExternPreludeItems | Scope::ExternPreludeFlags => {
@@ -186,20 +189,22 @@ pub(crate) fn visit_scopes<'r, T>(
                     MacroRulesScope::Invocation(invoc_id) => {
                         Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
                     }
-                    MacroRulesScope::Empty => Scope::Module(module, None),
+                    MacroRulesScope::Empty => Scope::ModuleNonGlobs(module, None),
                 },
-                Scope::Module(..) if module_and_extern_prelude => match ns {
+                Scope::ModuleNonGlobs(module, lint_id) => Scope::ModuleGlobs(module, lint_id),
+                Scope::ModuleGlobs(..) if module_only => break,
+                Scope::ModuleGlobs(..) if module_and_extern_prelude => match ns {
                     TypeNS => {
                         ctxt.adjust(ExpnId::root());
                         Scope::ExternPreludeItems
                     }
                     ValueNS | MacroNS => break,
                 },
-                Scope::Module(module, prev_lint_id) => {
+                Scope::ModuleGlobs(module, prev_lint_id) => {
                     use_prelude = !module.no_implicit_prelude;
                     match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
                         Some((parent_module, lint_id)) => {
-                            Scope::Module(parent_module, lint_id.or(prev_lint_id))
+                            Scope::ModuleNonGlobs(parent_module, lint_id.or(prev_lint_id))
                         }
                         None => {
                             ctxt.adjust(ExpnId::root());
@@ -335,13 +340,12 @@ pub(crate) fn resolve_ident_in_lexical_scope(
                     diag_metadata,
                 )));
             } else if let RibKind::Block(Some(module)) = rib.kind
-                && let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted(
-                    module,
+                && let Ok(binding) = self.cm().resolve_ident_in_scope_set(
                     ident,
-                    ns,
+                    ScopeSet::Module(ns, module),
                     parent_scope,
-                    Shadowing::Unrestricted,
                     finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
+                    finalize.is_some(),
                     ignore_binding,
                     None,
                 )
@@ -394,12 +398,14 @@ pub(crate) fn resolve_ident_in_scope_set<'r>(
         assert!(force || finalize.is_none()); // `finalize` implies `force`
 
         // Make sure `self`, `super` etc produce an error when passed to here.
-        if orig_ident.is_path_segment_keyword() {
+        if orig_ident.is_path_segment_keyword() && !matches!(scope_set, ScopeSet::Module(..)) {
             return Err(Determinacy::Determined);
         }
 
         let (ns, macro_kind) = match scope_set {
-            ScopeSet::All(ns) | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None),
+            ScopeSet::All(ns)
+            | ScopeSet::Module(ns, _)
+            | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None),
             ScopeSet::ExternPrelude => (TypeNS, None),
             ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
         };
@@ -429,10 +435,7 @@ pub(crate) fn resolve_ident_in_scope_set<'r>(
             orig_ident.span.ctxt(),
             derive_fallback_lint_id,
             |this, scope, use_prelude, ctxt| {
-                // We can break with an error at this step, it means we cannot determine the
-                // resolution right now, but we must block and wait until we can instead of
-                // considering outer scopes.
-                match this.reborrow().resolve_ident_in_scope(
+                let res = match this.reborrow().resolve_ident_in_scope(
                     orig_ident,
                     ns,
                     scope,
@@ -445,7 +448,18 @@ pub(crate) fn resolve_ident_in_scope_set<'r>(
                     force,
                     ignore_binding,
                     ignore_import,
-                )? {
+                ) {
+                    Ok(binding) => Ok(binding),
+                    // We can break with an error at this step, it means we cannot determine the
+                    // resolution right now, but we must block and wait until we can, instead of
+                    // considering outer scopes. Although there's no need to do that if we already
+                    // have a better solution.
+                    Err(ControlFlow::Break(determinacy)) if innermost_results.is_empty() => {
+                        return ControlFlow::Break(Err(determinacy));
+                    }
+                    Err(determinacy) => Err(determinacy.into_value()),
+                };
+                match res {
                     Ok(binding) if sub_namespace_match(binding.macro_kinds(), macro_kind) => {
                         // Below we report various ambiguity errors.
                         // We do not need to report them if we are either in speculative resolution,
@@ -459,6 +473,8 @@ pub(crate) fn resolve_ident_in_scope_set<'r>(
                             // Found another solution, if the first one was "weak", report an error.
                             if this.get_mut().maybe_push_ambiguity(
                                 orig_ident,
+                                ns,
+                                scope_set,
                                 parent_scope,
                                 binding,
                                 scope,
@@ -504,8 +520,7 @@ fn resolve_ident_in_scope<'r>(
         force: bool,
         ignore_binding: Option<NameBinding<'ra>>,
         ignore_import: Option<Import<'ra>>,
-    ) -> ControlFlow<Result<NameBinding<'ra>, Determinacy>, Result<NameBinding<'ra>, Determinacy>>
-    {
+    ) -> Result<NameBinding<'ra>, ControlFlow<Determinacy, Determinacy>> {
         let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt));
         let ret = match scope {
             Scope::DeriveHelpers(expn_id) => {
@@ -553,22 +568,28 @@ fn resolve_ident_in_scope<'r>(
                 MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined),
                 _ => Err(Determinacy::Determined),
             },
-            Scope::Module(module, derive_fallback_lint_id) => {
-                let (adjusted_parent_scope, adjusted_finalize) =
-                    if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) {
-                        (parent_scope, finalize)
-                    } else {
-                        (
-                            &ParentScope { module, ..*parent_scope },
-                            finalize.map(|f| Finalize { used: Used::Scope, ..f }),
-                        )
-                    };
-                let binding = self.reborrow().resolve_ident_in_module_unadjusted(
+            Scope::ModuleNonGlobs(module, derive_fallback_lint_id) => {
+                let (adjusted_parent_scope, adjusted_finalize) = if matches!(
+                    scope_set,
+                    ScopeSet::Module(..) | ScopeSet::ModuleAndExternPrelude(..)
+                ) {
+                    (parent_scope, finalize)
+                } else {
+                    (
+                        &ParentScope { module, ..*parent_scope },
+                        finalize.map(|f| Finalize { used: Used::Scope, ..f }),
+                    )
+                };
+                let binding = self.reborrow().resolve_ident_in_module_non_globs_unadjusted(
                     module,
                     ident,
                     ns,
                     adjusted_parent_scope,
-                    Shadowing::Restricted,
+                    if matches!(scope_set, ScopeSet::Module(..)) {
+                        Shadowing::Unrestricted
+                    } else {
+                        Shadowing::Restricted
+                    },
                     adjusted_finalize,
                     ignore_binding,
                     ignore_import,
@@ -590,11 +611,61 @@ fn resolve_ident_in_scope<'r>(
                         Ok(binding)
                     }
                     Err(ControlFlow::Continue(determinacy)) => Err(determinacy),
-                    Err(ControlFlow::Break(Determinacy::Undetermined)) => {
-                        return ControlFlow::Break(Err(Determinacy::determined(force)));
+                    Err(ControlFlow::Break(determinacy)) => {
+                        return Err(ControlFlow::Break(Determinacy::determined(
+                            determinacy == Determinacy::Determined || force,
+                        )));
                     }
-                    // Privacy errors, do not happen during in scope resolution.
-                    Err(ControlFlow::Break(Determinacy::Determined)) => unreachable!(),
+                }
+            }
+            Scope::ModuleGlobs(module, derive_fallback_lint_id) => {
+                let (adjusted_parent_scope, adjusted_finalize) = if matches!(
+                    scope_set,
+                    ScopeSet::Module(..) | ScopeSet::ModuleAndExternPrelude(..)
+                ) {
+                    (parent_scope, finalize)
+                } else {
+                    (
+                        &ParentScope { module, ..*parent_scope },
+                        finalize.map(|f| Finalize { used: Used::Scope, ..f }),
+                    )
+                };
+                let binding = self.reborrow().resolve_ident_in_module_globs_unadjusted(
+                    module,
+                    ident,
+                    ns,
+                    adjusted_parent_scope,
+                    if matches!(scope_set, ScopeSet::Module(..)) {
+                        Shadowing::Unrestricted
+                    } else {
+                        Shadowing::Restricted
+                    },
+                    adjusted_finalize,
+                    ignore_binding,
+                    ignore_import,
+                );
+                match binding {
+                    Ok(binding) => {
+                        if let Some(lint_id) = derive_fallback_lint_id {
+                            self.get_mut().lint_buffer.buffer_lint(
+                                PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+                                lint_id,
+                                orig_ident.span,
+                                errors::ProcMacroDeriveResolutionFallback {
+                                    span: orig_ident.span,
+                                    ns_descr: ns.descr(),
+                                    ident,
+                                },
+                            );
+                        }
+                        Ok(binding)
+                    }
+                    Err(ControlFlow::Continue(determinacy)) => Err(determinacy),
+                    Err(ControlFlow::Break(determinacy)) => {
+                        return Err(ControlFlow::Break(Determinacy::determined(
+                            determinacy == Determinacy::Determined || force,
+                        )));
+                    }
                 }
             }
             Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() {
@@ -628,13 +699,12 @@ fn resolve_ident_in_scope<'r>(
             Scope::StdLibPrelude => {
                 let mut result = Err(Determinacy::Determined);
                 if let Some(prelude) = self.prelude
-                    && let Ok(binding) = self.reborrow().resolve_ident_in_module_unadjusted(
-                        prelude,
+                    && let Ok(binding) = self.reborrow().resolve_ident_in_scope_set(
                         ident,
-                        ns,
+                        ScopeSet::Module(ns, prelude),
                         parent_scope,
-                        Shadowing::Unrestricted,
                         None,
+                        false,
                         ignore_binding,
                         ignore_import,
                     )
@@ -680,12 +750,14 @@ fn resolve_ident_in_scope<'r>(
             },
         };
 
-        ControlFlow::Continue(ret)
+        ret.map_err(ControlFlow::Continue)
     }
 
     fn maybe_push_ambiguity(
         &mut self,
         orig_ident: Ident,
+        ns: Namespace,
+        scope_set: ScopeSet<'ra>,
         parent_scope: &ParentScope<'ra>,
         binding: NameBinding<'ra>,
         scope: Scope<'ra>,
@@ -699,6 +771,7 @@ fn maybe_push_ambiguity(
 
         // FIXME: Use `scope` instead of `res` to detect built-in attrs and derive helpers,
         // it will exclude imports, make slightly more code legal, and will require lang approval.
+        let module_only = matches!(scope_set, ScopeSet::Module(..));
         let is_builtin = |res| matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..)));
         let derive_helper = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
         let derive_helper_compat = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat);
@@ -710,12 +783,12 @@ fn maybe_push_ambiguity(
         } else if res == derive_helper_compat && innermost_res != derive_helper {
             span_bug!(orig_ident.span, "impossible inner resolution kind")
         } else if matches!(innermost_scope, Scope::MacroRules(_))
-            && matches!(scope, Scope::Module(..))
+            && matches!(scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..))
             && !self.disambiguate_macro_rules_vs_modularized(innermost_binding, binding)
         {
             Some(AmbiguityKind::MacroRulesVsModularized)
         } else if matches!(scope, Scope::MacroRules(_))
-            && matches!(innermost_scope, Scope::Module(..))
+            && matches!(innermost_scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..))
         {
             // should be impossible because of visitation order in
             // visit_scopes
@@ -729,11 +802,24 @@ fn maybe_push_ambiguity(
             )
         } else if innermost_binding.is_glob_import() {
             Some(AmbiguityKind::GlobVsOuter)
-        } else if innermost_binding.may_appear_after(parent_scope.expansion, binding) {
+        } else if !module_only
+            && innermost_binding.may_appear_after(parent_scope.expansion, binding)
+        {
             Some(AmbiguityKind::MoreExpandedVsOuter)
+        } else if innermost_binding.expansion != LocalExpnId::ROOT
+            && (!module_only || ns == MacroNS)
+            && let Scope::ModuleGlobs(m1, _) = scope
+            && let Scope::ModuleNonGlobs(m2, _) = innermost_scope
+            && m1 == m2
+        {
+            // FIXME: this error is too conservative and technically unnecessary now when module
+            // scope is split into two scopes, at least when not resolving in `ScopeSet::Module`,
+            // remove it with lang team approval.
+            Some(AmbiguityKind::GlobVsExpanded)
         } else {
             None
         };
+
         if let Some(kind) = ambiguity_error_kind {
             // Skip ambiguity errors for extern flag bindings "overridden"
             // by extern item bindings.
@@ -742,8 +828,21 @@ fn maybe_push_ambiguity(
                 && innermost_results[1..].iter().any(|(b, s)| {
                     matches!(s, Scope::ExternPreludeItems) && *b != innermost_binding
                 });
+            // Skip ambiguity errors for nonglob module bindings "overridden"
+            // by glob module bindings in the same module.
+            // FIXME: Remove with lang team approval.
+            let issue_149681_hack = match scope {
+                Scope::ModuleGlobs(m1, _)
+                    if innermost_results[1..]
+                        .iter()
+                        .any(|(_, s)| matches!(*s, Scope::ModuleNonGlobs(m2, _) if m1 == m2)) =>
+                {
+                    true
+                }
+                _ => false,
+            };
 
-            if issue_145575_hack {
+            if issue_145575_hack || issue_149681_hack {
                 self.issue_145575_hack_applied = true;
             } else {
                 self.ambiguity_errors.push(AmbiguityError {
@@ -826,18 +925,15 @@ fn resolve_ident_in_virt_module_unadjusted<'r>(
         ignore_import: Option<Import<'ra>>,
     ) -> Result<NameBinding<'ra>, Determinacy> {
         match module {
-            ModuleOrUniformRoot::Module(module) => self
-                .resolve_ident_in_module_unadjusted(
-                    module,
-                    ident,
-                    ns,
-                    parent_scope,
-                    Shadowing::Unrestricted,
-                    finalize,
-                    ignore_binding,
-                    ignore_import,
-                )
-                .map_err(|determinacy| determinacy.into_value()),
+            ModuleOrUniformRoot::Module(module) => self.resolve_ident_in_scope_set(
+                ident,
+                ScopeSet::Module(ns, module),
+                parent_scope,
+                finalize,
+                finalize.is_some(),
+                ignore_binding,
+                ignore_import,
+            ),
             ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set(
                 ident,
                 ScopeSet::ModuleAndExternPrelude(ns, module),
@@ -887,48 +983,6 @@ fn resolve_ident_in_virt_module_unadjusted<'r>(
         }
     }
 
-    /// Attempts to resolve `ident` in namespace `ns` of `module`.
-    fn resolve_ident_in_module_unadjusted<'r>(
-        mut self: CmResolver<'r, 'ra, 'tcx>,
-        module: Module<'ra>,
-        ident: Ident,
-        ns: Namespace,
-        parent_scope: &ParentScope<'ra>,
-        shadowing: Shadowing,
-        finalize: Option<Finalize>,
-        // This binding should be ignored during in-module resolution, so that we don't get
-        // "self-confirming" import resolutions during import validation and checking.
-        ignore_binding: Option<NameBinding<'ra>>,
-        ignore_import: Option<Import<'ra>>,
-    ) -> Result<NameBinding<'ra>, ControlFlow<Determinacy, Determinacy>> {
-        let res = self.reborrow().resolve_ident_in_module_non_globs_unadjusted(
-            module,
-            ident,
-            ns,
-            parent_scope,
-            shadowing,
-            finalize,
-            ignore_binding,
-            ignore_import,
-        );
-
-        match res {
-            Ok(_) | Err(ControlFlow::Break(_)) => return res,
-            Err(ControlFlow::Continue(_)) => {}
-        }
-
-        self.resolve_ident_in_module_globs_unadjusted(
-            module,
-            ident,
-            ns,
-            parent_scope,
-            shadowing,
-            finalize,
-            ignore_binding,
-            ignore_import,
-        )
-    }
-
     /// Attempts to resolve `ident` in namespace `ns` of non-glob bindings in `module`.
     fn resolve_ident_in_module_non_globs_unadjusted<'r>(
         mut self: CmResolver<'r, 'ra, 'tcx>,
@@ -938,6 +992,8 @@ fn resolve_ident_in_module_non_globs_unadjusted<'r>(
         parent_scope: &ParentScope<'ra>,
         shadowing: Shadowing,
         finalize: Option<Finalize>,
+        // This binding should be ignored during in-module resolution, so that we don't get
+        // "self-confirming" import resolutions during import validation and checking.
         ignore_binding: Option<NameBinding<'ra>>,
         ignore_import: Option<Import<'ra>>,
     ) -> Result<NameBinding<'ra>, ControlFlow<Determinacy, Determinacy>> {
@@ -956,7 +1012,6 @@ fn resolve_ident_in_module_non_globs_unadjusted<'r>(
             return self.get_mut().finalize_module_binding(
                 ident,
                 binding,
-                if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None },
                 parent_scope,
                 module,
                 finalize,
@@ -1018,7 +1073,6 @@ fn resolve_ident_in_module_globs_unadjusted<'r>(
             return self.get_mut().finalize_module_binding(
                 ident,
                 binding,
-                if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None },
                 parent_scope,
                 module,
                 finalize,
@@ -1097,28 +1151,24 @@ fn resolve_ident_in_module_globs_unadjusted<'r>(
                 Some(None) => {}
                 None => continue,
             };
-            let result = self.reborrow().resolve_ident_in_module_unadjusted(
-                module,
+            let result = self.reborrow().resolve_ident_in_scope_set(
                 ident,
-                ns,
+                ScopeSet::Module(ns, module),
                 adjusted_parent_scope,
-                Shadowing::Unrestricted,
                 None,
+                false,
                 ignore_binding,
                 ignore_import,
             );
 
             match result {
-                Err(ControlFlow::Break(Determined) | ControlFlow::Continue(Determined)) => continue,
+                Err(Determined) => continue,
                 Ok(binding)
                     if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) =>
                 {
                     continue;
                 }
-                Ok(_)
-                | Err(ControlFlow::Break(Undetermined) | ControlFlow::Continue(Undetermined)) => {
-                    return Err(ControlFlow::Continue(Undetermined));
-                }
+                Ok(_) | Err(Undetermined) => return Err(ControlFlow::Continue(Undetermined)),
             }
         }
 
@@ -1130,7 +1180,6 @@ fn finalize_module_binding(
         &mut self,
         ident: Ident,
         binding: Option<NameBinding<'ra>>,
-        shadowed_glob: Option<NameBinding<'ra>>,
         parent_scope: &ParentScope<'ra>,
         module: Module<'ra>,
         finalize: Finalize,
@@ -1158,24 +1207,6 @@ fn finalize_module_binding(
             }
         }
 
-        // Forbid expanded shadowing to avoid time travel.
-        if let Some(shadowed_glob) = shadowed_glob
-            && shadowing == Shadowing::Restricted
-            && finalize.stage == Stage::Early
-            && binding.expansion != LocalExpnId::ROOT
-            && binding.res() != shadowed_glob.res()
-        {
-            self.ambiguity_errors.push(AmbiguityError {
-                kind: AmbiguityKind::GlobVsExpanded,
-                ident,
-                b1: binding,
-                b2: shadowed_glob,
-                scope1: Scope::Module(self.empty_module, None),
-                scope2: Scope::Module(self.empty_module, None),
-                warning: false,
-            });
-        }
-
         if shadowing == Shadowing::Unrestricted
             && binding.expansion != LocalExpnId::ROOT
             && let NameBindingKind::Import { import, .. } = binding.kind
@@ -1231,6 +1262,14 @@ fn single_import_can_define_name<'r>(
         parent_scope: &ParentScope<'ra>,
     ) -> bool {
         for single_import in &resolution.single_imports {
+            if let Some(binding) = resolution.non_glob_binding
+                && let NameBindingKind::Import { import, .. } = binding.kind
+                && import == *single_import
+            {
+                // Single import has already defined the name and we are aware of it,
+                // no need to block the globs.
+                continue;
+            }
             if ignore_import == Some(*single_import) {
                 continue;
             }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 4e0f3db..558f769 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -9,7 +9,7 @@
 use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err};
 use rustc_hir::def::{self, DefKind, PartialRes};
 use rustc_hir::def_id::{DefId, LocalDefIdMap};
-use rustc_middle::metadata::{AmbigModChild, AmbigModChildKind, ModChild, Reexport};
+use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport};
 use rustc_middle::span_bug;
 use rustc_middle::ty::Visibility;
 use rustc_session::lint::BuiltinLintDiag;
@@ -32,10 +32,9 @@
 };
 use crate::ref_mut::CmCell;
 use crate::{
-    AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion,
-    Module, ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope,
-    PathResult, PerNS, ResolutionError, Resolver, ScopeSet, Segment, Used, module_to_string,
-    names_to_string,
+    AmbiguityError, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion, Module,
+    ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult,
+    PerNS, ResolutionError, Resolver, ScopeSet, Segment, Used, module_to_string, names_to_string,
 };
 
 type Res = def::Res<NodeId>;
@@ -373,7 +372,6 @@ pub(crate) fn try_define_local(
                             resolution.glob_binding = Some(glob_binding);
                         } else if res != old_glob_binding.res() {
                             resolution.glob_binding = Some(this.new_ambiguity_binding(
-                                AmbiguityKind::GlobVsGlob,
                                 old_glob_binding,
                                 glob_binding,
                                 warn_ambiguity,
@@ -389,25 +387,11 @@ pub(crate) fn try_define_local(
                     (old_glob @ true, false) | (old_glob @ false, true) => {
                         let (glob_binding, non_glob_binding) =
                             if old_glob { (old_binding, binding) } else { (binding, old_binding) };
-                        if ns == MacroNS
-                            && non_glob_binding.expansion != LocalExpnId::ROOT
-                            && glob_binding.res() != non_glob_binding.res()
-                        {
-                            resolution.non_glob_binding = Some(this.new_ambiguity_binding(
-                                AmbiguityKind::GlobVsExpanded,
-                                non_glob_binding,
-                                glob_binding,
-                                false,
-                            ));
-                        } else {
-                            resolution.non_glob_binding = Some(non_glob_binding);
-                        }
-
+                        resolution.non_glob_binding = Some(non_glob_binding);
                         if let Some(old_glob_binding) = resolution.glob_binding {
                             assert!(old_glob_binding.is_glob_import());
                             if glob_binding.res() != old_glob_binding.res() {
                                 resolution.glob_binding = Some(this.new_ambiguity_binding(
-                                    AmbiguityKind::GlobVsGlob,
                                     old_glob_binding,
                                     glob_binding,
                                     false,
@@ -437,12 +421,11 @@ pub(crate) fn try_define_local(
 
     fn new_ambiguity_binding(
         &self,
-        ambiguity_kind: AmbiguityKind,
         primary_binding: NameBinding<'ra>,
         secondary_binding: NameBinding<'ra>,
         warn_ambiguity: bool,
     ) -> NameBinding<'ra> {
-        let ambiguity = Some((secondary_binding, ambiguity_kind));
+        let ambiguity = Some(secondary_binding);
         let data = NameBindingData { ambiguity, warn_ambiguity, ..*primary_binding };
         self.arenas.alloc_name_binding(data)
     }
@@ -658,7 +641,7 @@ pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet<NameBind
                 let Some(binding) = resolution.best_binding() else { continue };
 
                 if let NameBindingKind::Import { import, .. } = binding.kind
-                    && let Some((amb_binding, _)) = binding.ambiguity
+                    && let Some(amb_binding) = binding.ambiguity
                     && binding.res() != Res::Err
                     && exported_ambiguities.contains(&binding)
                 {
@@ -1566,9 +1549,7 @@ fn finalize_resolutions_in(
                     vis: binding.vis,
                     reexport_chain,
                 };
-                if let Some((ambig_binding1, ambig_binding2, ambig_kind)) =
-                    binding.descent_to_ambiguity()
-                {
+                if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() {
                     let main = child(ambig_binding1.reexport_chain(this));
                     let second = ModChild {
                         ident: ident.0,
@@ -1576,13 +1557,7 @@ fn finalize_resolutions_in(
                         vis: ambig_binding2.vis,
                         reexport_chain: ambig_binding2.reexport_chain(this),
                     };
-                    let kind = match ambig_kind {
-                        AmbiguityKind::GlobVsGlob => AmbigModChildKind::GlobVsGlob,
-                        AmbiguityKind::GlobVsExpanded => AmbigModChildKind::GlobVsExpanded,
-                        _ => unreachable!(),
-                    };
-
-                    ambig_children.push(AmbigModChild { main, second, kind })
+                    ambig_children.push(AmbigModChild { main, second })
                 } else {
                     children.push(child(binding.reexport_chain(this)));
                 }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 2630e2c..0bf6fcb 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -44,8 +44,8 @@
 
 use crate::{
     BindingError, BindingKey, Finalize, LexicalScopeBinding, Module, ModuleOrUniformRoot,
-    NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, TyCtxt, UseError,
-    Used, errors, path_names_to_string, rustdoc,
+    NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Stage, TyCtxt,
+    UseError, Used, errors, path_names_to_string, rustdoc,
 };
 
 mod diagnostics;
@@ -1514,7 +1514,7 @@ fn resolve_path(
             opt_ns,
             &self.parent_scope,
             Some(source),
-            finalize,
+            finalize.map(|finalize| Finalize { stage: Stage::Late, ..finalize }),
             Some(&self.ribs),
             None,
             None,
@@ -2851,6 +2851,7 @@ fn resolve_item(&mut self, item: &'ast Item) {
                 ref define_opaque,
                 ..
             }) => {
+                let is_type_const = attr::contains_name(&item.attrs, sym::type_const);
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(
@@ -2869,7 +2870,22 @@ fn resolve_item(&mut self, item: &'ast Item) {
 
                         this.with_lifetime_rib(
                             LifetimeRibKind::Elided(LifetimeRes::Static),
-                            |this| this.visit_ty(ty),
+                            |this| {
+                                if is_type_const
+                                    && !this.r.tcx.features().generic_const_parameter_types()
+                                {
+                                    this.with_rib(TypeNS, RibKind::ConstParamTy, |this| {
+                                        this.with_rib(ValueNS, RibKind::ConstParamTy, |this| {
+                                            this.with_lifetime_rib(
+                                                LifetimeRibKind::ConstParamTy,
+                                                |this| this.visit_ty(ty),
+                                            )
+                                        })
+                                    });
+                                } else {
+                                    this.visit_ty(ty);
+                                }
+                            },
                         );
 
                         if let Some(rhs) = rhs {
@@ -3209,6 +3225,7 @@ fn resolve_trait_items(&mut self, trait_items: &'ast [Box<AssocItem>]) {
                     define_opaque,
                     ..
                 }) => {
+                    let is_type_const = attr::contains_name(&item.attrs, sym::type_const);
                     self.with_generic_param_rib(
                         &generics.params,
                         RibKind::AssocItem,
@@ -3223,7 +3240,20 @@ fn resolve_trait_items(&mut self, trait_items: &'ast [Box<AssocItem>]) {
                                 },
                                 |this| {
                                     this.visit_generics(generics);
-                                    this.visit_ty(ty);
+                                    if is_type_const
+                                        && !this.r.tcx.features().generic_const_parameter_types()
+                                    {
+                                        this.with_rib(TypeNS, RibKind::ConstParamTy, |this| {
+                                            this.with_rib(ValueNS, RibKind::ConstParamTy, |this| {
+                                                this.with_lifetime_rib(
+                                                    LifetimeRibKind::ConstParamTy,
+                                                    |this| this.visit_ty(ty),
+                                                )
+                                            })
+                                        });
+                                    } else {
+                                        this.visit_ty(ty);
+                                    }
 
                                     // Only impose the restrictions of `ConstRibKind` for an
                                     // actual constant expression in a provided default.
@@ -3417,6 +3447,7 @@ fn resolve_impl_item(
                 ..
             }) => {
                 debug!("resolve_implementation AssocItemKind::Const");
+                let is_type_const = attr::contains_name(&item.attrs, sym::type_const);
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::AssocItem,
@@ -3453,7 +3484,28 @@ fn resolve_impl_item(
                                         );
 
                                         this.visit_generics(generics);
-                                        this.visit_ty(ty);
+                                        if is_type_const
+                                            && !this
+                                                .r
+                                                .tcx
+                                                .features()
+                                                .generic_const_parameter_types()
+                                        {
+                                            this.with_rib(TypeNS, RibKind::ConstParamTy, |this| {
+                                                this.with_rib(
+                                                    ValueNS,
+                                                    RibKind::ConstParamTy,
+                                                    |this| {
+                                                        this.with_lifetime_rib(
+                                                            LifetimeRibKind::ConstParamTy,
+                                                            |this| this.visit_ty(ty),
+                                                        )
+                                                    },
+                                                )
+                                            });
+                                        } else {
+                                            this.visit_ty(ty);
+                                        }
                                         if let Some(rhs) = rhs {
                                             // We allow arbitrary const expressions inside of associated consts,
                                             // even if they are potentially not const evaluatable.
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 7cbf208..fbd1607 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -123,10 +123,14 @@ enum Scope<'ra> {
     DeriveHelpersCompat,
     /// Textual `let`-like scopes introduced by `macro_rules!` items.
     MacroRules(MacroRulesScopeRef<'ra>),
-    /// Names declared in the given module.
+    /// Non-glob names declared in the given module.
     /// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
     /// lint if it should be reported.
-    Module(Module<'ra>, Option<NodeId>),
+    ModuleNonGlobs(Module<'ra>, Option<NodeId>),
+    /// Glob names declared in the given module.
+    /// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
+    /// lint if it should be reported.
+    ModuleGlobs(Module<'ra>, Option<NodeId>),
     /// Names introduced by `#[macro_use]` attributes on `extern crate` items.
     MacroUsePrelude,
     /// Built-in attributes.
@@ -149,6 +153,8 @@ enum Scope<'ra> {
 enum ScopeSet<'ra> {
     /// All scopes with the given namespace.
     All(Namespace),
+    /// Two scopes inside a module, for non-glob and glob bindings.
+    Module(Namespace, Module<'ra>),
     /// A module, then extern prelude (used for mixed 2015-2018 mode in macros).
     ModuleAndExternPrelude(Namespace, Module<'ra>),
     /// Just two extern prelude scopes.
@@ -801,7 +807,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[derive(Clone, Copy, Debug)]
 struct NameBindingData<'ra> {
     kind: NameBindingKind<'ra>,
-    ambiguity: Option<(NameBinding<'ra>, AmbiguityKind)>,
+    ambiguity: Option<NameBinding<'ra>>,
     /// Produce a warning instead of an error when reporting ambiguities inside this binding.
     /// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required.
     warn_ambiguity: bool,
@@ -931,9 +937,9 @@ fn import_source(&self) -> NameBinding<'ra> {
 
     fn descent_to_ambiguity(
         self: NameBinding<'ra>,
-    ) -> Option<(NameBinding<'ra>, NameBinding<'ra>, AmbiguityKind)> {
+    ) -> Option<(NameBinding<'ra>, NameBinding<'ra>)> {
         match self.ambiguity {
-            Some((ambig_binding, ambig_kind)) => Some((self, ambig_binding, ambig_kind)),
+            Some(ambig_binding) => Some((self, ambig_binding)),
             None => match self.kind {
                 NameBindingKind::Import { binding, .. } => binding.descent_to_ambiguity(),
                 _ => None,
@@ -1926,9 +1932,12 @@ fn traits_in_scope(
         let scope_set = ScopeSet::All(TypeNS);
         self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |this, scope, _, _| {
             match scope {
-                Scope::Module(module, _) => {
+                Scope::ModuleNonGlobs(module, _) => {
                     this.get_mut().traits_in_module(module, assoc_item, &mut found_traits);
                 }
+                Scope::ModuleGlobs(..) => {
+                    // Already handled in `ModuleNonGlobs` (but see #144993).
+                }
                 Scope::StdLibPrelude => {
                     if let Some(module) = this.prelude {
                         this.get_mut().traits_in_module(module, assoc_item, &mut found_traits);
@@ -2055,14 +2064,14 @@ fn record_use_inner(
         used: Used,
         warn_ambiguity: bool,
     ) {
-        if let Some((b2, kind)) = used_binding.ambiguity {
+        if let Some(b2) = used_binding.ambiguity {
             let ambiguity_error = AmbiguityError {
-                kind,
+                kind: AmbiguityKind::GlobVsGlob,
                 ident,
                 b1: used_binding,
                 b2,
-                scope1: Scope::Module(self.empty_module, None),
-                scope2: Scope::Module(self.empty_module, None),
+                scope1: Scope::ModuleGlobs(self.empty_module, None),
+                scope2: Scope::ModuleGlobs(self.empty_module, None),
                 warning: warn_ambiguity,
             };
             if !self.matches_previous_ambiguity_error(&ambiguity_error) {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index fe96dab..8c492fc 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -196,6 +196,8 @@ pub enum Offload {
     Device,
     /// Second step in the offload pipeline, generates the host code to call kernels.
     Host(String),
+    /// Test is similar to Host, but allows testing without a device artifact.
+    Test,
 }
 
 /// The different settings that the `-Z autodiff` flag can have.
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index f11ad12..21fa332 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -794,7 +794,8 @@ mod desc {
     pub(crate) const parse_list_with_polarity: &str =
         "a comma-separated list of strings, with elements beginning with + or -";
     pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`, `NoTT`";
-    pub(crate) const parse_offload: &str = "a comma separated list of settings: `Enable`";
+    pub(crate) const parse_offload: &str =
+        "a comma separated list of settings: `Host=<Absolute-Path>`, `Device`, `Test`";
     pub(crate) const parse_comma_list: &str = "a comma-separated list of strings";
     pub(crate) const parse_opt_comma_list: &str = parse_comma_list;
     pub(crate) const parse_number: &str = "a number";
@@ -1471,6 +1472,13 @@ pub(crate) fn parse_offload(slot: &mut Vec<Offload>, v: Option<&str>) -> bool {
                     }
                     Offload::Device
                 }
+                "Test" => {
+                    if let Some(_) = arg {
+                        // Test does not accept a value
+                        return false;
+                    }
+                    Offload::Test
+                }
                 _ => {
                     // FIXME(ZuseZ4): print an error saying which value is not recognized
                     return false;
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index 4954ebc..9e14bf6 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -155,11 +155,10 @@ fn relate<R: TypeRelation<I>>(
         let cx = relation.cx();
 
         if a.c_variadic != b.c_variadic {
-            return Err(TypeError::VariadicMismatch({
-                let a = a.c_variadic;
-                let b = b.c_variadic;
-                ExpectedFound::new(a, b)
-            }));
+            return Err(TypeError::VariadicMismatch(ExpectedFound::new(
+                a.c_variadic,
+                b.c_variadic,
+            )));
         }
 
         if a.safety != b.safety {
diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs
index aa973e0..d1c7cd4 100644
--- a/library/alloc/src/borrow.rs
+++ b/library/alloc/src/borrow.rs
@@ -16,19 +16,6 @@
 #[cfg(not(no_global_oom_handling))]
 use crate::string::String;
 
-// FIXME(inference): const bounds removed due to inference regressions found by crater;
-//   see https://github.com/rust-lang/rust/issues/147964
-// #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, B: ?Sized + ToOwned> Borrow<B> for Cow<'a, B>
-// where
-//     B::Owned: [const] Borrow<B>,
-{
-    fn borrow(&self) -> &B {
-        &**self
-    }
-}
-
 /// A generalization of `Clone` to borrowed data.
 ///
 /// Some types make it possible to go from borrowed to owned, usually by
@@ -192,6 +179,19 @@ pub enum Cow<'a, B: ?Sized + 'a>
     Owned(#[stable(feature = "rust1", since = "1.0.0")] <B as ToOwned>::Owned),
 }
 
+// FIXME(inference): const bounds removed due to inference regressions found by crater;
+//   see https://github.com/rust-lang/rust/issues/147964
+// #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, B: ?Sized + ToOwned> Borrow<B> for Cow<'a, B>
+// where
+//     B::Owned: [const] Borrow<B>,
+{
+    fn borrow(&self) -> &B {
+        &**self
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<B: ?Sized + ToOwned> Clone for Cow<'_, B> {
     fn clone(&self) -> Self {
diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs
index eee7be6..a438536 100644
--- a/library/alloc/src/collections/vec_deque/drain.rs
+++ b/library/alloc/src/collections/vec_deque/drain.rs
@@ -25,10 +25,10 @@ pub struct Drain<
     // drain_start is stored in deque.len
     pub(super) drain_len: usize,
     // index into the logical array, not the physical one (always lies in [0..deque.len))
-    idx: usize,
+    pub(super) idx: usize,
     // number of elements after the drained range
     pub(super) tail_len: usize,
-    remaining: usize,
+    pub(super) remaining: usize,
     // Needed to make Drain covariant over T
     _marker: PhantomData<&'a T>,
 }
@@ -53,7 +53,7 @@ pub(super) unsafe fn new(
 
     // Only returns pointers to the slices, as that's all we need
     // to drop them. May only be called if `self.remaining != 0`.
-    unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
+    pub(super) unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
         unsafe {
             let deque = self.deque.as_ref();
 
diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs
index f73ba79..184e8b7 100644
--- a/library/alloc/src/collections/vec_deque/spec_extend.rs
+++ b/library/alloc/src/collections/vec_deque/spec_extend.rs
@@ -1,7 +1,7 @@
 use core::iter::{Copied, Rev, TrustedLen};
 use core::slice;
 
-use super::VecDeque;
+use super::{Drain, VecDeque};
 use crate::alloc::Allocator;
 #[cfg(not(test))]
 use crate::vec;
@@ -77,8 +77,8 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A>
 }
 
 #[cfg(not(test))]
-impl<T, A: Allocator> SpecExtend<T, vec::IntoIter<T>> for VecDeque<T, A> {
-    fn spec_extend(&mut self, mut iterator: vec::IntoIter<T>) {
+impl<T, A1: Allocator, A2: Allocator> SpecExtend<T, vec::IntoIter<T, A2>> for VecDeque<T, A1> {
+    fn spec_extend(&mut self, mut iterator: vec::IntoIter<T, A2>) {
         let slice = iterator.as_slice();
         self.reserve(slice.len());
 
@@ -153,23 +153,27 @@ impl<T, I, A: Allocator> SpecExtendFront<T, I> for VecDeque<T, A>
 }
 
 #[cfg(not(test))]
-impl<T, A: Allocator> SpecExtendFront<T, vec::IntoIter<T>> for VecDeque<T, A> {
+impl<T, A1: Allocator, A2: Allocator> SpecExtendFront<T, vec::IntoIter<T, A2>> for VecDeque<T, A1> {
     #[track_caller]
-    fn spec_extend_front(&mut self, mut iterator: vec::IntoIter<T>) {
+    fn spec_extend_front(&mut self, mut iterator: vec::IntoIter<T, A2>) {
         let slice = iterator.as_slice();
-        // SAFETY: elements in the slice are forgotten after this call
+        self.reserve(slice.len());
+        // SAFETY: `slice.len()` space was just reserved and elements in the slice are forgotten after this call
         unsafe { prepend_reversed(self, slice) };
         iterator.forget_remaining_elements();
     }
 }
 
 #[cfg(not(test))]
-impl<T, A: Allocator> SpecExtendFront<T, Rev<vec::IntoIter<T>>> for VecDeque<T, A> {
+impl<T, A1: Allocator, A2: Allocator> SpecExtendFront<T, Rev<vec::IntoIter<T, A2>>>
+    for VecDeque<T, A1>
+{
     #[track_caller]
-    fn spec_extend_front(&mut self, iterator: Rev<vec::IntoIter<T>>) {
+    fn spec_extend_front(&mut self, iterator: Rev<vec::IntoIter<T, A2>>) {
         let mut iterator = iterator.into_inner();
         let slice = iterator.as_slice();
-        // SAFETY: elements in the slice are forgotten after this call
+        self.reserve(slice.len());
+        // SAFETY: `slice.len()` space was just reserved and elements in the slice are forgotten after this call
         unsafe { prepend(self, slice) };
         iterator.forget_remaining_elements();
     }
@@ -182,7 +186,8 @@ impl<'a, T, A: Allocator> SpecExtendFront<T, Copied<slice::Iter<'a, T>>> for Vec
     #[track_caller]
     fn spec_extend_front(&mut self, iter: Copied<slice::Iter<'a, T>>) {
         let slice = iter.into_inner().as_slice();
-        // SAFETY: T is Copy because Copied<slice::Iter<'a, T>> is Iterator
+        self.reserve(slice.len());
+        // SAFETY: `slice.len()` space was just reserved and T is Copy because Copied<slice::Iter<'a, T>> is Iterator
         unsafe { prepend_reversed(self, slice) };
     }
 }
@@ -194,17 +199,69 @@ impl<'a, T, A: Allocator> SpecExtendFront<T, Rev<Copied<slice::Iter<'a, T>>>> fo
     #[track_caller]
     fn spec_extend_front(&mut self, iter: Rev<Copied<slice::Iter<'a, T>>>) {
         let slice = iter.into_inner().into_inner().as_slice();
-        // SAFETY: T is Copy because Rev<Copied<slice::Iter<'a, T>>> is Iterator
+        self.reserve(slice.len());
+        // SAFETY: `slice.len()` space was just reserved and T is Copy because Rev<Copied<slice::Iter<'a, T>>> is Iterator
         unsafe { prepend(self, slice) };
     }
 }
 
+impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront<T, Drain<'a, T, A2>> for VecDeque<T, A1> {
+    #[track_caller]
+    fn spec_extend_front(&mut self, mut iter: Drain<'a, T, A2>) {
+        if iter.remaining == 0 {
+            return;
+        }
+
+        self.reserve(iter.remaining);
+        unsafe {
+            // SAFETY: iter.remaining != 0.
+            let (left, right) = iter.as_slices();
+            // SAFETY:
+            // - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`.
+            // - The elements in `left` and `right` are forgotten after these calls.
+            prepend_reversed(self, &*left);
+            prepend_reversed(self, &*right);
+        }
+
+        iter.idx += iter.remaining;
+        iter.remaining = 0;
+    }
+}
+
+impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront<T, Rev<Drain<'a, T, A2>>>
+    for VecDeque<T, A1>
+{
+    #[track_caller]
+    fn spec_extend_front(&mut self, iter: Rev<Drain<'a, T, A2>>) {
+        let mut iter = iter.into_inner();
+
+        if iter.remaining == 0 {
+            return;
+        }
+
+        self.reserve(iter.remaining);
+        unsafe {
+            // SAFETY: iter.remaining != 0.
+            let (left, right) = iter.as_slices();
+            // SAFETY:
+            // - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`.
+            // - The elements in `left` and `right` are forgotten after these calls.
+            prepend(self, &*right);
+            prepend(self, &*left);
+        }
+
+        iter.idx += iter.remaining;
+        iter.remaining = 0;
+    }
+}
+
+/// Prepends elements of `slice` to `deque` using a copy.
+///
 /// # Safety
 ///
-/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
+/// - `deque` must have space for `slice.len()` new elements.
+/// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`.
 unsafe fn prepend<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
-    deque.reserve(slice.len());
-
     unsafe {
         deque.head = deque.wrap_sub(deque.head, slice.len());
         deque.copy_slice(deque.head, slice);
@@ -212,12 +269,13 @@ unsafe fn prepend<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
     }
 }
 
+/// Prepends elements of `slice` to `deque` in reverse order using a copy.
+///
 /// # Safety
 ///
-/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
+/// - `deque` must have space for `slice.len()` new elements.
+/// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`.
 unsafe fn prepend_reversed<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
-    deque.reserve(slice.len());
-
     unsafe {
         deque.head = deque.wrap_sub(deque.head, slice.len());
         deque.copy_slice_reversed(deque.head, slice);
diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs
index 997c4c7..c8cc758 100644
--- a/library/alloc/src/vec/in_place_drop.rs
+++ b/library/alloc/src/vec/in_place_drop.rs
@@ -1,6 +1,5 @@
 use core::marker::PhantomData;
 use core::ptr::{self, NonNull, drop_in_place};
-use core::slice::{self};
 
 use crate::alloc::Global;
 use crate::raw_vec::RawVec;
@@ -22,7 +21,7 @@ impl<T> Drop for InPlaceDrop<T> {
     #[inline]
     fn drop(&mut self) {
         unsafe {
-            ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len()));
+            ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.inner, self.len()));
         }
     }
 }
diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs
index f5bcd3e..7c90884 100644
--- a/library/alloc/src/vec/spec_extend.rs
+++ b/library/alloc/src/vec/spec_extend.rs
@@ -28,8 +28,8 @@ impl<T, I, A: Allocator> SpecExtend<T, I> for Vec<T, A>
     }
 }
 
-impl<T, A: Allocator> SpecExtend<T, IntoIter<T>> for Vec<T, A> {
-    fn spec_extend(&mut self, mut iterator: IntoIter<T>) {
+impl<T, A1: Allocator, A2: Allocator> SpecExtend<T, IntoIter<T, A2>> for Vec<T, A1> {
+    fn spec_extend(&mut self, mut iterator: IntoIter<T, A2>) {
         unsafe {
             self.append_elements(iterator.as_slice() as _);
         }
diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs
index 82803c7..e9f860f 100644
--- a/library/alloctests/tests/vec_deque.rs
+++ b/library/alloctests/tests/vec_deque.rs
@@ -2157,6 +2157,116 @@ fn test_extend_front_specialization_copy_slice() {
 }
 
 #[test]
+fn test_extend_front_specialization_deque_drain() {
+    // trigger 8 code paths: all combinations of prepend and extend_front, wrap and no wrap (src deque), wrap and no wrap (dst deque)
+
+    /// Get deque containing `[1, 2, 3, 4]`, possibly wrapping in the middle (between the 2 and 3).
+    fn test_deque(wrap: bool) -> VecDeque<i32> {
+        if wrap {
+            let mut v = VecDeque::with_capacity(4);
+            v.extend([3, 4]);
+            v.prepend([1, 2]);
+            assert_eq!(v.as_slices(), ([1, 2].as_slice(), [3, 4].as_slice()));
+            v
+        } else {
+            VecDeque::from([1, 2, 3, 4])
+        }
+    }
+
+    // prepend, v2.head == 0
+
+    let mut v1 = VecDeque::with_capacity(7);
+
+    let mut v2 = test_deque(false);
+    v1.prepend(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    assert_eq!(v1, [1, 2, 3, 4]);
+    v1.pop_back();
+
+    let mut v2 = test_deque(false);
+    // this should wrap around the physical buffer
+    v1.prepend(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    // check it really wrapped
+    assert_eq!(v1.as_slices(), ([1].as_slice(), [2, 3, 4, 1, 2, 3].as_slice()));
+
+    // extend_front, v2.head == 0
+
+    let mut v1 = VecDeque::with_capacity(7);
+
+    let mut v2 = test_deque(false);
+    v1.extend_front(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    assert_eq!(v1, [4, 3, 2, 1]);
+    v1.pop_back();
+
+    let mut v2 = test_deque(false);
+    // this should wrap around the physical buffer
+    v1.extend_front(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    // check it really wrapped
+    assert_eq!(v1.as_slices(), ([4].as_slice(), [3, 2, 1, 4, 3, 2].as_slice()));
+
+    // prepend, v2.head != 0
+
+    let mut v1 = VecDeque::with_capacity(7);
+
+    let mut v2 = test_deque(true);
+    v1.prepend(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    assert_eq!(v1, [1, 2, 3, 4]);
+    v1.pop_back();
+
+    let mut v2 = test_deque(true);
+    // this should wrap around the physical buffer
+    v1.prepend(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    // check it really wrapped
+    assert_eq!(v1.as_slices(), ([1].as_slice(), [2, 3, 4, 1, 2, 3].as_slice()));
+
+    // extend_front, v2.head != 0
+
+    let mut v1 = VecDeque::with_capacity(7);
+
+    let mut v2 = test_deque(true);
+    v1.extend_front(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    assert_eq!(v1, [4, 3, 2, 1]);
+    v1.pop_back();
+
+    let mut v2 = test_deque(true);
+    // this should wrap around the physical buffer
+    v1.extend_front(v2.drain(..));
+    // drain removes all elements but keeps the buffer
+    assert_eq!(v2, []);
+    assert!(v2.capacity() >= 4);
+
+    // check it really wrapped
+    assert_eq!(v1.as_slices(), ([4].as_slice(), [3, 2, 1, 4, 3, 2].as_slice()));
+}
+
+#[test]
 fn test_splice() {
     let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]);
     let a = [10, 11, 12];
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index 9a3f716..661ea4a 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -27,10 +27,9 @@
 //!
 //! ## `Cell<T>`
 //!
-//! [`Cell<T>`] implements interior mutability by moving values in and out of the cell. That is, an
-//! `&mut T` to the inner value can never be obtained, and the value itself cannot be directly
-//! obtained without replacing it with something else. Both of these rules ensure that there is
-//! never more than one reference pointing to the inner value. This type provides the following
+//! [`Cell<T>`] implements interior mutability by moving values in and out of the cell. That is, a
+//! `&T` to the inner value can never be obtained, and the value itself cannot be directly
+//! obtained without replacing it with something else. This type provides the following
 //! methods:
 //!
 //!  - For types that implement [`Copy`], the [`get`](Cell::get) method retrieves the current
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 84da778..f106900 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -238,8 +238,7 @@ fn count(self) -> usize
     ///
     /// # Panics
     ///
-    /// This function might panic if the iterator has more than [`usize::MAX`]
-    /// elements.
+    /// This function might panic if the iterator is infinite.
     ///
     /// # Examples
     ///
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 68f2276..5ecc2a6 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -163,7 +163,7 @@ pub trait Sized: MetaSized {
 }
 
 /// Types with a size that can be determined from pointer metadata.
-#[unstable(feature = "sized_hierarchy", issue = "none")]
+#[unstable(feature = "sized_hierarchy", issue = "144404")]
 #[lang = "meta_sized"]
 #[diagnostic::on_unimplemented(
     message = "the size for values of type `{Self}` cannot be known",
@@ -181,7 +181,7 @@ pub trait MetaSized: PointeeSized {
 }
 
 /// Types that may or may not have a size.
-#[unstable(feature = "sized_hierarchy", issue = "none")]
+#[unstable(feature = "sized_hierarchy", issue = "144404")]
 #[lang = "pointee_sized"]
 #[diagnostic::on_unimplemented(
     message = "values of type `{Self}` may or may not have a size",
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index f078dec..4edd35b 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -152,6 +152,43 @@ pub enum TryLockError {
     WouldBlock,
 }
 
+/// An object providing access to a directory on the filesystem.
+///
+/// Directories are automatically closed when they go out of scope.  Errors detected
+/// on closing are ignored by the implementation of `Drop`.
+///
+/// # Platform-specific behavior
+///
+/// On supported systems (including Windows and some UNIX-based OSes), this function acquires a
+/// handle/file descriptor for the directory. This allows functions like [`Dir::open_file`] to
+/// avoid [TOCTOU] errors when the directory itself is being moved.
+///
+/// On other systems, it stores an absolute path (see [`canonicalize()`]). In the latter case, no
+/// [TOCTOU] guarantees are made.
+///
+/// # Examples
+///
+/// Opens a directory and then a file inside it.
+///
+/// ```no_run
+/// #![feature(dirfd)]
+/// use std::{fs::Dir, io};
+///
+/// fn main() -> std::io::Result<()> {
+///     let dir = Dir::open("foo")?;
+///     let mut file = dir.open_file("bar.txt")?;
+///     let contents = io::read_to_string(file)?;
+///     assert_eq!(contents, "Hello, world!");
+///     Ok(())
+/// }
+/// ```
+///
+/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
+#[unstable(feature = "dirfd", issue = "120426")]
+pub struct Dir {
+    inner: fs_imp::Dir,
+}
+
 /// Metadata information about a file.
 ///
 /// This structure is returned from the [`metadata`] or
@@ -1554,6 +1591,87 @@ fn stream_position(&mut self) -> io::Result<u64> {
     }
 }
 
+impl Dir {
+    /// Attempts to open a directory at `path` in read-only mode.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if `path` does not point to an existing directory.
+    /// Other errors may also be returned according to [`OpenOptions::open`].
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(dirfd)]
+    /// use std::{fs::Dir, io};
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let dir = Dir::open("foo")?;
+    ///     let mut f = dir.open_file("bar.txt")?;
+    ///     let contents = io::read_to_string(f)?;
+    ///     assert_eq!(contents, "Hello, world!");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "dirfd", issue = "120426")]
+    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
+        fs_imp::Dir::open(path.as_ref(), &OpenOptions::new().read(true).0)
+            .map(|inner| Self { inner })
+    }
+
+    /// Attempts to open a file in read-only mode relative to this directory.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if `path` does not point to an existing file.
+    /// Other errors may also be returned according to [`OpenOptions::open`].
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(dirfd)]
+    /// use std::{fs::Dir, io};
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let dir = Dir::open("foo")?;
+    ///     let mut f = dir.open_file("bar.txt")?;
+    ///     let contents = io::read_to_string(f)?;
+    ///     assert_eq!(contents, "Hello, world!");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "dirfd", issue = "120426")]
+    pub fn open_file<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
+        self.inner
+            .open_file(path.as_ref(), &OpenOptions::new().read(true).0)
+            .map(|f| File { inner: f })
+    }
+}
+
+impl AsInner<fs_imp::Dir> for Dir {
+    #[inline]
+    fn as_inner(&self) -> &fs_imp::Dir {
+        &self.inner
+    }
+}
+impl FromInner<fs_imp::Dir> for Dir {
+    fn from_inner(f: fs_imp::Dir) -> Dir {
+        Dir { inner: f }
+    }
+}
+impl IntoInner<fs_imp::Dir> for Dir {
+    fn into_inner(self) -> fs_imp::Dir {
+        self.inner
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl fmt::Debug for Dir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.inner.fmt(f)
+    }
+}
+
 impl OpenOptions {
     /// Creates a blank new set of options ready for configuration.
     ///
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index f15efc7..6a86705 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1,7 +1,11 @@
 use rand::RngCore;
 
+#[cfg(not(miri))]
+use super::Dir;
 use crate::assert_matches::assert_matches;
 use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError};
+#[cfg(not(miri))]
+use crate::io;
 use crate::io::prelude::*;
 use crate::io::{BorrowedBuf, ErrorKind, SeekFrom};
 use crate::mem::MaybeUninit;
@@ -2465,3 +2469,30 @@ fn test_fs_set_times_nofollow() {
     assert_ne!(target_metadata.accessed().unwrap(), accessed);
     assert_ne!(target_metadata.modified().unwrap(), modified);
 }
+
+#[test]
+// FIXME: libc calls fail on miri
+#[cfg(not(miri))]
+fn test_dir_smoke_test() {
+    let tmpdir = tmpdir();
+    let dir = Dir::open(tmpdir.path());
+    check!(dir);
+}
+
+#[test]
+// FIXME: libc calls fail on miri
+#[cfg(not(miri))]
+fn test_dir_read_file() {
+    let tmpdir = tmpdir();
+    let mut f = check!(File::create(tmpdir.join("foo.txt")));
+    check!(f.write(b"bar"));
+    check!(f.flush());
+    drop(f);
+    let dir = check!(Dir::open(tmpdir.path()));
+    let f = check!(dir.open_file("foo.txt"));
+    let buf = check!(io::read_to_string(f));
+    assert_eq!("bar", &buf);
+    let f = check!(dir.open_file(tmpdir.join("foo.txt")));
+    let buf = check!(io::read_to_string(f));
+    assert_eq!("bar", &buf);
+}
diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs
index 19b3040..5da5048 100644
--- a/library/std/src/sync/mod.rs
+++ b/library/std/src/sync/mod.rs
@@ -184,6 +184,8 @@
 #[unstable(feature = "mpmc_channel", issue = "126840")]
 pub mod mpmc;
 pub mod mpsc;
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+pub mod oneshot;
 
 pub(crate) mod once; // `pub(crate)` for the `sys::sync::once` implementations and `LazyLock`.
 
diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs
index ee9795a..8df81a5 100644
--- a/library/std/src/sync/mpmc/mod.rs
+++ b/library/std/src/sync/mpmc/mod.rs
@@ -654,7 +654,7 @@ fn clone(&self) -> Self {
 #[unstable(feature = "mpmc_channel", issue = "126840")]
 impl<T> fmt::Debug for Sender<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Sender { .. }")
+        f.debug_struct("Sender").finish_non_exhaustive()
     }
 }
 
@@ -1380,7 +1380,7 @@ fn clone(&self) -> Self {
 #[unstable(feature = "mpmc_channel", issue = "126840")]
 impl<T> fmt::Debug for Receiver<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.pad("Receiver { .. }")
+        f.debug_struct("Receiver").finish_non_exhaustive()
     }
 }
 
diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs
index f91c26a..0ae23f6 100644
--- a/library/std/src/sync/mpsc.rs
+++ b/library/std/src/sync/mpsc.rs
@@ -1114,8 +1114,10 @@ impl<T> error::Error for SendError<T> {}
 impl<T> fmt::Debug for TrySendError<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
-            TrySendError::Full(..) => "Full(..)".fmt(f),
-            TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f),
+            TrySendError::Full(..) => f.debug_tuple("TrySendError::Full").finish_non_exhaustive(),
+            TrySendError::Disconnected(..) => {
+                f.debug_tuple("TrySendError::Disconnected").finish_non_exhaustive()
+            }
         }
     }
 }
diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs
index ed3f8cf..307bf8e 100644
--- a/library/std/src/sync/nonpoison/mutex.rs
+++ b/library/std/src/sync/nonpoison/mutex.rs
@@ -422,7 +422,7 @@ fn from(t: T) -> Self {
 }
 
 #[unstable(feature = "nonpoison_mutex", issue = "134645")]
-impl<T: ?Sized + Default> Default for Mutex<T> {
+impl<T: Default> Default for Mutex<T> {
     /// Creates a `Mutex<T>`, with the `Default` value for T.
     fn default() -> Mutex<T> {
         Mutex::new(Default::default())
diff --git a/library/std/src/sync/oneshot.rs b/library/std/src/sync/oneshot.rs
new file mode 100644
index 0000000..b2c9ba3
--- /dev/null
+++ b/library/std/src/sync/oneshot.rs
@@ -0,0 +1,466 @@
+//! A single-producer, single-consumer (oneshot) channel.
+//!
+//! This is an experimental module, so the API will likely change.
+
+use crate::sync::mpmc;
+use crate::sync::mpsc::{RecvError, SendError};
+use crate::time::{Duration, Instant};
+use crate::{error, fmt};
+
+/// Creates a new oneshot channel, returning the sender/receiver halves.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(oneshot_channel)]
+/// use std::sync::oneshot;
+/// use std::thread;
+///
+/// let (sender, receiver) = oneshot::channel();
+///
+/// // Spawn off an expensive computation.
+/// thread::spawn(move || {
+/// #   fn expensive_computation() -> i32 { 42 }
+///     sender.send(expensive_computation()).unwrap();
+///     // `sender` is consumed by `send`, so we cannot use it anymore.
+/// });
+///
+/// # fn do_other_work() -> i32 { 42 }
+/// do_other_work();
+///
+/// // Let's see what that answer was...
+/// println!("{:?}", receiver.recv().unwrap());
+/// // `receiver` is consumed by `recv`, so we cannot use it anymore.
+/// ```
+#[must_use]
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
+    // Using a `sync_channel` with capacity 1 means that the internal implementation will use the
+    // `Array`-flavored channel implementation.
+    let (sender, receiver) = mpmc::sync_channel(1);
+    (Sender { inner: sender }, Receiver { inner: receiver })
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Sender
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// The sending half of a oneshot channel.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(oneshot_channel)]
+/// use std::sync::oneshot;
+/// use std::thread;
+///
+/// let (sender, receiver) = oneshot::channel();
+///
+/// thread::spawn(move || {
+///     sender.send("Hello from thread!").unwrap();
+/// });
+///
+/// assert_eq!(receiver.recv().unwrap(), "Hello from thread!");
+/// ```
+///
+/// `Sender` cannot be sent between threads if it is sending non-`Send` types.
+///
+/// ```compile_fail
+/// #![feature(oneshot_channel)]
+/// use std::sync::oneshot;
+/// use std::thread;
+/// use std::ptr;
+///
+/// let (sender, receiver) = oneshot::channel();
+///
+/// struct NotSend(*mut ());
+/// thread::spawn(move || {
+///     sender.send(NotSend(ptr::null_mut()));
+/// });
+///
+/// let reply = receiver.try_recv().unwrap();
+/// ```
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+pub struct Sender<T> {
+    /// The `oneshot` channel is simply a wrapper around a `mpmc` channel.
+    inner: mpmc::Sender<T>,
+}
+
+// SAFETY: Since the only methods in which synchronization must occur take full ownership of the
+// [`Sender`], it is perfectly safe to share a `&Sender` between threads (as it is effectively
+// useless without ownership).
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+unsafe impl<T> Sync for Sender<T> {}
+
+impl<T> Sender<T> {
+    /// Attempts to send a value through this channel. This can only fail if the corresponding
+    /// [`Receiver<T>`] has been dropped.
+    ///
+    /// This method is non-blocking (wait-free).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(oneshot_channel)]
+    /// use std::sync::oneshot;
+    /// use std::thread;
+    ///
+    /// let (tx, rx) = oneshot::channel();
+    ///
+    /// thread::spawn(move || {
+    ///     // Perform some computation.
+    ///     let result = 2 + 2;
+    ///     tx.send(result).unwrap();
+    /// });
+    ///
+    /// assert_eq!(rx.recv().unwrap(), 4);
+    /// ```
+    #[unstable(feature = "oneshot_channel", issue = "143674")]
+    pub fn send(self, t: T) -> Result<(), SendError<T>> {
+        self.inner.send(t)
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> fmt::Debug for Sender<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Sender").finish_non_exhaustive()
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Receiver
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// The receiving half of a oneshot channel.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(oneshot_channel)]
+/// use std::sync::oneshot;
+/// use std::thread;
+/// use std::time::Duration;
+///
+/// let (sender, receiver) = oneshot::channel();
+///
+/// thread::spawn(move || {
+///     thread::sleep(Duration::from_millis(100));
+///     sender.send("Hello after delay!").unwrap();
+/// });
+///
+/// println!("Waiting for message...");
+/// println!("{}", receiver.recv().unwrap());
+/// ```
+///
+/// `Receiver` cannot be sent between threads if it is receiving non-`Send` types.
+///
+/// ```compile_fail
+/// # #![feature(oneshot_channel)]
+/// # use std::sync::oneshot;
+/// # use std::thread;
+/// # use std::ptr;
+/// #
+/// let (sender, receiver) = oneshot::channel();
+///
+/// struct NotSend(*mut ());
+/// sender.send(NotSend(ptr::null_mut()));
+///
+/// thread::spawn(move || {
+///     let reply = receiver.try_recv().unwrap();
+/// });
+/// ```
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+pub struct Receiver<T> {
+    /// The `oneshot` channel is simply a wrapper around a `mpmc` channel.
+    inner: mpmc::Receiver<T>,
+}
+
+// SAFETY: Since the only methods in which synchronization must occur take full ownership of the
+// [`Receiver`], it is perfectly safe to share a `&Receiver` between threads (as it is unable to
+// receive any values without ownership).
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+unsafe impl<T> Sync for Receiver<T> {}
+
+impl<T> Receiver<T> {
+    /// Receives the value from the sending end, blocking the calling thread until it gets it.
+    ///
+    /// Can only fail if the corresponding [`Sender<T>`] has been dropped.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(oneshot_channel)]
+    /// use std::sync::oneshot;
+    /// use std::thread;
+    /// use std::time::Duration;
+    ///
+    /// let (tx, rx) = oneshot::channel();
+    ///
+    /// thread::spawn(move || {
+    ///     thread::sleep(Duration::from_millis(500));
+    ///     tx.send("Done!").unwrap();
+    /// });
+    ///
+    /// // This will block until the message arrives.
+    /// println!("{}", rx.recv().unwrap());
+    /// ```
+    #[unstable(feature = "oneshot_channel", issue = "143674")]
+    pub fn recv(self) -> Result<T, RecvError> {
+        self.inner.recv()
+    }
+
+    // Fallible methods.
+
+    /// Attempts to return a pending value on this receiver without blocking.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(oneshot_channel)]
+    /// use std::sync::oneshot;
+    /// use std::thread;
+    /// use std::time::Duration;
+    ///
+    /// let (sender, mut receiver) = oneshot::channel();
+    ///
+    /// thread::spawn(move || {
+    ///     thread::sleep(Duration::from_millis(100));
+    ///     sender.send(42).unwrap();
+    /// });
+    ///
+    /// // Keep trying until we get the message, doing other work in the process.
+    /// loop {
+    ///     match receiver.try_recv() {
+    ///         Ok(value) => {
+    ///             assert_eq!(value, 42);
+    ///             break;
+    ///         }
+    ///         Err(oneshot::TryRecvError::Empty(rx)) => {
+    ///             // Retake ownership of the receiver.
+    ///             receiver = rx;
+    /// #           fn do_other_work() { thread::sleep(Duration::from_millis(25)); }
+    ///             do_other_work();
+    ///         }
+    ///         Err(oneshot::TryRecvError::Disconnected) => panic!("Sender disconnected"),
+    ///     }
+    /// }
+    /// ```
+    #[unstable(feature = "oneshot_channel", issue = "143674")]
+    pub fn try_recv(self) -> Result<T, TryRecvError<T>> {
+        self.inner.try_recv().map_err(|err| match err {
+            mpmc::TryRecvError::Empty => TryRecvError::Empty(self),
+            mpmc::TryRecvError::Disconnected => TryRecvError::Disconnected,
+        })
+    }
+
+    /// Attempts to wait for a value on this receiver, returning an error if the corresponding
+    /// [`Sender`] half of this channel has been dropped, or if it waits more than `timeout`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(oneshot_channel)]
+    /// use std::sync::oneshot;
+    /// use std::thread;
+    /// use std::time::Duration;
+    ///
+    /// let (sender, receiver) = oneshot::channel();
+    ///
+    /// thread::spawn(move || {
+    ///     thread::sleep(Duration::from_millis(500));
+    ///     sender.send("Success!").unwrap();
+    /// });
+    ///
+    /// // Wait up to 1 second for the message
+    /// match receiver.recv_timeout(Duration::from_secs(1)) {
+    ///     Ok(msg) => println!("Received: {}", msg),
+    ///     Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Timed out!"),
+    ///     Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"),
+    /// }
+    /// ```
+    #[unstable(feature = "oneshot_channel", issue = "143674")]
+    pub fn recv_timeout(self, timeout: Duration) -> Result<T, RecvTimeoutError<T>> {
+        self.inner.recv_timeout(timeout).map_err(|err| match err {
+            mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self),
+            mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected,
+        })
+    }
+
+    /// Attempts to wait for a value on this receiver, returning an error if the corresponding
+    /// [`Sender`] half of this channel has been dropped, or if `deadline` is reached.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(oneshot_channel)]
+    /// use std::sync::oneshot;
+    /// use std::thread;
+    /// use std::time::{Duration, Instant};
+    ///
+    /// let (sender, receiver) = oneshot::channel();
+    ///
+    /// thread::spawn(move || {
+    ///     thread::sleep(Duration::from_millis(100));
+    ///     sender.send("Just in time!").unwrap();
+    /// });
+    ///
+    /// let deadline = Instant::now() + Duration::from_millis(500);
+    /// match receiver.recv_deadline(deadline) {
+    ///     Ok(msg) => println!("Received: {}", msg),
+    ///     Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Missed deadline!"),
+    ///     Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"),
+    /// }
+    /// ```
+    #[unstable(feature = "oneshot_channel", issue = "143674")]
+    pub fn recv_deadline(self, deadline: Instant) -> Result<T, RecvTimeoutError<T>> {
+        self.inner.recv_deadline(deadline).map_err(|err| match err {
+            mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self),
+            mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected,
+        })
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> fmt::Debug for Receiver<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Receiver").finish_non_exhaustive()
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Receiver Errors
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// An error returned from the [`try_recv`](Receiver::try_recv) method.
+///
+/// See the documentation for [`try_recv`] for more information on how to use this error.
+///
+/// [`try_recv`]: Receiver::try_recv
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+pub enum TryRecvError<T> {
+    /// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet
+    /// disconnected). This variant contains the [`Receiver`] that [`try_recv`](Receiver::try_recv)
+    /// took ownership over.
+    Empty(Receiver<T>),
+    /// The corresponding [`Sender`] half of this channel has become disconnected, and there will
+    /// never be any more data sent over the channel.
+    Disconnected,
+}
+
+/// An error returned from the [`recv_timeout`](Receiver::recv_timeout) or
+/// [`recv_deadline`](Receiver::recv_deadline) methods.
+///
+/// # Examples
+///
+/// Usage of this error is similar to [`TryRecvError`].
+///
+/// ```
+/// #![feature(oneshot_channel)]
+/// use std::sync::oneshot::{self, RecvTimeoutError};
+/// use std::thread;
+/// use std::time::Duration;
+///
+/// let (sender, receiver) = oneshot::channel();
+///
+/// let send_failure = thread::spawn(move || {
+///     // Simulate a long computation that takes longer than our timeout.
+///     thread::sleep(Duration::from_millis(250));
+///
+///     // This will likely fail to send because we drop the receiver in the main thread.
+///     sender.send("Goodbye!".to_string()).unwrap();
+/// });
+///
+/// // Try to receive the message with a short timeout.
+/// match receiver.recv_timeout(Duration::from_millis(10)) {
+///     Ok(msg) => println!("Received: {}", msg),
+///     Err(RecvTimeoutError::Timeout(rx)) => {
+///         println!("Timed out waiting for message!");
+///
+///         // Note that you can reuse the receiver without dropping it.
+///         drop(rx);
+///     },
+///     Err(RecvTimeoutError::Disconnected) => println!("Sender dropped!"),
+/// }
+///
+/// send_failure.join().unwrap_err();
+/// ```
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+pub enum RecvTimeoutError<T> {
+    /// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet
+    /// disconnected). This variant contains the [`Receiver`] that either
+    /// [`recv_timeout`](Receiver::recv_timeout) or [`recv_deadline`](Receiver::recv_deadline) took
+    /// ownership over.
+    Timeout(Receiver<T>),
+    /// The corresponding [`Sender`] half of this channel has become disconnected, and there will
+    /// never be any more data sent over the channel.
+    Disconnected,
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> fmt::Debug for TryRecvError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_tuple("TryRecvError").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> fmt::Display for TryRecvError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            TryRecvError::Empty(..) => "receiving on an empty oneshot channel".fmt(f),
+            TryRecvError::Disconnected => "receiving on a closed oneshot channel".fmt(f),
+        }
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> error::Error for TryRecvError<T> {}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> From<RecvError> for TryRecvError<T> {
+    /// Converts a `RecvError` into a `TryRecvError`.
+    ///
+    /// This conversion always returns `TryRecvError::Disconnected`.
+    ///
+    /// No data is allocated on the heap.
+    fn from(err: RecvError) -> TryRecvError<T> {
+        match err {
+            RecvError => TryRecvError::Disconnected,
+        }
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> fmt::Debug for RecvTimeoutError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_tuple("RecvTimeoutError").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> fmt::Display for RecvTimeoutError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            RecvTimeoutError::Timeout(..) => "timed out waiting on oneshot channel".fmt(f),
+            RecvTimeoutError::Disconnected => "receiving on a closed oneshot channel".fmt(f),
+        }
+    }
+}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> error::Error for RecvTimeoutError<T> {}
+
+#[unstable(feature = "oneshot_channel", issue = "143674")]
+impl<T> From<RecvError> for RecvTimeoutError<T> {
+    /// Converts a `RecvError` into a `RecvTimeoutError`.
+    ///
+    /// This conversion always returns `RecvTimeoutError::Disconnected`.
+    ///
+    /// No data is allocated on the heap.
+    fn from(err: RecvError) -> RecvTimeoutError<T> {
+        match err {
+            RecvError => RecvTimeoutError::Disconnected,
+        }
+    }
+}
diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs
index 7f9e5fe..6eccd8a 100644
--- a/library/std/src/sync/poison/mutex.rs
+++ b/library/std/src/sync/poison/mutex.rs
@@ -688,7 +688,7 @@ fn from(t: T) -> Self {
 }
 
 #[stable(feature = "mutex_default", since = "1.10.0")]
-impl<T: ?Sized + Default> Default for Mutex<T> {
+impl<T: Default> Default for Mutex<T> {
     /// Creates a `Mutex<T>`, with the `Default` value for T.
     fn default() -> Mutex<T> {
         Mutex::new(Default::default())
diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs
index fbd075d..4d47d39 100644
--- a/library/std/src/sys/fs/common.rs
+++ b/library/std/src/sys/fs/common.rs
@@ -1,9 +1,10 @@
 #![allow(dead_code)] // not used on all platforms
 
-use crate::fs;
 use crate::io::{self, Error, ErrorKind};
-use crate::path::Path;
+use crate::path::{Path, PathBuf};
+use crate::sys::fs::{File, OpenOptions};
 use crate::sys::helpers::ignore_notfound;
+use crate::{fmt, fs};
 
 pub(crate) const NOT_FILE_ERROR: Error = io::const_error!(
     ErrorKind::InvalidInput,
@@ -58,3 +59,23 @@ pub fn exists(path: &Path) -> io::Result<bool> {
         Err(error) => Err(error),
     }
 }
+
+pub struct Dir {
+    path: PathBuf,
+}
+
+impl Dir {
+    pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result<Self> {
+        path.canonicalize().map(|path| Self { path })
+    }
+
+    pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        File::open(&self.path.join(path), &opts)
+    }
+}
+
+impl fmt::Debug for Dir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Dir").field("path", &self.path).finish()
+    }
+}
diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs
index bb0c44d..1e281eb 100644
--- a/library/std/src/sys/fs/hermit.rs
+++ b/library/std/src/sys/fs/hermit.rs
@@ -10,7 +10,7 @@
 use crate::path::{Path, PathBuf};
 use crate::sync::Arc;
 use crate::sys::fd::FileDesc;
-pub use crate::sys::fs::common::{copy, exists};
+pub use crate::sys::fs::common::{Dir, copy, exists};
 use crate::sys::helpers::run_path_with_cstr;
 use crate::sys::time::SystemTime;
 use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, unsupported, unsupported_err};
@@ -18,6 +18,7 @@
 
 #[derive(Debug)]
 pub struct File(FileDesc);
+
 #[derive(Clone)]
 pub struct FileAttr {
     stat_val: stat_struct,
diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs
index fa046cb..0c297c5 100644
--- a/library/std/src/sys/fs/mod.rs
+++ b/library/std/src/sys/fs/mod.rs
@@ -59,7 +59,7 @@ pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&Path) -> io::Result<T>) -> i
 }
 
 pub use imp::{
-    DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+    Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
     ReadDir,
 };
 
diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs
index 8f3336e..2ae01db 100644
--- a/library/std/src/sys/fs/motor.rs
+++ b/library/std/src/sys/fs/motor.rs
@@ -4,7 +4,7 @@
 use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::path::{Path, PathBuf};
 use crate::sys::fd::FileDesc;
-pub use crate::sys::fs::common::exists;
+pub use crate::sys::fs::common::{Dir, exists};
 use crate::sys::time::SystemTime;
 use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, map_motor_error, unsupported};
 
diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs
index b6d41cc..114ffda 100644
--- a/library/std/src/sys/fs/solid.rs
+++ b/library/std/src/sys/fs/solid.rs
@@ -9,7 +9,7 @@
 use crate::os::solid::ffi::OsStrExt;
 use crate::path::{Path, PathBuf};
 use crate::sync::Arc;
-pub use crate::sys::fs::common::exists;
+pub use crate::sys::fs::common::{Dir, exists};
 use crate::sys::helpers::ignore_notfound;
 use crate::sys::pal::{abi, error};
 use crate::sys::time::SystemTime;
diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs
index f553096..74cacd6 100644
--- a/library/std/src/sys/fs/uefi.rs
+++ b/library/std/src/sys/fs/uefi.rs
@@ -6,6 +6,7 @@
 use crate::hash::Hash;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
 use crate::path::{Path, PathBuf};
+pub use crate::sys::fs::common::Dir;
 use crate::sys::pal::{helpers, unsupported};
 use crate::sys::time::SystemTime;
 
@@ -393,8 +394,43 @@ pub fn unlink(p: &Path) -> io::Result<()> {
     }
 }
 
-pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
-    unsupported()
+/// The implementation mirrors `mv` implementation in UEFI shell:
+/// https://github.com/tianocore/edk2/blob/66346d5edeac2a00d3cf2f2f3b5f66d423c07b3e/ShellPkg/Library/UefiShellLevel2CommandsLib/Mv.c#L455
+///
+/// In a nutshell we do the following:
+/// 1. Convert both old and new paths to absolute paths.
+/// 2. Check that both lie in the same disk.
+/// 3. Construct the target path relative to the current disk root.
+/// 4. Set this target path as the file_name in the file_info structure.
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+    let old_absolute = crate::path::absolute(old)?;
+    let new_absolute = crate::path::absolute(new)?;
+
+    let mut old_components = old_absolute.components();
+    let mut new_components = new_absolute.components();
+
+    let Some(old_disk) = old_components.next() else {
+        return Err(io::const_error!(io::ErrorKind::InvalidInput, "Old path is not valid"));
+    };
+    let Some(new_disk) = new_components.next() else {
+        return Err(io::const_error!(io::ErrorKind::InvalidInput, "New path is not valid"));
+    };
+
+    // Ensure that paths are on the same device.
+    if old_disk != new_disk {
+        return Err(io::const_error!(io::ErrorKind::CrossesDevices, "Cannot rename across device"));
+    }
+
+    // Construct an path relative the current disk root.
+    let new_relative =
+        [crate::path::Component::RootDir].into_iter().chain(new_components).collect::<PathBuf>();
+
+    let f = uefi_fs::File::from_path(old, file::MODE_READ | file::MODE_WRITE, 0)?;
+    let file_info = f.file_info()?;
+
+    let new_info = file_info.with_file_name(new_relative.as_os_str())?;
+
+    f.set_file_info(new_info)
 }
 
 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
@@ -759,12 +795,7 @@ pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
     }
 
     pub(crate) fn file_name_from_uefi(info: &UefiBox<file::Info>) -> OsString {
-        let file_name = {
-            let size = unsafe { (*info.as_ptr()).size };
-            let strlen = (size as usize - crate::mem::size_of::<file::Info<0>>() - 1) / 2;
-            unsafe { crate::slice::from_raw_parts((*info.as_ptr()).file_name.as_ptr(), strlen) }
-        };
-
-        OsString::from_wide(file_name)
+        let fname = info.file_name();
+        OsString::from_wide(&fname[..fname.len() - 1])
     }
 }
diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs
index 1cc2edd..327b0eb 100644
--- a/library/std/src/sys/fs/unix.rs
+++ b/library/std/src/sys/fs/unix.rs
@@ -257,7 +257,7 @@ pub struct FileAttr {
 
 // all DirEntry's will have a reference to this struct
 struct InnerReadDir {
-    dirp: Dir,
+    dirp: DirStream,
     root: PathBuf,
 }
 
@@ -272,10 +272,134 @@ fn new(inner: InnerReadDir) -> Self {
     }
 }
 
-struct Dir(*mut libc::DIR);
+struct DirStream(*mut libc::DIR);
 
-unsafe impl Send for Dir {}
-unsafe impl Sync for Dir {}
+// dir::Dir requires openat support
+cfg_select! {
+    any(
+        target_os = "redox",
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "nto",
+        target_os = "vxworks",
+    ) => {
+        pub use crate::sys::fs::common::Dir;
+    }
+    _ => {
+        mod dir;
+        pub use dir::Dir;
+    }
+}
+
+fn debug_path_fd<'a, 'b>(
+    fd: c_int,
+    f: &'a mut fmt::Formatter<'b>,
+    name: &str,
+) -> fmt::DebugStruct<'a, 'b> {
+    let mut b = f.debug_struct(name);
+
+    fn get_mode(fd: c_int) -> Option<(bool, bool)> {
+        let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
+        if mode == -1 {
+            return None;
+        }
+        match mode & libc::O_ACCMODE {
+            libc::O_RDONLY => Some((true, false)),
+            libc::O_RDWR => Some((true, true)),
+            libc::O_WRONLY => Some((false, true)),
+            _ => None,
+        }
+    }
+
+    b.field("fd", &fd);
+    if let Some(path) = get_path_from_fd(fd) {
+        b.field("path", &path);
+    }
+    if let Some((read, write)) = get_mode(fd) {
+        b.field("read", &read).field("write", &write);
+    }
+
+    b
+}
+
+fn get_path_from_fd(fd: c_int) -> Option<PathBuf> {
+    #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))]
+    fn get_path(fd: c_int) -> Option<PathBuf> {
+        let mut p = PathBuf::from("/proc/self/fd");
+        p.push(&fd.to_string());
+        run_path_with_cstr(&p, &readlink).ok()
+    }
+
+    #[cfg(any(target_vendor = "apple", target_os = "netbsd"))]
+    fn get_path(fd: c_int) -> Option<PathBuf> {
+        // FIXME: The use of PATH_MAX is generally not encouraged, but it
+        // is inevitable in this case because Apple targets and NetBSD define `fcntl`
+        // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
+        // alternatives. If a better method is invented, it should be used
+        // instead.
+        let mut buf = vec![0; libc::PATH_MAX as usize];
+        let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
+        if n == -1 {
+            cfg_select! {
+                target_os = "netbsd" => {
+                    // fallback to procfs as last resort
+                    let mut p = PathBuf::from("/proc/self/fd");
+                    p.push(&fd.to_string());
+                    return run_path_with_cstr(&p, &readlink).ok()
+                }
+                _ => {
+                    return None;
+                }
+            }
+        }
+        let l = buf.iter().position(|&c| c == 0).unwrap();
+        buf.truncate(l as usize);
+        buf.shrink_to_fit();
+        Some(PathBuf::from(OsString::from_vec(buf)))
+    }
+
+    #[cfg(target_os = "freebsd")]
+    fn get_path(fd: c_int) -> Option<PathBuf> {
+        let info = Box::<libc::kinfo_file>::new_zeroed();
+        let mut info = unsafe { info.assume_init() };
+        info.kf_structsize = size_of::<libc::kinfo_file>() as libc::c_int;
+        let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
+        if n == -1 {
+            return None;
+        }
+        let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
+        Some(PathBuf::from(OsString::from_vec(buf)))
+    }
+
+    #[cfg(target_os = "vxworks")]
+    fn get_path(fd: c_int) -> Option<PathBuf> {
+        let mut buf = vec![0; libc::PATH_MAX as usize];
+        let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
+        if n == -1 {
+            return None;
+        }
+        let l = buf.iter().position(|&c| c == 0).unwrap();
+        buf.truncate(l as usize);
+        Some(PathBuf::from(OsString::from_vec(buf)))
+    }
+
+    #[cfg(not(any(
+        target_os = "linux",
+        target_os = "vxworks",
+        target_os = "freebsd",
+        target_os = "netbsd",
+        target_os = "illumos",
+        target_os = "solaris",
+        target_vendor = "apple",
+    )))]
+    fn get_path(_fd: c_int) -> Option<PathBuf> {
+        // FIXME(#24570): implement this for other Unix platforms
+        None
+    }
+
+    get_path(fd)
+}
 
 #[cfg(any(
     target_os = "aix",
@@ -874,7 +998,7 @@ pub(crate) fn debug_assert_fd_is_open(fd: RawFd) {
     }
 }
 
-impl Drop for Dir {
+impl Drop for DirStream {
     fn drop(&mut self) {
         // dirfd isn't supported everywhere
         #[cfg(not(any(
@@ -902,6 +1026,11 @@ fn drop(&mut self) {
     }
 }
 
+// SAFETY: `int dirfd (DIR *dirstream)` is MT-safe, implying that the pointer
+// may be safely sent among threads.
+unsafe impl Send for DirStream {}
+unsafe impl Sync for DirStream {}
+
 impl DirEntry {
     pub fn path(&self) -> PathBuf {
         self.dir.root.join(self.file_name_os_str())
@@ -1860,102 +1989,8 @@ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
 
 impl fmt::Debug for File {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))]
-        fn get_path(fd: c_int) -> Option<PathBuf> {
-            let mut p = PathBuf::from("/proc/self/fd");
-            p.push(&fd.to_string());
-            run_path_with_cstr(&p, &readlink).ok()
-        }
-
-        #[cfg(any(target_vendor = "apple", target_os = "netbsd"))]
-        fn get_path(fd: c_int) -> Option<PathBuf> {
-            // FIXME: The use of PATH_MAX is generally not encouraged, but it
-            // is inevitable in this case because Apple targets and NetBSD define `fcntl`
-            // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
-            // alternatives. If a better method is invented, it should be used
-            // instead.
-            let mut buf = vec![0; libc::PATH_MAX as usize];
-            let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
-            if n == -1 {
-                cfg_select! {
-                    target_os = "netbsd" => {
-                        // fallback to procfs as last resort
-                        let mut p = PathBuf::from("/proc/self/fd");
-                        p.push(&fd.to_string());
-                        return run_path_with_cstr(&p, &readlink).ok()
-                    }
-                    _ => {
-                        return None;
-                    }
-                }
-            }
-            let l = buf.iter().position(|&c| c == 0).unwrap();
-            buf.truncate(l as usize);
-            buf.shrink_to_fit();
-            Some(PathBuf::from(OsString::from_vec(buf)))
-        }
-
-        #[cfg(target_os = "freebsd")]
-        fn get_path(fd: c_int) -> Option<PathBuf> {
-            let info = Box::<libc::kinfo_file>::new_zeroed();
-            let mut info = unsafe { info.assume_init() };
-            info.kf_structsize = size_of::<libc::kinfo_file>() as libc::c_int;
-            let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
-            if n == -1 {
-                return None;
-            }
-            let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
-            Some(PathBuf::from(OsString::from_vec(buf)))
-        }
-
-        #[cfg(target_os = "vxworks")]
-        fn get_path(fd: c_int) -> Option<PathBuf> {
-            let mut buf = vec![0; libc::PATH_MAX as usize];
-            let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
-            if n == -1 {
-                return None;
-            }
-            let l = buf.iter().position(|&c| c == 0).unwrap();
-            buf.truncate(l as usize);
-            Some(PathBuf::from(OsString::from_vec(buf)))
-        }
-
-        #[cfg(not(any(
-            target_os = "linux",
-            target_os = "vxworks",
-            target_os = "freebsd",
-            target_os = "netbsd",
-            target_os = "illumos",
-            target_os = "solaris",
-            target_vendor = "apple",
-        )))]
-        fn get_path(_fd: c_int) -> Option<PathBuf> {
-            // FIXME(#24570): implement this for other Unix platforms
-            None
-        }
-
-        fn get_mode(fd: c_int) -> Option<(bool, bool)> {
-            let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
-            if mode == -1 {
-                return None;
-            }
-            match mode & libc::O_ACCMODE {
-                libc::O_RDONLY => Some((true, false)),
-                libc::O_RDWR => Some((true, true)),
-                libc::O_WRONLY => Some((false, true)),
-                _ => None,
-            }
-        }
-
         let fd = self.as_raw_fd();
-        let mut b = f.debug_struct("File");
-        b.field("fd", &fd);
-        if let Some(path) = get_path(fd) {
-            b.field("path", &path);
-        }
-        if let Some((read, write)) = get_mode(fd) {
-            b.field("read", &read).field("write", &write);
-        }
+        let mut b = debug_path_fd(fd, f, "File");
         b.finish()
     }
 }
@@ -2033,7 +2068,7 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> {
         Err(Error::last_os_error())
     } else {
         let root = path.to_path_buf();
-        let inner = InnerReadDir { dirp: Dir(ptr), root };
+        let inner = InnerReadDir { dirp: DirStream(ptr), root };
         Ok(ReadDir::new(inner))
     }
 }
@@ -2493,7 +2528,8 @@ mod remove_dir_impl {
     use libc::{fdopendir, openat64 as openat, unlinkat};
 
     use super::{
-        AsRawFd, Dir, DirEntry, FromRawFd, InnerReadDir, IntoRawFd, OwnedFd, RawFd, ReadDir, lstat,
+        AsRawFd, DirEntry, DirStream, FromRawFd, InnerReadDir, IntoRawFd, OwnedFd, RawFd, ReadDir,
+        lstat,
     };
     use crate::ffi::CStr;
     use crate::io;
@@ -2517,7 +2553,7 @@ fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
         if ptr.is_null() {
             return Err(io::Error::last_os_error());
         }
-        let dirp = Dir(ptr);
+        let dirp = DirStream(ptr);
         // file descriptor is automatically closed by libc::closedir() now, so give up ownership
         let new_parent_fd = dir_fd.into_raw_fd();
         // a valid root is not needed because we do not call any functions involving the full path
diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs
new file mode 100644
index 0000000..094d1bf
--- /dev/null
+++ b/library/std/src/sys/fs/unix/dir.rs
@@ -0,0 +1,114 @@
+use libc::c_int;
+
+cfg_select! {
+    not(
+        any(
+            all(target_os = "linux", not(target_env = "musl")),
+            target_os = "l4re",
+            target_os = "android",
+            target_os = "hurd",
+        )
+    ) => {
+        use libc::{open as open64, openat as openat64};
+    }
+    _ => {
+        use libc::{open64, openat64};
+    }
+}
+
+use crate::ffi::CStr;
+use crate::os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd};
+#[cfg(target_family = "unix")]
+use crate::os::unix::io::{AsRawFd, FromRawFd};
+#[cfg(target_os = "wasi")]
+use crate::os::wasi::io::{AsRawFd, FromRawFd};
+use crate::path::Path;
+use crate::sys::fd::FileDesc;
+use crate::sys::fs::OpenOptions;
+use crate::sys::fs::unix::{File, debug_path_fd};
+use crate::sys::helpers::run_path_with_cstr;
+use crate::sys::{AsInner, FromInner, IntoInner, cvt_r};
+use crate::{fmt, fs, io};
+
+pub struct Dir(OwnedFd);
+
+impl Dir {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
+        run_path_with_cstr(path, &|path| Self::open_with_c(path, opts))
+    }
+
+    pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, &opts))
+    }
+
+    pub fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
+        let flags = libc::O_CLOEXEC
+            | libc::O_DIRECTORY
+            | opts.get_access_mode()?
+            | opts.get_creation_mode()?
+            | (opts.custom_flags as c_int & !libc::O_ACCMODE);
+        let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
+        Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
+    }
+
+    fn open_file_c(&self, path: &CStr, opts: &OpenOptions) -> io::Result<File> {
+        let flags = libc::O_CLOEXEC
+            | opts.get_access_mode()?
+            | opts.get_creation_mode()?
+            | (opts.custom_flags as c_int & !libc::O_ACCMODE);
+        let fd = cvt_r(|| unsafe {
+            openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int)
+        })?;
+        Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
+    }
+}
+
+impl fmt::Debug for Dir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let fd = self.0.as_raw_fd();
+        let mut b = debug_path_fd(fd, f, "Dir");
+        b.finish()
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl AsRawFd for fs::Dir {
+    fn as_raw_fd(&self) -> RawFd {
+        self.as_inner().0.as_raw_fd()
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl IntoRawFd for fs::Dir {
+    fn into_raw_fd(self) -> RawFd {
+        self.into_inner().0.into_raw_fd()
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl FromRawFd for fs::Dir {
+    unsafe fn from_raw_fd(fd: RawFd) -> Self {
+        Self::from_inner(Dir(unsafe { FromRawFd::from_raw_fd(fd) }))
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl AsFd for fs::Dir {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.as_inner().0.as_fd()
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl From<fs::Dir> for OwnedFd {
+    fn from(value: fs::Dir) -> Self {
+        value.into_inner().0
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl From<OwnedFd> for fs::Dir {
+    fn from(value: OwnedFd) -> Self {
+        Self::from_inner(Dir(value))
+    }
+}
diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs
index f222151..069b4fb 100644
--- a/library/std/src/sys/fs/unsupported.rs
+++ b/library/std/src/sys/fs/unsupported.rs
@@ -4,6 +4,7 @@
 use crate::hash::{Hash, Hasher};
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
 use crate::path::{Path, PathBuf};
+pub use crate::sys::fs::common::Dir;
 use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
 
diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs
index b588b91..2e3d50a 100644
--- a/library/std/src/sys/fs/windows.rs
+++ b/library/std/src/sys/fs/windows.rs
@@ -18,6 +18,8 @@
 use crate::sys::{Align8, AsInner, FromInner, IntoInner, c, cvt};
 use crate::{fmt, ptr, slice};
 
+mod dir;
+pub use dir::Dir;
 mod remove_dir_all;
 use remove_dir_all::remove_dir_all_iterative;
 
@@ -274,7 +276,7 @@ fn get_access_mode(&self) -> io::Result<u32> {
         }
     }
 
-    fn get_creation_mode(&self) -> io::Result<u32> {
+    fn get_cmode_disposition(&self) -> io::Result<(u32, u32)> {
         match (self.write, self.append) {
             (true, false) => {}
             (false, false) => {
@@ -296,16 +298,24 @@ fn get_creation_mode(&self) -> io::Result<u32> {
         }
 
         Ok(match (self.create, self.truncate, self.create_new) {
-            (false, false, false) => c::OPEN_EXISTING,
-            (true, false, false) => c::OPEN_ALWAYS,
-            (false, true, false) => c::TRUNCATE_EXISTING,
+            (false, false, false) => (c::OPEN_EXISTING, c::FILE_OPEN),
+            (true, false, false) => (c::OPEN_ALWAYS, c::FILE_OPEN_IF),
+            (false, true, false) => (c::TRUNCATE_EXISTING, c::FILE_OVERWRITE),
             // `CREATE_ALWAYS` has weird semantics so we emulate it using
             // `OPEN_ALWAYS` and a manual truncation step. See #115745.
-            (true, true, false) => c::OPEN_ALWAYS,
-            (_, _, true) => c::CREATE_NEW,
+            (true, true, false) => (c::OPEN_ALWAYS, c::FILE_OVERWRITE_IF),
+            (_, _, true) => (c::CREATE_NEW, c::FILE_CREATE),
         })
     }
 
+    fn get_creation_mode(&self) -> io::Result<u32> {
+        self.get_cmode_disposition().map(|(mode, _)| mode)
+    }
+
+    fn get_disposition(&self) -> io::Result<u32> {
+        self.get_cmode_disposition().map(|(_, mode)| mode)
+    }
+
     fn get_flags_and_attributes(&self) -> u32 {
         self.custom_flags
             | self.attributes
@@ -1019,14 +1029,23 @@ unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
     }
 }
 
+fn debug_path_handle<'a, 'b>(
+    handle: BorrowedHandle<'a>,
+    f: &'a mut fmt::Formatter<'b>,
+    name: &str,
+) -> fmt::DebugStruct<'a, 'b> {
+    // FIXME(#24570): add more info here (e.g., mode)
+    let mut b = f.debug_struct(name);
+    b.field("handle", &handle.as_raw_handle());
+    if let Ok(path) = get_path(handle) {
+        b.field("path", &path);
+    }
+    b
+}
+
 impl fmt::Debug for File {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // FIXME(#24570): add more info here (e.g., mode)
-        let mut b = f.debug_struct("File");
-        b.field("handle", &self.handle.as_raw_handle());
-        if let Ok(path) = get_path(self) {
-            b.field("path", &path);
-        }
+        let mut b = debug_path_handle(self.handle.as_handle(), f, "File");
         b.finish()
     }
 }
@@ -1292,8 +1311,8 @@ pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> {
                 Layout::from_size_align(struct_size as usize, align_of::<c::FILE_RENAME_INFO>())
                     .unwrap();
 
-            // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
             let file_rename_info;
+            // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
             unsafe {
                 file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFO>();
                 if file_rename_info.is_null() {
@@ -1530,10 +1549,10 @@ pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> {
     file.set_times(times)
 }
 
-fn get_path(f: &File) -> io::Result<PathBuf> {
+fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {
     fill_utf16_buf(
         |buf, sz| unsafe {
-            c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
+            c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
         },
         |buf| PathBuf::from(OsString::from_wide(buf)),
     )
@@ -1546,7 +1565,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
     // This flag is so we can open directories too
     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
     let f = File::open_native(p, &opts)?;
-    get_path(&f)
+    get_path(f.handle)
 }
 
 pub fn copy(from: &WCStr, to: &WCStr) -> io::Result<u64> {
diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs
new file mode 100644
index 0000000..3f61780
--- /dev/null
+++ b/library/std/src/sys/fs/windows/dir.rs
@@ -0,0 +1,153 @@
+use crate::os::windows::io::{
+    AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, HandleOrInvalid, IntoRawHandle,
+    OwnedHandle, RawHandle,
+};
+use crate::path::Path;
+use crate::sys::api::{UnicodeStrRef, WinError};
+use crate::sys::fs::windows::debug_path_handle;
+use crate::sys::fs::{File, OpenOptions};
+use crate::sys::handle::Handle;
+use crate::sys::path::{WCStr, with_native_path};
+use crate::sys::{AsInner, FromInner, IntoInner, IoResult, c, to_u16s};
+use crate::{fmt, fs, io, ptr};
+
+pub struct Dir {
+    handle: Handle,
+}
+
+/// A wrapper around a raw NtCreateFile call.
+///
+/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers.
+unsafe fn nt_create_file(
+    opts: &OpenOptions,
+    object_attributes: &c::OBJECT_ATTRIBUTES,
+    create_options: c::NTCREATEFILE_CREATE_OPTIONS,
+) -> io::Result<Handle> {
+    let mut handle = ptr::null_mut();
+    let mut io_status = c::IO_STATUS_BLOCK::PENDING;
+    // SYNCHRONIZE is included in FILE_GENERIC_READ, but not GENERIC_READ, so we add it manually
+    let access = opts.get_access_mode()? | c::SYNCHRONIZE;
+    // one of FILE_SYNCHRONOUS_IO_{,NON}ALERT is required for later operations to succeed.
+    let options = create_options | c::FILE_SYNCHRONOUS_IO_NONALERT;
+    let status = unsafe {
+        c::NtCreateFile(
+            &mut handle,
+            access,
+            object_attributes,
+            &mut io_status,
+            ptr::null(),
+            c::FILE_ATTRIBUTE_NORMAL,
+            opts.share_mode,
+            opts.get_disposition()?,
+            options,
+            ptr::null(),
+            0,
+        )
+    };
+    if c::nt_success(status) {
+        // SAFETY: nt_success guarantees that handle is no longer null
+        unsafe { Ok(Handle::from_raw_handle(handle)) }
+    } else {
+        Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) })).io_result()
+    }
+}
+
+impl Dir {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
+        with_native_path(path, &|path| Self::open_with_native(path, opts))
+    }
+
+    pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        // NtCreateFile will fail if given an absolute path and a non-null RootDirectory
+        if path.is_absolute() {
+            return File::open(path, opts);
+        }
+        let path = to_u16s(path)?;
+        let path = &path[..path.len() - 1]; // trim 0 byte
+        self.open_file_native(&path, opts).map(|handle| File { handle })
+    }
+
+    fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
+        let creation = opts.get_creation_mode()?;
+        let sa = c::SECURITY_ATTRIBUTES {
+            nLength: size_of::<c::SECURITY_ATTRIBUTES>() as u32,
+            lpSecurityDescriptor: ptr::null_mut(),
+            bInheritHandle: opts.inherit_handle as c::BOOL,
+        };
+        let handle = unsafe {
+            c::CreateFileW(
+                path.as_ptr(),
+                opts.get_access_mode()?,
+                opts.share_mode,
+                &raw const sa,
+                creation,
+                // FILE_FLAG_BACKUP_SEMANTICS is required to open a directory
+                opts.get_flags_and_attributes() | c::FILE_FLAG_BACKUP_SEMANTICS,
+                ptr::null_mut(),
+            )
+        };
+        match OwnedHandle::try_from(unsafe { HandleOrInvalid::from_raw_handle(handle) }) {
+            Ok(handle) => Ok(Self { handle: Handle::from_inner(handle) }),
+            Err(_) => Err(io::Error::last_os_error()),
+        }
+    }
+
+    fn open_file_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
+        let name = UnicodeStrRef::from_slice(path);
+        let object_attributes = c::OBJECT_ATTRIBUTES {
+            RootDirectory: self.handle.as_raw_handle(),
+            ObjectName: name.as_ptr(),
+            ..c::OBJECT_ATTRIBUTES::with_length()
+        };
+        unsafe { nt_create_file(opts, &object_attributes, c::FILE_NON_DIRECTORY_FILE) }
+    }
+}
+
+impl fmt::Debug for Dir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut b = debug_path_handle(self.handle.as_handle(), f, "Dir");
+        b.finish()
+    }
+}
+
+#[unstable(feature = "dirfd", issue = "120426")]
+impl AsRawHandle for fs::Dir {
+    fn as_raw_handle(&self) -> RawHandle {
+        self.as_inner().handle.as_raw_handle()
+    }
+}
+
+#[unstable(feature = "dirhandle", issue = "120426")]
+impl IntoRawHandle for fs::Dir {
+    fn into_raw_handle(self) -> RawHandle {
+        self.into_inner().handle.into_raw_handle()
+    }
+}
+
+#[unstable(feature = "dirhandle", issue = "120426")]
+impl FromRawHandle for fs::Dir {
+    unsafe fn from_raw_handle(handle: RawHandle) -> Self {
+        Self::from_inner(Dir { handle: unsafe { FromRawHandle::from_raw_handle(handle) } })
+    }
+}
+
+#[unstable(feature = "dirhandle", issue = "120426")]
+impl AsHandle for fs::Dir {
+    fn as_handle(&self) -> BorrowedHandle<'_> {
+        self.as_inner().handle.as_handle()
+    }
+}
+
+#[unstable(feature = "dirhandle", issue = "120426")]
+impl From<fs::Dir> for OwnedHandle {
+    fn from(value: fs::Dir) -> Self {
+        value.into_inner().handle.into_inner()
+    }
+}
+
+#[unstable(feature = "dirhandle", issue = "120426")]
+impl From<OwnedHandle> for fs::Dir {
+    fn from(value: OwnedHandle) -> Self {
+        Self::from_inner(Dir { handle: Handle::from_inner(value) })
+    }
+}
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs
index 400c116..9db72db 100644
--- a/library/std/src/sys/pal/uefi/helpers.rs
+++ b/library/std/src/sys/pal/uefi/helpers.rs
@@ -10,7 +10,7 @@
 //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles)
 
 use r_efi::efi::{self, Guid};
-use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell};
+use r_efi::protocols::{device_path, device_path_to_text, file, service_binding, shell};
 
 use crate::alloc::Layout;
 use crate::ffi::{OsStr, OsString};
@@ -791,7 +791,7 @@ pub(crate) fn new(len: usize) -> io::Result<Self> {
 
         match NonNull::new(ptr.cast()) {
             Some(inner) => Ok(Self { inner, size: len }),
-            None => Err(io::Error::new(io::ErrorKind::OutOfMemory, "Allocation failed")),
+            None => Err(const_error!(io::ErrorKind::OutOfMemory, "Allocation failed")),
         }
     }
 
@@ -818,3 +818,58 @@ fn drop(&mut self) {
         unsafe { crate::alloc::dealloc(self.inner.as_ptr().cast(), layout) };
     }
 }
+
+impl UefiBox<file::Info> {
+    fn size(&self) -> u64 {
+        unsafe { (*self.as_ptr()).size }
+    }
+
+    fn set_size(&mut self, s: u64) {
+        unsafe { (*self.as_mut_ptr()).size = s }
+    }
+
+    // Length of string (including NULL), not number of bytes.
+    fn file_name_len(&self) -> usize {
+        (self.size() as usize - size_of::<file::Info<0>>()) / size_of::<u16>()
+    }
+
+    pub(crate) fn file_name(&self) -> &[u16] {
+        unsafe {
+            crate::slice::from_raw_parts((*self.as_ptr()).file_name.as_ptr(), self.file_name_len())
+        }
+    }
+
+    fn file_name_mut(&mut self) -> &mut [u16] {
+        unsafe {
+            crate::slice::from_raw_parts_mut(
+                (*self.as_mut_ptr()).file_name.as_mut_ptr(),
+                self.file_name_len(),
+            )
+        }
+    }
+
+    pub(crate) fn with_file_name(mut self, name: &OsStr) -> io::Result<Self> {
+        // os_string_to_raw returns NULL terminated string. So no need to handle it separately.
+        let fname = os_string_to_raw(name)
+            .ok_or(const_error!(io::ErrorKind::OutOfMemory, "Allocation failed"))?;
+        let new_size = size_of::<file::Info<0>>() + fname.len() * size_of::<u16>();
+
+        // Reuse the current structure if the new name can fit in it.
+        if self.size() >= new_size as u64 {
+            self.file_name_mut()[..fname.len()].copy_from_slice(&fname);
+            self.set_size(new_size as u64);
+
+            return Ok(self);
+        }
+
+        let mut new_box = UefiBox::new(new_size)?;
+
+        unsafe {
+            crate::ptr::copy_nonoverlapping(self.as_ptr(), new_box.as_mut_ptr(), 1);
+        }
+        new_box.set_size(new_size as u64);
+        new_box.file_name_mut().copy_from_slice(&fname);
+
+        Ok(new_box)
+    }
+}
diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs
index 7ade9f6..32a7efd 100644
--- a/library/std/tests/sync/lib.rs
+++ b/library/std/tests/sync/lib.rs
@@ -1,5 +1,6 @@
 #![feature(mapped_lock_guards)]
 #![feature(mpmc_channel)]
+#![feature(oneshot_channel)]
 #![feature(once_cell_try)]
 #![feature(lock_value_accessors)]
 #![feature(reentrant_lock)]
@@ -26,6 +27,8 @@
 mod once;
 mod once_lock;
 #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))]
+mod oneshot;
+#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))]
 mod reentrant_lock;
 #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))]
 mod rwlock;
diff --git a/library/std/tests/sync/oneshot.rs b/library/std/tests/sync/oneshot.rs
new file mode 100644
index 0000000..6a87c72
--- /dev/null
+++ b/library/std/tests/sync/oneshot.rs
@@ -0,0 +1,342 @@
+//! Inspired by tests from <https://github.com/faern/oneshot/blob/main/tests/sync.rs>
+
+use std::sync::mpsc::RecvError;
+use std::sync::oneshot;
+use std::sync::oneshot::{RecvTimeoutError, TryRecvError};
+use std::time::{Duration, Instant};
+use std::{mem, thread};
+
+#[test]
+fn send_before_try_recv() {
+    let (sender, receiver) = oneshot::channel();
+
+    assert!(sender.send(19i128).is_ok());
+
+    match receiver.try_recv() {
+        Ok(19) => {}
+        _ => panic!("expected Ok(19)"),
+    }
+}
+
+#[test]
+fn send_before_recv() {
+    let (sender, receiver) = oneshot::channel::<()>();
+
+    assert!(sender.send(()).is_ok());
+    assert_eq!(receiver.recv(), Ok(()));
+
+    let (sender, receiver) = oneshot::channel::<u64>();
+
+    assert!(sender.send(42).is_ok());
+    assert_eq!(receiver.recv(), Ok(42));
+
+    let (sender, receiver) = oneshot::channel::<[u8; 4096]>();
+
+    assert!(sender.send([0b10101010; 4096]).is_ok());
+    assert!(receiver.recv().unwrap()[..] == [0b10101010; 4096][..]);
+}
+
+#[test]
+fn sender_drop() {
+    {
+        let (sender, receiver) = oneshot::channel::<u128>();
+
+        mem::drop(sender);
+
+        match receiver.recv() {
+            Err(RecvError) => {}
+            _ => panic!("expected recv error"),
+        }
+    }
+
+    {
+        let (sender, receiver) = oneshot::channel::<i32>();
+
+        mem::drop(sender);
+
+        match receiver.try_recv() {
+            Err(TryRecvError::Disconnected) => {}
+            _ => panic!("expected disconnected error"),
+        }
+    }
+    {
+        let (sender, receiver) = oneshot::channel::<i32>();
+
+        mem::drop(sender);
+
+        match receiver.recv_timeout(Duration::from_secs(1)) {
+            Err(RecvTimeoutError::Disconnected) => {}
+            _ => panic!("expected disconnected error"),
+        }
+    }
+}
+
+#[test]
+fn send_never_deadline() {
+    let (sender, receiver) = oneshot::channel::<i32>();
+
+    mem::drop(sender);
+
+    match receiver.recv_deadline(Instant::now()) {
+        Err(RecvTimeoutError::Disconnected) => {}
+        _ => panic!("expected disconnected error"),
+    }
+}
+
+#[test]
+fn send_before_recv_timeout() {
+    let (sender, receiver) = oneshot::channel();
+
+    assert!(sender.send(22i128).is_ok());
+
+    let start = Instant::now();
+
+    let timeout = Duration::from_secs(1);
+    match receiver.recv_timeout(timeout) {
+        Ok(22) => {}
+        _ => panic!("expected Ok(22)"),
+    }
+
+    assert!(start.elapsed() < timeout);
+}
+
+#[test]
+fn send_error() {
+    let (sender, receiver) = oneshot::channel();
+
+    mem::drop(receiver);
+
+    let send_error = sender.send(32u128).unwrap_err();
+    assert_eq!(send_error.0, 32);
+}
+
+#[test]
+fn recv_before_send() {
+    let (sender, receiver) = oneshot::channel();
+
+    let t1 = thread::spawn(move || {
+        thread::sleep(Duration::from_millis(10));
+        sender.send(9u128).unwrap();
+    });
+    let t2 = thread::spawn(move || {
+        assert_eq!(receiver.recv(), Ok(9));
+    });
+
+    t1.join().unwrap();
+    t2.join().unwrap();
+}
+
+#[test]
+fn recv_timeout_before_send() {
+    let (sender, receiver) = oneshot::channel();
+
+    let t = thread::spawn(move || {
+        thread::sleep(Duration::from_millis(100));
+        sender.send(99u128).unwrap();
+    });
+
+    match receiver.recv_timeout(Duration::from_secs(1)) {
+        Ok(99) => {}
+        _ => panic!("expected Ok(99)"),
+    }
+
+    t.join().unwrap();
+}
+
+#[test]
+fn recv_then_drop_sender() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+
+    let t1 = thread::spawn(move || match receiver.recv() {
+        Err(RecvError) => {}
+        _ => panic!("expected recv error"),
+    });
+
+    let t2 = thread::spawn(move || {
+        thread::sleep(Duration::from_millis(10));
+        mem::drop(sender);
+    });
+
+    t1.join().unwrap();
+    t2.join().unwrap();
+}
+
+#[test]
+fn drop_sender_then_recv() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+
+    let t1 = thread::spawn(move || {
+        thread::sleep(Duration::from_millis(10));
+        mem::drop(sender);
+    });
+
+    let t2 = thread::spawn(move || match receiver.recv() {
+        Err(RecvError) => {}
+        _ => panic!("expected disconnected error"),
+    });
+
+    t1.join().unwrap();
+    t2.join().unwrap();
+}
+
+#[test]
+fn try_recv_empty() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+    match receiver.try_recv() {
+        Err(TryRecvError::Empty(_)) => {}
+        _ => panic!("expected empty error"),
+    }
+    mem::drop(sender);
+}
+
+#[test]
+fn try_recv_then_drop_receiver() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+
+    let t1 = thread::spawn(move || {
+        thread::sleep(Duration::from_millis(100));
+        let _ = sender.send(42);
+    });
+
+    let t2 = thread::spawn(move || match receiver.try_recv() {
+        Ok(_) => {}
+        Err(TryRecvError::Empty(r)) => {
+            mem::drop(r);
+        }
+        Err(TryRecvError::Disconnected) => {}
+    });
+
+    t2.join().unwrap();
+    t1.join().unwrap();
+}
+
+#[test]
+fn recv_no_time() {
+    let (_sender, receiver) = oneshot::channel::<u128>();
+
+    let start = Instant::now();
+    match receiver.recv_deadline(start) {
+        Err(RecvTimeoutError::Timeout(_)) => {}
+        _ => panic!("expected timeout error"),
+    }
+
+    let (_sender, receiver) = oneshot::channel::<u128>();
+    match receiver.recv_timeout(Duration::from_millis(0)) {
+        Err(RecvTimeoutError::Timeout(_)) => {}
+        _ => panic!("expected timeout error"),
+    }
+}
+
+#[test]
+fn recv_deadline_passed() {
+    let (_sender, receiver) = oneshot::channel::<u128>();
+
+    let start = Instant::now();
+    let timeout = Duration::from_millis(100);
+
+    match receiver.recv_deadline(start + timeout) {
+        Err(RecvTimeoutError::Timeout(_)) => {}
+        _ => panic!("expected timeout error"),
+    }
+
+    assert!(start.elapsed() >= timeout);
+    assert!(start.elapsed() < timeout * 3);
+}
+
+#[test]
+fn recv_time_passed() {
+    let (_sender, receiver) = oneshot::channel::<u128>();
+
+    let start = Instant::now();
+    let timeout = Duration::from_millis(100);
+    match receiver.recv_timeout(timeout) {
+        Err(RecvTimeoutError::Timeout(_)) => {}
+        _ => panic!("expected timeout error"),
+    }
+    assert!(start.elapsed() >= timeout);
+    assert!(start.elapsed() < timeout * 3);
+}
+
+#[test]
+fn non_send_type_can_be_used_on_same_thread() {
+    use std::ptr;
+
+    #[derive(Debug, Eq, PartialEq)]
+    struct NotSend(*mut ());
+
+    let (sender, receiver) = oneshot::channel();
+    sender.send(NotSend(ptr::null_mut())).unwrap();
+    let reply = receiver.try_recv().unwrap();
+    assert_eq!(reply, NotSend(ptr::null_mut()));
+}
+
+/// Helper for testing drop behavior (taken directly from the `oneshot` crate).
+struct DropCounter {
+    count: std::rc::Rc<std::cell::RefCell<usize>>,
+}
+
+impl DropCounter {
+    fn new() -> (DropTracker, DropCounter) {
+        let count = std::rc::Rc::new(std::cell::RefCell::new(0));
+        (DropTracker { count: count.clone() }, DropCounter { count })
+    }
+
+    fn count(&self) -> usize {
+        *self.count.borrow()
+    }
+}
+
+struct DropTracker {
+    count: std::rc::Rc<std::cell::RefCell<usize>>,
+}
+
+impl Drop for DropTracker {
+    fn drop(&mut self) {
+        *self.count.borrow_mut() += 1;
+    }
+}
+
+#[test]
+fn message_in_channel_dropped_on_receiver_drop() {
+    let (sender, receiver) = oneshot::channel();
+
+    let (message, counter) = DropCounter::new();
+    assert_eq!(counter.count(), 0);
+
+    sender.send(message).unwrap();
+    assert_eq!(counter.count(), 0);
+
+    mem::drop(receiver);
+    assert_eq!(counter.count(), 1);
+}
+
+#[test]
+fn send_error_drops_message_correctly() {
+    let (sender, receiver) = oneshot::channel();
+    mem::drop(receiver);
+
+    let (message, counter) = DropCounter::new();
+
+    let send_error = sender.send(message).unwrap_err();
+    assert_eq!(counter.count(), 0);
+
+    mem::drop(send_error);
+    assert_eq!(counter.count(), 1);
+}
+
+#[test]
+fn send_error_drops_message_correctly_on_extract() {
+    let (sender, receiver) = oneshot::channel();
+    mem::drop(receiver);
+
+    let (message, counter) = DropCounter::new();
+
+    let send_error = sender.send(message).unwrap_err();
+    assert_eq!(counter.count(), 0);
+
+    let message = send_error.0; // Access the inner value directly
+    assert_eq!(counter.count(), 0);
+
+    mem::drop(message);
+    assert_eq!(counter.count(), 1);
+}
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index aa890f1..0e7051c 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -2871,24 +2871,26 @@ fn metadata(&self) -> Option<StepMetadata> {
 /// Tarball containing a prebuilt version of the libgccjit library,
 /// needed as a dependency for the GCC codegen backend (similarly to the LLVM
 /// backend needing a prebuilt libLLVM).
+///
+/// This component is used for `download-ci-gcc`.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
-pub struct Gcc {
+pub struct GccDev {
     target: TargetSelection,
 }
 
-impl Step for Gcc {
+impl Step for GccDev {
     type Output = GeneratedTarball;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.alias("gcc")
+        run.alias("gcc-dev")
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Gcc { target: run.target });
+        run.builder.ensure(GccDev { target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) -> Self::Output {
-        let tarball = Tarball::new(builder, "gcc", &self.target.triple);
+        let tarball = Tarball::new(builder, "gcc-dev", &self.target.triple);
         let output = builder
             .ensure(super::gcc::Gcc { target_pair: GccTargetPair::for_native_build(self.target) });
         tarball.add_file(output.libgccjit(), "lib", FileType::NativeLibrary);
@@ -2896,6 +2898,6 @@ fn run(self, builder: &Builder<'_>) -> Self::Output {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::dist("gcc", self.target))
+        Some(StepMetadata::dist("gcc-dev", self.target))
     }
 }
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 7d2b168..92a6eb8 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -989,7 +989,7 @@ macro_rules! describe {
                 dist::PlainSourceTarballGpl,
                 dist::BuildManifest,
                 dist::ReproducibleArtifacts,
-                dist::Gcc
+                dist::GccDev
             ),
             Kind::Install => describe!(
                 install::Docs,
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index d950dc1..43efdcd 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -381,7 +381,7 @@ pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) {
         }
         let base = &self.stage0_metadata.config.artifacts_server;
         let version = self.artifact_version_part(gcc_sha);
-        let filename = format!("gcc-{version}-{}.tar.xz", self.host_target.triple);
+        let filename = format!("gcc-dev-{version}-{}.tar.xz", self.host_target.triple);
         let tarball = gcc_cache.join(&filename);
         if !tarball.exists() {
             let help_on_error = "ERROR: failed to download gcc from ci
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh
index 4c95d4a..9579e0d 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh
@@ -9,8 +9,8 @@
     --include-default-paths \
     build-manifest bootstrap
 
-# Use GCC for building GCC, as it seems to behave badly when built with Clang
+# Use GCC for building GCC components, as it seems to behave badly when built with Clang
 # Only build GCC on full builds, not try builds
 if [ "${DIST_TRY_BUILD:-0}" == "0" ]; then
-    CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc
+    CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc-dev
 fi
diff --git a/src/ci/run.sh b/src/ci/run.sh
index b486f05..425ad38 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -187,8 +187,9 @@
     RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.static-libstdcpp"
   fi
 
-  # Download GCC from CI on test builders
-  RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=true"
+  # Download GCC from CI on test builders (temporarily disabled because the CI gcc component
+  # was renamed).
+  RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=false"
 
   # download-rustc seems to be broken on CI after the stage0 redesign
   # Disable it until these issues are debugged and resolved
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 707b48b..d75a936 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -323,6 +323,9 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg<'tcx>) -> ConstantKind
             // FIXME(mgca): proper printing :3
             ConstantKind::Path { path: "/* STRUCT EXPR */".to_string().into() }
         }
+        hir::ConstArgKind::TupleCall(..) => {
+            ConstantKind::Path { path: "/* TUPLE CALL */".to_string().into() }
+        }
         hir::ConstArgKind::Anon(anon) => ConstantKind::Anonymous { body: anon.body },
         hir::ConstArgKind::Infer(..) | hir::ConstArgKind::Error(..) => ConstantKind::Infer,
     }
@@ -1804,7 +1807,9 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
                     let ct = cx.tcx.normalize_erasing_regions(typing_env, ct);
                     print_const(cx, ct)
                 }
-                hir::ConstArgKind::Struct(..) | hir::ConstArgKind::Path(..) => {
+                hir::ConstArgKind::Struct(..)
+                | hir::ConstArgKind::Path(..)
+                | hir::ConstArgKind::TupleCall(..) => {
                     let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, FeedConstTy::No);
                     print_const(cx, ct)
                 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 90fa976..f515f998 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -320,6 +320,7 @@ fn const_arg(&self, const_arg: &Binding<&ConstArg<'_>>) {
                 self.body(field!(anon_const.body));
             },
             ConstArgKind::Struct(..) => chain!(self, "let ConstArgKind::Struct(..) = {const_arg}.kind"),
+            ConstArgKind::TupleCall(..) => chain!(self, "let ConstArgKind::TupleCall(..) = {const_arg}.kind"),
             ConstArgKind::Infer(..) => chain!(self, "let ConstArgKind::Infer(..) = {const_arg}.kind"),
             ConstArgKind::Error(..) => chain!(self, "let ConstArgKind::Error(..) = {const_arg}.kind"),
         }
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 78e3d6d..a44cd31 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -1140,7 +1140,7 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx
         ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value),
         ConstItemRhs::TypeConst(const_arg) => match const_arg.kind {
             ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value),
-            ConstArgKind::Struct(..) | ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => {
+            ConstArgKind::Struct(..) | ConstArgKind::TupleCall(..) | ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => {
                 None
             },
         },
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 03853b5..f1ee534 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -671,11 +671,19 @@ fn eq_const_arg(&mut self, left: &ConstArg<'_>, right: &ConstArg<'_>) -> bool {
                         .iter()
                         .zip(*inits_b)
                         .all(|(init_a, init_b)| self.eq_const_arg(init_a.expr, init_b.expr))
-            },
+            }
+            (ConstArgKind::TupleCall(path_a, args_a), ConstArgKind::TupleCall(path_b, args_b)) => {
+                self.eq_qpath(path_a, path_b)
+                    && args_a
+                        .iter()
+                        .zip(*args_b)
+                        .all(|(arg_a, arg_b)| self.eq_const_arg(arg_a, arg_b))
+            }
             // Use explicit match for now since ConstArg is undergoing flux.
             (
                 ConstArgKind::Path(..)
                 | ConstArgKind::Anon(..)
+                | ConstArgKind::TupleCall(..)
                 | ConstArgKind::Infer(..)
                 | ConstArgKind::Struct(..)
                 | ConstArgKind::Error(..),
@@ -1546,6 +1554,12 @@ fn hash_const_arg(&mut self, const_arg: &ConstArg<'_>) {
                     self.hash_const_arg(init.expr);
                 }
             },
+            ConstArgKind::TupleCall(path, args) => {
+                self.hash_qpath(path);
+                for arg in *args {
+                    self.hash_const_arg(arg);
+                }
+            },
             ConstArgKind::Infer(..) | ConstArgKind::Error(..) => {},
         }
     }
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index d1e0c5d..5865954 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -15,7 +15,7 @@
 };
 pub(crate) use crate::directives::file::FileDirectives;
 use crate::directives::handlers::DIRECTIVE_HANDLERS_MAP;
-use crate::directives::line::{DirectiveLine, line_directive};
+use crate::directives::line::DirectiveLine;
 use crate::directives::needs::CachedNeedsConditions;
 use crate::edition::{Edition, parse_edition};
 use crate::errors::ErrorKind;
@@ -29,6 +29,7 @@
 mod file;
 mod handlers;
 mod line;
+pub(crate) use line::line_directive;
 mod line_number;
 pub(crate) use line_number::LineNumber;
 mod needs;
diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs
index c83e00c..7099600 100644
--- a/src/tools/compiletest/src/runtest/debugger.rs
+++ b/src/tools/compiletest/src/runtest/debugger.rs
@@ -4,7 +4,7 @@
 
 use camino::{Utf8Path, Utf8PathBuf};
 
-use crate::directives::LineNumber;
+use crate::directives::{LineNumber, line_directive};
 use crate::runtest::ProcRes;
 
 /// Representation of information to invoke a debugger and check its output
@@ -17,10 +17,16 @@ pub(super) struct DebuggerCommands {
     check_lines: Vec<(LineNumber, String)>,
     /// Source file name
     file: Utf8PathBuf,
+    /// The revision being tested, if any
+    revision: Option<String>,
 }
 
 impl DebuggerCommands {
-    pub fn parse_from(file: &Utf8Path, debugger_prefix: &str) -> Result<Self, String> {
+    pub fn parse_from(
+        file: &Utf8Path,
+        debugger_prefix: &str,
+        test_revision: Option<&str>,
+    ) -> Result<Self, String> {
         let command_directive = format!("{debugger_prefix}-command");
         let check_directive = format!("{debugger_prefix}-check");
 
@@ -37,19 +43,33 @@ pub fn parse_from(file: &Utf8Path, debugger_prefix: &str) -> Result<Self, String
                 continue;
             }
 
-            let Some(line) = line.trim_start().strip_prefix("//@").map(str::trim_start) else {
+            let Some(directive) = line_directive(file, line_number, &line) else {
                 continue;
             };
 
-            if let Some(command) = parse_name_value(&line, &command_directive) {
-                commands.push(command);
+            if !directive.applies_to_test_revision(test_revision) {
+                continue;
             }
-            if let Some(pattern) = parse_name_value(&line, &check_directive) {
-                check_lines.push((line_number, pattern));
+
+            if directive.name == command_directive
+                && let Some(command) = directive.value_after_colon()
+            {
+                commands.push(command.to_string());
+            }
+            if directive.name == check_directive
+                && let Some(pattern) = directive.value_after_colon()
+            {
+                check_lines.push((line_number, pattern.to_string()));
             }
         }
 
-        Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_path_buf() })
+        Ok(Self {
+            commands,
+            breakpoint_lines,
+            check_lines,
+            file: file.to_path_buf(),
+            revision: test_revision.map(str::to_owned),
+        })
     }
 
     /// Given debugger output and lines to check, ensure that every line is
@@ -81,9 +101,11 @@ pub fn check_output(&self, debugger_run_result: &ProcRes) -> Result<(), String>
             Ok(())
         } else {
             let fname = self.file.file_name().unwrap();
+            let revision_suffix =
+                self.revision.as_ref().map_or(String::new(), |r| format!("#{}", r));
             let mut msg = format!(
-                "check directive(s) from `{}` not found in debugger output. errors:",
-                self.file
+                "check directive(s) from `{}{}` not found in debugger output. errors:",
+                self.file, revision_suffix
             );
 
             for (src_lineno, err_line) in missing {
@@ -103,18 +125,6 @@ pub fn check_output(&self, debugger_run_result: &ProcRes) -> Result<(), String>
     }
 }
 
-/// Split off from the main `parse_name_value_directive`, so that improvements
-/// to directive handling aren't held back by debuginfo test commands.
-fn parse_name_value(line: &str, name: &str) -> Option<String> {
-    if let Some(after_name) = line.strip_prefix(name)
-        && let Some(value) = after_name.strip_prefix(':')
-    {
-        Some(value.to_owned())
-    } else {
-        None
-    }
-}
-
 /// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
 fn check_single_line(line: &str, check_line: &str) -> bool {
     // Allow check lines to leave parts unspecified (e.g., uninitialized
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index 83b61b9..9d6edad 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -46,7 +46,7 @@ fn run_debuginfo_cdb_test(&self) {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "cdb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "cdb", self.revision)
             .unwrap_or_else(|e| self.fatal(&e));
 
         // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
@@ -105,7 +105,7 @@ fn run_debuginfo_cdb_test(&self) {
     }
 
     fn run_debuginfo_gdb_test(&self) {
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "gdb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "gdb", self.revision)
             .unwrap_or_else(|e| self.fatal(&e));
         let mut cmds = dbg_cmds.commands.join("\n");
 
@@ -366,7 +366,7 @@ fn run_debuginfo_lldb_test(&self) {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "lldb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "lldb", self.revision)
             .unwrap_or_else(|e| self.fatal(&e));
 
         // Write debugger script:
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index 395c37e..b314aaa 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -123,22 +123,21 @@
 
 [[package]]
 name = "capstone"
-version = "0.13.0"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a"
+checksum = "f442ae0f2f3f1b923334b4a5386c95c69c1cfa072bafa23d6fae6d9682eb1dd4"
 dependencies = [
  "capstone-sys",
- "libc",
+ "static_assertions",
 ]
 
 [[package]]
 name = "capstone-sys"
-version = "0.17.0"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0"
+checksum = "a4e8087cab6731295f5a2a2bd82989ba4f41d3a428aab2e7c98d8f4db38aac05"
 dependencies = [
  "cc",
- "libc",
 ]
 
 [[package]]
@@ -786,9 +785,9 @@
 
 [[package]]
 name = "libffi"
-version = "5.0.0"
+version = "5.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0444124f3ffd67e1b0b0c661a7f81a278a135eb54aaad4078e79fbc8be50c8a5"
+checksum = "0498fe5655f857803e156523e644dcdcdc3b3c7edda42ea2afdae2e09b2db87b"
 dependencies = [
  "libc",
  "libffi-sys",
@@ -796,9 +795,9 @@
 
 [[package]]
 name = "libffi-sys"
-version = "4.0.0"
+version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d722da8817ea580d0669da6babe2262d7b86a1af1103da24102b8bb9c101ce7"
+checksum = "71d4f1d4ce15091955144350b75db16a96d4a63728500122706fb4d29a26afbb"
 dependencies = [
  "cc",
 ]
@@ -1405,6 +1404,12 @@
 checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
 
 [[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 4a54a7e..e7c90e4 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -32,14 +32,14 @@
 [target.'cfg(unix)'.dependencies]
 libc = "0.2"
 # native-lib dependencies
-libffi = { version = "5.0.0", optional = true }
+libffi = { version = "5.1.0", optional = true }
 libloading = { version = "0.9", optional = true }
 serde = { version = "1.0.219", features = ["derive"], optional = true }
 
 [target.'cfg(target_os = "linux")'.dependencies]
 nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional = true }
 ipc-channel = { version = "0.20.0", optional = true }
-capstone = { version = "0.13", optional = true }
+capstone = { version = "0.14", optional = true }
 
 [target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies]
 genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true }
@@ -68,6 +68,7 @@
 tracing = ["serde_json"]
 native-lib = ["dep:libffi", "dep:libloading", "dep:capstone", "dep:ipc-channel", "dep:nix", "dep:serde"]
 jemalloc = []
+check_only = ["libffi?/check_only", "capstone?/check_only", "genmc-sys?/check_only"]
 
 [lints.rust.unexpected_cfgs]
 level = "warn"
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 2832ef5..d5ef9c7 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -219,7 +219,7 @@
 - We have unofficial support (not maintained by the Miri team itself) for some further operating systems.
   - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite.
   - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite.
-  - `android`: **maintainer wanted**. Basic OS APIs and concurrency work, but file system access is not supported.
+  - `android`: **maintainer wanted**. Supports the entire test suite.
 - For targets on other operating systems, Miri might fail before even reaching the `main` function.
 
 However, even for targets that we do support, the degree of support for accessing platform APIs
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index 2dd8fc7..c8e359c 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -30,14 +30,15 @@
 export CARGO_EXTRA_FLAGS="--locked"
 
 # Determine configuration for installed build (used by test-cargo-miri and `./miri bench`).
+# We use the default set of features for this.
 echo "Installing release version of Miri"
 time ./miri install
 
 # Prepare debug build for direct `./miri` invocations.
-# We enable all features to make sure the Stacked Borrows consistency check runs.
+# Here we enable some more features and checks.
 echo "Building debug version of Miri"
-export CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS --all-features"
-time ./miri build # the build that all the `./miri test` below will use
+export FEATURES="--features=expensive-consistency-checks,genmc"
+time ./miri build $FEATURES # the build that all the `./miri test` below will use
 
 endgroup
 
@@ -63,7 +64,7 @@
   if [ -n "${GC_STRESS-}" ]; then
     time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test $TARGET_FLAG
   else
-    time ./miri test $TARGET_FLAG
+    time ./miri test $FEATURES $TARGET_FLAG
   fi
 
   ## advanced tests
@@ -74,20 +75,20 @@
     # them. Also error locations change so we don't run the failing tests.
     # We explicitly enable debug-assertions here, they are disabled by -O but we have tests
     # which exist to check that we panic on debug assertion failures.
-    time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test $TARGET_FLAG tests/{pass,panic}
+    time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test $FEATURES $TARGET_FLAG tests/{pass,panic}
   fi
   if [ -n "${MANY_SEEDS-}" ]; then
     # Run many-seeds tests. (Also tests `./miri run`.)
     time for FILE in tests/many-seeds/*.rs; do
-      ./miri run "-Zmiri-many-seeds=0..$MANY_SEEDS" $TARGET_FLAG "$FILE"
+      ./miri run $FEATURES "-Zmiri-many-seeds=0..$MANY_SEEDS" $TARGET_FLAG "$FILE"
     done
+    # Smoke-test `./miri run --dep`.
+    ./miri run $FEATURES $TARGET_FLAG --dep tests/pass-dep/getrandom.rs
   fi
   if [ -n "${TEST_BENCH-}" ]; then
     # Check that the benchmarks build and run, but only once.
     time HYPERFINE="hyperfine -w0 -r1 --show-output" ./miri bench $TARGET_FLAG --no-install
   fi
-  # Smoke-test `./miri run --dep`.
-  ./miri run $TARGET_FLAG --dep tests/pass-dep/getrandom.rs
 
   ## test-cargo-miri
   # On Windows, there is always "python", not "python3" or "python2".
@@ -149,10 +150,11 @@
   i686-unknown-linux-gnu)
     # Host
     MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
+    # Fully, but not officially, supported tier 2
+    MANY_SEEDS=16 TEST_TARGET=aarch64-linux-android run_tests
     # Partially supported targets (tier 2)
     BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator
     UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
-    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd prctl
     TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
     TEST_TARGET=thumbv7em-none-eabihf  run_tests_minimal no_std
     ;;
diff --git a/src/tools/miri/etc/rust_analyzer_helix.toml b/src/tools/miri/etc/rust_analyzer_helix.toml
index dd222c5..1015b4b 100644
--- a/src/tools/miri/etc/rust_analyzer_helix.toml
+++ b/src/tools/miri/etc/rust_analyzer_helix.toml
@@ -27,7 +27,6 @@
 overrideCommand = [
     "./miri",
     "check",
-    "--no-default-features",
     "-Zunstable-options",
     "--compile-time-deps",
     "--message-format=json",
diff --git a/src/tools/miri/etc/rust_analyzer_vscode.json b/src/tools/miri/etc/rust_analyzer_vscode.json
index 97ba212..e82c648 100644
--- a/src/tools/miri/etc/rust_analyzer_vscode.json
+++ b/src/tools/miri/etc/rust_analyzer_vscode.json
@@ -21,7 +21,6 @@
     "rust-analyzer.cargo.buildScripts.overrideCommand": [
         "./miri",
         "check",
-        "--no-default-features",
         "-Zunstable-options",
         "--compile-time-deps",
         "--message-format=json",
diff --git a/src/tools/miri/etc/rust_analyzer_zed.json b/src/tools/miri/etc/rust_analyzer_zed.json
index 7f60a93..e5ff400 100644
--- a/src/tools/miri/etc/rust_analyzer_zed.json
+++ b/src/tools/miri/etc/rust_analyzer_zed.json
@@ -30,7 +30,6 @@
                         "overrideCommand": [
                             "./miri",
                             "check",
-                            "--no-default-features",
                             "-Zunstable-options",
                             "--compile-time-deps",
                             "--message-format=json"
diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml
index 6443ecd..37fcc58 100644
--- a/src/tools/miri/genmc-sys/Cargo.toml
+++ b/src/tools/miri/genmc-sys/Cargo.toml
@@ -13,3 +13,6 @@
 cmake = "0.1.54"
 git2 = { version = "0.20.2", default-features = false, features = ["https"] }
 cxx-build = { version = "1.0.173", features = ["parallel"] }
+
+[features]
+check_only = []
diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs
index a22e334..4fc3ce9 100644
--- a/src/tools/miri/genmc-sys/build.rs
+++ b/src/tools/miri/genmc-sys/build.rs
@@ -202,6 +202,11 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
 }
 
 fn main() {
+    // For check-only builds, we don't need to do anything.
+    if cfg!(feature = "check_only") {
+        return;
+    }
+
     // Select which path to use for the GenMC repo:
     let (genmc_path, always_configure) = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH")
     {
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index 5a8bf76..86f6253 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -391,7 +391,8 @@ fn build(features: Vec<String>, flags: Vec<String>) -> Result<()> {
         Ok(())
     }
 
-    fn check(features: Vec<String>, flags: Vec<String>) -> Result<()> {
+    fn check(mut features: Vec<String>, flags: Vec<String>) -> Result<()> {
+        features.push("check_only".into());
         let e = MiriEnv::new()?;
         e.check(".", &features, &flags)?;
         e.check("cargo-miri", &[], &flags)?;
@@ -405,7 +406,8 @@ fn doc(features: Vec<String>, flags: Vec<String>) -> Result<()> {
         Ok(())
     }
 
-    fn clippy(features: Vec<String>, flags: Vec<String>) -> Result<()> {
+    fn clippy(mut features: Vec<String>, flags: Vec<String>) -> Result<()> {
+        features.push("check_only".into());
         let e = MiriEnv::new()?;
         e.clippy(".", &features, &flags)?;
         e.clippy("cargo-miri", &[], &flags)?;
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index d32b6d0..c44422d 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-7fefa09b90ca57b8a0e0e4717d672d38a0ae58b5
+f57b9e6f565a1847e83a63f3e90faa3870536c1f
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index 05d3444..fed51ed 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -12,6 +12,7 @@
 
 pub use self::address_generator::AddressGenerator;
 use self::reuse_pool::ReusePool;
+use crate::alloc::MiriAllocParams;
 use crate::concurrency::VClock;
 use crate::diagnostics::SpanDedupDiagnostic;
 use crate::*;
@@ -162,18 +163,28 @@ fn addr_from_alloc_id_uncached(
                         this.get_alloc_bytes_unchecked_raw(alloc_id)?
                     }
                 }
-                AllocKind::Function | AllocKind::VTable => {
-                    // Allocate some dummy memory to get a unique address for this function/vtable.
-                    let alloc_bytes = MiriAllocBytes::from_bytes(
-                        &[0u8; 1],
-                        Align::from_bytes(1).unwrap(),
-                        params,
-                    );
-                    let ptr = alloc_bytes.as_ptr();
-                    // Leak the underlying memory to ensure it remains unique.
-                    std::mem::forget(alloc_bytes);
-                    ptr
+                #[cfg(all(unix, feature = "native-lib"))]
+                AllocKind::Function => {
+                    if let Some(GlobalAlloc::Function { instance, .. }) =
+                        this.tcx.try_get_global_alloc(alloc_id)
+                    {
+                        let fn_sig = this.tcx.fn_sig(instance.def_id()).skip_binder().skip_binder();
+                        let fn_ptr = crate::shims::native_lib::build_libffi_closure(this, fn_sig)?;
+
+                        #[expect(
+                            clippy::as_conversions,
+                            reason = "No better way to cast a function ptr to a ptr"
+                        )]
+                        {
+                            fn_ptr as *const _
+                        }
+                    } else {
+                        dummy_alloc(params)
+                    }
                 }
+                #[cfg(not(all(unix, feature = "native-lib")))]
+                AllocKind::Function => dummy_alloc(params),
+                AllocKind::VTable => dummy_alloc(params),
                 AllocKind::TypeId | AllocKind::Dead => unreachable!(),
             };
             // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`.
@@ -205,6 +216,15 @@ fn addr_from_alloc_id_uncached(
     }
 }
 
+fn dummy_alloc(params: MiriAllocParams) -> *const u8 {
+    // Allocate some dummy memory to get a unique address for this function/vtable.
+    let alloc_bytes = MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap(), params);
+    let ptr = alloc_bytes.as_ptr();
+    // Leak the underlying memory to ensure it remains unique.
+    std::mem::forget(alloc_bytes);
+    ptr
+}
+
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     // Returns the `AllocId` that corresponds to the specified addr,
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 1f87ac6..64c7096 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -138,7 +138,6 @@ pub enum NonHaltingDiagnostic {
     NativeCallSharedMem {
         tracing: bool,
     },
-    NativeCallFnPtr,
     WeakMemoryOutdatedLoad {
         ptr: Pointer,
     },
@@ -643,11 +642,6 @@ pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
             Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
             NativeCallSharedMem { .. } =>
                 ("sharing memory with a native function".to_string(), DiagLevel::Warning),
-            NativeCallFnPtr =>
-                (
-                    "sharing a function pointer with a native function".to_string(),
-                    DiagLevel::Warning,
-                ),
             ExternTypeReborrow =>
                 ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
             GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } =>
@@ -686,8 +680,6 @@ pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
             Int2Ptr { .. } => format!("integer-to-pointer cast"),
             NativeCallSharedMem { .. } =>
                 format!("sharing memory with a native function called via FFI"),
-            NativeCallFnPtr =>
-                format!("sharing a function pointer with a native function called via FFI"),
             WeakMemoryOutdatedLoad { ptr } =>
                 format!("weak memory emulation: outdated value returned from load at {ptr}"),
             ExternTypeReborrow =>
@@ -785,11 +777,6 @@ pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
                         ),
                     ]
                 },
-            NativeCallFnPtr => {
-                vec![note!(
-                    "calling Rust functions from C is not supported and will, in the best case, crash the program"
-                )]
-            }
             ExternTypeReborrow => {
                 assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
                     matches!(
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 502cdbd..f17bd5a 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -599,6 +599,9 @@ pub struct MiriMachine<'tcx> {
     pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
     #[cfg(not(all(unix, feature = "native-lib")))]
     pub native_lib: Vec<!>,
+    /// A memory location for exchanging the current `ecx` pointer with native code.
+    #[cfg(all(unix, feature = "native-lib"))]
+    pub native_lib_ecx_interchange: &'static Cell<usize>,
 
     /// Run a garbage collector for BorTags every N basic blocks.
     pub(crate) gc_interval: u32,
@@ -790,6 +793,8 @@ pub(crate) fn new(
                     lib_file_path.clone(),
                 )
             }).collect(),
+            #[cfg(all(unix, feature = "native-lib"))]
+            native_lib_ecx_interchange: Box::leak(Box::new(Cell::new(0))),
             #[cfg(not(all(unix, feature = "native-lib")))]
             native_lib: config.native_lib.iter().map(|_| {
                 panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
@@ -1026,6 +1031,8 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
             report_progress: _,
             basic_block_count: _,
             native_lib: _,
+            #[cfg(all(unix, feature = "native-lib"))]
+            native_lib_ecx_interchange: _,
             gc_interval: _,
             since_gc: _,
             num_cpus: _,
diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs
index 6915924..7c46398 100644
--- a/src/tools/miri/src/shims/env.rs
+++ b/src/tools/miri/src/shims/env.rs
@@ -119,9 +119,8 @@ fn get_tid(&self, thread: ThreadId) -> u32 {
         let this = self.eval_context_ref();
         let index = thread.to_u32();
         let target_os = &this.tcx.sess.target.os;
-        if matches!(target_os, Os::Linux | Os::NetBsd) {
-            // On Linux, the main thread has PID == TID so we uphold this. NetBSD also appears
-            // to exhibit the same behavior, though I can't find a citation.
+        if matches!(target_os, Os::Linux | Os::Android) {
+            // On Linux, the main thread has PID == TID so we uphold this.
             this.get_pid().strict_add(index)
         } else {
             // Other platforms do not display any relationship between PID and TID.
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index e51ace2..345e16b 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -6,7 +6,7 @@
 mod files;
 mod math;
 #[cfg(all(unix, feature = "native-lib"))]
-mod native_lib;
+pub mod native_lib;
 mod unix;
 mod windows;
 mod x86;
diff --git a/src/tools/miri/src/shims/native_lib/ffi.rs b/src/tools/miri/src/shims/native_lib/ffi.rs
deleted file mode 100644
index 196f43c..0000000
--- a/src/tools/miri/src/shims/native_lib/ffi.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Support code for dealing with libffi.
-
-use libffi::low::CodePtr;
-use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType};
-
-/// Perform the actual FFI call.
-///
-/// # Safety
-///
-/// The safety invariants of the foreign function being called must be upheld (if any).
-pub unsafe fn call<R: libffi::high::CType>(fun: CodePtr, args: &mut [OwnedArg]) -> R {
-    let cif = Cif::new(args.iter_mut().map(|arg| arg.ty.take().unwrap()), R::reify().into_middle());
-    let arg_ptrs: Vec<_> = args.iter().map(|arg| ArgPtr::new(&*arg.bytes)).collect();
-    // SAFETY: Caller upholds that the function is safe to call.
-    unsafe { cif.call(fun, &arg_ptrs) }
-}
-
-/// An argument for an FFI call.
-#[derive(Debug, Clone)]
-pub struct OwnedArg {
-    /// The type descriptor for this argument.
-    ty: Option<FfiType>,
-    /// Corresponding bytes for the value.
-    bytes: Box<[u8]>,
-}
-
-impl OwnedArg {
-    /// Instantiates an argument from a type descriptor and bytes.
-    pub fn new(ty: FfiType, bytes: Box<[u8]>) -> Self {
-        Self { ty: Some(ty), bytes }
-    }
-}
diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs
index 12abe84..8a76185 100644
--- a/src/tools/miri/src/shims/native_lib/mod.rs
+++ b/src/tools/miri/src/shims/native_lib/mod.rs
@@ -1,20 +1,22 @@
 //! Implements calling functions from a native library.
 
+use std::cell::Cell;
+use std::marker::PhantomData;
 use std::ops::Deref;
+use std::os::raw::c_void;
+use std::ptr;
 use std::sync::atomic::AtomicBool;
 
 use libffi::low::CodePtr;
 use libffi::middle::Type as FfiType;
 use rustc_abi::{HasDataLayout, Size};
 use rustc_data_structures::either;
-use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
-use rustc_middle::ty::{self, FloatTy, IntTy, Ty, UintTy};
+use rustc_middle::ty::layout::TyAndLayout;
+use rustc_middle::ty::{self, Ty};
 use rustc_span::Symbol;
 use serde::{Deserialize, Serialize};
 
-use self::helpers::ToSoft;
-
-mod ffi;
+use crate::*;
 
 #[cfg_attr(
     not(all(
@@ -26,8 +28,21 @@
 )]
 pub mod trace;
 
-use self::ffi::OwnedArg;
-use crate::*;
+/// An argument for an FFI call.
+#[derive(Debug, Clone)]
+pub struct OwnedArg {
+    /// The type descriptor for this argument.
+    ty: Option<FfiType>,
+    /// Corresponding bytes for the value.
+    bytes: Box<[u8]>,
+}
+
+impl OwnedArg {
+    /// Instantiates an argument from a type descriptor and bytes.
+    pub fn new(ty: FfiType, bytes: Box<[u8]>) -> Self {
+        Self { ty: Some(ty), bytes }
+    }
+}
 
 /// The final results of an FFI trace, containing every relevant event detected
 /// by the tracer.
@@ -76,98 +91,38 @@ fn end(&self) -> usize {
 
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
-    /// Call native host function and return the output as an immediate.
-    fn call_native_with_args(
+    /// Call native host function and return the output and the memory accesses
+    /// that occurred during the call.
+    fn call_native_raw(
         &mut self,
-        link_name: Symbol,
-        dest: &MPlaceTy<'tcx>,
         fun: CodePtr,
-        libffi_args: &mut [OwnedArg],
-    ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
+        args: &mut [OwnedArg],
+        ret: (FfiType, Size),
+    ) -> InterpResult<'tcx, (Box<[u8]>, Option<MemEvents>)> {
         let this = self.eval_context_mut();
         #[cfg(target_os = "linux")]
-        let alloc = this.machine.allocator.as_ref().unwrap();
+        let alloc = this.machine.allocator.as_ref().unwrap().clone();
         #[cfg(not(target_os = "linux"))]
         // Placeholder value.
         let alloc = ();
 
-        trace::Supervisor::do_ffi(alloc, || {
-            // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
-            // as the specified primitive integer type
-            let scalar = match dest.layout.ty.kind() {
-                // ints
-                ty::Int(IntTy::I8) => {
-                    // Unsafe because of the call to native code.
-                    // Because this is calling a C function it is not necessarily sound,
-                    // but there is no way around this and we've checked as much as we can.
-                    let x = unsafe { ffi::call::<i8>(fun, libffi_args) };
-                    Scalar::from_i8(x)
-                }
-                ty::Int(IntTy::I16) => {
-                    let x = unsafe { ffi::call::<i16>(fun, libffi_args) };
-                    Scalar::from_i16(x)
-                }
-                ty::Int(IntTy::I32) => {
-                    let x = unsafe { ffi::call::<i32>(fun, libffi_args) };
-                    Scalar::from_i32(x)
-                }
-                ty::Int(IntTy::I64) => {
-                    let x = unsafe { ffi::call::<i64>(fun, libffi_args) };
-                    Scalar::from_i64(x)
-                }
-                ty::Int(IntTy::Isize) => {
-                    let x = unsafe { ffi::call::<isize>(fun, libffi_args) };
-                    Scalar::from_target_isize(x.try_into().unwrap(), this)
-                }
-                // uints
-                ty::Uint(UintTy::U8) => {
-                    let x = unsafe { ffi::call::<u8>(fun, libffi_args) };
-                    Scalar::from_u8(x)
-                }
-                ty::Uint(UintTy::U16) => {
-                    let x = unsafe { ffi::call::<u16>(fun, libffi_args) };
-                    Scalar::from_u16(x)
-                }
-                ty::Uint(UintTy::U32) => {
-                    let x = unsafe { ffi::call::<u32>(fun, libffi_args) };
-                    Scalar::from_u32(x)
-                }
-                ty::Uint(UintTy::U64) => {
-                    let x = unsafe { ffi::call::<u64>(fun, libffi_args) };
-                    Scalar::from_u64(x)
-                }
-                ty::Uint(UintTy::Usize) => {
-                    let x = unsafe { ffi::call::<usize>(fun, libffi_args) };
-                    Scalar::from_target_usize(x.try_into().unwrap(), this)
-                }
-                ty::Float(FloatTy::F32) => {
-                    let x = unsafe { ffi::call::<f32>(fun, libffi_args) };
-                    Scalar::from_f32(x.to_soft())
-                }
-                ty::Float(FloatTy::F64) => {
-                    let x = unsafe { ffi::call::<f64>(fun, libffi_args) };
-                    Scalar::from_f64(x.to_soft())
-                }
-                // Functions with no declared return type (i.e., the default return)
-                // have the output_type `Tuple([])`.
-                ty::Tuple(t_list) if (*t_list).deref().is_empty() => {
-                    unsafe { ffi::call::<()>(fun, libffi_args) };
-                    return interp_ok(ImmTy::uninit(dest.layout));
-                }
-                ty::RawPtr(ty, ..) if ty.is_sized(*this.tcx, this.typing_env()) => {
-                    let x = unsafe { ffi::call::<*const ()>(fun, libffi_args) };
-                    let ptr = StrictPointer::new(Provenance::Wildcard, Size::from_bytes(x.addr()));
-                    Scalar::from_pointer(ptr, this)
-                }
-                _ =>
-                    return Err(err_unsup_format!(
-                        "unsupported return type for native call: {:?}",
-                        link_name
-                    ))
-                    .into(),
-            };
-            interp_ok(ImmTy::from_scalar(scalar, dest.layout))
-        })
+        // Expose InterpCx for use by closure callbacks.
+        this.machine.native_lib_ecx_interchange.set(ptr::from_mut(this).expose_provenance());
+
+        let res = trace::Supervisor::do_ffi(&alloc, || {
+            use libffi::middle::{Arg, Cif, Ret};
+
+            let cif = Cif::new(args.iter_mut().map(|arg| arg.ty.take().unwrap()), ret.0);
+            let arg_ptrs: Vec<_> = args.iter().map(|arg| Arg::new(&*arg.bytes)).collect();
+            let mut ret = vec![0u8; ret.1.bytes_usize()];
+
+            unsafe { cif.call_return_into(fun, &arg_ptrs, Ret::new::<[u8]>(&mut *ret)) };
+            ret.into()
+        });
+
+        this.machine.native_lib_ecx_interchange.set(0);
+
+        res
     }
 
     /// Get the pointer to the function of the specified name in the shared object file,
@@ -381,6 +336,30 @@ fn op_to_ffi_arg(&self, v: &OpTy<'tcx>, tracing: bool) -> InterpResult<'tcx, Own
         interp_ok(OwnedArg::new(ty, bytes))
     }
 
+    fn ffi_ret_to_mem(&mut self, v: Box<[u8]>, dest: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+        let len = v.len();
+        this.write_bytes_ptr(dest.ptr(), v)?;
+        if len == 0 {
+            return interp_ok(());
+        }
+        // We have no idea which provenance these bytes have, so we reset it to wildcard.
+        let tcx = this.tcx;
+        let (alloc_id, offset, _) = this.ptr_try_get_alloc_id(dest.ptr(), 0).unwrap();
+        let alloc = this.get_alloc_raw_mut(alloc_id)?.0;
+        alloc.process_native_write(&tcx, Some(alloc_range(offset, dest.layout.size)));
+        // Run the validation that would usually be part of `return`, also to reset
+        // any provenance and padding that would not survive the return.
+        if MiriMachine::enforce_validity(this, dest.layout) {
+            this.validate_operand(
+                &dest.clone().into(),
+                MiriMachine::enforce_validity_recursively(this, dest.layout),
+                /*reset_provenance_and_padding*/ true,
+            )?;
+        }
+        interp_ok(())
+    }
+
     /// Parses an ADT to construct the matching libffi type.
     fn adt_to_ffitype(
         &self,
@@ -388,6 +367,7 @@ fn adt_to_ffitype(
         adt_def: ty::AdtDef<'tcx>,
         args: &'tcx ty::List<ty::GenericArg<'tcx>>,
     ) -> InterpResult<'tcx, FfiType> {
+        let this = self.eval_context_ref();
         // TODO: unions, etc.
         if !adt_def.is_struct() {
             throw_unsup_format!("passing an enum or union over FFI: {orig_ty}");
@@ -397,7 +377,6 @@ fn adt_to_ffitype(
             throw_unsup_format!("passing a non-#[repr(C)] {} over FFI: {orig_ty}", adt_def.descr())
         }
 
-        let this = self.eval_context_ref();
         let mut fields = vec![];
         for field in &adt_def.non_enum_variant().fields {
             let layout = this.layout_of(field.ty(*this.tcx, args))?;
@@ -429,21 +408,92 @@ fn ty_to_ffitype(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, FfiType
                 Primitive::Float(Float::F32) => FfiType::f32(),
                 Primitive::Float(Float::F64) => FfiType::f64(),
                 Primitive::Pointer(AddressSpace::ZERO) => FfiType::pointer(),
-                _ =>
-                    throw_unsup_format!(
-                        "unsupported scalar argument type for native call: {}",
-                        layout.ty
-                    ),
+                _ => throw_unsup_format!("unsupported scalar type for native call: {}", layout.ty),
             });
         }
         interp_ok(match layout.ty.kind() {
             // Scalar types have already been handled above.
             ty::Adt(adt_def, args) => self.adt_to_ffitype(layout.ty, *adt_def, args)?,
-            _ => throw_unsup_format!("unsupported argument type for native call: {}", layout.ty),
+            // Rust uses `()` as return type for `void` function, which becomes `Tuple([])`.
+            ty::Tuple(t_list) if t_list.len() == 0 => FfiType::void(),
+            _ => {
+                throw_unsup_format!("unsupported type for native call: {}", layout.ty)
+            }
         })
     }
 }
 
+/// The data passed to the closure shim function used to intercept function pointer calls from
+/// native code.
+struct LibffiClosureData<'tcx> {
+    ecx_interchange: &'static Cell<usize>,
+    marker: PhantomData<MiriInterpCx<'tcx>>,
+}
+
+/// This function sets up a new libffi closure to intercept
+/// calls to rust code via function pointers passed to native code.
+///
+/// Calling this function leaks the data passed into the libffi closure as
+/// these need to be available until the execution terminates as the native
+/// code side could store a function pointer and only call it at a later point.
+pub fn build_libffi_closure<'tcx, 'this>(
+    this: &'this MiriInterpCx<'tcx>,
+    fn_sig: rustc_middle::ty::FnSig<'tcx>,
+) -> InterpResult<'tcx, unsafe extern "C" fn()> {
+    // Compute argument and return types in libffi representation.
+    let mut args = Vec::new();
+    for input in fn_sig.inputs().iter() {
+        let layout = this.layout_of(*input)?;
+        let ty = this.ty_to_ffitype(layout)?;
+        args.push(ty);
+    }
+    let res_type = fn_sig.output();
+    let res_type = {
+        let layout = this.layout_of(res_type)?;
+        this.ty_to_ffitype(layout)?
+    };
+
+    // Build the actual closure.
+    let closure_builder = libffi::middle::Builder::new().args(args).res(res_type);
+    let data = LibffiClosureData {
+        ecx_interchange: this.machine.native_lib_ecx_interchange,
+        marker: PhantomData,
+    };
+    let data = Box::leak(Box::new(data));
+    let closure = closure_builder.into_closure(libffi_closure_callback, data);
+    let closure = Box::leak(Box::new(closure));
+
+    // The actual argument/return type doesn't matter.
+    let fn_ptr = unsafe { closure.instantiate_code_ptr::<unsafe extern "C" fn()>() };
+    // Libffi returns a **reference** to a function ptr here.
+    // Therefore we need to dereference the reference to get the actual function pointer.
+    interp_ok(*fn_ptr)
+}
+
+/// A shim function to intercept calls back from native code into the interpreter
+/// via function pointers passed to the native code.
+///
+/// For now this shim only reports that such constructs are not supported by miri.
+/// As future improvement we might continue execution in the interpreter here.
+unsafe extern "C" fn libffi_closure_callback<'tcx>(
+    _cif: &libffi::low::ffi_cif,
+    _result: &mut c_void,
+    _args: *const *const c_void,
+    data: &LibffiClosureData<'tcx>,
+) {
+    let ecx = unsafe {
+        ptr::with_exposed_provenance_mut::<MiriInterpCx<'tcx>>(data.ecx_interchange.get())
+            .as_mut()
+            .expect("libffi closure called while no FFI call is active")
+    };
+    let err = err_unsup_format!("calling a function pointer through the FFI boundary");
+
+    crate::diagnostics::report_result(ecx, err.into());
+    // We abort the execution at this point as we cannot return the
+    // expected value here.
+    std::process::exit(1);
+}
+
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Call the native host function, with supplied arguments.
@@ -451,6 +501,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// a native form (through `libffi` call).
     /// Then, convert the return value from the native form into something that
     /// can be stored in Miri's internal memory.
+    ///
+    /// Returns `true` if a call has been made, `false` if no functions of this name was found.
     fn call_native_fn(
         &mut self,
         link_name: Symbol,
@@ -472,18 +524,12 @@ fn call_native_fn(
         for arg in args.iter() {
             libffi_args.push(this.op_to_ffi_arg(arg, tracing)?);
         }
+        let ret_ty = this.ty_to_ffitype(dest.layout)?;
 
         // Prepare all exposed memory (both previously exposed, and just newly exposed since a
         // pointer was passed as argument). Uninitialised memory is left as-is, but any data
         // exposed this way is garbage anyway.
         this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| {
-            if matches!(info.kind, AllocKind::Function) {
-                static DEDUP: AtomicBool = AtomicBool::new(false);
-                if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) {
-                    // Newly set, so first time we get here.
-                    this.emit_diagnostic(NonHaltingDiagnostic::NativeCallFnPtr);
-                }
-            }
             // If there is no data behind this pointer, skip this.
             if !matches!(info.kind, AllocKind::LiveData) {
                 return interp_ok(());
@@ -521,15 +567,13 @@ fn call_native_fn(
             interp_ok(())
         })?;
 
-        // Call the function and store output, depending on return type in the function signature.
+        // Call the function and store its output.
         let (ret, maybe_memevents) =
-            this.call_native_with_args(link_name, dest, code_ptr, &mut libffi_args)?;
-
+            this.call_native_raw(code_ptr, &mut libffi_args, (ret_ty, dest.layout.size))?;
         if tracing {
             this.tracing_apply_accesses(maybe_memevents.unwrap())?;
         }
-
-        this.write_immediate(*ret, dest)?;
+        this.ffi_ret_to_mem(ret, dest)?;
         interp_ok(true)
     }
 }
diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs
index 795ad4a..021ec2e 100644
--- a/src/tools/miri/src/shims/native_lib/trace/child.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/child.rs
@@ -5,7 +5,7 @@
 use ipc_channel::ipc;
 use nix::sys::{mman, ptrace, signal};
 use nix::unistd;
-use rustc_const_eval::interpret::InterpResult;
+use rustc_const_eval::interpret::{InterpResult, interp_ok};
 
 use super::CALLBACK_STACK_SIZE;
 use super::messages::{Confirmation, StartFfiInfo, TraceRequest};
@@ -58,16 +58,16 @@ unsafe fn protect_pages(
     /// Performs an arbitrary FFI call, enabling tracing from the supervisor.
     /// As this locks the supervisor via a mutex, no other threads may enter FFI
     /// until this function returns.
-    pub fn do_ffi<'tcx>(
+    pub fn do_ffi<'tcx, T>(
         alloc: &Rc<RefCell<IsolatedAlloc>>,
-        f: impl FnOnce() -> InterpResult<'tcx, crate::ImmTy<'tcx>>,
-    ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
+        f: impl FnOnce() -> T,
+    ) -> InterpResult<'tcx, (T, Option<MemEvents>)> {
         let mut sv_guard = SUPERVISOR.lock().unwrap();
         // If the supervisor is not initialised for whatever reason, fast-return.
         // As a side-effect, even on platforms where ptracing
         // is not implemented, we enforce that only one FFI call
         // happens at a time.
-        let Some(sv) = sv_guard.as_mut() else { return f().map(|v| (v, None)) };
+        let Some(sv) = sv_guard.as_mut() else { return interp_ok((f(), None)) };
 
         // Get pointers to all the pages the supervisor must allow accesses in
         // and prepare the callback stack.
@@ -147,7 +147,7 @@ pub fn do_ffi<'tcx>(
             })
             .ok();
 
-        res.map(|v| (v, events))
+        interp_ok((res, events))
     }
 }
 
diff --git a/src/tools/miri/src/shims/native_lib/trace/stub.rs b/src/tools/miri/src/shims/native_lib/trace/stub.rs
index 22787a6..a3f6c61 100644
--- a/src/tools/miri/src/shims/native_lib/trace/stub.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/stub.rs
@@ -1,4 +1,4 @@
-use rustc_const_eval::interpret::InterpResult;
+use rustc_const_eval::interpret::{InterpResult, interp_ok};
 
 static SUPERVISOR: std::sync::Mutex<()> = std::sync::Mutex::new(());
 
@@ -13,13 +13,13 @@ pub fn is_enabled() -> bool {
         false
     }
 
-    pub fn do_ffi<'tcx, T>(
+    pub fn do_ffi<'tcx, T, U>(
         _: T,
-        f: impl FnOnce() -> InterpResult<'tcx, crate::ImmTy<'tcx>>,
-    ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<super::MemEvents>)> {
+        f: impl FnOnce() -> U,
+    ) -> InterpResult<'tcx, (U, Option<super::MemEvents>)> {
         // We acquire the lock to ensure that no two FFI calls run concurrently.
         let _g = SUPERVISOR.lock().unwrap();
-        f().map(|v| (v, None))
+        interp_ok((f(), None))
     }
 }
 
diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs
index 6cb0d22..2b290b6 100644
--- a/src/tools/miri/src/shims/unix/android/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -8,6 +8,7 @@
 use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
 use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
 use crate::shims::unix::linux_like::syscall::syscall;
+use crate::shims::unix::*;
 use crate::*;
 
 pub fn is_dyn_sym(name: &str) -> bool {
@@ -25,6 +26,74 @@ fn emulate_foreign_item_inner(
     ) -> InterpResult<'tcx, EmulateItemResult> {
         let this = self.eval_context_mut();
         match link_name.as_str() {
+            // File related shims
+            "stat" => {
+                let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let result = this.stat(path, buf)?;
+                this.write_scalar(result, dest)?;
+            }
+            "lstat" => {
+                let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let result = this.lstat(path, buf)?;
+                this.write_scalar(result, dest)?;
+            }
+            "readdir" => {
+                let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let result = this.readdir64("dirent", dirp)?;
+                this.write_scalar(result, dest)?;
+            }
+            "pread64" => {
+                let [fd, buf, count, offset] = this.check_shim_sig(
+                    shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
+                    link_name,
+                    abi,
+                    args,
+                )?;
+                let fd = this.read_scalar(fd)?.to_i32()?;
+                let buf = this.read_pointer(buf)?;
+                let count = this.read_target_usize(count)?;
+                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
+                this.read(fd, buf, count, Some(offset), dest)?;
+            }
+            "pwrite64" => {
+                let [fd, buf, n, offset] = this.check_shim_sig(
+                    shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
+                    link_name,
+                    abi,
+                    args,
+                )?;
+                let fd = this.read_scalar(fd)?.to_i32()?;
+                let buf = this.read_pointer(buf)?;
+                let count = this.read_target_usize(n)?;
+                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
+                trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
+                this.write(fd, buf, count, Some(offset), dest)?;
+            }
+            "lseek64" => {
+                let [fd, offset, whence] = this.check_shim_sig(
+                    shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
+                    link_name,
+                    abi,
+                    args,
+                )?;
+                let fd = this.read_scalar(fd)?.to_i32()?;
+                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
+                let whence = this.read_scalar(whence)?.to_i32()?;
+                this.lseek64(fd, offset, whence, dest)?;
+            }
+            "ftruncate64" => {
+                let [fd, length] = this.check_shim_sig(
+                    shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32),
+                    link_name,
+                    abi,
+                    args,
+                )?;
+                let fd = this.read_scalar(fd)?.to_i32()?;
+                let length = this.read_scalar(length)?.to_int(length.layout.size)?;
+                let result = this.ftruncate64(fd, length)?;
+                this.write_scalar(result, dest)?;
+            }
+
             // epoll, eventfd
             "epoll_create1" => {
                 let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 64b8376..8eacdc3 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -510,7 +510,7 @@ fn emulate_foreign_item_inner(
             "pipe2" => {
                 // Currently this function does not exist on all Unixes, e.g. on macOS.
                 this.check_target_os(
-                    &[Os::Linux, Os::FreeBsd, Os::Solaris, Os::Illumos],
+                    &[Os::Linux, Os::Android, Os::FreeBsd, Os::Solaris, Os::Illumos],
                     link_name,
                 )?;
                 let [pipefd, flags] = this.check_shim_sig(
diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
index c48301c..fb2d3f7 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -140,12 +140,12 @@ fn emulate_foreign_item_inner(
             // since freebsd 12 the former form can be expected.
             "stat" | "stat@FBSD_1.0" => {
                 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.macos_fbsd_solarish_stat(path, buf)?;
+                let result = this.stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" | "lstat@FBSD_1.0" => {
                 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.macos_fbsd_solarish_lstat(path, buf)?;
+                let result = this.lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "fstat@FBSD_1.0" => {
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index e17456c..f43fd3f 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -527,15 +527,13 @@ fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
         interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
     }
 
-    fn macos_fbsd_solarish_stat(
-        &mut self,
-        path_op: &OpTy<'tcx>,
-        buf_op: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, Scalar> {
+    fn stat(&mut self, path_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos)
-        {
+        if !matches!(
+            &this.tcx.sess.target.os,
+            Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Android
+        ) {
             panic!("`macos_fbsd_solaris_stat` should not be called on {}", this.tcx.sess.target.os);
         }
 
@@ -558,15 +556,13 @@ fn macos_fbsd_solarish_stat(
     }
 
     // `lstat` is used to get symlink metadata.
-    fn macos_fbsd_solarish_lstat(
-        &mut self,
-        path_op: &OpTy<'tcx>,
-        buf_op: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, Scalar> {
+    fn lstat(&mut self, path_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos)
-        {
+        if !matches!(
+            &this.tcx.sess.target.os,
+            Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Android
+        ) {
             panic!(
                 "`macos_fbsd_solaris_lstat` should not be called on {}",
                 this.tcx.sess.target.os
@@ -595,7 +591,7 @@ fn fstat(&mut self, fd_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>) -> InterpResult<'tc
 
         if !matches!(
             &this.tcx.sess.target.os,
-            Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux
+            Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux | Os::Android
         ) {
             panic!("`fstat` should not be called on {}", this.tcx.sess.target.os);
         }
@@ -906,9 +902,11 @@ fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
     fn readdir64(&mut self, dirent_type: &str, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&this.tcx.sess.target.os, Os::Linux | Os::Solaris | Os::Illumos | Os::FreeBsd)
-        {
-            panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
+        if !matches!(
+            &this.tcx.sess.target.os,
+            Os::Linux | Os::Android | Os::Solaris | Os::Illumos | Os::FreeBsd
+        ) {
+            panic!("`readdir64` should not be called on {}", this.tcx.sess.target.os);
         }
 
         let dirp = this.read_target_usize(dirp_op)?;
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index dd7b95b..f798f64 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -48,12 +48,12 @@ fn emulate_foreign_item_inner(
             }
             "stat" | "stat$INODE64" => {
                 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.macos_fbsd_solarish_stat(path, buf)?;
+                let result = this.stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" | "lstat$INODE64" => {
                 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.macos_fbsd_solarish_lstat(path, buf)?;
+                let result = this.lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "fstat$INODE64" => {
diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
index ae72308..fa8c86b 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -92,12 +92,12 @@ fn emulate_foreign_item_inner(
             // File related shims
             "stat" => {
                 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.macos_fbsd_solarish_stat(path, buf)?;
+                let result = this.stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" => {
                 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.macos_fbsd_solarish_lstat(path, buf)?;
+                let result = this.lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "readdir" => {
diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs
index a320ac1..cc371b4 100644
--- a/src/tools/miri/src/shims/unix/unnamed_socket.rs
+++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs
@@ -459,7 +459,7 @@ fn socketpair(
 
         // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
         // if there is anything left at the end, that's an unsupported flag.
-        if this.tcx.sess.target.os == Os::Linux {
+        if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android) {
             // SOCK_NONBLOCK only exists on Linux.
             let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
             let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
diff --git a/src/tools/miri/src/shims/x86/avx512.rs b/src/tools/miri/src/shims/x86/avx512.rs
index a886f56..0466ba1 100644
--- a/src/tools/miri/src/shims/x86/avx512.rs
+++ b/src/tools/miri/src/shims/x86/avx512.rs
@@ -109,8 +109,66 @@ fn emulate_x86_avx512_intrinsic(
 
                 pshufb(this, left, right, dest)?;
             }
+
+            // Used to implement the _mm512_dpbusd_epi32 function.
+            "vpdpbusd.512" | "vpdpbusd.256" | "vpdpbusd.128" => {
+                this.expect_target_feature_for_intrinsic(link_name, "avx512vnni")?;
+                if matches!(unprefixed_name, "vpdpbusd.128" | "vpdpbusd.256") {
+                    this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
+                }
+
+                let [src, a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+
+                vpdpbusd(this, src, a, b, dest)?;
+            }
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
         interp_ok(EmulateItemResult::NeedsReturn)
     }
 }
+
+/// Multiply groups of 4 adjacent pairs of unsigned 8-bit integers in `a` with corresponding signed
+/// 8-bit integers in `b`, producing 4 intermediate signed 16-bit results. Sum these 4 results with
+/// the corresponding 32-bit integer in `src` (using wrapping arighmetic), and store the packed
+/// 32-bit results in `dst`.
+///
+/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_dpbusd_epi32>
+/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_dpbusd_epi32>
+/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_dpbusd_epi32>
+fn vpdpbusd<'tcx>(
+    ecx: &mut crate::MiriInterpCx<'tcx>,
+    src: &OpTy<'tcx>,
+    a: &OpTy<'tcx>,
+    b: &OpTy<'tcx>,
+    dest: &MPlaceTy<'tcx>,
+) -> InterpResult<'tcx, ()> {
+    let (src, src_len) = ecx.project_to_simd(src)?;
+    let (a, a_len) = ecx.project_to_simd(a)?;
+    let (b, b_len) = ecx.project_to_simd(b)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
+
+    // fn vpdpbusd(src: i32x16, a: i32x16, b: i32x16) -> i32x16;
+    // fn vpdpbusd256(src: i32x8, a: i32x8, b: i32x8) -> i32x8;
+    // fn vpdpbusd128(src: i32x4, a: i32x4, b: i32x4) -> i32x4;
+    assert_eq!(dest_len, src_len);
+    assert_eq!(dest_len, a_len);
+    assert_eq!(dest_len, b_len);
+
+    for i in 0..dest_len {
+        let src = ecx.read_scalar(&ecx.project_index(&src, i)?)?.to_i32()?;
+        let a = ecx.read_scalar(&ecx.project_index(&a, i)?)?.to_u32()?;
+        let b = ecx.read_scalar(&ecx.project_index(&b, i)?)?.to_u32()?;
+        let dest = ecx.project_index(&dest, i)?;
+
+        let zipped = a.to_le_bytes().into_iter().zip(b.to_le_bytes());
+        let intermediate_sum: i32 = zipped
+            .map(|(a, b)| i32::from(a).strict_mul(i32::from(b.cast_signed())))
+            .fold(0, |x, y| x.strict_add(y));
+
+        // Use `wrapping_add` because `src` is an arbitrary i32 and the addition can overflow.
+        let res = Scalar::from_i32(intermediate_sum.wrapping_add(src));
+        ecx.write_scalar(res, &dest)?;
+    }
+
+    interp_ok(())
+}
diff --git a/src/tools/miri/tests/native-lib/aggregate_arguments.c b/src/tools/miri/tests/native-lib/aggregate_arguments.c
index 8ad687f..e315642 100644
--- a/src/tools/miri/tests/native-lib/aggregate_arguments.c
+++ b/src/tools/miri/tests/native-lib/aggregate_arguments.c
@@ -24,6 +24,14 @@
   return pass_me.value + pass_me.other_value;
 }
 
+/* Test: test_return_struct */
+EXPORT PassMe return_struct(int32_t value, int64_t other_value) {
+  struct PassMe ret;
+  ret.value = value;
+  ret.other_value = other_value;
+  return ret;
+}
+
 /* Test: test_pass_struct_complex */
 
 typedef struct Part1 {
diff --git a/src/tools/miri/tests/native-lib/fail/call_function_ptr.notrace.stderr b/src/tools/miri/tests/native-lib/fail/call_function_ptr.notrace.stderr
new file mode 100644
index 0000000..faabba9
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/call_function_ptr.notrace.stderr
@@ -0,0 +1,31 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/fail/call_function_ptr.rs:LL:CC
+   |
+LL |         call_fn_ptr(Some(nop));
+   |         ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
+   = help: in particular, Miri assumes that the native call initializes all memory it has access to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = note: stack backtrace:
+           0: pass_fn_ptr
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+           1: main
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+
+error: unsupported operation: calling a function pointer through the FFI boundary
+  --> tests/native-lib/fail/call_function_ptr.rs:LL:CC
+   |
+LL |         call_fn_ptr(Some(nop));
+   |         ^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here
+   |
+   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
+   = note: stack backtrace:
+           0: pass_fn_ptr
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+           1: main
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
diff --git a/src/tools/miri/tests/native-lib/fail/call_function_ptr.rs b/src/tools/miri/tests/native-lib/fail/call_function_ptr.rs
new file mode 100644
index 0000000..b68c4d4
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/call_function_ptr.rs
@@ -0,0 +1,21 @@
+//@revisions: trace notrace
+//@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
+//@[trace] compile-flags: -Zmiri-native-lib-enable-tracing
+//@compile-flags: -Zmiri-permissive-provenance
+
+fn main() {
+    pass_fn_ptr()
+}
+
+fn pass_fn_ptr() {
+    extern "C" {
+        fn call_fn_ptr(s: Option<extern "C" fn()>);
+    }
+
+    extern "C" fn nop() {}
+
+    unsafe {
+        call_fn_ptr(None); // this one is fine
+        call_fn_ptr(Some(nop)); //~ ERROR: unsupported operation: calling a function pointer through the FFI boundary
+    }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/call_function_ptr.trace.stderr b/src/tools/miri/tests/native-lib/fail/call_function_ptr.trace.stderr
new file mode 100644
index 0000000..e56a5ec
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/call_function_ptr.trace.stderr
@@ -0,0 +1,32 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/fail/call_function_ptr.rs:LL:CC
+   |
+LL |         call_fn_ptr(Some(nop));
+   |         ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis
+   = help: in particular, Miri assumes that the native call initializes all memory it has written to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here
+   = note: stack backtrace:
+           0: pass_fn_ptr
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+           1: main
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+
+error: unsupported operation: calling a function pointer through the FFI boundary
+  --> tests/native-lib/fail/call_function_ptr.rs:LL:CC
+   |
+LL |         call_fn_ptr(Some(nop));
+   |         ^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here
+   |
+   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
+   = note: stack backtrace:
+           0: pass_fn_ptr
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+           1: main
+               at tests/native-lib/fail/call_function_ptr.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
diff --git a/src/tools/miri/tests/native-lib/fail/invalid_retval.rs b/src/tools/miri/tests/native-lib/fail/invalid_retval.rs
new file mode 100644
index 0000000..4967866
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/invalid_retval.rs
@@ -0,0 +1,14 @@
+// Only works on Unix targets
+//@ignore-target: windows wasm
+//@only-on-host
+//@normalize-stderr-test: "OS `.*`" -> "$$OS"
+
+extern "C" {
+    fn u8_id(x: u8) -> bool;
+}
+
+fn main() {
+    unsafe {
+        u8_id(2); //~ ERROR: invalid value: encountered 0x02, but expected a boolean
+    }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/invalid_retval.stderr b/src/tools/miri/tests/native-lib/fail/invalid_retval.stderr
new file mode 100644
index 0000000..9db2982
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/invalid_retval.stderr
@@ -0,0 +1,13 @@
+error: Undefined Behavior: constructing invalid value: encountered 0x02, but expected a boolean
+  --> tests/native-lib/fail/invalid_retval.rs:LL:CC
+   |
+LL |         u8_id(2);
+   |         ^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
index 55acb24..730e9d8 100644
--- a/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
+++ b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
@@ -1,6 +1,7 @@
 fn main() {
     test_pass_struct();
     test_pass_struct_complex();
+    test_return_struct();
 }
 
 /// Test passing a basic struct as an argument.
@@ -20,6 +21,23 @@ struct PassMe {
     assert_eq!(unsafe { pass_struct(pass_me) }, 42 + 1337);
 }
 
+fn test_return_struct() {
+    // Exactly two fields, so that we hit the ScalarPair case.
+    #[repr(C)]
+    struct PassMe {
+        value: i32,
+        other_value: i64,
+    }
+
+    extern "C" {
+        fn return_struct(v: i32, ov: i64) -> PassMe;
+    }
+
+    let pass_me = unsafe { return_struct(1, 2) };
+    assert_eq!(pass_me.value, 1);
+    assert_eq!(pass_me.other_value, 2);
+}
+
 /// Test passing a more complex struct as an argument.
 fn test_pass_struct_complex() {
     #[repr(C)]
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr
index b6bbb43..bc2fcac 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr
@@ -14,16 +14,3 @@
            1: main
                at tests/native-lib/pass/ptr_read_access.rs:LL:CC
 
-warning: sharing a function pointer with a native function called via FFI
-  --> tests/native-lib/pass/ptr_read_access.rs:LL:CC
-   |
-LL |         pass_fn_ptr(Some(nop)); // this one is not
-   |         ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function
-   |
-   = help: calling Rust functions from C is not supported and will, in the best case, crash the program
-   = note: stack backtrace:
-           0: pass_fn_ptr
-               at tests/native-lib/pass/ptr_read_access.rs:LL:CC
-           1: main
-               at tests/native-lib/pass/ptr_read_access.rs:LL:CC
-
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr
index 0d86ea0..c7f30c1 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr
@@ -15,16 +15,3 @@
            1: main
                at tests/native-lib/pass/ptr_read_access.rs:LL:CC
 
-warning: sharing a function pointer with a native function called via FFI
-  --> tests/native-lib/pass/ptr_read_access.rs:LL:CC
-   |
-LL |         pass_fn_ptr(Some(nop)); // this one is not
-   |         ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function
-   |
-   = help: calling Rust functions from C is not supported and will, in the best case, crash the program
-   = note: stack backtrace:
-           0: pass_fn_ptr
-               at tests/native-lib/pass/ptr_read_access.rs:LL:CC
-           1: main
-               at tests/native-lib/pass/ptr_read_access.rs:LL:CC
-
diff --git a/src/tools/miri/tests/native-lib/ptr_read_access.c b/src/tools/miri/tests/native-lib/ptr_read_access.c
index 5f071ca..44ba13a 100644
--- a/src/tools/miri/tests/native-lib/ptr_read_access.c
+++ b/src/tools/miri/tests/native-lib/ptr_read_access.c
@@ -68,3 +68,10 @@
 EXPORT void pass_fn_ptr(void f(void)) {
   (void)f; // suppress unused warning
 }
+
+/* Test: function_ptrs */
+EXPORT void call_fn_ptr(void f(void)) {
+    if (f != NULL) {
+        f();
+    }
+}
diff --git a/src/tools/miri/tests/native-lib/scalar_arguments.c b/src/tools/miri/tests/native-lib/scalar_arguments.c
index 10b6244..720f198 100644
--- a/src/tools/miri/tests/native-lib/scalar_arguments.c
+++ b/src/tools/miri/tests/native-lib/scalar_arguments.c
@@ -34,6 +34,10 @@
   return x + 1.5f;
 }
 
+EXPORT uint8_t u8_id(uint8_t x) {
+  return x;
+}
+
 // To test that functions not marked with EXPORT cannot be called by Miri.
 int32_t not_exported(void) {
   return 0;
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs
index 0500ba0..52de8c7 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs
@@ -1,5 +1,6 @@
 //@ignore-target: windows # File handling is not implemented yet
 //@ignore-target: solaris # Does not have flock
+//@ignore-target: android # Does not (always?) have flock
 //@compile-flags: -Zmiri-disable-isolation
 
 use std::fs::File;
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs
index ffbcf63..db68dae 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs
@@ -5,6 +5,7 @@
 
 #[path = "../../utils/libc.rs"]
 mod libc_utils;
+use libc_utils::*;
 
 fn main() {
     test_pipe();
@@ -13,6 +14,7 @@ fn main() {
     test_pipe_array();
     #[cfg(any(
         target_os = "linux",
+        target_os = "android",
         target_os = "illumos",
         target_os = "freebsd",
         target_os = "solaris"
@@ -25,69 +27,44 @@ fn main() {
 
 fn test_pipe() {
     let mut fds = [-1, -1];
-    let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
-    assert_eq!(res, 0);
+    errno_check(unsafe { libc::pipe(fds.as_mut_ptr()) });
 
     // Read size == data available in buffer.
-    let data = "12345".as_bytes().as_ptr();
-    let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
-    assert_eq!(res, 5);
-    let mut buf3: [u8; 5] = [0; 5];
-    let res = unsafe {
-        libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t)
-    };
-    assert_eq!(res, 5);
-    assert_eq!(buf3, "12345".as_bytes());
+    let data = b"12345";
+    write_all_from_slice(fds[1], data).unwrap();
+    let buf3 = read_all_into_array::<5>(fds[0]).unwrap();
+    assert_eq!(&buf3, data);
 
     // Read size > data available in buffer.
-    let data = "123".as_bytes();
-    let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) };
-    assert_eq!(res, 3);
+    let data = b"123";
+    write_all_from_slice(fds[1], data).unwrap();
     let mut buf4: [u8; 5] = [0; 5];
-    let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) };
-    assert!(res > 0 && res <= 3);
-    let res = res as usize;
-    assert_eq!(buf4[..res], data[..res]);
-    if res < 3 {
-        // Drain the rest from the read end.
-        let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) };
-        assert!(res > 0);
-    }
+    let (part1, rest) = read_into_slice(fds[0], &mut buf4).unwrap();
+    assert_eq!(part1[..], data[..part1.len()]);
+    // Write 2 more bytes so we can exactly fill the `rest`.
+    write_all_from_slice(fds[1], b"34").unwrap();
+    read_all_into_slice(fds[0], rest).unwrap();
 }
 
 fn test_pipe_threaded() {
     let mut fds = [-1, -1];
-    let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
-    assert_eq!(res, 0);
+    errno_check(unsafe { libc::pipe(fds.as_mut_ptr()) });
 
     let thread1 = thread::spawn(move || {
-        let mut buf: [u8; 5] = [0; 5];
-        let res: i64 = unsafe {
-            libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
-                .try_into()
-                .unwrap()
-        };
-        assert_eq!(res, 5);
-        assert_eq!(buf, "abcde".as_bytes());
+        let buf = read_all_into_array::<5>(fds[0]).unwrap();
+        assert_eq!(&buf, b"abcde");
     });
     thread::yield_now();
-    let data = "abcde".as_bytes().as_ptr();
-    let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
-    assert_eq!(res, 5);
+    write_all_from_slice(fds[1], b"abcde").unwrap();
     thread1.join().unwrap();
 
     // Read and write from different direction
     let thread2 = thread::spawn(move || {
         thread::yield_now();
-        let data = "12345".as_bytes().as_ptr();
-        let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
-        assert_eq!(res, 5);
+        write_all_from_slice(fds[1], b"12345").unwrap();
     });
-    let mut buf: [u8; 5] = [0; 5];
-    let res =
-        unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
-    assert_eq!(res, 5);
-    assert_eq!(buf, "12345".as_bytes());
+    let buf = read_all_into_array::<5>(fds[0]).unwrap();
+    assert_eq!(&buf, b"12345");
     thread2.join().unwrap();
 }
 
@@ -96,26 +73,17 @@ fn test_pipe_threaded() {
 fn test_race() {
     static mut VAL: u8 = 0;
     let mut fds = [-1, -1];
-    let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
-    assert_eq!(res, 0);
+    errno_check(unsafe { libc::pipe(fds.as_mut_ptr()) });
     let thread1 = thread::spawn(move || {
-        let mut buf: [u8; 1] = [0; 1];
         // write() from the main thread will occur before the read() here
         // because preemption is disabled and the main thread yields after write().
-        let res: i32 = unsafe {
-            libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
-                .try_into()
-                .unwrap()
-        };
-        assert_eq!(res, 1);
-        assert_eq!(buf, "a".as_bytes());
+        let buf = read_all_into_array::<1>(fds[0]).unwrap();
+        assert_eq!(&buf, b"a");
         // The read above establishes a happens-before so it is now safe to access this global variable.
         unsafe { assert_eq!(VAL, 1) };
     });
     unsafe { VAL = 1 };
-    let data = "a".as_bytes().as_ptr();
-    let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 1) };
-    assert_eq!(res, 1);
+    write_all_from_slice(fds[1], b"a").unwrap();
     thread::yield_now();
     thread1.join().unwrap();
 }
@@ -133,46 +101,53 @@ fn test_pipe_array() {
 /// Test if pipe2 (including the O_NONBLOCK flag) is supported.
 #[cfg(any(
     target_os = "linux",
+    target_os = "android",
     target_os = "illumos",
     target_os = "freebsd",
     target_os = "solaris"
 ))]
 fn test_pipe2() {
     let mut fds = [-1, -1];
-    let res = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK) };
-    assert_eq!(res, 0);
+    errno_check(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK) });
 }
 
 /// Basic test for pipe fcntl's F_SETFL and F_GETFL flag.
 fn test_pipe_setfl_getfl() {
     // Initialise pipe fds.
     let mut fds = [-1, -1];
-    let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
-    assert_eq!(res, 0);
+    errno_check(unsafe { libc::pipe(fds.as_mut_ptr()) });
 
     // Both sides should either have O_RONLY or O_WRONLY.
-    let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
-    assert_eq!(res, libc::O_RDONLY);
-    let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) };
-    assert_eq!(res, libc::O_WRONLY);
+    assert_eq!(
+        errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(),
+        libc::O_RDONLY
+    );
+    assert_eq!(
+        errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(),
+        libc::O_WRONLY
+    );
 
     // Add the O_NONBLOCK flag with F_SETFL.
-    let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) };
-    assert_eq!(res, 0);
+    errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) });
 
     // Test if the O_NONBLOCK flag is successfully added.
-    let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
-    assert_eq!(res, libc::O_RDONLY | libc::O_NONBLOCK);
+    assert_eq!(
+        errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(),
+        libc::O_RDONLY | libc::O_NONBLOCK
+    );
 
     // The other side remains unchanged.
-    let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) };
-    assert_eq!(res, libc::O_WRONLY);
+    assert_eq!(
+        errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(),
+        libc::O_WRONLY
+    );
 
     // Test if O_NONBLOCK flag can be unset.
-    let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) };
-    assert_eq!(res, 0);
-    let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
-    assert_eq!(res, libc::O_RDONLY);
+    errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) });
+    assert_eq!(
+        errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(),
+        libc::O_RDONLY
+    );
 }
 
 /// Test the behaviour of F_SETFL/F_GETFL when a fd is blocking.
@@ -183,28 +158,24 @@ fn test_pipe_setfl_getfl() {
 ///    then writes to fds[1] to unblock main thread's `read`.
 fn test_pipe_fcntl_threaded() {
     let mut fds = [-1, -1];
-    let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
-    assert_eq!(res, 0);
-    let mut buf: [u8; 5] = [0; 5];
+    errno_check(unsafe { libc::pipe(fds.as_mut_ptr()) });
     let thread1 = thread::spawn(move || {
         // Add O_NONBLOCK flag while pipe is still blocked on read.
-        let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) };
-        assert_eq!(res, 0);
+        errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) });
 
         // Check the new flag value while the main thread is still blocked on fds[0].
-        let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
-        assert_eq!(res, libc::O_NONBLOCK);
+        assert_eq!(
+            errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(),
+            libc::O_NONBLOCK
+        );
 
         // The write below will unblock the `read` in main thread: even though
         // the socket is now "non-blocking", the shim needs to deal correctly
         // with threads that were blocked before the socket was made non-blocking.
-        let data = "abcde".as_bytes().as_ptr();
-        let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
-        assert_eq!(res, 5);
+        write_all_from_slice(fds[1], b"abcde").unwrap();
     });
     // The `read` below will block.
-    let res =
-        unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
+    let buf = read_all_into_array::<5>(fds[0]).unwrap();
     thread1.join().unwrap();
-    assert_eq!(res, 5);
+    assert_eq!(&buf, b"abcde");
 }
diff --git a/src/tools/miri/tests/pass-dep/shims/gettid.rs b/src/tools/miri/tests/pass-dep/shims/gettid.rs
index b7a2fa4..9d5ff0d 100644
--- a/src/tools/miri/tests/pass-dep/shims/gettid.rs
+++ b/src/tools/miri/tests/pass-dep/shims/gettid.rs
@@ -165,7 +165,7 @@ fn main() {
     // The value is not important, we only care that whatever the value is,
     // won't change from execution to execution.
     if cfg!(with_isolation) {
-        if cfg!(target_os = "linux") {
+        if cfg!(any(target_os = "linux", target_os = "android")) {
             // Linux starts the TID at the PID, which is 1000.
             assert_eq!(tid, 1000);
         } else {
@@ -174,8 +174,8 @@ fn main() {
         }
     }
 
-    // On Linux and NetBSD, the first TID is the PID.
-    #[cfg(any(target_os = "linux", target_os = "netbsd"))]
+    // On Linux, the first TID is the PID.
+    #[cfg(any(target_os = "linux", target_os = "android"))]
     assert_eq!(tid, unsafe { libc::getpid() } as u64);
 
     #[cfg(any(target_vendor = "apple", windows))]
diff --git a/src/tools/miri/tests/pass-dep/tempfile.rs b/src/tools/miri/tests/pass-dep/tempfile.rs
index a44a7e7..885190b 100644
--- a/src/tools/miri/tests/pass-dep/tempfile.rs
+++ b/src/tools/miri/tests/pass-dep/tempfile.rs
@@ -7,15 +7,8 @@
 
 /// Test that the [`tempfile`] crate is compatible with miri for UNIX hosts and targets
 fn main() {
-    test_tempfile();
-    test_tempfile_in();
-}
-
-fn test_tempfile() {
-    tempfile::tempfile().unwrap();
-}
-
-fn test_tempfile_in() {
+    // Only create a file in our own tmp folder; the "host" temp folder
+    // can be nonsensical for cross-tests.
     let dir_path = utils::tmp();
     tempfile::tempfile_in(dir_path).unwrap();
 }
diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs
index 648c90b..50b5dbf 100644
--- a/src/tools/miri/tests/pass/shims/fs.rs
+++ b/src/tools/miri/tests/pass/shims/fs.rs
@@ -37,7 +37,7 @@ fn main() {
         test_canonicalize();
         #[cfg(unix)]
         test_pread_pwrite();
-        #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+        #[cfg(not(any(target_os = "solaris", target_os = "android")))]
         test_flock();
     }
 }
@@ -399,8 +399,8 @@ fn test_pread_pwrite() {
     assert_eq!(&buf1, b"  m");
 }
 
-// This function does seem to exist on Illumos but std does not expose it there.
-#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+// The standard library does not support this operation on Solaris, Android
+#[cfg(not(any(target_os = "solaris", target_os = "android")))]
 fn test_flock() {
     let bytes = b"Hello, World!\n";
     let path = utils::prepare_with_content("miri_test_fs_flock.txt", bytes);
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 f95429d..42acb6c 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
@@ -1,6 +1,6 @@
 // We're testing x86 target specific features
 //@only-target: x86_64 i686
-//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq
+//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq,+avx512vnni
 
 #[cfg(target_arch = "x86")]
 use std::arch::x86::*;
@@ -13,12 +13,14 @@ fn main() {
     assert!(is_x86_feature_detected!("avx512vl"));
     assert!(is_x86_feature_detected!("avx512bitalg"));
     assert!(is_x86_feature_detected!("avx512vpopcntdq"));
+    assert!(is_x86_feature_detected!("avx512vnni"));
 
     unsafe {
         test_avx512();
         test_avx512bitalg();
         test_avx512vpopcntdq();
         test_avx512ternarylogic();
+        test_avx512vnni();
     }
 }
 
@@ -411,6 +413,101 @@ unsafe fn test_mm_ternarylogic_epi32() {
     test_mm_ternarylogic_epi32();
 }
 
+#[target_feature(enable = "avx512vnni")]
+unsafe fn test_avx512vnni() {
+    #[target_feature(enable = "avx512vnni")]
+    unsafe fn test_mm512_dpbusd_epi32() {
+        const SRC: [i32; 16] = [
+            1,
+            // Test that addition with the `src` element uses wrapping arithmetic.
+            i32::MAX,
+            i32::MIN,
+            0,
+            0,
+            7,
+            12345,
+            -9876,
+            0x01020304,
+            -1,
+            42,
+            0,
+            1_000_000_000,
+            -1_000_000_000,
+            17,
+            -17,
+        ];
+
+        // The `A` array must be interpreted as a sequence of unsigned 8-bit integers. Setting
+        // the high bit of a byte tests that this is implemented correctly.
+        const A: [i32; 16] = [
+            0x01010101,
+            i32::from_le_bytes([1; 4]),
+            i32::from_le_bytes([1; 4]),
+            i32::from_le_bytes([u8::MAX; 4]),
+            i32::from_le_bytes([u8::MAX; 4]),
+            0x02_80_01_FF,
+            0x00_FF_00_FF,
+            0x7F_80_FF_01,
+            0x10_20_30_40,
+            0xDE_AD_BE_EFu32 as i32,
+            0x00_00_00_FF,
+            0x12_34_56_78,
+            0xFF_00_FF_00u32 as i32,
+            0x01_02_03_04,
+            0xAA_55_AA_55u32 as i32,
+            0x11_22_33_44,
+        ];
+
+        // The `B` array must be interpreted as a sequence of signed 8-bit integers. Setting
+        // the high bit of a byte tests that this is implemented correctly.
+        const B: [i32; 16] = [
+            0x01010101,
+            i32::from_le_bytes([1; 4]),
+            i32::from_le_bytes([(-1i8).cast_unsigned(); 4]),
+            i32::from_le_bytes([i8::MAX.cast_unsigned(); 4]),
+            i32::from_le_bytes([i8::MIN.cast_unsigned(); 4]),
+            0xFF_01_80_7Fu32 as i32,
+            0x01_FF_01_FF,
+            0x80_7F_00_FFu32 as i32,
+            0x7F_01_FF_80u32 as i32,
+            0x01_02_03_04,
+            0xFF_FF_FF_FFu32 as i32,
+            0x80_00_7F_FFu32 as i32,
+            0x7F_80_7F_80u32 as i32,
+            0x40_C0_20_E0u32 as i32,
+            0x00_01_02_03,
+            0x7F_7E_80_81u32 as i32,
+        ];
+
+        const DST: [i32; 16] = [
+            5,
+            i32::MAX.wrapping_add(4),
+            i32::MIN.wrapping_add(-4),
+            129540,
+            -130560,
+            32390,
+            11835,
+            -9877,
+            16902884,
+            2093,
+            -213,
+            8498,
+            1000064770,
+            -1000000096,
+            697,
+            -8738,
+        ];
+
+        let src = _mm512_loadu_si512(SRC.as_ptr().cast::<__m512i>());
+        let a = _mm512_loadu_si512(A.as_ptr().cast::<__m512i>());
+        let b = _mm512_loadu_si512(B.as_ptr().cast::<__m512i>());
+        let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>());
+
+        assert_eq_m512i(_mm512_dpbusd_epi32(src, a, b), dst);
+    }
+    test_mm512_dpbusd_epi32();
+}
+
 #[track_caller]
 unsafe fn assert_eq_m512i(a: __m512i, b: __m512i) {
     assert_eq!(transmute::<_, [i32; 16]>(a), transmute::<_, [i32; 16]>(b))
diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs
index e42f39c..0765bac 100644
--- a/src/tools/miri/tests/utils/libc.rs
+++ b/src/tools/miri/tests/utils/libc.rs
@@ -40,17 +40,35 @@ pub unsafe fn read_all(
     return read_so_far as libc::ssize_t;
 }
 
+/// Try to fill the given slice by reading from `fd`. Error if that many bytes could not be read.
+#[track_caller]
+pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> Result<(), libc::ssize_t> {
+    let res = unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) };
+    if res >= 0 {
+        assert_eq!(res as usize, buf.len());
+        Ok(())
+    } else {
+        Err(res)
+    }
+}
+
 /// Read exactly `N` bytes from `fd`. Error if that many bytes could not be read.
 #[track_caller]
 pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> Result<[u8; N], libc::ssize_t> {
     let mut buf = [0; N];
-    let res = unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) };
-    if res >= 0 {
-        assert_eq!(res as usize, buf.len());
-        Ok(buf)
-    } else {
-        Err(res)
-    }
+    read_all_into_slice(fd, &mut buf)?;
+    Ok(buf)
+}
+
+/// Do a single read from `fd` and return the part of the buffer that was written into,
+/// and the rest.
+#[track_caller]
+pub fn read_into_slice(
+    fd: libc::c_int,
+    buf: &mut [u8],
+) -> Result<(&mut [u8], &mut [u8]), libc::ssize_t> {
+    let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) };
+    if res >= 0 { Ok(buf.split_at_mut(res as usize)) } else { Err(res) }
 }
 
 pub unsafe fn write_all(
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 48b25f2..5515dbf1 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -454,7 +454,6 @@ fn main() -> anyhow::Result<()> {
             "clippy",
             "miri",
             "rustfmt",
-            "gcc",
             "generate-copyright",
             "bootstrap",
         ] {
diff --git a/tests/codegen-llvm/gpu_offload/gpu_host.rs b/tests/codegen-llvm/gpu_offload/gpu_host.rs
index b4d1714..dcbd65b 100644
--- a/tests/codegen-llvm/gpu_offload/gpu_host.rs
+++ b/tests/codegen-llvm/gpu_offload/gpu_host.rs
@@ -1,15 +1,10 @@
-//@ compile-flags: -Zoffload=Enable -Zunstable-options -C opt-level=3  -Clto=fat
+//@ compile-flags: -Zoffload=Test -Zunstable-options -C opt-level=3  -Clto=fat
 //@ no-prefer-dynamic
-//@ needs-enzyme
+//@ needs-offload
 
 // This test is verifying that we generate __tgt_target_data_*_mapper before and after a call to the
 // kernel_1. Better documentation to what each global or variable means is available in the gpu
-// offlaod code, or the LLVM offload documentation. This code does not launch any GPU kernels yet,
-// and will be rewritten once a proper offload frontend has landed.
-//
-// We currently only handle memory transfer for specific calls to functions named `kernel_{num}`,
-// when inside of a function called main. This, too, is a temporary workaround for not having a
-// frontend.
+// offload code, or the LLVM offload documentation.
 
 #![feature(rustc_attrs)]
 #![feature(core_intrinsics)]
@@ -22,6 +17,20 @@ fn main() {
     core::hint::black_box(&x);
 }
 
+#[unsafe(no_mangle)]
+#[inline(never)]
+pub fn kernel_1(x: &mut [f32; 256]) {
+    core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], (x,))
+}
+
+#[unsafe(no_mangle)]
+#[inline(never)]
+pub fn _kernel_1(x: &mut [f32; 256]) {
+    for i in 0..256 {
+        x[i] = 21.0;
+    }
+}
+
 // CHECK: %struct.ident_t = type { i32, i32, i32, i32, ptr }
 // CHECK: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr }
 // CHECK: %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr }
@@ -36,8 +45,9 @@ fn main() {
 // CHECK: @.offloading.entry_name._kernel_1 = internal unnamed_addr constant [10 x i8] c"_kernel_1\00", section ".llvm.rodata.offloading", align 1
 // CHECK: @.offloading.entry._kernel_1 = internal constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 0, ptr @._kernel_1.region_id, ptr @.offloading.entry_name._kernel_1, i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8
 
-// CHECK: Function Attrs: nounwind
 // CHECK: declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr)
+// CHECK: declare void @__tgt_register_lib(ptr) local_unnamed_addr
+// CHECK: declare void @__tgt_unregister_lib(ptr) local_unnamed_addr
 
 // CHECK: define{{( dso_local)?}} void @main()
 // CHECK-NEXT: start:
@@ -94,17 +104,3 @@ fn main() {
 // CHECK-NEXT:   call void @__tgt_unregister_lib(ptr nonnull %EmptyDesc)
 // CHECK-NEXT:   ret void
 // CHECK-NEXT: }
-
-#[unsafe(no_mangle)]
-#[inline(never)]
-pub fn kernel_1(x: &mut [f32; 256]) {
-    core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], (x,))
-}
-
-#[unsafe(no_mangle)]
-#[inline(never)]
-pub fn _kernel_1(x: &mut [f32; 256]) {
-    for i in 0..256 {
-        x[i] = 21.0;
-    }
-}
diff --git a/tests/codegen-llvm/issues/multiple-option-or-permutations.rs b/tests/codegen-llvm/issues/multiple-option-or-permutations.rs
new file mode 100644
index 0000000..9ec4ec8
--- /dev/null
+++ b/tests/codegen-llvm/issues/multiple-option-or-permutations.rs
@@ -0,0 +1,174 @@
+// Tests output of multiple permutations of `Option::or`
+//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled
+
+#![crate_type = "lib"]
+
+use std::num::NonZero;
+
+// CHECK-LABEL: @or_match_u8
+// CHECK-SAME: (i1{{.+}}%0, i8 %1, i1{{.+}}%optb.0, i8 %optb.1)
+#[no_mangle]
+pub fn or_match_u8(opta: Option<u8>, optb: Option<u8>) -> Option<u8> {
+    // CHECK: start:
+    // CHECK-DAG: [[A_OR_B:%.+]] = select i1 %0, i8 %1, i8 %optb.1
+    // CHECK-DAG: [[IS_SOME:%.+]] = or i1 {{%0, %optb.0|%optb.0, %0}}
+    // CHECK-NEXT: [[FLAG:%.+]] = insertvalue { i1, i8 } poison, i1 [[IS_SOME]], 0
+    // CHECK-NEXT: [[R:%.+]] = insertvalue { i1, i8 } [[FLAG]], i8 [[A_OR_B]], 1
+    // CHECK: ret { i1, i8 } [[R]]
+    match opta {
+        Some(x) => Some(x),
+        None => optb,
+    }
+}
+
+// CHECK-LABEL: @or_match_alt_u8
+// CHECK-SAME: (i1{{.+}}%opta.0, i8 %opta.1, i1{{.+}}%optb.0, i8 %optb.1)
+#[no_mangle]
+pub fn or_match_alt_u8(opta: Option<u8>, optb: Option<u8>) -> Option<u8> {
+    // CHECK: start:
+    // CHECK-DAG: [[A_OR_B:%.+]] = select i1 %opta.0, i8 %opta.1, i8 %optb.1
+    // CHECK-DAG: [[IS_SOME:%.+]] = or i1 {{%opta.0, %optb.0|%optb.0, %opta.0}}
+    // CHECK-NEXT: [[FLAG:%.+]] = insertvalue { i1, i8 } poison, i1 [[IS_SOME]], 0
+    // CHECK-NEXT: [[R:%.+]] = insertvalue { i1, i8 } [[FLAG]], i8 [[A_OR_B]], 1
+    // CHECK: ret { i1, i8 } [[R]]
+    match opta {
+        Some(_) => opta,
+        None => optb,
+    }
+}
+
+// CHECK-LABEL: @option_or_u8
+// CHECK-SAME: (i1{{.+}}%opta.0, i8 %opta.1, i1{{.+}}%optb.0, i8 %optb.1)
+#[no_mangle]
+pub fn option_or_u8(opta: Option<u8>, optb: Option<u8>) -> Option<u8> {
+    // CHECK: start:
+    // CHECK-DAG: [[A_OR_B:%.+]] = select i1 %opta.0, i8 %opta.1, i8 %optb.1
+    // CHECK-DAG: [[IS_SOME:%.+]] = or i1 {{%opta.0, %optb.0|%optb.0, %opta.0}}
+    // CHECK-NEXT: [[FLAG:%.+]] = insertvalue { i1, i8 } poison, i1 [[IS_SOME]], 0
+    // CHECK-NEXT: [[R:%.+]] = insertvalue { i1, i8 } [[FLAG]], i8 [[A_OR_B]], 1
+    // CHECK: ret { i1, i8 } [[R]]
+    opta.or(optb)
+}
+
+// CHECK-LABEL: @if_some_u8
+// CHECK-SAME: (i1{{.+}}%opta.0, i8 %opta.1, i1{{.+}}%optb.0, i8 %optb.1)
+#[no_mangle]
+pub fn if_some_u8(opta: Option<u8>, optb: Option<u8>) -> Option<u8> {
+    // CHECK: start:
+    // CHECK-DAG: [[A_OR_B:%.+]] = select i1 %opta.0, i8 %opta.1, i8 %optb.1
+    // CHECK-DAG: [[IS_SOME:%.+]] = or i1 {{%opta.0, %optb.0|%optb.0, %opta.0}}
+    // CHECK-NEXT: [[FLAG:%.+]] = insertvalue { i1, i8 } poison, i1 [[IS_SOME]], 0
+    // CHECK-NEXT: [[R:%.+]] = insertvalue { i1, i8 } [[FLAG]], i8 [[A_OR_B]], 1
+    // CHECK: ret { i1, i8 } [[R]]
+    if opta.is_some() { opta } else { optb }
+}
+
+// Tests a case where an input is a type that is represented as `BackendRepr::Memory`
+
+// CHECK-LABEL: @or_match_slice_u8
+// CHECK-SAME: (i16 %0, i16 %1)
+#[no_mangle]
+pub fn or_match_slice_u8(opta: Option<[u8; 1]>, optb: Option<[u8; 1]>) -> Option<[u8; 1]> {
+    // CHECK: start:
+    // CHECK-NEXT: [[SOME_A:%.+]] = trunc i16 %0 to i1
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[SOME_A]], i16 %0, i16 %1
+    // CHECK: ret i16 [[R]]
+    match opta {
+        Some(x) => Some(x),
+        None => optb,
+    }
+}
+
+// CHECK-LABEL: @or_match_slice_alt_u8
+// CHECK-SAME: (i16 %0, i16 %1)
+#[no_mangle]
+pub fn or_match_slice_alt_u8(opta: Option<[u8; 1]>, optb: Option<[u8; 1]>) -> Option<[u8; 1]> {
+    // CHECK: start:
+    // CHECK-NEXT: [[SOME_A:%.+]] = trunc i16 %0 to i1
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[SOME_A]], i16 %0, i16 %1
+    // CHECK: ret i16 [[R]]
+    match opta {
+        Some(_) => opta,
+        None => optb,
+    }
+}
+
+// CHECK-LABEL: @option_or_slice_u8
+// CHECK-SAME: (i16 %0, i16 %1)
+#[no_mangle]
+pub fn option_or_slice_u8(opta: Option<[u8; 1]>, optb: Option<[u8; 1]>) -> Option<[u8; 1]> {
+    // CHECK: start:
+    // CHECK-NEXT: [[SOME_A:%.+]] = trunc i16 %0 to i1
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[SOME_A]], i16 %0, i16 %1
+    // CHECK: ret i16 [[R]]
+    opta.or(optb)
+}
+
+// CHECK-LABEL: @if_some_slice_u8
+// CHECK-SAME: (i16 %0, i16 %1)
+#[no_mangle]
+pub fn if_some_slice_u8(opta: Option<[u8; 1]>, optb: Option<[u8; 1]>) -> Option<[u8; 1]> {
+    // CHECK: start:
+    // CHECK-NEXT: [[SOME_A:%.+]] = trunc i16 %0 to i1
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[SOME_A]], i16 %0, i16 %1
+    // CHECK: ret i16 [[R]]
+    if opta.is_some() { opta } else { optb }
+}
+
+// Test a niche optimization case of `NonZero<u8>`
+
+// CHECK-LABEL: @or_match_nz_u8
+// CHECK-SAME: (i8{{.+}}%0, i8{{.+}}%optb)
+#[no_mangle]
+pub fn or_match_nz_u8(opta: Option<NonZero<u8>>, optb: Option<NonZero<u8>>) -> Option<NonZero<u8>> {
+    // CHECK: start:
+    // CHECK-NEXT: [[NOT_A:%.+]] = icmp eq i8 %0, 0
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[NOT_A]], i8 %optb, i8 %0
+    // CHECK: ret i8 [[R]]
+    match opta {
+        Some(x) => Some(x),
+        None => optb,
+    }
+}
+
+// CHECK-LABEL: @or_match_alt_nz_u8
+// CHECK-SAME: (i8{{.+}}%opta, i8{{.+}}%optb)
+#[no_mangle]
+pub fn or_match_alt_nz_u8(
+    opta: Option<NonZero<u8>>,
+    optb: Option<NonZero<u8>>,
+) -> Option<NonZero<u8>> {
+    // CHECK: start:
+    // CHECK-NEXT: [[NOT_A:%.+]] = icmp eq i8 %opta, 0
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[NOT_A]], i8 %optb, i8 %opta
+    // CHECK: ret i8 [[R]]
+    match opta {
+        Some(_) => opta,
+        None => optb,
+    }
+}
+
+// CHECK-LABEL: @option_or_nz_u8
+// CHECK-SAME: (i8{{.+}}%opta, i8{{.+}}%optb)
+#[no_mangle]
+pub fn option_or_nz_u8(
+    opta: Option<NonZero<u8>>,
+    optb: Option<NonZero<u8>>,
+) -> Option<NonZero<u8>> {
+    // CHECK: start:
+    // CHECK-NEXT: [[NOT_A:%.+]] = icmp eq i8 %opta, 0
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[NOT_A]], i8 %optb, i8 %opta
+    // CHECK: ret i8 [[R]]
+    opta.or(optb)
+}
+
+// CHECK-LABEL: @if_some_nz_u8
+// CHECK-SAME: (i8{{.+}}%opta, i8{{.+}}%optb)
+#[no_mangle]
+pub fn if_some_nz_u8(opta: Option<NonZero<u8>>, optb: Option<NonZero<u8>>) -> Option<NonZero<u8>> {
+    // CHECK: start:
+    // CHECK-NEXT: [[NOT_A:%.+]] = icmp eq i8 %opta, 0
+    // CHECK-NEXT: [[R:%.+]] = select i1 [[NOT_A]], i8 %optb, i8 %opta
+    // CHECK: ret i8 [[R]]
+    if opta.is_some() { opta } else { optb }
+}
diff --git a/tests/crashes/136379.rs b/tests/crashes/136379.rs
deleted file mode 100644
index 077b373..0000000
--- a/tests/crashes/136379.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ known-bug: #136379
-#![feature(min_generic_const_args)]
-pub struct S();
-
-impl S {
-    pub fn f() -> [u8; S] {
-        []
-    }
-}
-
-pub fn main() {}
diff --git a/tests/crashes/138132.rs b/tests/crashes/138132.rs
deleted file mode 100644
index 3e31117c..0000000
--- a/tests/crashes/138132.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ known-bug: #138132
-#![feature(min_generic_const_args)]
-struct b(Box<[u8; c]>);
-impl b {
-    fn d(self) {
-        self.0.e()
-    }
-}
-struct c<'a>(&'a u8);
-fn main() {}
diff --git a/tests/debuginfo/macro-stepping.rs b/tests/debuginfo/macro-stepping.rs
index 0dff383..ba3a445 100644
--- a/tests/debuginfo/macro-stepping.rs
+++ b/tests/debuginfo/macro-stepping.rs
@@ -14,8 +14,10 @@
 #[macro_use]
 extern crate macro_stepping; // exports new_scope!()
 
-//@ compile-flags:-g -Zmir-enable-passes=-SingleUseConsts
-// SingleUseConsts shouldn't need to be disabled, see #128945
+//@ compile-flags: -g
+// FIXME(#128945): SingleUseConsts shouldn't need to be disabled.
+//@ revisions: default-mir-passes no-SingleUseConsts-mir-pass
+//@ [no-SingleUseConsts-mir-pass] compile-flags: -Zmir-enable-passes=-SingleUseConsts
 
 // === GDB TESTS ===================================================================================
 
@@ -48,7 +50,7 @@
 //@ gdb-check:[...]#inc-loc2[...]
 //@ gdb-command:next
 //@ gdb-command:frame
-//@ gdb-check:[...]#inc-loc3[...]
+//@ [no-SingleUseConsts-mir-pass] gdb-check:[...]#inc-loc3[...]
 
 // === LLDB TESTS ==================================================================================
 
diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs
index 7f8b303..50914fb 100644
--- a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs
+++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs
@@ -5,6 +5,7 @@
     min_generic_const_args,
     adt_const_params,
     unsized_const_params,
+    generic_const_parameter_types,
 )]
 #![allow(incomplete_features)]
 
diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr
index 7686871..fd49e15 100644
--- a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr
+++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr
@@ -1,11 +1,11 @@
 error: higher-ranked subtype error
-  --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:21:13
+  --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:22:13
    |
 LL |         K = const { () }
    |             ^^^^^^^^^^^^
 
 error: higher-ranked subtype error
-  --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:21:13
+  --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:22:13
    |
 LL |         K = const { () }
    |             ^^^^^^^^^^^^
diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs
index d3975bc..54e199d 100644
--- a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs
+++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs
@@ -8,6 +8,7 @@
     min_generic_const_args,
     adt_const_params,
     unsized_const_params,
+    generic_const_parameter_types,
 )]
 #![allow(incomplete_features)]
 
diff --git a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs
index 2571af5..7f9a04b 100644
--- a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs
+++ b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs
@@ -1,6 +1,11 @@
 // Detect and reject escaping late-bound generic params in
 // the type of assoc consts used in an equality bound.
-#![feature(associated_const_equality, min_generic_const_args, unsized_const_params)]
+#![feature(
+    associated_const_equality,
+    min_generic_const_args,
+    unsized_const_params,
+    generic_const_parameter_types,
+)]
 #![allow(incomplete_features)]
 
 trait Trait<'a> {
diff --git a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr
index 4430444..e22d9bf 100644
--- a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr
+++ b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr
@@ -1,5 +1,5 @@
 error: the type of the associated constant `K` cannot capture late-bound generic parameters
-  --> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:11:35
+  --> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:16:35
    |
 LL | fn take(_: impl for<'r> Trait<'r, K = const { &() }>) {}
    |                     --            ^ its type cannot capture the late-bound lifetime parameter `'r`
diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs
index 242ad38..ea2f60c 100644
--- a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs
+++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs
@@ -5,6 +5,7 @@
     min_generic_const_args,
     adt_const_params,
     unsized_const_params,
+    generic_const_parameter_types,
 )]
 #![allow(incomplete_features)]
 
diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr
index b742e68..ae6a35b 100644
--- a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr
+++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr
@@ -1,5 +1,5 @@
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:22:29
+  --> $DIR/assoc-const-eq-param-in-ty.rs:23:29
    |
 LL | fn take0<'r, A: 'r + ConstParamTy_, const Q: usize>(
    |          -- the lifetime parameter `'r` is defined here
@@ -10,7 +10,7 @@
    = note: `K` has type `&'r [A; Q]`
 
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:22:29
+  --> $DIR/assoc-const-eq-param-in-ty.rs:23:29
    |
 LL | fn take0<'r, A: 'r + ConstParamTy_, const Q: usize>(
    |              - the type parameter `A` is defined here
@@ -21,7 +21,7 @@
    = note: `K` has type `&'r [A; Q]`
 
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:22:29
+  --> $DIR/assoc-const-eq-param-in-ty.rs:23:29
    |
 LL | fn take0<'r, A: 'r + ConstParamTy_, const Q: usize>(
    |                                           - the const parameter `Q` is defined here
@@ -32,7 +32,7 @@
    = note: `K` has type `&'r [A; Q]`
 
 error: the type of the associated constant `SELF` must not depend on `impl Trait`
-  --> $DIR/assoc-const-eq-param-in-ty.rs:39:26
+  --> $DIR/assoc-const-eq-param-in-ty.rs:40:26
    |
 LL | fn take1(_: impl Project<SELF = const {}>) {}
    |             -------------^^^^------------
@@ -41,7 +41,7 @@
    |             the `impl Trait` is specified here
 
 error: the type of the associated constant `SELF` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:44:21
+  --> $DIR/assoc-const-eq-param-in-ty.rs:45:21
    |
 LL | fn take2<P: Project<SELF = const {}>>(_: P) {}
    |          -          ^^^^ its type must not depend on the type parameter `P`
@@ -51,7 +51,7 @@
    = note: `SELF` has type `P`
 
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:53:52
+  --> $DIR/assoc-const-eq-param-in-ty.rs:54:52
    |
 LL | trait Iface<'r>: ConstParamTy_ {
    |             -- the lifetime parameter `'r` is defined here
@@ -62,7 +62,7 @@
    = note: `K` has type `&'r [Self; Q]`
 
 error: the type of the associated constant `K` must not depend on `Self`
-  --> $DIR/assoc-const-eq-param-in-ty.rs:53:52
+  --> $DIR/assoc-const-eq-param-in-ty.rs:54:52
    |
 LL |     type Assoc<const Q: usize>: Trait<'r, Self, Q, K = const { loop {} }>
    |                                                    ^ its type must not depend on `Self`
@@ -70,7 +70,7 @@
    = note: `K` has type `&'r [Self; Q]`
 
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:53:52
+  --> $DIR/assoc-const-eq-param-in-ty.rs:54:52
    |
 LL |     type Assoc<const Q: usize>: Trait<'r, Self, Q, K = const { loop {} }>
    |                      -                             ^ its type must not depend on the const parameter `Q`
@@ -80,7 +80,7 @@
    = note: `K` has type `&'r [Self; Q]`
 
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:53:52
+  --> $DIR/assoc-const-eq-param-in-ty.rs:54:52
    |
 LL | trait Iface<'r>: ConstParamTy_ {
    |             -- the lifetime parameter `'r` is defined here
@@ -92,7 +92,7 @@
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: the type of the associated constant `K` must not depend on `Self`
-  --> $DIR/assoc-const-eq-param-in-ty.rs:53:52
+  --> $DIR/assoc-const-eq-param-in-ty.rs:54:52
    |
 LL |     type Assoc<const Q: usize>: Trait<'r, Self, Q, K = const { loop {} }>
    |                                                    ^ its type must not depend on `Self`
@@ -101,7 +101,7 @@
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: the type of the associated constant `K` must not depend on generic parameters
-  --> $DIR/assoc-const-eq-param-in-ty.rs:53:52
+  --> $DIR/assoc-const-eq-param-in-ty.rs:54:52
    |
 LL |     type Assoc<const Q: usize>: Trait<'r, Self, Q, K = const { loop {} }>
    |                      -                             ^ its type must not depend on the const parameter `Q`
diff --git a/tests/ui/associated-consts/assoc-const-eq-supertraits.rs b/tests/ui/associated-consts/assoc-const-eq-supertraits.rs
index d9b8a8c..c092285 100644
--- a/tests/ui/associated-consts/assoc-const-eq-supertraits.rs
+++ b/tests/ui/associated-consts/assoc-const-eq-supertraits.rs
@@ -8,6 +8,7 @@
     min_generic_const_args,
     adt_const_params,
     unsized_const_params,
+    generic_const_parameter_types,
 )]
 #![allow(incomplete_features)]
 
diff --git a/tests/ui/cfg/invalid-cli-cfg-pred.rs b/tests/ui/cfg/invalid-cli-cfg-pred.rs
new file mode 100644
index 0000000..fdfb6b1
--- /dev/null
+++ b/tests/ui/cfg/invalid-cli-cfg-pred.rs
@@ -0,0 +1,5 @@
+//@ compile-flags: --cfg foo=1x
+
+fn main() {}
+
+//~? ERROR invalid `--cfg` argument
diff --git a/tests/ui/cfg/invalid-cli-cfg-pred.stderr b/tests/ui/cfg/invalid-cli-cfg-pred.stderr
new file mode 100644
index 0000000..2f5ed55
--- /dev/null
+++ b/tests/ui/cfg/invalid-cli-cfg-pred.stderr
@@ -0,0 +1,7 @@
+error: invalid suffix `x` for number literal
+  |
+  = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
+  = note: this occurred on the command line: `--cfg=foo=1x`
+
+error: invalid `--cfg` argument: `foo=1x` (expected `key` or `key="value"`, ensure escaping is appropriate for your shell, try 'key="value"' or key=\"value\")
+
diff --git a/tests/ui/cfg/invalid-cli-check-cfg-pred.rs b/tests/ui/cfg/invalid-cli-check-cfg-pred.rs
new file mode 100644
index 0000000..30417a9
--- /dev/null
+++ b/tests/ui/cfg/invalid-cli-check-cfg-pred.rs
@@ -0,0 +1,5 @@
+//@ compile-flags: --check-cfg 'foo=1x'
+
+fn main() {}
+
+//~? ERROR invalid `--check-cfg` argument
diff --git a/tests/ui/cfg/invalid-cli-check-cfg-pred.stderr b/tests/ui/cfg/invalid-cli-check-cfg-pred.stderr
new file mode 100644
index 0000000..be8299a
--- /dev/null
+++ b/tests/ui/cfg/invalid-cli-check-cfg-pred.stderr
@@ -0,0 +1,10 @@
+error: invalid suffix `x` for number literal
+  |
+  = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
+  = note: this occurred on the command line: `--check-cfg=foo=1x`
+
+error: invalid `--check-cfg` argument: `foo=1x`
+   |
+   = note: expected `cfg(name, values("value1", "value2", ... "valueN"))`
+   = note: visit <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more details
+
diff --git a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs
index fbe310f..484d6f1 100644
--- a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs
+++ b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs
@@ -25,3 +25,14 @@ fn baz<T: Trait>() {
 }
 
 fn main() {}
+
+fn test_ice_missing_bound<T>() {
+    foo::<{Option::Some::<u32>{0: <T as Trait>::ASSOC}}>();
+    //~^ ERROR the trait bound `T: Trait` is not satisfied
+    //~| ERROR the constant `Option::<u32>::Some(_)` is not of type `Foo`
+}
+
+fn test_underscore_inference() {
+    foo::<{ Option::Some::<u32> { 0: _ } }>();
+    //~^ ERROR the constant `Option::<u32>::Some(_)` is not of type `Foo`
+}
diff --git a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr
index 8b2871f2..b4f03e0 100644
--- a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr
+++ b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr
@@ -22,5 +22,41 @@
 LL | fn foo<const N: Foo>() {}
    |        ^^^^^^^^^^^^ required by this const generic parameter in `foo`
 
-error: aborting due to 2 previous errors
+error[E0277]: the trait bound `T: Trait` is not satisfied
+  --> $DIR/printing_valtrees_supports_non_values.rs:30:5
+   |
+LL |     foo::<{Option::Some::<u32>{0: <T as Trait>::ASSOC}}>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T`
+   |
+help: consider restricting type parameter `T` with trait `Trait`
+   |
+LL | fn test_ice_missing_bound<T: Trait>() {
+   |                            +++++++
 
+error: the constant `Option::<u32>::Some(_)` is not of type `Foo`
+  --> $DIR/printing_valtrees_supports_non_values.rs:30:12
+   |
+LL |     foo::<{Option::Some::<u32>{0: <T as Trait>::ASSOC}}>();
+   |            ^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option<u32>`
+   |
+note: required by a const generic parameter in `foo`
+  --> $DIR/printing_valtrees_supports_non_values.rs:15:8
+   |
+LL | fn foo<const N: Foo>() {}
+   |        ^^^^^^^^^^^^ required by this const generic parameter in `foo`
+
+error: the constant `Option::<u32>::Some(_)` is not of type `Foo`
+  --> $DIR/printing_valtrees_supports_non_values.rs:36:13
+   |
+LL |     foo::<{ Option::Some::<u32> { 0: _ } }>();
+   |             ^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option<u32>`
+   |
+note: required by a const generic parameter in `foo`
+  --> $DIR/printing_valtrees_supports_non_values.rs:15:8
+   |
+LL | fn foo<const N: Foo>() {}
+   |        ^^^^^^^^^^^^ required by this const generic parameter in `foo`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs b/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs
new file mode 100644
index 0000000..a5b3d3d
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs
@@ -0,0 +1,52 @@
+//@ run-pass
+#![feature(min_generic_const_args, adt_const_params)]
+#![expect(incomplete_features)]
+#![allow(dead_code)]
+
+use std::marker::ConstParamTy;
+
+#[derive(Debug, Eq, PartialEq, ConstParamTy)]
+struct Point(u32, u32);
+
+#[derive(Debug, Eq, PartialEq, ConstParamTy)]
+enum MyEnum<T> {
+    Variant(T),
+    Other,
+}
+
+trait Trait {
+    #[type_const]
+    const ASSOC: u32;
+}
+
+fn with_point<const P: Point>() -> Point {
+    P
+}
+
+fn with_enum<const E: MyEnum<u32>>() -> MyEnum<u32> {
+    E
+}
+
+fn test<T: Trait, const N: u32>() {
+    with_point::<{ Point(<T as Trait>::ASSOC, N) }>();
+}
+
+fn test_basic<const N: u32>() {
+    with_point::<{ Point(N, N) }>();
+    with_point::<{ Point(const { 5 }, const { 10 }) }>();
+
+    with_enum::<{ MyEnum::Variant::<u32>(N) }>();
+    with_enum::<{ MyEnum::Variant::<u32>(const { 42 }) }>();
+
+    with_enum::<{ <MyEnum<u32>>::Variant(N) }>();
+}
+
+fn main() {
+    test_basic::<5>();
+
+    let p = with_point::<{ Point(const { 1 }, const { 2 }) }>();
+    assert_eq!(p, Point(1, 2));
+
+    let e = with_enum::<{ MyEnum::Variant::<u32>(const { 10 }) }>();
+    assert_eq!(e, MyEnum::Variant(10));
+}
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs
new file mode 100644
index 0000000..2e39f89
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs
@@ -0,0 +1,19 @@
+#![feature(min_generic_const_args, adt_const_params)]
+#![expect(incomplete_features)]
+
+use std::marker::ConstParamTy;
+
+#[derive(Eq, PartialEq, ConstParamTy)]
+struct Point(u32, u32);
+
+fn with_point<const P: Point>() {}
+
+fn test<const N: u32>() {
+    with_point::<{ Point(N + 1, N) }>();
+    //~^ ERROR complex const arguments must be placed inside of a `const` block
+
+    with_point::<{ Point(const { N + 1 }, N) }>();
+    //~^ ERROR generic parameters may not be used in const operations
+}
+
+fn main() {}
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr
new file mode 100644
index 0000000..e0ea3fd
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr
@@ -0,0 +1,14 @@
+error: complex const arguments must be placed inside of a `const` block
+  --> $DIR/tuple_ctor_complex_args.rs:12:26
+   |
+LL |     with_point::<{ Point(N + 1, N) }>();
+   |                          ^^^^^
+
+error: generic parameters may not be used in const operations
+  --> $DIR/tuple_ctor_complex_args.rs:15:34
+   |
+LL |     with_point::<{ Point(const { N + 1 }, N) }>();
+   |                                  ^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs
new file mode 100644
index 0000000..84ded05
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs
@@ -0,0 +1,46 @@
+#![feature(min_generic_const_args, adt_const_params)]
+#![expect(incomplete_features)]
+
+use std::marker::ConstParamTy;
+
+#[derive(Eq, PartialEq, ConstParamTy)]
+struct Point(u32, u32);
+
+#[derive(Eq, PartialEq, ConstParamTy)]
+enum MyEnum<T> {
+    Variant(T),
+    Unit,
+}
+
+const CONST_ITEM: u32 = 42;
+
+fn accepts_point<const P: Point>() {}
+fn accepts_enum<const E: MyEnum<u32>>() {}
+
+fn non_ctor() {}
+
+fn test_errors<const N: usize>() {
+    accepts_point::<{ Point(N) }>();
+    //~^ ERROR tuple constructor has 2 arguments but 1 were provided
+
+    accepts_point::<{ Point(N, N, N) }>();
+    //~^ ERROR tuple constructor has 2 arguments but 3 were provided
+
+    accepts_point::<{ UnresolvedIdent(N, N) }>();
+    //~^ ERROR cannot find function, tuple struct or tuple variant `UnresolvedIdent` in this scope
+    //~| ERROR tuple constructor with invalid base path
+
+    accepts_point::<{ non_ctor(N, N) }>();
+    //~^ ERROR tuple constructor with invalid base path
+
+    accepts_point::<{ CONST_ITEM(N, N) }>();
+    //~^ ERROR tuple constructor with invalid base path
+
+    accepts_point::<{ Point }>();
+    //~^ ERROR the constant `Point` is not of type `Point`
+
+    accepts_enum::<{ MyEnum::Variant::<u32> }>();
+    //~^ ERROR the constant `MyEnum::<u32>::Variant` is not of type `MyEnum<u32>`
+}
+
+fn main() {}
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr
new file mode 100644
index 0000000..fbcdf35
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr
@@ -0,0 +1,68 @@
+error[E0425]: cannot find function, tuple struct or tuple variant `UnresolvedIdent` in this scope
+  --> $DIR/tuple_ctor_erroneous.rs:29:23
+   |
+LL |     accepts_point::<{ UnresolvedIdent(N, N) }>();
+   |                       ^^^^^^^^^^^^^^^ not found in this scope
+   |
+help: you might be missing a const parameter
+   |
+LL | fn test_errors<const N: usize, const UnresolvedIdent: /* Type */>() {
+   |                              +++++++++++++++++++++++++++++++++++
+
+error: tuple constructor has 2 arguments but 1 were provided
+  --> $DIR/tuple_ctor_erroneous.rs:23:23
+   |
+LL |     accepts_point::<{ Point(N) }>();
+   |                       ^^^^^
+
+error: tuple constructor has 2 arguments but 3 were provided
+  --> $DIR/tuple_ctor_erroneous.rs:26:23
+   |
+LL |     accepts_point::<{ Point(N, N, N) }>();
+   |                       ^^^^^
+
+error: tuple constructor with invalid base path
+  --> $DIR/tuple_ctor_erroneous.rs:29:23
+   |
+LL |     accepts_point::<{ UnresolvedIdent(N, N) }>();
+   |                       ^^^^^^^^^^^^^^^
+
+error: tuple constructor with invalid base path
+  --> $DIR/tuple_ctor_erroneous.rs:33:23
+   |
+LL |     accepts_point::<{ non_ctor(N, N) }>();
+   |                       ^^^^^^^^
+
+error: tuple constructor with invalid base path
+  --> $DIR/tuple_ctor_erroneous.rs:36:23
+   |
+LL |     accepts_point::<{ CONST_ITEM(N, N) }>();
+   |                       ^^^^^^^^^^
+
+error: the constant `Point` is not of type `Point`
+  --> $DIR/tuple_ctor_erroneous.rs:39:23
+   |
+LL |     accepts_point::<{ Point }>();
+   |                       ^^^^^ expected `Point`, found struct constructor
+   |
+note: required by a const generic parameter in `accepts_point`
+  --> $DIR/tuple_ctor_erroneous.rs:17:18
+   |
+LL | fn accepts_point<const P: Point>() {}
+   |                  ^^^^^^^^^^^^^^ required by this const generic parameter in `accepts_point`
+
+error: the constant `MyEnum::<u32>::Variant` is not of type `MyEnum<u32>`
+  --> $DIR/tuple_ctor_erroneous.rs:42:22
+   |
+LL |     accepts_enum::<{ MyEnum::Variant::<u32> }>();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^ expected `MyEnum<u32>`, found enum constructor
+   |
+note: required by a const generic parameter in `accepts_enum`
+  --> $DIR/tuple_ctor_erroneous.rs:18:17
+   |
+LL | fn accepts_enum<const E: MyEnum<u32>>() {}
+   |                 ^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `accepts_enum`
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_in_array_len.rs b/tests/ui/const-generics/mgca/tuple_ctor_in_array_len.rs
new file mode 100644
index 0000000..5c7290b
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_in_array_len.rs
@@ -0,0 +1,16 @@
+//! Regression test for <https://github.com/rust-lang/rust/issues/136379>
+
+#![feature(min_generic_const_args)]
+#![expect(incomplete_features)]
+
+pub struct S();
+
+impl S {
+    pub fn f() -> [u8; S] {
+        //~^ ERROR the constant `S` is not of type `usize`
+        []
+        //~^ ERROR mismatched types [E0308]
+    }
+}
+
+pub fn main() {}
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_in_array_len.stderr b/tests/ui/const-generics/mgca/tuple_ctor_in_array_len.stderr
new file mode 100644
index 0000000..64c3cec
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_in_array_len.stderr
@@ -0,0 +1,23 @@
+error: the constant `S` is not of type `usize`
+  --> $DIR/tuple_ctor_in_array_len.rs:9:19
+   |
+LL |     pub fn f() -> [u8; S] {
+   |                   ^^^^^^^ expected `usize`, found struct constructor
+   |
+   = note: the length of array `[u8; S]` must be type `usize`
+
+error[E0308]: mismatched types
+  --> $DIR/tuple_ctor_in_array_len.rs:11:9
+   |
+LL |     pub fn f() -> [u8; S] {
+   |                   ------- expected `[u8; S]` because of return type
+LL |
+LL |         []
+   |         ^^ expected an array with a size of S, found one with a size of 0
+   |
+   = note: expected array `[u8; S]`
+              found array `[_; 0]`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_nested.rs b/tests/ui/const-generics/mgca/tuple_ctor_nested.rs
new file mode 100644
index 0000000..49a31ea
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_nested.rs
@@ -0,0 +1,38 @@
+//@ run-pass
+#![feature(min_generic_const_args, adt_const_params)]
+#![expect(incomplete_features)]
+
+use std::marker::ConstParamTy;
+
+#[derive(Debug, Eq, PartialEq, ConstParamTy)]
+struct Inner(u32);
+
+#[derive(Debug, Eq, PartialEq, ConstParamTy)]
+struct Outer(Inner);
+
+#[derive(Debug, Eq, PartialEq, ConstParamTy)]
+enum Container<T> {
+    Wrap(T),
+}
+
+fn with_outer<const O: Outer>() -> Outer {
+    O
+}
+
+fn with_container<const C: Container<Inner>>() -> Container<Inner> {
+    C
+}
+
+fn test<const N: u32>() {
+    with_outer::<{ Outer(Inner(N)) }>();
+    with_outer::<{ Outer(Inner(const { 42 })) }>();
+
+    with_container::<{ Container::Wrap::<Inner>(Inner(N)) }>();
+}
+
+fn main() {
+    test::<5>();
+
+    let o = with_outer::<{ Outer(Inner(const { 10 })) }>();
+    assert_eq!(o, Outer(Inner(10)));
+}
diff --git a/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs b/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs
new file mode 100644
index 0000000..d5a07cb
--- /dev/null
+++ b/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs
@@ -0,0 +1,26 @@
+//@ run-pass
+#![feature(min_generic_const_args, adt_const_params)]
+#![expect(incomplete_features)]
+
+use std::marker::ConstParamTy;
+
+#[derive(Debug, Eq, PartialEq, ConstParamTy)]
+enum Option<T> {
+    Some(T),
+}
+
+fn with_option<const O: Option<u32>>() -> Option<u32> {
+    O
+}
+
+fn test<const N: u32>() {
+    with_option::<{ <Option<u32>>::Some(N) }>();
+    with_option::<{ <Option<u32>>::Some(const { 42 }) }>();
+}
+
+fn main() {
+    test::<5>();
+
+    let o = with_option::<{ <Option<u32>>::Some(const { 10 }) }>();
+    assert_eq!(o, Option::Some(10));
+}
diff --git a/tests/ui/const-generics/mgca/type_as_const_in_array_len.rs b/tests/ui/const-generics/mgca/type_as_const_in_array_len.rs
new file mode 100644
index 0000000..b6b76fe
--- /dev/null
+++ b/tests/ui/const-generics/mgca/type_as_const_in_array_len.rs
@@ -0,0 +1,14 @@
+//! Regression test for <https://github.com/rust-lang/rust/issues/138132>
+
+#![feature(min_generic_const_args)]
+#![expect(incomplete_features)]
+
+struct B(Box<[u8; C]>);
+//~^ ERROR missing generics for struct `C`
+impl B {
+    fn d(self) {
+        self.0.e()
+    }
+}
+struct C<'a>(&'a u8);
+fn main() {}
diff --git a/tests/ui/const-generics/mgca/type_as_const_in_array_len.stderr b/tests/ui/const-generics/mgca/type_as_const_in_array_len.stderr
new file mode 100644
index 0000000..482fd73
--- /dev/null
+++ b/tests/ui/const-generics/mgca/type_as_const_in_array_len.stderr
@@ -0,0 +1,19 @@
+error[E0107]: missing generics for struct `C`
+  --> $DIR/type_as_const_in_array_len.rs:6:19
+   |
+LL | struct B(Box<[u8; C]>);
+   |                   ^ expected 1 lifetime argument
+   |
+note: struct defined here, with 1 lifetime parameter: `'a`
+  --> $DIR/type_as_const_in_array_len.rs:13:8
+   |
+LL | struct C<'a>(&'a u8);
+   |        ^ --
+help: add missing lifetime argument
+   |
+LL | struct B(Box<[u8; C<'a>]>);
+   |                    ++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.gate.stderr b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.gate.stderr
new file mode 100644
index 0000000..8a64af2
--- /dev/null
+++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.gate.stderr
@@ -0,0 +1,38 @@
+error: anonymous constants referencing generics are not yet supported
+  --> $DIR/type_const-generic-param-in-type.rs:9:53
+   |
+LL | const FOO<T: core::marker::ConstParamTy_>: [T; 0] = const { [] };
+   |                                                     ^^^^^^^^^^^^
+
+error: anonymous constants referencing generics are not yet supported
+  --> $DIR/type_const-generic-param-in-type.rs:14:38
+   |
+LL | const BAR<const N: usize>: [(); N] = const { [] };
+   |                                      ^^^^^^^^^^^^
+
+error: anonymous constants with lifetimes in their type are not yet supported
+  --> $DIR/type_const-generic-param-in-type.rs:19:30
+   |
+LL | const BAZ<'a>: [&'a (); 0] = const { [] };
+   |                              ^^^^^^^^^^^^
+
+error: anonymous constants referencing generics are not yet supported
+  --> $DIR/type_const-generic-param-in-type.rs:39:59
+   |
+LL |     const ASSOC<T: core::marker::ConstParamTy_>: [T; 0] = const { [] };
+   |                                                           ^^^^^^^^^^^^
+
+error: anonymous constants referencing generics are not yet supported
+  --> $DIR/type_const-generic-param-in-type.rs:44:50
+   |
+LL |     const ASSOC_CONST<const N: usize>: [(); N] = const { [] };
+   |                                                  ^^^^^^^^^^^^
+
+error: anonymous constants with lifetimes in their type are not yet supported
+  --> $DIR/type_const-generic-param-in-type.rs:49:39
+   |
+LL |     const ASSOC_LT<'a>: [&'a (); 0] = const { [] };
+   |                                       ^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr
new file mode 100644
index 0000000..14ae276
--- /dev/null
+++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr
@@ -0,0 +1,57 @@
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:9:45
+   |
+LL | const FOO<T: core::marker::ConstParamTy_>: [T; 0] = const { [] };
+   |                                             ^ the type must not depend on the parameter `T`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:14:33
+   |
+LL | const BAR<const N: usize>: [(); N] = const { [] };
+   |                                 ^ the type must not depend on the parameter `N`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:19:18
+   |
+LL | const BAZ<'a>: [&'a (); 0] = const { [] };
+   |                  ^^ the type must not depend on the parameter `'a`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:25:51
+   |
+LL |     const ASSOC<T: core::marker::ConstParamTy_>: [T; 0];
+   |                                                   ^ the type must not depend on the parameter `T`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:29:45
+   |
+LL |     const ASSOC_CONST<const N: usize>: [(); N];
+   |                                             ^ the type must not depend on the parameter `N`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:33:27
+   |
+LL |     const ASSOC_LT<'a>: [&'a (); 0];
+   |                           ^^ the type must not depend on the parameter `'a`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:39:51
+   |
+LL |     const ASSOC<T: core::marker::ConstParamTy_>: [T; 0] = const { [] };
+   |                                                   ^ the type must not depend on the parameter `T`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:44:45
+   |
+LL |     const ASSOC_CONST<const N: usize>: [(); N] = const { [] };
+   |                                             ^ the type must not depend on the parameter `N`
+
+error[E0770]: the type of const parameters must not depend on other generic parameters
+  --> $DIR/type_const-generic-param-in-type.rs:49:27
+   |
+LL |     const ASSOC_LT<'a>: [&'a (); 0] = const { [] };
+   |                           ^^ the type must not depend on the parameter `'a`
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0770`.
diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs
new file mode 100644
index 0000000..f1a4aba
--- /dev/null
+++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs
@@ -0,0 +1,54 @@
+//@ revisions: nogate gate
+//@ [gate] check-fail
+// FIXME(generic_const_parameter_types): this should pass
+#![expect(incomplete_features)]
+#![feature(adt_const_params, unsized_const_params, min_generic_const_args, generic_const_items)]
+#![cfg_attr(gate, feature(generic_const_parameter_types))]
+
+#[type_const]
+const FOO<T: core::marker::ConstParamTy_>: [T; 0] = const { [] };
+//[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+//[gate]~^^ ERROR anonymous constants referencing generics are not yet supported
+
+#[type_const]
+const BAR<const N: usize>: [(); N] = const { [] };
+//[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+//[gate]~^^ ERROR anonymous constants referencing generics are not yet supported
+
+#[type_const]
+const BAZ<'a>: [&'a (); 0] = const { [] };
+//[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+//[gate]~^^ ERROR anonymous constants with lifetimes in their type are not yet supported
+
+trait Tr {
+    #[type_const]
+    const ASSOC<T: core::marker::ConstParamTy_>: [T; 0];
+    //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+
+    #[type_const]
+    const ASSOC_CONST<const N: usize>: [(); N];
+    //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+
+    #[type_const]
+    const ASSOC_LT<'a>: [&'a (); 0];
+    //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+}
+
+impl Tr for () {
+    #[type_const]
+    const ASSOC<T: core::marker::ConstParamTy_>: [T; 0] = const { [] };
+    //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+    //[gate]~^^ ERROR anonymous constants referencing generics are not yet supported
+
+    #[type_const]
+    const ASSOC_CONST<const N: usize>: [(); N] = const { [] };
+    //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+    //[gate]~^^ ERROR anonymous constants referencing generics are not yet supported
+
+    #[type_const]
+    const ASSOC_LT<'a>: [&'a (); 0] = const { [] };
+    //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters
+    //[gate]~^^ ERROR anonymous constants with lifetimes in their type are not yet supported
+}
+
+fn main() {}
diff --git a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs b/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs
index de63211..0277da4 100644
--- a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs
+++ b/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs
@@ -1,6 +1,6 @@
+//@ check-pass
 //@ aux-crate: glob_vs_expanded=glob-vs-expanded.rs
 
 fn main() {
-    glob_vs_expanded::mac!(); //~ ERROR `mac` is ambiguous
-                              //~| WARN this was previously accepted
+    glob_vs_expanded::mac!(); // OK
 }
diff --git a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr b/tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr
deleted file mode 100644
index 4a9a6c9..0000000
--- a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr
+++ /dev/null
@@ -1,53 +0,0 @@
-error: `mac` is ambiguous
-  --> $DIR/ambiguous-glob-vs-expanded-extern.rs:4:23
-   |
-LL |     glob_vs_expanded::mac!();
-   |                       ^^^ ambiguous name
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #114095 <https://github.com/rust-lang/rust/issues/114095>
-   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
-note: `mac` could refer to the macro defined here
-  --> $DIR/auxiliary/glob-vs-expanded.rs:9:13
-   |
-LL |     () => { pub macro mac() {} }
-   |             ^^^^^^^^^^^^^
-LL | }
-LL | define_mac!();
-   | ------------- in this macro invocation
-note: `mac` could also refer to the macro defined here
-  --> $DIR/auxiliary/glob-vs-expanded.rs:5:9
-   |
-LL | pub use inner::*;
-   |         ^^^^^
-   = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default
-   = note: this error originates in the macro `define_mac` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 1 previous error
-
-Future incompatibility report: Future breakage diagnostic:
-error: `mac` is ambiguous
-  --> $DIR/ambiguous-glob-vs-expanded-extern.rs:4:23
-   |
-LL |     glob_vs_expanded::mac!();
-   |                       ^^^ ambiguous name
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #114095 <https://github.com/rust-lang/rust/issues/114095>
-   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
-note: `mac` could refer to the macro defined here
-  --> $DIR/auxiliary/glob-vs-expanded.rs:9:13
-   |
-LL |     () => { pub macro mac() {} }
-   |             ^^^^^^^^^^^^^
-LL | }
-LL | define_mac!();
-   | ------------- in this macro invocation
-note: `mac` could also refer to the macro defined here
-  --> $DIR/auxiliary/glob-vs-expanded.rs:5:9
-   |
-LL | pub use inner::*;
-   |         ^^^^^
-   = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default
-   = note: this error originates in the macro `define_mac` (in Nightly builds, run with -Z macro-backtrace for more info)
-
diff --git a/tests/ui/imports/issue-114682-1.stderr b/tests/ui/imports/issue-114682-1.stderr
index 85fb7f7..fd2776f 100644
--- a/tests/ui/imports/issue-114682-1.stderr
+++ b/tests/ui/imports/issue-114682-1.stderr
@@ -4,7 +4,7 @@
 LL |     A!();
    |     ^ ambiguous name
    |
-   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
 note: `A` could refer to the macro defined here
   --> $DIR/issue-114682-1.rs:7:9
    |
@@ -15,12 +15,13 @@
 ...
 LL |   mac!();
    |   ------ in this macro invocation
+   = help: use `crate::A` to refer to this macro unambiguously
 note: `A` could also refer to the macro imported here
   --> $DIR/issue-114682-1.rs:19:9
    |
 LL | pub use m::*;
    |         ^^^^
-   = help: consider adding an explicit import of `A` to disambiguate
+   = help: use `crate::A` to refer to this macro unambiguously
    = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.stderr b/tests/ui/imports/local-modularized-tricky-fail-1.stderr
index 52a01e8..b5b3be5 100644
--- a/tests/ui/imports/local-modularized-tricky-fail-1.stderr
+++ b/tests/ui/imports/local-modularized-tricky-fail-1.stderr
@@ -4,7 +4,7 @@
 LL | exported!();
    | ^^^^^^^^ ambiguous name
    |
-   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
 note: `exported` could refer to the macro defined here
   --> $DIR/local-modularized-tricky-fail-1.rs:6:5
    |
@@ -15,12 +15,13 @@
 ...
 LL |       define_exported!();
    |       ------------------ in this macro invocation
+   = help: use `crate::exported` to refer to this macro unambiguously
 note: `exported` could also refer to the macro imported here
   --> $DIR/local-modularized-tricky-fail-1.rs:23:5
    |
 LL | use inner1::*;
    |     ^^^^^^^^^
-   = help: consider adding an explicit import of `exported` to disambiguate
+   = help: use `crate::exported` to refer to this macro unambiguously
    = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0659]: `panic` is ambiguous
diff --git a/tests/ui/imports/macro-paths.stderr b/tests/ui/imports/macro-paths.stderr
index 5f113ce..56a40e9 100644
--- a/tests/ui/imports/macro-paths.stderr
+++ b/tests/ui/imports/macro-paths.stderr
@@ -4,7 +4,7 @@
 LL |     bar::m! {
    |     ^^^ ambiguous name
    |
-   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
 note: `bar` could refer to the module defined here
   --> $DIR/macro-paths.rs:14:9
    |
@@ -15,7 +15,6 @@
    |
 LL |     use foo::*;
    |         ^^^^^^
-   = help: consider adding an explicit import of `bar` to disambiguate
 
 error[E0659]: `baz` is ambiguous
   --> $DIR/macro-paths.rs:23:5
diff --git a/tests/ui/imports/macros.rs b/tests/ui/imports/macros.rs
index cf67e08..121f1f7 100644
--- a/tests/ui/imports/macros.rs
+++ b/tests/ui/imports/macros.rs
@@ -13,7 +13,7 @@ mod m1 {
 
 mod m2 {
     use two_macros::*;
-    m! { //~ ERROR ambiguous
+    m! { //~ ERROR `m` is ambiguous
         use crate::foo::m;
     }
 }
diff --git a/tests/ui/imports/macros.stderr b/tests/ui/imports/macros.stderr
index 25a678c..9c081cc 100644
--- a/tests/ui/imports/macros.stderr
+++ b/tests/ui/imports/macros.stderr
@@ -4,18 +4,19 @@
 LL |     m! {
    |     ^ ambiguous name
    |
-   = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
 note: `m` could refer to the macro imported here
   --> $DIR/macros.rs:17:13
    |
 LL |         use crate::foo::m;
    |             ^^^^^^^^^^^^^
+   = help: use `self::m` to refer to this macro unambiguously
 note: `m` could also refer to the macro imported here
   --> $DIR/macros.rs:15:9
    |
 LL |     use two_macros::*;
    |         ^^^^^^^^^^^^^
-   = help: consider adding an explicit import of `m` to disambiguate
+   = help: use `self::m` to refer to this macro unambiguously
 
 error[E0659]: `m` is ambiguous
   --> $DIR/macros.rs:29:9
diff --git a/tests/ui/imports/overwritten-glob-ambig.rs b/tests/ui/imports/overwritten-glob-ambig.rs
new file mode 100644
index 0000000..a591856
--- /dev/null
+++ b/tests/ui/imports/overwritten-glob-ambig.rs
@@ -0,0 +1,26 @@
+// Test for a regression introduced by splitting module scope into two scopes
+// (similar to issue #145575).
+
+//@ check-pass
+//@ edition: 2018..
+
+#[macro_use]
+mod one {
+    // Macro that is in a different module, but still in scope due to `macro_use`
+    macro_rules! mac { () => {} }
+    pub(crate) use mac;
+}
+
+mod other {
+    macro_rules! mac { () => {} }
+    pub(crate) use mac;
+}
+
+// Single import of the same in the current module.
+use one::mac;
+// Glob import of a different macro in the current module (should be an ambiguity).
+use other::*;
+
+fn main() {
+    mac!(); // OK for now, the ambiguity is not reported
+}