Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stabilize #![feature(target_feature_11)] #99767

Merged
merged 1 commit into from
Feb 28, 2023

Conversation

LeSeulArtichaut
Copy link
Contributor

@LeSeulArtichaut LeSeulArtichaut commented Jul 26, 2022

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.

// 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/.

Edge cases

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.

#[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


cc tracking issue #69098
r? @ghost

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jul 26, 2022
@LeSeulArtichaut
Copy link
Contributor Author

LeSeulArtichaut commented Jul 26, 2022

@rustbot label T-lang needs-fcp

@rustbot rustbot added needs-fcp This change is insta-stable, so needs a completed FCP to proceed. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jul 26, 2022
@rust-log-analyzer

This comment has been minimized.

@joshtriplett
Copy link
Member

@rfcbot merge

@rfcbot
Copy link

rfcbot commented Jul 27, 2022

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jul 27, 2022
@LeSeulArtichaut
Copy link
Contributor Author

LeSeulArtichaut commented Jul 27, 2022

Oops, I should probably have removed the T-compiler label, sorry for the inconvenience

@oli-obk oli-obk added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 27, 2022
@oli-obk
Copy link
Contributor

oli-obk commented Jul 27, 2022

@rfcbot cancel

@rfcbot
Copy link

rfcbot commented Jul 27, 2022

@oli-obk proposal cancelled.

@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jul 27, 2022
@oli-obk oli-obk removed the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jul 27, 2022
@LeSeulArtichaut LeSeulArtichaut marked this pull request as ready for review August 5, 2022 15:21
@LeSeulArtichaut
Copy link
Contributor Author

Tried to write up a stabilization report, feedback would be appreciated.

@LeSeulArtichaut LeSeulArtichaut force-pushed the stable-target-feature-11 branch 3 times, most recently from 3d7c5ad to ec98a1e Compare August 5, 2022 15:49
@rust-log-analyzer

This comment has been minimized.

@LeSeulArtichaut LeSeulArtichaut force-pushed the stable-target-feature-11 branch from ec98a1e to ebbd8f8 Compare August 5, 2022 21:08
@lcnr
Copy link
Contributor

lcnr commented Aug 6, 2022

how does this interact with type alias impl trait

#![feature(target_feature_11, type_alias_impl_trait)]

type Foo = impl FnOnce() + Default;

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

#[target_feature(enable = "avx2")]
fn qux() {
    let x: Foo = || avx2(); // currently errors cause the closure isn't `Default`.
}

in #77688 it was decided against adding Default for function types and closures but that decision wasn't "we will never add that impl in the future" so I do want something apart from closures aren't Default from preventing the above example to work

@LeSeulArtichaut
Copy link
Contributor Author

LeSeulArtichaut commented Aug 8, 2022

@lcnr I think this is analogous to something like this?

unsafe fn unsf() {}

// This has some safety requirements...
unsafe fn foo() {
    // ...which might be used in the closure here
    let x: Foo = || unsf();
}

I think calling x through Foo::default() would be unsound?

Or something like

fn bar() {
    // check that some invariants are upheld before doing some unsafe
    if !invariant_upheld { return; }

    let x: Foo = || unsafe { unsf(); };
}

Wouldn't it be surprising to be able to call x through Default?

This was raised in #77688 (comment) but I couldn't find a proposed solution/workaround. If there is one, would it be applicable here as well?

@lcnr
Copy link
Contributor

lcnr commented Aug 9, 2022

While || unsafe { some_unsafe_fn() } is now a safe function pointer which we could leak this way, we still need unsafe somewhere to skip that boundary.

