From 6b29bb6680e0df3bbcd8e9defdf0ad142e80cdf0 Mon Sep 17 00:00:00 2001
From: Sa4dUs <dmmarcelo27@gmail.com>
Date: Sun, 2 Mar 2025 23:38:55 +0100
Subject: [PATCH 01/25] Prevent ICE in autodiff validation by emitting
 user-friendly errors

---
 compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 673740b4aab9f..c8f13dc0bae61 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -930,13 +930,19 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
         }
     }
 
+    // Validate input and return activities
+    let mut msg = "".to_string();
     for &input in &arg_activities {
         if !valid_input_activity(mode, input) {
-            span_bug!(attr.span(), "Invalid input activity {} for {} mode", input, mode);
+            msg = format!("Invalid input activity {} for {} mode", input, mode);
         }
     }
     if !valid_ret_activity(mode, ret_activity) {
-        span_bug!(attr.span(), "Invalid return activity {} for {} mode", ret_activity, mode);
+        msg = format!("Invalid return activity {} for {} mode", ret_activity, mode);
+    }
+    if msg != "".to_string() {
+        tcx.dcx().struct_span_err(attr.span(), msg).with_note("invalid activity").emit();
+        return Some(AutoDiffAttrs::error());
     }
 
     Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })

From 9067f7cad6b52a139af3f62c243a94adbf6be9f9 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 9 Mar 2025 19:04:37 +0000
Subject: [PATCH 02/25] Explain weird quirk in user type annotation lowering

---
 compiler/rustc_mir_build/src/thir/cx/mod.rs   |  2 +-
 .../rustc_mir_build/src/thir/pattern/mod.rs   |  2 +-
 compiler/rustc_mir_build/src/thir/util.rs     | 22 ++++++++++++++++++-
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index 2e069cae4269f..3d55c1401b637 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -207,7 +207,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
         &self,
         hir_id: HirId,
     ) -> Option<ty::CanonicalUserType<'tcx>> {
-        crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id)
+        crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id)
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 8dc3f998e0916..4bfeab44bf4b9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -539,7 +539,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         &self,
         hir_id: hir::HirId,
     ) -> Option<ty::CanonicalUserType<'tcx>> {
-        crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id)
+        crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id)
     }
 
     /// Takes a HIR Path. If the path is a constant, evaluates it and feeds
diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs
index 60a47a94e3a1f..457957f5fce95 100644
--- a/compiler/rustc_mir_build/src/thir/util.rs
+++ b/compiler/rustc_mir_build/src/thir/util.rs
@@ -1,12 +1,16 @@
+use std::assert_matches::assert_matches;
+
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_middle::bug;
-use rustc_middle::ty::{self, CanonicalUserType};
+use rustc_middle::ty::{self, CanonicalUserType, TyCtxt};
 use tracing::debug;
 
 /// Looks up the type associated with this hir-id and applies the
 /// user-given generic parameters; the hir-id must map to a suitable
 /// type.
 pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>(
+    tcx: TyCtxt<'tcx>,
     typeck_results: &ty::TypeckResults<'tcx>,
     hir_id: hir::HirId,
 ) -> Option<CanonicalUserType<'tcx>> {
