Skip to content

Commit 0e10c39

Browse files
eddybGrigorenkoPV
authored andcommitted
Allow reifying intrinsics to fn pointers.
1 parent 124cc92 commit 0e10c39

File tree

11 files changed

+127
-64
lines changed

11 files changed

+127
-64
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::rc::Rc;
44
use std::{fmt, iter, mem};
55

6-
use rustc_abi::FieldIdx;
6+
use rustc_abi::{ExternAbi, FieldIdx};
77
use rustc_data_structures::frozen::Frozen;
88
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
99
use rustc_errors::ErrorGuaranteed;
@@ -39,7 +39,8 @@ use rustc_mir_dataflow::move_paths::MoveData;
3939
use rustc_mir_dataflow::points::DenseLocationMap;
4040
use rustc_span::def_id::CRATE_DEF_ID;
4141
use rustc_span::source_map::Spanned;
42-
use rustc_span::{DUMMY_SP, Span, sym};
42+
use rustc_span::symbol::sym;
43+
use rustc_span::{DUMMY_SP, Span};
4344
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
4445
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
4546
use tracing::{debug, instrument, trace};
@@ -1743,6 +1744,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
17431744
};
17441745
}
17451746

1747+
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`
1748+
// in `rustc_typeck::check::coercion`.
1749+
let src_sig = src_sig.map_bound(|mut sig| -> _ {
1750+
if matches!(sig.abi, ExternAbi::RustIntrinsic) {
1751+
sig.abi = ExternAbi::Rust;
1752+
}
1753+
1754+
sig
1755+
});
1756+
17461757
let src_ty = Ty::new_fn_ptr(tcx, src_sig);
17471758
// HACK: We want to assert that the signature of the source fn is
17481759
// 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
@@ -38,7 +38,6 @@ use rustc_macros::{TypeFoldable, TypeVisitable};
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::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef};
4342
use rustc_middle::{bug, span_bug};
4443
use rustc_session::lint;
@@ -748,9 +747,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
748747
AllowTwoPhase::No,
749748
None,
750749
);
751-
if let Err(TypeError::IntrinsicCast) = res {
752-
return Err(CastError::IllegalCast);
753-
}
754750
if res.is_err() {
755751
return Err(CastError::NonScalar);
756752
}

compiler/rustc_hir_typeck/src/coercion.rs

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

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

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

934+
if let ty::FnDef(def_id, _) = *a.kind() {
920935
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
921936
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
922937
return Err(TypeError::ForceInlineCast);
@@ -1266,10 +1281,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12661281
}
12671282
};
12681283
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
1269-
// Intrinsics are not coercible to function pointers.
1270-
if a_sig.abi() == ExternAbi::RustIntrinsic || b_sig.abi() == ExternAbi::RustIntrinsic {
1271-
return Err(TypeError::IntrinsicCast);
1272-
}
12731284
// The signature must match.
12741285
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
12751286
let sig = self

compiler/rustc_middle/src/ty/error.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ impl<'tcx> TypeError<'tcx> {
112112
TypeError::ForceInlineCast => {
113113
"cannot coerce functions which must be inlined to function pointers".into()
114114
}
115-
TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
116115
TypeError::TargetFeatureCast(_) => {
117116
"cannot coerce functions with `#[target_feature]` to safe function pointers".into()
118117
}

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,10 @@ impl<'tcx> Instance<'tcx> {
656656
// unresolved instance.
657657
resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }
658658
}
659+
InstanceKind::Intrinsic(def_id) => {
660+
debug!(" => fn pointer created for intrinsic call");
661+
resolved.def = InstanceKind::ReifyShim(def_id, reason);
662+
}
659663
_ => {}
660664
}
661665

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,7 +2321,7 @@ impl<'tcx> ObligationCause<'tcx> {
23212321
{
23222322
FailureCode::Error0644
23232323
}
2324-
TypeError::IntrinsicCast | TypeError::ForceInlineCast => FailureCode::Error0308,
2324+
TypeError::ForceInlineCast => FailureCode::Error0308,
23252325
_ => FailureCode::Error0308,
23262326
},
23272327
}
@@ -2387,9 +2387,6 @@ impl<'tcx> ObligationCause<'tcx> {
23872387
TypeError::ForceInlineCast => {
23882388
ObligationCauseFailureCode::CantCoerceForceInline { span, subdiags }
23892389
}
2390-
TypeError::IntrinsicCast => {
2391-
ObligationCauseFailureCode::CantCoerceIntrinsic { span, subdiags }
2392-
}
23932390
_ => ObligationCauseFailureCode::Generic { span, subdiags },
23942391
},
23952392
}

compiler/rustc_type_ir/src/error.rs

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

53-
IntrinsicCast,
5453
/// `#[rustc_force_inline]` functions must be inlined and must not be codegened independently,
5554
/// so casting to a function pointer must be prohibited.
5655
ForceInlineCast,
@@ -86,8 +85,7 @@ impl<I: Interner> TypeError<I> {
8685
| ProjectionMismatched(_)
8786
| ExistentialMismatch(_)
8887
| ConstMismatch(_)
89-
| ForceInlineCast
90-
| IntrinsicCast => true,
88+
| ForceInlineCast => 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 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 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 fn(f32) -> f32; 2] = [
17-
std::intrinsics::floorf32, //~ ERROR cannot coerce
20+
#[inline(never)]
21+
fn c() -> [unsafe fn(f32) -> f32; 2] {
22+
let fs = [
23+
std::intrinsics::floorf32,
1824
std::intrinsics::log2f32,
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+
let [floorf32_ptr, log2f32_ptr] = c();
35+
assert_eq!(floorf32_ptr(1.5), 1.0);
36+
assert_eq!(log2f32_ptr(2.0), 1.0);
37+
}
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: "\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)