Skip to content

Commit

Permalink
Auto merge of rust-lang#99767 - LeSeulArtichaut:stable-target-feature…
Browse files Browse the repository at this point in the history
…-11, r=estebank

Stabilize `#![feature(target_feature_11)]`

## Stabilization report

### Summary

Allows for safe functions to be marked with `#[target_feature]` attributes.

Functions marked with `#[target_feature]` are generally considered as unsafe functions: they are unsafe to call, cannot be assigned to safe function pointers, and don't implement the `Fn*` traits.

However, calling them from other `#[target_feature]` functions with a superset of features is safe.

```rust
// Demonstration function
#[target_feature(enable = "avx2")]
fn avx2() {}

fn foo() {
    // Calling `avx2` here is unsafe, as we must ensure
    // that AVX is available first.
    unsafe {
        avx2();
    }
}

#[target_feature(enable = "avx2")]
fn bar() {
    // Calling `avx2` here is safe.
    avx2();
}
```

### Test cases

Tests for this feature can be found in [`src/test/ui/rfcs/rfc-2396-target_feature-11/`](https://github.com/rust-lang/rust/tree/b67ba9ba208ac918228a18321fc3a11a99b1c62b/src/test/ui/rfcs/rfc-2396-target_feature-11/).

### Edge cases

- rust-lang#73631

Closures defined inside functions marked with `#[target_feature]` inherit the target features of their parent function. They can still be assigned to safe function pointers and implement the appropriate `Fn*` traits.

```rust
#[target_feature(enable = "avx2")]
fn qux() {
    let my_closure = || avx2(); // this call to `avx2` is safe
    let f: fn() = my_closure;
}
```

This means that in order to call a function with `#[target_feature]`, you must show that the target-feature is available while the function executes *and* for as long as whatever may escape from that function lives.

### Documentation

- Reference: rust-lang/reference#1181

---
cc tracking issue rust-lang#69098
r? `@ghost`
  • Loading branch information
bors committed Feb 28, 2023
2 parents 6290ae9 + b379d21 commit b583ede
Show file tree
Hide file tree
Showing 28 changed files with 78 additions and 151 deletions.
47 changes: 18 additions & 29 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,34 +224,23 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
if !tcx.is_closure(did.to_def_id())
&& tcx.fn_sig(did).skip_binder().unsafety() == hir::Unsafety::Normal
{
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
// The `#[target_feature]` attribute is allowed on
// WebAssembly targets on all functions, including safe
// ones. Other targets require that `#[target_feature]` is
// only applied to unsafe functions (pending the
// `target_feature_11` feature) because on most targets
// execution of instructions that are not supported is
// considered undefined behavior. For WebAssembly which is a
// 100% safe target at execution time it's not possible to
// execute undefined instructions, and even if a future
// feature was added in some form for this it would be a
// deterministic trap. There is no undefined behavior when
// executing WebAssembly so `#[target_feature]` is allowed
// on safe functions (but again, only for WebAssembly)
//
// Note that this is also allowed if `actually_rustdoc` so
// if a target is documenting some wasm-specific code then
// it's not spuriously denied.
} else if !tcx.features().target_feature_11 {
let mut err = feature_err(
&tcx.sess.parse_sess,
sym::target_feature_11,
attr.span,
"`#[target_feature(..)]` can only be applied to `unsafe` functions",
);
err.span_label(tcx.def_span(did), "not an `unsafe` function");
err.emit();
} else {
// The `#[target_feature]` attribute is allowed on
// WebAssembly targets on all functions, including safe
// ones. Other targets have conditions on the usage of
// `#[target_feature]` because on most targets
// execution of instructions that are not supported is
// considered undefined behavior. For WebAssembly which is a
// 100% safe target at execution time it's not possible to
// execute undefined instructions, and even if a future
// feature was added in some form for this it would be a
// deterministic trap. There is no undefined behavior when
// executing WebAssembly so `#[target_feature]` is allowed
// on safe functions (but again, only for WebAssembly)
//
// Note that this is also allowed if `actually_rustdoc` so
// if a target is documenting some wasm-specific code then
// it's not spuriously denied.
if !(tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc) {
check_target_feature_trait_unsafe(tcx, did, attr.span);
}
}
Expand Down Expand Up @@ -478,7 +467,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
});

