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
+17-14Lines changed: 17 additions & 14 deletions
Original file line number
Diff line number
Diff line change
@@ -9,7 +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.
12
+
Moreover, emit a lint for existing code that could use the new operator.
Rust contains two operators that perform place-to-rvalue conversion (matching `&` in C): one to create a reference (with some given mutability) and one to create a raw pointer (with some given mutability).
93
+
Rust contains two operators that perform place-to-value conversion (matching `&` in C): one to create a reference (with some given mutability) and one to create a raw pointer (with some given mutability).
96
94
In the MIR, this is reflected as either a distinct `Rvalue` or a flag on the existing `Ref` variant.
95
+
Lowering to MIR should *not* insert an implicit reborrow of `<place>` in `&raw mut <place>`; that reborrow would assert validity and thus defeat the entire point.
97
96
The borrow checker should do the usual checks on the place used in `&raw`, but can just ignore the result of this operation and the newly created "reference" can have any lifetime.
98
97
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`.
99
98
When interpreting MIR in the Miri engine, the engine will know not to enforce any invariants on the raw pointer created by `&raw`.
100
99
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).
102
-
There is no situation in which this code is okay; it creates a reference that violates basic invariants.
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.
104
-
"Unsafety checking" is thus not even a good term for this, maybe it should be a special pass dedicated to packed fields traversing MIR, or this can happen when lowering HIR to MIR.
105
-
This check has nothing to do with whether we are in an unsafe block or not.
106
-
107
100
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.
108
101
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.
109
102
The details of this are best worked out in the implementation phase of this RFC.
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`.
103
+
The lint should, at the very least, fire for the cases covered by the [syntactic sugar extension][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`.
111
104
112
105
# Drawbacks
113
106
[drawbacks]: #drawbacks
@@ -177,12 +170,21 @@ In the future, if Rust's type ascriptions end up performing coercions, those coe
177
170
So `&packed.field: *const _` would be `&raw const packed.field`.
178
171
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 }`.
179
172
180
-
## Other
173
+
## Encouraging / requiring `&raw` in situations where references are often/definitely incorrect
174
+
175
+
We could 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).
176
+
There is no situation in which this code is okay; it creates a reference that violates basic invariants.
177
+
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.
181
178
182
179
It has been suggested to [remove `static mut`][static-mut] because it is too easy to accidentally create references with lifetime `'static`.
183
180
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).
184
181
185
-
As mentioned above, expressions such as `&raw mut x.field` still trigger more UB than might be expected---as witnessed by a [couple of attempts found in the wild of people implementing `offsetof`][offset-of] with something like:
182
+
## Other
183
+
184
+
**Lowering of casts.** Currently, `mut_ref as *mut _` has a reborrow inserted, i.e., it gets lowered to `&mut *mut_ref as *mut _`.
185
+
It seems like a good idea to lower this to `&raw mut *mut_ref` instead to avoid any effects the reborrow might have in terms of permitted aliasing.
186
+
187
+
**`offsetof` woes.** As mentioned above, expressions such as `&raw mut x.field` still trigger more UB than might be expected---as witnessed by a [couple of attempts found in the wild of people implementing `offsetof`][offset-of] with something like:
186
188
187
189
```rust
188
190
letx:*mutStruct=NonNull::dangling().as_ptr();
@@ -198,7 +200,8 @@ let field: *mut Field = &raw mut x.field;
198
200
199
201
which is better, but still UB: we emit a `getelementptr inbounds` for the `.field` offset computation.
200
202
It might be a good idea to just not do that -- we know that references are fine, but we could decide that when raw pointers are involved that might be dangling, we do not want to assert anything from just the fact that an offset is being computed.
201
-
A plain `getelementptr` still provides some information to the alias analysis, and if we derive the `inbounds` entirely from the fact that the pointer is created from or turned into a reference, we would be able to not have any clause in the language semantics that calls out the offset computation as potentially triggering UB
203
+
However, there are concerns that a plain `getelementptr` will not be sufficiently optimized because it also permits arithmetic that wraps around the end of the address space.
204
+
LLVM currently does not support a `getelementptr nowrap` that disallows wrapping but permits cross-allocation arithmetic, but if that could be added, using it for raw pointers could save us from having to talk about the "no outofbounds arithmetic" rule in the semantics of field access (the UB triggered by creating dangling references would be enough).
202
205
If people just hear "`&raw` means my pointer can be dangling" they might think the second version above is actually okay, forgetting that the field access itself has its own subtle rule; getting rid of that rule would remove one foot-gun for unsafe code authors to worry about.
0 commit comments