Skip to content

Reference updates for forbidding object lifetime changing pointer casts #1951

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
34 changes: 19 additions & 15 deletions src/expressions/operator-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ reference types and `mut` or `const` in pointer types.
| Enumeration | Integer type | [Enum cast][expr.as.enum] |
| `bool` or `char` | Integer type | [Primitive to integer cast][expr.as.bool-char-as-int] |
| `u8` | `char` | [`u8` to `char` cast][expr.as.u8-as-char] |
| `*T` | `*V` [^meta-compat] | [Pointer to pointer cast][expr.as.pointer] |
| `*T` | `*V` (when [compatible][expr.as.pointer]) | [Pointer to pointer cast][expr.as.pointer] |
| `*T` where `T: Sized` | Integer type | [Pointer to address cast][expr.as.pointer-as-int] |
| Integer type | `*V` where `V: Sized` | [Address to pointer cast][expr.as.int-as-pointer] |
| `&m₁ [T; n]` | `*m₂ T` [^lessmut] | Array to pointer cast |
Expand All @@ -539,13 +539,6 @@ reference types and `mut` or `const` in pointer types.
| [Function pointer] | Integer | Function pointer to address cast |
| Closure [^no-capture] | Function pointer | Closure to function pointer cast |

[^meta-compat]: where `T` and `V` have compatible metadata:
* `V: Sized`, or
* Both slice metadata (`*[u16]` -> `*[u8]`, `*str` -> `*(u8, [u32])`), or
* Both the same trait object metadata, modulo dropping auto traits (`*dyn Debug` -> `*(u16, dyn Debug)`, `*dyn Debug + Send` -> `*dyn Debug`)
* **Note**: *adding* auto traits is only allowed if the principal trait has the auto trait as a super trait (given `trait T: Send {}`, `*dyn T` -> `*dyn T + Send` is valid, but `*dyn Debug` -> `*dyn Debug + Send` is not)
* **Note**: Generics (including lifetimes) must match (`*dyn T<'a, A>` -> `*dyn T<'b, B>` requires `'a = 'b` and `A = B`)

[^lessmut]: only when `m₁` is `mut` or `m₂` is `const`. Casting `mut` reference/pointer to
`const` pointer is allowed.

Expand Down Expand Up @@ -737,18 +730,29 @@ r[expr.as.pointer.behavior]
r[expr.as.pointer.sized]
- If `T` and `U` are both sized, the pointer is returned unchanged.

r[expr.as.pointer.discard-metadata]
- If `T` is unsized and `U` is sized, the cast discards all metadata that completes the wide pointer `T` and produces a thin pointer `U` consisting of the data part of the unsized pointer.

r[expr.as.pointer.unsized]
- If `T` and `U` are both unsized, the pointer is also returned unchanged.
In particular, the metadata is preserved exactly.
The cast can only be performed if the metadata is compatible according to the below rules:

For instance, a cast from `*const [T]` to `*const [U]` preserves the number of elements.
Note that, as a consequence, such casts do not necessarily preserve the size of the pointer's referent
(e.g., casting `*const [u16]` to `*const [u8]` will result in a raw pointer which refers to an object of half the size of the original).
The same holds for `str` and any compound type whose unsized tail is a slice type,
such as `struct Foo(i32, [u8])` or `(u64, Foo)`.
r[expr.as.pointer.unsized.slice]
- When `T` and `U` are unsized with slice metadata, they are always compatible.
The metadata of a slice is the number of elements, so casting `*[u16] -> *[u8]` is legal but will result in reducing the number of bytes by half.

r[expr.as.pointer.discard-metadata]
- If `T` is unsized and `U` is sized, the cast discards all metadata that completes the wide pointer `T` and produces a thin pointer `U` consisting of the data part of the unsized pointer.
r[expr.as.pointer.unsized.trait]
- When `T` and `U` are unsized with trait object metadata, the metadata is compatible only when all of the following holds:
1. The principal trait must be the same. (you can't cast from `dyn Foo` to `dyn Bar`)
2. Auto traits may be removed, but not added. (you can cast `dyn Foo + Send` to `dyn Foo`, but the opposite is not legal)
3. Trailing lifetimes may only be shortened. (you can cast `dyn Foo + 'long` to `dyn Foo + 'short`, but the opposite is not legal)
4. Generics (including lifetimes) and associated types must match exactly. (`*dyn T<'a, A>` -> `*dyn T<'b, B>` requires `'a = 'b` and `A = B`)

Note that [trait upcasting][coerce.unsize.trait-upcast] (including the addition of auto traits) requires a coercion and is not supported by `as` casts.

r[expr.as.pointer.unsized.compound]
- When `T` or `U` is a struct or tuple type whose last field is unsized, it has the same metadata and compatibility rules as its last field.

r[expr.assign]
## Assignment expressions
Expand Down