@@ -16,7 +20,23 @@ pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>(
     let ty = typeck_results.node_type(hir_id);
     match ty.kind() {
         ty::Adt(adt_def, ..) => {
+            // This "fixes" user type annotations for tupled ctor patterns for ADTs.
+            // That's because `type_of(ctor_did)` returns a FnDef, but we actually
+            // want to be annotating the type of the ADT itself. It's a bit goofy,
+            // but it's easier to adjust this here rather than in the path lowering
+            // code for patterns in HIR.
             if let ty::UserTypeKind::TypeOf(did, _) = &mut user_ty.value.kind {
+                // This is either already set up correctly (struct, union, enum, or variant),
+                // or needs adjusting (ctor). Make sure we don't start adjusting other
+                // user annotations like consts or fn calls.
+                assert_matches!(
+                    tcx.def_kind(*did),
+                    DefKind::Ctor(..)
+                        | DefKind::Struct
+                        | DefKind::Enum
+                        | DefKind::Union
+                        | DefKind::Variant
+                );
                 *did = adt_def.did();
             }
             Some(user_ty)

From 8ab05adc37667c3169fb120b3a9f53be9a909c3b Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 9 Mar 2025 19:12:42 +0000
Subject: [PATCH 03/25] Do not write user type annotation for const param value
 path

---
 compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs        |  7 +++++++
 tests/crashes/138048.rs                               |  8 --------
 .../generic_const_parameter_types/bad-param-in-pat.rs | 10 ++++++++++
 .../bad-param-in-pat.stderr                           | 11 +++++++++++
 4 files changed, 28 insertions(+), 8 deletions(-)
 delete mode 100644 tests/crashes/138048.rs
 create mode 100644 tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs
 create mode 100644 tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr

diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index c46a42c5de1e3..36ad0ae1c3016 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -220,6 +220,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         debug!("fcx {}", self.tag());
 
+        // Don't write user type annotations for const param types, since we give them
+        // identity args just so that we can trivially substitute their `EarlyBinder`.
+        // We enforce that they match their type in MIR later on.
+        if matches!(self.tcx.def_kind(def_id), DefKind::ConstParam) {
+            return;
+        }
+
         if Self::can_contain_user_lifetime_bounds((args, user_self_ty)) {
             let canonicalized = self.canonicalize_user_type_annotation(ty::UserType::new(
                 ty::UserTypeKind::TypeOf(def_id, UserArgs { args, user_self_ty }),
diff --git a/tests/crashes/138048.rs b/tests/crashes/138048.rs
deleted file mode 100644
index fd59f46c752f7..0000000000000
--- a/tests/crashes/138048.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #138048
-struct Foo;
-
-impl<'b> Foo {
-    fn bar<const V: u8>() {
-        let V;
-    }
-}
diff --git a/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs
new file mode 100644
index 0000000000000..bc54aad59ba96
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs
@@ -0,0 +1,10 @@
+struct Foo<'a>(&'a ());
+
+// We need a lifetime in scope or else we do not write a user type annotation as a fast-path.
+impl<'a> Foo<'a> {
+    fn bar<const V: u8>() {
+        let V;
+        //~^ ERROR constant parameters cannot be referenced in patterns
+    }
+}
+fn main() {}
diff --git a/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr
new file mode 100644
index 0000000000000..299ff4165e0d5
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr
@@ -0,0 +1,11 @@
+error[E0158]: constant parameters cannot be referenced in patterns
+  --> $DIR/bad-param-in-pat.rs:6:13
+   |
+LL |     fn bar<const V: u8>() {
+   |            ----------- constant defined here
+LL |         let V;
+   |             ^ can't be used in patterns
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0158`.

From ed6dfddfeb345e0fe94ef735a54d6290d278a6b8 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 9 Mar 2025 18:23:17 +0000
Subject: [PATCH 04/25] Do not feed anon const a type that references generics
 that it does not have

---
 .../src/hir_ty_lowering/mod.rs                | 25 +++++++++++++------
 tests/crashes/137865.rs                       |  5 ----
 .../references-parent-generics.feat.stderr    | 25 +++++++++++++++++++
 .../references-parent-generics.nofeat.stderr  | 16 ++++++++++++
 .../references-parent-generics.rs             | 18 +++++++++++++
 5 files changed, 76 insertions(+), 13 deletions(-)
 delete mode 100644 tests/crashes/137865.rs
 create mode 100644 tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr
 create mode 100644 tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr
 create mode 100644 tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs

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 dd6c40bfbb8dc..5f91f1d7b3e1f 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2294,18 +2294,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         {
             let anon_const_type = tcx.type_of(param_def_id).instantiate(tcx, args);
 
-            // We must error if the instantiated type has any inference variables as we will
-            // use this type to feed the `type_of` and query results must not contain inference
-            // variables otherwise we will ICE.
-            //
+            // FIXME(generic_const_parameter_types): Ideally we remove these errors below when
+            // we have the ability to intermix typeck of anon const const args with the parent
+            // bodies typeck.
+
             // We also error if the type contains any regions as effectively any region will wind
             // up as a region variable in mir borrowck. It would also be somewhat concerning if
             // hir typeck was using equality but mir borrowck wound up using subtyping as that could
             // result in a non-infer in hir typeck but a region variable in borrowck.
-            //
-            // FIXME(generic_const_parameter_types): Ideally we remove these errors one day when
-            // we have the ability to intermix typeck of anon const const args with the parent
-            // bodies typeck.
             if tcx.features().generic_const_parameter_types()
                 && (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions())
             {
@@ -2316,6 +2312,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e)));
                 return ty::Const::new_error(tcx, e);
             }
+            // We must error if the instantiated type has any inference variables as we will
+            // use this type to feed the `type_of` and query results must not contain inference
+            // variables otherwise we will ICE.
             if anon_const_type.has_non_region_infer() {
                 let e = tcx.dcx().span_err(
                     const_arg.span(),
@@ -2324,6 +2323,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e)));
                 return ty::Const::new_error(tcx, e);
             }
+            // We error when the type contains unsubstituted generics since we do not currently
+            // give the anon const any of the generics from the parent.
+            if anon_const_type.has_non_region_param() {
+                let e = tcx.dcx().span_err(
+                    const_arg.span(),
+                    "anonymous constants referencing generics are not yet supported",
+                );
+                tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e)));
+                return ty::Const::new_error(tcx, e);
+            }
 
             tcx.feed_anon_const_type(
                 anon.def_id,
diff --git a/tests/crashes/137865.rs b/tests/crashes/137865.rs
deleted file mode 100644
index 7ecd8c734d322..0000000000000
--- a/tests/crashes/137865.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-//@ known-bug: #137865
-trait Foo {
-    type Assoc<const N: Self>;
-    fn foo() -> Self::Assoc<3>;
-}
diff --git a/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr
new file mode 100644
index 0000000000000..29171c8006afb
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr
@@ -0,0 +1,25 @@
+warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/references-parent-generics.rs:3:27
+   |
+LL | #![cfg_attr(feat, feature(generic_const_parameter_types))]
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #137626 <https://github.com/rust-lang/rust/issues/137626> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: `Self` is forbidden as the type of a const generic parameter
+  --> $DIR/references-parent-generics.rs:7:25
+   |
+LL |     type Assoc<const N: Self>;
+   |                         ^^^^
+   |
+   = note: the only supported types are integers, `bool`, and `char`
+
+error: anonymous constants referencing generics are not yet supported
+  --> $DIR/references-parent-generics.rs:14:21
+   |
+LL |     let x: T::Assoc<3>;
+   |                     ^
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
diff --git a/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr
new file mode 100644
index 0000000000000..64b413188144d
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr
@@ -0,0 +1,16 @@
+error: `Self` is forbidden as the type of a const generic parameter
+  --> $DIR/references-parent-generics.rs:7:25
+   |
+LL |     type Assoc<const N: Self>;
+   |                         ^^^^
+   |
+   = note: the only supported types are integers, `bool`, and `char`
+
+error: anonymous constants referencing generics are not yet supported
+  --> $DIR/references-parent-generics.rs:14:21
+   |
+LL |     let x: T::Assoc<3>;
+   |                     ^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs
new file mode 100644
index 0000000000000..21994eb83b42c
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs
@@ -0,0 +1,18 @@
+//@ revisions: feat nofeat
+
+#![cfg_attr(feat, feature(generic_const_parameter_types))]
+//[feat]~^ WARN the feature `generic_const_parameter_types` is incomplete
+
+trait Foo {
+    type Assoc<const N: Self>;
+    //~^ ERROR `Self` is forbidden as the type of a const generic parameter
+}
+
+fn foo<T: Foo>() {
+    // We used to end up feeding the type of this anon const to be `T`, but the anon const
+    // doesn't inherit the generics of `foo`, which led to index oob errors.
+    let x: T::Assoc<3>;
+    //~^ ERROR anonymous constants referencing generics are not yet supported
+}
+
+fn main() {}

From f525b173edd08cf68f025a5b2db38b665887771d Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 10 Mar 2025 02:31:16 +0000
Subject: [PATCH 05/25] Remove AdtFlags::IS_ANONYMOUS and Copy/Clone condition
 for anonymous ADT

---
 compiler/rustc_middle/src/ty/adt.rs                     | 8 --------
 compiler/rustc_middle/src/ty/mod.rs                     | 2 +-
 compiler/rustc_trait_selection/src/traits/select/mod.rs | 9 ---------
 3 files changed, 1 insertion(+), 18 deletions(-)

diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 3585f28b4a550..c1dc6a894cae5 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -53,8 +53,6 @@ bitflags::bitflags! {
         const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
         /// Indicates whether the type is `UnsafeCell`.
         const IS_UNSAFE_CELL              = 1 << 9;
-        /// Indicates whether the type is anonymous.
-        const IS_ANONYMOUS                = 1 << 10;
     }
 }
 rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -402,12 +400,6 @@ impl<'tcx> AdtDef<'tcx> {
         self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
     }
 
-    /// Returns `true` if this is an anonymous adt
-    #[inline]
-    pub fn is_anonymous(self) -> bool {
-        self.flags().contains(AdtFlags::IS_ANONYMOUS)
-    }
-
     /// Returns `true` if this type has a destructor.
     pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
         self.destructor(tcx).is_some()
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c5509c0a6087e..97cfe5946af15 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1151,7 +1151,7 @@ pub struct VariantDef {
     /// `DefId` that identifies the variant's constructor.
     /// If this variant is a struct variant, then this is `None`.
     pub ctor: Option<(CtorKind, DefId)>,
-    /// Variant or struct name, maybe empty for anonymous adt (struct or union).
+    /// Variant or struct name.
     pub name: Symbol,
     /// Discriminant of this variant.
     pub discr: VariantDiscr,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 0a54b8468fe3c..e1adabbeaa662 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2231,15 +2231,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 }
             }
 
-            // `Copy` and `Clone` are automatically implemented for an anonymous adt
-            // if all of its fields are `Copy` and `Clone`
-            ty::Adt(adt, args) if adt.is_anonymous() => {
-                // (*) binder moved here
-                Where(obligation.predicate.rebind(
-                    adt.non_enum_variant().fields.iter().map(|f| f.ty(self.tcx(), args)).collect(),
-                ))
-            }
-
             ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {
                 // Fallback to whatever user-defined impls exist in this case.
                 None

From 02bb2d4410d4db07c40bed308f8ba0d2af28d069 Mon Sep 17 00:00:00 2001
From: Bastian Kersting <bkersting@google.com>
Date: Tue, 4 Mar 2025 14:31:03 +0000
Subject: [PATCH 06/25] Disable CFI for weakly linked syscalls

Currently, when enabling CFI via -Zsanitizer=cfi and executing e.g.
std::sys::random::getrandom, we can observe a CFI violation. This is
the case for all consumers of the std::sys::pal::weak::weak macro,
as it is defining weak functions which don't show up in LLVM IR
metadata. CFI fails for all these functions.

Similar to other such cases in
https://github.com/rust-lang/rust/issues/115199, this change stops
emitting the CFI typecheck for consumers of the macro via the
\#[no_sanitize(cfi)] attribute.
---
 library/std/src/sys/fs/unix.rs                       | 12 ++++++++++++
 library/std/src/sys/pal/unix/fd.rs                   |  1 +
 library/std/src/sys/pal/unix/process/process_unix.rs |  1 +
 library/std/src/sys/pal/unix/thread.rs               |  1 +
 library/std/src/sys/pal/unix/time.rs                 |  9 +++++++++
 library/std/src/sys/pal/unix/weak.rs                 |  3 +++
 6 files changed, 27 insertions(+)

diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs
index 914971934bfb0..d95735978c199 100644
--- a/library/std/src/sys/fs/unix.rs
+++ b/library/std/src/sys/fs/unix.rs
@@ -1454,6 +1454,18 @@ impl File {
         Ok(())
     }
 
+    #[cfg_attr(
+        any(
+            target_os = "android",
+            all(
+                target_os = "linux",
+                target_env = "gnu",
+                target_pointer_width = "32",
+                not(target_arch = "riscv32")
+            )
+        ),
+        no_sanitize(cfi)
+    )]
     pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
         #[cfg(not(any(
             target_os = "redox",
diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs
index 2fc33bdfefbf5..6da329288f746 100644
--- a/library/std/src/sys/pal/unix/fd.rs
+++ b/library/std/src/sys/pal/unix/fd.rs
@@ -251,6 +251,7 @@ impl FileDesc {
     }
 
     #[cfg(all(target_os = "android", target_pointer_width = "32"))]
+    #[no_sanitize(cfi)]
     pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
         super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
 
diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs
index 25d9e9353320f..6d3680d231e6b 100644
--- a/library/std/src/sys/pal/unix/process/process_unix.rs
+++ b/library/std/src/sys/pal/unix/process/process_unix.rs
@@ -434,6 +434,7 @@ impl Command {
         target_os = "nto",
         target_vendor = "apple",
     ))]
+    #[cfg_attr(target_os = "linux", no_sanitize(cfi))]
     fn posix_spawn(
         &mut self,
         stdio: &ChildPipes,
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 11f6998cac118..33db8e6939a4d 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -188,6 +188,7 @@ impl Thread {
     }
 
     #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
+    #[no_sanitize(cfi)]
     pub fn set_name(name: &CStr) {
         weak! {
             fn pthread_setname_np(
diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs
index 0271626380c5f..fc60c307f34e1 100644
--- a/library/std/src/sys/pal/unix/time.rs
+++ b/library/std/src/sys/pal/unix/time.rs
@@ -96,6 +96,15 @@ impl Timespec {
         }
     }
 
+    #[cfg_attr(
+        all(
+            target_os = "linux",
+            target_env = "gnu",
+            target_pointer_width = "32",
+            not(target_arch = "riscv32")
+        ),
+        no_sanitize(cfi)
+    )]
     pub fn now(clock: libc::clockid_t) -> Timespec {
         use crate::mem::MaybeUninit;
         use crate::sys::cvt;
diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs
index 7ec4787f1eab7..9a718b71f4696 100644
--- a/library/std/src/sys/pal/unix/weak.rs
+++ b/library/std/src/sys/pal/unix/weak.rs
@@ -144,6 +144,9 @@ unsafe fn fetch(name: &str) -> *mut libc::c_void {
 #[cfg(not(any(target_os = "linux", target_os = "android")))]
 pub(crate) macro syscall {
     (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+        // FIXME: Rust currently omits weak function definitions
+        // and its metadata from LLVM IR.
+        #[no_sanitize(cfi)]
         unsafe fn $name($($arg_name: $t),*) -> $ret {
             weak! { fn $name($($t),*) -> $ret }
 

From e5dc1e378619b11b296a953ca08abb2cdf38f9e0 Mon Sep 17 00:00:00 2001
From: Bastian Kersting <bkersting@google.com>
Date: Mon, 10 Mar 2025 08:59:24 +0000
Subject: [PATCH 07/25] Add comments for #[no_sanitize(cfi)] in stdlib

---
 library/std/src/sys/fs/unix.rs                       | 2 ++
 library/std/src/sys/pal/unix/fd.rs                   | 2 ++
 library/std/src/sys/pal/unix/process/process_unix.rs | 2 ++
 library/std/src/sys/pal/unix/thread.rs               | 2 ++
 library/std/src/sys/pal/unix/time.rs                 | 2 ++
 library/std/src/sys/pal/unix/weak.rs                 | 2 +-
 6 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs
index d95735978c199..d944bc9c9a2a0 100644
--- a/library/std/src/sys/fs/unix.rs
+++ b/library/std/src/sys/fs/unix.rs
@@ -1454,6 +1454,8 @@ impl File {
         Ok(())
     }
 
+    // FIXME(#115199): Rust currently omits weak function definitions
+    // and its metadata from LLVM IR.
     #[cfg_attr(
         any(
             target_os = "android",
diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs
index 6da329288f746..a08c7ccec2da3 100644
--- a/library/std/src/sys/pal/unix/fd.rs
+++ b/library/std/src/sys/pal/unix/fd.rs
@@ -251,6 +251,8 @@ impl FileDesc {
     }
 
     #[cfg(all(target_os = "android", target_pointer_width = "32"))]
+    // FIXME(#115199): Rust currently omits weak function definitions
+    // and its metadata from LLVM IR.
     #[no_sanitize(cfi)]
     pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
         super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs
index 6d3680d231e6b..be9a7e919905f 100644
--- a/library/std/src/sys/pal/unix/process/process_unix.rs
+++ b/library/std/src/sys/pal/unix/process/process_unix.rs
@@ -434,6 +434,8 @@ impl Command {
         target_os = "nto",
         target_vendor = "apple",
     ))]
+    // FIXME(#115199): Rust currently omits weak function definitions
+    // and its metadata from LLVM IR.
     #[cfg_attr(target_os = "linux", no_sanitize(cfi))]
     fn posix_spawn(
         &mut self,
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 33db8e6939a4d..abe8d3fbf681e 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -188,6 +188,8 @@ impl Thread {
     }
 
     #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
+    // FIXME(#115199): Rust currently omits weak function definitions
+    // and its metadata from LLVM IR.
     #[no_sanitize(cfi)]
     pub fn set_name(name: &CStr) {
         weak! {
diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs
index fc60c307f34e1..c0a3044660b7e 100644
--- a/library/std/src/sys/pal/unix/time.rs
+++ b/library/std/src/sys/pal/unix/time.rs
@@ -96,6 +96,8 @@ impl Timespec {
         }
     }
 
+    // FIXME(#115199): Rust currently omits weak function definitions
+    // and its metadata from LLVM IR.
     #[cfg_attr(
         all(
             target_os = "linux",
diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs
index 9a718b71f4696..ce3f66a83748b 100644
--- a/library/std/src/sys/pal/unix/weak.rs
+++ b/library/std/src/sys/pal/unix/weak.rs
@@ -144,7 +144,7 @@ unsafe fn fetch(name: &str) -> *mut libc::c_void {
 #[cfg(not(any(target_os = "linux", target_os = "android")))]
 pub(crate) macro syscall {
     (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
-        // FIXME: Rust currently omits weak function definitions
+        // FIXME(#115199): Rust currently omits weak function definitions
         // and its metadata from LLVM IR.
         #[no_sanitize(cfi)]
         unsafe fn $name($($arg_name: $t),*) -> $ret {

From f38819ce17a5e2bfcaf58982956597214d5d034f Mon Sep 17 00:00:00 2001
From: Oli Scherer <github333195615777966@oli-obk.de>
Date: Thu, 30 Jan 2025 07:56:43 +0000
Subject: [PATCH 08/25] Add some layout tests for pattern type edge cases

---
 tests/ui/type/pattern_types/range_patterns.rs |  21 +-
 .../type/pattern_types/range_patterns.stderr  | 210 +++++++++++++++++-
 2 files changed, 229 insertions(+), 2 deletions(-)

diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs
index 446a33195c8bd..3aa8426178a17 100644
--- a/tests/ui/type/pattern_types/range_patterns.rs
+++ b/tests/ui/type/pattern_types/range_patterns.rs
@@ -1,4 +1,4 @@
-#![feature(pattern_types, rustc_attrs)]
+#![feature(pattern_types, rustc_attrs, const_trait_impl, pattern_type_range_trait)]
 #![feature(pattern_type_macro)]
 #![allow(incomplete_features)]
 
@@ -18,6 +18,25 @@ type A = Option<std::num::NonZeroU32>; //~ ERROR layout_of
 #[rustc_layout(debug)]
 struct NonZeroU32New(pattern_type!(u32 is 1..)); //~ ERROR layout_of
 
+#[rustc_layout(debug)]
+type EMPTY = pattern_type!(u32 is 1..1); //~ ERROR layout_of
+
+#[rustc_layout(debug)]
+type WRAP = pattern_type!(u32 is 1..0); //~ ERROR unknown layout
+//~^ ERROR: evaluation of constant value failed
+
+#[rustc_layout(debug)]
+type WRAP2 = pattern_type!(u32 is 5..2); //~ ERROR layout_of
+
+#[rustc_layout(debug)]
+type SIGN = pattern_type!(i8 is -10..=10); //~ ERROR layout_of
+
+#[rustc_layout(debug)]
+type MIN = pattern_type!(i8 is -128..=0); //~ ERROR layout_of
+
+#[rustc_layout(debug)]
+type SignedWrap = pattern_type!(i8 is 120..=-120); //~ ERROR layout_of
+
 fn main() {
     let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(42_u32) };
 }
diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr
index cb24a303404c6..b6abcf462249d 100644
--- a/tests/ui/type/pattern_types/range_patterns.stderr
+++ b/tests/ui/type/pattern_types/range_patterns.stderr
@@ -357,5 +357,213 @@ error: layout_of(NonZeroU32New) = Layout {
 LL | struct NonZeroU32New(pattern_type!(u32 is 1..));
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 5 previous errors
+error: layout_of((u32) is 1..=0) = Layout {
+           size: Size(4 bytes),
+           align: AbiAndPrefAlign {
+               abi: Align(4 bytes),
+               pref: $SOME_ALIGN,
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Int(
+                       I32,
+                       false,
+                   ),
+                   valid_range: (..=0) | (1..),
+               },
+           ),
+           fields: Primitive,
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I32,
+                       false,
+                   ),
+                   valid_range: (..=0) | (1..),
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(4 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/range_patterns.rs:22:1
+   |
+LL | type EMPTY = pattern_type!(u32 is 1..1);
+   | ^^^^^^^^^^
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/range_patterns.rs:25:37
+   |
+LL | type WRAP = pattern_type!(u32 is 1..0);
+   |                                     ^ evaluation panicked: exclusive range end at minimum value of type
+
+error: the type has an unknown layout
+  --> $DIR/range_patterns.rs:25:1
+   |
+LL | type WRAP = pattern_type!(u32 is 1..0);
+   | ^^^^^^^^^
+
+error: layout_of((u32) is 5..=1) = Layout {
+           size: Size(4 bytes),
+           align: AbiAndPrefAlign {
+               abi: Align(4 bytes),
+               pref: $SOME_ALIGN,
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Int(
+                       I32,
+                       false,
+                   ),
+                   valid_range: (..=1) | (5..),
+               },
+           ),
+           fields: Primitive,
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I32,
+                       false,
+                   ),
+                   valid_range: (..=1) | (5..),
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(4 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/range_patterns.rs:29:1
+   |
+LL | type WRAP2 = pattern_type!(u32 is 5..2);
+   | ^^^^^^^^^^
+
+error: layout_of((i8) is -10..=10) = Layout {
+           size: Size(1 bytes),
+           align: AbiAndPrefAlign {
+               abi: Align(1 bytes),
+               pref: $SOME_ALIGN,
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Int(
+                       I8,
+                       true,
+                   ),
+                   valid_range: (..=10) | (246..),
+               },
+           ),
+           fields: Primitive,
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I8,
+                       true,
+                   ),
+                   valid_range: (..=10) | (246..),
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(1 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/range_patterns.rs:32:1
+   |
+LL | type SIGN = pattern_type!(i8 is -10..=10);
+   | ^^^^^^^^^
+
+error: layout_of((i8) is i8::MIN..=0) = Layout {
+           size: Size(1 bytes),
+           align: AbiAndPrefAlign {
+               abi: Align(1 bytes),
+               pref: $SOME_ALIGN,
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Int(
+                       I8,
+                       true,
+                   ),
+                   valid_range: (..=0) | (128..),
+               },
+           ),
+           fields: Primitive,
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I8,
+                       true,
+                   ),
+                   valid_range: (..=0) | (128..),
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(1 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/range_patterns.rs:35:1
+   |
+LL | type MIN = pattern_type!(i8 is -128..=0);
+   | ^^^^^^^^
+
+error: layout_of((i8) is 120..=-120) = Layout {
+           size: Size(1 bytes),
+           align: AbiAndPrefAlign {
+               abi: Align(1 bytes),
+               pref: $SOME_ALIGN,
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Int(
+                       I8,
+                       true,
+                   ),
+                   valid_range: 120..=136,
+               },
+           ),
+           fields: Primitive,
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Int(
+                       I8,
+                       true,
+                   ),
+                   valid_range: 120..=136,
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(1 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/range_patterns.rs:38:1
+   |
+LL | type SignedWrap = pattern_type!(i8 is 120..=-120);
+   | ^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
 
+For more information about this error, try `rustc --explain E0080`.

From 916f9552e975287d2bc9aed16d3a1c35b7c52695 Mon Sep 17 00:00:00 2001
From: Oli Scherer <github333195615777966@oli-obk.de>
Date: Thu, 30 Jan 2025 08:41:20 +0000
Subject: [PATCH 09/25] Reject wrapping ranges of pattern types

---
 compiler/rustc_ty_utils/src/layout.rs         |  28 +++++
 tests/ui/type/pattern_types/range_patterns.rs |   6 +-
 .../type/pattern_types/range_patterns.stderr  | 113 ++----------------
 3 files changed, 41 insertions(+), 106 deletions(-)

diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 5a4bb2c95da6f..7334beb52c9d5 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -220,6 +220,34 @@ fn layout_of_uncached<'tcx>(
                             .try_to_bits(tcx, cx.typing_env)
                             .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
 
+                        // FIXME(pattern_types): create implied bounds from pattern types in signatures
+                        // that require that the range end is >= the range start so that we can't hit
+                        // this error anymore without first having hit a trait solver error.
+                        // Very fuzzy on the details here, but pattern types are an internal impl detail,
+                        // so we can just go with this for now
+                        if scalar.is_signed() {
+                            let range = scalar.valid_range_mut();
+                            let start = layout.size.sign_extend(range.start);
+                            let end = layout.size.sign_extend(range.end);
+                            if end < start {
+                                let guar = tcx.dcx().err(format!(
+                                    "pattern type ranges cannot wrap: {start}..={end}"
+                                ));
+
+                                return Err(error(cx, LayoutError::ReferencesError(guar)));
+                            }
+                        } else {
+                            let range = scalar.valid_range_mut();
+                            if range.end < range.start {
+                                let guar = tcx.dcx().err(format!(
+                                    "pattern type ranges cannot wrap: {}..={}",
+                                    range.start, range.end
+                                ));
+
+                                return Err(error(cx, LayoutError::ReferencesError(guar)));
+                            }
+                        };
+
                         let niche = Niche {
                             offset: Size::ZERO,
                             value: scalar.primitive(),
diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs
index 3aa8426178a17..dda7eb0ae4eca 100644
--- a/tests/ui/type/pattern_types/range_patterns.rs
+++ b/tests/ui/type/pattern_types/range_patterns.rs
@@ -19,14 +19,14 @@ type A = Option<std::num::NonZeroU32>; //~ ERROR layout_of
 struct NonZeroU32New(pattern_type!(u32 is 1..)); //~ ERROR layout_of
 
 #[rustc_layout(debug)]
-type EMPTY = pattern_type!(u32 is 1..1); //~ ERROR layout_of
+type EMPTY = pattern_type!(u32 is 1..1); //~ ERROR unknown layout
 
 #[rustc_layout(debug)]
 type WRAP = pattern_type!(u32 is 1..0); //~ ERROR unknown layout
 //~^ ERROR: evaluation of constant value failed
 
 #[rustc_layout(debug)]
-type WRAP2 = pattern_type!(u32 is 5..2); //~ ERROR layout_of
+type WRAP2 = pattern_type!(u32 is 5..2); //~ ERROR unknown layout
 
 #[rustc_layout(debug)]
 type SIGN = pattern_type!(i8 is -10..=10); //~ ERROR layout_of
@@ -35,7 +35,7 @@ type SIGN = pattern_type!(i8 is -10..=10); //~ ERROR layout_of
 type MIN = pattern_type!(i8 is -128..=0); //~ ERROR layout_of
 
 #[rustc_layout(debug)]
-type SignedWrap = pattern_type!(i8 is 120..=-120); //~ ERROR layout_of
+type SignedWrap = pattern_type!(i8 is 120..=-120); //~ ERROR unknown layout
 
 fn main() {
     let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(42_u32) };
diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr
index b6abcf462249d..a05995a33f9ba 100644
--- a/tests/ui/type/pattern_types/range_patterns.stderr
+++ b/tests/ui/type/pattern_types/range_patterns.stderr
@@ -357,40 +357,9 @@ error: layout_of(NonZeroU32New) = Layout {
 LL | struct NonZeroU32New(pattern_type!(u32 is 1..));
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: layout_of((u32) is 1..=0) = Layout {
-           size: Size(4 bytes),
-           align: AbiAndPrefAlign {
-               abi: Align(4 bytes),
-               pref: $SOME_ALIGN,
-           },
-           backend_repr: Scalar(
-               Initialized {
-                   value: Int(
-                       I32,
-                       false,
-                   ),
-                   valid_range: (..=0) | (1..),
-               },
-           ),
-           fields: Primitive,
-           largest_niche: Some(
-               Niche {
-                   offset: Size(0 bytes),
-                   value: Int(
-                       I32,
-                       false,
-                   ),
-                   valid_range: (..=0) | (1..),
-               },
-           ),
-           uninhabited: false,
-           variants: Single {
-               index: 0,
-           },
-           max_repr_align: None,
-           unadjusted_abi_align: Align(4 bytes),
-           randomization_seed: $SEED,
-       }
+error: pattern type ranges cannot wrap: 1..=0
+
+error: the type has an unknown layout
   --> $DIR/range_patterns.rs:22:1
    |
 LL | type EMPTY = pattern_type!(u32 is 1..1);
@@ -408,40 +377,9 @@ error: the type has an unknown layout
 LL | type WRAP = pattern_type!(u32 is 1..0);
    | ^^^^^^^^^
 
-error: layout_of((u32) is 5..=1) = Layout {
-           size: Size(4 bytes),
-           align: AbiAndPrefAlign {
-               abi: Align(4 bytes),
-               pref: $SOME_ALIGN,
-           },
-           backend_repr: Scalar(
-               Initialized {
-                   value: Int(
-                       I32,
-                       false,
-                   ),
-                   valid_range: (..=1) | (5..),
-               },
-           ),
-           fields: Primitive,
-           largest_niche: Some(
-               Niche {
-                   offset: Size(0 bytes),
-                   value: Int(
-                       I32,
-                       false,
-                   ),
-                   valid_range: (..=1) | (5..),
-               },
-           ),
-           uninhabited: false,
-           variants: Single {
-               index: 0,
-           },
-           max_repr_align: None,
-           unadjusted_abi_align: Align(4 bytes),
-           randomization_seed: $SEED,
-       }
+error: pattern type ranges cannot wrap: 5..=1
+
+error: the type has an unknown layout
   --> $DIR/range_patterns.rs:29:1
    |
 LL | type WRAP2 = pattern_type!(u32 is 5..2);
@@ -525,45 +463,14 @@ error: layout_of((i8) is i8::MIN..=0) = Layout {
 LL | type MIN = pattern_type!(i8 is -128..=0);
    | ^^^^^^^^
 
-error: layout_of((i8) is 120..=-120) = Layout {
-           size: Size(1 bytes),
-           align: AbiAndPrefAlign {
-               abi: Align(1 bytes),
-               pref: $SOME_ALIGN,
-           },
-           backend_repr: Scalar(
-               Initialized {
-                   value: Int(
-                       I8,
-                       true,
-                   ),
-                   valid_range: 120..=136,
-               },
-           ),
-           fields: Primitive,
-           largest_niche: Some(
-               Niche {
-                   offset: Size(0 bytes),
-                   value: Int(
-                       I8,
-                       true,
-                   ),
-                   valid_range: 120..=136,
-               },
-           ),
-           uninhabited: false,
-           variants: Single {
-               index: 0,
-           },
-           max_repr_align: None,
-           unadjusted_abi_align: Align(1 bytes),
-           randomization_seed: $SEED,
-       }
+error: pattern type ranges cannot wrap: 120..=-120
+
+error: the type has an unknown layout
   --> $DIR/range_patterns.rs:38:1
    |
 LL | type SignedWrap = pattern_type!(i8 is 120..=-120);
    | ^^^^^^^^^^^^^^^
 
-error: aborting due to 12 previous errors
+error: aborting due to 15 previous errors
 
 For more information about this error, try `rustc --explain E0080`.

From 9d87d4e4f5b249312408058fdbb26d99693adf15 Mon Sep 17 00:00:00 2001
From: Oli Scherer <github333195615777966@oli-obk.de>
Date: Wed, 29 Jan 2025 16:37:58 +0000
Subject: [PATCH 10/25] Add tests for pattern type literals

---
 tests/ui/type/pattern_types/literals.rs     | 143 ++++++++++
 tests/ui/type/pattern_types/literals.stderr | 276 ++++++++++++++++++++
 2 files changed, 419 insertions(+)
 create mode 100644 tests/ui/type/pattern_types/literals.rs
 create mode 100644 tests/ui/type/pattern_types/literals.stderr

diff --git a/tests/ui/type/pattern_types/literals.rs b/tests/ui/type/pattern_types/literals.rs
new file mode 100644
index 0000000000000..7ff3fdf092052
--- /dev/null
+++ b/tests/ui/type/pattern_types/literals.rs
@@ -0,0 +1,143 @@
+//! Check where literals can be used to initialize pattern types and where not.
+
+#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
+#![feature(pattern_type_macro)]
+
+use std::pat::pattern_type;
+
+fn out_of_range() -> pattern_type!(u32 is 1..) {
+    0
+    //~^ mismatched types
+}
+
+fn at_range_start() -> pattern_type!(u32 is 1..) {
+    1
+    //~^ mismatched types
+}
+
+fn in_range() -> pattern_type!(u32 is 1..) {
+    2
+    //~^ mismatched types
+}
+
+fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) {
+    -3
+    //~^ mismatched types
+}
+
+fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) {
+    -2
+    //~^ mismatched types
+}
+
+fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) {
+    2
+    //~^ mismatched types
+}
+
+fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) {
+    -5
+    //~^ mismatched types
+}
+
+fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) {
+    4
+    //~^ mismatched types
+}
+
+fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) {
+    5
+    //~^ mismatched types
+}
+
+fn wrong_lit_kind() -> pattern_type!(u32 is 1..) {
+    '3'
+    //~^ mismatched types
+}
+
+fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') {
+    'b'
+    //~^ mismatched types
+}
+
+fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') {
+    'A'
+    //~^ mismatched types
+}
+
+fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) {
+    1
+    //~^ mismatched types
+}
+
+fn single_element_range() -> pattern_type!(u32 is 0..=0) {
+    0
+    //~^ mismatched types
+}
+
+fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) {
+    1
+    //~^ mismatched types
+}
+
+fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
+    1
+    //~^ mismatched types
+}
+
+fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
+    0
+    //~^ mismatched types
+}
+
+fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) {
+    //~^ evaluation of constant value failed
+    0
+}
+
+fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) {
+    //~^ evaluation of constant value failed
+    1
+}
+
+fn empty_range() -> pattern_type!(u32 is 1..1) {
+    0
+    //~^ mismatched types
+}
+
+fn empty_range2() -> pattern_type!(u32 is 1..1) {
+    1
+    //~^ mismatched types
+}
+
+fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) {
+    //~^ evaluation of constant value failed
+    1
+}
+
+fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) {
+    //~^ evaluation of constant value failed
+    0
+}
+
+fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) {
+    //~^ evaluation of constant value failed
+    2
+}
+
+fn wraparound_range() -> pattern_type!(u32 is 2..1) {
+    1
+    //~^ mismatched types
+}
+
+fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) {
+    0
+    //~^ mismatched types
+}
+
+fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) {
+    2
+    //~^ mismatched types
+}
+
+fn main() {}
diff --git a/tests/ui/type/pattern_types/literals.stderr b/tests/ui/type/pattern_types/literals.stderr
new file mode 100644
index 0000000000000..ae09872862035
--- /dev/null
+++ b/tests/ui/type/pattern_types/literals.stderr
@@ -0,0 +1,276 @@
+error[E0080]: evaluation of constant value failed
+  --> $DIR/literals.rs:93:62
+   |
+LL | fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) {
+   |                                                              ^ evaluation panicked: exclusive range end at minimum value of type
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/literals.rs:98:63
+   |
+LL | fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) {
+   |                                                               ^ evaluation panicked: exclusive range end at minimum value of type
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/literals.rs:113:65
+   |
+LL | fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) {
+   |                                                                 ^ evaluation panicked: exclusive range end at minimum value of type
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/literals.rs:118:66
+   |
+LL | fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) {
+   |                                                                  ^ evaluation panicked: exclusive range end at minimum value of type
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/literals.rs:123:66
+   |
+LL | fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) {
+   |                                                                  ^ evaluation panicked: exclusive range end at minimum value of type
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:9:5
+   |
+LL | fn out_of_range() -> pattern_type!(u32 is 1..) {
+   |                      ------------------------- expected `(u32) is 1..` because of return type
+LL |     0
+   |     ^ expected `(u32) is 1..`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:14:5
+   |
+LL | fn at_range_start() -> pattern_type!(u32 is 1..) {
+   |                        ------------------------- expected `(u32) is 1..` because of return type
+LL |     1
+   |     ^ expected `(u32) is 1..`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:19:5
+   |
+LL | fn in_range() -> pattern_type!(u32 is 1..) {
+   |                  ------------------------- expected `(u32) is 1..` because of return type
+LL |     2
+   |     ^ expected `(u32) is 1..`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:24:5
+   |
+LL | fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) {
+   |                                     ------------------------- expected `(u32) is 1..` because of return type
+LL |     -3
+   |     ^^ expected `(u32) is 1..`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:29:5
+   |
+LL | fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) {
+   |                               -------------------------- expected `(i8) is -5..=4` because of return type
+LL |     -2
+   |     ^^ expected `(i8) is -5..=4`, found integer
+   |
+   = note: expected pattern type `(i8) is -5..=4`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:34:5
+   |
+LL | fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) {
+   |                                         -------------------------- expected `(i8) is -5..=4` because of return type
+LL |     2
+   |     ^ expected `(i8) is -5..=4`, found integer
+   |
+   = note: expected pattern type `(i8) is -5..=4`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:39:5
+   |
+LL | fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) {
+   |                                     -------------------------- expected `(i8) is -5..=4` because of return type
+LL |     -5
+   |     ^^ expected `(i8) is -5..=4`, found integer
+   |
+   = note: expected pattern type `(i8) is -5..=4`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:44:5
+   |
+LL | fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) {
+   |                                   -------------------------- expected `(i8) is -5..=4` because of return type
+LL |     4
+   |     ^ expected `(i8) is -5..=4`, found integer
+   |
+   = note: expected pattern type `(i8) is -5..=4`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:49:5
+   |
+LL | fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) {
+   |                                  -------------------------- expected `(i8) is -5..=4` because of return type
+LL |     5
+   |     ^ expected `(i8) is -5..=4`, found integer
+   |
+   = note: expected pattern type `(i8) is -5..=4`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:54:5
+   |
+LL | fn wrong_lit_kind() -> pattern_type!(u32 is 1..) {
+   |                        ------------------------- expected `(u32) is 1..` because of return type
+LL |     '3'
+   |     ^^^ expected `(u32) is 1..`, found `char`
+   |
+   = note: expected pattern type `(u32) is 1..`
+                      found type `char`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:59:5
+   |
+LL | fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') {
+   |                           ------------------------------- expected `(char) is 'a'..='y'` because of return type
+LL |     'b'
+   |     ^^^ expected `(char) is 'a'..='y'`, found `char`
+   |
+   = note: expected pattern type `(char) is 'a'..='y'`
+                      found type `char`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:64:5
+   |
+LL | fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') {
+   |                               ------------------------------- expected `(char) is 'a'..='y'` because of return type
+LL |     'A'
+   |     ^^^ expected `(char) is 'a'..='y'`, found `char`
+   |
+   = note: expected pattern type `(char) is 'a'..='y'`
+                      found type `char`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:69:5
+   |
+LL | fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) {
+   |                                             --------------------------- expected `(u32) is 0..=1` because of return type
+LL |     1
+   |     ^ expected `(u32) is 0..=1`, found integer
+   |
+   = note: expected pattern type `(u32) is 0..=1`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:74:5
+   |
+LL | fn single_element_range() -> pattern_type!(u32 is 0..=0) {
+   |                              --------------------------- expected `(u32) is 0..=0` because of return type
+LL |     0
+   |     ^ expected `(u32) is 0..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 0..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:79:5
+   |
+LL | fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) {
+   |                                      --------------------------- expected `(u32) is 0..=0` because of return type
+LL |     1
+   |     ^ expected `(u32) is 0..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 0..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:84:5
+   |
+LL | fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
+   |                                                -------------------------- expected `(u32) is 0..=0` because of return type
+LL |     1
+   |     ^ expected `(u32) is 0..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 0..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:89:5
+   |
+LL | fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
+   |                                        -------------------------- expected `(u32) is 0..=0` because of return type
+LL |     0
+   |     ^ expected `(u32) is 0..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 0..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:104:5
+   |
+LL | fn empty_range() -> pattern_type!(u32 is 1..1) {
+   |                     -------------------------- expected `(u32) is 1..=0` because of return type
+LL |     0
+   |     ^ expected `(u32) is 1..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:109:5
+   |
+LL | fn empty_range2() -> pattern_type!(u32 is 1..1) {
+   |                      -------------------------- expected `(u32) is 1..=0` because of return type
+LL |     1
+   |     ^ expected `(u32) is 1..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 1..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:129:5
+   |
+LL | fn wraparound_range() -> pattern_type!(u32 is 2..1) {
+   |                          -------------------------- expected `(u32) is 2..=0` because of return type
+LL |     1
+   |     ^ expected `(u32) is 2..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 2..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:134:5
+   |
+LL | fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) {
+   |                                 -------------------------- expected `(u32) is 2..=0` because of return type
+LL |     0
+   |     ^ expected `(u32) is 2..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 2..=0`
+                      found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/literals.rs:139:5
+   |
+LL | fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) {
+   |                                       -------------------------- expected `(u32) is 2..=0` because of return type
+LL |     2
+   |     ^ expected `(u32) is 2..=0`, found integer
+   |
+   = note: expected pattern type `(u32) is 2..=0`
+                      found type `{integer}`
+
+error: aborting due to 27 previous errors
+
+Some errors have detailed explanations: E0080, E0308.
+For more information about an error, try `rustc --explain E0080`.

From f87e58f1948c493aef6d645f339c2b9043e5ba59 Mon Sep 17 00:00:00 2001
From: Oli Scherer <github333195615777966@oli-obk.de>
Date: Wed, 29 Jan 2025 16:44:08 +0000
Subject: [PATCH 11/25] Allow int literals for pattern types with int base
 types

---
 .../rustc_hir_typeck/src/fn_ctxt/checks.rs    |  15 +-
 .../src/builder/expr/as_constant.rs           |   7 +-
 tests/ui/type/pattern_types/literals.rs       |  11 +-
 tests/ui/type/pattern_types/literals.stderr   | 139 ++++--------------
 tests/ui/type/pattern_types/nested.rs         |   4 +-
 tests/ui/type/pattern_types/nested.stderr     |   9 +-
 6 files changed, 55 insertions(+), 130 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index db947b6744da6..253ba74a19127 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1647,7 +1647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ast::LitKind::Char(_) => tcx.types.char,
             ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)),
             ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)),
-            ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
+            ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => {
                 let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
                     ty::Int(_) | ty::Uint(_) => Some(ty),
                     // These exist to direct casts like `0x61 as char` to use
@@ -1656,6 +1656,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ty::Char => Some(tcx.types.u8),
                     ty::RawPtr(..) => Some(tcx.types.usize),
                     ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize),
+                    &ty::Pat(base, _) if base.is_integral() => {
+                        let layout = tcx
+                            .layout_of(self.typing_env(self.param_env).as_query_input(ty))
+                            .ok()?;
+                        assert!(!layout.uninhabited);
+
+                        match layout.backend_repr {
+                            rustc_abi::BackendRepr::Scalar(scalar) => {
+                                scalar.valid_range(&tcx).contains(u128::from(i.get())).then_some(ty)
+                            }
+                            _ => unreachable!(),
+                        }
+                    }
                     _ => None,
                 });
                 opt_ty.unwrap_or_else(|| self.next_int_var())
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
index 84c9297e65898..f743ea60a456c 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
@@ -117,7 +117,12 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
         ConstValue::Scalar(Scalar::from_uint(result, width))
     };
 
