Skip to content

Make niches part of layout and expand call ABI #153

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

Closed
wants to merge 16 commits into from
Closed
49 changes: 47 additions & 2 deletions reference/src/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,16 @@ For some more information, see [this blog post](https://www.ralfj.de/blog/2018/0

#### Layout

The *layout* of a type defines its size and alignment as well as the offsets of its subobjects (e.g. fields of structs/unions/enum/... or elements of arrays).
Moreover, the layout of a type records its *function call ABI* (or just *ABI* for short): how the type is passed *by value* across a function boundary.
The *layout* of a type defines:

* its size,
* its alignment,
* its field offsets (e.g. fields of structs, union, enums, arrays, ...)
* its [niches](#niche),
* its [call abi](#call-abi).

Note: due to discriminant-elision optimizations, niches are required to compute
the layout of, e.g., `Option<T>`, from the layout of `T`.

Note: Originally, *layout* and *representation* were treated as synonyms, and Rust language features like the `#[repr]` attribute reflect this.
In this document, *layout* and *representation* are not synonyms.
Expand All @@ -73,6 +81,43 @@ niches. For example, the "all bits uninitialized" is an invalid bit-pattern for
`&mut T`, but this bit-pattern cannot be used by layout optimizations, and is not a
niche.

#### Call ABI

The *call ABI* determines how a type is passed *by value* across a function boundary.

Note: The set of possible call ABIs is not stable. Currently, it consists of:

```rust,ignore
enum Abi {
Uninhabited,
Scalar(Scalar),
Copy link
Member

Choose a reason for hiding this comment

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

ScalarPair is missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's intentional. ScalarPair is an implementation detail useful for some optimizations that we don't want to guarantee it exists - right @eddyb ?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, it's just an aggregate that we pass around as two scalars only in Rust ABIs and within functions.

Vector {
element: Scalar,
count: u64
},
Aggregate {
// If true, the layout size of the aggregate is exact.
// Otherwise, the layout size of the aggregate is
// a lower bound.
sized: bool,
}
}
```

where a `Scalar` is either a primitive integer, floating point, or a pointer to
a sized type.

For example, the call ABI of:

* `i32` is `Scalar(I32)`,
* `#[repr(C)] struct Wrapper(i32);` is `Aggregate { sized: true }`.
* `#[repr(transparent)] struct Wrapper(i32);` is `Scalar(I32)`.

The call ABI of `repr(Rust)` types is unspecified. The following is not
guaranteed, but right now the call ABI of:

* `/*#[repr(Rust)]*/ struct Wrapper(i32);` (without `repr(transparent)`) is also
`Scalar(I32)` - only larger `struct`s are aggregates.

### TODO

Expand Down
5 changes: 3 additions & 2 deletions reference/src/layout/arrays-and-slices.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ The _offset_ of the first array element is `0`, that is, a pointer to the array
and a pointer to its first element both point to the same memory address.

The _alignment_ of array types is greater or equal to the alignment of its
element type. If the element type is `repr(C)` the layout of the array is
guaranteed to be the same as the layout of a C array with the same element type.
element type. If the element type is `repr(C)` the size and alignment of the
array is guaranteed to be the same as the size and alignment of a C array with
the same element type.

> **Note**: the type of array arguments in C function signatures, e.g., `void
> foo(T x[N])`, decays to a pointer. That is, these functions do not take arrays
Expand Down
2 changes: 1 addition & 1 deletion reference/src/layout/function-pointers.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ or the address of a function.

### Representation

The ABI and layout of `(unsafe)? (extern "ABI")? fn(Args...) -> Ret`
The size, alignment, and call ABI of `(unsafe)? (extern "ABI")? fn(Args...) -> Ret`
is exactly that of the corresponding C type --
the lack of a null value does not change this.
On common platforms, this means that `*const ()` and `fn(Args...) -> Ret` have
Expand Down
29 changes: 18 additions & 11 deletions reference/src/layout/pointers.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ and are at least one word.

### Notes

The layouts of `&T`, `&mut T`, `*const T` and `*mut T` are the same.
The size, alignment, and call ABI of `&T`, `&mut T`, `*const T` and `*mut T` are
the same.

If `T` is sized, references and pointers to `T` have a size and alignment of one
word and have therefore the same layout as C pointers.
If `T` is sized, the size, alignment, and call ABI of references and pointers to
`T` is the same as that of C pointers.

> **warning**: while the layout of references and pointers is compatible with
> the layout of C pointers, references come with a _validity_ invariant that
> does not allow them to be used when they could be `NULL`, unaligned, dangling,
> or, in the case of `&mut T`, aliasing.
> **warning**: while the size, alignment, and call ABI of references and
> pointers is compatible with the layout of C pointers, references come with a
> _validity_ invariant that does not allow them to be used when they could be
> `NULL`, unaligned, dangling, or, in the case of `&mut T`, aliasing. Since the
> niches of references and pointers are not the same, these types are not
> layout-compatible with each other.

We do not make any guarantees about the layout of
multi-trait objects `&(dyn Trait1 + Trait2)` or references to other dynamically sized types,
Expand All @@ -53,7 +56,7 @@ struct DynObject {

> **note**: In the layout of `&dyn mut Trait` the field `data` is of the type `*mut u8`.

The layout of `&[T]` is the same as that of:
The size, alignment, and call ABI of `&[T]` and `&mut [T]` are the same as that of:
```rust
#[repr(C)]
struct Slice<T> {
Expand All @@ -62,7 +65,11 @@ struct Slice<T> {
}
```

> **note**: In the layout of `&mut [T]` the field `ptr` is of the type `*mut T`.
> **note**: the field `ptr` is of the type `*mut T` for `&mut [T]`.

The layout of `&str` is the same as that of `&[u8]`, and the layout of `&mut str` is
the same as that of `&mut [u8]`.
The size, alignment, and call ABI of `&str` is the same as that of `&[u8]`, and
the layout of `&mut str` is the same as that of `&mut [u8]`.

#### Unresolved question

* Does `&str` have the same niches as `&[u8]` ?
6 changes: 3 additions & 3 deletions reference/src/layout/scalars.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ layout `#[repr()]` flags.

## `bool`

Rust's `bool` has the same layout as C17's` _Bool`, that is, its size and
alignment are implementation-defined. Any `bool` can be cast into an integer,
taking on the values 1 (`true`) or 0 (`false`).
Rust's `bool` has the same layout as C17's` _Bool` which is
implementation-defined. Any `bool` can be cast into an integer, taking on the
values 1 (`true`) or 0 (`false`).

> **Note**: on all platforms that Rust's currently supports, its size and
> alignment are 1, and its ABI class is `INTEGER` - see [Rust Layout and ABIs].
Expand Down