Skip to content

Commit

Permalink
Coerce safe-to-call target_feature functions to fn pointers.
Browse files Browse the repository at this point in the history
  • Loading branch information
veluca93 committed Jan 15, 2025
1 parent 341f603 commit ea653b8
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 13 deletions.
29 changes: 18 additions & 11 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {

match b.kind() {
ty::FnPtr(_, b_hdr) => {
let a_sig = a.fn_sig(self.tcx);
let mut a_sig = a.fn_sig(self.tcx);
if let ty::FnDef(def_id, _) = *a.kind() {
// Intrinsics are not coercible to function pointers
if self.tcx.intrinsic(def_id).is_some() {
Expand All @@ -932,19 +932,26 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
return Err(TypeError::ForceInlineCast);
}

let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
return Err(TypeError::ForceInlineCast);
}

// FIXME(target_feature): Safe `#[target_feature]` functions could be cast to safe fn pointers (RFC 2396),
// as you can already write that "cast" in user code by wrapping a target_feature fn call in a closure,
// which is safe. This is sound because you already need to be executing code that is satisfying the target
// feature constraints..
if b_hdr.safety.is_safe()
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
{
return Err(TypeError::TargetFeatureCast(def_id));
// Allow the coercion if the current function has all the features that would be
// needed to call the coercee safely.
let coercee_features = &self.tcx.codegen_fn_attrs(def_id).target_features;
let body_features =
&self.tcx.codegen_fn_attrs(self.fcx.body_id).target_features;
if !self.tcx.sess.target.options.is_like_wasm
&& !coercee_features
.iter()
.all(|feature| body_features.iter().any(|f| f.name == feature.name))
{
return Err(TypeError::TargetFeatureCast(def_id));
} else {
// The coercee behaves like a safe function, since it is a target_feature
// function that would be callable safely in this context.
a_sig = a_sig
.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig })
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@

#![feature(target_feature_11)]

#[target_feature(enable = "avx")]
fn foo_avx() {}

#[target_feature(enable = "sse2")]
fn foo() {}

#[target_feature(enable = "sse2")]
fn bar() {
let foo: fn() = foo; // this is OK, as we have the necessary target features.
let foo: fn() = foo_avx; //~ ERROR mismatched types
}

fn main() {
if std::is_x86_feature_detected!("sse2") {
unsafe {
bar();
}
}
let foo: fn() = foo; //~ ERROR mismatched types
}
19 changes: 17 additions & 2 deletions tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
error[E0308]: mismatched types
--> $DIR/fn-ptr.rs:9:21
--> $DIR/fn-ptr.rs:14:21
|
LL | #[target_feature(enable = "avx")]
| --------------------------------- `#[target_feature]` added here
...
LL | let foo: fn() = foo_avx;
| ---- ^^^^^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
| |
| expected due to this
|
= note: expected fn pointer `fn()`
found fn item `#[target_features] fn() {foo_avx}`
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers

error[E0308]: mismatched types
--> $DIR/fn-ptr.rs:23:21
|
LL | #[target_feature(enable = "sse2")]
| ---------------------------------- `#[target_feature]` added here
Expand All @@ -13,6 +28,6 @@ LL | let foo: fn() = foo;
found fn item `#[target_features] fn() {foo}`
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers

error: aborting due to 1 previous error
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
22 changes: 22 additions & 0 deletions tests/ui/rfcs/rfc-2396-target_feature-11/return-fn-ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ only-x86_64
//@ run-pass

#![feature(target_feature_11)]

#[target_feature(enable = "sse2")]
fn foo() -> bool {
true
}

#[target_feature(enable = "sse2")]
fn bar() -> fn() -> bool {
foo
}

fn main() {
if !std::is_x86_feature_detected!("sse2") {
return;
}
let f = unsafe { bar() };
assert!(f());
}

0 comments on commit ea653b8

Please sign in to comment.