-    let value = match (lit, ty.kind()) {
+    let lit_ty = match *ty.kind() {
+        ty::Pat(base, _) => base,
+        _ => ty,
+    };
+
+    let value = match (lit, lit_ty.kind()) {
         (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
             let s = s.as_str();
             let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
diff --git a/tests/ui/type/pattern_types/literals.rs b/tests/ui/type/pattern_types/literals.rs
index 7ff3fdf092052..97a918645f3e5 100644
--- a/tests/ui/type/pattern_types/literals.rs
+++ b/tests/ui/type/pattern_types/literals.rs
@@ -12,27 +12,24 @@ fn out_of_range() -> pattern_type!(u32 is 1..) {
 
 fn at_range_start() -> pattern_type!(u32 is 1..) {
     1
-    //~^ mismatched types
 }
 
 fn in_range() -> pattern_type!(u32 is 1..) {
     2
-    //~^ mismatched types
 }
 
 fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) {
     -3
-    //~^ mismatched types
+    //~^ ERROR: cannot apply unary operator `-` to type `(u32) is 1..`
 }
 
 fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) {
     -2
-    //~^ mismatched types
+    //~^ ERROR: cannot apply unary operator `-` to type `(i8) is -5..=4`
 }
 
 fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) {
     2
-    //~^ mismatched types
 }
 
 fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) {
@@ -42,7 +39,6 @@ fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) {
 
 fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) {
     4
-    //~^ mismatched types
 }
 
 fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) {
@@ -67,12 +63,10 @@ fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') {
 
 fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) {
     1
-    //~^ mismatched types
 }
 
 fn single_element_range() -> pattern_type!(u32 is 0..=0) {
     0
-    //~^ mismatched types
 }
 
 fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) {
@@ -87,7 +81,6 @@ fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
 
 fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
     0
-    //~^ mismatched types
 }
 
 fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) {
diff --git a/tests/ui/type/pattern_types/literals.stderr b/tests/ui/type/pattern_types/literals.stderr
index ae09872862035..5c926742f3cdb 100644
--- a/tests/ui/type/pattern_types/literals.stderr
+++ b/tests/ui/type/pattern_types/literals.stderr
@@ -1,29 +1,29 @@
 error[E0080]: evaluation of constant value failed
-  --> $DIR/literals.rs:93:62
+  --> $DIR/literals.rs:86:62
    |
 LL | fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) {
    |                                                              ^ evaluation panicked: exclusive range end at minimum value of type
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/literals.rs:98:63
+  --> $DIR/literals.rs:91:63
    |
 LL | fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) {
    |                                                               ^ evaluation panicked: exclusive range end at minimum value of type
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/literals.rs:113:65
+  --> $DIR/literals.rs:106:65
    |
 LL | fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) {
    |                                                                 ^ evaluation panicked: exclusive range end at minimum value of type
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/literals.rs:118:66
+  --> $DIR/literals.rs:111:66
    |
 LL | fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) {
    |                                                                  ^ evaluation panicked: exclusive range end at minimum value of type
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/literals.rs:123:66
+  --> $DIR/literals.rs:116:66
    |
 LL | fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) {
    |                                                                  ^ evaluation panicked: exclusive range end at minimum value of type
@@ -39,63 +39,20 @@ LL |     0
    = note: expected pattern type `(u32) is 1..`
                       found type `{integer}`
 
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:14:5
-   |
-LL | fn at_range_start() -> pattern_type!(u32 is 1..) {
-   |                        ------------------------- expected `(u32) is 1..` because of return type
-LL |     1
-   |     ^ expected `(u32) is 1..`, found integer
-   |
-   = note: expected pattern type `(u32) is 1..`
-                      found type `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:19:5
-   |
-LL | fn in_range() -> pattern_type!(u32 is 1..) {
-   |                  ------------------------- expected `(u32) is 1..` because of return type
-LL |     2
-   |     ^ expected `(u32) is 1..`, found integer
-   |
-   = note: expected pattern type `(u32) is 1..`
-                      found type `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:24:5
+error[E0600]: cannot apply unary operator `-` to type `(u32) is 1..`
+  --> $DIR/literals.rs:22:5
    |
-LL | fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) {
-   |                                     ------------------------- expected `(u32) is 1..` because of return type
 LL |     -3
-   |     ^^ expected `(u32) is 1..`, found integer
-   |
-   = note: expected pattern type `(u32) is 1..`
-                      found type `{integer}`
+   |     ^^ cannot apply unary operator `-`
 
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:29:5
+error[E0600]: cannot apply unary operator `-` to type `(i8) is -5..=4`
+  --> $DIR/literals.rs:27:5
    |
-LL | fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) {
-   |                               -------------------------- expected `(i8) is -5..=4` because of return type
 LL |     -2
-   |     ^^ expected `(i8) is -5..=4`, found integer
-   |
-   = note: expected pattern type `(i8) is -5..=4`
-                      found type `{integer}`
+   |     ^^ cannot apply unary operator `-`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:34:5
-   |
-LL | fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) {
-   |                                         -------------------------- expected `(i8) is -5..=4` because of return type
-LL |     2
-   |     ^ expected `(i8) is -5..=4`, found integer
-   |
-   = note: expected pattern type `(i8) is -5..=4`
-                      found type `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:39:5
+  --> $DIR/literals.rs:36:5
    |
 LL | fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) {
    |                                     -------------------------- expected `(i8) is -5..=4` because of return type
@@ -106,18 +63,7 @@ LL |     -5
                       found type `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:44:5
-   |
-LL | fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) {
-   |                                   -------------------------- expected `(i8) is -5..=4` because of return type
-LL |     4
-   |     ^ expected `(i8) is -5..=4`, found integer
-   |
-   = note: expected pattern type `(i8) is -5..=4`
-                      found type `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:49:5
+  --> $DIR/literals.rs:45:5
    |
 LL | fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) {
    |                                  -------------------------- expected `(i8) is -5..=4` because of return type
@@ -128,7 +74,7 @@ LL |     5
                       found type `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:54:5
+  --> $DIR/literals.rs:50:5
    |
 LL | fn wrong_lit_kind() -> pattern_type!(u32 is 1..) {
    |                        ------------------------- expected `(u32) is 1..` because of return type
@@ -139,7 +85,7 @@ LL |     '3'
                       found type `char`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:59:5
+  --> $DIR/literals.rs:55:5
    |
 LL | fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') {
    |                           ------------------------------- expected `(char) is 'a'..='y'` because of return type
@@ -150,7 +96,7 @@ LL |     'b'
                       found type `char`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:64:5
+  --> $DIR/literals.rs:60:5
    |
 LL | fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') {
    |                               ------------------------------- expected `(char) is 'a'..='y'` because of return type
@@ -161,29 +107,7 @@ LL |     'A'
                       found type `char`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:69:5
-   |
-LL | fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) {
-   |                                             --------------------------- expected `(u32) is 0..=1` because of return type
-LL |     1
-   |     ^ expected `(u32) is 0..=1`, found integer
-   |
-   = note: expected pattern type `(u32) is 0..=1`
-                      found type `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:74:5
-   |
-LL | fn single_element_range() -> pattern_type!(u32 is 0..=0) {
-   |                              --------------------------- expected `(u32) is 0..=0` because of return type
-LL |     0
-   |     ^ expected `(u32) is 0..=0`, found integer
-   |
-   = note: expected pattern type `(u32) is 0..=0`
-                      found type `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:79:5
+  --> $DIR/literals.rs:73:5
    |
 LL | fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) {
    |                                      --------------------------- expected `(u32) is 0..=0` because of return type
@@ -194,7 +118,7 @@ LL |     1
                       found type `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:84:5
+  --> $DIR/literals.rs:78:5
    |
 LL | fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
    |                                                -------------------------- expected `(u32) is 0..=0` because of return type
@@ -204,19 +128,10 @@ LL |     1
    = note: expected pattern type `(u32) is 0..=0`
                       found type `{integer}`
 
-error[E0308]: mismatched types
-  --> $DIR/literals.rs:89:5
-   |
-LL | fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
-   |                                        -------------------------- expected `(u32) is 0..=0` because of return type
-LL |     0
-   |     ^ expected `(u32) is 0..=0`, found integer
-   |
-   = note: expected pattern type `(u32) is 0..=0`
-                      found type `{integer}`
+error: pattern type ranges cannot wrap: 1..=0
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:104:5
+  --> $DIR/literals.rs:97:5
    |
 LL | fn empty_range() -> pattern_type!(u32 is 1..1) {
    |                     -------------------------- expected `(u32) is 1..=0` because of return type
@@ -227,7 +142,7 @@ LL |     0
                       found type `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:109:5
+  --> $DIR/literals.rs:102:5
    |
 LL | fn empty_range2() -> pattern_type!(u32 is 1..1) {
    |                      -------------------------- expected `(u32) is 1..=0` because of return type
@@ -237,8 +152,10 @@ LL |     1
    = note: expected pattern type `(u32) is 1..=0`
                       found type `{integer}`
 
+error: pattern type ranges cannot wrap: 2..=0
+
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:129:5
+  --> $DIR/literals.rs:122:5
    |
 LL | fn wraparound_range() -> pattern_type!(u32 is 2..1) {
    |                          -------------------------- expected `(u32) is 2..=0` because of return type
@@ -249,7 +166,7 @@ LL |     1
                       found type `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:134:5
+  --> $DIR/literals.rs:127:5
    |
 LL | fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) {
    |                                 -------------------------- expected `(u32) is 2..=0` because of return type
@@ -260,7 +177,7 @@ LL |     0
                       found type `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/literals.rs:139:5
+  --> $DIR/literals.rs:132:5
    |
 LL | fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) {
    |                                       -------------------------- expected `(u32) is 2..=0` because of return type
@@ -270,7 +187,7 @@ LL |     2
    = note: expected pattern type `(u32) is 2..=0`
                       found type `{integer}`
 
-error: aborting due to 27 previous errors
+error: aborting due to 22 previous errors
 
-Some errors have detailed explanations: E0080, E0308.
+Some errors have detailed explanations: E0080, E0308, E0600.
 For more information about an error, try `rustc --explain E0080`.
diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs
index 0d8cd22190e55..fd950d7329148 100644
--- a/tests/ui/type/pattern_types/nested.rs
+++ b/tests/ui/type/pattern_types/nested.rs
@@ -1,6 +1,6 @@
 //! Check that pattern types can only have specific base types
 
-#![feature(pattern_types)]
+#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
 #![feature(pattern_type_macro)]
 
 use std::pat::pattern_type;
@@ -14,7 +14,7 @@ const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
 // We want to get the most narrowest version that a pattern could be
 const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
 //~^ ERROR: not a valid base type for range patterns
-//~| ERROR: mismatched types
+//~| ERROR: cannot apply unary operator `-` to type `(i32) is 1..`
 
 const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
 //~^ ERROR: not a valid base type for range patterns
diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr
index f79d12bc3f376..bb206d9db3db8 100644
--- a/tests/ui/type/pattern_types/nested.stderr
+++ b/tests/ui/type/pattern_types/nested.stderr
@@ -43,14 +43,11 @@ LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = tod
              u128
            and 5 others
 
-error[E0308]: mismatched types
+error[E0600]: cannot apply unary operator `-` to type `(i32) is 1..`
   --> $DIR/nested.rs:15:67
    |
 LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
-   |                                                                   ^^ expected `(i32) is 1..`, found integer
-   |
-   = note: expected pattern type `(i32) is 1..`
-                      found type `{integer}`
+   |                                                                   ^^ cannot apply unary operator `-`
 
 error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
   --> $DIR/nested.rs:19:35
@@ -180,5 +177,5 @@ LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
 
 error: aborting due to 11 previous errors
 
-Some errors have detailed explanations: E0277, E0308.
+Some errors have detailed explanations: E0277, E0308, E0600.
 For more information about an error, try `rustc --explain E0277`.

From 53237c86564deb04def176251a58bd967f941ceb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Wed, 5 Mar 2025 11:00:56 +0100
Subject: [PATCH 12/25] Refactor GCC compilation

---
 src/bootstrap/src/core/build_steps/gcc.rs | 217 +++++++++++-----------
 1 file changed, 112 insertions(+), 105 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index 70789fbbeeba0..1382d5b07f919 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -14,13 +14,68 @@ use std::sync::OnceLock;
 
 use build_helper::ci::CiEnv;
 
-use crate::Kind;
-use crate::core::builder::{Builder, Cargo, RunConfig, ShouldRun, Step};
+use crate::Config;
+use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
 use crate::utils::exec::command;
 use crate::utils::helpers::{self, t};
 
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct Gcc {
+    pub target: TargetSelection,
+}
+
+#[derive(Clone)]
+pub struct GccOutput {
+    pub libgccjit: PathBuf,
+}
+
+impl Step for Gcc {
+    type Output = GccOutput;
+
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/gcc").alias("gcc")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(Gcc { target: run.target });
+    }
+
+    /// Compile GCC (specifically `libgccjit`) for `target`.
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let target = self.target;
+
+        // If GCC has already been built, we avoid building it again.
+        let metadata = match get_gcc_build_status(builder, target) {
+            GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path },
+            GccBuildStatus::ShouldBuild(m) => m,
+        };
+
+        let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
+        t!(metadata.stamp.remove());
+        let _time = helpers::timeit(builder);
+
+        let libgccjit_path = libgccjit_built_path(&metadata.install_dir);
+        if builder.config.dry_run() {
+            return GccOutput { libgccjit: libgccjit_path };
+        }
+
+        build_gcc(&metadata, builder, target);
+
+        let lib_alias = metadata.install_dir.join("lib/libgccjit.so.0");
+        if !lib_alias.exists() {
+            t!(builder.symlink_file(&libgccjit_path, lib_alias));
+        }
+
+        t!(metadata.stamp.write());
+
+        GccOutput { libgccjit: libgccjit_path }
+    }
+}
+
 pub struct Meta {
     stamp: BuildStamp,
     out_dir: PathBuf,
@@ -38,7 +93,7 @@ pub enum GccBuildStatus {
 ///
 /// It's used to avoid busting caches during x.py check -- if we've already built
 /// GCC, it's fine for us to not try to avoid doing so.
-pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
+pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
     // Initialize the gcc submodule if not initialized already.
     builder.config.update_submodule("src/gcc");
 
@@ -87,104 +142,65 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
     install_dir.join("lib/libgccjit.so")
 }
 
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub struct Gcc {
-    pub target: TargetSelection,
-}
-
-#[derive(Clone)]
-pub struct GccOutput {
-    pub libgccjit: PathBuf,
-}
-
-impl Step for Gcc {
-    type Output = GccOutput;
-
-    const ONLY_HOSTS: bool = true;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("src/gcc").alias("gcc")
-    }
-
-    fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Gcc { target: run.target });
-    }
-
-    /// Compile GCC (specifically `libgccjit`) for `target`.
-    fn run(self, builder: &Builder<'_>) -> Self::Output {
-        let target = self.target;
-
-        // If GCC has already been built, we avoid building it again.
-        let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config(builder, target)
-        {
-            GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path },
-            GccBuildStatus::ShouldBuild(m) => m,
-        };
-
-        let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
-        t!(stamp.remove());
-        let _time = helpers::timeit(builder);
-        t!(fs::create_dir_all(&out_dir));
-        t!(fs::create_dir_all(&install_dir));
-
-        let libgccjit_path = libgccjit_built_path(&install_dir);
-        if builder.config.dry_run() {
-            return GccOutput { libgccjit: libgccjit_path };
+fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
+    let Meta { stamp: _, out_dir, install_dir, root } = metadata;
+
+    t!(fs::create_dir_all(out_dir));
+    t!(fs::create_dir_all(install_dir));
+
+    // GCC creates files (e.g. symlinks to the downloaded dependencies)
+    // in the source directory, which does not work with our CI setup, where we mount
+    // source directories as read-only on Linux.
+    // Therefore, as a part of the build in CI, we first copy the whole source directory
+    // to the build directory, and perform the build from there.
+    let src_dir = if CiEnv::is_ci() {
+        let src_dir = builder.gcc_out(target).join("src");
+        if src_dir.exists() {
+            builder.remove_dir(&src_dir);
         }
-
-        // GCC creates files (e.g. symlinks to the downloaded dependencies)
-        // in the source directory, which does not work with our CI setup, where we mount
-        // source directories as read-only on Linux.
-        // Therefore, as a part of the build in CI, we first copy the whole source directory
-        // to the build directory, and perform the build from there.
-        let src_dir = if CiEnv::is_ci() {
-            let src_dir = builder.gcc_out(target).join("src");
-            if src_dir.exists() {
-                builder.remove_dir(&src_dir);
-            }
-            builder.create_dir(&src_dir);
-            builder.cp_link_r(&root, &src_dir);
-            src_dir
-        } else {
-            root
-        };
-
-        command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder);
-        let mut configure_cmd = command(src_dir.join("configure"));
-        configure_cmd
-            .current_dir(&out_dir)
-            // On CI, we compile GCC with Clang.
-            // The -Wno-everything flag is needed to make GCC compile with Clang 19.
-            // `-g -O2` are the default flags that are otherwise used by Make.
-            // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml.
-            .env("CXXFLAGS", "-Wno-everything -g -O2")
-            .env("CFLAGS", "-Wno-everything -g -O2")
-            .arg("--enable-host-shared")
-            .arg("--enable-languages=jit")
-            .arg("--enable-checking=release")
-            .arg("--disable-bootstrap")
-            .arg("--disable-multilib")
-            .arg(format!("--prefix={}", install_dir.display()));
-        let cc = builder.build.cc(target).display().to_string();
-        let cc = builder
+        builder.create_dir(&src_dir);
+        builder.cp_link_r(root, &src_dir);
+        src_dir
+    } else {
+        root.clone()
+    };
+
+    command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder);
+    let mut configure_cmd = command(src_dir.join("configure"));
+    configure_cmd
+        .current_dir(out_dir)
+        // On CI, we compile GCC with Clang.
+        // The -Wno-everything flag is needed to make GCC compile with Clang 19.
+        // `-g -O2` are the default flags that are otherwise used by Make.
+        // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml.
+        .env("CXXFLAGS", "-Wno-everything -g -O2")
+        .env("CFLAGS", "-Wno-everything -g -O2")
+        .arg("--enable-host-shared")
+        .arg("--enable-languages=jit")
+        .arg("--enable-checking=release")
+        .arg("--disable-bootstrap")
+        .arg("--disable-multilib")
+        .arg(format!("--prefix={}", install_dir.display()));
+    let cc = builder.build.cc(target).display().to_string();
+    let cc = builder
+        .build
+        .config
+        .ccache
+        .as_ref()
+        .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}"));
+    configure_cmd.env("CC", cc);
+
+    if let Ok(ref cxx) = builder.build.cxx(target) {
+        let cxx = cxx.display().to_string();
+        let cxx = builder
             .build
             .config
             .ccache
             .as_ref()
-            .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}"));
-        configure_cmd.env("CC", cc);
-
-        if let Ok(ref cxx) = builder.build.cxx(target) {
-            let cxx = cxx.display().to_string();
-            let cxx = builder
-                .build
-                .config
-                .ccache
-                .as_ref()
-                .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}"));
-            configure_cmd.env("CXX", cxx);
-        }
-        configure_cmd.run(builder);
+            .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}"));
+        configure_cmd.env("CXX", cxx);
+    }
+    configure_cmd.run(builder);
 
         command("make")
             .current_dir(&out_dir)
