Skip to content

Commit 2c49bef

Browse files
authored
Merge pull request #1758 from nox/repr-transparent
Specify #[repr(transparent)]
2 parents acb75a7 + c86cfd3 commit 2c49bef

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

text/1758-repr-transparent.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
- Feature Name: `repr_transparent`
2+
- Start Date: 2016-09-26
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/1758
4+
- Rust Issue:https://github.com/rust-lang/rust/issues/43036
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Extend the existing `#[repr]` attribute on newtypes with a `transparent` option
10+
specifying that the type representation is the representation of its only field.
11+
This matters in FFI context where `struct Foo(T)` might not behave the same
12+
as `T`.
13+
14+
15+
# Motivation
16+
[motivation]: #motivation
17+
18+
On some ABIs, structures with one field aren't handled the same way as values of
19+
the same type as the single field. For example on ARM64, functions returning
20+
a structure with a single `f64` field return nothing and take a pointer to be
21+
filled with the return value, whereas functions returning a `f64` return the
22+
floating-point number directly.
23+
24+
This means that if someone wants to wrap a `f64` value in a struct tuple
25+
wrapper and use that wrapper as the return type of a FFI function that actually
26+
returns a bare `f64`, the calls to this function will be compiled incorrectly
27+
by Rust and the execution of the program will segfault.
28+
29+
This also means that `UnsafeCell<T>` cannot be soundly used in place of a
30+
bare `T` in FFI context, which might be necessary to signal to the Rust side
31+
of things that this `T` value may unexpectedly be mutated.
32+
33+
```c
34+
// The value is returned directly in a floating-point register on ARM64.
35+
double do_something_and_return_a_double(void);
36+
```
37+
38+
```rust
39+
mod bogus {
40+
#[repr(C)]
41+
struct FancyWrapper(f64);
42+
43+
extern {
44+
// Incorrect: the wrapped value on ARM64 is indirectly returned and the
45+
// function takes a pointer to where the return value must be stored.
46+
fn do_something_and_return_a_double() -> FancyWrapper;
47+
}
48+
}
49+
50+
mod correct {
51+
#[repr(transparent)]
52+
struct FancyWrapper(f64);
53+
54+
extern {
55+
// Correct: FancyWrapper is handled exactly the same as f64 on all
56+
// platforms.
57+
fn do_something_and_return_a_double() -> FancyWrapper;
58+
}
59+
}
60+
```
61+
62+
Given this attribute delegates all representation concerns, no other `repr`
63+
attribute should be present on the type. This means the following definitions
64+
are illegal:
65+
66+
```rust
67+
#[repr(transparent, align = "128")]
68+
struct BogusAlign(f64);
69+
70+
#[repr(transparent, packed)]
71+
struct BogusPacked(f64);
72+
```
73+
74+
# Detailed design
75+
[design]: #detailed-design
76+
77+
The `#[repr]` attribute on newtypes will be extended to include a form such as:
78+
79+
```rust
80+
#[repr(transparent)]
81+
struct TransparentNewtype(f64);
82+
```
83+
84+
This structure will still have the same representation as a raw `f64` value.
85+
86+
Syntactically, the `repr` meta list will be extended to accept a meta item
87+
with the name "transparent". This attribute can be placed on newtypes,
88+
i.e. structures (and structure tuples) with a single field, and on structures
89+
that are logically equivalent to a newtype, i.e. structures with multiple fields
90+
where only a single one of them has a non-zero size.
91+
92+
Some examples of `#[repr(transparent)]` are:
93+
94+
```rust
95+
// Transparent struct tuple.
96+
#[repr(transparent)]
97+
struct TransparentStructTuple(i32);
98+
99+
// Transparent structure.
100+
#[repr(transparent)]
101+
struct TransparentStructure { only_field: f64 }
102+
103+
// Transparent struct wrapper with a marker.
104+
#[repr(transparent)]
105+
struct TransparentWrapper<T> {
106+
only_non_zero_sized_field: f64,
107+
marker: PhantomData<T>,
108+
}
109+
```
110+
111+
This new representation is mostly useful when the structure it is put on must be
112+
used in FFI context as a wrapper to the underlying type without actually being
113+
affected by any ABI semantics.
114+
115+
It is also useful for `AtomicUsize`-like types, which [RFC 1649] states should
116+
have the same representation as their underlying types.
117+
118+
[RFC 1649]: https://github.com/rust-lang/rfcs/pull/1649
119+
120+
This new representation cannot be used with any other representation attribute
121+
but alignment, to be able to specify a transparent wrapper with additional
122+
alignment constraints:
123+
124+
```rust
125+
#[repr(transparent, align = "128")]
126+
struct OverAligned(f64); // Behaves as a bare f64 with 128 bits alignment.
127+
128+
#[repr(C, transparent)]
129+
struct BogusRepr(f64); // Nonsensical, repr cannot be C and transparent.
130+
```
131+
132+
As a matter of optimisation, eligible `#[repr(Rust)]` structs behave as if
133+
they were `#[repr(transparent)]` but as an implementation detail that can't be
134+
relied upon by users.
135+
136+
```rust
137+
struct ImplicitlyTransparentWrapper(f64);
138+
139+
#[repr(C)]
140+
struct BogusRepr {
141+
// While ImplicitlyTransparentWrapper implicitly has the same representation
142+
// as f64, this will fail to compile because ImplicitlyTransparentWrapper
143+
// has no explicit transparent or C representation.
144+
wrapper: ImplicitlyTransparentWrapper,
145+
}
146+
```
147+
148+
The representation of a transparent wrapper is the representation of its
149+
only non-zero-sized field, transitively:
150+
151+
```rust
152+
#[repr(transparent)]
153+
struct Transparent<T>(T);
154+
155+
#[repr(transparent)]
156+
struct F64(f64);
157+
158+
#[repr(C)]
159+
struct C(usize);
160+
161+
type TransparentF64 = Transparent<F64>; // Behaves as f64.
162+
163+
type TransparentString = Transparent<String>; // Representation is Rust.
164+
165+
type TransparentC = Transparent<C>; // Representation is C.
166+
167+
type TransparentTransparentC = Transparent<Transparent<C>>; // Transitively C.
168+
```
169+
170+
Coercions and casting between the transparent wrapper and its non-zero-sized
171+
types are forbidden.
172+
173+
# Drawbacks
174+
[drawbacks]: #drawbacks
175+
176+
None.
177+
178+
# Alternatives
179+
[alternatives]: #alternatives
180+
181+
The only alternative to such a construct for FFI purposes is to use the exact
182+
same types as specified in the C header (or wherever the FFI types come from)
183+
and to make additional wrappers for them in Rust. This does not help if a
184+
field using interior mutability (i.e. uses `UnsafeCell<T>`) has to be passed
185+
to the FFI side, so this alternative does not actually cover all the uses cases
186+
allowed by `#[repr(transparent)]`.
187+
188+
# Unresolved questions
189+
[unresolved]: #unresolved-questions
190+
191+
* None

0 commit comments

Comments
 (0)