From 0e10c396396085049c62323b146f1ea38b4b9d5c Mon Sep 17 00:00:00 2001
From: Eduard-Mihai Burtescu <eddyb@lyken.rs>
Date: Mon, 28 Jun 2021 22:16:06 +0300
Subject: [PATCH 1/3] Allow reifying intrinsics to `fn` pointers.

---
 compiler/rustc_borrowck/src/type_check/mod.rs | 15 +++++++-
 compiler/rustc_hir_typeck/src/cast.rs         |  4 --
 compiler/rustc_hir_typeck/src/coercion.rs     | 29 +++++++++-----
 compiler/rustc_middle/src/ty/error.rs         |  1 -
 compiler/rustc_middle/src/ty/instance.rs      |  4 ++
 .../src/error_reporting/infer/mod.rs          |  5 +--
 compiler/rustc_type_ir/src/error.rs           |  4 +-
 tests/ui/intrinsics/reify-intrinsic.rs        | 38 +++++++++++++------
 tests/ui/intrinsics/reify-intrinsic.stderr    | 30 ---------------
 .../transmute-different-sizes-reified.rs      | 31 +++++++++++++++
 .../transmute-different-sizes-reified.stderr  | 30 +++++++++++++++
 11 files changed, 127 insertions(+), 64 deletions(-)
 delete mode 100644 tests/ui/intrinsics/reify-intrinsic.stderr
 create mode 100644 tests/ui/transmute/transmute-different-sizes-reified.rs
 create mode 100644 tests/ui/transmute/transmute-different-sizes-reified.stderr

diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 26a5d438edb98..26f42a0ea3247 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -3,7 +3,7 @@
 use std::rc::Rc;
 use std::{fmt, iter, mem};
 
-use rustc_abi::FieldIdx;
+use rustc_abi::{ExternAbi, FieldIdx};
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::ErrorGuaranteed;
@@ -39,7 +39,8 @@ use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::Spanned;
-use rustc_span::{DUMMY_SP, Span, sym};
+use rustc_span::symbol::sym;
+use rustc_span::{DUMMY_SP, Span};
 use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
 use tracing::{debug, instrument, trace};
@@ -1743,6 +1744,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             };
                         }
 
+                        // NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`
+                        // in `rustc_typeck::check::coercion`.
+                        let src_sig = src_sig.map_bound(|mut sig| -> _ {
+                            if matches!(sig.abi, ExternAbi::RustIntrinsic) {
+                                sig.abi = ExternAbi::Rust;
+                            }
+
+                            sig
+                        });
+
                         let src_ty = Ty::new_fn_ptr(tcx, src_sig);
                         // HACK: We want to assert that the signature of the source fn is
                         // well-formed, because we don't enforce that via the WF of FnDef
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index d8015bcc91424..86c162806db80 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -38,7 +38,6 @@ use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::cast::{CastKind, CastTy};
-use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef};
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -748,9 +747,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             AllowTwoPhase::No,
                             None,
                         );
-                        if let Err(TypeError::IntrinsicCast) = res {
-                            return Err(CastError::IllegalCast);
-                        }
                         if res.is_err() {
                             return Err(CastError::NonScalar);
                         }
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 14dd0f32d8206..830bfda6403d9 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -98,6 +98,23 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
 
 type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
 
+/// Make any adjustments necessary for a function signature to be compatible
+/// with reification to a `fn` pointer. In particular, intrinsics are imported
+/// using pseudo-ABIs (`extern "rust-intrinsic" {...}`) currently, but that's
+/// an implementation detail and any `fn` pointers that may be taken to them
+/// should be indistinguishable from those to regular Rust functions, in order
+/// to allow e.g. libcore public APIs to be replaced with intrinsics, without
+/// breaking code that was, explicitly or implicitly, creating `fn` pointers.
+// FIXME(eddyb) intrinsics shouldn't use pseudo-ABIs, but rather the  Rust ABI
+// and some other way to indicate that they are intrinsics (e.g. new attributes).
+fn prepare_fn_sig_for_reify<'tcx>(mut sig: ty::FnSig<'tcx>) -> ty::FnSig<'tcx> {
+    if matches!(sig.abi, ExternAbi::RustIntrinsic) {
+        sig.abi = ExternAbi::Rust;
+    }
+
+    sig
+}
+
 /// Coercing a mutable reference to an immutable works, while
 /// coercing `&T` to `&mut T` should be forbidden.
 fn coerce_mutbls<'tcx>(
