Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ignore uninhabited non-exhaustive variant fields #65414

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 39 additions & 49 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,16 +394,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
}
}

fn is_non_exhaustive_variant<'p>(&self, pattern: &'p Pat<'tcx>) -> bool {
match *pattern.kind {
PatKind::Variant { adt_def, variant_index, .. } => {
let ref variant = adt_def.variants[variant_index];
variant.is_field_list_non_exhaustive()
}
_ => false,
}
}

fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
match ty.kind {
ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
Expand Down Expand Up @@ -1252,19 +1242,12 @@ pub fn is_useful<'p, 'a, 'tcx>(
debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]);

if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty);
debug!("is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}",
constructors, is_declared_nonexhaustive);

if is_declared_nonexhaustive {
Useful
} else {
split_grouped_constructors(
cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id),
).into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)
).find(|result| result.is_useful()).unwrap_or(NotUseful)
}
debug!("is_useful - expanding constructors: {:#?}", constructors);
split_grouped_constructors(
davidtwco marked this conversation as resolved.
Show resolved Hide resolved
cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id),
).into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)
).find(|result| result.is_useful()).unwrap_or(NotUseful)
} else {
debug!("is_useful - expanding wildcard");

Expand Down Expand Up @@ -1548,27 +1531,30 @@ fn constructor_sub_pattern_tys<'a, 'tcx>(
// Use T as the sub pattern type of Box<T>.
vec![substs.type_at(0)]
} else {
adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.iter().map(|field| {
let variant = &adt.variants[ctor.variant_index_for_adt(cx, adt)];
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(ty);
variant.fields.iter().map(|field| {
let is_visible = adt.is_enum()
|| field.vis.is_accessible_from(cx.module, cx.tcx);
if is_visible {
let ty = field.ty(cx.tcx, substs);
match ty.kind {
// If the field type returned is an array of an unknown
// size return an TyErr.
ty::Array(_, len)
if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
cx.tcx.types.err,
_ => ty,
}
} else {
// Treat all non-visible fields as TyErr. They
// can't appear in any other pattern from
// this match (because they are private),
// so their type does not matter - but
// we don't want to know they are
// uninhabited.
cx.tcx.types.err
let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
match (is_visible, is_non_exhaustive, is_uninhabited) {
// Treat all uninhabited types in non-exhaustive variants as `TyErr`.
(_, true, true) => cx.tcx.types.err,
// Treat all non-visible fields as `TyErr`. They can't appear in any
// other pattern from this match (because they are private), so their
// type does not matter - but we don't want to know they are uninhabited.
(false, ..) => cx.tcx.types.err,
(true, ..) => {
let ty = field.ty(cx.tcx, substs);
match ty.kind {
// If the field type returned is an array of an unknown
// size return an TyErr.
ty::Array(_, len)
if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
cx.tcx.types.err,
_ => ty,
}
},
}
}).collect()
}
Expand Down Expand Up @@ -1874,15 +1860,18 @@ fn constructor_covered_by_range<'tcx>(
}
}

fn patterns_for_variant<'p, 'tcx>(
fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
cx: &mut MatchCheckCtxt<'a, 'tcx>,
subpatterns: &'p [FieldPat<'tcx>],
wild_patterns: &[&'p Pat<'tcx>])
-> SmallVec<[&'p Pat<'tcx>; 2]>
{
wild_patterns: &[&'p Pat<'tcx>],
is_non_exhaustive: bool,
) -> SmallVec<[&'p Pat<'tcx>; 2]> {
let mut result = SmallVec::from_slice(wild_patterns);

for subpat in subpatterns {
result[subpat.field.index()] = &subpat.pattern;
if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) {
result[subpat.field.index()] = &subpat.pattern;
}
}

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

PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
let ref variant = adt_def.variants[variant_index];
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty);
Some(Variant(variant.def_id))
.filter(|variant_constructor| variant_constructor == constructor)
.map(|_| patterns_for_variant(subpatterns, wild_patterns))
.map(|_| patterns_for_variant(cx, subpatterns, wild_patterns, is_non_exhaustive))
}

PatKind::Leaf { ref subpatterns } => {
Some(patterns_for_variant(subpatterns, wild_patterns))
Some(patterns_for_variant(cx, subpatterns, wild_patterns, false))
}

PatKind::Deref { ref subpattern } => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// aux-build:uninhabited.rs
#![deny(unreachable_patterns)]
#![feature(never_type)]
#![feature(non_exhaustive)]

extern crate uninhabited;

use uninhabited::PartiallyInhabitedVariants;

// This test checks a redundant/useless pattern of a non-exhaustive enum/variant is still
// warned against.

pub fn foo(x: PartiallyInhabitedVariants) {
match x {
PartiallyInhabitedVariants::Struct { .. } => {},
PartiallyInhabitedVariants::Struct { .. } => {},
//~^ ERROR unreachable pattern
_ => {},
}
}

fn main() { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: unreachable pattern
--> $DIR/issue-65157-repeated-match-arm.rs:16:9
|
LL | PartiallyInhabitedVariants::Struct { .. } => {},
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/issue-65157-repeated-match-arm.rs:2:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error