// #73631: closures inherit `#[target_feature]` annotations
if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) {
if tcx.is_closure(did.to_def_id()) {
let owner_id = tcx.parent(did.to_def_id());
if tcx.def_kind(owner_id).has_codegen_attrs() {
codegen_fn_attrs
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ declare_features! (
(accepted, struct_variant, "1.0.0", None, None),
/// Allows `#[target_feature(...)]`.
(accepted, target_feature, "1.27.0", None, None),
/// Allows the use of `#[target_feature]` on safe functions.
(accepted, target_feature_11, "CURRENT_RUSTC_VERSION", Some(69098), None),
/// Allows `fn main()` with return types which implements `Termination` (RFC 1937).
(accepted, termination_trait, "1.26.0", Some(43301), None),
/// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937).
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,6 @@ declare_features! (
(active, strict_provenance, "1.61.0", Some(95228), None),
/// Allows string patterns to dereference values to match them.
(active, string_deref_patterns, "1.67.0", Some(87121), None),
/// Allows the use of `#[target_feature]` on safe functions.
(active, target_feature_11, "1.45.0", Some(69098), None),
/// Allows using `#[thread_local]` on `static` items.
(active, thread_local, "1.0.0", Some(29594), None),
/// Allows defining `trait X = A + B;` alias items.
Expand Down
1 change: 0 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@
#![feature(simd_ffi)]
#![feature(staged_api)]
#![feature(stmt_expr_attributes)]
#![feature(target_feature_11)]
#![feature(trait_alias)]
#![feature(transparent_unions)]
#![feature(try_blocks)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
fn inlined_no_sanitize() -> () {
let mut _0: (); // return place in scope 0 at $DIR/inline_compatibility.rs:+0:37: +0:37
let _1: (); // in scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:18
+ scope 1 (inlined no_sanitize) { // at $DIR/inline_compatibility.rs:24:5: 24:18
+ scope 1 (inlined no_sanitize) { // at $DIR/inline_compatibility.rs:23:5: 23:18
+ }

bb0: {
StorageLive(_1); // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:18
- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:18
- // mir::Constant
- // + span: $DIR/inline_compatibility.rs:24:5: 24:16
- // + span: $DIR/inline_compatibility.rs:23:5: 23:16
- // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(<ZST>) }
- }
-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
fn inlined_target_feature() -> () {
let mut _0: (); // return place in scope 0 at $DIR/inline_compatibility.rs:+0:40: +0:40
let _1: (); // in scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:21
+ scope 1 (inlined target_feature) { // at $DIR/inline_compatibility.rs:13:5: 13:21
+ scope 1 (inlined target_feature) { // at $DIR/inline_compatibility.rs:12:5: 12:21
+ }

bb0: {
StorageLive(_1); // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:21
- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:21
- // mir::Constant
- // + span: $DIR/inline_compatibility.rs:13:5: 13:19
- // + span: $DIR/inline_compatibility.rs:12:5: 12:19
- // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(<ZST>) }
- }
-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
StorageLive(_1); // scope 0 at $DIR/inline_compatibility.rs:+1:9: +1:10
_1 = sum(const 4_u32, const 4_u32, const 30_u32, const 200_u32, const 1000_u32) -> bb1; // scope 0 at $DIR/inline_compatibility.rs:+1:13: +1:52
// mir::Constant
// + span: $DIR/inline_compatibility.rs:42:13: 42:16
// + span: $DIR/inline_compatibility.rs:41:13: 41:16
// + literal: Const { ty: unsafe extern "C" fn(u32, ...) -> u32 {sum}, val: Value(<ZST>) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
StorageLive(_1); // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:18
_1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:18
// mir::Constant
// + span: $DIR/inline_compatibility.rs:29:5: 29:16
// + span: $DIR/inline_compatibility.rs:28:5: 28:16
// + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(<ZST>) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
StorageLive(_1); // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:21
_1 = target_feature() -> bb1; // scope 0 at $DIR/inline_compatibility.rs:+1:5: +1:21
// mir::Constant
// + span: $DIR/inline_compatibility.rs:18:5: 18:19
// + span: $DIR/inline_compatibility.rs:17:5: 17:19
// + literal: Const { ty: unsafe fn() {target_feature}, val: Value(<ZST>) }
}

Expand Down
1 change: 0 additions & 1 deletion tests/mir-opt/inline/inline_compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#![crate_type = "lib"]
#![feature(no_sanitize)]
#![feature(target_feature_11)]
#![feature(c_variadic)]

// EMIT_MIR inline_compatibility.inlined_target_feature.Inline.diff
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/asm/x86_64/issue-89875.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// needs-asm-support
// only-x86_64

#![feature(target_feature_11)]

use std::arch::asm;

#[target_feature(enable = "avx")]
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
// revisions: mir thir
// [thir]compile-flags: -Z thir-unsafeck

#![feature(target_feature_11)]

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
// [thir]compile-flags: -Z thir-unsafeck
// only-x86_64

#![feature(target_feature_11)]

#[target_feature(enable="avx")]
fn also_use_avx() {
println!("Hello from AVX")
Expand Down

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/fn-ptr.rs:11:21
--> $DIR/fn-ptr.rs:9:21
|
LL | #[target_feature(enable = "sse2")]
| ---------------------------------- `#[target_feature]` added here
Expand Down
2 changes: 0 additions & 2 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,8 +2,6 @@
// [thir]compile-flags: -Z thir-unsafeck
// only-x86_64

#![feature(target_feature_11)]

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/fn-ptr.rs:11:21
--> $DIR/fn-ptr.rs:9:21
|
LL | #[target_feature(enable = "sse2")]
| ---------------------------------- `#[target_feature]` added here
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// only-x86_64

#![feature(target_feature_11)]

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

Expand Down
24 changes: 12 additions & 12 deletions tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: expected a `Fn<()>` closure, found `fn() {foo}`
--> $DIR/fn-traits.rs:24:10
--> $DIR/fn-traits.rs:22:10
|
LL | call(foo);
| ---- ^^^ expected an `Fn<()>` closure, found `fn() {foo}`
Expand All @@ -10,13 +10,13 @@ LL | call(foo);
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call`
--> $DIR/fn-traits.rs:11:17
--> $DIR/fn-traits.rs:9:17
|
LL | fn call(f: impl Fn()) {
| ^^^^ required by this bound in `call`

error[E0277]: expected a `FnMut<()>` closure, found `fn() {foo}`
--> $DIR/fn-traits.rs:25:14
--> $DIR/fn-traits.rs:23:14
|
LL | call_mut(foo);
| -------- ^^^ expected an `FnMut<()>` closure, found `fn() {foo}`
Expand All @@ -27,13 +27,13 @@ LL | call_mut(foo);
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call_mut`
--> $DIR/fn-traits.rs:15:21
--> $DIR/fn-traits.rs:13:21
|
LL | fn call_mut(f: impl FnMut()) {
| ^^^^^^^ required by this bound in `call_mut`

error[E0277]: expected a `FnOnce<()>` closure, found `fn() {foo}`
--> $DIR/fn-traits.rs:26:15
--> $DIR/fn-traits.rs:24:15
|
LL | call_once(foo);
| --------- ^^^ expected an `FnOnce<()>` closure, found `fn() {foo}`
Expand All @@ -44,13 +44,13 @@ LL | call_once(foo);
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call_once`
--> $DIR/fn-traits.rs:19:22
--> $DIR/fn-traits.rs:17:22
|
LL | fn call_once(f: impl FnOnce()) {
| ^^^^^^^^ required by this bound in `call_once`

error[E0277]: expected a `Fn<()>` closure, found `unsafe fn() {foo_unsafe}`
--> $DIR/fn-traits.rs:28:10
--> $DIR/fn-traits.rs:26:10
|
LL | call(foo_unsafe);
| ---- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
Expand All @@ -61,13 +61,13 @@ LL | call(foo_unsafe);
= note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call`
--> $DIR/fn-traits.rs:11:17
--> $DIR/fn-traits.rs:9:17
|
LL | fn call(f: impl Fn()) {
| ^^^^ required by this bound in `call`

error[E0277]: expected a `FnMut<()>` closure, found `unsafe fn() {foo_unsafe}`
--> $DIR/fn-traits.rs:30:14
--> $DIR/fn-traits.rs:28:14
|
LL | call_mut(foo_unsafe);
| -------- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
Expand All @@ -78,13 +78,13 @@ LL | call_mut(foo_unsafe);
= note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call_mut`
--> $DIR/fn-traits.rs:15:21
--> $DIR/fn-traits.rs:13:21
|
LL | fn call_mut(f: impl FnMut()) {
| ^^^^^^^ required by this bound in `call_mut`

error[E0277]: expected a `FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}`
--> $DIR/fn-traits.rs:32:15
--> $DIR/fn-traits.rs:30:15
|
LL | call_once(foo_unsafe);
| --------- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
Expand All @@ -95,7 +95,7 @@ LL | call_once(foo_unsafe);
= note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call_once`
--> $DIR/fn-traits.rs:19:22
--> $DIR/fn-traits.rs:17:22
|
LL | fn call_once(f: impl FnOnce()) {
| ^^^^^^^^ required by this bound in `call_once`
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/rfcs/rfc-2396-target_feature-11/issue-99876.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// check-pass

#![feature(target_feature_11)]

struct S<T>(T)
where
[T; (|| {}, 1).1]: Copy;
Expand Down
Loading

0 comments on commit b583ede

Please sign in to comment.