From 2a87bae48d415b9ced69ae52513f004d06e34283 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 16 Nov 2023 04:28:22 +0100 Subject: Reveal opaque types in exhaustiveness checking --- compiler/rustc_pattern_analysis/src/rustc.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_pattern_analysis/src/rustc.rs') diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 65c90aa9f1d..d2cfb9a8b06 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -44,6 +44,7 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat { pub tcx: TyCtxt<'tcx>, + pub typeck_results: &'tcx ty::TypeckResults<'tcx>, /// The module in which the match occurs. This is necessary for /// checking inhabited-ness of types because whether a type is (visibly) /// inhabited can depend on whether it was defined in the current module or @@ -101,6 +102,21 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { } } + /// Type inference occasionally gives us opaque types in places where corresponding patterns + /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited + /// types, we use the corresponding concrete type if possible. + fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() { + if let Some(local_def_id) = alias_ty.def_id.as_local() { + let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args }; + if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) { + return real_ty.ty; + } + } + } + ty + } + // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. // This lists the fields we keep along with their types. @@ -873,8 +889,9 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { fn is_exhaustive_patterns_feature_on(&self) -> bool { self.tcx.features().exhaustive_patterns } - fn is_opaque_ty(ty: Self::Ty) -> bool { - matches!(ty.kind(), ty::Alias(ty::Opaque, ..)) + + fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.reveal_opaque_ty(ty) } fn ctor_arity(&self, ctor: &crate::constructor::Constructor, ty: Self::Ty) -> usize { -- cgit 1.4.1-3-g733a5 From 5fccaee59c290b85612d4adb120b3e745d3f16dd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 22 Dec 2023 23:47:44 +0100 Subject: Clarify the situation with dummy patterns and `PatData` Use an explicit `Option` instead of requiring a `Default` bound --- .../rustc_mir_build/src/thir/pattern/check_match.rs | 6 +++--- compiler/rustc_pattern_analysis/src/lib.rs | 5 ++--- compiler/rustc_pattern_analysis/src/lints.rs | 4 ++-- compiler/rustc_pattern_analysis/src/pat.rs | 18 +++++++++++------- compiler/rustc_pattern_analysis/src/rustc.rs | 6 +++--- compiler/rustc_pattern_analysis/src/usefulness.rs | 3 +-- 6 files changed, 22 insertions(+), 20 deletions(-) (limited to 'compiler/rustc_pattern_analysis/src/rustc.rs') diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index d67dc59c618..fc6e2cc961c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -860,7 +860,7 @@ fn report_arm_reachability<'p, 'tcx>( for (arm, is_useful) in report.arm_usefulness.iter() { match is_useful { Usefulness::Redundant => { - report_unreachable_pattern(*arm.pat.data(), arm.arm_data, catchall) + report_unreachable_pattern(*arm.pat.data().unwrap(), arm.arm_data, catchall) } Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {} // The arm is reachable, but contains redundant subpatterns (from or-patterns). @@ -869,12 +869,12 @@ fn report_arm_reachability<'p, 'tcx>( // Emit lints in the order in which they occur in the file. redundant_subpats.sort_unstable_by_key(|pat| pat.data()); for pat in redundant_subpats { - report_unreachable_pattern(*pat.data(), arm.arm_data, None); + report_unreachable_pattern(*pat.data().unwrap(), arm.arm_data, None); } } } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { - catchall = Some(*arm.pat.data()); + catchall = Some(*arm.pat.data().unwrap()); } } } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 9f758dd8263..a1c9b157666 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -58,9 +58,8 @@ pub trait TypeCx: Sized + fmt::Debug { type StrLit: Clone + PartialEq + fmt::Debug; /// Extra data to store in a match arm. type ArmData: Copy + Clone + fmt::Debug; - /// Extra data to store in a pattern. `Default` needed when we create fictitious wildcard - /// patterns during analysis. - type PatData: Clone + Default; + /// Extra data to store in a pattern. + type PatData: Clone; /// FIXME(Nadrieril): `Cx` should only give us revealed types. fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty; diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 450a5cb0a10..2be6e8e3db3 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -203,7 +203,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( }; use rustc_errors::DecorateLint; - let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data(), ""); + let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data().unwrap(), ""); err.set_primary_message(decorator.msg()); decorator.decorate_lint(&mut err); err.emit(); @@ -253,8 +253,8 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( let mut suffixes: SmallVec<[_; 1]> = Default::default(); // Iterate on patterns that contained `overlap`. for pat in column.iter() { - let this_span = *pat.data(); let Constructor::IntRange(this_range) = pat.ctor() else { continue }; + let this_span = *pat.data().unwrap(); if this_range.is_singleton() { // Don't lint when one of the ranges is a singleton. continue; diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 90772003a0a..9efd3e864da 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -26,14 +26,16 @@ pub struct DeconstructedPat<'p, Cx: TypeCx> { ctor: Constructor, fields: &'p [DeconstructedPat<'p, Cx>], ty: Cx::Ty, - data: Cx::PatData, + /// Extra data to store in a pattern. `None` if the pattern is a wildcard that does not + /// correspond to a user-supplied pattern. + data: Option, /// Whether removing this arm would change the behavior of the match expression. useful: Cell, } impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { - pub fn wildcard(ty: Cx::Ty, data: Cx::PatData) -> Self { - Self::new(Wildcard, &[], ty, data) + pub fn wildcard(ty: Cx::Ty) -> Self { + DeconstructedPat { ctor: Wildcard, fields: &[], ty, data: None, useful: Cell::new(false) } } pub fn new( @@ -42,7 +44,7 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { ty: Cx::Ty, data: Cx::PatData, ) -> Self { - DeconstructedPat { ctor, fields, ty, data, useful: Cell::new(false) } + DeconstructedPat { ctor, fields, ty, data: Some(data), useful: Cell::new(false) } } pub(crate) fn is_or_pat(&self) -> bool { @@ -63,8 +65,10 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { pub fn ty(&self) -> Cx::Ty { self.ty } - pub fn data(&self) -> &Cx::PatData { - &self.data + /// Returns the extra data stored in a pattern. Returns `None` if the pattern is a wildcard that + /// does not correspond to a user-supplied pattern. + pub fn data(&self) -> Option<&Cx::PatData> { + self.data.as_ref() } pub fn iter_fields<'a>( @@ -83,7 +87,7 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { let wildcard_sub_tys = || { let tys = pcx.ctor_sub_tys(other_ctor); tys.iter() - .map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default())) + .map(|ty| DeconstructedPat::wildcard(*ty)) .map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_) .collect() }; diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d2cfb9a8b06..adaa2ecbe4f 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -416,7 +416,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { ty::Tuple(fs) => { ctor = Struct; let mut wilds: SmallVec<[_; 2]> = - fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect(); + fs.iter().map(|ty| DeconstructedPat::wildcard(ty)).collect(); for pat in subpatterns { wilds[pat.field.index()] = self.lower_pat(&pat.pattern); } @@ -439,7 +439,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { let pat = if let Some(pat) = pattern { self.lower_pat(&pat.pattern) } else { - DeconstructedPat::wildcard(args.type_at(0), pat.span) + DeconstructedPat::wildcard(args.type_at(0)) }; ctor = Struct; fields = singleton(pat); @@ -464,7 +464,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { ty }); let mut wilds: SmallVec<[_; 2]> = - tys.map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect(); + tys.map(|ty| DeconstructedPat::wildcard(ty)).collect(); for pat in subpatterns { if let Some(i) = field_id_to_id[pat.field.index()] { wilds[i] = self.lower_pat(&pat.pattern); diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index b1190d84c8e..a7cd944c0f1 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -840,8 +840,7 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> { scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> Self { - let wild_pattern = - wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Default::default())); + let wild_pattern = wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); let wildcard_row = PatStack::from_pattern(wild_pattern); let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), -- cgit 1.4.1-3-g733a5 From 34307ab7c55de1c81d2476c86b44d03728b3f0f0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 16 Nov 2023 08:53:21 +0100 Subject: Reveal empty opaques in depth --- .../src/ty/inhabitedness/inhabited_predicate.rs | 75 +++++++++++++++++----- compiler/rustc_middle/src/ty/inhabitedness/mod.rs | 16 ++++- compiler/rustc_pattern_analysis/src/rustc.rs | 18 ++++-- tests/ui/pattern/usefulness/impl-trait.rs | 21 ++++-- tests/ui/pattern/usefulness/impl-trait.stderr | 34 +++++++--- 5 files changed, 128 insertions(+), 36 deletions(-) (limited to 'compiler/rustc_pattern_analysis/src/rustc.rs') diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index ae17942785f..c571ac50724 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -1,7 +1,7 @@ use smallvec::SmallVec; use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, ParamEnv, Ty}; +use crate::ty::{self, DefId, OpaqueTypeKey, ParamEnv, Ty}; /// Represents whether some type is inhabited in a given context. /// Examples of uninhabited types are `!`, `enum Void {}`, or a struct @@ -23,6 +23,8 @@ pub enum InhabitedPredicate<'tcx> { /// Inhabited if some generic type is inhabited. /// These are replaced by calling [`Self::instantiate`]. GenericType(Ty<'tcx>), + /// Inhabited if either we don't know the hidden type or we know it and it is inhabited. + OpaqueType(OpaqueTypeKey<'tcx>), /// A AND B And(&'tcx [InhabitedPredicate<'tcx>; 2]), /// A OR B @@ -30,35 +32,53 @@ pub enum InhabitedPredicate<'tcx> { } impl<'tcx> InhabitedPredicate<'tcx> { - /// Returns true if the corresponding type is inhabited in the given - /// `ParamEnv` and module + /// Returns true if the corresponding type is inhabited in the given `ParamEnv` and module. pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool { - let Ok(result) = self.apply_inner::(tcx, param_env, &mut Default::default(), &|id| { - Ok(tcx.is_descendant_of(module_def_id, id)) - }); + self.apply_revealing_opaque(tcx, param_env, module_def_id, &|_| None) + } + + /// Returns true if the corresponding type is inhabited in the given `ParamEnv` and module, + /// revealing opaques when possible. + pub fn apply_revealing_opaque( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + module_def_id: DefId, + reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option>, + ) -> bool { + let Ok(result) = self.apply_inner::( + tcx, + param_env, + &mut Default::default(), + &|id| Ok(tcx.is_descendant_of(module_def_id, id)), + reveal_opaque, + ); result } /// Same as `apply`, but returns `None` if self contains a module predicate pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(())).ok() + self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(()), &|_| None).ok() } /// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is, /// privately uninhabited types are considered always uninhabited. pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool { let Ok(result) = - self.apply_inner::(tcx, param_env, &mut Default::default(), &|_| Ok(true)); + self.apply_inner::(tcx, param_env, &mut Default::default(), &|_| Ok(true), &|_| { + None + }); result } - #[instrument(level = "debug", skip(tcx, param_env, in_module), ret)] + #[instrument(level = "debug", skip(tcx, param_env, in_module, reveal_opaque), ret)] fn apply_inner( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection in_module: &impl Fn(DefId) -> Result, + reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option>, ) -> Result { match self { Self::False => Ok(false), @@ -84,18 +104,41 @@ impl<'tcx> InhabitedPredicate<'tcx> { return Ok(true); // Recover; this will error later. } eval_stack.push(t); - let ret = pred.apply_inner(tcx, param_env, eval_stack, in_module); + let ret = + pred.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque); eval_stack.pop(); ret } } } - Self::And([a, b]) => { - try_and(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module)) - } - Self::Or([a, b]) => { - try_or(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module)) - } + Self::OpaqueType(key) => match reveal_opaque(key) { + // Unknown opaque is assumed inhabited. + None => Ok(true), + // Known opaque type is inspected recursively. + Some(t) => { + // A cyclic opaque type can happen in corner cases that would only error later. + // See e.g. `tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs`. + if eval_stack.contains(&t) { + return Ok(true); // Recover; this will error later. + } + eval_stack.push(t); + let ret = t.inhabited_predicate(tcx).apply_inner( + tcx, + param_env, + eval_stack, + in_module, + reveal_opaque, + ); + eval_stack.pop(); + ret + } + }, + Self::And([a, b]) => try_and(a, b, |x| { + x.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque) + }), + Self::Or([a, b]) => try_or(a, b, |x| { + x.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque) + }), } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index d67fb9fd0b7..bbcc244cb26 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -45,7 +45,7 @@ use crate::query::Providers; use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, Ty, VariantDef, Visibility}; +use crate::ty::{self, DefId, Ty, TypeVisitableExt, VariantDef, Visibility}; use rustc_type_ir::TyKind::*; @@ -105,6 +105,7 @@ impl<'tcx> VariantDef { impl<'tcx> Ty<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> { + debug_assert!(!self.has_infer()); match self.kind() { // For now, unions are always considered inhabited Adt(adt, _) if adt.is_union() => InhabitedPredicate::True, @@ -113,7 +114,18 @@ impl<'tcx> Ty<'tcx> { InhabitedPredicate::True } Never => InhabitedPredicate::False, - Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self), + Param(_) | Alias(ty::Projection | ty::Weak, _) => InhabitedPredicate::GenericType(self), + Alias(ty::Opaque, alias_ty) => { + match alias_ty.def_id.as_local() { + // Foreign opaque is considered inhabited. + None => InhabitedPredicate::True, + // Local opaque type may possibly be revealed. + Some(local_def_id) => { + let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args }; + InhabitedPredicate::OpaqueType(key) + } + } + } // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above. // However it's unclear if the args passed to `InhabitedPredicate::instantiate` are of the correct // format, i.e. don't contain parent args. If you hit this case, please verify this beforehand. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index adaa2ecbe4f..a5a47724f3f 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; @@ -74,8 +74,16 @@ impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> { } impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { - pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - !ty.is_inhabited_from(self.tcx, self.module, self.param_env) + fn reveal_opaque(&self, key: OpaqueTypeKey<'tcx>) -> Option> { + self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty) + } + pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + !ty.inhabited_predicate(self.tcx).apply_revealing_opaque( + self.tcx, + self.param_env, + self.module, + &|key| self.reveal_opaque(key), + ) } /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. @@ -319,7 +327,9 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { let is_inhabited = v .inhabited_predicate(cx.tcx, *def) .instantiate(cx.tcx, args) - .apply(cx.tcx, cx.param_env, cx.module); + .apply_revealing_opaque(cx.tcx, cx.param_env, cx.module, &|key| { + cx.reveal_opaque(key) + }); // Variants that depend on a disabled unstable feature. let is_unstable = matches!( cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None), diff --git a/tests/ui/pattern/usefulness/impl-trait.rs b/tests/ui/pattern/usefulness/impl-trait.rs index 6e8c0feb710..ceb97315e9d 100644 --- a/tests/ui/pattern/usefulness/impl-trait.rs +++ b/tests/ui/pattern/usefulness/impl-trait.rs @@ -46,9 +46,7 @@ fn option_never(x: Void) -> Option { } match option_never(x) { None => {} - // FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque - // types. - _ => {} + _ => {} //~ ERROR unreachable } } Some(x) @@ -137,10 +135,21 @@ fn nested_empty_opaque(x: Void) -> X { let opaque_void = nested_empty_opaque(x); let secretely_void = SecretelyVoid(opaque_void); match secretely_void { - // FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque - // types. - _ => {} + _ => {} //~ ERROR unreachable } } x } + +type Y = (impl Copy, impl Copy); +struct SecretelyDoubleVoid(Y); +fn super_nested_empty_opaque(x: Void) -> Y { + if false { + let opaque_void = super_nested_empty_opaque(x); + let secretely_void = SecretelyDoubleVoid(opaque_void); + match secretely_void { + _ => {} //~ ERROR unreachable + } + } + (x, x) +} diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr index df6d43dbb3f..ba8b12f9f66 100644 --- a/tests/ui/pattern/usefulness/impl-trait.stderr +++ b/tests/ui/pattern/usefulness/impl-trait.stderr @@ -23,25 +23,31 @@ LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:61:13 + --> $DIR/impl-trait.rs:49:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:59:13 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:65:13 + --> $DIR/impl-trait.rs:63:13 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/impl-trait.rs:78:9 + --> $DIR/impl-trait.rs:76:9 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/impl-trait.rs:88:9 + --> $DIR/impl-trait.rs:86:9 | LL | _ => {} | - matches any value @@ -49,25 +55,37 @@ LL | Some((a, b)) => {} | ^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:96:13 + --> $DIR/impl-trait.rs:94:13 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/impl-trait.rs:107:9 + --> $DIR/impl-trait.rs:105:9 | LL | Some((mut x, mut y)) => { | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:126:13 + --> $DIR/impl-trait.rs:124:13 | LL | _ => {} | - matches any value LL | Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern +error: unreachable pattern + --> $DIR/impl-trait.rs:138:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:151:13 + | +LL | _ => {} + | ^ + error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty --> $DIR/impl-trait.rs:23:11 | @@ -96,6 +114,6 @@ LL + _ => todo!(), LL + } | -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0004`. -- cgit 1.4.1-3-g733a5