Skip to content

Commit 00db2ae

Browse files
eddybGrigorenkoPV
authored andcommitted
Allow reifying intrinsics to fn pointers.
1 parent 9dcaa7f commit 00db2ae

File tree

11 files changed

+126
-62
lines changed

11 files changed

+126
-62
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use rustc_span::symbol::sym;
4040
use rustc_span::Span;
4141
use rustc_span::DUMMY_SP;
4242
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
43+
use rustc_target::spec::abi::Abi;
4344
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
4445
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
4546
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -1994,6 +1995,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
19941995
// and hence may contain unnormalized results.
19951996
let fn_sig = self.normalize(fn_sig, location);
19961997

1998+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`
1999+
// in `rustc_typeck::check::coercion`.
2000+
let fn_sig = fn_sig.map_bound(|mut sig| {
2001+
if matches!(sig.abi, Abi::RustIntrinsic) {
2002+
sig.abi = Abi::Rust;
2003+
}
2004+
2005+
sig
2006+
});
2007+
19972008
let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
19982009

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

compiler/rustc_hir_typeck/src/cast.rs

-4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ use rustc_middle::bug;
4040
use rustc_middle::mir::Mutability;
4141
use rustc_middle::ty::adjustment::AllowTwoPhase;
4242
use rustc_middle::ty::cast::{CastKind, CastTy};
43-
use rustc_middle::ty::error::TypeError;
4443
use rustc_middle::ty::TyCtxt;
4544
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef};
4645
use rustc_session::lint;
@@ -692,9 +691,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
692691
AllowTwoPhase::No,
693692
None,
694693
);
695-
if let Err(TypeError::IntrinsicCast) = res {
696-
return Err(CastError::IllegalCast);
697-
}
698694
if res.is_err() {
699695
return Err(CastError::NonScalar);
700696
}

compiler/rustc_hir_typeck/src/coercion.rs

+22-9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,23 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
9191

9292
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
9393

94+
/// Make any adjustments necessary for a function signature to be compatible
95+
/// with reification to a `fn` pointer. In particular, intrinsics are imported
96+
/// using pseudo-ABIs (`extern "rust-intrinsic" {...}`) currently, but that's
97+
/// an implementation detail and any `fn` pointers that may be taken to them
98+
/// should be indistinguishable from those to regular Rust functions, in order
99+
/// to allow e.g. libcore public APIs to be replaced with intrinsics, without
100+
/// breaking code that was, explicitly or implicitly, creating `fn` pointers.
101+
// FIXME(eddyb) intrinsics shouldn't use pseudo-ABIs, but rather the Rust ABI
102+
// and some other way to indicate that they are intrinsics (e.g. new attributes).
103+
fn prepare_fn_sig_for_reify<'tcx>(mut sig: ty::FnSig<'tcx>) -> ty::FnSig<'tcx> {
104+
if matches!(sig.abi, Abi::RustIntrinsic) {
105+
sig.abi = Abi::Rust;
106+
}
107+
108+
sig
109+
}
110+
94111
/// Coercing a mutable reference to an immutable works, while
95112
/// coercing `&T` to `&mut T` should be forbidden.
96113
fn coerce_mutbls<'tcx>(
@@ -843,12 +860,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
843860
match b.kind() {
844861
ty::FnPtr(b_sig) => {
845862
let a_sig = a.fn_sig(self.tcx);
863+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
864+
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
846865
if let ty::FnDef(def_id, _) = *a.kind() {
847-
// Intrinsics are not coercible to function pointers
848-
if self.tcx.intrinsic(def_id).is_some() {
849-
return Err(TypeError::IntrinsicCast);
850-
}
851-
852866
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
853867

854868
if b_sig.safety() == hir::Safety::Safe
@@ -1150,10 +1164,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11501164
}
11511165
};
11521166
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
1153-
// Intrinsics are not coercible to function pointers.
1154-
if a_sig.abi() == Abi::RustIntrinsic || b_sig.abi() == Abi::RustIntrinsic {
1155-
return Err(TypeError::IntrinsicCast);
1156-
}
1167+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
1168+
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
1169+
let b_sig = b_sig.map_bound(prepare_fn_sig_for_reify);
11571170
// The signature must match.
11581171
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
11591172
let sig = self

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

-4
Original file line numberDiff line numberDiff line change
@@ -2039,7 +2039,6 @@ impl<'tcx> ObligationCause<'tcx> {
20392039
{
20402040
FailureCode::Error0644
20412041
}
2042-
TypeError::IntrinsicCast => FailureCode::Error0308,
20432042
_ => FailureCode::Error0308,
20442043
},
20452044
}
@@ -2105,9 +2104,6 @@ impl<'tcx> ObligationCause<'tcx> {
21052104
{
21062105
ObligationCauseFailureCode::ClosureSelfref { span }
21072106
}
2108-
TypeError::IntrinsicCast => {
2109-
ObligationCauseFailureCode::CantCoerce { span, subdiags }
2110-
}
21112107
_ => ObligationCauseFailureCode::Generic { span, subdiags },
21122108
},
21132109
}

compiler/rustc_middle/src/ty/error.rs

-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ impl<'tcx> TypeError<'tcx> {
113113
TypeError::ConstMismatch(ref values) => {
114114
format!("expected `{}`, found `{}`", values.expected, values.found).into()
115115
}
116-
TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
117116
TypeError::TargetFeatureCast(_) => {
118117
"cannot coerce functions with `#[target_feature]` to safe function pointers".into()
119118
}

compiler/rustc_middle/src/ty/instance.rs

+4
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,10 @@ impl<'tcx> Instance<'tcx> {
649649
// unresolved instance.
650650
resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }
651651
}
652+
InstanceKind::Intrinsic(def_id) => {
653+
debug!(" => fn pointer created for intrinsic call");
654+
resolved.def = InstanceKind::ReifyShim(def_id, reason);
655+
}
652656
_ => {}
653657
}
654658

compiler/rustc_type_ir/src/error.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ pub enum TypeError<I: Interner> {
6161
ExistentialMismatch(ExpectedFound<I::BoundExistentialPredicates>),
6262
ConstMismatch(ExpectedFound<I::Const>),
6363

64-
IntrinsicCast,
6564
/// Safe `#[target_feature]` functions are not assignable to safe function pointers.
6665
TargetFeatureCast(I::DefId),
6766
}
@@ -93,8 +92,7 @@ impl<I: Interner> TypeError<I> {
9392
| Traits(_)
9493
| ProjectionMismatched(_)
9594
| ExistentialMismatch(_)
96-
| ConstMismatch(_)
97-
| IntrinsicCast => true,
95+
| ConstMismatch(_) => true,
9896
}
9997
}
10098
}
+27-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +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 _: [unsafe extern "rust-intrinsic" fn(bool) -> bool; 2] = [
17-
std::intrinsics::likely, //~ ERROR cannot coerce
20+
#[inline(never)]
21+
fn c() -> [fn(bool) -> bool; 2] {
22+
let fs = [
23+
std::intrinsics::likely,
1824
std::intrinsics::unlikely,
1925
];
26+
fs
2027
}
2128

22-
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+
}

tests/ui/intrinsics/reify-intrinsic.stderr

-30
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)