Skip to content

Commit 50e8c41

Browse files
authored
Rollup merge of #65414 - davidtwco:issue-65157-non-exhaustive-always-useful, r=varkor
ignore uninhabited non-exhaustive variant fields Fixes #65157. This PR modifies the uninhabitedness checking so that the fields of a non-exhaustive variant (which is not local) are ignored if they are uninhabited. This is an improvement over the previous behaviour which considered all non-local non-exhaustive variants useful because unreachable patterns are now detected. r? @arielb1 cc @varkor
2 parents ee7f9de + 7ffbd62 commit 50e8c41

File tree

3 files changed

+75
-49
lines changed

3 files changed

+75
-49
lines changed

src/librustc_mir/hair/pattern/_match.rs

+39-49
Original file line numberDiff line numberDiff line change
@@ -394,16 +394,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
394394
}
395395
}
396396

397-
fn is_non_exhaustive_variant<'p>(&self, pattern: &'p Pat<'tcx>) -> bool {
398-
match *pattern.kind {
399-
PatKind::Variant { adt_def, variant_index, .. } => {
400-
let ref variant = adt_def.variants[variant_index];
401-
variant.is_field_list_non_exhaustive()
402-
}
403-
_ => false,
404-
}
405-
}
406-
407397
fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
408398
match ty.kind {
409399
ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
@@ -1252,19 +1242,12 @@ pub fn is_useful<'p, 'a, 'tcx>(
12521242
debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]);
12531243

12541244
if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
1255-
let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty);
1256-
debug!("is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}",
1257-
constructors, is_declared_nonexhaustive);
1258-
1259-
if is_declared_nonexhaustive {
1260-
Useful
1261-
} else {
1262-
split_grouped_constructors(
1263-
cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id),
1264-
).into_iter().map(|c|
1265-
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)
1266-
).find(|result| result.is_useful()).unwrap_or(NotUseful)
1267-
}
1245+
debug!("is_useful - expanding constructors: {:#?}", constructors);
1246+
split_grouped_constructors(
1247+
cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id),
1248+
).into_iter().map(|c|
1249+
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)
1250+
).find(|result| result.is_useful()).unwrap_or(NotUseful)
12681251
} else {
12691252
debug!("is_useful - expanding wildcard");
12701253

@@ -1548,27 +1531,30 @@ fn constructor_sub_pattern_tys<'a, 'tcx>(
15481531
// Use T as the sub pattern type of Box<T>.
15491532
vec![substs.type_at(0)]
15501533
} else {
1551-
adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.iter().map(|field| {
1534+
let variant = &adt.variants[ctor.variant_index_for_adt(cx, adt)];
1535+
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(ty);
1536+
variant.fields.iter().map(|field| {
15521537
let is_visible = adt.is_enum()
15531538
|| field.vis.is_accessible_from(cx.module, cx.tcx);
1554-
if is_visible {
1555-
let ty = field.ty(cx.tcx, substs);
1556-
match ty.kind {
1557-
// If the field type returned is an array of an unknown
1558-
// size return an TyErr.
1559-
ty::Array(_, len)
1560-
if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
1561-
cx.tcx.types.err,
1562-
_ => ty,
1563-
}
1564-
} else {
1565-
// Treat all non-visible fields as TyErr. They
1566-
// can't appear in any other pattern from
1567-
// this match (because they are private),
1568-
// so their type does not matter - but
1569-
// we don't want to know they are
1570-
// uninhabited.
1571-
cx.tcx.types.err
1539+
let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
1540+
match (is_visible, is_non_exhaustive, is_uninhabited) {
1541+
// Treat all uninhabited types in non-exhaustive variants as `TyErr`.
1542+
(_, true, true) => cx.tcx.types.err,
1543+
// Treat all non-visible fields as `TyErr`. They can't appear in any
1544+
// other pattern from this match (because they are private), so their
1545+
// type does not matter - but we don't want to know they are uninhabited.
1546+
(false, ..) => cx.tcx.types.err,
1547+
(true, ..) => {
1548+
let ty = field.ty(cx.tcx, substs);
1549+
match ty.kind {
1550+
// If the field type returned is an array of an unknown
1551+
// size return an TyErr.
1552+
ty::Array(_, len)
1553+
if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
1554+
cx.tcx.types.err,
1555+
_ => ty,
1556+
}
1557+
},
15721558
}
15731559
}).collect()
15741560
}
@@ -1874,15 +1860,18 @@ fn constructor_covered_by_range<'tcx>(
18741860
}
18751861
}
18761862

1877-
fn patterns_for_variant<'p, 'tcx>(
1863+
fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
1864+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
18781865
subpatterns: &'p [FieldPat<'tcx>],
1879-
wild_patterns: &[&'p Pat<'tcx>])
1880-
-> SmallVec<[&'p Pat<'tcx>; 2]>
1881-
{
1866+
wild_patterns: &[&'p Pat<'tcx>],
1867+
is_non_exhaustive: bool,
1868+
) -> SmallVec<[&'p Pat<'tcx>; 2]> {
18821869
let mut result = SmallVec::from_slice(wild_patterns);
18831870

18841871
for subpat in subpatterns {
1885-
result[subpat.field.index()] = &subpat.pattern;
1872+
if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) {
1873+
result[subpat.field.index()] = &subpat.pattern;
1874+
}
18861875
}
18871876

18881877
debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result);
@@ -1916,13 +1905,14 @@ fn specialize<'p, 'a: 'p, 'tcx>(
19161905

19171906
PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
19181907
let ref variant = adt_def.variants[variant_index];
1908+
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty);
19191909
Some(Variant(variant.def_id))
19201910
.filter(|variant_constructor| variant_constructor == constructor)
1921-
.map(|_| patterns_for_variant(subpatterns, wild_patterns))
1911+
.map(|_| patterns_for_variant(cx, subpatterns, wild_patterns, is_non_exhaustive))
19221912
}
19231913

19241914
PatKind::Leaf { ref subpatterns } => {
1925-
Some(patterns_for_variant(subpatterns, wild_patterns))
1915+
Some(patterns_for_variant(cx, subpatterns, wild_patterns, false))
19261916
}
19271917

19281918
PatKind::Deref { ref subpattern } => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// aux-build:uninhabited.rs
2+
#![deny(unreachable_patterns)]
3+
#![feature(never_type)]
4+
#![feature(non_exhaustive)]
5+
6+
extern crate uninhabited;
7+
8+
use uninhabited::PartiallyInhabitedVariants;
9+
10+
// This test checks a redundant/useless pattern of a non-exhaustive enum/variant is still
11+
// warned against.
12+
13+
pub fn foo(x: PartiallyInhabitedVariants) {
14+
match x {
15+
PartiallyInhabitedVariants::Struct { .. } => {},
16+
PartiallyInhabitedVariants::Struct { .. } => {},
17+
//~^ ERROR unreachable pattern
18+
_ => {},
19+
}
20+
}
21+
22+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: unreachable pattern
2+
--> $DIR/issue-65157-repeated-match-arm.rs:16:9
3+
|
4+
LL | PartiallyInhabitedVariants::Struct { .. } => {},
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/issue-65157-repeated-match-arm.rs:2:9
9+
|
10+
LL | #![deny(unreachable_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)