Change:
Say I have the following enum in crate : And I match on it in crate : The second branch is not detected to be unreachable. This is on current nightly; .
The reason for this behaviour is straightforward: reports tuple variants with non-exhaustive fields as always useful. This line was introduced in commit . I'm not quite sure what the intended semantics are so I wouldn't know how to fix it.
Please wait for to be merged before fixing this; I'm dreading having to rebase on top of the fix
cc

Discussion:
} } <del> 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, } } </del> fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind { ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),

debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]); if let Some(constructors) = pat_constructors(cx, v[0], pcx) { <del> 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) } </del> <ins> debug!("is_useful - expanding constructors: {:#?}", constructors); 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) </ins> } else { debug!("is_useful - expanding wildcard");

// Use T as the sub pattern type of Box<T>. vec![substs.type_at(0)] } else { <del> adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.iter().map(|field| { </del> <ins> 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| { </ins> let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); <del> 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 </del> <ins> 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, } }, </ins> } }).collect() }

} } <del> fn patterns_for_variant<'p, 'tcx>( </del> <ins> fn patterns_for_variant<'p, 'a: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, </ins> subpatterns: &'p [FieldPat<'tcx>], <del> wild_patterns: &[&'p Pat<'tcx>]) -> SmallVec<[&'p Pat<'tcx>; 2]> { </del> <ins> wild_patterns: &[&'p Pat<'tcx>], is_non_exhaustive: bool, ) -> SmallVec<[&'p Pat<'tcx>; 2]> { </ins> let mut result = SmallVec::from_slice(wild_patterns); for subpat in subpatterns { <del> result[subpat.field.index()] = &subpat.pattern; </del> <ins> if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) { result[subpat.field.index()] = &subpat.pattern; } </ins> } debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result);

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

<ins> // 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() { } </ins>

<ins> 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 </ins>