Skip to content

Commit bbcd0c3

Browse files
committed
Merge remote-tracking branch 'gnzlbg/tf11'
2 parents 5add98b + 71b9069 commit bbcd0c3

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed

text/0000-target-feature-1.1.md

+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
- Feature Name: `#[target_feature]` 1.1
2+
- Start Date: 2018-04-06
3+
- RFC PR: (leave this empty)
4+
- Rust Issue: (leave this empty)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
This RFC attempts to resolve some of the unresolved questions in [RFC 2045
10+
(`target_feature`)]. In particular, it allows:
11+
12+
* specifying `#[target_feature]` functions without making them `unsafe fn`
13+
* calling `#[target_feature]` functions in some contexts without `unsafe { }` blocks
14+
15+
It achieves this by proposing three incremental steps that we can sequentially
16+
make to improve the ergonomics and the safety of target-specific functionality
17+
without adding run-time overhead.
18+
19+
[RFC 2045 (`target_feature`)]: https://github.com/rust-lang/rfcs/pull/2045
20+
21+
# Motivation
22+
[motivation]: #motivation
23+
24+
> This is a brief recap of [RFC 2045 (`target_feature`)].
25+
26+
The `#[target_feature]` attribute allows Rust to generate machine code for a
27+
function under the assumption that the hardware where the function will be
28+
executed on supports some specific "features".
29+
30+
If the hardware does not support the features, the machine code was generated
31+
under assumptions that do not hold, and the behavior of executing the function
32+
is undefined.
33+
34+
[RFC 2045 (`target_feature`)] guarantees safety by requiring all
35+
`#[target_feature]` functions to be `unsafe fn`, thus preventing them from being
36+
called from safe code. That is, users have to open an `unsafe { }` block to call
37+
these functions, and they have to manually ensure that their pre-conditions
38+
hold - for example, that they will only be executed on the appropriate hardware
39+
by doing run-time feature detection, or using conditional compilation.
40+
41+
And that's it. That's all [RFC 2045 (`target_feature`)] had to say about this.
42+
Back then, there were many other problems that needed to be solved for all of
43+
this to be minimally useful, and [RFC 2045 (`target_feature`)] dealt with those.
44+
45+
However, the consensus back then was that this is far from ideal for many
46+
reasons:
47+
48+
* when calling `#[target_feature]` functions from other `#[target_feature]`
49+
functions with the same features, the calls are currently still `unsafe` but
50+
they are actually safe to call.
51+
* making all `#[target_feature]` functions `unsafe fn`s and requiring `unsafe
52+
{}` to call them everywhere hides other potential sources of `unsafe` within
53+
these functions. Users get used to upholding `#[target_feature]`-related
54+
pre-conditions, and other types of pre-conditions get glossed by.
55+
* `#[target_feature]` functions are not inlined across mismatching contexts,
56+
which can have disastrous performance implications. Currently calling
57+
`#[target_feature]` function from all contexts looks identical which makes it
58+
easy for users to make these mistakes (which get reported often).
59+
60+
The solution proposed in this RFC solves these problems.
61+
62+
# Guide-level explanation
63+
[guide-level-explanation]: #guide-level-explanation
64+
65+
Currently, we require that `#[target_feature]` functions be declared as `unsafe
66+
fn`. This RFC relaxes this restriction:
67+
68+
* safe `#[target_feature]` functions can be called _without_ an `unsafe {}`
69+
block _only_ from functions that have at least the exact same set of
70+
`#[target_feature]`s. Calling them from other contexts (other functions, static
71+
variable initializers, etc.) requires opening an `unsafe {}` even though they
72+
are not marked as `unsafe`:
73+
74+
```rust
75+
// Example 1:
76+
#[target_feature(enable = "sse2")] unsafe fn foo() { } // RFC2045
77+
#[target_feature(enable = "sse2")] fn bar() { } // NEW
78+
79+
// This function does not have the "sse2" target feature:
80+
fn meow() {
81+
foo(); // ERROR (unsafe block required)
82+
unsafe { foo() }; // OK
83+
bar(); // ERROR (meow is not sse2)
84+
unsafe { bar() }; // OK
85+
}
86+
87+
#[target_feature(enable = "sse2")]
88+
fn bark() {
89+
foo(); // ERROR (foo is unsafe: unsafe block required)
90+
unsafe { foo() }; // OK
91+
bar(); // OK (bark is sse2 and bar is safe)
92+
unsafe { bar() }; // OK (as well - warning: unnecessary unsafe block)
93+
}
94+
95+
#[target_feature(enable = "avx")] // avx != sse2
96+
fn moo() {
97+
foo(); // ERROR (unsafe block required)
98+
unsafe { foo() }; // OK
99+
bar(); // ERROR (moo is not sse2 but bar requires it)
100+
unsafe { bar() }; // OK
101+
}
102+
```
103+
104+
> Note: while it is safe to call an SSE2 function from _some_ AVX functions,
105+
> this would require specifying how features relate to each other in
106+
> hierarchies. It is unclear whether those hierarchies actually exist, but
107+
> adding them to this RFC would unnecessarily complicate it and can be done
108+
> later or in parallel to this one, once we agree on the fundamentals.
109+
110+
First, this is still sound. The caller has a super-set of `#[target_features]`
111+
of the callee. That is, the `#[target_feature]`-related pre-conditions of the
112+
callee are uphold by the caller, therefore calling the callee is safe.
113+
114+
This change already solves all three issues mentioned in the motivation:
115+
116+
* When calling `#[target_feature]` functions from other `#[target_feature]`
117+
functions with the same features, we don't need `unsafe` code anymore.
118+
* Since `#[target_feature]` functions do not need to be `unsafe` anymore,
119+
`#[target_feature]` functions that are marked with `unsafe` become more
120+
visible, making it harder for users to oversee that there are other
121+
pre-conditions that must be uphold.
122+
* `#[target_feature]` function calls across mismatching contexts require
123+
`unsafe`, making them more visible. This makes it easier to identify
124+
calls-sites across which they cannot be inlined while making call-sites across
125+
which they can be inlined more ergonomic to write.
126+
127+
The `#[target_feature]` attribute continues to be allowed on inherent methods -
128+
this RFC does not change that.
129+
130+
The `#[target_feature]` attribute continues to not be allowed on safe trait
131+
method implementations because that would require an `unsafe` trait method
132+
declaration:
133+
134+
```rust
135+
// Example 2:
136+
trait Foo { fn foo(); }
137+
struct Fooish();
138+
impl Foo for Fooish {
139+
#[target_feature(enable = "sse2")] fn foo() { }
140+
// ^ ERROR: #[target_feature] on trait method impl requires
141+
// unsafe fn but Foo::foo is safe
142+
// (this is already an error per RFC2045)
143+
}
144+
145+
trait Bar { unsafe fn bar(); }
146+
struct Barish();
147+
impl Bar for Barish {
148+
#[target_feature(enable = "sse2")] unsafe fn bar() { } // OK (RFC2045)
149+
}
150+
```
151+
152+
* safe `#[target_feature]` functions are not assignable to safe `fn` pointers.
153+
154+
155+
```rust
156+
// Example 3
157+
#[target_feature(enable = "avx")] fn meow() {}
158+
159+
static x: fn () -> () = meow;
160+
// ^ ERROR: meow can only be assigned to unsafe fn pointers due to
161+
// #[target_feature] but function pointer x with type fn()->() is safe.
162+
static y: unsafe fn () -> () = meow as unsafe fn()->(); // OK
163+
```
164+
165+
# Reference-level explanation
166+
[reference-level-explanation]: #reference-level-explanation
167+
168+
This RFC proposes to changes to the language with respect to [RFC 2045 (`target_feature`)]:
169+
170+
* safe `#[target_feature]` functions can be called _without_ an `unsafe {}`
171+
block _only_ from functions that have at least the exact same set of
172+
`#[target_feature]`s. Calling them from other contexts (other functions, static
173+
variable initializers, etc.) requires opening an `unsafe {}` even though they
174+
are not marked as `unsafe`
175+
176+
* safe `#[target_feature]` functions are not assignable to safe `fn` pointers.
177+
178+
# Drawbacks
179+
[drawbacks]: #drawbacks
180+
181+
This RFC extends the typing rules for `#[target_feature]`, which might
182+
unnecessarily complicate future language features like an effect system.
183+
184+
# Rationale and alternatives
185+
[alternatives]: #alternatives
186+
187+
Since `#[target_feature]` are effects or restrictions (depending on whether we
188+
`enable` or `disable` them), the alternative would be to integrate them with an
189+
effect system.
190+
191+
# Prior art
192+
[prior-art]: #prior-art
193+
194+
[RFC2212 target feature unsafe](https://github.com/rust-lang/rfcs/pull/2212)
195+
attempted to solve this problem. This RFC builds on the discussion that was
196+
produced by that RFC and by many discussions in the `stdsimd` repo.
197+
198+
# Unresolved questions
199+
[unresolved]: #unresolved-questions
200+
201+
## Negative features
202+
203+
[RFC 2045 (`target_feature`)] introduced the `#[target_feature(enable = "x")]`
204+
syntax to allow introducing negative features in future RFCs in the form of
205+
`#[target_feature(disable = "y")]`. Since these have not been introduced yet we
206+
can only speculate about how they would interact with the extensions proposed in
207+
this RFC but we probably can make the following work in some form:
208+
209+
```rust
210+
// #[target_feature(enable = "sse")]
211+
fn foo() {}
212+
213+
#[target_feature(disable = "sse")]
214+
fn bar() {
215+
foo(); // ERROR: (bar is not sse)
216+
unsafe { foo() }; // OK
217+
}
218+
219+
fn baz() {
220+
bar(); // OK
221+
}
222+
```
223+
224+
## Effect system
225+
226+
It is unclear how `#[target_feature]` would interact with an effect system for
227+
Rust like the one being tracked
228+
[here](https://github.com/Centril/rfc-effects/issues) and discussed in
229+
[RFC2237](https://github.com/rust-lang/rfcs/pull/2237).
230+
231+
In particular, it is unclear how the typing rules being proposed here would be
232+
covered by such an effect system, and whether such system would support
233+
attributes in effect/restriction position.
234+
235+
Such an effect-system might need to introduce first-class target-features into
236+
the language (beyond just a simple attribute) which could lead to the
237+
deprecation of the `#[target_feature]` attribute.
238+
239+
It is also unclear how any of this interacts with effect-polymorphism at this
240+
point, but we could _maybe_ support something like `impl const Trait` and `T:
241+
const Trait`:
242+
243+
```rust
244+
impl #[target_feature(enable = "...")] Trait for Type { ... }
245+
fn foo<T: #[target_feature(enable = "...")] Trait>(...) { ...}
246+
```
247+
248+
if all trait methods are `unsafe`; otherwise they can't have the
249+
`#[target_feature]` attribute.
250+

0 commit comments

Comments
 (0)