Skip to content

Commit fb5980b

Browse files
committed
Coerce safe-to-call target_feature functions to fn pointers.
1 parent b1deae5 commit fb5980b

File tree

3 files changed

+54
-12
lines changed

3 files changed

+54
-12
lines changed

Diff for: compiler/rustc_hir_typeck/src/coercion.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
921921
match b.kind() {
922922
ty::FnPtr(_, b_hdr) => {
923923
let a_sig = a.fn_sig(self.tcx);
924+
let mut allow_unsafe_to_safe_coercion = false;
924925
if let ty::FnDef(def_id, _) = *a.kind() {
925926
// Intrinsics are not coercible to function pointers
926927
if self.tcx.intrinsic(def_id).is_some() {
@@ -932,26 +933,38 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
932933
return Err(TypeError::ForceInlineCast);
933934
}
934935

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..
944936
if b_hdr.safety.is_safe()
945937
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
946938
{
947-
return Err(TypeError::TargetFeatureCast(def_id));
939+
// Allow the coercion if the current function has all the features that would be
940+
// needed to call the coercee safely.
941+
let coercee_features = &self.tcx.codegen_fn_attrs(def_id).target_features;
942+
let body_features =
943+
&self.tcx.codegen_fn_attrs(self.fcx.body_id).target_features;
944+
if !self.tcx.sess.target.options.is_like_wasm
945+
&& !coercee_features
946+
.iter()
947+
.all(|feature| body_features.iter().any(|f| f.name == feature.name))
948+
{
949+
return Err(TypeError::TargetFeatureCast(def_id));
950+
} else {
951+
allow_unsafe_to_safe_coercion = true;
952+
}
948953
}
949954
}
950955

951956
let InferOk { value: a_sig, obligations: o1 } =
952957
self.at(&self.cause, self.param_env).normalize(a_sig);
953958
obligations.extend(o1);
954959

960+
let a_sig = if allow_unsafe_to_safe_coercion {
961+
// The coercee behaves like a safe function, since it is a target_feature
962+
// function that would be callable safely in this context.
963+
a_sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig })
964+
} else {
965+
a_sig
966+
};
967+
955968
let a_fn_pointer = Ty::new_fn_ptr(self.tcx, a_sig);
956969
let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn(
957970
a_fn_pointer,

Diff for: 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
}

Diff for: 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`.

0 commit comments

Comments
 (0)