@@ -196,15 +212,6 @@ impl Step for Gcc {
             .arg("--silent")
             .arg("install")
             .run_capture_stdout(builder);
-
-        let lib_alias = install_dir.join("lib/libgccjit.so.0");
-        if !lib_alias.exists() {
-            t!(builder.symlink_file(&libgccjit_path, lib_alias));
-        }
-
-        t!(stamp.write());
-
-        GccOutput { libgccjit: libgccjit_path }
     }
 }
 

From 009aba0aa3c3e97df5d27dbb722363888c972534 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Wed, 5 Mar 2025 10:16:22 +0100
Subject: [PATCH 13/25] Add `gcc` bootstrap config section

---
 config.example.toml                     | 5 +++++
 src/bootstrap/src/core/config/config.rs | 9 ++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/config.example.toml b/config.example.toml
index ac5e491b4b520..2dddb0099c132 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -163,6 +163,11 @@
 # Custom CMake defines to set when building LLVM.
 #build-config = {}
 
+# =============================================================================
+# Tweaking how GCC is compiled
+# =============================================================================
+[gcc]
+
 # =============================================================================
 # General build configuration options
 # =============================================================================
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index ac24da9f86b25..7fda61b70f9de 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -676,6 +676,7 @@ pub(crate) struct TomlConfig {
     build: Option<Build>,
     install: Option<Install>,
     llvm: Option<Llvm>,
+    gcc: Option<Gcc>,
     rust: Option<Rust>,
     target: Option<HashMap<String, TomlTarget>>,
     dist: Option<Dist>,
@@ -710,7 +711,7 @@ trait Merge {
 impl Merge for TomlConfig {
     fn merge(
         &mut self,
-        TomlConfig { build, install, llvm, rust, dist, target, profile, change_id }: Self,
+        TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id }: Self,
         replace: ReplaceOpt,
     ) {
         fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) {
@@ -729,6 +730,7 @@ impl Merge for TomlConfig {
         do_merge(&mut self.build, build, replace);
         do_merge(&mut self.install, install, replace);
         do_merge(&mut self.llvm, llvm, replace);
+        do_merge(&mut self.gcc, gcc, replace);
         do_merge(&mut self.rust, rust, replace);
         do_merge(&mut self.dist, dist, replace);
 
@@ -995,6 +997,11 @@ define_config! {
     }
 }
 
+define_config! {
+    /// TOML representation of how the GCC build is configured.
+    struct Gcc {}
+}
+
 define_config! {
     struct Dist {
         sign_folder: Option<String> = "sign-folder",

From c68a5ec6c36b9ea955968d8fcbf1c65c647a14c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Wed, 5 Mar 2025 11:26:16 +0100
Subject: [PATCH 14/25] Add `[gcc] download-ci-gcc` option

---
 config.example.toml                     |  5 +++++
 src/bootstrap/src/core/config/config.rs | 28 ++++++++++++++++++++++++-
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/config.example.toml b/config.example.toml
index 2dddb0099c132..c3d2ad094ceba 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -167,6 +167,11 @@
 # Tweaking how GCC is compiled
 # =============================================================================
 [gcc]
+# Download GCC from CI instead of building it locally.
+# Note that this will attempt to download GCC even if there are local
+# modifications to the `src/gcc` submodule.
+# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
+# download-ci-gcc = false
 
 # =============================================================================
 # General build configuration options
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 7fda61b70f9de..e73659773ab2b 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -171,6 +171,17 @@ impl LldMode {
     }
 }
 
+/// Determines how will GCC be provided.
+#[derive(Default, Clone)]
+pub enum GccCiMode {
+    /// Build GCC from the local `src/gcc` submodule.
+    #[default]
+    BuildLocally,
+    /// Try to download GCC from CI.
+    /// If it is not available on CI, it will be built locally instead.
+    DownloadFromCi,
+}
+
 /// Global configuration for the entire build and/or bootstrap.
 ///
 /// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters.
@@ -283,6 +294,9 @@ pub struct Config {
     pub llvm_ldflags: Option<String>,
     pub llvm_use_libcxx: bool,
 
+    // gcc codegen options
+    pub gcc_ci_mode: GccCiMode,
+
     // rust codegen options
     pub rust_optimize: RustOptimize,
     pub rust_codegen_units: Option<u32>,
@@ -999,7 +1013,9 @@ define_config! {
 
 define_config! {
     /// TOML representation of how the GCC build is configured.
-    struct Gcc {}
+    struct Gcc {
+        download_ci_gcc: Option<bool> = "download-ci-gcc",
+    }
 }
 
 define_config! {
@@ -2143,6 +2159,16 @@ impl Config {
             config.llvm_from_ci = config.parse_download_ci_llvm(None, false);
         }
 
+        if let Some(gcc) = toml.gcc {
+            config.gcc_ci_mode = match gcc.download_ci_gcc {
+                Some(value) => match value {
+                    true => GccCiMode::DownloadFromCi,
+                    false => GccCiMode::BuildLocally,
+                },
+                None => GccCiMode::default(),
+            };
+        }
+
         if let Some(t) = toml.target {
             for (triple, cfg) in t {
                 let mut target = Target::from_triple(&triple);

From 3de10b0756bc34a6bf98eb0be5c89b3806466bdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Wed, 5 Mar 2025 10:35:16 +0100
Subject: [PATCH 15/25] Add `download-ci-gcc-stamp` file

---
 src/bootstrap/download-ci-gcc-stamp | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 src/bootstrap/download-ci-gcc-stamp

diff --git a/src/bootstrap/download-ci-gcc-stamp b/src/bootstrap/download-ci-gcc-stamp
new file mode 100644
index 0000000000000..bbe26afc95269
--- /dev/null
+++ b/src/bootstrap/download-ci-gcc-stamp
@@ -0,0 +1,4 @@
+Change this file to make users of the `download-ci-gcc` configuration download
+a new version of GCC from CI, even if the GCC submodule hasn’t changed.
+
+Last change is for: https://github.com/rust-lang/rust/pull/138051

From bc6302ca6d452e54365bf7a724bf96beffd7280e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Wed, 5 Mar 2025 11:38:05 +0100
Subject: [PATCH 16/25] Implement downloading GCC from CI

---
 src/bootstrap/src/core/build_steps/gcc.rs | 96 ++++++++++++++++++-----
 src/bootstrap/src/core/download.rs        | 28 +++++++
 2 files changed, 104 insertions(+), 20 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index 1382d5b07f919..7e1ec39659a30 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -13,10 +13,11 @@ use std::path::{Path, PathBuf};
 use std::sync::OnceLock;
 
 use build_helper::ci::CiEnv;
+use build_helper::git::get_closest_merge_commit;
 
 use crate::Config;
 use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
-use crate::core::config::TargetSelection;
+use crate::core::config::{GccCiMode, TargetSelection};
 use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
 use crate::utils::exec::command;
 use crate::utils::helpers::{self, t};
@@ -89,17 +90,39 @@ pub enum GccBuildStatus {
     ShouldBuild(Meta),
 }
 
-/// This returns whether we've already previously built GCC.
+/// Tries to download GCC from CI if it is enabled and GCC artifacts
+/// are available for the given target.
+/// Returns a path to the libgccjit.so file.
+fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<PathBuf> {
+    // Try to download GCC from CI if configured and available
+    if !matches!(builder.config.gcc_ci_mode, GccCiMode::DownloadFromCi) {
+        return None;
+    }
+    if target != "x86_64-unknown-linux-gnu" {
+        eprintln!("GCC CI download is only available for the `x86_64-unknown-linux-gnu` target");
+        return None;
+    }
+    let sha =
+        detect_gcc_sha(&builder.config, builder.config.rust_info.is_managed_git_subrepository());
+    let root = ci_gcc_root(&builder.config);
+    let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&sha);
+    if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() {
+        builder.config.download_ci_gcc(&sha, &root);
+        t!(gcc_stamp.write());
+    }
+    // FIXME: put libgccjit.so into a lib directory in dist::Gcc
+    Some(root.join("libgccjit.so"))
+}
+
+/// This returns information about whether GCC should be built or if it's already built.
+/// It transparently handles downloading GCC from CI if needed.
 ///
 /// It's used to avoid busting caches during x.py check -- if we've already built
 /// GCC, it's fine for us to not try to avoid doing so.
 pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
-    // Initialize the gcc submodule if not initialized already.
-    builder.config.update_submodule("src/gcc");
-
-    let root = builder.src.join("src/gcc");
-    let out_dir = builder.gcc_out(target).join("build");
-    let install_dir = builder.gcc_out(target).join("install");
+    if let Some(path) = try_download_gcc(builder, target) {
+        return GccBuildStatus::AlreadyBuilt(path);
+    }
 
     static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
     let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
@@ -110,6 +133,13 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G
         )
     });
 
+    // Initialize the gcc submodule if not initialized already.
+    builder.config.update_submodule("src/gcc");
+
+    let root = builder.src.join("src/gcc");
+    let out_dir = builder.gcc_out(target).join("build");
+    let install_dir = builder.gcc_out(target).join("install");
+
     let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash);
 
     if stamp.is_up_to_date() {
@@ -142,7 +172,7 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
     install_dir.join("lib/libgccjit.so")
 }
 
-fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
+fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
     let Meta { stamp: _, out_dir, install_dir, root } = metadata;
 
     t!(fs::create_dir_all(out_dir));
@@ -202,17 +232,12 @@ fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
     }
     configure_cmd.run(builder);
 
-        command("make")
-            .current_dir(&out_dir)
-            .arg("--silent")
-            .arg(format!("-j{}", builder.jobs()))
-            .run_capture_stdout(builder);
-        command("make")
-            .current_dir(&out_dir)
-            .arg("--silent")
-            .arg("install")
-            .run_capture_stdout(builder);
-    }
+    command("make")
+        .current_dir(out_dir)
+        .arg("--silent")
+        .arg(format!("-j{}", builder.jobs()))
+        .run_capture_stdout(builder);
+    command("make").current_dir(out_dir).arg("--silent").arg("install").run_capture_stdout(builder);
 }
 
 /// Configures a Cargo invocation so that it can build the GCC codegen backend.
@@ -220,3 +245,34 @@ pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) {
     // Add the path to libgccjit.so to the linker search paths.
     cargo.rustflag(&format!("-L{}", gcc.libgccjit.parent().unwrap().to_str().unwrap()));
 }
