Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix uninhabitedness of non-exhaustive enums.
This commit ensures that non-exhaustive enums are considered inhabited
when used in extern crates.
  • Loading branch information
davidtwco committed May 10, 2019
1 parent 0d034a2 commit 8838b91
Show file tree
Hide file tree
Showing 15 changed files with 398 additions and 109 deletions.
11 changes: 8 additions & 3 deletions src/librustc/ty/inhabitedness/mod.rs
Expand Up @@ -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())
}))
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/librustc_mir/hair/pattern/check_match.rs
Expand Up @@ -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
}
Expand Down
55 changes: 17 additions & 38 deletions src/test/ui/rfc-2008-non-exhaustive/uninhabited.rs
@@ -1,59 +1,38 @@
// aux-build:uninhabited.rs
// compile-pass
#![deny(unreachable_patterns)]
#![feature(exhaustive_patterns)]
#![feature(never_type)]

extern crate uninhabited;

use uninhabited::{
PartiallyInhabitedVariants,
UninhabitedEnum,
UninhabitedStruct,
UninhabitedTupleStruct,
UninhabitedVariants,
};

fn uninhabited_enum() -> Option<UninhabitedEnum> {
None
}
// This test checks that uninhabited non-exhaustive types cannot coerce to any type, as the never
// type can.

fn uninhabited_variant() -> Option<UninhabitedVariants> {
None
}
struct A;

fn partially_inhabited_variant() -> PartiallyInhabitedVariants {
PartiallyInhabitedVariants::Tuple(3)
fn can_coerce_never_type_to_anything(x: !) -> A {
x
}

fn uninhabited_struct() -> Option<UninhabitedStruct> {
None
fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
x //~ ERROR mismatched types
}

fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> {
None
fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
x //~ ERROR mismatched types
}

// 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.
}
fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
x //~ ERROR mismatched types
}

while let Some(_x) = uninhabited_tuple_struct() { // This line would normally error.
}
fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
x //~ ERROR mismatched types
}

fn main() {}
47 changes: 47 additions & 0 deletions src/test/ui/rfc-2008-non-exhaustive/uninhabited.stderr
@@ -0,0 +1,47 @@
error[E0308]: mismatched types
--> $DIR/uninhabited.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/uninhabited.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/uninhabited.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/uninhabited.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`.
19 changes: 19 additions & 0 deletions src/test/ui/rfc-2008-non-exhaustive/uninhabited_match.rs
@@ -0,0 +1,19 @@
// aux-build:uninhabited.rs
#![feature(never_type)]

extern crate uninhabited;

use uninhabited::{
UninhabitedEnum,
};

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 main() {}
11 changes: 11 additions & 0 deletions src/test/ui/rfc-2008-non-exhaustive/uninhabited_match.stderr
@@ -0,0 +1,11 @@
error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
--> $DIR/uninhabited_match.rs:16: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 previous error

For more information about this error, try `rustc --explain E0004`.
@@ -0,0 +1,18 @@
// compile-pass
#![feature(never_type)]
#![feature(non_exhaustive)]

#[non_exhaustive]
pub enum UninhabitedEnum {
}

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 main() {}
@@ -0,0 +1,22 @@
// aux-build:uninhabited.rs
#![deny(unreachable_patterns)]
#![feature(exhaustive_patterns)]
#![feature(never_type)]

extern crate uninhabited;

use uninhabited::{
UninhabitedEnum,
};

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 main() {}
@@ -0,0 +1,11 @@
error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
--> $DIR/uninhabited_match_with_exhaustive_patterns.rs:19: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 previous error

For more information about this error, try `rustc --explain E0004`.
@@ -0,0 +1,21 @@
// compile-pass
#![deny(unreachable_patterns)]
#![feature(exhaustive_patterns)]
#![feature(never_type)]
#![feature(non_exhaustive)]

#[non_exhaustive]
pub enum UninhabitedEnum {
}

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.

fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
match x {}
}

fn main() {}
59 changes: 59 additions & 0 deletions 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.
}
}
@@ -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
}
}

0 comments on commit 8838b91

Please sign in to comment.