diff options
| author | David Wood <david@davidtw.co> | 2019-05-04 11:03:06 +0100 |
|---|---|---|
| committer | David Wood <david@davidtw.co> | 2019-05-10 17:52:12 +0100 |
| commit | 8838b9130e2f6551077f432dddd5a8077ae4d2e1 (patch) | |
| tree | d717430ddb3eb5d195028c28c1868b163af46de3 /src | |
| parent | 0d034a2e4dd9418fa41aba4ebdd1ebc31a08c9e9 (diff) | |
| download | rust-8838b9130e2f6551077f432dddd5a8077ae4d2e1.tar.gz rust-8838b9130e2f6551077f432dddd5a8077ae4d2e1.zip | |
Fix uninhabitedness of non-exhaustive enums.
This commit ensures that non-exhaustive enums are considered inhabited when used in extern crates.
Diffstat (limited to 'src')
15 files changed, 398 insertions, 109 deletions
diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 2fc07fb20bf..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()) + })) + } } } 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/test/ui/rfc-2008-non-exhaustive/uninhabited.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited.rs index 97061310d19..80b9dc4c1c3 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited.rs +++ b/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() {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited.stderr new file mode 100644 index 00000000000..490a6c10117 --- /dev/null +++ b/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`. 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..0166b2e46cb --- /dev/null +++ b/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() {} 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..3000e1b0930 --- /dev/null +++ b/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`. 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..d8b1c3810f3 --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_match_same_crate.rs @@ -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() {} 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..d82010158cc --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_match_with_exhaustive_patterns.rs @@ -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() {} 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..73d9e689b55 --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_match_with_exhaustive_patterns.stderr @@ -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`. 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..da814a70a75 --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_match_with_exhaustive_patterns_same_crate.rs @@ -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() {} 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..8e995632b2f --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_patterns_same_crate.stderr @@ -0,0 +1,38 @@ +error: unreachable pattern + --> $DIR/uninhabited_patterns_same_crate.rs:53:9 + | +LL | Some(_x) => (), + | ^^^^^^^^ + | +note: lint level defined here + --> $DIR/uninhabited_patterns_same_crate.rs:1:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/uninhabited_patterns_same_crate.rs:58:9 + | +LL | Some(_x) => (), + | ^^^^^^^^ + +error: unreachable pattern + --> $DIR/uninhabited_patterns_same_crate.rs:62:15 + | +LL | while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/uninhabited_patterns_same_crate.rs:66:15 + | +LL | while let Some(_x) = uninhabited_struct() { + | ^^^^^^^^ + +error: unreachable pattern + --> $DIR/uninhabited_patterns_same_crate.rs:69:15 + | +LL | while let Some(_x) = uninhabited_tuple_struct() { + | ^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.rs index 302a35cab5f..803a542f8aa 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.rs @@ -1,5 +1,3 @@ -#![deny(unreachable_patterns)] -#![feature(exhaustive_patterns)] #![feature(never_type)] #![feature(non_exhaustive)] @@ -20,52 +18,29 @@ pub enum UninhabitedVariants { #[non_exhaustive] Struct { x: ! } } -pub enum PartiallyInhabitedVariants { - Tuple(u8), - #[non_exhaustive] Struct { x: ! } -} +struct A; -fn uninhabited_enum() -> Option<UninhabitedEnum> { - None -} +// This test checks that uninhabited non-exhaustive types defined in the same crate cannot coerce +// to any type, as the never type can. -fn uninhabited_variant() -> Option<UninhabitedVariants> { - None +fn can_coerce_never_type_to_anything(x: !) -> A { + x } -fn partially_inhabited_variant() -> PartiallyInhabitedVariants { - PartiallyInhabitedVariants::Tuple(3) +fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A { + x //~ ERROR mismatched types } -fn uninhabited_struct() -> Option<UninhabitedStruct> { - None +fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A { + x //~ ERROR mismatched types } -fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> { - None +fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A { + x //~ ERROR mismatched types } -// 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 - } +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_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.stderr index 942f004c3cf..ea79e7105d5 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited_same_crate.stderr @@ -1,38 +1,47 @@ -error: unreachable pattern - --> $DIR/uninhabited_same_crate.rs:53:9 +error[E0308]: mismatched types + --> $DIR/uninhabited_same_crate.rs:31:5 | -LL | Some(_x) => (), - | ^^^^^^^^ +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: lint level defined here - --> $DIR/uninhabited_same_crate.rs:1:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ + = note: expected type `A` + found type `UninhabitedEnum` -error: unreachable pattern - --> $DIR/uninhabited_same_crate.rs:58:9 +error[E0308]: mismatched types + --> $DIR/uninhabited_same_crate.rs:35:5 | -LL | Some(_x) => (), - | ^^^^^^^^ - -error: unreachable pattern - --> $DIR/uninhabited_same_crate.rs:62:15 +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` | -LL | while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected type `A` + found type `UninhabitedTupleStruct` -error: unreachable pattern - --> $DIR/uninhabited_same_crate.rs:66:15 +error[E0308]: mismatched types + --> $DIR/uninhabited_same_crate.rs:39:5 | -LL | while let Some(_x) = uninhabited_struct() { - | ^^^^^^^^ +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: unreachable pattern - --> $DIR/uninhabited_same_crate.rs:69:15 +error[E0308]: mismatched types + --> $DIR/uninhabited_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` | -LL | while let Some(_x) = uninhabited_tuple_struct() { - | ^^^^^^^^ + = note: expected type `A` + found type `UninhabitedVariants` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0308`. |
