about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-02-11 20:48:27 +0000
committerbors <bors@rust-lang.org>2020-02-11 20:48:27 +0000
commitfc23a81831d5b41510d3261c20c34dd8d32f0f31 (patch)
tree90c1cf4eb8be2a6149857e0562326259cfaac727
parent3f32e3001e3a64c1baa509d3d1734dff53f14d81 (diff)
parent1b12232b8f6ede69485d4fd48f1ac8044d5d7c7c (diff)
downloadrust-fc23a81831d5b41510d3261c20c34dd8d32f0f31.tar.gz
rust-fc23a81831d5b41510d3261c20c34dd8d32f0f31.zip
Auto merge of #68491 - pnkfelix:hide-niches-under-unsafe-cell, r=oli
Hide niches under UnsafeCell

Hide any niche of T from type-construction context of `UnsafeCell<T>`.

Fix #68303
Fix #68206
-rw-r--r--src/ci/docker/dist-various-2/Dockerfile2
-rw-r--r--src/libcore/cell.rs1
-rw-r--r--src/libcore/lib.rs1
-rw-r--r--src/librustc/ty/layout.rs25
-rw-r--r--src/librustc/ty/mod.rs8
-rw-r--r--src/librustc_attr/builtin.rs2
-rw-r--r--src/librustc_builtin_macros/deriving/generic/mod.rs3
-rw-r--r--src/librustc_feature/active.rs4
-rw-r--r--src/librustc_passes/check_attr.rs24
-rw-r--r--src/librustc_span/symbol.rs2
-rw-r--r--src/libstd/sys/sgx/rwlock.rs24
-rw-r--r--src/test/ui/layout/unsafe-cell-hides-niche.rs32
-rw-r--r--src/test/ui/repr/feature-gate-no-niche.rs20
-rw-r--r--src/test/ui/repr/feature-gate-no-niche.stderr35
-rw-r--r--src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs14
-rw-r--r--src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr19
-rw-r--r--src/test/ui/repr/repr-no-niche.rs329
17 files changed, 521 insertions, 24 deletions
diff --git a/src/ci/docker/dist-various-2/Dockerfile b/src/ci/docker/dist-various-2/Dockerfile
index 2ae6c58941e..5bb5436bec5 100644
--- a/src/ci/docker/dist-various-2/Dockerfile
+++ b/src/ci/docker/dist-various-2/Dockerfile
@@ -48,7 +48,7 @@ RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc
 COPY dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/
 # We pass the commit id of the port of LLVM's libunwind to the build script.
 # Any update to the commit id here, should cause the container image to be re-built from this point on.
-RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh "53b586346f2c7870e20b170decdc30729d97c42b"
+RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh "5125c169b30837208a842f85f7ae44a83533bd0e"
 
 COPY dist-various-2/build-wasi-toolchain.sh /tmp/
 RUN /tmp/build-wasi-toolchain.sh
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index e7eecf7540a..8b8bda2e6b4 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -1475,6 +1475,7 @@ impl<T: ?Sized + fmt::Display> fmt::Display for RefMut<'_, T> {
 #[lang = "unsafe_cell"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[repr(transparent)]
+#[cfg_attr(not(bootstrap), repr(no_niche))] // rust-lang/rust#68303.
 pub struct UnsafeCell<T: ?Sized> {
     value: T,
 }
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 176c36664a9..12647fae900 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -139,6 +139,7 @@
 #![feature(const_type_id)]
 #![feature(const_caller_location)]
 #![feature(assoc_int_consts)]
+#![cfg_attr(not(bootstrap), feature(no_niche))] // rust-lang/rust#68303
 
 #[prelude_import]
 #[allow(unused)]
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index dbf421d26cb..e8bf2eb9a12 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -356,12 +356,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
             debug!("univariant offset: {:?} field: {:#?}", offset, field);
             offsets[i as usize] = offset;
 
