diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index a99ae38850..d5fa2a088f 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -100,7 +100,7 @@ r[undefined.dangling.zero-size] If the [size is 0][zero-sized], then the pointer is trivially never "dangling" (even if it is a null pointer). r[undefined.dangling.dynamic-size] -Note that dynamically sized types (such as slices and strings) point to their entire range, so it is important that the length metadata is never too large. +Note that dynamically sized types (such as slices and strings) point to their entire range, so it is important that the length [metadata] is never too large. r[undefined.dangling.alloc-limit] In particular, the dynamic size of a Rust value (as determined by `size_of_val`) must never exceed `isize::MAX`, since it is impossible for a single allocation to be larger than `isize::MAX`. @@ -142,12 +142,17 @@ r[undefined.validity.union] * For a `union`, the exact validity requirements are not decided yet. Obviously, all values that can be created entirely in safe code are valid. If the union has a [zero-sized] field, then every possible value is valid. Further details are [still being debated](https://github.com/rust-lang/unsafe-code-guidelines/issues/438). r[undefined.validity.reference-box] -* A reference or [`Box`] must be aligned and non-null, it cannot be [dangling], and it must point to a valid value (in case of dynamically sized types, using the actual dynamic type of the pointee as determined by the metadata). Note that the last point (about pointing to a valid value) remains a subject of some debate. +* A reference or [`Box`] must be aligned and non-null, it cannot be [dangling], and it must point to a valid value (in case of dynamically sized types, using the actual dynamic type of the pointee as determined by the [metadata]). Note that the last point (about pointing to a valid value) remains a subject of some debate. r[undefined.validity.wide] -* The metadata of a wide reference, [`Box`], or raw pointer must match the type of the unsized tail: +* The [metadata] of a wide reference, [`Box`], or raw pointer must match the type of the [unsized tail]: * `dyn Trait` metadata must be a pointer to a compiler-generated vtable for `Trait`. (For raw pointers, this requirement remains a subject of some debate.) - * Slice (`[T]`) metadata must be a valid `usize`. Furthermore, for wide references and [`Box`], slice metadata is invalid if it makes the total size of the pointed-to value bigger than `isize::MAX`. + * Slice (`[T]`) and `str` metadata must be a valid `usize`. + + In addition, for a wide reference or [`Box`], the metadata is invalid if it makes the total size of the pointed-to value (as determined by `size_of_val`) bigger than `isize::MAX`. + + > [!NOTE] + > This bound is on the size of the entire pointed-to value, not just its unsized tail, and it constrains `dyn Trait` metadata just as it does a slice or `str` length. A valid vtable describes an erased type no larger than `isize::MAX`, but a sized prefix can still carry the total past the limit. r[undefined.validity.valid-range] * If a type has a custom range of a valid values, then a valid value must be in that range. In the standard library, this affects [`NonNull`] and [`NonZero`]. @@ -156,7 +161,7 @@ r[undefined.validity.valid-range] > `rustc` achieves this with the unstable `rustc_layout_scalar_valid_range_*` attributes. r[undefined.validity.const-provenance] -* **In [const contexts]**: In addition to what is described above, further provenance-related requirements apply during const evaluation. Any value that holds pure integer data (the `i*`/`u*`/`f*` types as well as `bool` and `char`, enum discriminants, and slice metadata) must not carry any provenance. Any value that holds pointer data (references, raw pointers, function pointers, and `dyn Trait` metadata) must either carry no provenance, or all bytes must be fragments of the same original pointer value in the correct order. +* **In [const contexts]**: In addition to what is described above, further provenance-related requirements apply during const evaluation. Any value that holds pure integer data (the `i*`/`u*`/`f*` types as well as `bool` and `char`, enum discriminants, and slice [metadata]) must not carry any provenance. Any value that holds pointer data (references, raw pointers, function pointers, and `dyn Trait` metadata) must either carry no provenance, or all bytes must be fragments of the same original pointer value in the correct order. This implies that transmuting or otherwise reinterpreting a pointer (reference, raw pointer, or function pointer) into a non-pointer type (such as integers) is undefined behavior if the pointer had provenance. @@ -194,6 +199,7 @@ r[undefined.validity.undef] [`target_feature`]: attributes/codegen.md#the-target_feature-attribute [`UnsafeCell`]: std::cell::UnsafeCell [Rustonomicon]: ../nomicon/index.html +[metadata]: dynamic-sized.pointer-types [`NonNull`]: core::ptr::NonNull [`NonZero`]: core::num::NonZero [place expression context]: expressions.md#place-expressions-and-value-expressions @@ -203,6 +209,7 @@ r[undefined.validity.undef] [project-field]: expressions/field-expr.md [project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions [project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions +[unsized tail]: dynamic-sized.tail [unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries [const-promoted]: destructors.md#constant-promotion [lifetime-extended]: destructors.md#temporary-lifetime-extension diff --git a/src/dynamically-sized-types.md b/src/dynamically-sized-types.md index cd90adb75d..52c7ac3f43 100644 --- a/src/dynamically-sized-types.md +++ b/src/dynamically-sized-types.md @@ -8,9 +8,10 @@ r[dynamic-sized.restriction] Such types can only be used in certain cases: r[dynamic-sized.pointer-types] -* [Pointer types] to DSTs are sized but have twice the size of pointers to sized types - * Pointers to slices and `str` also store the number of elements. - * Pointers to trait objects also store a pointer to a vtable. +* [Pointer types] to DSTs are sized but have twice the size of pointers to sized types, since they also store *metadata*: + * Pointers to slices store the number of elements; pointers to `str` store the length in bytes. + * Pointers to trait objects store a pointer to a vtable. + * Pointers to a struct or tuple with an [unsized tail] store the same metadata as a pointer to that tail. r[dynamic-sized.question-sized] * DSTs can be provided as type arguments to generic type parameters having the special `?Sized` bound. They can also be used for associated type definitions when the corresponding associated type declaration has a `?Sized` bound. By default, any type parameter or associated type has a `Sized` bound, unless it is relaxed using `?Sized`. @@ -25,10 +26,18 @@ r[dynamic-sized.struct-field] > [!NOTE] > [Variables], function parameters, [const] items, and [static] items must be `Sized`. +r[dynamic-sized.tail] +The *unsized tail* of a type is the dynamically sized component that the [metadata] of a pointer to the type describes. A [slice] (`[T]`) and a [`str`] are each their own unsized tail, described by a length; a [trait object] (`dyn Trait`) is its own unsized tail, described by a pointer to a vtable. When a struct (per [dynamic-sized.struct-field]) or a tuple has an unsized last field, its unsized tail is the unsized tail of that field. A sized type has no unsized tail. + +[metadata]: dynamic-sized.pointer-types [sized]: special-types-and-traits.md#sized +[unsized tail]: dynamic-sized.tail [Slices]: types/slice.md +[slice]: types/slice.md [str]: types/str.md +[`str`]: types/str.md [trait objects]: types/trait-object.md +[trait object]: types/trait-object.md [Pointer types]: types/pointer.md [Variables]: variables.md [const]: items/constant-items.md diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 6c5e4ffbf1..c692122bf4 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -732,7 +732,7 @@ r[expr.as.pointer.sized] > ``` 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. +- 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. > [!EXAMPLE] > ```rust @@ -1244,6 +1244,7 @@ As with normal assignment expressions, compound assignment expressions always pr [logical not]: ../types/boolean.md#logical-not [logical or]: ../types/boolean.md#logical-or [logical xor]: ../types/boolean.md#logical-xor +[metadata]: dynamic-sized.pointer-types [moved from]: expr.move.movable-place [mutable]: ../expressions.md#mutability [place expression]: ../expressions.md#place-expressions-and-value-expressions