Skip to content

Commit c82cf47

Browse files
committed
Allow reifying intrinsics to fn pointers.
1 parent 910692d commit c82cf47

File tree

11 files changed

+127
-68
lines changed

11 files changed

+127
-68
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use rustc_middle::ty::{
3535
use rustc_span::def_id::CRATE_DEF_ID;
3636
use rustc_span::{Span, DUMMY_SP};
3737
use rustc_target::abi::VariantIdx;
38+
use rustc_target::spec::abi::Abi;
3839
use rustc_trait_selection::infer::InferCtxtExt as _;
3940
use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt};
4041
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
@@ -323,6 +324,7 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
323324
}
324325
}
325326

327+
#[track_caller]
326328
fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) {
327329
// We sometimes see MIR failures (notably predicate failures) due to
328330
// the fact that we check rvalue sized predicates here. So use `delay_span_bug`
@@ -2054,6 +2056,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
20542056
// and hence may contain unnormalized results.
20552057
let fn_sig = self.normalize(fn_sig, location);
20562058

2059+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`
2060+
// in `rustc_typeck::check::coercion`.
2061+
let fn_sig = fn_sig.map_bound(|mut sig| {
2062+
if matches!(sig.abi, Abi::RustIntrinsic | Abi::PlatformIntrinsic) {
2063+
sig.abi = Abi::Rust;
2064+
}
2065+
2066+
sig
2067+
});
2068+
20572069
let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
20582070

20592071
if let Err(terr) = self.eq_types(

compiler/rustc_infer/src/infer/error_reporting/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -2628,9 +2628,6 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
26282628
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
26292629
Error0644("closure/generator type that references itself")
26302630
}
2631-
TypeError::IntrinsicCast => {
2632-
Error0308("cannot coerce intrinsics to function pointers")
2633-
}
26342631
TypeError::ObjectUnsafeCoercion(did) => Error0038(*did),
26352632
_ => Error0308("mismatched types"),
26362633
},

compiler/rustc_middle/src/ty/error.rs

-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ pub enum TypeError<'tcx> {
6666
ObjectUnsafeCoercion(DefId),
6767
ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>),
6868

69-
IntrinsicCast,
7069
/// Safe `#[target_feature]` functions are not assignable to safe function pointers.
7170
TargetFeatureCast(DefId),
7271
}
@@ -197,7 +196,6 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
197196
ConstMismatch(ref values) => {
198197
write!(f, "expected `{}`, found `{}`", values.expected, values.found)
199198
}
200-
IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"),
201199
TargetFeatureCast(_) => write!(
202200
f,
203201
"cannot coerce functions with `#[target_feature]` to safe function pointers"
@@ -229,7 +227,6 @@ impl<'tcx> TypeError<'tcx> {
229227
| ProjectionMismatched(_)
230228
| ExistentialMismatch(_)
231229
| ConstMismatch(_)
232-
| IntrinsicCast
233230
| ObjectUnsafeCoercion(_) => true,
234231
}
235232
}

compiler/rustc_middle/src/ty/instance.rs

+4
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ impl<'tcx> Instance<'tcx> {
394394
debug!(" => fn pointer created for virtual call");
395395
resolved.def = InstanceDef::ReifyShim(def_id);
396396
}
397+
InstanceDef::Intrinsic(def_id) => {
398+
debug!(" => fn pointer created for intrinsic call");
399+
resolved.def = InstanceDef::ReifyShim(def_id);
400+
}
397401
_ => {}
398402
}
399403

compiler/rustc_middle/src/ty/structural_impls.rs

-1
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
619619
Sorts(x) => return tcx.lift(x).map(Sorts),
620620
ExistentialMismatch(x) => return tcx.lift(x).map(ExistentialMismatch),
621621
ConstMismatch(x) => return tcx.lift(x).map(ConstMismatch),
622-
IntrinsicCast => IntrinsicCast,
623622
TargetFeatureCast(x) => TargetFeatureCast(x),
624623
ObjectUnsafeCoercion(x) => return tcx.lift(x).map(ObjectUnsafeCoercion),
625624
})

compiler/rustc_typeck/src/check/cast.rs

-4
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ use rustc_hir::lang_items::LangItem;
3838
use rustc_middle::mir::Mutability;
3939
use rustc_middle::ty::adjustment::AllowTwoPhase;
4040
use rustc_middle::ty::cast::{CastKind, CastTy};
41-
use rustc_middle::ty::error::TypeError;
4241
use rustc_middle::ty::subst::SubstsRef;
4342
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
4443
use rustc_session::lint;
@@ -700,9 +699,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
700699
AllowTwoPhase::No,
701700
None,
702701
);
703-
if let Err(TypeError::IntrinsicCast) = res {
704-
return Err(CastError::IllegalCast);
705-
}
706702
if res.is_err() {
707703
return Err(CastError::NonScalar);
708704
}

compiler/rustc_typeck/src/check/coercion.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
8484

8585
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
8686

