Skip to content

Commit 8838b91

Browse files
committed
Fix uninhabitedness of non-exhaustive enums.
This commit ensures that non-exhaustive enums are considered inhabited when used in extern crates.
1 parent 0d034a2 commit 8838b91

15 files changed

+398
-109
lines changed

src/librustc/ty/inhabitedness/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,14 @@ impl<'a, 'gcx, 'tcx> AdtDef {
113113
tcx: TyCtxt<'a, 'gcx, 'tcx>,
114114
substs: SubstsRef<'tcx>) -> DefIdForest
115115
{
116-
DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
117-
v.uninhabited_from(tcx, substs, self.adt_kind())
118-
}))
116+
// Non-exhaustive ADTs from other crates are always considered inhabited.
117+
if self.is_variant_list_non_exhaustive() && !self.did.is_local() {
118+
DefIdForest::empty()
119+
} else {
120+
DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
121+
v.uninhabited_from(tcx, substs, self.adt_kind())
122+
}))
123+
}
119124
}
120125
}
121126

src/librustc_mir/hair/pattern/check_match.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,11 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
208208
.map(|variant| variant.ident)
209209
.collect();
210210
}
211-
def.variants.is_empty()
211+
212+
let is_non_exhaustive_and_non_local =
213+
def.is_variant_list_non_exhaustive() && !def.did.is_local();
214+
215+
!(is_non_exhaustive_and_non_local) && def.variants.is_empty()
212216
},
213217
_ => false
214218
}
Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,38 @@
11
// aux-build:uninhabited.rs
2-
// compile-pass
3-
#![deny(unreachable_patterns)]
4-
#![feature(exhaustive_patterns)]
2+
#![feature(never_type)]
53

64
extern crate uninhabited;
75

86
use uninhabited::{
9-
PartiallyInhabitedVariants,
107
UninhabitedEnum,
118
UninhabitedStruct,
129
UninhabitedTupleStruct,
1310
UninhabitedVariants,
1411
};
1512

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

20-
fn uninhabited_variant() -> Option<UninhabitedVariants> {
21-
None
22-
}
16+
struct A;
2317