With target features the skip can be fully implicit (except for a #[target_feature(...)] attribute on the function constraining the opaque type).

Wouldn't it be surprising to be able to call x through Default?

as Foo would need an explicit Default bound, I think no ^^

I realized another issue with implementing Default for closures automatically. In the future, we may uncover that an opaque type implements Default using specialization, changing implicit impls of Default to change currently sound APIs to be unsound, making this a breaking change.

I guess I am fine with stabilizing this then ^^ while it seems like a final nail in the coffin of Default for closures and function definitions, there are already a few other nails.

@est31
Copy link
Member

est31 commented Aug 27, 2022

👋 Hello, I'm writing this comment in this stabilization PR to notify you, the authors of this PR, that #100591 has been merged, which implemented a change in how features are stabilized.

Your PR has been filed before the change, so will likely require modifications in order to comply with the new rules. I recommend you to:

  1. rebase the PR onto latest master, so that uses of the placeholder are possible.
  2. replace the version numbers in the PR with the placeholder CURRENT_RUSTC_VERSION. For language changes, this means the version numbers in accepted.rs (example: 4caedba). For library changes, this means the since fields (example e576a9b).

That's it! The CURRENT_RUSTC_VERSION placeholder will, as part of the release process, be replaced with the version number that the PR merged for. It can be used anywhere in rust-lang/rust, not just accepted.rs and the since fields.

If you have any questions, feel free to drop by the zulip stream, or ping me directly in this PR's thread. Thanks! 👋

@LeSeulArtichaut LeSeulArtichaut force-pushed the stable-target-feature-11 branch from ebbd8f8 to 7001d17 Compare August 27, 2022 20:29
@LeSeulArtichaut
Copy link
Contributor Author

LeSeulArtichaut commented Aug 27, 2022

Done! Thank you very much for the heads-up @est31

@LeSeulArtichaut LeSeulArtichaut force-pushed the stable-target-feature-11 branch from cdb5b0c to b379d21 Compare February 1, 2023 07:56
Copy link
Contributor

@estebank estebank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me

compiler/rustc_feature/src/accepted.rs Show resolved Hide resolved
@Noratrieb
Copy link
Member

The comment was resolved and @estebank approved it, so
@bors r=estebank

@bors
Copy link
Contributor

bors commented Feb 25, 2023

📌 Commit b379d21 has been approved by estebank

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 25, 2023
compiler-errors added a commit to compiler-errors/rust that referenced this pull request Feb 26, 2023
…re-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`
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Feb 27, 2023
…re-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``
@workingjubilee
Copy link
Member

...Hm. I was not aware this had been FCP'd. We are aware that this makes downgrading feature levels a backwards incompatible change for targets, right?

@bors
Copy link
Contributor

bors commented Feb 28, 2023

⌛ Testing commit b379d21 with merge b583ede...

@bors
Copy link
Contributor

bors commented Feb 28, 2023

☀️ Test successful - checks-actions
Approved by: estebank
Pushing b583ede to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Feb 28, 2023
@bors bors merged commit b583ede into rust-lang:master Feb 28, 2023
@rustbot rustbot added this to the 1.69.0 milestone Feb 28, 2023
@est31
Copy link
Member

est31 commented Feb 28, 2023

@workingjubilee

this makes downgrading feature levels a backwards incompatible change for targets

You mean when proposals like this get accepted, the "default" set of default-enabled target features shrinks and some code calling #[target_feature] enabled attributes that were before part of the default set starts to fail compiling?

From testing the implementation, that seems to have been avoided by not having a default set of target features. Even if the target feature is enabled by default, code still must use unsafe. I wasn't involved much during implementation of this RFC so I can't comment on how intentional that was, but I vaguely remember a comment that they intentionally made the most restricted version, where there is also no implication between features that came after another, e.g. sse2 -> sse.

Take this code (playground):

#![feature(target_feature_11)]

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

#[target_feature(enable = "sse")] fn bar() { foo(); }

fn main() {
    #[cfg(target_feature = "sse")]
    bar(); //~ERROR call to function with `#[target_feature]` is unsafe and requires unsafe function or block
}

It will print the error as indicated. The test is written in a way that shows that sse is enabled by default (as is required on AMD 64 bit), and if it weren't, then it wouldn't error as the cfg would not expand. But it still errors even though sse is enabled.

Also note that downgrading of target requirements are breaking changes already, for example you could have code like:

extern { fn fancy_fn_that_uses_sse(); }

fn main() {
    #[cfg(target_feature = "sse")]
    unsafe { fancy_fn_that_uses_sse(); }

    #[cfg(not(target_feature = "sse"))]
    compile_error!("sorry but we only have an sse using implementation of this!");
}

It doesn't use target_feature_11 at all, but it would fail to compile upon a removal of sse from the default set of enabled target features. Edit: technically, increasing requirements is also a breaking change, which has been done over the years every once in a while.

In these scenarios I also think that compiler errors are preferable to runtime SIGILLs or worse.