87+
/// Make any adjustments necessary for a function signature to be compatible
88+
/// with reification to a `fn` pointer. In particular, intrinsics are imported
89+
/// using pseudo-ABIs (`extern "rust-intrinsic" {...}`) currently, but that's
90+
/// an implementation detail and any `fn` pointers that may be taken to them
91+
/// should be indistinguishable from those to regular Rust functions, in order
92+
/// to allow e.g. libcore public APIs to be replaced with intrinsics, without
93+
/// breaking code that was, explicitly or implicitly, creating `fn` pointers.
94+
// FIXME(eddyb) intrinsics shouldn't use pseudo-ABIs, but rather the Rust ABI
95+
// and some other way to indicate that they are intrinsics (e.g. new attributes).
96+
fn prepare_fn_sig_for_reify(mut sig: ty::FnSig<'tcx>) -> ty::FnSig<'tcx> {
97+
if matches!(sig.abi, Abi::RustIntrinsic | Abi::PlatformIntrinsic) {
98+
sig.abi = Abi::Rust;
99+
}
100+
101+
sig
102+
}
103+
87104
/// Coercing a mutable reference to an immutable works, while
88105
/// coercing `&T` to `&mut T` should be forbidden.
89106
fn coerce_mutbls<'tcx>(
@@ -779,10 +796,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
779796
match b.kind() {
780797
ty::FnPtr(b_sig) => {
781798
let a_sig = a.fn_sig(self.tcx);
782-
// Intrinsics are not coercible to function pointers
783-
if a_sig.abi() == Abi::RustIntrinsic || a_sig.abi() == Abi::PlatformIntrinsic {
784-
return Err(TypeError::IntrinsicCast);
785-
}
799+
800+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
801+
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
786802

787803
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
788804
if let ty::FnDef(def_id, _) = *a.kind() {
@@ -1043,14 +1059,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10431059
}
10441060
};
10451061
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
1046-
// Intrinsics are not coercible to function pointers.
1047-
if a_sig.abi() == Abi::RustIntrinsic
1048-
|| a_sig.abi() == Abi::PlatformIntrinsic
1049-
|| b_sig.abi() == Abi::RustIntrinsic
1050-
|| b_sig.abi() == Abi::PlatformIntrinsic
1051-
{
1052-
return Err(TypeError::IntrinsicCast);
1053-
}
1062+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
1063+
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
1064+
let b_sig = b_sig.map_bound(prepare_fn_sig_for_reify);
1065+
10541066
// The signature must match.
10551067
let a_sig = self.normalize_associated_types_in(new.span, a_sig);
10561068
let b_sig = self.normalize_associated_types_in(new.span, b_sig);

src/test/ui/reify-intrinsic.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,38 @@
1-
// check-fail
1+
// run-pass
22

33
#![feature(core_intrinsics, intrinsics)]
44

5-
fn a() {
6-
let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute;
7-
//~^ ERROR cannot coerce
5+
// NOTE(eddyb) `#[inline(never)]` and returning `fn` pointers from functions is
6+
// done to force codegen (of the reification-to-`fn`-ptr shims around intrinsics).
7+
8+
#[inline(never)]
9+
fn a() -> unsafe fn(isize) -> usize {
10+
let f: unsafe fn(isize) -> usize = std::mem::transmute;
11+
f
812
}
913

10-
fn b() {
11-
let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;
12-
//~^ ERROR casting
14+
#[inline(never)]
15+
fn b() -> unsafe fn(isize) -> usize {
16+
let f = std::mem::transmute as unsafe fn(isize) -> usize;
17+
f
1318
}
1419

15-
fn c() {
16-
let _ = [
20+
#[inline(never)]
21+
fn c() -> [fn(bool) -> bool; 2] {
22+
let fs = [
1723
std::intrinsics::likely,
1824
std::intrinsics::unlikely,
19-
//~^ ERROR cannot coerce
2025
];
26+
fs
2127
}
2228

23-
fn main() {}
29+
fn main() {
30+
unsafe {
31+
assert_eq!(a()(-1), !0);
32+
assert_eq!(b()(-1), !0);
33+
}
34+
35+
let [likely_ptr, unlikely_ptr] = c();
36+
assert!(likely_ptr(true));
37+
assert!(unlikely_ptr(true));
38+
}

src/test/ui/reify-intrinsic.stderr

-34
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// normalize-stderr-test "\d+ bits" -> "N bits"
2+
3+
// Tests that `transmute` cannot be indirectly called on types of different size.
4+
5+
#![allow(warnings)]
6+
#![feature(specialization)]
7+
8+
use std::mem::transmute;
9+
10+
unsafe fn f() {
11+
let _: i8 = (transmute as unsafe fn(_) -> _)(16i16);
12+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
13+
}
14+
15+
unsafe fn g<T>(x: &T) {
16+
let _: i8 = (transmute as unsafe fn(_) -> _)(x);
17+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
18+
}
19+
20+
trait Specializable { type Output; }
21+
22+
impl<T> Specializable for T {
23+
default type Output = u16;
24+
}
25+
26+
unsafe fn specializable<T>(x: u16) -> <T as Specializable>::Output {
27+
(transmute as unsafe fn(_) -> _)(x)
28+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
29+
}
30+
31+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
2+
--> $DIR/transmute-different-sizes-reified.rs:11:18
3+
|
4+
LL | let _: i8 = (transmute as unsafe fn(_) -> _)(16i16);
5+
| ^^^^^^^^^
6+
|
7+
= note: source type: `i16` (N bits)
8+
= note: target type: `i8` (N bits)
9+
10+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
11+
--> $DIR/transmute-different-sizes-reified.rs:16:18
12+
|
13+
LL | let _: i8 = (transmute as unsafe fn(_) -> _)(x);
14+
| ^^^^^^^^^
15+
|
16+
= note: source type: `&T` (N bits)
17+
= note: target type: `i8` (N bits)
18+
19+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
20+
--> $DIR/transmute-different-sizes-reified.rs:27:6
21+
|
22+
LL | (transmute as unsafe fn(_) -> _)(x)
23+
| ^^^^^^^^^
24+
|
25+
= note: source type: `u16` (N bits)
26+
= note: target type: `<T as Specializable>::Output` (this type does not have a fixed size)
27+
28+
error: aborting due to 3 previous errors
29+
30+
For more information about this error, try `rustc --explain E0512`.

0 commit comments

Comments
 (0)