+
+/// The absolute path to the downloaded GCC artifacts.
+fn ci_gcc_root(config: &Config) -> PathBuf {
+    config.out.join(config.build).join("ci-gcc")
+}
+
+/// This retrieves the GCC sha we *want* to use, according to git history.
+fn detect_gcc_sha(config: &Config, is_git: bool) -> String {
+    let gcc_sha = if is_git {
+        get_closest_merge_commit(
+            Some(&config.src),
+            &config.git_config(),
+            &[config.src.join("src/gcc"), config.src.join("src/bootstrap/download-ci-gcc-stamp")],
+        )
+        .unwrap()
+    } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
+        info.sha.trim().to_owned()
+    } else {
+        "".to_owned()
+    };
+
+    if gcc_sha.is_empty() {
+        eprintln!("error: could not find commit hash for downloading GCC");
+        eprintln!("HELP: maybe your repository history is too shallow?");
+        eprintln!("HELP: consider disabling `download-ci-gcc`");
+        eprintln!("HELP: or fetch enough history to include one upstream commit");
+        panic!();
+    }
+
+    gcc_sha
+}
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index c477bdb829a91..95feb41ffd0cc 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -826,6 +826,34 @@ download-rustc = false
         let llvm_root = self.ci_llvm_root();
         self.unpack(&tarball, &llvm_root, "rust-dev");
     }
+
+    pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) {
+        let cache_prefix = format!("gcc-{gcc_sha}");
+        let cache_dst =
+            self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
+
+        let gcc_cache = cache_dst.join(cache_prefix);
+        if !gcc_cache.exists() {
+            t!(fs::create_dir_all(&gcc_cache));
+        }
+        let base = &self.stage0_metadata.config.artifacts_server;
+        let filename = format!("gcc-nightly-{}.tar.xz", self.build.triple);
+        let tarball = gcc_cache.join(&filename);
+        if !tarball.exists() {
+            let help_on_error = "ERROR: failed to download gcc from ci
+
+    HELP: There could be two reasons behind this:
+        1) The host triple is not supported for `download-ci-gcc`.
+        2) Old builds get deleted after a certain time.
+    HELP: In either case, disable `download-ci-gcc` in your config.toml:
+
+    [gcc]
+    download-ci-gcc = false
+    ";
+            self.download_file(&format!("{base}/{gcc_sha}/{filename}"), &tarball, help_on_error);
+        }
+        self.unpack(&tarball, root_dir, "gcc");
+    }
 }
 
 fn path_is_dylib(path: &Path) -> bool {

From 2b1b09ce0e0143ccf84f91b0691b52e2c8ecb6c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Wed, 5 Mar 2025 11:39:43 +0100
Subject: [PATCH 17/25] Add change tracker entry

---
 src/bootstrap/src/utils/change_tracker.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 425ffdccad57f..ec27109c117ae 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -370,4 +370,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "The rust.description option has moved to build.description and rust.description is now deprecated.",
     },
+    ChangeInfo {
+        change_id: 138051,
+        severity: ChangeSeverity::Info,
+        summary: "There is now a new `gcc` config section that can be used to download GCC from CI using `gcc.download-ci-gcc = true`",
+    },
 ];

From dcc2b307dc3b4807fb9a1fb2ef8c26396bb8649b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Mon, 10 Mar 2025 11:29:04 +0100
Subject: [PATCH 18/25] Add triagebot entry for GCC modifications

---
 triagebot.toml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/triagebot.toml b/triagebot.toml
index e4231b2966baa..91d0edbe76289 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -958,6 +958,9 @@ If appropriate, please update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/util
 [mentions."src/bootstrap/src/core/build_steps/llvm.rs"]
 message = "This PR changes how LLVM is built. Consider updating src/bootstrap/download-ci-llvm-stamp."
 
+[mentions."src/bootstrap/src/core/build_steps/gcc.rs"]
+message = "This PR changes how GCC is built. Consider updating src/bootstrap/download-ci-gcc-stamp."
+
 [mentions."tests/crashes"]
 message = "This PR changes a file inside `tests/crashes`. If a crash was fixed, please move into the corresponding `ui` subdir and add 'Fixes #<issueNr>' to the PR description to autoclose the issue upon merge."
 

From bf58a3521f8e5fb0fa693e77ce9ef7e5e3c4a915 Mon Sep 17 00:00:00 2001
From: onur-ozkan <work@onurozkan.dev>
Date: Sun, 9 Mar 2025 01:36:51 +0300
Subject: [PATCH 19/25] stabilize `ci_rustc_if_unchanged_logic` test for local
 environments