@@ -911,12 +928,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         match b.kind() {
             ty::FnPtr(_, b_hdr) => {
                 let mut a_sig = a.fn_sig(self.tcx);
-                if let ty::FnDef(def_id, _) = *a.kind() {
-                    // Intrinsics are not coercible to function pointers
-                    if self.tcx.intrinsic(def_id).is_some() {
-                        return Err(TypeError::IntrinsicCast);
-                    }
+                // NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
+                a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
 
+                if let ty::FnDef(def_id, _) = *a.kind() {
                     let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
                     if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
                         return Err(TypeError::ForceInlineCast);
@@ -1266,10 +1281,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         };
         if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
-            // Intrinsics are not coercible to function pointers.
-            if a_sig.abi() == ExternAbi::RustIntrinsic || b_sig.abi() == ExternAbi::RustIntrinsic {
-                return Err(TypeError::IntrinsicCast);
-            }
             // The signature must match.
             let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
             let sig = self
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 8c1991ddb36ee..eef27c57e4d84 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -112,7 +112,6 @@ impl<'tcx> TypeError<'tcx> {
             TypeError::ForceInlineCast => {
                 "cannot coerce functions which must be inlined to function pointers".into()
             }
-            TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
             TypeError::TargetFeatureCast(_) => {
                 "cannot coerce functions with `#[target_feature]` to safe function pointers".into()
             }
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index b7a648aae3f19..085a4d03ed10e 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -656,6 +656,10 @@ impl<'tcx> Instance<'tcx> {
                     // unresolved instance.
                     resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }
                 }
+                InstanceKind::Intrinsic(def_id) => {
+                    debug!(" => fn pointer created for intrinsic call");
+                    resolved.def = InstanceKind::ReifyShim(def_id, reason);
+                }
                 _ => {}
             }
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 091773009e98d..af30825e80bff 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -2321,7 +2321,7 @@ impl<'tcx> ObligationCause<'tcx> {
                 {
                     FailureCode::Error0644
                 }
-                TypeError::IntrinsicCast | TypeError::ForceInlineCast => FailureCode::Error0308,
+                TypeError::ForceInlineCast => FailureCode::Error0308,
                 _ => FailureCode::Error0308,
             },
         }
@@ -2387,9 +2387,6 @@ impl<'tcx> ObligationCause<'tcx> {
                 TypeError::ForceInlineCast => {
                     ObligationCauseFailureCode::CantCoerceForceInline { span, subdiags }
                 }
-                TypeError::IntrinsicCast => {
-                    ObligationCauseFailureCode::CantCoerceIntrinsic { span, subdiags }
-                }
                 _ => ObligationCauseFailureCode::Generic { span, subdiags },
             },
         }
diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs
index 68b11489ae7c1..5d73e00dbf26b 100644
--- a/compiler/rustc_type_ir/src/error.rs
+++ b/compiler/rustc_type_ir/src/error.rs
@@ -50,7 +50,6 @@ pub enum TypeError<I: Interner> {
     ExistentialMismatch(ExpectedFound<I::BoundExistentialPredicates>),
     ConstMismatch(ExpectedFound<I::Const>),
 
-    IntrinsicCast,
     /// `#[rustc_force_inline]` functions must be inlined and must not be codegened independently,
     /// so casting to a function pointer must be prohibited.
     ForceInlineCast,
@@ -86,8 +85,7 @@ impl<I: Interner> TypeError<I> {
             | ProjectionMismatched(_)
             | ExistentialMismatch(_)
             | ConstMismatch(_)
-            | ForceInlineCast
-            | IntrinsicCast => true,
+            | ForceInlineCast => true,
         }
     }
 }