-            if let Some(mut niche) = field.largest_niche.clone() {
-                let available = niche.available(dl);
-                if available > largest_niche_available {
-                    largest_niche_available = available;
-                    niche.offset += offset;
-                    largest_niche = Some(niche);
+            if !repr.hide_niche() {
+                if let Some(mut niche) = field.largest_niche.clone() {
+                    let available = niche.available(dl);
+                    if available > largest_niche_available {
+                        largest_niche_available = available;
+                        niche.offset += offset;
+                        largest_niche = Some(niche);
+                    }
                 }
             }
 
@@ -838,7 +840,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                             }
 
                             // Update `largest_niche` if we have introduced a larger niche.
-                            let niche = Niche::from_scalar(dl, Size::ZERO, scalar.clone());
+                            let niche = if def.repr.hide_niche() {
+                                None
+                            } else {
+                                Niche::from_scalar(dl, Size::ZERO, scalar.clone())
+                            };
                             if let Some(niche) = niche {
                                 match &st.largest_niche {
                                     Some(largest_niche) => {
@@ -863,6 +869,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                     return Ok(tcx.intern_layout(st));
                 }
 
+                // At this point, we have handled all unions and
+                // structs. (We have also handled univariant enums
+                // that allow representation optimization.)
+                assert!(def.is_enum());
+
                 // The current code for niche-filling relies on variant indices
                 // instead of actual discriminants, so dataful enums with
                 // explicit discriminants (RFC #2363) would misbehave.
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 0118fc4c8ac..60296b8116d 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2041,7 +2041,8 @@ bitflags! {
         const IS_TRANSPARENT     = 1 << 2;
         // Internal only for now. If true, don't reorder fields.
         const IS_LINEAR          = 1 << 3;
-
+        // If true, don't expose any niche to type's context.
+        const HIDE_NICHE         = 1 << 4;
         // Any of these flags being set prevent field reordering optimisation.
         const IS_UNOPTIMISABLE   = ReprFlags::IS_C.bits |
                                    ReprFlags::IS_SIMD.bits |
@@ -2078,6 +2079,7 @@ impl ReprOptions {
                         ReprFlags::empty()
                     }
                     attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
+                    attr::ReprNoNiche => ReprFlags::HIDE_NICHE,
                     attr::ReprSimd => ReprFlags::IS_SIMD,
                     attr::ReprInt(i) => {
                         size = Some(i);
@@ -2118,6 +2120,10 @@ impl ReprOptions {
     pub fn linear(&self) -> bool {
         self.flags.contains(ReprFlags::IS_LINEAR)
     }
+    #[inline]
+    pub fn hide_niche(&self) -> bool {
+        self.flags.contains(ReprFlags::HIDE_NICHE)
+    }
 
     pub fn discr_type(&self) -> attr::IntType {
         self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize))
diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs
index 3c13735bbf4..ac1a191fa23 100644
--- a/src/librustc_attr/builtin.rs
+++ b/src/librustc_attr/builtin.rs
@@ -840,6 +840,7 @@ pub enum ReprAttr {
     ReprSimd,
     ReprTransparent,
     ReprAlign(u32),
+    ReprNoNiche,
 }
 
 #[derive(Eq, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone, HashStable_Generic)]
@@ -895,6 +896,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
                         sym::packed => Some(ReprPacked(1)),
                         sym::simd => Some(ReprSimd),
                         sym::transparent => Some(ReprTransparent),
+                        sym::no_niche => Some(ReprNoNiche),
                         name => int_type_of_word(name).map(ReprInt),
                     };
 
diff --git a/src/librustc_builtin_macros/deriving/generic/mod.rs b/src/librustc_builtin_macros/deriving/generic/mod.rs
index 364d8ff8e22..f99008a6d5c 100644
--- a/src/librustc_builtin_macros/deriving/generic/mod.rs
+++ b/src/librustc_builtin_macros/deriving/generic/mod.rs
@@ -825,7 +825,8 @@ fn find_repr_type_name(sess: &ParseSess, type_attrs: &[ast::Attribute]) -> &'sta
                 attr::ReprPacked(_)
                 | attr::ReprSimd
                 | attr::ReprAlign(_)
-                | attr::ReprTransparent => continue,
+                | attr::ReprTransparent
+                | attr::ReprNoNiche => continue,
 
                 attr::ReprC => "i32",
 
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index 135500c0a8d..0082f4f1a6e 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -204,6 +204,10 @@ declare_features! (
     /// Added for testing E0705; perma-unstable.
     (active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)),
 
+    /// Allows `#[repr(no_niche)]` (an implementation detail of `rustc`,
+    /// it is not on path for eventual stabilization).
+    (active, no_niche, "1.42.0", None, None),
+
     // no-tracking-issue-end
 
     // -------------------------------------------------------------------------
diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs
index ecffd615e71..855970d9342 100644
--- a/src/librustc_passes/check_attr.rs
+++ b/src/librustc_passes/check_attr.rs
@@ -16,9 +16,10 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::DUMMY_HIR_ID;
 use rustc_hir::{self, HirId, Item, ItemKind, TraitItem};
 use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES};
+use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
-use syntax::ast::Attribute;
+use syntax::ast::{Attribute, NestedMetaItem};
 use syntax::attr;
 
 fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
@@ -278,6 +279,21 @@ impl CheckAttrVisitor<'tcx> {
                         _ => ("a", "struct, enum, or union"),
                     }
                 }
+                sym::no_niche => {
+                    if !self.tcx.features().enabled(sym::no_niche) {
+                        feature_err(
+                            &self.tcx.sess.parse_sess,
+                            sym::no_niche,
+                            hint.span(),
+                            "the attribute `repr(no_niche)` is currently unstable",
+                        )
+                        .emit();
+                    }
+                    match target {
+                        Target::Struct | Target::Enum => continue,
+                        _ => ("a", "struct or enum"),
+                    }
+                }
                 sym::i8
                 | sym::u8
                 | sym::i16
@@ -305,8 +321,10 @@ impl CheckAttrVisitor<'tcx> {
         // 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 {
+        // Error on repr(transparent, <anything else apart from no_niche>).
+        let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
+        let non_no_niche_count = hints.iter().filter(non_no_niche).count();
+        if is_transparent && non_no_niche_count > 1 {
             let hint_spans: Vec<_> = hint_spans.clone().collect();
             struct_span_err!(
                 self.tcx.sess,
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 49582e064ea..1cc4a277880 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -491,6 +491,7 @@ symbols! {
         non_exhaustive,
         non_modrs_mods,
         no_sanitize,
+        no_niche,
         no_stack_check,
         no_start,
         no_std,
@@ -587,6 +588,7 @@ symbols! {
         repr128,
         repr_align,
         repr_align_enum,
+        repr_no_niche,
         repr_packed,
         repr_simd,
         repr_transparent,
diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs
index fda2bb504d4..722b4f5e0ba 100644
--- a/src/libstd/sys/sgx/rwlock.rs
+++ b/src/libstd/sys/sgx/rwlock.rs
@@ -10,10 +10,10 @@ pub struct RWLock {
     writer: SpinMutex<WaitVariable<bool>>,
 }
 
-// Below is to check at compile time, that RWLock has size of 128 bytes.
+// Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below)
 #[allow(dead_code)]
 unsafe fn rw_lock_size_assert(r: RWLock) {
-    mem::transmute::<RWLock, [u8; 128]>(r);
+    mem::transmute::<RWLock, [u8; 144]>(r);
 }
 
 impl RWLock {
@@ -210,15 +210,17 @@ mod tests {
     // be changed too.
     #[test]
     fn test_c_rwlock_initializer() {
+        #[rustfmt::skip]
         const RWLOCK_INIT: &[u8] = &[
-            0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
         ];
 
         #[inline(never)]
@@ -239,7 +241,7 @@ mod tests {
             zero_stack();
             let mut init = MaybeUninit::<RWLock>::zeroed();
             rwlock_new(&mut init);
-            assert_eq!(mem::transmute::<_, [u8; 128]>(init.assume_init()).as_slice(), RWLOCK_INIT)
+            assert_eq!(mem::transmute::<_, [u8; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT)
         };
     }
 }
diff --git a/src/test/ui/layout/unsafe-cell-hides-niche.rs b/src/test/ui/layout/unsafe-cell-hides-niche.rs
new file mode 100644
index 00000000000..4ca3f7a1aad
--- /dev/null
+++ b/src/test/ui/layout/unsafe-cell-hides-niche.rs
@@ -0,0 +1,32 @@
+// For rust-lang/rust#68303: the contents of `UnsafeCell<T>` cannot
+// participate in the niche-optimization for enum discriminants. This
+// test checks that an `Option<UnsafeCell<NonZeroU32>>` has the same
+// size in memory as an `Option<UnsafeCell<u32>>` (namely, 8 bytes).
+
+// run-pass
+
+#![feature(no_niche)]
+
+use std::cell::UnsafeCell;
+use std::mem::size_of;
+use std::num::NonZeroU32 as N32;
+
+struct Wrapper<T>(T);
+
+#[repr(transparent)]
+struct Transparent<T>(T);
+
+#[repr(no_niche)]
+struct NoNiche<T>(T);
+
+fn main() {
+    assert_eq!(size_of::<Option<Wrapper<u32>>>(),     8);
+    assert_eq!(size_of::<Option<Wrapper<N32>>>(),     4);
+    assert_eq!(size_of::<Option<Transparent<u32>>>(), 8);
+    assert_eq!(size_of::<Option<Transparent<N32>>>(), 4);
+    assert_eq!(size_of::<Option<NoNiche<u32>>>(),     8);
+    assert_eq!(size_of::<Option<NoNiche<N32>>>(),     8);
+
+    assert_eq!(size_of::<Option<UnsafeCell<u32>>>(),  8);
+    assert_eq!(size_of::<Option<UnsafeCell<N32>>>(),  8);
+}
diff --git a/src/test/ui/repr/feature-gate-no-niche.rs b/src/test/ui/repr/feature-gate-no-niche.rs
new file mode 100644
index 00000000000..8872ee7119e
--- /dev/null
+++ b/src/test/ui/repr/feature-gate-no-niche.rs
@@ -0,0 +1,20 @@
+use std::num::NonZeroU8 as N8;
+use std::num::NonZeroU16 as N16;
+
+#[repr(no_niche)]
+pub struct Cloaked(N16);
+//~^^ ERROR the attribute `repr(no_niche)` is currently unstable [E0658]
+
+#[repr(transparent, no_niche)]
+pub struct Shadowy(N16);
+//~^^ ERROR the attribute `repr(no_niche)` is currently unstable [E0658]
+
+#[repr(no_niche)]
+pub enum Cloaked1 { _A(N16), }
+//~^^ ERROR the attribute `repr(no_niche)` is currently unstable [E0658]
+
+#[repr(no_niche)]
+pub enum Cloaked2 { _A(N16), _B(u8, N8) }
+//~^^ ERROR the attribute `repr(no_niche)` is currently unstable [E0658]
+
+fn main() { }
diff --git a/src/test/ui/repr/feature-gate-no-niche.stderr b/src/test/ui/repr/feature-gate-no-niche.stderr
new file mode 100644
index 00000000000..34fd417cc99
--- /dev/null
+++ b/src/test/ui/repr/feature-gate-no-niche.stderr
@@ -0,0 +1,35 @@
+error[E0658]: the attribute `repr(no_niche)` is currently unstable
+  --> $DIR/feature-gate-no-niche.rs:4:8
+   |
+LL | #[repr(no_niche)]
+   |        ^^^^^^^^
+   |
+   = help: add `#![feature(no_niche)]` to the crate attributes to enable
+
+error[E0658]: the attribute `repr(no_niche)` is currently unstable
+  --> $DIR/feature-gate-no-niche.rs:8:21
+   |
+LL | #[repr(transparent, no_niche)]
+   |                     ^^^^^^^^
+   |
+   = help: add `#![feature(no_niche)]` to the crate attributes to enable
+
+error[E0658]: the attribute `repr(no_niche)` is currently unstable
+  --> $DIR/feature-gate-no-niche.rs:12:8
+   |
+LL | #[repr(no_niche)]
+   |        ^^^^^^^^
+   |
+   = help: add `#![feature(no_niche)]` to the crate attributes to enable
+
+error[E0658]: the attribute `repr(no_niche)` is currently unstable
+  --> $DIR/feature-gate-no-niche.rs:16:8
+   |
+LL | #[repr(no_niche)]
+   |        ^^^^^^^^
+   |
+   = help: add `#![feature(no_niche)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs
new file mode 100644
index 00000000000..308634651a3
--- /dev/null
+++ b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs
@@ -0,0 +1,14 @@
+#![feature(no_niche)]
+
+use std::num::NonZeroU8 as N8;
+use std::num::NonZeroU16 as N16;
+
+#[repr(no_niche)]
+pub union Cloaked1 { _A: N16 }
+//~^^ ERROR attribute should be applied to struct or enum [E0517]
+
+#[repr(no_niche)]
+pub union Cloaked2 { _A: N16, _B: (u8, N8) }
+//~^^ ERROR attribute should be applied to struct or enum [E0517]
+
+fn main() { }
diff --git a/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr
new file mode 100644
index 00000000000..4c542c5f0da
--- /dev/null
+++ b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr
@@ -0,0 +1,19 @@
+error[E0517]: attribute should be applied to struct or enum
+  --> $DIR/repr-no-niche-inapplicable-to-unions.rs:6:8
+   |
+LL | #[repr(no_niche)]
+   |        ^^^^^^^^
+LL | pub union Cloaked1 { _A: N16 }
+   | ------------------------------ not a struct or enum
+
+error[E0517]: attribute should be applied to struct or enum
+  --> $DIR/repr-no-niche-inapplicable-to-unions.rs:10:8
+   |
+LL | #[repr(no_niche)]
+   |        ^^^^^^^^
+LL | pub union Cloaked2 { _A: N16, _B: (u8, N8) }
+   | -------------------------------------------- not a struct or enum
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0517`.
diff --git a/src/test/ui/repr/repr-no-niche.rs b/src/test/ui/repr/repr-no-niche.rs
new file mode 100644
index 00000000000..a7f0d509af5
--- /dev/null
+++ b/src/test/ui/repr/repr-no-niche.rs
@@ -0,0 +1,329 @@
+// run-pass
+
+// This file tests repr(no_niche), which causes an struct/enum to hide
+// any niche space that may exist in its internal state from the
+// context it appears in.
+
+// Here are the axes this test is seeking to cover:
+//
+// repr annotation:
+// visible: (); cloaked: (no_niche); transparent: (transparent); shadowy: (transparent, no_niche)
+//
+// enum vs struct
+//
+// niche-type via type-parameter vs inline declaration
+
+#![feature(decl_macro)]
+#![feature(no_niche)]
+
+use std::mem::size_of;
+use std::num::{NonZeroU8, NonZeroU16};
+
+mod struct_inline {
+    use std::num::NonZeroU16 as N16;
+
+    #[derive(Debug)] pub struct Visible(N16);
+
+    #[repr(no_niche)]
+    #[derive(Debug)] pub struct Cloaked(N16);
+
+    #[repr(transparent)]
+    #[derive(Debug)] pub struct Transparent(N16);
+
+    #[repr(transparent, no_niche)]
+    #[derive(Debug)] pub struct Shadowy(N16);
+}
+
+mod struct_param {
+    #[derive(Debug)] pub struct Visible<T>(T);
+
+    #[repr(no_niche)]
+    #[derive(Debug)] pub struct Cloaked<T>(T);
+
+    #[repr(transparent)]
+    #[derive(Debug)] pub struct Transparent<T>(T);
+
+    #[repr(transparent, no_niche)]
+    #[derive(Debug)] pub struct Shadowy<T>(T);
+}
+
+mod enum_inline {
+    use crate::two_fifty_six_variant_enum;
+    use std::num::{NonZeroU8 as N8, NonZeroU16 as N16};
+
+    #[derive(Debug)] pub enum Visible1 { _A(N16), }
+
+    #[repr(no_niche)]
+    #[derive(Debug)] pub enum Cloaked1 { _A(N16), }
+
+    // (N.B.: transparent enums must be univariant)
+    #[repr(transparent)]
+    #[derive(Debug)] pub enum Transparent { _A(N16), }
+
+    #[repr(transparent, no_niche)]
+    #[derive(Debug)] pub enum Shadowy { _A(N16), }
+
+    // including multivariant enums for completeness. Payload and
+    // number of variants (i.e. discriminant size) have been chosen so
+    // that layout including discriminant is 4 bytes, with no space in
+    // padding to hide another discrimnant from the surrounding
+    // context.
+    //
+    // (Note that multivariant enums cannot usefully expose a niche in
+    // general; this test is relying on that.)
+    two_fifty_six_variant_enum!(Visible2, N8);
+
+    #[repr(no_niche)]
+    two_fifty_six_variant_enum!(Cloaked2, N8);
+}
+
+mod enum_param {
+    use super::two_fifty_six_variant_enum;
+
+    #[derive(Debug)] pub enum Visible1<T> { _A(T), }
+
+    #[repr(no_niche)]
+    #[derive(Debug)] pub enum Cloaked1<T> { _A(T), }
+
+    // (N.B.: transparent enums must be univariant)
+    #[repr(transparent)]
+    #[derive(Debug)] pub enum Transparent<T> { _A(T), }
+
+    #[repr(transparent, no_niche)]
+    #[derive(Debug)] pub enum Shadowy<T> { _A(T), }
+
+    // including multivariant enums for completeness. Same notes apply
+    // here as above (assuming `T` is instantiated with `NonZeroU8`).
+    two_fifty_six_variant_enum!(Visible2<T>);
+
+    #[repr(no_niche)]
+    two_fifty_six_variant_enum!(Cloaked2<T>);
+}
+
+fn main() {
+    // sanity-checks
+    assert_eq!(size_of::<struct_inline::Visible>(),               2);
+    assert_eq!(size_of::<struct_inline::Cloaked>(),               2);
+    assert_eq!(size_of::<struct_inline::Transparent>(),           2);
+    assert_eq!(size_of::<struct_inline::Shadowy>(),               2);
+
+    assert_eq!(size_of::<struct_param::Visible<NonZeroU16>>(), 2);
+    assert_eq!(size_of::<struct_param::Cloaked<NonZeroU16>>(), 2);
+    assert_eq!(size_of::<struct_param::Transparent<NonZeroU16>>(), 2);
+    assert_eq!(size_of::<struct_param::Shadowy<NonZeroU16>>(), 2);
+
+    assert_eq!(size_of::<enum_inline::Visible1>(),    2);
+    assert_eq!(size_of::<enum_inline::Cloaked1>(),    2);
+    assert_eq!(size_of::<enum_inline::Transparent>(), 2); // transparent enums are univariant
+    assert_eq!(size_of::<enum_inline::Shadowy>(),     2);
+    assert_eq!(size_of::<enum_inline::Visible2>(),    4);
+    assert_eq!(size_of::<enum_inline::Cloaked2>(),    4);
+
+    assert_eq!(size_of::<enum_param::Visible1<NonZeroU16>>(),    2);
+    assert_eq!(size_of::<enum_param::Cloaked1<NonZeroU16>>(),    2);
+    assert_eq!(size_of::<enum_param::Transparent<NonZeroU16>>(), 2);
+    assert_eq!(size_of::<enum_param::Shadowy<NonZeroU16>>(),     2);
+    assert_eq!(size_of::<enum_param::Visible2<NonZeroU8>>(),     4);
+    assert_eq!(size_of::<enum_param::Cloaked2<NonZeroU8>>(),     4);
+
+    // now the actual tests of no_niche: how do inputs above compose
+    // with `Option` type constructor. The cases with a `_+2` are the
+    // ones where no_niche fires.
+    assert_eq!(size_of::<Option<struct_inline::Visible>>(),       2);
+    assert_eq!(size_of::<Option<struct_inline::Cloaked>>(),       2+2);
+    assert_eq!(size_of::<Option<struct_inline::Transparent>>(),   2);
+    assert_eq!(size_of::<Option<struct_inline::Shadowy>>(),       2+2);
+
+    assert_eq!(size_of::<Option<struct_param::Visible<NonZeroU16>>>(),     2);
+    assert_eq!(size_of::<Option<struct_param::Cloaked<NonZeroU16>>>(),     2+2);
+    assert_eq!(size_of::<Option<struct_param::Transparent<NonZeroU16>>>(), 2);
+    assert_eq!(size_of::<Option<struct_param::Shadowy<NonZeroU16>>>(),     2+2);
+
+    assert_eq!(size_of::<Option<enum_inline::Visible1>>(),    2);
+    assert_eq!(size_of::<Option<enum_inline::Cloaked1>>(),    2+2);
+    assert_eq!(size_of::<Option<enum_inline::Transparent>>(), 2);
+    assert_eq!(size_of::<Option<enum_inline::Shadowy>>(),     2+2);
+    // cannot use niche of multivariant payload
+    assert_eq!(size_of::<Option<enum_inline::Visible2>>(),    4+2);
+    assert_eq!(size_of::<Option<enum_inline::Cloaked2>>(),    4+2);
+
+    assert_eq!(size_of::<Option<enum_param::Visible1<NonZeroU16>>>(),    2);
+    assert_eq!(size_of::<Option<enum_param::Cloaked1<NonZeroU16>>>(),    2+2);
+    assert_eq!(size_of::<Option<enum_param::Transparent<NonZeroU16>>>(), 2);
+    assert_eq!(size_of::<Option<enum_param::Shadowy<NonZeroU16>>>(),     2+2);
+    // cannot use niche of multivariant payload
+    assert_eq!(size_of::<Option<enum_param::Visible2<NonZeroU8>>>(),    4+2);
+    assert_eq!(size_of::<Option<enum_param::Cloaked2<NonZeroU8>>>(),    4+2);
+}
+
+macro two_fifty_six_variant_enum {
+    ($name:ident<$param:ident>) => {
+        #[derive(Debug)]
+        pub enum $name<$param> {
+            _V00($param, u16), _V01(u16, $param), _V02($param, u16), _V03(u16, $param),
+            _V04($param, u16), _V05(u16, $param), _V06($param, u16), _V07(u16, $param),
+            _V08($param, u16), _V09(u16, $param), _V0a($param, u16), _V0b(u16, $param),
+            _V0c($param, u16), _V0d(u16, $param), _V0e($param, u16), _V0f(u16, $param),
+
+            _V10($param, u16), _V11(u16, $param), _V12($param, u16), _V13(u16, $param),
+            _V14($param, u16), _V15(u16, $param), _V16($param, u16), _V17(u16, $param),
+            _V18($param, u16), _V19(u16, $param), _V1a($param, u16), _V1b(u16, $param),
+            _V1c($param, u16), _V1d(u16, $param), _V1e($param, u16), _V1f(u16, $param),
+
+            _V20($param, u16), _V21(u16, $param), _V22($param, u16), _V23(u16, $param),
+            _V24($param, u16), _V25(u16, $param), _V26($param, u16), _V27(u16, $param),
+            _V28($param, u16), _V29(u16, $param), _V2a($param, u16), _V2b(u16, $param),
+            _V2c($param, u16), _V2d(u16, $param), _V2e($param, u16), _V2f(u16, $param),
+
+            _V30($param, u16), _V31(u16, $param), _V32($param, u16), _V33(u16, $param),
+            _V34($param, u16), _V35(u16, $param), _V36($param, u16), _V37(u16, $param),
+            _V38($param, u16), _V39(u16, $param), _V3a($param, u16), _V3b(u16, $param),
+            _V3c($param, u16), _V3d(u16, $param), _V3e($param, u16), _V3f(u16, $param),
+
+            _V40($param, u16), _V41(u16, $param), _V42($param, u16), _V43(u16, $param),
+            _V44($param, u16), _V45(u16, $param), _V46($param, u16), _V47(u16, $param),
+            _V48($param, u16), _V49(u16, $param), _V4a($param, u16), _V4b(u16, $param),
+            _V4c($param, u16), _V4d(u16, $param), _V4e($param, u16), _V4f(u16, $param),
+
+            _V50($param, u16), _V51(u16, $param), _V52($param, u16), _V53(u16, $param),
+            _V54($param, u16), _V55(u16, $param), _V56($param, u16), _V57(u16, $param),
+            _V58($param, u16), _V59(u16, $param), _V5a($param, u16), _V5b(u16, $param),
+            _V5c($param, u16), _V5d(u16, $param), _V5e($param, u16), _V5f(u16, $param),
+
+            _V60($param, u16), _V61(u16, $param), _V62($param, u16), _V63(u16, $param),
+            _V64($param, u16), _V65(u16, $param), _V66($param, u16), _V67(u16, $param),
+            _V68($param, u16), _V69(u16, $param), _V6a($param, u16), _V6b(u16, $param),
+            _V6c($param, u16), _V6d(u16, $param), _V6e($param, u16), _V6f(u16, $param),
+
+            _V70($param, u16), _V71(u16, $param), _V72($param, u16), _V73(u16, $param),
+            _V74($param, u16), _V75(u16, $param), _V76($param, u16), _V77(u16, $param),
+            _V78($param, u16), _V79(u16, $param), _V7a($param, u16), _V7b(u16, $param),
+            _V7c($param, u16), _V7d(u16, $param), _V7e($param, u16), _V7f(u16, $param),
+
+            _V80($param, u16), _V81(u16, $param), _V82($param, u16), _V83(u16, $param),
+            _V84($param, u16), _V85(u16, $param), _V86($param, u16), _V87(u16, $param),
+            _V88($param, u16), _V89(u16, $param), _V8a($param, u16), _V8b(u16, $param),
+            _V8c($param, u16), _V8d(u16, $param), _V8e($param, u16), _V8f(u16, $param),
+
+            _V90($param, u16), _V91(u16, $param), _V92($param, u16), _V93(u16, $param),
+            _V94($param, u16), _V95(u16, $param), _V96($param, u16), _V97(u16, $param),
+            _V98($param, u16), _V99(u16, $param), _V9a($param, u16), _V9b(u16, $param),
+            _V9c($param, u16), _V9d(u16, $param), _V9e($param, u16), _V9f(u16, $param),
+
+            _Va0($param, u16), _Va1(u16, $param), _Va2($param, u16), _Va3(u16, $param),
+            _Va4($param, u16), _Va5(u16, $param), _Va6($param, u16), _Va7(u16, $param),
+            _Va8($param, u16), _Va9(u16, $param), _Vaa($param, u16), _Vab(u16, $param),
+            _Vac($param, u16), _Vad(u16, $param), _Vae($param, u16), _Vaf(u16, $param),
+
+            _Vb0($param, u16), _Vb1(u16, $param), _Vb2($param, u16), _Vb3(u16, $param),
+            _Vb4($param, u16), _Vb5(u16, $param), _Vb6($param, u16), _Vb7(u16, $param),
+            _Vb8($param, u16), _Vb9(u16, $param), _Vba($param, u16), _Vbb(u16, $param),
+            _Vbc($param, u16), _Vbd(u16, $param), _Vbe($param, u16), _Vbf(u16, $param),
+
+            _Vc0($param, u16), _Vc1(u16, $param), _Vc2($param, u16), _Vc3(u16, $param),
+            _Vc4($param, u16), _Vc5(u16, $param), _Vc6($param, u16), _Vc7(u16, $param),
+            _Vc8($param, u16), _Vc9(u16, $param), _Vca($param, u16), _Vcb(u16, $param),
+            _Vcc($param, u16), _Vcd(u16, $param), _Vce($param, u16), _Vcf(u16, $param),
+
+            _Vd0($param, u16), _Vd1(u16, $param), _Vd2($param, u16), _Vd3(u16, $param),
+            _Vd4($param, u16), _Vd5(u16, $param), _Vd6($param, u16), _Vd7(u16, $param),
+            _Vd8($param, u16), _Vd9(u16, $param), _Vda($param, u16), _Vdb(u16, $param),
+            _Vdc($param, u16), _Vdd(u16, $param), _Vde($param, u16), _Vdf(u16, $param),
+
+            _Ve0($param, u16), _Ve1(u16, $param), _Ve2($param, u16), _Ve3(u16, $param),
+            _Ve4($param, u16), _Ve5(u16, $param), _Ve6($param, u16), _Ve7(u16, $param),
+            _Ve8($param, u16), _Ve9(u16, $param), _Vea($param, u16), _Veb(u16, $param),
+            _Vec($param, u16), _Ved(u16, $param), _Vee($param, u16), _Vef(u16, $param),
+
+            _Vf0($param, u16), _Vf1(u16, $param), _Vf2($param, u16), _Vf3(u16, $param),
+            _Vf4($param, u16), _Vf5(u16, $param), _Vf6($param, u16), _Vf7(u16, $param),
+            _Vf8($param, u16), _Vf9(u16, $param), _Vfa($param, u16), _Vfb(u16, $param),
+            _Vfc($param, u16), _Vfd(u16, $param), _Vfe($param, u16), _Vff(u16, $param),
+        }
+    },
+
+    ($name:ident, $param:ty) => {
+        #[derive(Debug)]
+        pub enum $name {
+            _V00($param, u16), _V01(u16, $param), _V02($param, u16), _V03(u16, $param),
+            _V04($param, u16), _V05(u16, $param), _V06($param, u16), _V07(u16, $param),
+            _V08($param, u16), _V09(u16, $param), _V0a($param, u16), _V0b(u16, $param),
+            _V0c($param, u16), _V0d(u16, $param), _V0e($param, u16), _V0f(u16, $param),
+
+            _V10($param, u16), _V11(u16, $param), _V12($param, u16), _V13(u16, $param),
+            _V14($param, u16), _V15(u16, $param), _V16($param, u16), _V17(u16, $param),
+            _V18($param, u16), _V19(u16, $param), _V1a($param, u16), _V1b(u16, $param),
+            _V1c($param, u16), _V1d(u16, $param), _V1e($param, u16), _V1f(u16, $param),
+
+            _V20($param, u16), _V21(u16, $param), _V22($param, u16), _V23(u16, $param),
+            _V24($param, u16), _V25(u16, $param), _V26($param, u16), _V27(u16, $param),
+            _V28($param, u16), _V29(u16, $param), _V2a($param, u16), _V2b(u16, $param),
+            _V2c($param, u16), _V2d(u16, $param), _V2e($param, u16), _V2f(u16, $param),
+
+            _V30($param, u16), _V31(u16, $param), _V32($param, u16), _V33(u16, $param),
+            _V34($param, u16), _V35(u16, $param), _V36($param, u16), _V37(u16, $param),
+            _V38($param, u16), _V39(u16, $param), _V3a($param, u16), _V3b(u16, $param),
+            _V3c($param, u16), _V3d(u16, $param), _V3e($param, u16), _V3f(u16, $param),
+
+            _V40($param, u16), _V41(u16, $param), _V42($param, u16), _V43(u16, $param),
+            _V44($param, u16), _V45(u16, $param), _V46($param, u16), _V47(u16, $param),
+            _V48($param, u16), _V49(u16, $param), _V4a($param, u16), _V4b(u16, $param),
+            _V4c($param, u16), _V4d(u16, $param), _V4e($param, u16), _V4f(u16, $param),
+
+            _V50($param, u16), _V51(u16, $param), _V52($param, u16), _V53(u16, $param),
+            _V54($param, u16), _V55(u16, $param), _V56($param, u16), _V57(u16, $param),
+            _V58($param, u16), _V59(u16, $param), _V5a($param, u16), _V5b(u16, $param),
+            _V5c($param, u16), _V5d(u16, $param), _V5e($param, u16), _V5f(u16, $param),
+
+            _V60($param, u16), _V61(u16, $param), _V62($param, u16), _V63(u16, $param),
+            _V64($param, u16), _V65(u16, $param), _V66($param, u16), _V67(u16, $param),
+            _V68($param, u16), _V69(u16, $param), _V6a($param, u16), _V6b(u16, $param),
+            _V6c($param, u16), _V6d(u16, $param), _V6e($param, u16), _V6f(u16, $param),
+
+            _V70($param, u16), _V71(u16, $param), _V72($param, u16), _V73(u16, $param),
+            _V74($param, u16), _V75(u16, $param), _V76($param, u16), _V77(u16, $param),
+            _V78($param, u16), _V79(u16, $param), _V7a($param, u16), _V7b(u16, $param),
+            _V7c($param, u16), _V7d(u16, $param), _V7e($param, u16), _V7f(u16, $param),
+
+            _V80($param, u16), _V81(u16, $param), _V82($param, u16), _V83(u16, $param),
+            _V84($param, u16), _V85(u16, $param), _V86($param, u16), _V87(u16, $param),
+            _V88($param, u16), _V89(u16, $param), _V8a($param, u16), _V8b(u16, $param),
+            _V8c($param, u16), _V8d(u16, $param), _V8e($param, u16), _V8f(u16, $param),
+
+            _V90($param, u16), _V91(u16, $param), _V92($param, u16), _V93(u16, $param),
+            _V94($param, u16), _V95(u16, $param), _V96($param, u16), _V97(u16, $param),
+            _V98($param, u16), _V99(u16, $param), _V9a($param, u16), _V9b(u16, $param),
+            _V9c($param, u16), _V9d(u16, $param), _V9e($param, u16), _V9f(u16, $param),
+
+            _Va0($param, u16), _Va1(u16, $param), _Va2($param, u16), _Va3(u16, $param),
+            _Va4($param, u16), _Va5(u16, $param), _Va6($param, u16), _Va7(u16, $param),
+            _Va8($param, u16), _Va9(u16, $param), _Vaa($param, u16), _Vab(u16, $param),
+            _Vac($param, u16), _Vad(u16, $param), _Vae($param, u16), _Vaf(u16, $param),
+
+            _Vb0($param, u16), _Vb1(u16, $param), _Vb2($param, u16), _Vb3(u16, $param),
+            _Vb4($param, u16), _Vb5(u16, $param), _Vb6($param, u16), _Vb7(u16, $param),
+            _Vb8($param, u16), _Vb9(u16, $param), _Vba($param, u16), _Vbb(u16, $param),
+            _Vbc($param, u16), _Vbd(u16, $param), _Vbe($param, u16), _Vbf(u16, $param),
+
+            _Vc0($param, u16), _Vc1(u16, $param), _Vc2($param, u16), _Vc3(u16, $param),
+            _Vc4($param, u16), _Vc5(u16, $param), _Vc6($param, u16), _Vc7(u16, $param),
+            _Vc8($param, u16), _Vc9(u16, $param), _Vca($param, u16), _Vcb(u16, $param),
+            _Vcc($param, u16), _Vcd(u16, $param), _Vce($param, u16), _Vcf(u16, $param),
+
+            _Vd0($param, u16), _Vd1(u16, $param), _Vd2($param, u16), _Vd3(u16, $param),
+            _Vd4($param, u16), _Vd5(u16, $param), _Vd6($param, u16), _Vd7(u16, $param),
+            _Vd8($param, u16), _Vd9(u16, $param), _Vda($param, u16), _Vdb(u16, $param),
+            _Vdc($param, u16), _Vdd(u16, $param), _Vde($param, u16), _Vdf(u16, $param),
+
+            _Ve0($param, u16), _Ve1(u16, $param), _Ve2($param, u16), _Ve3(u16, $param),
+            _Ve4($param, u16), _Ve5(u16, $param), _Ve6($param, u16), _Ve7(u16, $param),
+            _Ve8($param, u16), _Ve9(u16, $param), _Vea($param, u16), _Veb(u16, $param),
+            _Vec($param, u16), _Ved(u16, $param), _Vee($param, u16), _Vef(u16, $param),
+
+            _Vf0($param, u16), _Vf1(u16, $param), _Vf2($param, u16), _Vf3(u16, $param),
+            _Vf4($param, u16), _Vf5(u16, $param), _Vf6($param, u16), _Vf7(u16, $param),
+            _Vf8($param, u16), _Vf9(u16, $param), _Vfa($param, u16), _Vfb(u16, $param),
+            _Vfc($param, u16), _Vfd(u16, $param), _Vfe($param, u16), _Vff(u16, $param),
+        }
+    }
+}