Signed-off-by: onur-ozkan <work@onurozkan.dev>
---
 src/bootstrap/src/core/builder/tests.rs | 10 ++++++++--
 src/bootstrap/src/core/config/config.rs |  3 +++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 63a1bbc24f16e..1f96ac729e2a3 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -261,8 +261,14 @@ fn ci_rustc_if_unchanged_logic() {
     // Make sure "if-unchanged" logic doesn't try to use CI rustc while there are changes
     // in compiler and/or library.
     if config.download_rustc_commit.is_some() {
-        let has_changes =
-            config.last_modified_commit(&["compiler", "library"], "download-rustc", true).is_none();
+        let mut paths = vec!["compiler"];
+
+        // Handle library tree the same way as in `Config::download_ci_rustc_commit`.
+        if build_helper::ci::CiEnv::is_ci() {
+            paths.push("library");
+        }
+
+        let has_changes = config.last_modified_commit(&paths, "download-rustc", true).is_none();
 
         assert!(
             !has_changes,
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index ac24da9f86b25..09dc9dfbc04ad 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -2985,6 +2985,9 @@ impl Config {
         // these changes to speed up the build process for library developers. This provides consistent
         // functionality for library developers between `download-rustc=true` and `download-rustc="if-unchanged"`
         // options.
+        //
+        // If you update "library" logic here, update `builder::tests::ci_rustc_if_unchanged_logic` test
+        // logic accordingly.
         if !CiEnv::is_ci() {
             allowed_paths.push(":!library");
         }

From cf8e1f5e0faed0a8f50ccfecedb1e4fad8d79191 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?=
 <69964857+Sa4dUs@users.noreply.github.com>
Date: Fri, 7 Mar 2025 17:37:50 +0100
Subject: [PATCH 20/25] Fix ICE for invalid return activity and proper error
 handling

---
 compiler/rustc_builtin_macros/messages.ftl    |  2 +
 compiler/rustc_builtin_macros/src/autodiff.rs | 15 +++++-
 compiler/rustc_builtin_macros/src/errors.rs   |  9 ++++
 .../rustc_codegen_ssa/src/codegen_attrs.rs    | 19 +------
 tests/ui/autodiff/autodiff_illegal.rs         | 52 +++++++++++++------
 tests/ui/autodiff/autodiff_illegal.stderr     | 52 ++++++++++++++-----
 6 files changed, 100 insertions(+), 49 deletions(-)

diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 4cac7cb93f581..9dac7a8aaf428 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -75,8 +75,10 @@ builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Revers
 builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode
 builtin_macros_autodiff_not_build = this rustc version does not support autodiff
 builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found}
+builtin_macros_autodiff_ret_activity = invalid return activity {$act} in {$mode} Mode
 builtin_macros_autodiff_ty_activity = {$act} can not be used for this type
 
+
 builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}`
 builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s
     .label = not applicable here
diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
index 6d9c35756574e..6707ab8edde9f 100644
--- a/compiler/rustc_builtin_macros/src/autodiff.rs
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -8,7 +8,8 @@ mod llvm_enzyme {
     use std::string::String;
 
     use rustc_ast::expand::autodiff_attrs::{
-        AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ty_for_activity,
+        AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
+        valid_ty_for_activity,
     };
     use rustc_ast::ptr::P;
     use rustc_ast::token::{Token, TokenKind};
@@ -632,10 +633,22 @@ mod llvm_enzyme {
                 errors = true;
             }
         }
+
+        if has_ret && !valid_ret_activity(x.mode, x.ret_activity) {
+            dcx.emit_err(errors::AutoDiffInvalidRetAct {
+                span,
+                mode: x.mode.to_string(),
+                act: x.ret_activity.to_string(),
+            });
+            // We don't set `errors = true` to avoid annoying type errors relative
+            // to the expanded macro type signature
+        }
+
         if errors {
             // This is not the right signature, but we can continue parsing.
             return (sig.clone(), new_inputs, idents, true);
         }
+
         let unsafe_activities = x
             .input_activity
             .iter()
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index ab1e0d8ee896b..30597944124cb 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -185,6 +185,15 @@ mod autodiff {
         pub(crate) act: String,
     }
 
+    #[derive(Diagnostic)]
+    #[diag(builtin_macros_autodiff_ret_activity)]
+    pub(crate) struct AutoDiffInvalidRetAct {
+        #[primary_span]
+        pub(crate) span: Span,
+        pub(crate) mode: String,
+        pub(crate) act: String,
+    }
+
     #[derive(Diagnostic)]
     #[diag(builtin_macros_autodiff_mode)]
     pub(crate) struct AutoDiffInvalidMode {
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index c8f13dc0bae61..a01f5d372a2da 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -1,9 +1,7 @@
 use std::str::FromStr;
 
 use rustc_abi::ExternAbi;
-use rustc_ast::expand::autodiff_attrs::{
-    AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
-};
+use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
 use rustc_ast::{MetaItem, MetaItemInner, attr};
 use rustc_attr_parsing::ReprAttr::ReprAlign;
 use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
@@ -930,21 +928,6 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
         }
     }
 
-    // Validate input and return activities
-    let mut msg = "".to_string();
-    for &input in &arg_activities {
-        if !valid_input_activity(mode, input) {
-            msg = format!("Invalid input activity {} for {} mode", input, mode);
-        }
-    }
-    if !valid_ret_activity(mode, ret_activity) {
-        msg = format!("Invalid return activity {} for {} mode", ret_activity, mode);
-    }
-    if msg != "".to_string() {
-        tcx.dcx().struct_span_err(attr.span(), msg).with_note("invalid activity").emit();
-        return Some(AutoDiffAttrs::error());
-    }
-
     Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
 }
 
diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs
index c0548d2bbb8ff..e810b9ba565ba 100644
--- a/tests/ui/autodiff/autodiff_illegal.rs
+++ b/tests/ui/autodiff/autodiff_illegal.rs
@@ -12,41 +12,40 @@ use std::autodiff::autodiff;
 // We can't use Duplicated on scalars
 #[autodiff(df1, Reverse, Duplicated)]
 pub fn f1(x: f64) {
-//~^ ERROR     Duplicated can not be used for this type
+    //~^ ERROR     Duplicated can not be used for this type
     unimplemented!()
 }
 
 // Too many activities
 #[autodiff(df3, Reverse, Duplicated, Const)]
 pub fn f3(x: f64) {
-//~^^ ERROR     expected 1 activities, but found 2
+    //~^^ ERROR     expected 1 activities, but found 2
     unimplemented!()
 }
 
 // To few activities
 #[autodiff(df4, Reverse)]
 pub fn f4(x: f64) {
-//~^^ ERROR     expected 1 activities, but found 0
+    //~^^ ERROR     expected 1 activities, but found 0
     unimplemented!()
 }
 
 // We can't use Dual in Reverse mode
 #[autodiff(df5, Reverse, Dual)]
 pub fn f5(x: f64) {
-//~^^ ERROR     Dual can not be used in Reverse Mode
+    //~^^ ERROR     Dual can not be used in Reverse Mode
     unimplemented!()
 }
 
 // We can't use Duplicated in Forward mode
 #[autodiff(df6, Forward, Duplicated)]
 pub fn f6(x: f64) {
-//~^^ ERROR Duplicated can not be used in Forward Mode
-//~^^ ERROR Duplicated can not be used for this type
+    //~^^ ERROR Duplicated can not be used in Forward Mode
+    //~^^ ERROR Duplicated can not be used for this type
     unimplemented!()
 }
 
 fn dummy() {
-
     #[autodiff(df7, Forward, Dual)]
     let mut x = 5;
     //~^ ERROR autodiff must be applied to function
@@ -64,21 +63,21 @@ fn dummy() {
 // Malformed, where args?
 #[autodiff]
 pub fn f7(x: f64) {
-//~^ ERROR autodiff must be applied to function
+    //~^ ERROR autodiff must be applied to function
     unimplemented!()
 }
 
 // Malformed, where args?
 #[autodiff()]
 pub fn f8(x: f64) {
-//~^ ERROR autodiff requires at least a name and mode
+    //~^ ERROR autodiff requires at least a name and mode
     unimplemented!()
 }
 
 // Invalid attribute syntax
 #[autodiff = ""]
 pub fn f9(x: f64) {
-//~^ ERROR autodiff must be applied to function
+    //~^ ERROR autodiff must be applied to function
     unimplemented!()
 }
 
@@ -87,21 +86,21 @@ fn fn_exists() {}
 // We colide with an already existing function
 #[autodiff(fn_exists, Reverse, Active)]
 pub fn f10(x: f64) {
-//~^^ ERROR the name `fn_exists` is defined multiple times [E0428]
+    //~^^ ERROR the name `fn_exists` is defined multiple times [E0428]
     unimplemented!()
 }
 
 // Malformed, missing a mode
 #[autodiff(df11)]
 pub fn f11() {
-//~^ ERROR autodiff requires at least a name and mode
+    //~^ ERROR autodiff requires at least a name and mode
     unimplemented!()
 }
 
 // Invalid Mode
 #[autodiff(df12, Debug)]
 pub fn f12() {
-//~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse`
+    //~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse`
     unimplemented!()
 }
 
@@ -109,7 +108,7 @@ pub fn f12() {
 // or use two autodiff macros.
 #[autodiff(df13, Forward, Reverse)]
 pub fn f13() {
-//~^^ ERROR did not recognize Activity: `Reverse`
+    //~^^ ERROR did not recognize Activity: `Reverse`
     unimplemented!()
 }
 
@@ -130,7 +129,7 @@ type MyFloat = f32;
 // like THIR which has type information available.
 #[autodiff(df15, Reverse, Active, Active)]
 fn f15(x: MyFloat) -> f32 {
-//~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433]
+    //~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433]
     unimplemented!()
 }
 
@@ -141,7 +140,9 @@ fn f16(x: f32) -> MyFloat {
 }
 
 #[repr(transparent)]
-struct F64Trans { inner: f64 }
+struct F64Trans {
+    inner: f64,
+}
 
 // We would like to support `#[repr(transparent)]` f32/f64 wrapper in return type in the future
 #[autodiff(df17, Reverse, Active, Active)]
@@ -156,5 +157,24 @@ fn f18(x: F64Trans) -> f64 {
     unimplemented!()
 }
 
+// Invalid return activity
+#[autodiff(df19, Forward, Dual, Active)]
+fn f19(x: f32) -> f32 {
+    //~^^ ERROR invalid return activity Active in Forward Mode
+    unimplemented!()
+}
+
+#[autodiff(df20, Reverse, Active, Dual)]
+fn f20(x: f32) -> f32 {
+    //~^^ ERROR invalid return activity Dual in Reverse Mode
+    unimplemented!()
+}
+
+// Duplicated cannot be used as return activity
+#[autodiff(df21, Reverse, Active, Duplicated)]
+fn f21(x: f32) -> f32 {
+    //~^^ ERROR invalid return activity Duplicated in Reverse Mode
+    unimplemented!()
+}
 
 fn main() {}
diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr
index 3a7242b2f5d95..47d53492700ba 100644
--- a/tests/ui/autodiff/autodiff_illegal.stderr
+++ b/tests/ui/autodiff/autodiff_illegal.stderr
@@ -1,5 +1,5 @@
 error[E0658]: attributes on expressions are experimental
-  --> $DIR/autodiff_illegal.rs:54:5
+  --> $DIR/autodiff_illegal.rs:53:5
    |
 LL |     #[autodiff(df7, Forward, Dual)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -53,25 +53,25 @@ LL | pub fn f6(x: f64) {
    |              ^^^
 
 error: autodiff must be applied to function
-  --> $DIR/autodiff_illegal.rs:51:5
+  --> $DIR/autodiff_illegal.rs:50:5
    |
 LL |     let mut x = 5;
    |     ^^^^^^^^^^^^^^
 
 error: autodiff must be applied to function
-  --> $DIR/autodiff_illegal.rs:55:5
+  --> $DIR/autodiff_illegal.rs:54:5
    |
 LL |     x = x + 3;
    |     ^
 
 error: autodiff must be applied to function
-  --> $DIR/autodiff_illegal.rs:60:5
+  --> $DIR/autodiff_illegal.rs:59:5
    |
 LL |     let add_one_v2 = |x: u32| -> u32 { x + 1 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: autodiff must be applied to function
-  --> $DIR/autodiff_illegal.rs:66:1
+  --> $DIR/autodiff_illegal.rs:65:1
    |
 LL | / pub fn f7(x: f64) {
 LL | |
@@ -80,7 +80,7 @@ LL | | }
    | |_^
 
 error: autodiff requires at least a name and mode
-  --> $DIR/autodiff_illegal.rs:73:1
+  --> $DIR/autodiff_illegal.rs:72:1
    |
 LL | / pub fn f8(x: f64) {
 LL | |
@@ -89,7 +89,7 @@ LL | | }
    | |_^
 
 error: autodiff must be applied to function
-  --> $DIR/autodiff_illegal.rs:80:1
+  --> $DIR/autodiff_illegal.rs:79:1
    |
 LL | / pub fn f9(x: f64) {
 LL | |
@@ -98,7 +98,7 @@ LL | | }
    | |_^
 
 error[E0428]: the name `fn_exists` is defined multiple times
-  --> $DIR/autodiff_illegal.rs:88:1
+  --> $DIR/autodiff_illegal.rs:87:1
    |
 LL | fn fn_exists() {}
    | -------------- previous definition of the value `fn_exists` here
@@ -110,7 +110,7 @@ LL | #[autodiff(fn_exists, Reverse, Active)]
    = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: autodiff requires at least a name and mode
-  --> $DIR/autodiff_illegal.rs:96:1
+  --> $DIR/autodiff_illegal.rs:95:1
    |
 LL | / pub fn f11() {
 LL | |
@@ -119,19 +119,43 @@ LL | | }
    | |_^
 
 error: unknown Mode: `Debug`. Use `Forward` or `Reverse`
-  --> $DIR/autodiff_illegal.rs:102:18
+  --> $DIR/autodiff_illegal.rs:101:18
    |
 LL | #[autodiff(df12, Debug)]
    |                  ^^^^^
 
 error: did not recognize Activity: `Reverse`
-  --> $DIR/autodiff_illegal.rs:110:27
+  --> $DIR/autodiff_illegal.rs:109:27
    |
 LL | #[autodiff(df13, Forward, Reverse)]
    |                           ^^^^^^^
 
+error: invalid return activity Active in Forward Mode
+  --> $DIR/autodiff_illegal.rs:161:1
+   |
+LL | #[autodiff(df19, Forward, Dual, Active)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: invalid return activity Dual in Reverse Mode
+  --> $DIR/autodiff_illegal.rs:167:1
+   |
+LL | #[autodiff(df20, Reverse, Active, Dual)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: invalid return activity Duplicated in Reverse Mode
+  --> $DIR/autodiff_illegal.rs:174:1
+   |
+LL | #[autodiff(df21, Reverse, Active, Duplicated)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error[E0433]: failed to resolve: use of undeclared type `MyFloat`
-  --> $DIR/autodiff_illegal.rs:131:1
+  --> $DIR/autodiff_illegal.rs:130:1
    |
 LL | #[autodiff(df15, Reverse, Active, Active)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat`
@@ -139,14 +163,14 @@ LL | #[autodiff(df15, Reverse, Active, Active)]
    = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0433]: failed to resolve: use of undeclared type `F64Trans`
-  --> $DIR/autodiff_illegal.rs:153:1
+  --> $DIR/autodiff_illegal.rs:154:1
    |
 LL | #[autodiff(df18, Reverse, Active, Active)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans`
    |
    = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 19 previous errors
+error: aborting due to 22 previous errors
 
 Some errors have detailed explanations: E0428, E0433, E0658.
 For more information about an error, try `rustc --explain E0428`.

From 33f9a491eb97af0cc72df70fd10724eda2453d45 Mon Sep 17 00:00:00 2001
From: Sa4dUs <dmmarcelo27@gmail.com>
Date: Mon, 10 Mar 2025 13:58:10 +0100
Subject: [PATCH 21/25] Combine autodiff errors together

---
 compiler/rustc_builtin_macros/messages.ftl | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 9dac7a8aaf428..3f03834f8d781 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -77,9 +77,8 @@ builtin_macros_autodiff_not_build = this rustc version does not support autodiff
 builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found}
 builtin_macros_autodiff_ret_activity = invalid return activity {$act} in {$mode} Mode
 builtin_macros_autodiff_ty_activity = {$act} can not be used for this type
-
-
 builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}`
+
 builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s
     .label = not applicable here
     .label2 = not a `struct`, `enum` or `union`

From 8546e015b4a04e9c397478ae1e27a5370d2c638b Mon Sep 17 00:00:00 2001
From: Sa4dUs <dmmarcelo27@gmail.com>
Date: Mon, 10 Mar 2025 16:05:27 +0100
Subject: [PATCH 22/25] Add individual activity span availability FIXME

---
 compiler/rustc_builtin_macros/src/autodiff.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
index 6707ab8edde9f..cae806c6da776 100644
--- a/compiler/rustc_builtin_macros/src/autodiff.rs
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -586,6 +586,8 @@ mod llvm_enzyme {
     //
     // Error handling: If the user provides an invalid configuration (incorrect numbers, types, or
     // both), we emit an error and return the original signature. This allows us to continue parsing.
+    // FIXME(Sa4dUs): make individual activities' span available so errors
+    // can point to only the activity instead of the entire attribute
     fn gen_enzyme_decl(
         ecx: &ExtCtxt<'_>,
         sig: &ast::FnSig,

From 3846f942300c4fd8f43a8a8a1324ad5e358b9459 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Tue, 11 Mar 2025 14:32:03 +0100
Subject: [PATCH 23/25] miri native_calls: ensure we actually expose *mutable*
 provenance to the memory FFI can access

---
 compiler/rustc_const_eval/src/interpret/memory.rs     | 4 ++++
 compiler/rustc_middle/src/mir/interpret/allocation.rs | 5 +++++
 src/tools/miri/src/alloc_addresses/mod.rs             | 4 ++--
 src/tools/miri/src/shims/native_lib.rs                | 2 +-
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index ce0b5a350e030..e5af067362919 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -982,6 +982,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                     todo.push(id);
                 }
             }
+            // Also expose the provenance of the interpreter-level allocation, so it can
+            // be read by FFI. The `black_box` is defensive programming as LLVM likes
+            // to (incorrectly) optimize away ptr2int casts whose result is unused.
+            std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
 
             // Prepare for possible write from native code if mutable.
             if info.mutbl.is_mut() {
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index ba65a7118155c..b24f6bc7770dc 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -679,6 +679,11 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
         // Set provenance of all bytes to wildcard.
         self.provenance.write_wildcards(self.len());
 
+        // Also expose the provenance of the interpreter-level allocation, so it can
+        // be written by FFI. The `black_box` is defensive programming as LLVM likes
+        // to (incorrectly) optimize away ptr2int casts whose result is unused.
+        std::hint::black_box(self.get_bytes_unchecked_raw_mut().expose_provenance());
+
         Ok(())
     }
 
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index ff3a25e94bd5d..5d257029a46e5 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -198,8 +198,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
                 AllocKind::Dead => unreachable!(),
             };
-            // Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
-            return interp_ok(base_ptr.expose_provenance().try_into().unwrap());
+            // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`.
+            return interp_ok(base_ptr.addr().try_into().unwrap());
         }
         // We are not in native lib mode, so we control the addresses ourselves.
         if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
index c6fcb0355eb80..0258a76c3e703 100644
--- a/src/tools/miri/src/shims/native_lib.rs
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -266,7 +266,7 @@ fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'
             CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
         ty::RawPtr(..) => {
             let s = v.to_scalar().to_pointer(cx)?.addr();
-            // This relies on the `expose_provenance` in `addr_from_alloc_id`.
+            // This relies on the `expose_provenance` in `prepare_for_native_call`.
             CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
         }
         _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),

From ba6c40685418a24334afcb9acd7aa9bcd8b65181 Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Tue, 11 Mar 2025 15:30:39 +0100
Subject: [PATCH 24/25] let the bodies hit the floor

remove unnecessary `body`  arguments
---
 .../src/type_check/input_output.rs            |  54 +++--
 .../src/type_check/liveness/mod.rs            |  20 +-
 .../src/type_check/liveness/trace.rs          |  42 ++--
 compiler/rustc_borrowck/src/type_check/mod.rs | 210 +++++++++---------
 4 files changed, 153 insertions(+), 173 deletions(-)

diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index f70b17e33624b..c6b29fe36fd9c 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -24,9 +24,9 @@ use crate::universal_regions::DefiningTy;
 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     /// Check explicit closure signature annotation,
     /// e.g., `|x: FxIndexMap<_, &'static u32>| ...`.
-    #[instrument(skip(self, body), level = "debug")]
-    pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
-        let mir_def_id = body.source.def_id().expect_local();
+    #[instrument(skip(self), level = "debug")]
+    pub(super) fn check_signature_annotation(&mut self) {
+        let mir_def_id = self.body.source.def_id().expect_local();
 
         if !self.tcx().is_closure_like(mir_def_id.to_def_id()) {
             return;
@@ -38,9 +38,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         // (e.g., the `_` in the code above) with fresh variables.
         // Then replace the bound items in the fn sig with fresh variables,
         // so that they represent the view from "inside" the closure.
-        let user_provided_sig = self.instantiate_canonical(body.span, &user_provided_poly_sig);
+        let user_provided_sig = self.instantiate_canonical(self.body.span, &user_provided_poly_sig);
         let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
-            body.span,
+            self.body.span,
             BoundRegionConversionTime::FnCall,
             user_provided_sig,
         );
@@ -66,12 +66,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 Ty::new_tup(self.tcx(), user_provided_sig.inputs()),
                 args.tupled_upvars_ty(),
                 args.coroutine_captures_by_ref_ty(),
-                self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(body.span), || {
-                    RegionCtxt::Unknown
-                }),
+                self.infcx
+                    .next_region_var(RegionVariableOrigin::MiscVariable(self.body.span), || {
+                        RegionCtxt::Unknown
+                    }),
             );
 
-            let next_ty_var = || self.infcx.next_ty_var(body.span);
+            let next_ty_var = || self.infcx.next_ty_var(self.body.span);
             let output_ty = Ty::new_coroutine(
                 self.tcx(),
                 self.tcx().coroutine_for_closure(mir_def_id),
@@ -107,9 +108,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip_eq(
             // In MIR, closure args begin with an implicit `self`.
             // Also, coroutines have a resume type which may be implicitly `()`.
-            body.args_iter()
+            self.body
+                .args_iter()
                 .skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 })
-                .map(|local| &body.local_decls[local]),
+                .map(|local| &self.body.local_decls[local]),
         ) {
             self.ascribe_user_type_skip_wf(
                 arg_decl.ty,
@@ -119,7 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
 
         // If the user explicitly annotated the output type, enforce it.
-        let output_decl = &body.local_decls[RETURN_PLACE];
+        let output_decl = &self.body.local_decls[RETURN_PLACE];
         self.ascribe_user_type_skip_wf(
             output_decl.ty,
             ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())),
@@ -127,12 +129,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         );
     }
 
