Skip to content

Commit df9e491

Browse files
authored
Rollup merge of #67026 - Nadrieril:improve-usefulness-empty, r=varkor,Centril,estebank
Improve diagnostics and code for exhaustiveness of empty matches There was a completely separate check and diagnostics for the case of an empty match. This led to slightly different error messages and duplicated code. This improves code reuse and generally clarifies what happens for empty matches. This also clarifies the action of the `exhaustive_patterns` feature, and ensures that this feature doesn't change diagnostics in places it doesn't need to.
2 parents 3964a55 + fbd2cd0 commit df9e491

20 files changed

+889
-182
lines changed

src/librustc_mir/hair/pattern/_match.rs

+69-50
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ use super::{FieldPat, Pat, PatKind, PatRange};
238238
use rustc::hir::def_id::DefId;
239239
use rustc::hir::{HirId, RangeEnd};
240240
use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx};
241-
use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable};
241+
use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef};
242242

243243
use rustc::lint;
244244
use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
@@ -354,7 +354,7 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> {
354354
}
355355

356356
impl<'tcx> Pat<'tcx> {
357-
fn is_wildcard(&self) -> bool {
357+
pub(super) fn is_wildcard(&self) -> bool {
358358
match *self.kind {
359359
PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true,
360360
_ => false,
@@ -596,9 +596,21 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
596596
}
597597
}
598598

599-
fn is_local(&self, ty: Ty<'tcx>) -> bool {
599+
// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
600+
pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
600601
match ty.kind {
601-
ty::Adt(adt_def, ..) => adt_def.did.is_local(),
602+
ty::Adt(def, ..) => {
603+
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
604+
}
605+
_ => false,
606+
}
607+
}
608+
609+
// Returns whether the given variant is from another crate and has its fields declared
610+
// `#[non_exhaustive]`.
611+
fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool {
612+
match ty.kind {
613+
ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(),
602614
_ => false,
603615
}
604616
}
@@ -758,6 +770,10 @@ impl<'tcx> Constructor<'tcx> {
758770
// Returns the set of constructors covered by `self` but not by
759771
// anything in `other_ctors`.
760772
fn subtract_ctors(&self, other_ctors: &Vec<Constructor<'tcx>>) -> Vec<Constructor<'tcx>> {
773+
if other_ctors.is_empty() {
774+
return vec![self.clone()];
775+
}
776+
761777
match self {
762778
// Those constructors can only match themselves.
763779
Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
@@ -858,8 +874,7 @@ impl<'tcx> Constructor<'tcx> {
858874
vec![Pat::wildcard_from_ty(substs.type_at(0))]
859875
} else {
860876
let variant = &adt.variants[self.variant_index_for_adt(cx, adt)];
861-
let is_non_exhaustive =
862-
variant.is_field_list_non_exhaustive() && !cx.is_local(ty);
877+
let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant);
863878
variant
864879
.fields
865880
.iter()
@@ -1205,6 +1220,8 @@ impl<'tcx> Witness<'tcx> {
12051220
///
12061221
/// We make sure to omit constructors that are statically impossible. E.g., for
12071222
/// `Option<!>`, we do not include `Some(_)` in the returned list of constructors.
1223+
/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by
1224+
/// `cx.is_uninhabited()`).
12081225
fn all_constructors<'a, 'tcx>(
12091226
cx: &mut MatchCheckCtxt<'a, 'tcx>,
12101227
pcx: PatCtxt<'tcx>,
@@ -1235,47 +1252,45 @@ fn all_constructors<'a, 'tcx>(
12351252
vec![Slice(Slice { array_len: None, kind })]
12361253
}
12371254
ty::Adt(def, substs) if def.is_enum() => {
1238-
let ctors: Vec<_> = def
1239-
.variants
1240-
.iter()
1241-
.filter(|v| {
1242-
!cx.tcx.features().exhaustive_patterns
1243-
|| !v
1244-
.uninhabited_from(cx.tcx, substs, def.adt_kind())
1255+
let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns {
1256+
// If `exhaustive_patterns` is enabled, we exclude variants known to be
1257+
// uninhabited.
1258+
def.variants
1259+
.iter()
1260+
.filter(|v| {
1261+
!v.uninhabited_from(cx.tcx, substs, def.adt_kind())
12451262
.contains(cx.tcx, cx.module)
1246-
})
1247-
.map(|v| Variant(v.def_id))
1248-
.collect();
1249-
1250-
// If our scrutinee is *privately* an empty enum, we must treat it as though it had an
1251-
// "unknown" constructor (in that case, all other patterns obviously can't be variants)
1252-
// to avoid exposing its emptyness. See the `match_privately_empty` test for details.
1253-
// FIXME: currently the only way I know of something can be a privately-empty enum is
1254-
// when the exhaustive_patterns feature flag is not present, so this is only needed for
1255-
// that case.
1256-
let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
1257-
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
1258-
// additionnal "unknown" constructor.
1259-
let is_declared_nonexhaustive =
1260-
def.is_variant_list_non_exhaustive() && !cx.is_local(pcx.ty);
1261-
1262-
if is_privately_empty || is_declared_nonexhaustive {
1263-
// There is no point in enumerating all possible variants, because the user can't
1264-
// actually match against them themselves. So we return only the fictitious
1265-
// constructor.
1266-
// E.g., in an example like:
1267-
// ```
1268-
// let err: io::ErrorKind = ...;
1269-
// match err {
1270-
// io::ErrorKind::NotFound => {},
1271-
// }
1272-
// ```
1273-
// we don't want to show every possible IO error, but instead have only `_` as the
1274-
// witness.
1275-
vec![NonExhaustive]
1263+
})
1264+
.map(|v| Variant(v.def_id))
1265+
.collect()
12761266
} else {
1277-
ctors
1278-
}
1267+
def.variants.iter().map(|v| Variant(v.def_id)).collect()
1268+
};
1269+
1270+
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
1271+
// additional "unknown" constructor.
1272+
// There is no point in enumerating all possible variants, because the user can't
1273+
// actually match against them all themselves. So we always return only the fictitious
1274+
// constructor.
1275+
// E.g., in an example like:
1276+
// ```
1277+
// let err: io::ErrorKind = ...;
1278+
// match err {
1279+
// io::ErrorKind::NotFound => {},
1280+
// }
1281+
// ```
1282+
// we don't want to show every possible IO error, but instead have only `_` as the
1283+
// witness.
1284+
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
1285+
1286+
// If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
1287+
// as though it had an "unknown" constructor to avoid exposing its emptyness. Note that
1288+
// an empty match will still be considered exhaustive because that case is handled
1289+
// separately in `check_match`.
1290+
let is_secretly_empty =
1291+
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
1292+
1293+
if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors }
12791294
}
12801295
ty::Char => {
12811296
vec![
@@ -1605,6 +1620,7 @@ pub fn is_useful<'p, 'tcx>(
16051620
v: &PatStack<'p, 'tcx>,
16061621
witness_preference: WitnessPreference,
16071622
hir_id: HirId,
1623+
is_top_level: bool,
16081624
) -> Usefulness<'tcx, 'p> {
16091625
let &Matrix(ref rows) = matrix;
16101626
debug!("is_useful({:#?}, {:#?})", matrix, v);
@@ -1632,7 +1648,7 @@ pub fn is_useful<'p, 'tcx>(
16321648
let mut unreachable_pats = Vec::new();
16331649
let mut any_is_useful = false;
16341650
for v in vs {
1635-
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id);
1651+
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
16361652
match res {
16371653
Useful(pats) => {
16381654
any_is_useful = true;
@@ -1732,7 +1748,7 @@ pub fn is_useful<'p, 'tcx>(
17321748
} else {
17331749
let matrix = matrix.specialize_wildcard();
17341750
let v = v.to_tail();
1735-
let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id);
1751+
let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
17361752

17371753
// In this case, there's at least one "free"
17381754
// constructor that is only matched against by
@@ -1761,7 +1777,10 @@ pub fn is_useful<'p, 'tcx>(
17611777
// `(<direction-1>, <direction-2>, true)` - we are
17621778
// satisfied with `(_, _, true)`. In this case,
17631779
// `used_ctors` is empty.
1764-
if missing_ctors.all_ctors_are_missing() {
1780+
// The exception is: if we are at the top-level, for example in an empty match, we
1781+
// sometimes prefer reporting the list of constructors instead of just `_`.
1782+
let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty);
1783+
if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard {
17651784
// All constructors are unused. Add a wild pattern
17661785
// rather than each individual constructor.
17671786
usefulness.apply_wildcard(pcx.ty)
@@ -1793,7 +1812,7 @@ fn is_useful_specialized<'p, 'tcx>(
17931812
cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty));
17941813
let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns);
17951814
v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns)
1796-
.map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id))
1815+
.map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false))
17971816
.map(|u| u.apply_constructor(cx, &ctor, lty))
17981817
.unwrap_or(NotUseful)
17991818
}
@@ -2308,7 +2327,7 @@ fn specialize_one_pattern<'p, 'tcx>(
23082327

23092328
PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
23102329
let ref variant = adt_def.variants[variant_index];
2311-
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty);
2330+
let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant);
23122331
Some(Variant(variant.def_id))
23132332
.filter(|variant_constructor| variant_constructor == constructor)
23142333
.map(|_| {

src/librustc_mir/hair/pattern/check_match.rs

+54-79
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
148148
self.tables,
149149
);
150150
patcx.include_lint_checks();
151-
let pattern: &_ =
152-
cx.pattern_arena.alloc(expand_pattern(cx, patcx.lower_pattern(&arm.pat)));
151+
let pattern = patcx.lower_pattern(&arm.pat);
152+
let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
153153
if !patcx.errors.is_empty() {
154154
patcx.report_inlining_errors(arm.pat.span);
155155
have_errors = true;
@@ -166,73 +166,12 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
166166
// Fourth, check for unreachable arms.
167167
let matrix = check_arms(cx, &inlined_arms, source);
168168

169-
// Then, if the match has no arms, check whether the scrutinee
170-
// is uninhabited.
171-
let pat_ty = self.tables.node_type(scrut.hir_id);
172-
let module = self.tcx.hir().get_module_parent(scrut.hir_id);
173-
let mut def_span = None;
174-
let mut missing_variants = vec![];
175-
if inlined_arms.is_empty() {
176-
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
177-
self.tcx.is_ty_uninhabited_from(module, pat_ty)
178-
} else {
179-
match pat_ty.kind {
180-
ty::Never => true,
181-
ty::Adt(def, _) => {
182-
def_span = self.tcx.hir().span_if_local(def.did);
183-
if def.variants.len() < 4 && !def.variants.is_empty() {
184-
// keep around to point at the definition of non-covered variants
185-
missing_variants =
186-
def.variants.iter().map(|variant| variant.ident).collect();
187-
}
188-
189-
let is_non_exhaustive_and_non_local =
190-
def.is_variant_list_non_exhaustive() && !def.did.is_local();
191-
192-
!(is_non_exhaustive_and_non_local) && def.variants.is_empty()
193-
}
194-
_ => false,
195-
}
196-
};
197-
if !scrutinee_is_uninhabited {
198-
// We know the type is inhabited, so this must be wrong
199-
let mut err = create_e0004(
200-
self.tcx.sess,
201-
scrut.span,
202-
format!(
203-
"non-exhaustive patterns: {}",
204-
match missing_variants.len() {
205-
0 => format!("type `{}` is non-empty", pat_ty),
206-
1 => format!(
207-
"pattern `{}` of type `{}` is not handled",
208-
missing_variants[0].name, pat_ty,
209-
),
210-
_ => format!(
211-
"multiple patterns of type `{}` are not handled",
212-
pat_ty
213-
),
214-
}
215-
),
216-
);
217-
err.help(
218-
"ensure that all possible cases are being handled, \
219-
possibly by adding wildcards or more match arms",
220-
);
221-
if let Some(sp) = def_span {
222-
err.span_label(sp, format!("`{}` defined here", pat_ty));
223-
}
224-
// point at the definition of non-covered enum variants
225-
for variant in &missing_variants {
226-
err.span_label(variant.span, "variant not covered");
227-
}
228-
err.emit();
229-
}
230-
// If the type *is* uninhabited, it's vacuously exhaustive
231-
return;
232-
}
233-
169+
// Fifth, check if the match is exhaustive.
234170
let scrut_ty = self.tables.node_type(scrut.hir_id);
235-
check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id);
171+
// Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
172+
// since an empty matrix can occur when there are arms, if those arms all have guards.
173+
let is_empty_match = inlined_arms.is_empty();
174+
check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
236175
})
237176
}
238177

@@ -390,7 +329,7 @@ fn check_arms<'p, 'tcx>(
390329
for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
391330
let v = PatStack::from_pattern(pat);
392331

393-
match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) {
332+
match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
394333
NotUseful => {
395334
match source {
396335
hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
@@ -478,7 +417,8 @@ fn check_not_useful<'p, 'tcx>(
478417
hir_id: HirId,
479418
) -> Result<(), Vec<super::Pat<'tcx>>> {
480419
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
481-
match is_useful(cx, matrix, &PatStack::from_pattern(wild_pattern), ConstructWitness, hir_id) {
420+
let v = PatStack::from_pattern(wild_pattern);
421+
match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) {
482422
NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
483423
UsefulWithWitness(pats) => Err(if pats.is_empty() {
484424
bug!("Exhaustiveness check returned no witnesses")
@@ -495,25 +435,60 @@ fn check_exhaustive<'p, 'tcx>(
495435
sp: Span,
496436
matrix: &Matrix<'p, 'tcx>,
497437
hir_id: HirId,
438+
is_empty_match: bool,
498439
) {
440+
// In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
441+
// `is_useful` to exhaustively match uninhabited types, so we manually check here.
442+
if is_empty_match && !cx.tcx.features().exhaustive_patterns {
443+
let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
444+
ty::Never => true,
445+
ty::Adt(def, _) => {
446+
def.is_enum()
447+
&& def.variants.is_empty()
448+
&& !cx.is_foreign_non_exhaustive_enum(scrut_ty)
449+
}
450+
_ => false,
451+
};
452+
if scrutinee_is_visibly_uninhabited {
453+
// If the type *is* uninhabited, an empty match is vacuously exhaustive.
454+
return;
455+
}
456+
}
457+
499458
let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
500459
Ok(_) => return,
501460
Err(err) => err,
502461
};
503462

504-
let joined_patterns = joined_uncovered_patterns(&witnesses);
505-
let mut err = create_e0004(
506-
cx.tcx.sess,
507-
sp,
508-
format!("non-exhaustive patterns: {} not covered", joined_patterns),
509-
);
510-
err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
463+
let non_empty_enum = match scrut_ty.kind {
464+
ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
465+
_ => false,
466+
};
467+
// In the case of an empty match, replace the '`_` not covered' diagnostic with something more
468+
// informative.
469+
let mut err;
470+
if is_empty_match && !non_empty_enum {
471+
err = create_e0004(
472+
cx.tcx.sess,
473+
sp,
474+
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
475+
);
476+
} else {
477+
let joined_patterns = joined_uncovered_patterns(&witnesses);
478+
err = create_e0004(
479+
cx.tcx.sess,
480+
sp,
481+
format!("non-exhaustive patterns: {} not covered", joined_patterns),
482+
);
483+
err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
484+
};
485+
511486
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
512487
err.help(
513488
"ensure that all possible cases are being handled, \
514489
possibly by adding wildcards or more match arms",
515-
)
516-
.emit();
490+
);
491+
err.emit();
517492
}
518493

519494
fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {

0 commit comments

Comments
 (0)