about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-05-10 19:56:15 +0000
committerbors <bors@rust-lang.org>2019-05-10 19:56:15 +0000
commitd595b113584f8f446957469951fd5d31adc2a44e (patch)
treed10ce3919c64959afcebbcd361097565f80ccd64
parentcff1bdbd77d29a28a94ff9f5bf1e1c84e5bb6259 (diff)
parent329275a06f53c2655a59c0dbdd65af365fcdd746 (diff)
downloadrust-d595b113584f8f446957469951fd5d31adc2a44e.tar.gz
rust-d595b113584f8f446957469951fd5d31adc2a44e.zip
Auto merge of #60708 - Centril:rollup-j5smdo0, r=Centril
Rollup of 6 pull requests

Successful merges:

 - #60529 (RFC 2008: Uninhabitedness fixes for enum variants and tests)
 - #60620 (Fix a couple of FIXMEs in ext::tt::transcribe)
 - #60659 (Tweak `Symbol` and `InternedString`)
 - #60692 (Extend #60676 test for nested mut patterns.)
 - #60697 (add regression test for #60629)
 - #60701 (Update mailmap for mati865)

Failed merges:

r? @ghost
-rw-r--r--.mailmap2
-rw-r--r--src/librustc/ty/inhabitedness/mod.rs22
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs27
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs6
-rw-r--r--src/libsyntax/ext/tt/transcribe.rs16
-rw-r--r--src/libsyntax_pos/symbol.rs84
-rw-r--r--src/test/incremental/issue-60629.rs10
-rw-r--r--src/test/ui/async-await/issue-60674.rs6
-rw-r--r--src/test/ui/async-await/issue-60674.stdout2
-rw-r--r--src/test/ui/pattern/const-pat-ice.stderr2
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs33
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs38
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr47
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs46
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr47
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs36
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr35
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs52
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr59
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs40
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr35
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs58
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.rs34
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr35
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs42
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr49
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs37
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr35
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs48
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs59
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs71
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr38
32 files changed, 1106 insertions, 45 deletions
diff --git a/.mailmap b/.mailmap
index 679e9d79847..63a49cd4132 100644
--- a/.mailmap
+++ b/.mailmap
@@ -155,6 +155,8 @@ Mark Sinclair <mark.edward.x@gmail.com> =Mark Sinclair <=125axel125@gmail.com>
 Markus Westerlind <marwes91@gmail.com> Markus <marwes91@gmail.com>
 Martin Hafskjold Thoresen <martinhath@gmail.com>
 Matej Lach <matej.lach@gmail.com> Matej Ľach <matej.lach@gmail.com>
+Mateusz Mikuła <matti@marinelayer.io> <mati865@gmail.com>
+Mateusz Mikuła <matti@marinelayer.io> <mati865@users.noreply.github.com>
 Matt Brubeck <mbrubeck@limpet.net> <mbrubeck@cs.hmc.edu>
 Matthew Auld <matthew.auld@intel.com>
 Matthew McPherrin <matthew@mcpherrin.ca> <matt@mcpherrin.ca>
diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs
index 197d3325f51..be1d973c2cd 100644
--- a/src/librustc/ty/inhabitedness/mod.rs
+++ b/src/librustc/ty/inhabitedness/mod.rs
@@ -113,9 +113,14 @@ impl<'a, 'gcx, 'tcx> AdtDef {
         tcx: TyCtxt<'a, 'gcx, 'tcx>,
         substs: SubstsRef<'tcx>) -> DefIdForest
     {
-        DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
-            v.uninhabited_from(tcx, substs, self.adt_kind())
-        }))
+        // Non-exhaustive ADTs from other crates are always considered inhabited.
+        if self.is_variant_list_non_exhaustive() && !self.did.is_local() {
+            DefIdForest::empty()
+        } else {
+            DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
+                v.uninhabited_from(tcx, substs, self.adt_kind())
+            }))
+        }
     }
 }
 
@@ -134,9 +139,14 @@ impl<'a, 'gcx, 'tcx> VariantDef {
             AdtKind::Enum => true,
             AdtKind::Struct => false,
         };
