Skip to content

Commit ea653b8

Browse files
committed
Coerce safe-to-call target_feature functions to fn pointers.
1 parent 341f603 commit ea653b8

File tree

4 files changed

+71
-13
lines changed

4 files changed

+71
-13
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
920920

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

935-
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
936-
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
937-
return Err(TypeError::ForceInlineCast);
938-
}
939-
940-
// FIXME(target_feature): Safe `#[target_feature]` functions could be cast to safe fn pointers (RFC 2396),
941-
// as you can already write that "cast" in user code by wrapping a target_feature fn call in a closure,
942-
// which is safe. This is sound because you already need to be executing code that is satisfying the target
943-
// feature constraints..
944935
if b_hdr.safety.is_safe()
945936
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
946937
{
947-
return Err(TypeError::TargetFeatureCast(def_id));
938+
// Allow the coercion if the current function has all the features that would be
939+
// needed to call the coercee safely.
940+
let coercee_features = &self.tcx.codegen_fn_attrs(def_id).target_features;
941+
let body_features =
942+
&self.tcx.codegen_fn_attrs(self.fcx.body_id).target_features;
943+
if !self.tcx.sess.target.options.is_like_wasm
944+
&& !coercee_features
945+
.iter()
946+
.all(|feature| body_features.iter().any(|f| f.name == feature.name))
947+
{
948+
return Err(TypeError::TargetFeatureCast(def_id));
949+
} else {
950+
// The coercee behaves like a safe function, since it is a target_feature
951+
// function that would be callable safely in this context.
952+
a_sig = a_sig
953+
.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig })
954+
}
948955
}
949956
}
950957

tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs

+14
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,23 @@
22

33
#![feature(target_feature_11)]
44

5+
#[target_feature(enable = "avx")]
6+
fn foo_avx() {}
7+
58
#[target_feature(enable = "sse2")]
69
fn foo() {}
710

11+
#[target_feature(enable = "sse2")]
12+
fn bar() {
13+
let foo: fn() = foo; // this is OK, as we have the necessary target features.
14+
let foo: fn() = foo_avx; //~ ERROR mismatched types
15+
}
16+
817
fn main() {
18+
if std::is_x86_feature_detected!("sse2") {
19+
unsafe {
20+
bar();
21+
}
22+
}
923
let foo: fn() = foo; //~ ERROR mismatched types
1024
}

tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
error[E0308]: mismatched types
2-
--> $DIR/fn-ptr.rs:9:21
2+
--> $DIR/fn-ptr.rs:14:21
3+
|
4+
LL | #[target_feature(enable = "avx")]
5+
| --------------------------------- `#[target_feature]` added here
6+
...
7+
LL | let foo: fn() = foo_avx;
8+
| ---- ^^^^^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
9+
| |
10+
| expected due to this
11+
|
12+
= note: expected fn pointer `fn()`
13+
found fn item `#[target_features] fn() {foo_avx}`
14+
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/fn-ptr.rs:23:21
318
|
419
LL | #[target_feature(enable = "sse2")]
520
| ---------------------------------- `#[target_feature]` added here
@@ -13,6 +28,6 @@ LL | let foo: fn() = foo;
1328
found fn item `#[target_features] fn() {foo}`
1429
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
1530

16-
error: aborting due to 1 previous error
31+
error: aborting due to 2 previous errors
1732

1833
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@ only-x86_64
2+
//@ run-pass
3+
4+
#![feature(target_feature_11)]
5+
6+
#[target_feature(enable = "sse2")]
7+
fn foo() -> bool {
8+
true
9+
}
10+
11+
#[target_feature(enable = "sse2")]
12+
fn bar() -> fn() -> bool {
13+
foo
14+
}
15+
16+
fn main() {
17+
if !std::is_x86_feature_detected!("sse2") {
18+
return;
19+
}
20+
let f = unsafe { bar() };
21+
assert!(f());
22+
}

0 commit comments

Comments
 (0)