Skip to content

Commit d94cb9f

Browse files
authored
Rollup merge of rust-lang#60529 - davidtwco:rfc-2008-uninhabited, r=petrochenkov
RFC 2008: Uninhabitedness fixes for enum variants and tests Part of rust-lang#44109. At the request of @Centril, this PR adds tests asserting that uninhabited non-exhaustive types are considered inhabited in extern crates. In adding these tests, I fixed an oversight in the implementation of RFC 2008 on enum variants that resulted in non-exhaustive enum variants being considered uninhabited in extern crates. Before this PR, these lines would error: ```rust // extern crate pub enum UninhabitedVariants { #[non_exhaustive] Tuple(!), #[non_exhaustive] Struct { x: ! } } pub enum PartiallyInhabitedVariants { Tuple(u8), #[non_exhaustive] Struct { x: ! } } // current crate match uninhabited_variant() /* fn() -> Option<UninhabitedVariants> */ { Some(_x) => (), //~ ERROR unreachable pattern None => (), } while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() /* fn() -> PartiallyInhabitedVariants */ { //~^ ERROR unreachable pattern } ``` cc @Centril r? @petrochenkov
2 parents cff1bdb + 1f0fb03 commit d94cb9f

26 files changed

+1019
-12
lines changed

src/librustc/ty/inhabitedness/mod.rs

+16-6
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

@@ -134,9 +139,14 @@ impl<'a, 'gcx, 'tcx> VariantDef {
134139
AdtKind::Enum => true,
135140
AdtKind::Struct => false,
136141
};
137-
DefIdForest::union(tcx, self.fields.iter().map(|f| {
138-
f.uninhabited_from(tcx, substs, is_enum)
139-
}))
142+
// Non-exhaustive variants from other crates are always considered inhabited.
143+
if self.is_field_list_non_exhaustive() && !self.def_id.is_local() {
144+
DefIdForest::empty()
145+
} else {
146+
DefIdForest::union(tcx, self.fields.iter().map(|f| {
147+
f.uninhabited_from(tcx, substs, is_enum)
148+
}))
149+
}
140150
}
141151
}
142152

src/librustc_mir/hair/pattern/_match.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,18 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
388388
}
389389
}
390390

391+
fn is_non_exhaustive_variant<'p>(&self, pattern: &'p Pattern<'tcx>) -> bool
392+
where 'a: 'p
393+
{
394+
match *pattern.kind {
395+
PatternKind::Variant { adt_def, variant_index, .. } => {
396+
let ref variant = adt_def.variants[variant_index];
397+
variant.is_field_list_non_exhaustive()
398+
}
399+
_ => false,
400+
}
401+
}
402+
391403
fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
392404
match ty.sty {
393405
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>,
10971109
debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]);
10981110

10991111
if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
1100-
debug!("is_useful - expanding constructors: {:#?}", constructors);
1101-
split_grouped_constructors(cx.tcx, constructors, matrix, pcx.ty).into_iter().map(|c|
1102-
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)
1103-
).find(|result| result.is_useful()).unwrap_or(NotUseful)
1112+
let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty);
1113+
debug!("is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}",
1114+
constructors, is_declared_nonexhaustive);
1115+
1116+
if is_declared_nonexhaustive {
1117+
Useful
1118+
} else {
1119+
split_grouped_constructors(cx.tcx, constructors, matrix, pcx.ty).into_iter().map(|c|
1120+
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)
1121+
).find(|result| result.is_useful()).unwrap_or(NotUseful)
1122+
}
11041123
} else {
11051124
debug!("is_useful - expanding wildcard");
11061125

src/librustc_mir/hair/pattern/check_match.rs

+5-1
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
}

src/test/ui/pattern/const-pat-ice.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir/hair/pattern/_match.rs:1071:5
1+
thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir/hair/pattern/_match.rs:1083:5
22
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
33