-        DefIdForest::union(tcx, self.fields.iter().map(|f| {
-            f.uninhabited_from(tcx, substs, is_enum)
-        }))
+        // Non-exhaustive variants from other crates are always considered inhabited.
+        if self.is_field_list_non_exhaustive() && !self.def_id.is_local() {
+            DefIdForest::empty()
+        } else {
+            DefIdForest::union(tcx, self.fields.iter().map(|f| {
+                f.uninhabited_from(tcx, substs, is_enum)
+            }))
+        }
     }
 }
 
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index edd36abc0b8..fd4416fc2b7 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -388,6 +388,18 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
         }
     }
 
+    fn is_non_exhaustive_variant<'p>(&self, pattern: &'p Pattern<'tcx>) -> bool
+        where 'a: 'p
+    {
+        match *pattern.kind {
+            PatternKind::Variant { adt_def, variant_index, .. } => {
+                let ref variant = adt_def.variants[variant_index];
+                variant.is_field_list_non_exhaustive()
+            }
+            _ => false,
+        }
+    }
+
     fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
         match ty.sty {
             ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
@@ -1097,10 +1109,17 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
     debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]);
 
     if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
-        debug!("is_useful - expanding constructors: {:#?}", constructors);
-        split_grouped_constructors(cx.tcx, constructors, matrix, pcx.ty).into_iter().map(|c|
-            is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)
-        ).find(|result| result.is_useful()).unwrap_or(NotUseful)
+        let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty);
+        debug!("is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}",
+               constructors, is_declared_nonexhaustive);
+
+        if is_declared_nonexhaustive {
+            Useful
+        } else {
+            split_grouped_constructors(cx.tcx, constructors, matrix, pcx.ty).into_iter().map(|c|
+                is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)
+            ).find(|result| result.is_useful()).unwrap_or(NotUseful)
+        }
     } else {
         debug!("is_useful - expanding wildcard");
 
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index 1a7266859ad..8c7155e1df3 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -208,7 +208,11 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
                                     .map(|variant| variant.ident)
                                     .collect();
                             }
-                            def.variants.is_empty()
+
+                            let is_non_exhaustive_and_non_local =
+                                def.is_variant_list_non_exhaustive() && !def.did.is_local();
+
+                            !(is_non_exhaustive_and_non_local) && def.variants.is_empty()
                         },
                         _ => false
                     }
diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs
index 0cefcf1ce03..e3586c1854c 100644
--- a/src/libsyntax/ext/tt/transcribe.rs
+++ b/src/libsyntax/ext/tt/transcribe.rs
@@ -170,10 +170,9 @@ pub fn transcribe(
                     }
 
                     LockstepIterSize::Contradiction(ref msg) => {
-                        // FIXME: this should be impossible. I (mark-i-m) believe it would
-                        // represent a bug in the macro_parser.
-                        // FIXME #2887 blame macro invoker instead
-                        cx.span_fatal(seq.span(), &msg[..]);
+                        // This should never happen because the macro parser should generate
+                        // properly-sized matches for all meta-vars.
+                        cx.span_bug(seq.span(), &msg[..]);
                     }
 
                     LockstepIterSize::Constraint(len, _) => {
@@ -188,14 +187,13 @@ pub fn transcribe(
                         // Is the repetition empty?
                         if len == 0 {
                             if seq.op == quoted::KleeneOp::OneOrMore {
-                                // FIXME: this should be impossible because we check for this in
-                                // macro_parser.rs
-                                // FIXME #2887 blame invoker
-                                cx.span_fatal(sp.entire(), "this must repeat at least once");
+                                // This should be impossible because the macro parser would not
+                                // match the given macro arm.
+                                cx.span_bug(sp.entire(), "this must repeat at least once");
                             }
                         } else {
                             // 0 is the initial counter (we have done 0 repretitions so far). `len`
-                            //   is the total number of reptitions we should generate.
+                            // is the total number of reptitions we should generate.
                             repeats.push((0, len));
 
                             // The first time we encounter the sequence we push it to the stack. It
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 20759217b54..d0ba09af30b 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -344,9 +344,22 @@ impl Decodable for Ident {
     }
 }
 
-/// A symbol is an interned or gensymed string. The use of `newtype_index!` means
-/// that `Option<Symbol>` only takes up 4 bytes, because `newtype_index!` reserves
-/// the last 256 values for tagging purposes.
+/// A symbol is an interned or gensymed string. A gensym is a symbol that is
+/// never equal to any other symbol. E.g.:
+/// ```
+/// assert_eq!(Symbol::intern("x"), Symbol::intern("x"))
+/// assert_ne!(Symbol::gensym("x"), Symbol::intern("x"))
+/// assert_ne!(Symbol::gensym("x"), Symbol::gensym("x"))
+/// ```
+/// Conceptually, a gensym can be thought of as a normal symbol with an
+/// invisible unique suffix. Gensyms are useful when creating new identifiers
+/// that must not match any existing identifiers, e.g. during macro expansion
+/// and syntax desugaring.
+///
+/// Internally, a Symbol is implemented as an index, and all operations
+/// (including hashing, equality, and ordering) operate on that index. The use
+/// of `newtype_index!` means that `Option<Symbol>` only takes up 4 bytes,
+/// because `newtype_index!` reserves the last 256 values for tagging purposes.
 ///
 /// Note that `Symbol` cannot directly be a `newtype_index!` because it implements
 /// `fmt::Debug`, `Encodable`, and `Decodable` in special ways.
@@ -367,10 +380,6 @@ impl Symbol {
         with_interner(|interner| interner.intern(string))
     }
 
-    pub fn interned(self) -> Self {
-        with_interner(|interner| interner.interned(self))
-    }
-
     /// Gensyms a new `usize`, using the current interner.
     pub fn gensym(string: &str) -> Self {
         with_interner(|interner| interner.gensym(string))
@@ -380,6 +389,7 @@ impl Symbol {
         with_interner(|interner| interner.gensymed(self))
     }
 
+    // WARNING: this function is deprecated and will be removed in the future.
     pub fn is_gensymed(self) -> bool {
         with_interner(|interner| interner.is_gensymed(self))
     }
@@ -488,11 +498,11 @@ impl Interner {
         name
     }
 
-    pub fn interned(&self, symbol: Symbol) -> Symbol {
+    fn interned(&self, symbol: Symbol) -> Symbol {
         if (symbol.0.as_usize()) < self.strings.len() {
             symbol
         } else {
-            self.interned(self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize])
+            self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize]
         }
     }
 
@@ -510,10 +520,15 @@ impl Interner {
         symbol.0.as_usize() >= self.strings.len()
     }
 
+    // Get the symbol as a string. `Symbol::as_str()` should be used in
+    // preference to this function.
     pub fn get(&self, symbol: Symbol) -> &str {
         match self.strings.get(symbol.0.as_usize()) {
             Some(string) => string,
-            None => self.get(self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize]),
+            None => {
+                let symbol = self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize];
+                self.strings[symbol.0.as_usize()]
+            }
         }
     }
 }
@@ -611,11 +626,17 @@ fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
     GLOBALS.with(|globals| f(&mut *globals.symbol_interner.lock()))
 }
 
-/// Represents a string stored in the interner. Because the interner outlives any thread
-/// which uses this type, we can safely treat `string` which points to interner data,
-/// as an immortal string, as long as this type never crosses between threads.
-// FIXME: ensure that the interner outlives any thread which uses `LocalInternedString`,
-// by creating a new thread right after constructing the interner.
+/// An alternative to `Symbol` and `InternedString`, useful when the chars
+/// within the symbol need to be accessed. It is best used for temporary
+/// values.
+///
+/// Because the interner outlives any thread which uses this type, we can
+/// safely treat `string` which points to interner data, as an immortal string,
+/// as long as this type never crosses between threads.
+//
+// FIXME: ensure that the interner outlives any thread which uses
+// `LocalInternedString`, by creating a new thread right after constructing the
+// interner.
 #[derive(Clone, Copy, Hash, PartialOrd, Eq, Ord)]
 pub struct LocalInternedString {
     string: &'static str,
@@ -708,7 +729,19 @@ impl Encodable for LocalInternedString {
     }
 }
 