One could think about extending the target_feature feature to also take the default-enabled target features into account, but that should be discussed in other threads, not here.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (b583ede): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
3.0% [3.0%, 3.0%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 3.0% [3.0%, 3.0%] 1

Cycles

This benchmark run did not return any relevant results for this metric.

@LeSeulArtichaut LeSeulArtichaut deleted the stable-target-feature-11 branch February 28, 2023 06:32
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 2, 2023
…iler-errors

Revert stabilization of `#![feature(target_feature_11)]`

This reverts rust-lang#99767 due to the presence of bugs rust-lang#108645 and rust-lang#108646.

cc `@joshtriplett`
cc tracking issue rust-lang#69098
r? `@ghost`
fmease added a commit to fmease/rust that referenced this pull request Feb 11, 2025
Stabilize target_feature_11

# Stabilization report

This is an updated version of rust-lang#116114, which is itself a redo of rust-lang#99767. Most of this commit and report were copied from those PRs. Thanks `@LeSeulArtichaut` and `@calebzulawski!`

## 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 *generally* 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();
}
```

Moreover, once rust-lang#135504 is merged, they can be converted to safe function pointers in a context in which calling them is safe:

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

fn foo() -> fn() {
    // Converting `avx2` to fn() is a compilation error here.
    avx2
}

#[target_feature(enable = "avx2")]
fn bar() -> fn() {
    // `avx2` coerces to fn() here
    avx2
}
```

See the section "Closures" below for justification of this behaviour.

## Test cases
Tests for this feature can be found in [`tests/ui/target_feature/`](https://github.com/rust-lang/rust/tree/f6cb952dc115fd1311b02b694933e31d8dc8b002/tests/ui/target-feature).

## Edge cases
### Closures
 * [target-feature 1.1: should closures inherit target-feature annotations? rust-lang#73631](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 guarantee that the target-feature is available while the function, any closures defined inside it, as well as any safe function pointers obtained from target-feature functions inside it, execute.

This is usually ensured because target features are assumed to never disappear, and:
- on any unsafe call to a `#[target_feature]` function, presence of the target feature is guaranteed by the programmer through the safety requirements of the unsafe call.
- on any safe call, this is guaranteed recursively by the caller.

If you work in an environment where target features can be disabled, it is your responsibility to ensure that no code inside a target feature function (including inside a closure) runs after this (until the feature is enabled again).

**Note:** this has an effect on existing code, as nowadays closures do not inherit features from the enclosing function, and thus this strengthens a safety requirement. It was originally proposed in rust-lang#73631 to solve this by adding a new type of UB: “taking a target feature away from your process after having run code that uses that target feature is UB” .
This was motivated by userspace code already assuming in a few places that CPU features never disappear from a program during execution (see i.e. https://github.com/rust-lang/stdarch/blob/2e29bdf90832931ea499755bb4ad7a6b0809295a/crates/std_detect/src/detect/arch/x86.rs); however, concerns were raised in the context of the Linux kernel; thus, we propose to relax that requirement to "causing the set of usable features to be reduced is unsafe; when doing so, the programmer is required to ensure that no closures or safe fn pointers that use removed features are still in scope".

* [Fix #[inline(always)] on closures with target feature 1.1 rust-lang#111836](rust-lang#111836)

Closures accept `#[inline(always)]`, even within functions marked with `#[target_feature]`. Since these attributes conflict, `#[inline(always)]` wins out to maintain compatibility.

### ABI concerns
* [The extern "C" ABI of SIMD vector types depends on target features rust-lang#116558](rust-lang#116558)

The ABI of some types can change when compiling a function with different target features. This could have introduced unsoundness with target_feature_11, but recent fixes (rust-lang#133102, rust-lang#132173) either make those situations invalid or make the ABI no longer dependent on features. Thus, those issues should no longer occur.

### Special functions
The `#[target_feature]` attribute is forbidden from a variety of special functions, such as main, current and future lang items (e.g. `#[start]`, `#[panic_handler]`), safe default trait implementations and safe trait methods.

This was not disallowed at the time of the first stabilization PR for target_features_11, and resulted in the following issues/PRs:
* [`#[target_feature]` is allowed on `main` rust-lang#108645](rust-lang#108645)
* [`#[target_feature]` is allowed on default implementations rust-lang#108646](rust-lang#108646)
* [#[target_feature] is allowed on #[panic_handler] with target_feature 1.1 rust-lang#109411](rust-lang#109411)
* [Prevent using `#[target_feature]` on lang item functions rust-lang#115910](rust-lang#115910)

## Documentation
 * Reference: [Document the `target_feature_11` feature reference#1181](rust-lang/reference#1181)
---

cc tracking issue rust-lang#69098
cc `@workingjubilee`
cc `@RalfJung`
r? `@rust-lang/lang`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. merged-by-bors This PR was explicitly merged by bors. needs-fcp This change is insta-stable, so needs a completed FCP to proceed. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.