44
error: internal compiler error: unexpected panic
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![crate_type = "rlib"]
2+
#![feature(never_type)]
3+
#![feature(non_exhaustive)]
4+
5+
#[non_exhaustive]
6+
pub enum UninhabitedEnum {
7+
}
8+
9+
#[non_exhaustive]
10+
pub struct UninhabitedStruct {
11+
_priv: !,
12+
}
13+
14+
#[non_exhaustive]
15+
pub struct UninhabitedTupleStruct(!);
16+
17+
pub enum UninhabitedVariants {
18+
#[non_exhaustive] Tuple(!),
19+
#[non_exhaustive] Struct { x: ! }
20+
}
21+
22+
pub enum PartiallyInhabitedVariants {
23+
Tuple(u8),
24+
#[non_exhaustive] Struct { x: ! }
25+
}
26+
27+
pub struct IndirectUninhabitedEnum(UninhabitedEnum);
28+
29+
pub struct IndirectUninhabitedStruct(UninhabitedStruct);
30+
31+
pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
32+
33+
pub struct IndirectUninhabitedVariants(UninhabitedVariants);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// aux-build:uninhabited.rs
2+
#![feature(never_type)]
3+
4+
extern crate uninhabited;
5+
6+
use uninhabited::{
7+
UninhabitedEnum,
8+
UninhabitedStruct,
9+
UninhabitedTupleStruct,
10+
UninhabitedVariants,
11+
};
12+
13+
// This test checks that uninhabited non-exhaustive types cannot coerce to any type, as the never
14+
// type can.
15+
16+
struct A;
17+
18+
fn can_coerce_never_type_to_anything(x: !) -> A {
19+
x
20+
}
21+
22+
fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
23+
x //~ ERROR mismatched types
24+
}
25+
26+
fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
27+
x //~ ERROR mismatched types
28+
}
29+
30+
fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
31+
x //~ ERROR mismatched types
32+
}
33+
34+
fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
35+
x //~ ERROR mismatched types
36+
}
37+
38+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/coercions.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/coercions.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/coercions.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/coercions.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`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![feature(never_type)]
2+
#![feature(non_exhaustive)]
3+
4+
#[non_exhaustive]
5+
pub enum UninhabitedEnum {
6+
}
7+
8+
#[non_exhaustive]
9+
pub struct UninhabitedTupleStruct(!);
10+
11+
#[non_exhaustive]
12+
pub struct UninhabitedStruct {
13+
_priv: !,
14+
}
15+
16+
pub enum UninhabitedVariants {
17+
#[non_exhaustive] Tuple(!),
18+
#[non_exhaustive] Struct { x: ! }
19+
}
20+
21+
struct A;
22+
23+
// This test checks that uninhabited non-exhaustive types defined in the same crate cannot coerce
24+
// to any type, as the never type can.
25+
26+
fn can_coerce_never_type_to_anything(x: !) -> A {
27+
x
28+
}
29+
30+
fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A {
31+
x //~ ERROR mismatched types
32+
}
33+
34+
fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
35+
x //~ ERROR mismatched types
36+
}
37+
38+
fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A {
39+
x //~ ERROR mismatched types
40+
}
41+
42+
fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A {
43+
x //~ ERROR mismatched types
44+
}
45+
46+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/coercions_same_crate.rs:31: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 `UninhabitedEnum`
8+
|
9+
= note: expected type `A`
10+
found type `UninhabitedEnum`
11+
12+
error[E0308]: mismatched types
13+
--> $DIR/coercions_same_crate.rs:35: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 `UninhabitedTupleStruct`
19+
|
20+
= note: expected type `A`
21+
found type `UninhabitedTupleStruct`
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/coercions_same_crate.rs:39: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 `UninhabitedStruct`
30+
|
31+
= note: expected type `A`
32+
found type `UninhabitedStruct`
33+
34+
error[E0308]: mismatched types
35+
--> $DIR/coercions_same_crate.rs:43: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 `UninhabitedVariants`
41+
|
42+
= note: expected type `A`
43+
found type `UninhabitedVariants`
44+
45+
error: aborting due to 4 previous errors
46+
47+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// aux-build:uninhabited.rs
2+
#![feature(never_type)]
3+
4+
extern crate uninhabited;
5+
6+
use uninhabited::{
7+
IndirectUninhabitedEnum,
8+
IndirectUninhabitedStruct,
9+
IndirectUninhabitedTupleStruct,
10+
IndirectUninhabitedVariants,
11+
};
12+
13+
struct A;
14+
15+
// This test checks that an empty match on a non-exhaustive uninhabited type through a level of
16+
// indirection from an extern crate will not compile.
17+
18+
fn cannot_empty_match_on_empty_enum_to_anything(x: IndirectUninhabitedEnum) -> A {
19+
match x {} //~ ERROR non-exhaustive patterns
20+
}
21+
22+
fn cannot_empty_match_on_empty_struct_to_anything(x: IndirectUninhabitedStruct) -> A {
23+
match x {} //~ ERROR non-exhaustive patterns
24+
}
25+
26+
fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: IndirectUninhabitedTupleStruct) -> A {
27+
match x {} //~ ERROR non-exhaustive patterns
28+
}
29+
30+
fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(
31+
x: IndirectUninhabitedVariants,
32+
) -> A {
33+
match x {} //~ ERROR non-exhaustive patterns
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `uninhabited::IndirectUninhabitedEnum` is not handled
2+
--> $DIR/indirect_match.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[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `uninhabited::IndirectUninhabitedStruct` is not handled
10+
--> $DIR/indirect_match.rs:23:11
11+
|
12+
LL | match x {}
13+
| ^
14+
|
15+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
16+
17+
error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `uninhabited::IndirectUninhabitedTupleStruct` is not handled
18+
--> $DIR/indirect_match.rs:27:11
19+
|
20+
LL | match x {}
21+
| ^
22+
|
23+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
24+
25+
error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `uninhabited::IndirectUninhabitedVariants` is not handled
26+
--> $DIR/indirect_match.rs:33:11
27+
|
28+
LL | match x {}
29+
| ^
30+
|
31+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
32+
33+
error: aborting due to 4 previous errors
34+
35+
For more information about this error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)