-/// Represents a string stored in the string interner.
+/// An alternative to `Symbol` that is focused on string contents. It has two
+/// main differences to `Symbol`.
+///
+/// First, its implementations of `Hash`, `PartialOrd` and `Ord` work with the
+/// string chars rather than the symbol integer. This is useful when hash
+/// stability is required across compile sessions, or a guaranteed sort
+/// ordering is required.
+///
+/// Second, gensym-ness is irrelevant. E.g.:
+/// ```
+/// assert_ne!(Symbol::gensym("x"), Symbol::gensym("x"))
+/// assert_eq!(Symbol::gensym("x").as_interned_str(), Symbol::gensym("x").as_interned_str())
+/// ```
 #[derive(Clone, Copy, Eq)]
 pub struct InternedString {
     symbol: Symbol,
@@ -725,6 +758,15 @@ impl InternedString {
         unsafe { f(&*str) }
     }
 
+    fn with2<F: FnOnce(&str, &str) -> R, R>(self, other: &InternedString, f: F) -> R {
+        let (self_str, other_str) = with_interner(|interner| {
+            (interner.get(self.symbol) as *const str,
+             interner.get(other.symbol) as *const str)
+        });
+        // This is safe for the same reason that `with` is safe.
+        unsafe { f(&*self_str, &*other_str) }
+    }
+
     pub fn as_symbol(self) -> Symbol {
         self.symbol
     }
@@ -745,7 +787,7 @@ impl PartialOrd<InternedString> for InternedString {
         if self.symbol == other.symbol {
             return Some(Ordering::Equal);
         }
-        self.with(|self_str| other.with(|other_str| self_str.partial_cmp(other_str)))
+        self.with2(other, |self_str, other_str| self_str.partial_cmp(other_str))
     }
 }
 
@@ -754,7 +796,7 @@ impl Ord for InternedString {
         if self.symbol == other.symbol {
             return Ordering::Equal;
         }
-        self.with(|self_str| other.with(|other_str| self_str.cmp(&other_str)))
+        self.with2(other, |self_str, other_str| self_str.cmp(other_str))
     }
 }
 
@@ -794,12 +836,6 @@ impl<'a> PartialEq<InternedString> for &'a String {
     }
 }
 
