Skip to content

Commit

Permalink
Merge pull request #1567 from RalfJung/raw-ptr-op
Browse files Browse the repository at this point in the history
operator expressions: add &raw
  • Loading branch information
ehuss authored Aug 20, 2024
2 parents b1b50e4 + 60ccf42 commit b1fced7
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/behavior-considered-undefined.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ the pointer that was dereferenced, *not* the type of the field that is being
accessed.

Note that a place based on a misaligned pointer only leads to undefined behavior
when it is loaded from or stored to. `addr_of!`/`addr_of_mut!` on such a place
when it is loaded from or stored to. `&raw const`/`&raw mut` on such a place
is allowed. `&`/`&mut` on a place requires the alignment of the field type (or
else the program would be "producing an invalid value"), which generally is a
less restrictive requirement than being based on an aligned pointer. Taking a
Expand Down
4 changes: 2 additions & 2 deletions src/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ A *value expression* is an expression that represents an actual value.
The following contexts are *place expression* contexts:

* The left operand of a [compound assignment] expression.
* The operand of a unary [borrow], [address-of][addr-of] or [dereference][deref] operator.
* The operand of a unary [borrow], [raw borrow] or [dereference][deref] operator.
* The operand of a field expression.
* The indexed operand of an array indexing expression.
* The operand of any [implicit borrow].
Expand Down Expand Up @@ -276,7 +276,6 @@ They are never allowed before:

[assign]: expressions/operator-expr.md#assignment-expressions
[borrow]: expressions/operator-expr.md#borrow-operators
[addr-of]: expressions/operator-expr.md#raw-address-of-operators
[comparison]: expressions/operator-expr.md#comparison-operators
[compound assignment]: expressions/operator-expr.md#compound-assignment-expressions
[deref]: expressions/operator-expr.md#the-dereference-operator
Expand All @@ -294,6 +293,7 @@ They are never allowed before:
[Mutable `static` items]: items/static-items.md#mutable-statics
[scrutinee]: glossary.md#scrutinee
[promoted]: destructors.md#constant-promotion
[raw borrow]: expressions/operator-expr.md#raw-borrow-operators
[slice]: types/slice.md
[statement]: statements.md
[static variables]: items/static-items.md
Expand Down
26 changes: 12 additions & 14 deletions src/expressions/operator-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ The following things are considered to be overflow:
> **<sup>Syntax</sup>**\
> _BorrowExpression_ :\
> &nbsp;&nbsp; &nbsp;&nbsp; (`&`|`&&`) [_Expression_]\
> &nbsp;&nbsp; | (`&`|`&&`) `mut` [_Expression_]
> &nbsp;&nbsp; | (`&`|`&&`) `mut` [_Expression_]\
> &nbsp;&nbsp; | (`&`|`&&`) `raw` `const` [_Expression_]\
> &nbsp;&nbsp; | (`&`|`&&`) `raw` `mut` [_Expression_]
The `&` (shared borrow) and `&mut` (mutable borrow) operators are unary prefix operators.
When applied to a [place expression], this expressions produces a reference (pointer) to the location that the value refers to.
Expand Down Expand Up @@ -79,20 +81,18 @@ let a = && && mut 10;
let a = & & & & mut 10;
```

### Raw address-of operators
### Raw borrow operators

Related to the borrow operators are the *raw address-of operators*, which do not have first-class syntax, but are exposed via the macros [`ptr::addr_of!(expr)`][addr_of] and [`ptr::addr_of_mut!(expr)`][addr_of_mut].
The expression `expr` is evaluated in place expression context.
`ptr::addr_of!(expr)` then creates a const raw pointer of type `*const T` to the given place, and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`.
`&raw const` and `&raw mut` are the *raw borrow operators*.
The operand expression of these operators is evaluated in place expression context.
`&raw const expr` then creates a const raw pointer of type `*const T` to the given place, and `&raw mut expr` creates a mutable raw pointer of type `*mut T`.

