Auto merge of #47158 - rkruppe:repr-transparent, r=eddyb
Implement repr(transparent) r? @eddyb for the functional changes. The bulk of the PR is error messages and docs, might be good to have a doc person look over those. cc #43036 cc @nox
This commit is contained in:
commit
b887317da6
176
src/doc/unstable-book/src/language-features/repr-transparent.md
Normal file
176
src/doc/unstable-book/src/language-features/repr-transparent.md
Normal file
@ -0,0 +1,176 @@
|
||||
# `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
|
@ -2003,7 +2003,69 @@ that refers to itself. That is permitting, since the closure would be
|
||||
invoking itself via a virtual call, and hence does not directly
|
||||
reference its own *type*.
|
||||
|
||||
"##, }
|
||||
"##,
|
||||
|
||||
E0692: r##"
|
||||
A `repr(transparent)` type was also annotated with other, incompatible
|
||||
representation hints.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0692
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent, C)] // error: incompatible representation hints
|
||||
struct Grams(f32);
|
||||
```
|
||||
|
||||
A type annotated as `repr(transparent)` delegates all representation concerns to
|
||||
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);
|
||||
```
|
||||
|
||||
Alternatively, move the other attributes to the contained type:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(C)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
// ...
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct FooWrapper(Foo);
|
||||
```
|
||||
|
||||
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);
|
||||
|
||||
#[repr(C)]
|
||||
struct Float(f32);
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Grams2(Float); // this is not equivalent to `Grams` above
|
||||
```
|
||||
|
||||
Here, `Grams2` is a not equivalent to `Grams` -- the former transparently wraps
|
||||
a (non-transparent) struct containing a single float, while `Grams` is a
|
||||
transparent wrapper around a float. This can make a difference for the ABI.
|
||||
"##,
|
||||
|
||||
}
|
||||
|
||||
|
||||
register_diagnostics! {
|
||||
|
@ -92,6 +92,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
|
||||
let mut int_reprs = 0;
|
||||
let mut is_c = false;
|
||||
let mut is_simd = false;
|
||||
let mut is_transparent = false;
|
||||
|
||||
for hint in &hints {
|
||||
let name = if let Some(name) = hint.name() {
|
||||
@ -137,6 +138,14 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
|
||||
continue
|
||||
}
|
||||
}
|
||||
"transparent" => {
|
||||
is_transparent = true;
|
||||
if target != Target::Struct {
|
||||
("a", "struct")
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
"i8" | "u8" | "i16" | "u16" |
|
||||
"i32" | "u32" | "i64" | "u64" |
|
||||
"isize" | "usize" => {
|
||||
@ -155,14 +164,22 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
|
||||
.emit();
|
||||
}
|
||||
|
||||
// Just point at all repr hints if there are any incompatibilities.
|
||||
// This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
|
||||
let hint_spans = hints.iter().map(|hint| hint.span);
|
||||
|
||||
// Error on repr(transparent, <anything else>).
|
||||
if is_transparent && hints.len() > 1 {
|
||||
let hint_spans: Vec<_> = hint_spans.clone().collect();
|
||||
span_err!(self.tcx.sess, hint_spans, E0692,
|
||||
"transparent struct cannot have other repr hints");
|
||||
}
|
||||
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
|
||||
if (int_reprs > 1)
|
||||
|| (is_simd && is_c)
|
||||
|| (int_reprs == 1 && is_c && is_c_like_enum(item)) {
|
||||
// Just point at all repr hints. This is not ideal, but tracking
|
||||
// precisely which ones are at fault is a huge hassle.
|
||||
let spans: Vec<_> = hints.iter().map(|hint| hint.span).collect();
|
||||
span_warn!(self.tcx.sess, spans, E0566,
|
||||
let hint_spans: Vec<_> = hint_spans.collect();
|
||||
span_warn!(self.tcx.sess, hint_spans, E0566,
|
||||
"conflicting representation hints");
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@
|
||||
#![feature(box_syntax)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(copy_closures, clone_closures)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(drain_filter)]
|
||||
#![feature(dyn_trait)]
|
||||
|
@ -1499,8 +1499,9 @@ bitflags! {
|
||||
const IS_C = 1 << 0;
|
||||
const IS_PACKED = 1 << 1;
|
||||
const IS_SIMD = 1 << 2;
|
||||
const IS_TRANSPARENT = 1 << 3;
|
||||
// Internal only for now. If true, don't reorder fields.
|
||||
const IS_LINEAR = 1 << 3;
|
||||
const IS_LINEAR = 1 << 4;
|
||||
|
||||
// Any of these flags being set prevent field reordering optimisation.
|
||||
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
|
||||
@ -1540,6 +1541,7 @@ impl ReprOptions {
|
||||
flags.insert(match r {
|
||||
attr::ReprC => ReprFlags::IS_C,
|
||||
attr::ReprPacked => ReprFlags::IS_PACKED,
|
||||
attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
|
||||
attr::ReprSimd => ReprFlags::IS_SIMD,
|
||||
attr::ReprInt(i) => {
|
||||
size = Some(i);
|
||||
@ -1567,6 +1569,8 @@ impl ReprOptions {
|
||||
#[inline]
|
||||
pub fn packed(&self) -> bool { self.flags.contains(ReprFlags::IS_PACKED) }
|
||||
#[inline]
|
||||
pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
|
||||
#[inline]
|
||||
pub fn linear(&self) -> bool { self.flags.contains(ReprFlags::IS_LINEAR) }
|
||||
|
||||
pub fn discr_type(&self) -> attr::IntType {
|
||||
|
@ -416,7 +416,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
}
|
||||
match def.adt_kind() {
|
||||
AdtKind::Struct => {
|
||||
if !def.repr.c() {
|
||||
if !def.repr.c() && !def.repr.transparent() {
|
||||
return FfiUnsafe("found struct without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(C)] attribute to the type");
|
||||
@ -427,13 +427,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
adding a member to this struct");
|
||||
}
|
||||
|
||||
// We can't completely trust repr(C) markings; make sure the
|
||||
// fields are actually safe.
|
||||
// We can't completely trust repr(C) and repr(transparent) markings;
|
||||
// make sure the fields are actually safe.
|
||||
let mut all_phantom = true;
|
||||
for field in &def.non_enum_variant().fields {
|
||||
let field_ty = cx.fully_normalize_associated_types_in(
|
||||
&field.ty(cx, substs)
|
||||
);
|
||||
// repr(transparent) types are allowed to have arbitrary ZSTs, not just
|
||||
// PhantomData -- skip checking all ZST fields
|
||||
if def.repr.transparent() {
|
||||
let is_zst = (cx, cx.param_env(field.did))
|
||||
.layout_of(field_ty)
|
||||
.map(|layout| layout.is_zst())
|
||||
.unwrap_or(false);
|
||||
if is_zst {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let r = self.check_type_for_ffi(cache, field_ty);
|
||||
match r {
|
||||
FfiSafe => {
|
||||
|
@ -101,6 +101,7 @@ use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::maps::Providers;
|
||||
use rustc::ty::util::{Representability, IntTypeExt};
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use errors::{DiagnosticBuilder, DiagnosticId};
|
||||
use require_c_abi_if_variadic;
|
||||
use session::{CompileIncomplete, config, Session};
|
||||
@ -1104,6 +1105,7 @@ fn check_struct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
check_simd(tcx, span, def_id);
|
||||
}
|
||||
|
||||
check_transparent(tcx, span, def_id);
|
||||
check_packed(tcx, span, def_id);
|
||||
}
|
||||
|
||||
@ -1517,6 +1519,42 @@ fn check_packed_inner<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
false
|
||||
}
|
||||
|
||||
fn check_transparent<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, def_id: DefId) {
|
||||
let adt = tcx.adt_def(def_id);
|
||||
if !adt.repr.transparent() {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each field, figure out if it's known to be a ZST and align(1)
|
||||
let field_infos: Vec<_> = adt.non_enum_variant().fields.iter().map(|field| {
|
||||
let ty = field.ty(tcx, Substs::identity_for_item(tcx, field.did));
|
||||
let param_env = tcx.param_env(field.did);
|
||||
let layout = (tcx, param_env).layout_of(ty);
|
||||
// We are currently checking the type this field came from, so it must be local
|
||||
let span = tcx.hir.span_if_local(field.did).unwrap();
|
||||
let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false);
|
||||
let align1 = layout.map(|layout| layout.align.abi() == 1).unwrap_or(false);
|
||||
(span, zst, align1)
|
||||
}).collect();
|
||||
|
||||
let non_zst_fields = field_infos.iter().filter(|(_span, zst, _align1)| !*zst);
|
||||
let non_zst_count = non_zst_fields.clone().count();
|
||||
if non_zst_count != 1 {
|
||||
let field_spans: Vec<_> = non_zst_fields.map(|(span, _zst, _align1)| *span).collect();
|
||||
struct_span_err!(tcx.sess, sp, E0690,
|
||||
"transparent struct needs exactly one non-zero-sized field, but has {}",
|
||||
non_zst_count)
|
||||
.span_note(field_spans, "non-zero-sized field")
|
||||
.emit();
|
||||
}
|
||||
for &(span, zst, align1) in &field_infos {
|
||||
if zst && !align1 {
|
||||
span_err!(tcx.sess, span, E0691,
|
||||
"zero-sized field in transparent struct has alignment larger than 1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
sp: Span,
|
||||
|
@ -4605,6 +4605,99 @@ let _ = x.powi(2);
|
||||
let _ = (2.0 as f32).powi(2);
|
||||
```
|
||||
"##,
|
||||
|
||||
E0690: r##"
|
||||
A struct with the representation hint `repr(transparent)` had zero or more than
|
||||
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
|
||||
unit: U,
|
||||
}
|
||||
```
|
||||
|
||||
Because transparent structs are represented exactly like one of their fields at
|
||||
run time, said field must be uniquely determined. If there is no field, or if
|
||||
there are multiple fields, it is not clear how the struct should be represented.
|
||||
Note that fields of zero-typed types (e.g., `PhantomData`) can also exist
|
||||
alongside the field that contains the actual data, they do not count for this
|
||||
error. When generic types are involved (as in the above example), an error is
|
||||
reported because the type parameter could be non-zero-sized.
|
||||
|
||||
To combine `repr(transparent)` with type parameters, `PhantomData` may be
|
||||
useful:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct LengthWithUnit<U> {
|
||||
value: f32,
|
||||
unit: PhantomData<U>,
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0691: r##"
|
||||
A struct with the `repr(transparent)` representation hint contains a zero-sized
|
||||
field that requires non-trivial alignment.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0691
|
||||
#![feature(repr_transparent, repr_align, attr_literals)]
|
||||
|
||||
#[repr(align(32))]
|
||||
struct ForceAlign32;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent
|
||||
// struct has alignment larger than 1
|
||||
```
|
||||
|
||||
A transparent struct is supposed to be represented exactly like the piece of
|
||||
data it contains. Zero-sized fields with different alignment requirements
|
||||
potentially conflict with this property. In the example above, `Wrapper` would
|
||||
have to be aligned to 32 bytes even though `f32` has a smaller alignment
|
||||
requirement.
|
||||
|
||||
Consider removing the over-aligned zero-sized field:
|
||||
|
||||
```
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Wrapper(f32);
|
||||
```
|
||||
|
||||
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)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[repr(align(32))]
|
||||
struct ForceAlign32;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Wrapper(f32, PhantomData<ForceAlign32>);
|
||||
```
|
||||
|
||||
Note that empty arrays `[T; 0]` have the same alignment requirement as the
|
||||
element type `T`. Also note that the error is conservatively reported even when
|
||||
the alignment of the zero-sized type is less than or equal to the data field's
|
||||
alignment.
|
||||
"##,
|
||||
}
|
||||
|
||||
register_diagnostics! {
|
||||
|
@ -77,6 +77,7 @@ This API is completely unstable and subject to change.
|
||||
#![feature(box_syntax)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
#![feature(copy_closures, clone_closures)]
|
||||
#![feature(from_ref)]
|
||||
#![feature(match_default_bindings)]
|
||||
#![feature(never_type)]
|
||||
|
@ -992,7 +992,8 @@ pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute],
|
||||
/// Valid repr contents: any of the primitive integral type names (see
|
||||
/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
|
||||
/// the same discriminant size that the corresponding C enum would or C
|
||||
/// structure layout, and `packed` to remove padding.
|
||||
/// structure layout, `packed` to remove padding, and `transparent` to elegate representation
|
||||
/// concerns to the only non-ZST field.
|
||||
pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> {
|
||||
let mut acc = Vec::new();
|
||||
if attr.path == "repr" {
|
||||
@ -1011,6 +1012,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
|
||||
"C" => Some(ReprC),
|
||||
"packed" => Some(ReprPacked),
|
||||
"simd" => Some(ReprSimd),
|
||||
"transparent" => Some(ReprTransparent),
|
||||
_ => match int_type_of_word(word) {
|
||||
Some(ity) => Some(ReprInt(ity)),
|
||||
None => {
|
||||
@ -1082,6 +1084,7 @@ pub enum ReprAttr {
|
||||
ReprC,
|
||||
ReprPacked,
|
||||
ReprSimd,
|
||||
ReprTransparent,
|
||||
ReprAlign(u32),
|
||||
}
|
||||
|
||||
|
@ -452,6 +452,9 @@ declare_features! (
|
||||
|
||||
// `extern` in paths
|
||||
(active, extern_in_paths, "1.23.0", Some(44660)),
|
||||
|
||||
// Allows `#[repr(transparent)]` attribute on newtype structs
|
||||
(active, repr_transparent, "1.25.0", Some(43036)),
|
||||
);
|
||||
|
||||
declare_features! (
|
||||
@ -1524,6 +1527,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
"the struct `#[repr(align(u16))]` attribute \
|
||||
is experimental");
|
||||
}
|
||||
if item.check_name("transparent") {
|
||||
gate_feature_post!(&self, repr_transparent, attr.span,
|
||||
"the `#[repr(transparent)]` attribute \
|
||||
is experimental");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -830,7 +830,9 @@ fn find_repr_type_name(diagnostic: &Handler, type_attrs: &[ast::Attribute]) -> &
|
||||
for a in type_attrs {
|
||||
for r in &attr::find_repr_attrs(diagnostic, a) {
|
||||
repr_type_name = match *r {
|
||||
attr::ReprPacked | attr::ReprSimd | attr::ReprAlign(_) => continue,
|
||||
attr::ReprPacked | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
|
||||
continue,
|
||||
|
||||
attr::ReprC => "i32",
|
||||
|
||||
attr::ReprInt(attr::SignedInt(ast::IntTy::Isize)) => "isize",
|
||||
|
53
src/test/codegen/repr-transparent-aggregates-1.rs
Normal file
53
src/test/codegen/repr-transparent-aggregates-1.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
// ignore-arm
|
||||
// ignore-mips
|
||||
// ignore-mips64
|
||||
// ignore-powerpc
|
||||
// ignore-powerpc64
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Big([u32; 16]);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BigW(Big);
|
||||
|
||||
// CHECK: define void @test_Big(%Big* [[BIG_RET_ATTRS:.*]], %Big* [[BIG_ARG_ATTRS:.*]])
|
||||
#[no_mangle]
|
||||
pub extern fn test_Big(_: Big) -> Big { loop {} }
|
||||
|
||||
// CHECK: define void @test_BigW(%BigW* [[BIG_RET_ATTRS]], %BigW* [[BIG_ARG_ATTRS]])
|
||||
#[no_mangle]
|
||||
pub extern fn test_BigW(_: BigW) -> BigW { loop {} }
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
pub union BigU {
|
||||
foo: [u32; 16],
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BigUw(BigU);
|
||||
|
||||
// CHECK: define void @test_BigU(%BigU* [[BIGU_RET_ATTRS:.*]], %BigU* [[BIGU_ARG_ATTRS:.*]])
|
||||
#[no_mangle]
|
||||
pub extern fn test_BigU(_: BigU) -> BigU { loop {} }
|
||||
|
||||
// CHECK: define void @test_BigUw(%BigUw* [[BIGU_RET_ATTRS]], %BigUw* [[BIGU_ARG_ATTRS]])
|
||||
#[no_mangle]
|
||||
pub extern fn test_BigUw(_: BigUw) -> BigUw { loop {} }
|
54
src/test/codegen/repr-transparent-aggregates-2.rs
Normal file
54
src/test/codegen/repr-transparent-aggregates-2.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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.
|
||||
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
// ignore-aarch64
|
||||
// ignore-asmjs
|
||||
// ignore-s390x
|
||||
// ignore-wasm
|
||||
// ignore-x86
|
||||
// ignore-x86_64
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Big([u32; 16]);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BigW(Big);
|
||||
|
||||
// CHECK: define void @test_Big(%Big* [[BIG_RET_ATTRS:.*]], [16 x i32]
|
||||
#[no_mangle]
|
||||
pub extern fn test_Big(_: Big) -> Big { loop {} }
|
||||
|
||||
// CHECK: define void @test_BigW(%BigW* [[BIG_RET_ATTRS]], [16 x i32]
|
||||
#[no_mangle]
|
||||
pub extern fn test_BigW(_: BigW) -> BigW { loop {} }
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
pub union BigU {
|
||||
foo: [u32; 16],
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BigUw(BigU);
|
||||
|
||||
// CHECK: define void @test_BigU(%BigU* [[BIGU_RET_ATTRS:.*]], [16 x i32]
|
||||
#[no_mangle]
|
||||
pub extern fn test_BigU(_: BigU) -> BigU { loop {} }
|
||||
|
||||
// CHECK: define void @test_BigUw(%BigUw* [[BIGU_RET_ATTRS]], [16 x i32]
|
||||
#[no_mangle]
|
||||
pub extern fn test_BigUw(_: BigUw) -> BigUw { loop {} }
|
177
src/test/codegen/repr-transparent.rs
Normal file
177
src/test/codegen/repr-transparent.rs
Normal file
@ -0,0 +1,177 @@
|
||||
// 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.
|
||||
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_transparent, repr_simd)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct Zst1;
|
||||
pub struct Zst2(());
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct F32(f32);
|
||||
|
||||
// CHECK: define float @test_F32(float %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_F32(_: F32) -> F32 { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Ptr(*mut u8);
|
||||
|
||||
// CHECK: define i8* @test_Ptr(i8* %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Ptr(_: Ptr) -> Ptr { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct WithZst(u64, Zst1);
|
||||
|
||||
// CHECK: define i64 @test_WithZst(i64 %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_WithZst(_: WithZst) -> WithZst { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct WithZeroSizedArray(*const f32, [i8; 0]);
|
||||
|
||||
// Apparently we use i32* when newtype-unwrapping f32 pointers. Whatever.
|
||||
// CHECK: define i32* @test_WithZeroSizedArray(i32* %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Generic<T>(T);
|
||||
|
||||
// CHECK: define double @test_Generic(double %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Generic(_: Generic<f64>) -> Generic<f64> { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct GenericPlusZst<T>(T, Zst2);
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum Bool { True, False, FileNotFound }
|
||||
|
||||
// CHECK: define{{( zeroext)?}} i8 @test_Gpz(i8{{( zeroext)?}} %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Gpz(_: GenericPlusZst<Bool>) -> GenericPlusZst<Bool> { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LifetimePhantom<'a, T: 'a>(*const T, PhantomData<&'a T>);
|
||||
|
||||
// CHECK: define i16* @test_LifetimePhantom(i16* %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_LifetimePhantom(_: LifetimePhantom<i16>) -> LifetimePhantom<i16> { loop {} }
|
||||
|
||||
// This works despite current alignment resrictions because PhantomData is always align(1)
|
||||
#[repr(transparent)]
|
||||
pub struct UnitPhantom<T, U> { val: T, unit: PhantomData<U> }
|
||||
|
||||
pub struct Px;
|
||||
|
||||
// CHECK: define float @test_UnitPhantom(float %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_UnitPhantom(_: UnitPhantom<f32, Px>) -> UnitPhantom<f32, Px> { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TwoZsts(Zst1, i8, Zst2);
|
||||
|
||||
// CHECK: define{{( signext)?}} i8 @test_TwoZsts(i8{{( signext)?}} %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_TwoZsts(_: TwoZsts) -> TwoZsts { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Nested1(Zst2, Generic<f64>);
|
||||
|
||||
// CHECK: define double @test_Nested1(double %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Nested1(_: Nested1) -> Nested1 { loop {} }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Nested2(Nested1, Zst1);
|
||||
|
||||
// CHECK: define double @test_Nested2(double %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Nested2(_: Nested2) -> Nested2 { loop {} }
|
||||
|
||||
#[repr(simd)]
|
||||
struct f32x4(f32, f32, f32, f32);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Vector(f32x4);
|
||||
|
||||
// CHECK: define <4 x float> @test_Vector(<4 x float> %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Vector(_: Vector) -> Vector { loop {} }
|
||||
|
||||
trait Mirror { type It: ?Sized; }
|
||||
impl<T: ?Sized> Mirror for T { type It = Self; }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct StructWithProjection(<f32 as Mirror>::It);
|
||||
|
||||
// CHECK: define float @test_Projection(float %arg0)
|
||||
#[no_mangle]
|
||||
pub extern fn test_Projection(_: StructWithProjection) -> StructWithProjection { loop {} }
|
||||
|
||||
|
||||
// The rest of this file tests newtypes around small aggregates on an ABI where small aggregates are
|
||||
// packed into one register. This is ABI-dependent, so instead we focus on one ABI and supply a
|
||||
// dummy definition for other ABIs to keep FileCheck happy.
|
||||
//
|
||||
// Bigger aggregates are tested in separate files called repr-transparent-aggregate-*.rs because
|
||||
// there, the expected LLVM IR function signatures vary so much that it's not reasonably possible to
|
||||
// cover all of them with a single CHECK line. Instead we group ABIs by the general "shape" of the
|
||||
// signature and have a separate test file for each bin.
|
||||
//
|
||||
// PS: You may be wondering why we don't just compare the return types and argument types for
|
||||
// equality with FileCheck regex captures. Well, rustc doesn't perform newtype unwrapping on
|
||||
// newtypes containing aggregates. This is OK on all ABIs we support, but because LLVM has not
|
||||
// gotten rid of pointee types yet, the IR function signature will be syntactically different (%Foo*
|
||||
// vs %FooWrapper*).
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Rgb8 { r: u8, g: u8, b: u8 }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Rgb8Wrap(Rgb8);
|
||||
|
||||
// NB: closing parenthesis is missing because sometimes the argument has a name and sometimes not
|
||||
// CHECK: define i32 @test_Rgb8Wrap(i32
|
||||
#[no_mangle]
|
||||
#[cfg(all(target_arch="x86_64", target_os="linux"))]
|
||||
pub extern fn test_Rgb8Wrap(_: Rgb8Wrap) -> Rgb8Wrap { loop {} }
|
||||
|
||||
#[cfg(not(all(target_arch="x86_64", target_os="linux")))]
|
||||
#[no_mangle]
|
||||
pub extern fn test_Rgb8Wrap(_: u32) -> u32 { loop {} }
|
||||
|
||||
// Same as with the small struct above: ABI-dependent, we only test the interesting case
|
||||
// (ABIs that pack the aggregate into a scalar) and stub it out on other ABIs
|
||||
|
||||
#[repr(C)]
|
||||
pub union FloatBits {
|
||||
float: f32,
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct SmallUnion(FloatBits);
|
||||
|
||||
// NB: closing parenthesis is missing because sometimes the argument has a name and sometimes not
|
||||
// CHECK: define i32 @test_SmallUnion(i32
|
||||
#[no_mangle]
|
||||
#[cfg(all(target_arch="x86_64", target_os="linux"))]
|
||||
pub extern fn test_SmallUnion(_: SmallUnion) -> SmallUnion { loop {} }
|
||||
|
||||
#[cfg(not(all(target_arch="x86_64", target_os="linux")))]
|
||||
#[no_mangle]
|
||||
pub extern fn test_SmallUnion(_: u32) -> u32 { loop {} }
|
@ -9,10 +9,12 @@
|
||||
// except according to those terms.
|
||||
|
||||
#![deny(improper_ctypes)]
|
||||
#![feature(libc, i128_type)]
|
||||
#![feature(libc, i128_type, repr_transparent)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
trait Mirror { type It: ?Sized; }
|
||||
impl<T: ?Sized> Mirror for T { type It = Self; }
|
||||
#[repr(C)]
|
||||
@ -28,6 +30,22 @@ pub type RustFn = fn();
|
||||
pub type RustBadRet = extern fn() -> Box<u32>;
|
||||
pub type CVoidRet = ();
|
||||
pub struct Foo;
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentI128(i128);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentStr(&'static str);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentBadFn(RustBadRet);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentInt(u32);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentRef<'a>(&'a TransparentInt);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentUnit<U>(f32, PhantomData<U>);
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentCustomZst(i32, ZeroSize);
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
|
||||
@ -51,6 +69,9 @@ extern {
|
||||
pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust
|
||||
pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust
|
||||
pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without
|
||||
pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128`
|
||||
pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str`
|
||||
pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without
|
||||
|
||||
pub fn good3(fptr: Option<extern fn()>);
|
||||
pub fn good4(aptr: &[u8; 4 as usize]);
|
||||
@ -62,6 +83,11 @@ extern {
|
||||
pub fn good10() -> CVoidRet;
|
||||
pub fn good11(size: isize);
|
||||
pub fn good12(size: usize);
|
||||
pub fn good13(n: TransparentInt);
|
||||
pub fn good14(p: TransparentRef);
|
||||
pub fn good15(p: TransparentLifetime);
|
||||
pub fn good16(p: TransparentUnit<ZeroSize>);
|
||||
pub fn good17(p: TransparentCustomZst);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
40
src/test/compile-fail/repr-transparent-other-items.rs
Normal file
40
src/test/compile-fail/repr-transparent-other-items.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
// See also repr-transparent.rs
|
||||
|
||||
#[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum
|
||||
enum Void {} //~| ERROR should be applied to struct
|
||||
|
||||
#[repr(transparent)] //~ ERROR should be applied to struct
|
||||
enum FieldlessEnum {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[repr(transparent)] //~ ERROR should be applied to struct
|
||||
enum Enum {
|
||||
Foo(String),
|
||||
Bar(u32),
|
||||
}
|
||||
|
||||
#[repr(transparent)] //~ ERROR should be applied to struct
|
||||
union Foo {
|
||||
u: u32,
|
||||
s: i32
|
||||
}
|
||||
|
||||
#[repr(transparent)] //~ ERROR should be applied to struct
|
||||
fn cant_repr_this() {}
|
||||
|
||||
#[repr(transparent)] //~ ERROR should be applied to struct
|
||||
static CANT_REPR_THIS: u32 = 0;
|
28
src/test/compile-fail/repr-transparent-other-reprs.rs
Normal file
28
src/test/compile-fail/repr-transparent-other-reprs.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
#![feature(repr_transparent, repr_align, attr_literals)]
|
||||
|
||||
// See also repr-transparent.rs
|
||||
|
||||
#[repr(transparent, C)] //~ ERROR cannot have other repr
|
||||
struct TransparentPlusC {
|
||||
ptr: *const u8
|
||||
}
|
||||
|
||||
#[repr(transparent, packed)] //~ ERROR cannot have other repr
|
||||
struct TransparentPlusPacked(*const u8);
|
||||
|
||||
#[repr(transparent, align(2))] //~ ERROR cannot have other repr
|
||||
struct TransparentPlusAlign(u8);
|
||||
|
||||
#[repr(transparent)] //~ ERROR cannot have other repr
|
||||
#[repr(C)]
|
||||
struct SeparateAttributes(*mut u8);
|
51
src/test/compile-fail/repr-transparent.rs
Normal file
51
src/test/compile-fail/repr-transparent.rs
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
// This file tests repr(transparent)-related errors reported during typeck. Other errors
|
||||
// that are reported earlier and therefore preempt these are tested in:
|
||||
// - repr-transparent-other-reprs.rs
|
||||
// - repr-transparent-other-items.rs
|
||||
|
||||
#![feature(repr_align, attr_literals)]
|
||||
#![feature(repr_transparent)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[repr(transparent)]
|
||||
struct NoFields; //~ ERROR needs exactly one non-zero-sized field
|
||||
|
||||
#[repr(transparent)]
|
||||
struct ContainsOnlyZst(()); //~ ERROR needs exactly one non-zero-sized field
|
||||
|
||||
#[repr(transparent)]
|
||||
struct ContainsOnlyZstArray([bool; 0]); //~ ERROR needs exactly one non-zero-sized field
|
||||
|
||||
#[repr(transparent)]
|
||||
struct ContainsMultipleZst(PhantomData<*const i32>, NoFields);
|
||||
//~^ ERROR needs exactly one non-zero-sized field
|
||||
|
||||
#[repr(transparent)]
|
||||
struct MultipleNonZst(u8, u8); //~ ERROR needs exactly one non-zero-sized field
|
||||
|
||||
trait Mirror { type It: ?Sized; }
|
||||
impl<T: ?Sized> Mirror for T { type It = Self; }
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct StructWithProjection(f32, <f32 as Mirror>::It);
|
||||
//~^ ERROR needs exactly one non-zero-sized field
|
||||
|
||||
#[repr(transparent)]
|
||||
struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1
|
||||
|
||||
#[repr(align(32))]
|
||||
struct ZstAlign32<T>(PhantomData<T>);
|
||||
|
||||
#[repr(transparent)]
|
||||
struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR alignment larger than 1
|
14
src/test/ui/feature-gate-repr_transparent.rs
Normal file
14
src/test/ui/feature-gate-repr_transparent.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// 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() {}
|
10
src/test/ui/feature-gate-repr_transparent.stderr
Normal file
10
src/test/ui/feature-gate-repr_transparent.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error[E0658]: the `#[repr(transparent)]` attribute is experimental (see issue #43036)
|
||||
--> $DIR/feature-gate-repr_transparent.rs:11:1
|
||||
|
|
||||
11 | #[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
|
||||
|
Loading…
x
Reference in New Issue
Block a user