about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/doc/unstable-book/src/language-features/repr-transparent.md176
-rw-r--r--src/librustc/diagnostics.rs64
-rw-r--r--src/librustc/hir/check_attr.rs25
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/ty/mod.rs6
-rw-r--r--src/librustc_lint/types.rs17
-rw-r--r--src/librustc_typeck/check/mod.rs38
-rw-r--r--src/librustc_typeck/diagnostics.rs93
-rw-r--r--src/librustc_typeck/lib.rs1
-rw-r--r--src/libsyntax/attr.rs5
-rw-r--r--src/libsyntax/feature_gate.rs8
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs4
-rw-r--r--src/test/codegen/repr-transparent-aggregates-1.rs53
-rw-r--r--src/test/codegen/repr-transparent-aggregates-2.rs54
-rw-r--r--src/test/codegen/repr-transparent.rs177
-rw-r--r--src/test/compile-fail/lint-ctypes.rs28
-rw-r--r--src/test/compile-fail/repr-transparent-other-items.rs40
-rw-r--r--src/test/compile-fail/repr-transparent-other-reprs.rs28
-rw-r--r--src/test/compile-fail/repr-transparent.rs51
-rw-r--r--src/test/ui/feature-gate-repr_transparent.rs14
-rw-r--r--src/test/ui/feature-gate-repr_transparent.stderr10
21 files changed, 881 insertions, 12 deletions
diff --git a/src/doc/unstable-book/src/language-features/repr-transparent.md b/src/doc/unstable-book/src/language-features/repr-transparent.md
new file mode 100644
index 00000000000..62202dc96fd
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/repr-transparent.md
@@ -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
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 7b48e7801df..1d7cde81265 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -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! {
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index 4b528a0fdc7..a073910fdc8 100644
--- a/src/librustc/hir/check_attr.rs
+++ b/src/librustc/hir/check_attr.rs
@@ -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");
         }
     }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 075ee0b8c7c..d727dfb0c4b 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -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)]
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 2b4d2c80c6f..deaea70f760 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -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 {
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index bf5d16696f9..2267d2b89cc 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -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 => {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 69236d77ed3..cb8ce8d5ac3 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -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,
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 1913b940c08..4f661a4f5e6 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -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! {
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 1dc98bc7a00..786a6783440 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -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)]
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 4291f811f3f..d18d6f5e6bd 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -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),
 }
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 196fadcc997..953b2deb877 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -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");
+                        }
                     }
                 }
             }
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 08a7f85d14b..3660d2fe46a 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -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",
diff --git a/src/test/codegen/repr-transparent-aggregates-1.rs b/src/test/codegen/repr-transparent-aggregates-1.rs
new file mode 100644
index 00000000000..2eeed2b788c
--- /dev/null
+++ b/src/test/codegen/repr-transparent-aggregates-1.rs
@@ -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 {} }
diff --git a/src/test/codegen/repr-transparent-aggregates-2.rs b/src/test/codegen/repr-transparent-aggregates-2.rs
new file mode 100644
index 00000000000..e6374928a5c
--- /dev/null
+++ b/src/test/codegen/repr-transparent-aggregates-2.rs
@@ -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 {} }
diff --git a/src/test/codegen/repr-transparent.rs b/src/test/codegen/repr-transparent.rs
new file mode 100644
index 00000000000..31020d8b94f
--- /dev/null
+++ b/src/test/codegen/repr-transparent.rs
@@ -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 {} }
diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/compile-fail/lint-ctypes.rs
index fd825563eba..c22239dee0a 100644
--- a/src/test/compile-fail/lint-ctypes.rs
+++ b/src/test/compile-fail/lint-ctypes.rs
@@ -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"))]
diff --git a/src/test/compile-fail/repr-transparent-other-items.rs b/src/test/compile-fail/repr-transparent-other-items.rs
new file mode 100644
index 00000000000..cf0870866c7
--- /dev/null
+++ b/src/test/compile-fail/repr-transparent-other-items.rs
@@ -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;
diff --git a/src/test/compile-fail/repr-transparent-other-reprs.rs b/src/test/compile-fail/repr-transparent-other-reprs.rs
new file mode 100644
index 00000000000..7b91a6f68e3
--- /dev/null
+++ b/src/test/compile-fail/repr-transparent-other-reprs.rs
@@ -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);
diff --git a/src/test/compile-fail/repr-transparent.rs b/src/test/compile-fail/repr-transparent.rs
new file mode 100644
index 00000000000..b5e6a0fa0b1
--- /dev/null
+++ b/src/test/compile-fail/repr-transparent.rs
@@ -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
diff --git a/src/test/ui/feature-gate-repr_transparent.rs b/src/test/ui/feature-gate-repr_transparent.rs
new file mode 100644
index 00000000000..deadf2e535d
--- /dev/null
+++ b/src/test/ui/feature-gate-repr_transparent.rs
@@ -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() {}
diff --git a/src/test/ui/feature-gate-repr_transparent.stderr b/src/test/ui/feature-gate-repr_transparent.stderr
new file mode 100644
index 00000000000..d1292e95491
--- /dev/null
+++ b/src/test/ui/feature-gate-repr_transparent.stderr
@@ -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
+