Skip to content

Commit

Permalink
Allow reifying intrinsics to fn pointers.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb authored and GrigorenkoPV committed Jun 17, 2024
1 parent 3baa20b commit c9bb59a
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 62 deletions.
11 changes: 11 additions & 0 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
Expand Down Expand Up @@ -1979,6 +1980,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// and hence may contain unnormalized results.
let fn_sig = self.normalize(fn_sig, location);

// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`
// in `rustc_typeck::check::coercion`.
let fn_sig = fn_sig.map_bound(|mut sig| {
if matches!(sig.abi, Abi::RustIntrinsic) {
sig.abi = Abi::Rust;
}

sig
});

let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);

if let Err(terr) = self.eq_types(
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_hir_typeck/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ use rustc_middle::bug;
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::TyCtxt;
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef};
use rustc_session::lint;
Expand Down Expand Up @@ -696,9 +695,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);
}
Expand Down
31 changes: 22 additions & 9 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,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, Abi::RustIntrinsic) {
sig.abi = Abi::Rust;
}

sig
}

/// Coercing a mutable reference to an immutable works, while
/// coercing `&T` to `&mut T` should be forbidden.
fn coerce_mutbls<'tcx>(
Expand Down Expand Up @@ -843,12 +860,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
match b.kind() {
ty::FnPtr(b_sig) => {
let a_sig = a.fn_sig(self.tcx);
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
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);
}

// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).

if b_sig.safety() == hir::Safety::Safe
Expand Down Expand Up @@ -1150,10 +1164,9 @@ 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() == Abi::RustIntrinsic || b_sig.abi() == Abi::RustIntrinsic {
return Err(TypeError::IntrinsicCast);
}
// NOTE(eddyb) see comment on `prepare_fn_sig_for_reify`.
let a_sig = a_sig.map_bound(prepare_fn_sig_for_reify);
let b_sig = b_sig.map_bound(prepare_fn_sig_for_reify);
// The signature must match.
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
let sig = self
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2869,7 +2869,6 @@ impl<'tcx> ObligationCause<'tcx> {
{
FailureCode::Error0644
}
TypeError::IntrinsicCast => FailureCode::Error0308,
_ => FailureCode::Error0308,
},
}
Expand Down Expand Up @@ -2935,9 +2934,6 @@ impl<'tcx> ObligationCause<'tcx> {
{
ObligationCauseFailureCode::ClosureSelfref { span }
}
TypeError::IntrinsicCast => {
ObligationCauseFailureCode::CantCoerce { span, subdiags }
}
_ => ObligationCauseFailureCode::Generic { span, subdiags },
},
}
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ impl<'tcx> TypeError<'tcx> {
TypeError::ConstMismatch(ref values) => {
format!("expected `{}`, found `{}`", values.expected, values.found).into()
}
TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
TypeError::TargetFeatureCast(_) => {
"cannot coerce functions with `#[target_feature]` to safe function pointers".into()
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,10 @@ impl<'tcx> Instance<'tcx> {
// unresolved instance.
resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args }
}
InstanceDef::Intrinsic(def_id) => {
debug!(" => fn pointer created for intrinsic call");
resolved.def = InstanceDef::ReifyShim(def_id, reason);
}
_ => {}
}

Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_type_ir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ pub enum TypeError<I: Interner> {
ExistentialMismatch(ExpectedFound<I::BoundExistentialPredicates>),
ConstMismatch(ExpectedFound<I::Const>),

IntrinsicCast,
/// Safe `#[target_feature]` functions are not assignable to safe function pointers.
TargetFeatureCast(I::DefId),
}
Expand Down Expand Up @@ -93,8 +92,7 @@ impl<I: Interner> TypeError<I> {
| Traits(_)
| ProjectionMismatched(_)
| ExistentialMismatch(_)
| ConstMismatch(_)
| IntrinsicCast => true,
| ConstMismatch(_) => true,
}
}
}
Expand Down
38 changes: 27 additions & 11 deletions tests/ui/intrinsics/reify-intrinsic.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
//@ check-fail
//@ run-pass

#![feature(core_intrinsics, intrinsics)]

fn a() {
let _: unsafe extern "rust-intrinsic" 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 extern "rust-intrinsic" 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 extern "rust-intrinsic" fn(bool) -> bool; 2] = [
std::intrinsics::likely, //~ ERROR cannot coerce
#[inline(never)]
fn c() -> [fn(bool) -> bool; 2] {
let fs = [
std::intrinsics::likely,
std::intrinsics::unlikely,
];
fs
}

fn main() {}
fn main() {
unsafe {
assert_eq!(a()(-1), !0);
assert_eq!(b()(-1), !0);
}

let [likely_ptr, unlikely_ptr] = c();
assert!(likely_ptr(true));
assert!(unlikely_ptr(true));
}
30 changes: 0 additions & 30 deletions tests/ui/intrinsics/reify-intrinsic.stderr

This file was deleted.

31 changes: 31 additions & 0 deletions tests/ui/transmute/transmute-different-sizes-reified.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//@ normalize-stderr-test "\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() {}
30 changes: 30 additions & 0 deletions tests/ui/transmute/transmute-different-sizes-reified.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit c9bb59a

Please sign in to comment.