Skip to content

Commit 6123252

Browse files
eddybGrigorenkoPV
authored andcommitted
Allow reifying intrinsics to fn pointers.
1 parent 66701c4 commit 6123252

File tree

11 files changed

+126
-62
lines changed

11 files changed

+126
-62
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use rustc_span::source_map::Spanned;
4141
use rustc_span::symbol::sym;
4242
use rustc_span::{DUMMY_SP, Span};
4343
use rustc_target::abi::{FIRST_VARIANT, FieldIdx};
44+
use rustc_target::spec::abi::Abi;
4445
use rustc_trait_selection::traits::query::type_op::custom::{
4546
CustomTypeOp, scrape_region_constraints,
4647
};
@@ -2028,6 +2029,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
20282029
};
20292030
}
20302031

2032+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`
2033+
// in `rustc_typeck::check::coercion`.
2034+
let src_sig = src_sig.map_bound(|mut sig| -> _ {
2035+
if matches!(sig.abi, Abi::RustIntrinsic) {
2036+
sig.abi = Abi::Rust;
2037+
}
2038+
2039+
sig
2040+
});
2041+
20312042
let src_ty = Ty::new_fn_ptr(tcx, src_sig);
20322043
// HACK: We want to assert that the signature of the source fn is
20332044
// well-formed, because we don't enforce that via the WF of FnDef

compiler/rustc_hir_typeck/src/cast.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use rustc_macros::{TypeFoldable, TypeVisitable};
3737
use rustc_middle::mir::Mutability;
3838
use rustc_middle::ty::adjustment::AllowTwoPhase;
3939
use rustc_middle::ty::cast::{CastKind, CastTy};
40-
use rustc_middle::ty::error::TypeError;
4140
use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef};
4241
use rustc_middle::{bug, span_bug};
4342
use rustc_session::lint;
@@ -746,9 +745,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
746745
AllowTwoPhase::No,
747746
None,
748747
);
749-
if let Err(TypeError::IntrinsicCast) = res {
750-
return Err(CastError::IllegalCast);
751-
}
752748
if res.is_err() {
753749
return Err(CastError::NonScalar);
754750
}

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,23 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
9999

100100
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
101101

102+
/// Make any adjustments necessary for a function signature to be compatible
103+
/// with reification to a `fn` pointer. In particular, intrinsics are imported
104+
/// using pseudo-ABIs (`extern "rust-intrinsic" {...}`) currently, but that's
105+
/// an implementation detail and any `fn` pointers that may be taken to them
106+
/// should be indistinguishable from those to regular Rust functions, in order
107+
/// to allow e.g. libcore public APIs to be replaced with intrinsics, without
108+
/// breaking code that was, explicitly or implicitly, creating `fn` pointers.
109+
// FIXME(eddyb) intrinsics shouldn't use pseudo-ABIs, but rather the Rust ABI
110+
// and some other way to indicate that they are intrinsics (e.g. new attributes).
111+
fn prepare_fn_sig_for_reify<'tcx>(mut sig: ty::FnSig<'tcx>) -> ty::FnSig<'tcx> {
112+
if matches!(sig.abi, Abi::RustIntrinsic) {
113+
sig.abi = Abi::Rust;
114+
}
115+
116+
sig
117+
}
118+
102119
/// Coercing a mutable reference to an immutable works, while
103120
/// coercing `&T` to `&mut T` should be forbidden.
104121
fn coerce_mutbls<'tcx>(
@@ -915,12 +932,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
915932
match b.kind() {
916933
ty::FnPtr(_, b_hdr) => {
917934
let a_sig = a.fn_sig(self.tcx);
935+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
936+
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
918937
if let ty::FnDef(def_id, _) = *a.kind() {
919-
// Intrinsics are not coercible to function pointers
920-
if self.tcx.intrinsic(def_id).is_some() {
921-
return Err(TypeError::IntrinsicCast);
922-
}
923-
924938
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
925939

926940
if b_hdr.safety == hir::Safety::Safe
@@ -1246,10 +1260,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12461260
}
12471261
};
12481262
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
1249-
// Intrinsics are not coercible to function pointers.
1250-
if a_sig.abi() == Abi::RustIntrinsic || b_sig.abi() == Abi::RustIntrinsic {
1251-
return Err(TypeError::IntrinsicCast);
1252-
}
1263+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
1264+
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
1265+
let b_sig = b_sig.map_bound(prepare_fn_sig_for_reify);
12531266
// The signature must match.
12541267
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
12551268
let sig = self

compiler/rustc_middle/src/ty/error.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ impl<'tcx> TypeError<'tcx> {
110110
TypeError::ConstMismatch(ref values) => {
111111
format!("expected `{}`, found `{}`", values.expected, values.found).into()
112112
}
113-
TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
114113
TypeError::TargetFeatureCast(_) => {
115114
"cannot coerce functions with `#[target_feature]` to safe function pointers".into()
116115
}

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,10 @@ impl<'tcx> Instance<'tcx> {
639639
// unresolved instance.
640640
resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }
641641
}
642+
InstanceKind::Intrinsic(def_id) => {
643+
debug!(" => fn pointer created for intrinsic call");
644+
resolved.def = InstanceKind::ReifyShim(def_id, reason);
645+
}
642646
_ => {}
643647
}
644648

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,7 +2051,6 @@ impl<'tcx> ObligationCause<'tcx> {
20512051
{
20522052
FailureCode::Error0644
20532053
}
2054-
TypeError::IntrinsicCast => FailureCode::Error0308,
20552054
_ => FailureCode::Error0308,
20562055
},
20572056
}
@@ -2117,9 +2116,6 @@ impl<'tcx> ObligationCause<'tcx> {
21172116
{
21182117
ObligationCauseFailureCode::ClosureSelfref { span }
21192118
}
2120-
TypeError::IntrinsicCast => {
2121-
ObligationCauseFailureCode::CantCoerce { span, subdiags }
2122-
}
21232119
_ => ObligationCauseFailureCode::Generic { span, subdiags },
21242120
},
21252121
}

compiler/rustc_type_ir/src/error.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ pub enum TypeError<I: Interner> {
5454
ExistentialMismatch(ExpectedFound<I::BoundExistentialPredicates>),
5555
ConstMismatch(ExpectedFound<I::Const>),
5656

57-
IntrinsicCast,
5857
/// Safe `#[target_feature]` functions are not assignable to safe function pointers.
5958
TargetFeatureCast(I::DefId),
6059
}
@@ -86,8 +85,7 @@ impl<I: Interner> TypeError<I> {
8685
| Traits(_)
8786
| ProjectionMismatched(_)
8887
| ExistentialMismatch(_)
89-
| ConstMismatch(_)
90-
| IntrinsicCast => true,
88+
| ConstMismatch(_) => true,
9189
}
9290
}
9391
}
Lines changed: 27 additions & 11 deletions
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

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
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() {}
Lines changed: 30 additions & 0 deletions
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)