Stabilize #[repr(transparent)]
Tracking issue FCP: https://github.com/rust-lang/rust/issues/43036#issuecomment-394094318 Reference PR: https://github.com/rust-lang-nursery/reference/pull/353
This commit is contained in:
parent
4367e41ea2
commit
e2aef92c19
@ -1,176 +0,0 @@
|
||||
# `repr_transparent`
|
||||
|
||||
The tracking issue for this feature is: [#43036]
|
||||
|
||||
[#43036]: https://github.com/rust-lang/rust/issues/43036
|
||||
|
||||
------------------------
|
||||
|
||||
This feature enables the `repr(transparent)` attribute on structs, which enables
|
||||
the use of newtypes without the usual ABI implications of wrapping the value in
|
||||
a struct.
|
||||
|
||||
## Background
|
||||
|
||||
It's sometimes useful to add additional type safety by introducing *newtypes*.
|
||||
For example, code that handles numeric quantities in different units such as
|
||||
millimeters, centimeters, grams, kilograms, etc. may want to use the type system
|
||||
to rule out mistakes such as adding millimeters to grams:
|
||||
|
||||
```rust
|
||||
use std::ops::Add;
|
||||
|
||||
struct Millimeters(f64);
|
||||
struct Grams(f64);
|
||||
|
||||
impl Add<Millimeters> for Millimeters {
|
||||
type Output = Millimeters;
|
||||
|
||||
fn add(self, other: Millimeters) -> Millimeters {
|
||||
Millimeters(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Likewise: impl Add<Grams> for Grams {}
|
||||
```
|
||||
|
||||
Other uses of newtypes include using `PhantomData` to add lifetimes to raw
|
||||
pointers or to implement the "phantom types" pattern. See the [PhantomData]
|
||||
documentation and [the Nomicon][nomicon-phantom] for more details.
|
||||
|
||||
The added type safety is especially useful when interacting with C or other
|
||||
languages. However, in those cases we need to ensure the newtypes we add do not
|
||||
introduce incompatibilities with the C ABI.
|
||||
|
||||
## Newtypes in FFI
|
||||
|
||||
Luckily, `repr(C)` newtypes are laid out just like the type they wrap on all
|
||||
platforms which Rust currently supports, and likely on many more. For example,
|
||||
consider this C declaration:
|
||||
|
||||
```C
|
||||
struct Object {
|
||||
double weight; //< in grams
|
||||
double height; //< in millimeters
|
||||
// ...
|
||||
}
|
||||
|
||||
void frobnicate(struct Object *);
|
||||
```
|
||||
|
||||
While using this C code from Rust, we could add `repr(C)` to the `Grams` and
|
||||
`Millimeters` newtypes introduced above and use them to add some type safety
|
||||
while staying compatible with the memory layout of `Object`:
|
||||
|
||||
```rust,no_run
|
||||
#[repr(C)]
|
||||
struct Grams(f64);
|
||||
|
||||
#[repr(C)]
|
||||
struct Millimeters(f64);
|
||||
|
||||
#[repr(C)]
|
||||
struct Object {
|
||||
weight: Grams,
|
||||
height: Millimeters,
|
||||
// ...
|
||||
}
|
||||
|
||||
extern {
|
||||
fn frobnicate(_: *mut Object);
|
||||
}
|
||||
```
|
||||
|
||||
This works even when adding some `PhantomData` fields, because they are
|
||||
zero-sized and therefore don't have to affect the memory layout.
|
||||
|
||||
However, there's more to the ABI than just memory layout: there's also the
|
||||
question of how function call arguments and return values are passed. Many
|
||||
common ABI treat a struct containing a single field differently from that field
|
||||
itself, at least when the field is a scalar (e.g., integer or float or pointer).
|
||||
|
||||
To continue the above example, suppose the C library also exposes a function
|
||||
like this:
|
||||
|
||||
```C
|
||||
double calculate_weight(double height);
|
||||
```
|
||||
|
||||
Using our newtypes on the Rust side like this will cause an ABI mismatch on many
|
||||
platforms:
|
||||
|
||||
```rust,ignore
|
||||
extern {
|
||||
fn calculate_weight(height: Millimeters) -> Grams;
|
||||
}
|
||||
```
|
||||
|
||||
For example, on x86_64 Linux, Rust will pass the argument in an integer
|
||||
register, while the C function expects the argument to be in a floating-point
|
||||
register. Likewise, the C function will return the result in a floating-point
|
||||
register while Rust will expect it in an integer register.
|
||||
|
||||
Note that this problem is not specific to floats: To give another example,
|
||||
32-bit x86 linux will pass and return `struct Foo(i32);` on the stack while
|
||||
`i32` is placed in registers.
|
||||
|
||||
## Enter `repr(transparent)`
|
||||
|
||||
So while `repr(C)` happens to do the right thing with respect to memory layout,
|
||||
it's not quite the right tool for newtypes in FFI. Instead of declaring a C
|
||||
struct, we need to communicate to the Rust compiler that our newtype is just for
|
||||
type safety on the Rust side. This is what `repr(transparent)` does.
|
||||
|
||||
The attribute can be applied to a newtype-like structs that contains a single
|
||||
field. It indicates that the newtype should be represented exactly like that
|
||||
field's type, i.e., the newtype should be ignored for ABI purpopses: not only is
|
||||
it laid out the same in memory, it is also passed identically in function calls.
|
||||
|
||||
In the above example, the ABI mismatches can be prevented by making the newtypes
|
||||
`Grams` and `Millimeters` transparent like this:
|
||||
|
||||
```rust
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Grams(f64);
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Millimeters(f64);
|
||||
```
|
||||
|
||||
In addition to that single field, any number of zero-sized fields are permitted,
|
||||
including but not limited to `PhantomData`:
|
||||
|
||||
```rust
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct Foo { /* ... */ }
|
||||
|
||||
#[repr(transparent)]
|
||||
struct FooPtrWithLifetime<'a>(*const Foo, PhantomData<&'a Foo>);
|
||||
|
||||
#[repr(transparent)]
|
||||
struct NumberWithUnit<T, U>(T, PhantomData<U>);
|
||||
|
||||
struct CustomZst;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct PtrWithCustomZst<'a> {
|
||||
ptr: FooPtrWithLifetime<'a>,
|
||||
some_marker: CustomZst,
|
||||
}
|
||||
```
|
||||
|
||||
Transparent structs can be nested: `PtrWithCustomZst` is also represented
|
||||
exactly like `*const Foo`.
|
||||
|
||||
Because `repr(transparent)` delegates all representation concerns to another
|
||||
type, it is incompatible with all other `repr(..)` attributes. It also cannot be
|
||||
applied to enums, unions, empty structs, structs whose fields are all
|
||||
zero-sized, or structs with *multiple* non-zero-sized fields.
|
||||
|
||||
[PhantomData]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html
|
||||
[nomicon-phantom]: https://doc.rust-lang.org/nomicon/phantom-data.html
|
@ -105,7 +105,7 @@
|
||||
#![feature(pin)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(ptr_offset_from)]
|
||||
#![feature(repr_transparent)]
|
||||
#![cfg_attr(stage0, feature(repr_transparent))]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(specialization)]
|
||||
#![feature(staged_api)]
|
||||
|
@ -100,7 +100,7 @@
|
||||
#![feature(optin_builtin_traits)]
|
||||
#![feature(prelude_import)]
|
||||
#![feature(repr_simd, platform_intrinsics)]
|
||||
#![feature(repr_transparent)]
|
||||
#![cfg_attr(stage0, feature(repr_transparent))]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustc_const_unstable)]
|
||||
#![feature(simd_ffi)]
|
||||
|
@ -1958,8 +1958,6 @@ representation hints.
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0692
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent, C)] // error: incompatible representation hints
|
||||
struct Grams(f32);
|
||||
```
|
||||
@ -1969,8 +1967,6 @@ another type, so adding more representation hints is contradictory. Remove
|
||||
either the `transparent` hint or the other hints, like this:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Grams(f32);
|
||||
```
|
||||
@ -1978,8 +1974,6 @@ struct Grams(f32);
|
||||
Alternatively, move the other attributes to the contained type:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(C)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
@ -1994,8 +1988,6 @@ Note that introducing another `struct` just to have a place for the other
|
||||
attributes may have unintended side effects on the representation:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Grams(f32);
|
||||
|
||||
|
@ -4581,8 +4581,6 @@ on fields that were not guaranteed to be zero-sized.
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0690
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent)]
|
||||
struct LengthWithUnit<U> { // error: transparent struct needs exactly one
|
||||
value: f32, // non-zero-sized field, but has 2
|
||||
@ -4602,8 +4600,6 @@ To combine `repr(transparent)` with type parameters, `PhantomData` may be
|
||||
useful:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[repr(transparent)]
|
||||
@ -4621,7 +4617,7 @@ field that requires non-trivial alignment.
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0691
|
||||
#![feature(repr_transparent, repr_align, attr_literals)]
|
||||
#![feature(repr_align, attr_literals)]
|
||||
|
||||
#[repr(align(32))]
|
||||
struct ForceAlign32;
|
||||
@ -4640,8 +4636,6 @@ requirement.
|
||||
Consider removing the over-aligned zero-sized field:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Wrapper(f32);
|
||||
```
|
||||
@ -4650,7 +4644,7 @@ Alternatively, `PhantomData<T>` has alignment 1 for all `T`, so you can use it
|
||||
if you need to keep the field for some reason:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent, repr_align, attr_literals)]
|
||||
#![feature(repr_align, attr_literals)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -399,9 +399,6 @@ declare_features! (
|
||||
// `extern` in paths
|
||||
(active, extern_in_paths, "1.23.0", Some(44660), None),
|
||||
|
||||
// Allows `#[repr(transparent)]` attribute on newtype structs
|
||||
(active, repr_transparent, "1.25.0", Some(43036), None),
|
||||
|
||||
// Use `?` as the Kleene "at most one" operator
|
||||
(active, macro_at_most_once_rep, "1.25.0", Some(48075), None),
|
||||
|
||||
@ -615,6 +612,8 @@ declare_features! (
|
||||
(accepted, termination_trait_test, "1.27.0", Some(48854), None),
|
||||
// The #[global_allocator] attribute
|
||||
(accepted, global_allocator, "1.28.0", Some(27389), None),
|
||||
// Allows `#[repr(transparent)]` attribute on newtype structs
|
||||
(accepted, repr_transparent, "1.28.0", Some(43036), None),
|
||||
);
|
||||
|
||||
// If you change this, please modify src/doc/unstable-book as well. You must
|
||||
@ -1595,11 +1594,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
gate_feature_post!(&self, repr_simd, attr.span,
|
||||
"SIMD types are experimental and possibly buggy");
|
||||
}
|
||||
if item.check_name("transparent") {
|
||||
gate_feature_post!(&self, repr_transparent, attr.span,
|
||||
"the `#[repr(transparent)]` attribute \
|
||||
is experimental");
|
||||
}
|
||||
if let Some((name, _)) = item.name_value_literal() {
|
||||
if name == "packed" {
|
||||
gate_feature_post!(&self, repr_packed, attr.span,
|
||||
|
@ -18,7 +18,6 @@
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -22,7 +22,6 @@
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -14,7 +14,6 @@
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -13,7 +13,6 @@
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Rgb8 { r: u8, g: u8, b: u8 }
|
||||
|
@ -11,7 +11,7 @@
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent, repr_simd)]
|
||||
#![feature(repr_simd)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -8,8 +8,6 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
// See also repr-transparent.rs
|
||||
|
||||
#[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum
|
||||
|
@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(repr_transparent, repr_align, attr_literals)]
|
||||
#![feature(repr_align, attr_literals)]
|
||||
|
||||
// See also repr-transparent.rs
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
// - repr-transparent-other-items.rs
|
||||
|
||||
#![feature(repr_align, attr_literals)]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[repr(transparent)] //~ error: the `#[repr(transparent)]` attribute is experimental
|
||||
struct Foo(u64);
|
||||
|
||||
fn main() {}
|
@ -1,11 +0,0 @@
|
||||
error[E0658]: the `#[repr(transparent)]` attribute is experimental (see issue #43036)
|
||||
--> $DIR/feature-gate-repr_transparent.rs:11:1
|
||||
|
|
||||
LL | #[repr(transparent)] //~ error: the `#[repr(transparent)]` attribute is experimental
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(repr_transparent)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
#![deny(improper_ctypes)]
|
||||
#![feature(libc, repr_transparent)]
|
||||
#![feature(libc)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user