diff --git a/tests/ui/intrinsics/reify-intrinsic.rs b/tests/ui/intrinsics/reify-intrinsic.rs
index 5b2324235c1a9..c048e6c8dd5f2 100644
--- a/tests/ui/intrinsics/reify-intrinsic.rs
+++ b/tests/ui/intrinsics/reify-intrinsic.rs
@@ -1,22 +1,38 @@
-//@ check-fail
+//@ run-pass
 
 #![feature(core_intrinsics, intrinsics)]
 
-fn a() {
-    let _: unsafe fn(isize) -> usize = std::mem::transmute;
-    //~^ ERROR cannot coerce
+// NOTE(eddyb) `#[inline(never)]` and returning `fn` pointers from functions is
+// done to force codegen (of the reification-to-`fn`-ptr shims around intrinsics).
+
+#[inline(never)]
+fn a() -> unsafe fn(isize) -> usize {
+    let f: unsafe fn(isize) -> usize = std::mem::transmute;
+    f
 }
 
-fn b() {
-    let _ = std::mem::transmute as unsafe fn(isize) -> usize;
-    //~^ ERROR casting
+#[inline(never)]
+fn b() -> unsafe fn(isize) -> usize {
+    let f = std::mem::transmute as unsafe fn(isize) -> usize;
+    f
 }
 
-fn c() {
-    let _: [unsafe fn(f32) -> f32; 2] = [
-        std::intrinsics::floorf32, //~ ERROR cannot coerce
+#[inline(never)]
+fn c() -> [unsafe fn(f32) -> f32; 2] {
+    let fs = [
+        std::intrinsics::floorf32,
         std::intrinsics::log2f32,
     ];
+    fs
 }
 
-fn main() {}
+fn main() {
+    unsafe {
+        assert_eq!(a()(-1), !0);
+        assert_eq!(b()(-1), !0);
+
+        let [floorf32_ptr, log2f32_ptr] = c();
+        assert_eq!(floorf32_ptr(1.5), 1.0);
+        assert_eq!(log2f32_ptr(2.0), 1.0);
+    }
+}
diff --git a/tests/ui/intrinsics/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr
deleted file mode 100644
index aea6f838e0d44..0000000000000
--- a/tests/ui/intrinsics/reify-intrinsic.stderr
+++ /dev/null
@@ -1,30 +0,0 @@
-error[E0308]: cannot coerce intrinsics to function pointers
-  --> $DIR/reify-intrinsic.rs:6:40
-   |
-LL |     let _: unsafe fn(isize) -> usize = std::mem::transmute;
-   |            -------------------------   ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
-   |            |
-   |            expected due to this
-   |
-   = note: expected fn pointer `unsafe fn(isize) -> usize`
-                 found fn item `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}`
-
-error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe fn(isize) -> usize` is invalid
-  --> $DIR/reify-intrinsic.rs:11:13
-   |
-LL |     let _ = std::mem::transmute as unsafe fn(isize) -> usize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0308]: cannot coerce intrinsics to function pointers
-  --> $DIR/reify-intrinsic.rs:17:9
-   |
-LL |         std::intrinsics::floorf32,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
-   |
-   = note: expected fn pointer `unsafe fn(_) -> _`
-                 found fn item `unsafe fn(_) -> _ {floorf32}`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0606.
-For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/transmute/transmute-different-sizes-reified.rs b/tests/ui/transmute/transmute-different-sizes-reified.rs
new file mode 100644
index 0000000000000..f355a6b78b4de
--- /dev/null
+++ b/tests/ui/transmute/transmute-different-sizes-reified.rs
@@ -0,0 +1,31 @@
+//@ normalize-stderr: "\d+ bits" -> "N bits"
+
+// Tests that `transmute` cannot be indirectly called on types of different size.
+
+#![allow(warnings)]
+#![feature(specialization)]
+
+use std::mem::transmute;
+
+unsafe fn f() {
+    let _: i8 = (transmute as unsafe fn(_) -> _)(16i16);
+    //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
+}
+
+unsafe fn g<T>(x: &T) {
+    let _: i8 = (transmute as unsafe fn(_) -> _)(x);
+    //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
+}
+
+trait Specializable { type Output; }
+
+impl<T> Specializable for T {
+    default type Output = u16;
+}
+
+unsafe fn specializable<T>(x: u16) -> <T as Specializable>::Output {
+    (transmute as unsafe fn(_) -> _)(x)
+    //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
+}
+
+fn main() {}
diff --git a/tests/ui/transmute/transmute-different-sizes-reified.stderr b/tests/ui/transmute/transmute-different-sizes-reified.stderr
new file mode 100644
index 0000000000000..bde5da74ee7db
--- /dev/null
+++ b/tests/ui/transmute/transmute-different-sizes-reified.stderr
@@ -0,0 +1,30 @@
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-different-sizes-reified.rs:11:18
+   |
+LL |     let _: i8 = (transmute as unsafe fn(_) -> _)(16i16);
+   |                  ^^^^^^^^^
+   |
+   = note: source type: `i16` (N bits)
+   = note: target type: `i8` (N bits)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-different-sizes-reified.rs:16:18
+   |
+LL |     let _: i8 = (transmute as unsafe fn(_) -> _)(x);
+   |                  ^^^^^^^^^
+   |
+   = note: source type: `&T` (N bits)
+   = note: target type: `i8` (N bits)
+
+error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
+  --> $DIR/transmute-different-sizes-reified.rs:27:6
+   |
+LL |     (transmute as unsafe fn(_) -> _)(x)
+   |      ^^^^^^^^^
+   |
+   = note: source type: `u16` (N bits)
+   = note: target type: `<T as Specializable>::Output` (this type does not have a fixed size)
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0512`.

From 1aed3822543830187f60389ee770c3b98358fa69 Mon Sep 17 00:00:00 2001
From: Eduard-Mihai Burtescu <eddyb@lyken.rs>
Date: Thu, 15 Jul 2021 12:44:49 +0300
Subject: [PATCH 2/3] rustc_mir: run the lower_intrinsics pass on MIR shims.

---
 compiler/rustc_mir_transform/src/shim.rs      |  4 +++-
 ...#1-discriminant_value.LowerIntrinsics.diff | 23 +++++++++++++++++++
 ....intrinsics-#1-forget.LowerIntrinsics.diff | 23 +++++++++++++++++++
 ...intrinsics-#1-size_of.LowerIntrinsics.diff | 23 +++++++++++++++++++
 ...insics-#1-unreachable.LowerIntrinsics.diff | 22 ++++++++++++++++++
 ...nsics-#1-wrapping_add.LowerIntrinsics.diff | 23 +++++++++++++++++++
 tests/mir-opt/lower_intrinsics.rs             | 12 ++++++++++
 7 files changed, 129 insertions(+), 1 deletion(-)
 create mode 100644 tests/mir-opt/lower_intrinsics.core.intrinsics-#1-discriminant_value.LowerIntrinsics.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.core.intrinsics-#1-forget.LowerIntrinsics.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.core.intrinsics-#1-size_of.LowerIntrinsics.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.core.intrinsics-#1-unreachable.LowerIntrinsics.diff
 create mode 100644 tests/mir-opt/lower_intrinsics.core.intrinsics-#1-wrapping_add.LowerIntrinsics.diff

diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index f7ec0f740d300..b2bb6c7f9e18c 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -21,7 +21,8 @@ use tracing::{debug, instrument};
 
 use crate::{
     abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline,
-    instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
+    instsimplify, lower_intrinsics, mentioned_items, pass_manager as pm, remove_noop_landing_pads,
+    simplify,
 };
 
 mod async_destructor_ctor;
@@ -154,6 +155,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<
             &add_moves_for_packed_drops::AddMovesForPackedDrops,
             &deref_separator::Derefer,
             &remove_noop_landing_pads::RemoveNoopLandingPads,
+            &lower_intrinsics::LowerIntrinsics,
             &simplify::SimplifyCfg::MakeShim,
             &instsimplify::InstSimplify::BeforeInline,
             // Perform inlining of `#[rustc_force_inline]`-annotated callees.
diff --git a/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-discriminant_value.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-discriminant_value.LowerIntrinsics.diff
new file mode 100644
index 0000000000000..ef3bcf4be0362
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-discriminant_value.LowerIntrinsics.diff
@@ -0,0 +1,23 @@
+- // MIR for `discriminant_value` before LowerIntrinsics
++ // MIR for `discriminant_value` after LowerIntrinsics
+
+  fn discriminant_value(_1: &T) -> <T as DiscriminantKind>::Discriminant {
+      let mut _0: <T as std::marker::DiscriminantKind>::Discriminant; // return place in scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+
+      bb0: {
+-         _0 = discriminant_value::<T>(move _1) -> bb1; // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // mir::Constant
+-                                          // + span: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // + literal: Const { ty: for<'r> extern "rust-intrinsic" fn(&'r T) -> <T as std::marker::DiscriminantKind>::Discriminant {std::intrinsics::discriminant_value::<T>}, val: Value(Scalar(<ZST>)) }
++         _0 = discriminant((*_1));        // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
++         goto -> bb1;                     // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb1: {
+          return;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb2 (cleanup): {
+          resume;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+  }
\ No newline at end of file
diff --git a/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-forget.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-forget.LowerIntrinsics.diff
new file mode 100644
index 0000000000000..23d0092e0e957
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-forget.LowerIntrinsics.diff
@@ -0,0 +1,23 @@
+- // MIR for `std::intrinsics::forget` before LowerIntrinsics
++ // MIR for `std::intrinsics::forget` after LowerIntrinsics
+
+  fn std::intrinsics::forget(_1: T) -> () {
+      let mut _0: ();                      // return place in scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+
+      bb0: {
+-         _0 = std::intrinsics::forget::<T>(move _1) -> bb1; // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // mir::Constant
+-                                          // + span: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // + literal: Const { ty: extern "rust-intrinsic" fn(T) {std::intrinsics::forget::<T>}, val: Value(Scalar(<ZST>)) }
++         _0 = const ();                   // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
++         goto -> bb1;                     // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb1: {
+          return;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb2 (cleanup): {
+          resume;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+  }
\ No newline at end of file
diff --git a/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-size_of.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-size_of.LowerIntrinsics.diff
new file mode 100644
index 0000000000000..461689a2d1a23
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-size_of.LowerIntrinsics.diff
@@ -0,0 +1,23 @@
+- // MIR for `std::intrinsics::size_of` before LowerIntrinsics
++ // MIR for `std::intrinsics::size_of` after LowerIntrinsics
+
+  fn std::intrinsics::size_of() -> usize {
+      let mut _0: usize;                   // return place in scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+
+      bb0: {
+-         _0 = std::intrinsics::size_of::<T>() -> bb1; // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // mir::Constant
+-                                          // + span: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::<T>}, val: Value(Scalar(<ZST>)) }
++         _0 = SizeOf(T);                  // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
++         goto -> bb1;                     // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb1: {
+          return;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb2 (cleanup): {
+          resume;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+  }
\ No newline at end of file
diff --git a/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-unreachable.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-unreachable.LowerIntrinsics.diff
new file mode 100644
index 0000000000000..01bfbc158c553
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-unreachable.LowerIntrinsics.diff
@@ -0,0 +1,22 @@
+- // MIR for `std::intrinsics::unreachable` before LowerIntrinsics
++ // MIR for `std::intrinsics::unreachable` after LowerIntrinsics
+
+  fn std::intrinsics::unreachable() -> ! {
+      let mut _0: !;                       // return place in scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+
+      bb0: {
+-         _0 = std::intrinsics::unreachable() -> bb1; // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // mir::Constant
+-                                          // + span: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // + literal: Const { ty: unsafe extern "rust-intrinsic" fn() -> ! {std::intrinsics::unreachable}, val: Value(Scalar(<ZST>)) }
++         unreachable;                     // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb1: {
+          return;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb2 (cleanup): {
+          resume;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+  }
\ No newline at end of file
diff --git a/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-wrapping_add.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-wrapping_add.LowerIntrinsics.diff
new file mode 100644
index 0000000000000..1965aca03a889
--- /dev/null
+++ b/tests/mir-opt/lower_intrinsics.core.intrinsics-#1-wrapping_add.LowerIntrinsics.diff
@@ -0,0 +1,23 @@
+- // MIR for `wrapping_add` before LowerIntrinsics
++ // MIR for `wrapping_add` after LowerIntrinsics
+
+  fn wrapping_add(_1: T, _2: T) -> T {
+      let mut _0: T;                       // return place in scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+
+      bb0: {
+-         _0 = wrapping_add::<T>(move _1, move _2) -> bb1; // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // mir::Constant
+-                                          // + span: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+-                                          // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {std::intrinsics::wrapping_add::<T>}, val: Value(Scalar(<ZST>)) }
++         _0 = Add(move _1, move _2);      // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
++         goto -> bb1;                     // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb1: {
+          return;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+
+      bb2 (cleanup): {
+          resume;                          // scope 0 at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+      }
+  }
\ No newline at end of file
diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs
index 4859d93546196..8e8d7d9191aab 100644
--- a/tests/mir-opt/lower_intrinsics.rs
+++ b/tests/mir-opt/lower_intrinsics.rs
@@ -267,3 +267,15 @@ pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug
     let _usize = ptr_metadata(b);
     let _vtable = ptr_metadata(c);
 }
+
+// Check that the MIR shims used for reifying intrinsics to `fn` pointers,
+// also go through the lowering pass.
+pub fn reify_intrinsics() -> impl Copy {
+    (
+        core::intrinsics::wrapping_add::<u32> as unsafe fn(_, _) -> _,
+        core::intrinsics::size_of::<u8> as unsafe fn() -> _,
+        core::intrinsics::unreachable as unsafe fn() -> !,
+        core::intrinsics::forget::<E> as unsafe fn(_),
+        core::intrinsics::discriminant_value::<E> as unsafe fn(_) -> _,
+    )
+}

From e8ef4efa8d8fb4875b3fad7dbbafc07c401c343d Mon Sep 17 00:00:00 2001
From: Pavel Grigorenko <GrigorenkoPV@ya.ru>
Date: Mon, 28 Oct 2024 15:57:02 +0300
Subject: [PATCH 3/3] This ICEs

---
 tests/ui/intrinsics/reify-intrinsic.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tests/ui/intrinsics/reify-intrinsic.rs b/tests/ui/intrinsics/reify-intrinsic.rs
index c048e6c8dd5f2..384812471a4a1 100644
--- a/tests/ui/intrinsics/reify-intrinsic.rs
+++ b/tests/ui/intrinsics/reify-intrinsic.rs
@@ -26,6 +26,12 @@ fn c() -> [unsafe fn(f32) -> f32; 2] {
     fs
 }
 
+fn call_by_ptr() {
+    let ptr: fn(u8, u8) -> u8 = std::intrinsics::wrapping_add::<u8>;
+    let res = ptr(u8::MAX, 1);
+    assert_eq!(res, 0);
+}
+
 fn main() {
     unsafe {
         assert_eq!(a()(-1), !0);
@@ -35,4 +41,6 @@ fn main() {
         assert_eq!(floorf32_ptr(1.5), 1.0);
         assert_eq!(log2f32_ptr(2.0), 1.0);
     }
+
+    call_by_ptr();
 }