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

operator expressions: add &raw #1567

Merged
merged 3 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Member Author

Choose a reason for hiding this comment

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

I assume this will fail in CI since the operator is not stable yet. Not sure how you usually stage this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, CI will just be red until the stabilization PR hits nightly, after which point we can merge this.

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