The raw address-of operators must be used instead of a borrow operator whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type, or whenever creating a reference would introduce incorrect aliasing assumptions.
In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed using an address-of operator.
The raw borrow operators must be used instead of a borrow operator whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type, or whenever creating a reference would introduce incorrect aliasing assumptions.
In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed.

The following is an example of creating a raw pointer to an unaligned place through a `packed` struct:

```rust
use std::ptr;

#[repr(packed)]
struct Packed {
f1: u8,
Expand All @@ -101,14 +101,14 @@ struct Packed {

let packed = Packed { f1: 1, f2: 2 };
// `&packed.f2` would create an unaligned reference, and thus be undefined behavior!
let raw_f2 = ptr::addr_of!(packed.f2);
let raw_f2 = &raw const packed.f2;
assert_eq!(unsafe { raw_f2.read_unaligned() }, 2);
```

The following is an example of creating a raw pointer to a place that does not contain a valid value:

```rust
use std::{ptr, mem::MaybeUninit};
use std::mem::MaybeUninit;

struct Demo {
field: bool,
Expand All @@ -117,7 +117,7 @@ struct Demo {
let mut uninit = MaybeUninit::<Demo>::uninit();
// `&uninit.as_mut().field` would create a reference to an uninitialized `bool`,
// and thus be undefined behavior!
let f1_ptr = unsafe { ptr::addr_of_mut!((*uninit.as_mut_ptr()).field) };
let f1_ptr = unsafe { &raw mut (*uninit.as_mut_ptr()).field };
unsafe { f1_ptr.write(true); }
let init = unsafe { uninit.assume_init() };
```
Expand Down Expand Up @@ -674,8 +674,6 @@ See [this test] for an example of using this dependency.
[Function pointer]: ../types/function-pointer.md
[Function item]: ../types/function-item.md
[undefined behavior]: ../behavior-considered-undefined.md
[addr_of]: std::ptr::addr_of
[addr_of_mut]: std::ptr::addr_of_mut

[_BorrowExpression_]: #borrow-operators
[_DereferenceExpression_]: #the-dereference-operator
Expand Down
2 changes: 1 addition & 1 deletion src/items/static-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ unsafe fn bump_levels_unsafe() -> u32 {
// must still guard against concurrent access.
fn bump_levels_safe() -> u32 {
unsafe {
return atomic_add(std::ptr::addr_of_mut!(LEVELS), 1);
return atomic_add(&raw mut LEVELS, 1);
}
}
```
Expand Down
4 changes: 2 additions & 2 deletions src/type-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,9 +575,9 @@ was wrapped in a newtype `struct` with the same `align` modifier.
> println!("{}", {e.f2});
> // Or if you need a pointer, use the unaligned methods for reading and writing
> // instead of dereferencing the pointer directly.
> let ptr: *const u16 = std::ptr::addr_of!(e.f2);
> let ptr: *const u16 = &raw const e.f2;
> let value = unsafe { ptr.read_unaligned() };
> let mut_ptr: *mut u16 = std::ptr::addr_of_mut!(e.f2);
> let mut_ptr: *mut u16 = &raw mut e.f2;
> unsafe { mut_ptr.write_unaligned(3) }
> ```
Expand Down
2 changes: 1 addition & 1 deletion src/types/pointer.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ they exist to support interoperability with foreign code, and writing performanc
When comparing raw pointers they are compared by their address, rather than by what they point to.
When comparing raw pointers to [dynamically sized types] they also have their additional data compared.

Raw pointers can be created directly using [`core::ptr::addr_of!`] for `*const` pointers and [`core::ptr::addr_of_mut!`] for `*mut` pointers.
Raw pointers can be created directly using `&raw const` for `*const` pointers and `&raw mut` for `*mut` pointers.

## Smart Pointers

Expand Down

0 comments on commit b1fced7

Please sign in to comment.