24-
fn partially_inhabited_variant() -> PartiallyInhabitedVariants {
25-
PartiallyInhabitedVariants::Tuple(3)
18+
fn can_coerce_never_type_to_anything(x: !) -> A {
19+
x
2620
}
2721

28-
fn uninhabited_struct() -> Option<UninhabitedStruct> {
29-
None
22+
fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
23+
x //~ ERROR mismatched types
3024
}
3125

32-
fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> {
33-
None
26+
fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
27+
x //~ ERROR mismatched types
3428
}
3529

36-
// This test checks that non-exhaustive types that would normally be considered uninhabited within
37-
// the defining crate are not considered uninhabited from extern crates.
38-
39-
fn main() {
40-
match uninhabited_enum() {
41-
Some(_x) => (), // This line would normally error.
42-
None => (),
43-
}
44-
45-
match uninhabited_variant() {
46-
Some(_x) => (), // This line would normally error.
47-
None => (),
48-
}
49-
50-
// This line would normally error.
51-
while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() {
52-
}
53-
54-
while let Some(_x) = uninhabited_struct() { // This line would normally error.
55-
}
30+
fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
31+
x //~ ERROR mismatched types
32+
}
5633

57-
while let Some(_x) = uninhabited_tuple_struct() { // This line would normally error.
58-
}
34+
fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
35+
x //~ ERROR mismatched types
5936
}
37+
38+
fn main() {}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/uninhabited.rs:23:5
3+
|
4+
LL | fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
5+
| - expected `A` because of return type
6+
LL | x
7+
| ^ expected struct `A`, found enum `uninhabited::UninhabitedEnum`
8+
|
9+
= note: expected type `A`
10+
found type `uninhabited::UninhabitedEnum`
11+
12+
error[E0308]: mismatched types
13+
--> $DIR/uninhabited.rs:27:5
14+
|
15+
LL | fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
16+
| - expected `A` because of return type
17+
LL | x
18+
| ^ expected struct `A`, found struct `uninhabited::UninhabitedTupleStruct`
19+
|
20+
= note: expected type `A`
21+
found type `uninhabited::UninhabitedTupleStruct`
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/uninhabited.rs:31:5
25+
|
26+
LL | fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
27+
| - expected `A` because of return type
28+
LL | x
29+
| ^ expected struct `A`, found struct `uninhabited::UninhabitedStruct`
30+
|
31+
= note: expected type `A`
32+
found type `uninhabited::UninhabitedStruct`
33+
34+
error[E0308]: mismatched types
35+
--> $DIR/uninhabited.rs:35:5
36+
|
37+
LL | fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
38+
| - expected `A` because of return type
39+
LL | x
40+
| ^ expected struct `A`, found enum `uninhabited::UninhabitedVariants`
41+
|
42+
= note: expected type `A`
43+
found type `uninhabited::UninhabitedVariants`
44+
45+
error: aborting due to 4 previous errors
46+
47+
For more information about this error, try `rustc --explain E0308`.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// aux-build:uninhabited.rs
2+
#![feature(never_type)]
3+
4+
extern crate uninhabited;
5+
6+
use uninhabited::{
7+
UninhabitedEnum,
8+
};
9+
10+
struct A;
11+
12+
// This test checks that an empty match on a non-exhaustive uninhabited type from an extern crate
13+
// will not compile.
14+
15+
fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
16+
match x {} //~ ERROR non-exhaustive patterns
17+
}
18+
19+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
2+
--> $DIR/uninhabited_match.rs:16:11
3+
|
4+
LL | match x {}
5+
| ^
6+
|
7+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0004`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// compile-pass
2+
#![feature(never_type)]
3+
#![feature(non_exhaustive)]
4+
5+
#[non_exhaustive]
6+
pub enum UninhabitedEnum {
7+
}
8+
9+
struct A;
10+
11+
// This test checks that an empty match on a non-exhaustive uninhabited type from the defining crate
12+
// will compile.
13+
14+
fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
15+
match x {}
16+
}
17+
18+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// aux-build:uninhabited.rs
2+
#![deny(unreachable_patterns)]
3+
#![feature(exhaustive_patterns)]
4+
#![feature(never_type)]
5+
6+
extern crate uninhabited;
7+
8+
use uninhabited::{
9+
UninhabitedEnum,
10+
};
11+
12+
struct A;
13+
14+
// This test checks that an empty match on a non-exhaustive uninhabited type from an extern crate
15+
// will not compile. In particular, this enables the `exhaustive_patterns` feature as this can
16+
// change the branch used in the compiler to determine this.
17+
18+
fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
19+
match x {} //~ ERROR non-exhaustive patterns
20+
}
21+
22+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
2+
--> $DIR/uninhabited_match_with_exhaustive_patterns.rs:19:11
3+
|
4+
LL | match x {}
5+
| ^
6+
|
7+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0004`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-pass
2+
#![deny(unreachable_patterns)]
3+
#![feature(exhaustive_patterns)]
4+
#![feature(never_type)]
5+
#![feature(non_exhaustive)]
6+
7+
#[non_exhaustive]
8+
pub enum UninhabitedEnum {
9+
}
10+
11+
struct A;
12+
13+
// This test checks that an empty match on a non-exhaustive uninhabited type from the defining crate
14+
// will compile. In particular, this enables the `exhaustive_patterns` feature as this can
15+
// change the branch used in the compiler to determine this.
16+
17+
fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
18+
match x {}
19+
}
20+
21+
fn main() {}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// aux-build:uninhabited.rs
2+
// compile-pass
3+
#![deny(unreachable_patterns)]
4+
#![feature(exhaustive_patterns)]
5+
6+
extern crate uninhabited;
7+
8+
use uninhabited::{
9+
PartiallyInhabitedVariants,
10+
UninhabitedEnum,
11+
UninhabitedStruct,
12+
UninhabitedTupleStruct,
13+
UninhabitedVariants,
14+
};
15+
16+
fn uninhabited_enum() -> Option<UninhabitedEnum> {
17+
None
18+
}
19+
20+
fn uninhabited_variant() -> Option<UninhabitedVariants> {
21+
None
22+
}
23+
24+
fn partially_inhabited_variant() -> PartiallyInhabitedVariants {
25+
PartiallyInhabitedVariants::Tuple(3)
26+
}
27+
28+
fn uninhabited_struct() -> Option<UninhabitedStruct> {
29+
None
30+
}
31+
32+
fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> {
33+
None
34+
}
35+
36+
// This test checks that non-exhaustive types that would normally be considered uninhabited within
37+
// the defining crate are not considered uninhabited from extern crates.
38+
39+
fn main() {
40+
match uninhabited_enum() {
41+
Some(_x) => (), // This line would normally error.
42+
None => (),
43+
}
44+
45+
match uninhabited_variant() {
46+
Some(_x) => (), // This line would normally error.
47+
None => (),
48+
}
49+
50+
// This line would normally error.
51+
while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() {
52+
}
53+
54+
while let Some(_x) = uninhabited_struct() { // This line would normally error.
55+
}
56+
57+
while let Some(_x) = uninhabited_tuple_struct() { // This line would normally error.
58+
}
59+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#![deny(unreachable_patterns)]
2+
#![feature(exhaustive_patterns)]
3+
#![feature(never_type)]
4+
#![feature(non_exhaustive)]
5+
6+
#[non_exhaustive]
7+
pub enum UninhabitedEnum {
8+
}
9+
10+
#[non_exhaustive]
11+
pub struct UninhabitedTupleStruct(!);
12+
13+
#[non_exhaustive]
14+
pub struct UninhabitedStruct {
15+
_priv: !,
16+
}
17+
18+
pub enum UninhabitedVariants {
19+
#[non_exhaustive] Tuple(!),
20+
#[non_exhaustive] Struct { x: ! }
21+
}
22+
23+
pub enum PartiallyInhabitedVariants {
24+
Tuple(u8),
25+
#[non_exhaustive] Struct { x: ! }
26+
}
27+
28+
fn uninhabited_enum() -> Option<UninhabitedEnum> {
29+
None
30+
}
31+
32+
fn uninhabited_variant() -> Option<UninhabitedVariants> {
33+
None
34+
}
35+
36+
fn partially_inhabited_variant() -> PartiallyInhabitedVariants {
37+
PartiallyInhabitedVariants::Tuple(3)
38+
}
39+
40+
fn uninhabited_struct() -> Option<UninhabitedStruct> {
41+
None
42+
}
43+
44+
fn uninhabited_tuple_struct() -> Option<UninhabitedTupleStruct> {
45+
None
46+
}
47+
48+
// This test checks that non-exhaustive types that would normally be considered uninhabited within
49+
// the defining crate are still considered uninhabited.
50+
51+
fn main() {
52+
match uninhabited_enum() {
53+
Some(_x) => (), //~ ERROR unreachable pattern
54+
None => (),
55+
}
56+
57+
match uninhabited_variant() {
58+
Some(_x) => (), //~ ERROR unreachable pattern
59+
None => (),
60+
}
61+
62+
while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() {
63+
//~^ ERROR unreachable pattern
64+
}
65+
66+
while let Some(_x) = uninhabited_struct() { //~ ERROR unreachable pattern
67+
}
68+
69+
while let Some(_x) = uninhabited_tuple_struct() { //~ ERROR unreachable pattern
70+
}
71+
}

0 commit comments

Comments
 (0)