-impl std::convert::From<InternedString> for String {
-    fn from(val: InternedString) -> String {
-        val.as_symbol().to_string()
-    }
-}
-
 impl fmt::Debug for InternedString {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.with(|str| fmt::Debug::fmt(&str, f))
diff --git a/src/test/incremental/issue-60629.rs b/src/test/incremental/issue-60629.rs
new file mode 100644
index 00000000000..4807af4b3cf
--- /dev/null
+++ b/src/test/incremental/issue-60629.rs
@@ -0,0 +1,10 @@
+// revisions:rpass1 rpass2
+
+struct A;
+
+#[cfg(rpass2)]
+impl From<A> for () {
+    fn from(_: A) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/async-await/issue-60674.rs b/src/test/ui/async-await/issue-60674.rs
index 37e356e5baf..ecb80803383 100644
--- a/src/test/ui/async-await/issue-60674.rs
+++ b/src/test/ui/async-await/issue-60674.rs
@@ -11,4 +11,10 @@ extern crate issue_60674;
 #[issue_60674::attr]
 async fn f(mut x: u8) {}
 
+#[issue_60674::attr]
+async fn g((mut x, y, mut z): (u8, u8, u8)) {}
+
+#[issue_60674::attr]
+async fn g(mut x: u8, (a, mut b, c): (u8, u8, u8), y: u8) {}
+
 fn main() {}
diff --git a/src/test/ui/async-await/issue-60674.stdout b/src/test/ui/async-await/issue-60674.stdout
index a93944db1c5..86c3591b3af 100644
--- a/src/test/ui/async-await/issue-60674.stdout
+++ b/src/test/ui/async-await/issue-60674.stdout
@@ -1 +1,3 @@
 async fn f(mut x: u8) { }
+async fn g((mut x, y, mut z): (u8, u8, u8)) { }
+async fn g(mut x: u8, (a, mut b, c): (u8, u8, u8), y: u8) { }
diff --git a/src/test/ui/pattern/const-pat-ice.stderr b/src/test/ui/pattern/const-pat-ice.stderr
index 261e95229a7..260c2e04d74 100644
--- a/src/test/ui/pattern/const-pat-ice.stderr
+++ b/src/test/ui/pattern/const-pat-ice.stderr
@@ -1,4 +1,4 @@
-thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir/hair/pattern/_match.rs:1071:5
+thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir/hair/pattern/_match.rs:1083:5
 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 
 error: internal compiler error: unexpected panic
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs
new file mode 100644
index 00000000000..8cb9a8cf1f6
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs
@@ -0,0 +1,33 @@
+#![crate_type = "rlib"]
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+pub enum PartiallyInhabitedVariants {
+    Tuple(u8),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+pub struct IndirectUninhabitedEnum(UninhabitedEnum);
+
+pub struct IndirectUninhabitedStruct(UninhabitedStruct);
+
+pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
+
+pub struct IndirectUninhabitedVariants(UninhabitedVariants);
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs
new file mode 100644
index 00000000000..80b9dc4c1c3
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs
@@ -0,0 +1,38 @@
+// aux-build:uninhabited.rs
+#![feature(never_type)]
+
+extern crate uninhabited;
+
+use uninhabited::{
+    UninhabitedEnum,
+    UninhabitedStruct,
+    UninhabitedTupleStruct,
+    UninhabitedVariants,
+};
+
+// This test checks that uninhabited non-exhaustive types cannot coerce to any type, as the never
+// type can.
+
+struct A;
+
+fn can_coerce_never_type_to_anything(x: !) -> A {
+    x
+}
+
+fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr
new file mode 100644
index 00000000000..d05ee1d39ec
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/coercions.rs:23:5
+   |
+LL | fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+   |                                                                - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found enum `uninhabited::UninhabitedEnum`
+   |
+   = note: expected type `A`
+              found type `uninhabited::UninhabitedEnum`
+
+error[E0308]: mismatched types
+  --> $DIR/coercions.rs:27:5
+   |
+LL | fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+   |                                                                               - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found struct `uninhabited::UninhabitedTupleStruct`
+   |
+   = note: expected type `A`
+              found type `uninhabited::UninhabitedTupleStruct`
+
+error[E0308]: mismatched types
+  --> $DIR/coercions.rs:31:5
+   |
+LL | fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+   |                                                                    - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found struct `uninhabited::UninhabitedStruct`
+   |
+   = note: expected type `A`
+              found type `uninhabited::UninhabitedStruct`
+
+error[E0308]: mismatched types
+  --> $DIR/coercions.rs:35:5
+   |
+LL | fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
+   |                                                                                  - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found enum `uninhabited::UninhabitedVariants`
+   |
+   = note: expected type `A`
+              found type `uninhabited::UninhabitedVariants`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs
new file mode 100644
index 00000000000..803a542f8aa
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs
@@ -0,0 +1,46 @@
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+struct A;
+
+// This test checks that uninhabited non-exhaustive types defined in the same crate cannot coerce
+// to any type, as the never type can.
+
+fn can_coerce_never_type_to_anything(x: !) -> A {
+    x
+}
+
+fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
+    x //~ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr
new file mode 100644
index 00000000000..8f6b709bb1f
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/coercions_same_crate.rs:31:5
+   |
+LL | fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+   |                                                                - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found enum `UninhabitedEnum`
+   |
+   = note: expected type `A`
+              found type `UninhabitedEnum`
+
+error[E0308]: mismatched types
+  --> $DIR/coercions_same_crate.rs:35:5
+   |
+LL | fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+   |                                                                               - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found struct `UninhabitedTupleStruct`
+   |
+   = note: expected type `A`
+              found type `UninhabitedTupleStruct`
+
+error[E0308]: mismatched types
+  --> $DIR/coercions_same_crate.rs:39:5
+   |
+LL | fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+   |                                                                    - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found struct `UninhabitedStruct`
+   |
+   = note: expected type `A`
+              found type `UninhabitedStruct`
+
+error[E0308]: mismatched types
+  --> $DIR/coercions_same_crate.rs:43:5
+   |
+LL | fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
+   |                                                                                  - expected `A` because of return type
+LL |     x
+   |     ^ expected struct `A`, found enum `UninhabitedVariants`
+   |
+   = note: expected type `A`
+              found type `UninhabitedVariants`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs
new file mode 100644
index 00000000000..98a7fdbc504
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs
@@ -0,0 +1,36 @@
+// aux-build:uninhabited.rs
+#![feature(never_type)]
+
+extern crate uninhabited;
+
+use uninhabited::{
+    IndirectUninhabitedEnum,
+    IndirectUninhabitedStruct,
+    IndirectUninhabitedTupleStruct,
+    IndirectUninhabitedVariants,
+};
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type through a level of
+// indirection from an extern crate will not compile.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: IndirectUninhabitedEnum) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: IndirectUninhabitedStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: IndirectUninhabitedTupleStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(
+    x: IndirectUninhabitedVariants,
+) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr
new file mode 100644
index 00000000000..af82022e1da
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr
@@ -0,0 +1,35 @@
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `uninhabited::IndirectUninhabitedEnum` is not handled
+  --> $DIR/indirect_match.rs:19:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `uninhabited::IndirectUninhabitedStruct` is not handled
+  --> $DIR/indirect_match.rs:23:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `uninhabited::IndirectUninhabitedTupleStruct` is not handled
+  --> $DIR/indirect_match.rs:27:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `uninhabited::IndirectUninhabitedVariants` is not handled
+  --> $DIR/indirect_match.rs:33:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs
new file mode 100644
index 00000000000..3c8d495e12c
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs
@@ -0,0 +1,52 @@
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+pub struct IndirectUninhabitedEnum(UninhabitedEnum);
+
+pub struct IndirectUninhabitedStruct(UninhabitedStruct);
+
+pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
+
+pub struct IndirectUninhabitedVariants(UninhabitedVariants);
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type through a level of
+// indirection from the defining crate will not compile without `#![feature(exhaustive_patterns)]`.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: IndirectUninhabitedEnum) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: IndirectUninhabitedStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: IndirectUninhabitedTupleStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(
+    x: IndirectUninhabitedVariants,
+) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr
new file mode 100644
index 00000000000..27b120792d6
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr
@@ -0,0 +1,59 @@
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `IndirectUninhabitedEnum` is not handled
+  --> $DIR/indirect_match_same_crate.rs:35:11
+   |
+LL | pub struct IndirectUninhabitedEnum(UninhabitedEnum);
+   | ----------------------------------------------------
+   | |          |
+   | |          variant not covered
+   | `IndirectUninhabitedEnum` defined here
+...
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `IndirectUninhabitedStruct` is not handled
+  --> $DIR/indirect_match_same_crate.rs:39:11
+   |
+LL | pub struct IndirectUninhabitedStruct(UninhabitedStruct);
+   | --------------------------------------------------------
+   | |          |
+   | |          variant not covered
+   | `IndirectUninhabitedStruct` defined here
+...
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `IndirectUninhabitedTupleStruct` is not handled
+  --> $DIR/indirect_match_same_crate.rs:43:11
+   |
+LL | pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
+   | ------------------------------------------------------------------
+   | |          |
+   | |          variant not covered
+   | `IndirectUninhabitedTupleStruct` defined here
+...
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `IndirectUninhabitedVariants` is not handled
+  --> $DIR/indirect_match_same_crate.rs:49:11
+   |
+LL | pub struct IndirectUninhabitedVariants(UninhabitedVariants);
+   | ------------------------------------------------------------
+   | |          |
+   | |          variant not covered
+   | `IndirectUninhabitedVariants` defined here
+...
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs
new file mode 100644
index 00000000000..be86519ecb1
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs
@@ -0,0 +1,40 @@
+// aux-build:uninhabited.rs
+#![deny(unreachable_patterns)]
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+
+extern crate uninhabited;
+
+use uninhabited::{
+    IndirectUninhabitedEnum,
+    IndirectUninhabitedStruct,
+    IndirectUninhabitedTupleStruct,
+    IndirectUninhabitedVariants,
+};
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type through a level of
+// indirection from an extern crate will not compile. In particular, this enables the
+// `exhaustive_patterns` feature as this can change the branch used in the compiler to determine
+// this.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: IndirectUninhabitedEnum) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: IndirectUninhabitedStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: IndirectUninhabitedTupleStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(
+    x: IndirectUninhabitedVariants,
+) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr
new file mode 100644
index 00000000000..17a8d010072
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr
@@ -0,0 +1,35 @@
+error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty
+  --> $DIR/indirect_match_with_exhaustive_patterns.rs:23:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty
+  --> $DIR/indirect_match_with_exhaustive_patterns.rs:27:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty
+  --> $DIR/indirect_match_with_exhaustive_patterns.rs:31:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty
+  --> $DIR/indirect_match_with_exhaustive_patterns.rs:37:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs
new file mode 100644
index 00000000000..5dbd38e07df
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs
@@ -0,0 +1,58 @@
+// compile-pass
+// skip-codegen
+#![deny(unreachable_patterns)]
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+pub struct IndirectUninhabitedEnum(UninhabitedEnum);
+
+pub struct IndirectUninhabitedStruct(UninhabitedStruct);
+
+pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
+
+pub struct IndirectUninhabitedVariants(UninhabitedVariants);
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type from the defining crate
+// will compile. In particular, this enables the `exhaustive_patterns` feature as this can
+// change the branch used in the compiler to determine this.
+// Codegen is skipped because tests with long names can cause issues on Windows CI, see #60648.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: IndirectUninhabitedEnum) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: IndirectUninhabitedStruct) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: IndirectUninhabitedTupleStruct) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(
+    x: IndirectUninhabitedVariants,
+) -> A {
+    match x {}
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.rs
new file mode 100644
index 00000000000..e54098d4d48
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.rs
@@ -0,0 +1,34 @@
+// aux-build:uninhabited.rs
+#![feature(never_type)]
+
+extern crate uninhabited;
+
+use uninhabited::{
+    UninhabitedEnum,
+    UninhabitedStruct,
+    UninhabitedTupleStruct,
+    UninhabitedVariants,
+};
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type from an extern crate
+// will not compile.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr
new file mode 100644
index 00000000000..de39688f45a
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr
@@ -0,0 +1,35 @@
+error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
+  --> $DIR/match.rs:19:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `uninhabited::UninhabitedStruct` is not handled
+  --> $DIR/match.rs:23:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `uninhabited::UninhabitedTupleStruct` is not handled
+  --> $DIR/match.rs:27:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: multiple patterns of type `uninhabited::UninhabitedVariants` are not handled
+  --> $DIR/match.rs:31:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs
new file mode 100644
index 00000000000..6405dd3bd65
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs
@@ -0,0 +1,42 @@
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type from the defining crate
+// will compile.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr
new file mode 100644
index 00000000000..410285a39a9
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr
@@ -0,0 +1,49 @@
+error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `UninhabitedStruct` is not handled
+  --> $DIR/match_same_crate.rs:31:11
+   |
+LL |   pub struct UninhabitedStruct {
+   |   -          ----------------- variant not covered
+   |  _|
+   | |
+LL | |     _priv: !,
+LL | | }
+   | |_- `UninhabitedStruct` defined here
+...
+LL |       match x {}
+   |             ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `UninhabitedTupleStruct` is not handled
+  --> $DIR/match_same_crate.rs:35:11
+   |
+LL | pub struct UninhabitedTupleStruct(!);
+   | -------------------------------------
+   | |          |
+   | |          variant not covered
+   | `UninhabitedTupleStruct` defined here
+...
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: multiple patterns of type `UninhabitedVariants` are not handled
+  --> $DIR/match_same_crate.rs:39:11
+   |
+LL | / pub enum UninhabitedVariants {
+LL | |     #[non_exhaustive] Tuple(!),
+   | |                       ----- variant not covered
+LL | |     #[non_exhaustive] Struct { x: ! }
+   | |                       ------ variant not covered
+LL | | }
+   | |_- `UninhabitedVariants` defined here
+...
+LL |       match x {}
+   |             ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs
new file mode 100644
index 00000000000..900dfff652e
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs
@@ -0,0 +1,37 @@
+// aux-build:uninhabited.rs
+#![deny(unreachable_patterns)]
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+
+extern crate uninhabited;
+
+use uninhabited::{
+    UninhabitedEnum,
+    UninhabitedStruct,
+    UninhabitedTupleStruct,
+    UninhabitedVariants,
+};
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type from an extern crate
+// will not compile. In particular, this enables the `exhaustive_patterns` feature as this can
+// change the branch used in the compiler to determine this.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A {
+    match x {} //~ ERROR non-exhaustive patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr
new file mode 100644
index 00000000000..48a888bc50b
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr
@@ -0,0 +1,35 @@
+error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
+  --> $DIR/match_with_exhaustive_patterns.rs:22:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty
+  --> $DIR/match_with_exhaustive_patterns.rs:26:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty
+  --> $DIR/match_with_exhaustive_patterns.rs:30:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedVariants` is non-empty
+  --> $DIR/match_with_exhaustive_patterns.rs:34:11
+   |
+LL |     match x {}
+   |           ^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs
new file mode 100644
index 00000000000..74922d4bcb5
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs
@@ -0,0 +1,48 @@
+// compile-pass
+// skip-codegen
+#![deny(unreachable_patterns)]
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+struct A;
+
+// This test checks that an empty match on a non-exhaustive uninhabited type from the defining crate
+// will compile. In particular, this enables the `exhaustive_patterns` feature as this can
+// change the branch used in the compiler to determine this.
+// Codegen is skipped because tests with long names can cause issues on Windows CI, see #60648.
+
+fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
+    match x {}
+}
+
+fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A {
+    match x {}
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs
new file mode 100644
index 00000000000..97061310d19
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs
@@ -0,0 +1,59 @@
+// aux-build:uninhabited.rs
+// compile-pass
+#![deny(unreachable_patterns)]
+#![feature(exhaustive_patterns)]
+
+extern crate uninhabited;
+
+use uninhabited::{
+    PartiallyInhabitedVariants,
+    UninhabitedEnum,
+    UninhabitedStruct,
+    UninhabitedTupleStruct,
+    UninhabitedVariants,
+};
+
+fn uninhabited_enum() -> Option<UninhabitedEnum> {
+    None
+}
+
+fn uninhabited_variant() -> Option<UninhabitedVariants> {
+    None
+}
+
+fn partially_inhabited_variant() -> PartiallyInhabitedVariants {
+    PartiallyInhabitedVariants::Tuple(3)
+}
+
+fn uninhabited_struct() -> Option<UninhabitedStruct> {
+    None
+}
+
+fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> {
+    None
+}
+
+// This test checks that non-exhaustive types that would normally be considered uninhabited within
+// the defining crate are not considered uninhabited from extern crates.
+
+fn main() {
+    match uninhabited_enum() {
+        Some(_x) => (), // This line would normally error.
+        None => (),
+    }
+
+    match uninhabited_variant() {
+        Some(_x) => (), // This line would normally error.
+        None => (),
+    }
+
+    // This line would normally error.
+    while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() {
+    }
+
+    while let Some(_x) = uninhabited_struct() { // This line would normally error.
+    }
+
+    while let Some(_x) = uninhabited_tuple_struct() { // This line would normally error.
+    }
+}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs
new file mode 100644
index 00000000000..302a35cab5f
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs
@@ -0,0 +1,71 @@
+#![deny(unreachable_patterns)]
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+pub enum UninhabitedEnum {
+}
+
+#[non_exhaustive]
+pub struct UninhabitedTupleStruct(!);
+
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+    _priv: !,
+}
+
+pub enum UninhabitedVariants {
+    #[non_exhaustive] Tuple(!),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+pub enum PartiallyInhabitedVariants {
+    Tuple(u8),
+    #[non_exhaustive] Struct { x: ! }
+}
+
+fn uninhabited_enum() -> Option<UninhabitedEnum> {
+    None
+}
+
+fn uninhabited_variant() -> Option<UninhabitedVariants> {
+    None
+}
+
+fn partially_inhabited_variant() -> PartiallyInhabitedVariants {
+    PartiallyInhabitedVariants::Tuple(3)
+}
+
+fn uninhabited_struct() -> Option<UninhabitedStruct> {
+    None
+}
+
+fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> {
+    None
+}
+
+// This test checks that non-exhaustive types that would normally be considered uninhabited within
+// the defining crate are still considered uninhabited.
+
+fn main() {
+    match uninhabited_enum() {
+        Some(_x) => (), //~ ERROR unreachable pattern
+        None => (),
+    }
+
+    match uninhabited_variant() {
+        Some(_x) => (), //~ ERROR unreachable pattern
+        None => (),
+    }
+
+    while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() {
+        //~^ ERROR unreachable pattern
+    }
+
+    while let Some(_x) = uninhabited_struct() { //~ ERROR unreachable pattern
+    }
+
+    while let Some(_x) = uninhabited_tuple_struct() { //~ ERROR unreachable pattern
+    }
+}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr
new file mode 100644
index 00000000000..72f37d9a60b
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr
@@ -0,0 +1,38 @@
+error: unreachable pattern
+  --> $DIR/patterns_same_crate.rs:53:9
+   |
+LL |         Some(_x) => (),
+   |         ^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/patterns_same_crate.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/patterns_same_crate.rs:58:9
+   |
+LL |         Some(_x) => (),
+   |         ^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/patterns_same_crate.rs:62:15
+   |
+LL |     while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/patterns_same_crate.rs:66:15
+   |
+LL |     while let Some(_x) = uninhabited_struct() {
+   |               ^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/patterns_same_crate.rs:69:15
+   |
+LL |     while let Some(_x) = uninhabited_tuple_struct() {
+   |               ^^^^^^^^
+
+error: aborting due to 5 previous errors
+