-    #[instrument(skip(self, body), level = "debug")]
-    pub(super) fn equate_inputs_and_outputs(
-        &mut self,
-        body: &Body<'tcx>,
-        normalized_inputs_and_output: &[Ty<'tcx>],
-    ) {
+    #[instrument(skip(self), level = "debug")]
+    pub(super) fn equate_inputs_and_outputs(&mut self, normalized_inputs_and_output: &[Ty<'tcx>]) {
         let (&normalized_output_ty, normalized_input_tys) =
             normalized_inputs_and_output.split_last().unwrap();
 
@@ -141,18 +139,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
         // Equate expected input tys with those in the MIR.
         for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
-            if argument_index + 1 >= body.local_decls.len() {
+            if argument_index + 1 >= self.body.local_decls.len() {
                 self.tcx()
                     .dcx()
-                    .span_bug(body.span, "found more normalized_input_ty than local_decls");
+                    .span_bug(self.body.span, "found more normalized_input_ty than local_decls");
             }
 
             // In MIR, argument N is stored in local N+1.
             let local = Local::from_usize(argument_index + 1);
 
-            let mir_input_ty = body.local_decls[local].ty;
+            let mir_input_ty = self.body.local_decls[local].ty;
 
-            let mir_input_span = body.local_decls[local].source_info.span;
+            let mir_input_span = self.body.local_decls[local].source_info.span;
             self.equate_normalized_input_or_output(
                 normalized_input_ty,
                 mir_input_ty,
@@ -160,8 +158,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             );
         }
 
-        if let Some(mir_yield_ty) = body.yield_ty() {
-            let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
+        if let Some(mir_yield_ty) = self.body.yield_ty() {
+            let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span;
             self.equate_normalized_input_or_output(
                 self.universal_regions.yield_ty.unwrap(),
                 mir_yield_ty,
@@ -169,8 +167,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             );
         }
 
-        if let Some(mir_resume_ty) = body.resume_ty() {
-            let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
+        if let Some(mir_resume_ty) = self.body.resume_ty() {
+            let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span;
             self.equate_normalized_input_or_output(
                 self.universal_regions.resume_ty.unwrap(),
                 mir_resume_ty,
@@ -179,8 +177,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
 
         // Return types are a bit more complex. They may contain opaque `impl Trait` types.
-        let mir_output_ty = body.local_decls[RETURN_PLACE].ty;
-        let output_span = body.local_decls[RETURN_PLACE].source_info.span;
+        let mir_output_ty = self.body.local_decls[RETURN_PLACE].ty;
+        let output_span = self.body.local_decls[RETURN_PLACE].source_info.span;
         self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span);
     }
 
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
index dcc179030020d..f17ad23f4bf64 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
@@ -31,7 +31,6 @@ mod trace;
 /// performed before
 pub(super) fn generate<'a, 'tcx>(
     typeck: &mut TypeChecker<'_, 'tcx>,
-    body: &Body<'tcx>,
     location_map: &DenseLocationMap,
     flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
     move_data: &MoveData<'tcx>,
@@ -51,23 +50,16 @@ pub(super) fn generate<'a, 'tcx>(
     // We do record these regions in the polonius context, since they're used to differentiate
     // relevant and boring locals, which is a key distinction used later in diagnostics.
     if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
-        let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
+        let (_, boring_locals) =
+            compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
         typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals =
             boring_locals.into_iter().collect();
         free_regions = typeck.universal_regions.universal_regions_iter().collect();
     }
     let (relevant_live_locals, boring_locals) =
-        compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
-
-    trace::trace(
-        typeck,
-        body,
-        location_map,
-        flow_inits,
-        move_data,
-        relevant_live_locals,
-        boring_locals,
-    );
+        compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
+
+    trace::trace(typeck, location_map, flow_inits, move_data, relevant_live_locals, boring_locals);
 
     // Mark regions that should be live where they appear within rvalues or within a call: like
     // args, regions, and types.
@@ -76,7 +68,7 @@ pub(super) fn generate<'a, 'tcx>(
         &mut typeck.constraints.liveness_constraints,
         &typeck.universal_regions,
         &mut typeck.polonius_liveness,
-        body,
+        typeck.body,
     );
 }
 
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 32d398dc1605c..7718644b9a9a8 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -39,17 +39,15 @@ use crate::type_check::{NormalizeLocation, TypeChecker};
 /// this respects `#[may_dangle]` annotations).
 pub(super) fn trace<'a, 'tcx>(
     typeck: &mut TypeChecker<'_, 'tcx>,
-    body: &Body<'tcx>,
     location_map: &DenseLocationMap,
     flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
     move_data: &MoveData<'tcx>,
     relevant_live_locals: Vec<Local>,
     boring_locals: Vec<Local>,
 ) {
-    let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body);
+    let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body);
     let cx = LivenessContext {
         typeck,
-        body,
         flow_inits,
         location_map,
         local_use_map,
@@ -69,14 +67,13 @@ pub(super) fn trace<'a, 'tcx>(
 /// Contextual state for the type-liveness coroutine.
 struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
     /// Current type-checker, giving us our inference context etc.
+    ///
+    /// This also stores the body we're currently analyzing.
     typeck: &'a mut TypeChecker<'typeck, 'tcx>,
 
     /// Defines the `PointIndex` mapping
     location_map: &'a DenseLocationMap,
 
-    /// MIR we are analyzing.
-    body: &'a Body<'tcx>,
-
     /// Mapping to/from the various indices used for initialization tracking.
     move_data: &'a MoveData<'tcx>,
 
@@ -139,7 +136,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
             self.compute_use_live_points_for(local);
             self.compute_drop_live_points_for(local);
 
-            let local_ty = self.cx.body.local_decls[local].ty;
+            let local_ty = self.cx.body().local_decls[local].ty;
 
             if !self.use_live_at.is_empty() {
                 self.cx.add_use_live_facts_for(local_ty, &self.use_live_at);
@@ -164,8 +161,8 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
     /// and can therefore safely be dropped.
     fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
         for local in boring_locals {
-            let local_ty = self.cx.body.local_decls[local].ty;
-            let local_span = self.cx.body.local_decls[local].source_info.span;
+            let local_ty = self.cx.body().local_decls[local].ty;
+            let local_span = self.cx.body().local_decls[local].source_info.span;
             let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
                 let typeck = &self.cx.typeck;
                 move || LivenessContext::compute_drop_data(typeck, local_ty, local_span)
@@ -173,7 +170,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
 
             drop_data.dropck_result.report_overflows(
                 self.cx.typeck.infcx.tcx,
-                self.cx.body.local_decls[local].source_info.span,
+                self.cx.typeck.body.local_decls[local].source_info.span,
                 local_ty,
             );
         }
@@ -202,7 +199,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
                 .var_dropped_at
                 .iter()
                 .filter_map(|&(local, location_index)| {
-                    let local_ty = self.cx.body.local_decls[local].ty;
+                    let local_ty = self.cx.body().local_decls[local].ty;
                     if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() {
                         return None;
                     }
@@ -278,9 +275,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
 
                 let block = self.cx.location_map.to_location(block_start).block;
                 self.stack.extend(
-                    self.cx.body.basic_blocks.predecessors()[block]
+                    self.cx.body().basic_blocks.predecessors()[block]
                         .iter()
-                        .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb))
+                        .map(|&pred_bb| self.cx.body().terminator_loc(pred_bb))
                         .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)),
                 );
             }
@@ -305,7 +302,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
         // Find the drops where `local` is initialized.
         for drop_point in self.cx.local_use_map.drops(local) {
             let location = self.cx.location_map.to_location(drop_point);
-            debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);
+            debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,);
 
             if self.cx.initialized_at_terminator(location.block, mpi)
                 && self.drop_live_at.insert(drop_point)
@@ -351,7 +348,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
         // block. One of them may be either a definition or use
         // live point.
         let term_location = self.cx.location_map.to_location(term_point);
-        debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
+        debug_assert_eq!(self.cx.body().terminator_loc(term_location.block), term_location,);
         let block = term_location.block;
         let entry_point = self.cx.location_map.entry_point(term_location.block);
         for p in (entry_point..term_point).rev() {
@@ -376,7 +373,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
             }
         }
 
-        let body = self.cx.body;
+        let body = self.cx.typeck.body;
         for &pred_block in body.basic_blocks.predecessors()[block].iter() {
             debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
 
@@ -403,7 +400,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
                 continue;
             }
 
-            let pred_term_loc = self.cx.body.terminator_loc(pred_block);
+            let pred_term_loc = self.cx.body().terminator_loc(pred_block);
             let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc);
 
             // If the terminator of this predecessor either *assigns*
@@ -463,6 +460,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
 }
 
 impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
+    fn body(&self) -> &Body<'tcx> {
+        self.typeck.body
+    }
     /// Returns `true` if the local variable (or some part of it) is initialized at the current
     /// cursor position. Callers should call one of the `seek` methods immediately before to point
     /// the cursor to the desired location.
@@ -481,7 +481,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
     /// DROP of some local variable will have an effect -- note that
     /// drops, as they may unwind, are always terminators.
     fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
-        self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block));
+        self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block));
         self.initialized_at_curr_loc(mpi)
     }
 
@@ -491,7 +491,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
     /// **Warning:** Does not account for the result of `Call`
     /// instructions.
     fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
-        self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block));
+        self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block));
         self.initialized_at_curr_loc(mpi)
     }
 
@@ -526,7 +526,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
             values::pretty_print_points(self.location_map, live_at.iter()),
         );
 
-        let local_span = self.body.local_decls()[dropped_local].source_info.span;
+        let local_span = self.body().local_decls()[dropped_local].source_info.span;
         let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
             let typeck = &self.typeck;
             move || Self::compute_drop_data(typeck, dropped_ty, local_span)
@@ -544,7 +544,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
 
         drop_data.dropck_result.report_overflows(
             self.typeck.infcx.tcx,
-            self.body.source_info(*drop_locations.first().unwrap()).span,
+            self.typeck.body.source_info(*drop_locations.first().unwrap()).span,
             dropped_ty,
         );
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index c1e23cb54117b..9d5022f2bef4c 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -174,11 +174,11 @@ pub(crate) fn type_check<'a, 'tcx>(
     let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span };
     verifier.visit_body(body);
 
-    typeck.typeck_mir(body);
-    typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output);
-    typeck.check_signature_annotation(body);
+    typeck.typeck_mir();
+    typeck.equate_inputs_and_outputs(&normalized_inputs_and_output);
+    typeck.check_signature_annotation();
 
-    liveness::generate(&mut typeck, body, &location_map, flow_inits, move_data);
+    liveness::generate(&mut typeck, &location_map, flow_inits, move_data);
 
     let opaque_type_values =
         opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
@@ -485,6 +485,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     fn visit_body(&mut self, body: &Body<'tcx>) {
+        debug_assert!(std::ptr::eq(self.typeck.body, body));
         // We intentionally do not recurse into `body.required_consts` or
         // `body.mentioned_items` here as the MIR at this phase should still
         // refer to all items and we don't want to check them multiple times.
@@ -542,7 +543,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
 
         self.visit_body(promoted_body);
 
-        self.typeck.typeck_mir(promoted_body);
+        self.typeck.typeck_mir();
 
         self.typeck.body = parent_body;
         // Merge the outlives constraints back in, at the given location.
@@ -892,8 +893,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         self.infcx.tcx
     }
 
