You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/0000-raw-reference-mir-operator.md
+41-50Lines changed: 41 additions & 50 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,9 +9,7 @@
9
9
Introduce new variants of the `&` operator: `&raw mut <place>` to create a `*mut <T>`, and `&raw const <place>` to create a `*const <T>`.
10
10
This creates a raw pointer directly, as opposed to the already existing `&mut <place> as *mut _`/`&<place> as *const _`, which create a temporary reference and then cast that to a raw pointer.
11
11
As a consequence, the existing expressions `<term> as *mut <T>` and `<term> as *const <T>` where `<term>` has reference type are equivalent to `&raw mut *<term>` and `&raw const *<term>`, respectively.
12
-
Moreover, add a lint to existing code that could use the new operator, and treat existing code that creates a reference and immediately casts or coerces it to a raw pointer as if it had been written with the new syntax.
13
-
14
-
As an option (referred to as [SUGAR] below), we could treat `&mut <place> as *mut _`/`&<place> as *const _` as if they had been written with `&raw` to avoid creating temporary references when that was likely not the intention.
12
+
Moreover, add a lint to existing code that could use the new operator.
15
13
16
14
# Motivation
17
15
[motivation]: #motivation
@@ -79,8 +77,7 @@ let &x = &X; // this is actually dereferencing the pointer, certainly UB
79
77
let_=&X; // throwing away the value immediately changes nothing
80
78
&X; // different syntax for the same thing
81
79
82
-
letx=&Xas*constT; // this is casting to raw but "too late", an intermediate reference has been created (only if we do no adapt [SUGAR])
83
-
letx=&Xas&Tas*constT; // this is casting to raw but "too late" even if we adapt [SUGAR]
80
+
letx=&Xas*constT; // this is casting to raw but "too late", an intermediate reference has been created
84
81
```
85
82
86
83
The only way to create a pointer to an unaligned or dangling location without triggering undefined behavior is to use `&raw`, which creates a raw pointer without an intermediate reference.
@@ -90,19 +87,6 @@ The following is valid:
90
87
letpacked_cast=&rawconstpacked.field;
91
88
```
92
89
93
-
As an optional extension ([SUGAR]) to keep existing code working and to provide a way for projects to adjust to these rules before the syntax bikeshed is finished, and to do so in a way that they do not have to drop support for old Rust versions, we could also treat all of the following as if they had been written using `&raw const` instead of `&`:
The intention is to cover all cases where a reference, just created, is immediately explicitly used as a value of raw pointer type.
103
-
104
-
Notice that this only applies if no automatic call to `deref` or `deref_mut` got inserted:
105
-
those are regular function calls taking a reference, so in that case a reference is created and it must satisfy the usual guarantees.
106
90
107
91
108
92
# Reference-level explanation
@@ -114,11 +98,6 @@ The borrow checker should do the usual checks on the place used in `&raw`, but c
114
98
When translating MIR to LLVM, nothing special has to happen as references and raw pointers have the same LLVM type anyway; the new operation behaves like `Ref`.
115
99
When interpreting MIR in the Miri engine, the engine will know not to enforce any invariants on the raw pointer created by `&raw`.
116
100
117
-
For the [SUGAR] option, when translating HIR to MIR, we recognize `&[mut] <place> as *[mut|const] ?T` (where `?T` can be any type, also a partial one like `_`) as well as coercions from `&[mut] <place>` to a raw pointer type as a special pattern and treat them as if they would have been written `&raw [mut|const] <place>`.
118
-
We do this *after* auto-deref, meaning this pattern does not apply when a call to `deref` or `deref_mut` got inserted.
119
-
Redundant parentheses are ignored, but block expressions are not:
120
-
`{ &[mut] <place> }` materializes a reference that must be valid, no matter which coercions or casts follow outside the block.
121
-
122
101
When doing unsafety checking, we make references to packed fields that do *not* use this new "raw reference" operation a *hard error even in unsafe blocks* (after a transition period).
123
102
There is no situation in which this code is okay; it creates a reference that violates basic invariants.
124
103
Taking a raw reference to a packed field, on the other hand, is a safe operation as the raw pointer comes with no special promises.
@@ -128,35 +107,16 @@ This check has nothing to do with whether we are in an unsafe block or not.
128
107
Moreover, to prevent programmers from accidentally creating a safe reference when they did not want to, we add a lint that identifies situations where the programmer likely wants a raw reference, and suggest an explicit cast in that case.
129
108
One possible heuristic here would be: If a safe reference (shared or mutable) is only ever used to create raw pointers, then likely it could be a raw pointer to begin with.
130
109
The details of this are best worked out in the implementation phase of this RFC.
131
-
The lint should, at the very least, fire for the cases covered by [SUGAR] if we do *not* adopt that option, and it should fire when the factor that prevents this matching [SUGAR] is just a redundant block, such as `{ &mut <place> } as *mut ?T`.
110
+
The lint should, at the very least, fire for the cases covered by the syntactic sugar extension (see [Future possibilities][future-possibilities]), and it should fire when the factor that prevents this matching the sugar is just a redundant block, such as `{ &mut <place> } as *mut ?T`.
132
111
133
112
# Drawbacks
134
113
[drawbacks]: #drawbacks
135
114
136
-
If we adapt [SUGAR], it might be surprising that the following two pieces of code are not equivalent:
Notice, however, that the lint should fire in variant 1.
147
-
148
-
If `as` ever becomes an operation that can be overloaded, the behavior of `&packed.field as *const _` with [SUGAR] can *not* be obtained by dispatching to the overloaded `as` operator.
149
-
Calling that method would assert validity of the reference.
115
+
This introduces new clauses into our grammar for a niche operation.
[SUGAR] is a compromise to keep the analysis that affects code generation simple.
155
-
Detecting "Variant 1" (from the "Drawbacks" section) would need a much less local analysis.
156
-
Hence the proposal to make them not equivalent.
157
-
158
-
A drawback of not adapting [SUGAR] is that we will have to wait longer (namely, until stabilization of the new syntax) until people can finally write UB-free versions of code that handles dangling or unaligned raw pointers.
159
-
160
120
One alternative to introducing a new primitive operation might be to somehow exempt "references immediately cast to a raw pointer" from the invariant.
161
121
(Basically, a "dynamic" version of the static analysis performed by the lint.)
162
122
However, I believe that the semantics of a MIR program, including whether it as undefined behavior, should be deducible by executing it one step at a time.
@@ -171,11 +131,6 @@ The need for taking a raw reference only arise because of Rust having both of th
171
131
# Unresolved questions
172
132
[unresolved-questions]: #unresolved-questions
173
133
174
-
With [SUGAR], should the lint apply to cases that are covered by the special desugaring or not?
175
-
Also, if not, should the lint become `deny` eventually (maybe only on some editions)?
176
-
(Without [SUGAR], the lint clearly must apply to `&mut <place> as *mut _`/`&<place> as *const _`, and that pattern is common enough that the cost of `deny` is too high.)
177
-
178
-
The interaction with auto-deref is a bit unfortunate.
179
134
Maybe the lint should also cover cases that look like `&[mut] <place> as *[mut|const] ?T` in the surface syntax but had a method call inserted, thus manifesting a reference (with the associated guarantees).
180
135
The lint as described would not fire because the reference actually gets used as such (being passed to `deref`).
181
136
However, what would the lint suggest to do instead?
@@ -184,10 +139,46 @@ There just is no way to write this code without creating a reference.
184
139
# Future possibilities
185
140
[future-possibilities]: #future-possibilities
186
141
187
-
With [SUGAR], if Rust's type ascriptions end up performing coercions, those coercions should trigger the raw reference operator just like other coercions do.
142
+
## "Syntactic sugar" extension
143
+
144
+
We could treat `&mut <place> as *mut _`/`&<place> as *const _` as if they had been written with `&raw` to avoid creating temporary references when that was likely not the intention.
145
+
We could also do this when `&mut <place>`/`& <place>` is used in a coercion site and gets coerced to a raw pointer.
146
+
147
+
```rust
148
+
letx=&Xas*constT; // this is fine now
149
+
letx:*constT; // this is fine if we also apply the "sugar" for coercions
150
+
letx=&Xas&Tas*constT; // this is casting to raw but "too late" even if we adapt [SUGAR]
151
+
letx= { &X } as*constT; // this is likely also too late (but should be covered by the lint)
152
+
letx:*constT=ifb { &X } else { &Y }; // this is likely also too late (and hopefully covered by the lint)
153
+
```
154
+
155
+
Notice that this only applies if no automatic call to `deref` or `deref_mut` got inserted:
156
+
those are regular function calls taking a reference, so in that case a reference is created and it must satisfy the usual guarantees.
157
+
158
+
The point of this to keep existing code working and to provide a way for projects to adjust to these rules before stabilization.
159
+
Another good reason for this extension is that code could be adjusted without having to drop support for old Rust versions.
160
+
161
+
However, it might be surprising that the following two pieces of code are not equivalent:
letx=unsafe { &packed.field as*const_ }; // good code
169
+
```
170
+
171
+
This is at least partially mitigated by the fact that the lint should fire in variant 1.
172
+
173
+
Another problem is that if `as` ever becomes an operation that can be overloaded, the behavior of `&packed.field as *const _` can *not* be obtained by dispatching to the overloaded `as` operator.
174
+
Calling that method would assert validity of the reference.
175
+
176
+
In the future, if Rust's type ascriptions end up performing coercions, those coercions should trigger the raw reference operator just like other coercions do.
188
177
So `&packed.field: *const _` would be `&raw const packed.field`.
189
178
If Rust ever gets type ascriptions with coercions for binders, likewise these coercions would be subject to these rules in cases like `match &packed.field { x: *const _ => x }`.
190
179
180
+
## Other
181
+
191
182
It has been suggested to [remove `static mut`][static-mut] because it is too easy to accidentally create references with lifetime `'static`.
192
183
With `&raw` we could instead restrict `static mut` to only allow taking raw pointers (`&raw [mut|const] STATIC`) and entirely disallow creating references (`&[mut] STATIC`) even in safe code (in a future edition, likely; with lints in older editions).
0 commit comments