Implement repr(transparent)

This commit is contained in:
Robin Kruppe 2018-01-03 17:43:30 +01:00
parent 79a521bb9a
commit 2be697bc21
21 changed files with 881 additions and 12 deletions

View 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

View file

@ -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! {

View file

@ -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");
}
}

View file

@ -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)]

View file

@ -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 {

View file

@ -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 => {

View file

@ -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,

View file

@ -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! {

View file

@ -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)]

View file

@ -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),
}

View file

@ -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");
}
}
}
}

View file

@ -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",

View 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 {} }

View 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 {} }

View 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 {} }

View file

@ -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"))]

View 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;

View 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);

View 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

View 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() {}

View 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