-    #[instrument(skip(self, body), level = "debug")]
-    fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
+    #[instrument(skip(self), level = "debug")]
+    fn check_stmt(&mut self, stmt: &Statement<'tcx>, location: Location) {
         let tcx = self.tcx();
         debug!("stmt kind: {:?}", stmt.kind);
         match &stmt.kind {
@@ -916,11 +917,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     Some(l)
-                        if matches!(body.local_decls[l].local_info(), LocalInfo::AggregateTemp) =>
+                        if matches!(
+                            self.body.local_decls[l].local_info(),
+                            LocalInfo::AggregateTemp
+                        ) =>
                     {
                         ConstraintCategory::Usage
                     }
-                    Some(l) if !body.local_decls[l].is_user_variable() => {
+                    Some(l) if !self.body.local_decls[l].is_user_variable() => {
                         ConstraintCategory::Boring
                     }
                     _ => ConstraintCategory::Assignment,
@@ -928,14 +932,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 debug!(
                     "assignment category: {:?} {:?}",
                     category,
-                    place.as_local().map(|l| &body.local_decls[l])
+                    place.as_local().map(|l| &self.body.local_decls[l])
                 );
 
-                let place_ty = place.ty(body, tcx).ty;
+                let place_ty = place.ty(self.body, tcx).ty;
                 debug!(?place_ty);
                 let place_ty = self.normalize(place_ty, location);
                 debug!("place_ty normalized: {:?}", place_ty);
-                let rv_ty = rv.ty(body, tcx);
+                let rv_ty = rv.ty(self.body, tcx);
                 debug!(?rv_ty);
                 let rv_ty = self.normalize(rv_ty, location);
                 debug!("normalized rv_ty: {:?}", rv_ty);
@@ -972,7 +976,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                 }
 
-                self.check_rvalue(body, rv, location);
+                self.check_rvalue(rv, location);
                 if !self.unsized_feature_enabled() {
                     let trait_ref = ty::TraitRef::new(
                         tcx,
@@ -987,7 +991,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
             }
             StatementKind::AscribeUserType(box (place, projection), variance) => {
-                let place_ty = place.ty(body, tcx).ty;
+                let place_ty = place.ty(self.body, tcx).ty;
                 if let Err(terr) = self.relate_type_and_user_type(
                     place_ty,
                     *variance,
@@ -1029,13 +1033,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
     }
 
-    #[instrument(skip(self, body, term_location), level = "debug")]
-    fn check_terminator(
-        &mut self,
-        body: &Body<'tcx>,
-        term: &Terminator<'tcx>,
-        term_location: Location,
-    ) {
+    #[instrument(skip(self, term_location), level = "debug")]
+    fn check_terminator(&mut self, term: &Terminator<'tcx>, term_location: Location) {
         let tcx = self.tcx();
         debug!("terminator kind: {:?}", term.kind);
         match &term.kind {
@@ -1055,7 +1054,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             TerminatorKind::SwitchInt { discr, .. } => {
                 self.check_operand(discr, term_location);
 
-                let switch_ty = discr.ty(body, tcx);
+                let switch_ty = discr.ty(self.body, tcx);
                 if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
                     span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
                 }
@@ -1074,7 +1073,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     self.check_operand(&arg.node, term_location);
                 }
 
-                let func_ty = func.ty(body, tcx);
+                let func_ty = func.ty(self.body, tcx);
                 debug!("func_ty.kind: {:?}", func_ty.kind());
 
                 let sig = match func_ty.kind() {
@@ -1142,7 +1141,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
 
                 if let TerminatorKind::Call { destination, target, .. } = term.kind {
-                    self.check_call_dest(body, term, &sig, destination, target, term_location);
+                    self.check_call_dest(term, &sig, destination, target, term_location);
                 }
 
                 // The ordinary liveness rules will ensure that all
@@ -1157,21 +1156,21 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     self.constraints.liveness_constraints.add_location(region_vid, term_location);
                 }
 
-                self.check_call_inputs(body, term, func, &sig, args, term_location, call_source);
+                self.check_call_inputs(term, func, &sig, args, term_location, call_source);
             }
             TerminatorKind::Assert { cond, msg, .. } => {
                 self.check_operand(cond, term_location);
 
-                let cond_ty = cond.ty(body, tcx);
+                let cond_ty = cond.ty(self.body, tcx);
                 if cond_ty != tcx.types.bool {
                     span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
                 }
 
                 if let AssertKind::BoundsCheck { len, index } = &**msg {
-                    if len.ty(body, tcx) != tcx.types.usize {
+                    if len.ty(self.body, tcx) != tcx.types.usize {
                         span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
                     }
-                    if index.ty(body, tcx) != tcx.types.usize {
+                    if index.ty(self.body, tcx) != tcx.types.usize {
                         span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
                     }
                 }
@@ -1179,10 +1178,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             TerminatorKind::Yield { value, resume_arg, .. } => {
                 self.check_operand(value, term_location);
 
-                match body.yield_ty() {
+                match self.body.yield_ty() {
                     None => span_mirbug!(self, term, "yield in non-coroutine"),
                     Some(ty) => {
-                        let value_ty = value.ty(body, tcx);
+                        let value_ty = value.ty(self.body, tcx);
                         if let Err(terr) = self.sub_types(
                             value_ty,
                             ty,
@@ -1201,10 +1200,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                 }
 
-                match body.resume_ty() {
+                match self.body.resume_ty() {
                     None => span_mirbug!(self, term, "yield in non-coroutine"),
                     Some(ty) => {
-                        let resume_ty = resume_arg.ty(body, tcx);
+                        let resume_ty = resume_arg.ty(self.body, tcx);
                         if let Err(terr) = self.sub_types(
                             ty,
                             resume_ty.ty,
@@ -1228,7 +1227,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
     fn check_call_dest(
         &mut self,
-        body: &Body<'tcx>,
         term: &Terminator<'tcx>,
         sig: &ty::FnSig<'tcx>,
         destination: Place<'tcx>,
@@ -1238,7 +1236,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         let tcx = self.tcx();
         match target {
             Some(_) => {
-                let dest_ty = destination.ty(body, tcx).ty;
+                let dest_ty = destination.ty(self.body, tcx).ty;
                 let dest_ty = self.normalize(dest_ty, term_location);
                 let category = match destination.as_local() {
                     Some(RETURN_PLACE) => {
@@ -1254,7 +1252,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             ConstraintCategory::Return(ReturnConstraint::Normal)
                         }
                     }
-                    Some(l) if !body.local_decls[l].is_user_variable() => {
+                    Some(l) if !self.body.local_decls[l].is_user_variable() => {
                         ConstraintCategory::Boring
                     }
                     // The return type of a call is interesting for diagnostics.
@@ -1295,10 +1293,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
     }
 
-    #[instrument(level = "debug", skip(self, body, term, func, term_location, call_source))]
+    #[instrument(level = "debug", skip(self, term, func, term_location, call_source))]
     fn check_call_inputs(
         &mut self,
-        body: &Body<'tcx>,
         term: &Terminator<'tcx>,
         func: &Operand<'tcx>,
         sig: &ty::FnSig<'tcx>,
@@ -1310,7 +1307,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
         }
 
-        let func_ty = func.ty(body, self.infcx.tcx);
+        let func_ty = func.ty(self.body, self.infcx.tcx);
         if let ty::FnDef(def_id, _) = *func_ty.kind() {
             // Some of the SIMD intrinsics are special: they need a particular argument to be a
             // constant. (Eventually this should use const-generics, but those are not up for the
@@ -1334,7 +1331,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         debug!(?func_ty);
 
         for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() {
-            let op_arg_ty = op_arg.node.ty(body, self.tcx());
+            let op_arg_ty = op_arg.node.ty(self.body, self.tcx());
 
             let op_arg_ty = self.normalize(op_arg_ty, term_location);
             let category = if call_source.from_hir_call() {
@@ -1358,16 +1355,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
     }
 
-    fn check_iscleanup(&mut self, body: &Body<'tcx>, block_data: &BasicBlockData<'tcx>) {
+    fn check_iscleanup(&mut self, block_data: &BasicBlockData<'tcx>) {
         let is_cleanup = block_data.is_cleanup;
         self.last_span = block_data.terminator().source_info.span;
         match block_data.terminator().kind {
             TerminatorKind::Goto { target } => {
-                self.assert_iscleanup(body, block_data, target, is_cleanup)
+                self.assert_iscleanup(block_data, target, is_cleanup)
             }
             TerminatorKind::SwitchInt { ref targets, .. } => {
                 for target in targets.all_targets() {
-                    self.assert_iscleanup(body, block_data, *target, is_cleanup);
+                    self.assert_iscleanup(block_data, *target, is_cleanup);
                 }
             }
             TerminatorKind::UnwindResume => {
@@ -1399,55 +1396,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 if is_cleanup {
                     span_mirbug!(self, block_data, "yield in cleanup block")
                 }
-                self.assert_iscleanup(body, block_data, resume, is_cleanup);
+                self.assert_iscleanup(block_data, resume, is_cleanup);
                 if let Some(drop) = drop {
-                    self.assert_iscleanup(body, block_data, drop, is_cleanup);
+                    self.assert_iscleanup(block_data, drop, is_cleanup);
                 }
             }
             TerminatorKind::Unreachable => {}
             TerminatorKind::Drop { target, unwind, .. }
             | TerminatorKind::Assert { target, unwind, .. } => {
-                self.assert_iscleanup(body, block_data, target, is_cleanup);
-                self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
+                self.assert_iscleanup(block_data, target, is_cleanup);
+                self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
             }
             TerminatorKind::Call { ref target, unwind, .. } => {
                 if let &Some(target) = target {
-                    self.assert_iscleanup(body, block_data, target, is_cleanup);
+                    self.assert_iscleanup(block_data, target, is_cleanup);
                 }
-                self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
+                self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
             }
             TerminatorKind::FalseEdge { real_target, imaginary_target } => {
-                self.assert_iscleanup(body, block_data, real_target, is_cleanup);
-                self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup);
+                self.assert_iscleanup(block_data, real_target, is_cleanup);
+                self.assert_iscleanup(block_data, imaginary_target, is_cleanup);
             }
             TerminatorKind::FalseUnwind { real_target, unwind } => {
-                self.assert_iscleanup(body, block_data, real_target, is_cleanup);
-                self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
+                self.assert_iscleanup(block_data, real_target, is_cleanup);
+                self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
             }
             TerminatorKind::InlineAsm { ref targets, unwind, .. } => {
                 for &target in targets {
-                    self.assert_iscleanup(body, block_data, target, is_cleanup);
+                    self.assert_iscleanup(block_data, target, is_cleanup);
                 }
-                self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
+                self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
             }
         }
     }
 
-    fn assert_iscleanup(
-        &mut self,
-        body: &Body<'tcx>,
-        ctxt: &dyn fmt::Debug,
-        bb: BasicBlock,
-        iscleanuppad: bool,
-    ) {
-        if body[bb].is_cleanup != iscleanuppad {
+    fn assert_iscleanup(&mut self, ctxt: &dyn fmt::Debug, bb: BasicBlock, iscleanuppad: bool) {
+        if self.body[bb].is_cleanup != iscleanuppad {
             span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad);
         }
     }
 
     fn assert_iscleanup_unwind(
         &mut self,
-        body: &Body<'tcx>,
         ctxt: &dyn fmt::Debug,
         unwind: UnwindAction,
         is_cleanup: bool,
@@ -1457,7 +1447,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 if is_cleanup {
                     span_mirbug!(self, ctxt, "unwind on cleanup block")
                 }
-                self.assert_iscleanup(body, ctxt, unwind, true);
+                self.assert_iscleanup(ctxt, unwind, true);
             }
             UnwindAction::Continue => {
                 if is_cleanup {
@@ -1468,8 +1458,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
     }
 
-    fn check_local(&mut self, body: &Body<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) {
-        match body.local_kind(local) {
+    fn check_local(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
+        match self.body.local_kind(local) {
             LocalKind::ReturnPointer | LocalKind::Arg => {
                 // return values of normal functions are required to be
                 // sized by typeck, but return values of ADT constructors are
@@ -1598,23 +1588,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         }
     }
 
-    #[instrument(skip(self, body), level = "debug")]
-    fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
+    #[instrument(skip(self), level = "debug")]
+    fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         let tcx = self.tcx();
-        let span = body.source_info(location).span;
+        let span = self.body.source_info(location).span;
 
         match rvalue {
             Rvalue::Aggregate(ak, ops) => {
                 for op in ops {
                     self.check_operand(op, location);
                 }
-                self.check_aggregate_rvalue(body, rvalue, ak, ops, location)
+                self.check_aggregate_rvalue(rvalue, ak, ops, location)
             }
 
             Rvalue::Repeat(operand, len) => {
                 self.check_operand(operand, location);
 
-                let array_ty = rvalue.ty(body.local_decls(), tcx);
+                let array_ty = rvalue.ty(self.body.local_decls(), tcx);
                 self.prove_predicate(
                     ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())),
                     Locations::Single(location),
@@ -1633,7 +1623,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                         Operand::Move(place) => {
                             // Make sure that repeated elements implement `Copy`.
-                            let ty = place.ty(body, tcx).ty;
+                            let ty = place.ty(self.body, tcx).ty;
                             let trait_ref = ty::TraitRef::new(
                                 tcx,
                                 tcx.require_lang_item(LangItem::Copy, Some(span)),
@@ -1688,7 +1678,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 match *cast_kind {
                     CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => {
                         let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
-                        let src_ty = op.ty(body, tcx);
+                        let src_ty = op.ty(self.body, tcx);
                         let mut src_sig = src_ty.fn_sig(tcx);
                         if let ty::FnDef(def_id, _) = src_ty.kind()
                             && let ty::FnPtr(_, target_hdr) = *ty.kind()
@@ -1697,7 +1687,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             && let Some(safe_sig) = tcx.adjust_target_feature_sig(
                                 *def_id,
                                 src_sig,
-                                body.source.def_id(),
+                                self.body.source.def_id(),
                             )
                         {
                             src_sig = safe_sig;
@@ -1790,7 +1780,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         PointerCoercion::ClosureFnPointer(safety),
                         coercion_source,
                     ) => {
-                        let sig = match op.ty(body, tcx).kind() {
+                        let sig = match op.ty(self.body, tcx).kind() {
                             ty::Closure(_, args) => args.as_closure().sig(),
                             _ => bug!(),
                         };
@@ -1819,7 +1809,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         PointerCoercion::UnsafeFnPointer,
                         coercion_source,
                     ) => {
-                        let fn_sig = op.ty(body, tcx).fn_sig(tcx);
+                        let fn_sig = op.ty(self.body, tcx).fn_sig(tcx);
 
                         // The type that we see in the fcx is like
                         // `foo::<'a, 'b>`, where `foo` is the path to a
@@ -1853,7 +1843,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         let trait_ref = ty::TraitRef::new(
                             tcx,
                             tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)),
-                            [op.ty(body, tcx), ty],
+                            [op.ty(self.body, tcx), ty],
                         );
 
                         let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
@@ -1879,7 +1869,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             _ => panic!("Invalid dyn* cast_ty"),
                         };
 
-                        let self_ty = op.ty(body, tcx);
+                        let self_ty = op.ty(self.body, tcx);
 
                         let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
                         self.prove_predicates(
@@ -1906,7 +1896,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         PointerCoercion::MutToConstPointer,
                         coercion_source,
                     ) => {
-                        let ty::RawPtr(ty_from, hir::Mutability::Mut) = op.ty(body, tcx).kind()
+                        let ty::RawPtr(ty_from, hir::Mutability::Mut) =
+                            op.ty(self.body, tcx).kind()
                         else {
                             span_mirbug!(self, rvalue, "unexpected base type for cast {:?}", ty,);
                             return;
@@ -1934,7 +1925,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
 
                     CastKind::PointerCoercion(PointerCoercion::ArrayToPointer, coercion_source) => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
 
                         let opt_ty_elem_mut = match ty_from.kind() {
                             ty::RawPtr(array_ty, array_mut) => match array_ty.kind() {
@@ -1997,7 +1988,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
 
                     CastKind::PointerExposeProvenance => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2015,7 +2006,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
 
                     CastKind::PointerWithExposedProvenance => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2032,7 +2023,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::IntToInt => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2049,7 +2040,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::IntToFloat => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2066,7 +2057,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::FloatToInt => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2083,7 +2074,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::FloatToFloat => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2100,7 +2091,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::FnPtrToPtr => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2117,7 +2108,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                     CastKind::PtrToPtr => {
-                        let ty_from = op.ty(body, tcx);
+                        let ty_from = op.ty(self.body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
@@ -2193,7 +2184,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             }
 
             Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
-                self.add_reborrow_constraint(body, location, *region, borrowed_place);
+                self.add_reborrow_constraint(location, *region, borrowed_place);
             }
 
             Rvalue::BinaryOp(
@@ -2203,12 +2194,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 self.check_operand(left, location);
                 self.check_operand(right, location);
 
-                let ty_left = left.ty(body, tcx);
+                let ty_left = left.ty(self.body, tcx);
                 match ty_left.kind() {
                     // Types with regions are comparable if they have a common super-type.
                     ty::RawPtr(_, _) | ty::FnPtr(..) => {
-                        let ty_right = right.ty(body, tcx);
-                        let common_ty = self.infcx.next_ty_var(body.source_info(location).span);
+                        let ty_right = right.ty(self.body, tcx);
+                        let common_ty =
+                            self.infcx.next_ty_var(self.body.source_info(location).span);
                         self.sub_types(
                             ty_left,
                             common_ty,
@@ -2237,7 +2229,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     // For types with no regions we can just check that the
                     // both operands have the same type.
                     ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_)
-                        if ty_left == right.ty(body, tcx) => {}
+                        if ty_left == right.ty(self.body, tcx) => {}
                     // Other types are compared by trait methods, not by
                     // `Rvalue::BinaryOp`.
                     _ => span_mirbug!(
@@ -2245,7 +2237,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         rvalue,
                         "unexpected comparison types {:?} and {:?}",
                         ty_left,
-                        right.ty(body, tcx)
+                        right.ty(self.body, tcx)
                     ),
                 }
             }
@@ -2326,7 +2318,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
     fn check_aggregate_rvalue(
         &mut self,
-        body: &Body<'tcx>,
         rvalue: &Rvalue<'tcx>,
         aggregate_kind: &AggregateKind<'tcx>,
         operands: &IndexSlice<FieldIdx, Operand<'tcx>>,
@@ -2359,7 +2350,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     continue;
                 }
             };
-            let operand_ty = operand.ty(body, tcx);
+            let operand_ty = operand.ty(self.body, tcx);
             let operand_ty = self.normalize(operand_ty, location);
 
             if let Err(terr) = self.sub_types(
@@ -2389,7 +2380,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     /// - `borrowed_place`: the place `P` being borrowed
     fn add_reborrow_constraint(
         &mut self,
-        body: &Body<'tcx>,
         location: Location,
         borrow_region: ty::Region<'tcx>,
         borrowed_place: &Place<'tcx>,
@@ -2428,7 +2418,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         let def = self.body.source.def_id().expect_local();
         let upvars = tcx.closure_captures(def);
         let field =
-            path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body);
+            path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), self.body);
         let category = if let Some(field) = field {
             ConstraintCategory::ClosureUpvar(field)
         } else {
@@ -2440,7 +2430,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
             match elem {
                 ProjectionElem::Deref => {
-                    let base_ty = base.ty(body, tcx).ty;
+                    let base_ty = base.ty(self.body, tcx).ty;
 
                     debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
                     match base_ty.kind() {
@@ -2449,7 +2439,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 sup: ref_region.as_var(),
                                 sub: borrow_region.as_var(),
                                 locations: location.to_locations(),
-                                span: location.to_locations().span(body),
+                                span: location.to_locations().span(self.body),
                                 category,
                                 variance_info: ty::VarianceDiagInfo::default(),
                                 from_closure: false,
@@ -2634,27 +2624,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         tcx.predicates_of(def_id).instantiate(tcx, args)
     }
 
-    #[instrument(skip(self, body), level = "debug")]
-    fn typeck_mir(&mut self, body: &Body<'tcx>) {
-        self.last_span = body.span;
-        debug!(?body.span);
+    #[instrument(skip(self), level = "debug")]
+    fn typeck_mir(&mut self) {
+        self.last_span = self.body.span;
+        debug!(?self.body.span);
 
-        for (local, local_decl) in body.local_decls.iter_enumerated() {
-            self.check_local(body, local, local_decl);
+        for (local, local_decl) in self.body.local_decls.iter_enumerated() {
+            self.check_local(local, local_decl);
         }
 
-        for (block, block_data) in body.basic_blocks.iter_enumerated() {
+        for (block, block_data) in self.body.basic_blocks.iter_enumerated() {
             let mut location = Location { block, statement_index: 0 };
             for stmt in &block_data.statements {
                 if !stmt.source_info.span.is_dummy() {
                     self.last_span = stmt.source_info.span;
                 }
-                self.check_stmt(body, stmt, location);
+                self.check_stmt(stmt, location);
                 location.statement_index += 1;
             }
 
-            self.check_terminator(body, block_data.terminator(), location);
-            self.check_iscleanup(body, block_data);
+            self.check_terminator(block_data.terminator(), location);
+            self.check_iscleanup(block_data);
         }
     }
 }

From 75a69a48f3aebaaa7cb3ce2ffcd3816f10895f70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Tue, 11 Mar 2025 11:53:57 +0100
Subject: [PATCH 25/25] Do not download GCC in tests

---
 src/bootstrap/src/core/build_steps/gcc.rs | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index 7e1ec39659a30..5a4bc9bdbcba9 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -13,11 +13,9 @@ use std::path::{Path, PathBuf};
 use std::sync::OnceLock;
 
 use build_helper::ci::CiEnv;
-use build_helper::git::get_closest_merge_commit;
 
-use crate::Config;
 use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
-use crate::core::config::{GccCiMode, TargetSelection};
+use crate::core::config::TargetSelection;
 use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
 use crate::utils::exec::command;
 use crate::utils::helpers::{self, t};
@@ -93,9 +91,10 @@ pub enum GccBuildStatus {
 /// Tries to download GCC from CI if it is enabled and GCC artifacts
 /// are available for the given target.
 /// Returns a path to the libgccjit.so file.
+#[cfg(not(test))]
 fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<PathBuf> {
     // Try to download GCC from CI if configured and available
-    if !matches!(builder.config.gcc_ci_mode, GccCiMode::DownloadFromCi) {
+    if !matches!(builder.config.gcc_ci_mode, crate::core::config::GccCiMode::DownloadFromCi) {
         return None;
     }
     if target != "x86_64-unknown-linux-gnu" {
@@ -114,6 +113,11 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<Pa
     Some(root.join("libgccjit.so"))
 }
 
+#[cfg(test)]
+fn try_download_gcc(_builder: &Builder<'_>, _target: TargetSelection) -> Option<PathBuf> {
+    None
+}
+
 /// This returns information about whether GCC should be built or if it's already built.
 /// It transparently handles downloading GCC from CI if needed.
 ///
@@ -247,12 +251,16 @@ pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) {
 }
 
 /// The absolute path to the downloaded GCC artifacts.
-fn ci_gcc_root(config: &Config) -> PathBuf {
+#[cfg(not(test))]
+fn ci_gcc_root(config: &crate::Config) -> PathBuf {
     config.out.join(config.build).join("ci-gcc")
 }
 
 /// This retrieves the GCC sha we *want* to use, according to git history.
-fn detect_gcc_sha(config: &Config, is_git: bool) -> String {
+#[cfg(not(test))]
+fn detect_gcc_sha(config: &crate::Config, is_git: bool) -> String {
+    use build_helper::git::get_closest_merge_commit;
+
     let gcc_sha = if is_git {
         get_closest_merge_commit(
             Some(&config.src),