diff options
Diffstat (limited to '')
5 files changed, 287 insertions, 212 deletions
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 063c07647..858129c74 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -7,7 +7,7 @@ use super::{PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, + error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; @@ -347,27 +347,35 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let span_end = affix.last().unwrap().unwrap().0; let span = span_start.to(span_end); let cnt = affix.len(); - cx.tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, top, span, |lint| { - let s = pluralize!(cnt); - let mut diag = lint.build(&format!("{kind} irrefutable pattern{s} in let chain")); - diag.note(&format!( - "{these} pattern{s} will always match", - these = pluralize!("this", cnt), - )); - diag.help(&format!( - "consider moving {} {suggestion}", - if cnt > 1 { "them" } else { "it" } - )); - diag.emit() - }); + let s = pluralize!(cnt); + cx.tcx.struct_span_lint_hir( + IRREFUTABLE_LET_PATTERNS, + top, + span, + format!("{kind} irrefutable pattern{s} in let chain"), + |lint| { + lint.note(format!( + "{these} pattern{s} will always match", + these = pluralize!("this", cnt), + )) + .help(format!( + "consider moving {} {suggestion}", + if cnt > 1 { "them" } else { "it" } + )) + }, + ); }; if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 { // The chain has a non-zero prefix of irrefutable `let` statements. // Check if the let source is while, for there is no alternative place to put a prefix, // and we shouldn't lint. + // For let guards inside a match, prefixes might use bindings of the match pattern, + // so can't always be moved out. + // FIXME: Add checking whether the bindings are actually used in the prefix, + // and lint if they are not. let let_source = let_source_parent(self.tcx, top, None); - if !matches!(let_source, LetSource::WhileLet) { + if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) { // Emit the lint let prefix = &chain_refutabilities[..until]; lint_affix(prefix, "leading", "outside of the construct"); @@ -487,12 +495,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { ], Applicability::HasPlaceholders, ); - if !bindings.is_empty() && cx.tcx.sess.is_nightly_build() { + if !bindings.is_empty() { err.span_suggestion_verbose( semi_span.shrink_to_lo(), &format!( - "alternatively, on nightly, you might want to use \ - `#![feature(let_else)]` to handle the variant{} that {} matched", + "alternatively, you might want to use \ + let else to handle the variant{} that {} matched", pluralize!(witnesses.len()), match witnesses.len() { 1 => "isn't", @@ -561,26 +569,28 @@ fn check_for_bindings_named_same_as_variants( BINDINGS_WITH_VARIANT_NAME, p.hir_id, p.span, + DelayDm(|| format!( + "pattern binding `{}` is named the same as one \ + of the variants of the type `{}`", + ident, cx.tcx.def_path_str(edef.did()) + )), |lint| { let ty_path = cx.tcx.def_path_str(edef.did()); - let mut err = lint.build(&format!( - "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", - ident, ty_path - )); - err.code(error_code!(E0170)); + lint.code(error_code!(E0170)); + // If this is an irrefutable pattern, and there's > 1 variant, // then we can't actually match on this. Applying the below // suggestion would produce code that breaks on `check_irrefutable`. if rf == Refutable || variant_count == 1 { - err.span_suggestion( + lint.span_suggestion( p.span, "to match on the variant, qualify the path", format!("{}::{}", ty_path, ident), Applicability::MachineApplicable, ); } - err.emit(); + + lint }, ) } @@ -598,14 +608,13 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { } fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) { - tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| { - let mut err = lint.build("unreachable pattern"); + tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| { if let Some(catchall) = catchall { // We had a catchall pattern, hint at that. - err.span_label(span, "unreachable pattern"); - err.span_label(catchall, "matches any value"); + lint.span_label(span, "unreachable pattern"); + lint.span_label(catchall, "matches any value"); } - err.emit(); + lint }); } @@ -621,6 +630,11 @@ fn irrefutable_let_patterns( count: usize, span: Span, ) { + let span = match source { + LetSource::LetElse(span) => span, + _ => span, + }; + macro_rules! emit_diag { ( $lint:expr, @@ -630,18 +644,23 @@ fn irrefutable_let_patterns( ) => {{ let s = pluralize!(count); let these = pluralize!("this", count); - let mut diag = $lint.build(&format!("irrefutable {} pattern{s}", $source_name)); - diag.note(&format!("{these} pattern{s} will always match, so the {}", $note_sufix)); - diag.help(concat!("consider ", $help_sufix)); - diag.emit() + tcx.struct_span_lint_hir( + IRREFUTABLE_LET_PATTERNS, + id, + span, + format!("irrefutable {} pattern{s}", $source_name), + |lint| { + lint.note(&format!( + "{these} pattern{s} will always match, so the {}", + $note_sufix + )) + .help(concat!("consider ", $help_sufix)) + }, + ) }}; } - let span = match source { - LetSource::LetElse(span) => span, - _ => span, - }; - tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source { + match source { LetSource::GenericLet => { emit_diag!(lint, "`let`", "`let` is useless", "removing `let`"); } @@ -677,7 +696,7 @@ fn irrefutable_let_patterns( "instead using a `loop { ... }` with a `let` inside it" ); } - }); + }; } fn is_let_irrefutable<'p, 'tcx>( @@ -849,22 +868,22 @@ fn non_exhaustive_match<'p, 'tcx>( )); } [.., prev, last] if prev.span.eq_ctxt(last.span) => { - if let Ok(snippet) = sm.span_to_snippet(prev.span.between(last.span)) { - let comma = if matches!(last.body.kind, hir::ExprKind::Block(..)) - && last.span.eq_ctxt(last.body.span) - { - "" - } else { - "," - }; + let comma = if matches!(last.body.kind, hir::ExprKind::Block(..)) + && last.span.eq_ctxt(last.body.span) + { + "" + } else { + "," + }; + let spacing = if sm.is_multiline(prev.span.between(last.span)) { + sm.indentation_before(last.span).map(|indent| format!("\n{indent}")) + } else { + Some(" ".to_string()) + }; + if let Some(spacing) = spacing { suggestion = Some(( last.span.shrink_to_hi(), - format!( - "{}{}{} => todo!()", - comma, - snippet.strip_prefix(',').unwrap_or(&snippet), - pattern - ), + format!("{}{}{} => todo!()", comma, spacing, pattern), )); } } @@ -985,8 +1004,8 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( } /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`. -fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool { - !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env) +fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId) -> bool { + !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx, cx.param_env) } /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns. @@ -1012,7 +1031,7 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa // Get the binding move, extract the mutability if by-ref. let mut_outer = match typeck_results.extract_binding_mode(sess, pat.hir_id, pat.span) { - Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id, pat.span) => { + Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id) => { // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); sub.each_binding(|_, hir_id, span, _| { @@ -1051,7 +1070,7 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`. _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction. }, - Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id, span) => { + Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id) => { conflicts_move.push((span, name)) // `ref mut?` + by-move conflict. } Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine. @@ -1136,10 +1155,14 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); - if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) = - parent_parent_node - { - return LetSource::LetElse(*span); + match parent_parent_node { + hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) => { + return LetSource::LetElse(*span); + } + hir::Node::Arm(hir::Arm { guard: Some(hir::Guard::If(_)), .. }) => { + return LetSource::IfLetGuard; + } + _ => {} } let parent_parent_parent = hir.get_parent_node(parent_parent); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index d6dd0f017..ad12e0116 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,3 +1,4 @@ +use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -19,25 +20,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(super) fn const_to_pat( &self, cv: mir::ConstantKind<'tcx>, id: hir::HirId, span: Span, mir_structural_match_violation: bool, - ) -> Pat<'tcx> { - let pat = self.tcx.infer_ctxt().enter(|infcx| { - let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(cv, mir_structural_match_violation) - }); - - debug!(?pat); - pat + ) -> Box<Pat<'tcx>> { + let infcx = self.tcx.infer_ctxt().build(); + let mut convert = ConstToPat::new(self, id, span, infcx); + convert.to_pat(cv, mir_structural_match_violation) } } -struct ConstToPat<'a, 'tcx> { +struct ConstToPat<'tcx> { id: hir::HirId, span: Span, param_env: ty::ParamEnv<'tcx>, @@ -57,7 +54,7 @@ struct ConstToPat<'a, 'tcx> { behind_reference: Cell<bool>, // inference context used for checking `T: Structural` bounds. - infcx: InferCtxt<'a, 'tcx>, + infcx: InferCtxt<'tcx>, include_lint_checks: bool, @@ -73,21 +70,19 @@ mod fallback_to_const_ref { /// hoops to get a reference to the value. pub(super) struct FallbackToConstRef(()); - pub(super) fn fallback_to_const_ref<'a, 'tcx>( - c2p: &super::ConstToPat<'a, 'tcx>, - ) -> FallbackToConstRef { + pub(super) fn fallback_to_const_ref<'tcx>(c2p: &super::ConstToPat<'tcx>) -> FallbackToConstRef { assert!(c2p.behind_reference.get()); FallbackToConstRef(()) } } use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef}; -impl<'a, 'tcx> ConstToPat<'a, 'tcx> { +impl<'tcx> ConstToPat<'tcx> { fn new( pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, - infcx: InferCtxt<'a, 'tcx>, + infcx: InferCtxt<'tcx>, ) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { @@ -159,7 +154,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { &mut self, cv: mir::ConstantKind<'tcx>, mir_structural_match_violation: bool, - ) -> Pat<'tcx> { + ) -> Box<Pat<'tcx>> { trace!(self.treat_byte_string_as_slice); // This method is just a wrapper handling a validity check; the heavy lifting is // performed by the recursive `recur` method, which is not meant to be @@ -168,7 +163,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // once indirect_structural_match is a full fledged error, this // level of indirection can be eliminated - let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap(); + let inlined_const_as_pat = + self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| { + Box::new(Pat { + span: self.span, + ty: cv.ty(), + kind: PatKind::Constant { value: cv }, + }) + }); if self.include_lint_checks && !self.saw_const_match_error.get() { // If we were able to successfully convert the const to some pat, @@ -201,9 +203,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - |lint| { - lint.build(&msg).emit(); - }, + msg, + |lint| lint, ); } else { debug!( @@ -269,7 +270,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { &self, cv: mir::ConstantKind<'tcx>, mir_structural_match_violation: bool, - ) -> Result<Pat<'tcx>, FallbackToConstRef> { + ) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> { let id = self.id; let span = self.span; let tcx = self.tcx(); @@ -282,9 +283,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, - |lint| { - lint.build("floating-point types cannot be used in patterns").emit(); - }, + "floating-point types cannot be used in patterns", + |lint| lint, ); } PatKind::Constant { value: cv } @@ -336,15 +336,15 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, - |lint| { - let msg = format!( + DelayDm(|| { + format!( "to use a constant of type `{}` in a pattern, \ `{}` must be annotated with `#[derive(PartialEq, Eq)]`", cv.ty(), cv.ty(), - ); - lint.build(&msg).emit(); - }, + ) + }), + |lint| lint, ); } // Since we are behind a reference, we can just bubble the error up so we get a @@ -396,7 +396,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .map(|val| self.recur(*val, false)) .collect::<Result<_, _>>()?, slice: None, - suffix: Vec::new(), + suffix: Box::new([]), }, ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { // These are not allowed and will error elsewhere anyway. @@ -423,8 +423,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let old = self.behind_reference.replace(true); let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { - subpattern: Pat { - kind: Box::new(PatKind::Array { + subpattern: Box::new(Pat { + kind: PatKind::Array { prefix: tcx .destructure_mir_constant(param_env, array) .fields @@ -432,11 +432,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .map(|val| self.recur(*val, false)) .collect::<Result<_, _>>()?, slice: None, - suffix: vec![], - }), + suffix: Box::new([]), + }, span, ty: *pointee_ty, - }, + }), }; self.behind_reference.set(old); val @@ -449,8 +449,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let old = self.behind_reference.replace(true); let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { - subpattern: Pat { - kind: Box::new(PatKind::Slice { + subpattern: Box::new(Pat { + kind: PatKind::Slice { prefix: tcx .destructure_mir_constant(param_env, array) .fields @@ -458,11 +458,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .map(|val| self.recur(*val, false)) .collect::<Result<_, _>>()?, slice: None, - suffix: vec![], - }), + suffix: Box::new([]), + }, span, ty: tcx.mk_slice(elem_ty), - }, + }), }; self.behind_reference.set(old); val @@ -484,7 +484,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - |lint| {lint.build(&msg).emit();}, + msg, + |lint| lint, ); } PatKind::Constant { value: cv } @@ -505,7 +506,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // convert the dereferenced constant to a pattern that is the sub-pattern of the // deref pattern. _ => { - if !pointee_ty.is_sized(tcx.at(span), param_env) { + if !pointee_ty.is_sized(tcx, param_env) { // `tcx.deref_mir_constant()` below will ICE with an unsized type // (except slices, which are handled in a separate arm above). let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty); @@ -533,7 +534,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => { PatKind::Constant { value: cv } } - ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => { + ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => { PatKind::Constant { value: cv } } // FIXME: these can have very surprising behaviour where optimization levels or other @@ -552,9 +553,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::POINTER_STRUCTURAL_MATCH, id, span, - |lint| { - lint.build(msg).emit(); - }, + msg, + |lint| lint, ); } PatKind::Constant { value: cv } @@ -590,12 +590,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH, id, span, - |lint| { - lint.build(&msg).emit(); - }, + msg, + |lint| lint, ); } - Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) }) + Ok(Box::new(Pat { span, ty: cv.ty(), kind })) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 8d6f8efb6..595abc8f6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -71,9 +71,9 @@ use std::ops::RangeInclusive; /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { - if let PatKind::Or { pats } = pat.kind.as_ref() { - for pat in pats { - expand(pat, vec); + if let PatKind::Or { pats } = &pat.kind { + for pat in pats.iter() { + expand(&pat, vec); } } else { vec.push(pat) @@ -252,10 +252,14 @@ impl IntRange { let kind = if lo == hi { PatKind::Constant { value: lo_const } } else { - PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) + PatKind::Range(Box::new(PatRange { + lo: lo_const, + hi: hi_const, + end: RangeEnd::Included, + })) }; - Pat { ty, span: DUMMY_SP, kind: Box::new(kind) } + Pat { ty, span: DUMMY_SP, kind } } /// Lint on likely incorrect range patterns (#63987) @@ -295,10 +299,10 @@ impl IntRange { lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, pcx.span, + "multiple patterns overlap on their endpoints", |lint| { - let mut err = lint.build("multiple patterns overlap on their endpoints"); for (int_range, span) in overlaps { - err.span_label( + lint.span_label( span, &format!( "this range overlaps on `{}`...", @@ -306,9 +310,9 @@ impl IntRange { ), ); } - err.span_label(pcx.span, "... with this range"); - err.note("you likely meant to write mutually exclusive ranges"); - err.emit(); + lint.span_label(pcx.span, "... with this range"); + lint.note("you likely meant to write mutually exclusive ranges"); + lint }, ); } @@ -984,10 +988,12 @@ impl<'tcx> SplitWildcard<'tcx> { .filter(|(_, v)| { // If `exhaustive_patterns` is enabled, we exclude variants known to be // uninhabited. - let is_uninhabited = is_exhaustive_pat_feature - && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module); - !is_uninhabited + !is_exhaustive_pat_feature + || v.inhabited_predicate(cx.tcx, *def).subst(cx.tcx, substs).apply( + cx.tcx, + cx.param_env, + cx.module, + ) }) .map(|(idx, _)| Variant(idx)) .collect(); @@ -1297,7 +1303,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); let ctor; let fields; - match pat.kind.as_ref() { + match &pat.kind { PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern), PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { @@ -1342,9 +1348,9 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { fields = Fields::singleton(cx, pat); } ty::Adt(adt, _) => { - ctor = match pat.kind.as_ref() { + ctor = match pat.kind { PatKind::Leaf { .. } => Single, - PatKind::Variant { variant_index, .. } => Variant(*variant_index), + PatKind::Variant { variant_index, .. } => Variant(variant_index), _ => bug!(), }; let variant = &adt.variant(ctor.variant_index_for_adt(*adt)); @@ -1402,7 +1408,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } } - &PatKind::Range(PatRange { lo, hi, end }) => { + &PatKind::Range(box PatRange { lo, hi, end }) => { let ty = lo.ty(); ctor = if let Some(int_range) = IntRange::from_range( cx.tcx, @@ -1429,7 +1435,8 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { FixedLen(prefix.len() + suffix.len()) }; ctor = Slice(Slice::new(array_len, kind)); - fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); + fields = + Fields::from_iter(cx, prefix.iter().chain(suffix.iter()).map(|p| mkpat(&*p))); } PatKind::Or { .. } => { ctor = Or; @@ -1442,15 +1449,15 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> { let is_wildcard = |pat: &Pat<'_>| { - matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) + matches!(pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) }; - let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); - let pat = match &self.ctor { + let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx))); + let kind = match &self.ctor { Single | Variant(_) => match self.ty.kind() { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .map(|(i, pattern)| FieldPat { field: Field::new(i), pattern }) .collect(), }, ty::Adt(adt_def, _) if adt_def.is_box() => { @@ -1485,7 +1492,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { FixedLen(_) => PatKind::Slice { prefix: subpatterns.collect(), slice: None, - suffix: vec![], + suffix: Box::new([]), }, VarLen(prefix, _) => { let mut subpatterns = subpatterns.peekable(); @@ -1504,14 +1511,18 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { subpatterns.next(); } } - let suffix: Vec<_> = subpatterns.collect(); + let suffix: Box<[_]> = subpatterns.collect(); let wild = Pat::wildcard_from_ty(self.ty); - PatKind::Slice { prefix, slice: Some(wild), suffix } + PatKind::Slice { + prefix: prefix.into_boxed_slice(), + slice: Some(Box::new(wild)), + suffix, + } } } } &Str(value) => PatKind::Constant { value }, - &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + &FloatRange(lo, hi, end) => PatKind::Range(Box::new(PatRange { lo, hi, end })), IntRange(range) => return range.to_pat(cx.tcx, self.ty), Wildcard | NonExhaustive => PatKind::Wild, Missing { .. } => bug!( @@ -1523,7 +1534,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } }; - Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) } + Pat { ty: self.ty, span: DUMMY_SP, kind } } pub(super) fn is_or_pat(&self) -> bool { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a13748a2d..2526522a2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -29,27 +29,27 @@ use rustc_span::{Span, Symbol}; use std::cmp::Ordering; #[derive(Clone, Debug)] -pub(crate) enum PatternError { +enum PatternError { AssocConstInPattern(Span), ConstParamInPattern(Span), StaticInPattern(Span), NonConstPath(Span), } -pub(crate) struct PatCtxt<'a, 'tcx> { - pub(crate) tcx: TyCtxt<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, - pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, - pub(crate) errors: Vec<PatternError>, +struct PatCtxt<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + errors: Vec<PatternError>, include_lint_checks: bool, } -pub(crate) fn pat_from_hir<'a, 'tcx>( +pub(super) fn pat_from_hir<'a, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, -) -> Pat<'tcx> { +) -> Box<Pat<'tcx>> { let mut pcx = PatCtxt::new(tcx, param_env, typeck_results); let result = pcx.lower_pattern(pat); if !pcx.errors.is_empty() { @@ -61,7 +61,7 @@ pub(crate) fn pat_from_hir<'a, 'tcx>( } impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - pub(crate) fn new( + fn new( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -69,12 +69,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { PatCtxt { tcx, param_env, typeck_results, errors: vec![], include_lint_checks: false } } - pub(crate) fn include_lint_checks(&mut self) -> &mut Self { + fn include_lint_checks(&mut self) -> &mut Self { self.include_lint_checks = true; self } - pub(crate) fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { // When implicit dereferences have been inserted in this pattern, the unadjusted lowered // pattern has the type that results *after* dereferencing. For example, in this code: // @@ -86,7 +86,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // ``` // // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is - // determined in rustc_typeck::check::match). The adjustments would be + // determined in rustc_hir_analysis::check::match). The adjustments would be // // `vec![&&Option<i32>, &Option<i32>]`. // @@ -97,13 +97,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let unadjusted_pat = self.lower_pattern_unadjusted(pat); self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( unadjusted_pat, - |pat, ref_ty| { + |pat: Box<_>, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); - Pat { + Box::new(Pat { span: pat.span, ty: *ref_ty, - kind: Box::new(PatKind::Deref { subpattern: pat }), - } + kind: PatKind::Deref { subpattern: pat }, + }) }, ) } @@ -113,7 +113,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> (PatKind<'tcx>, Option<Ascription<'tcx>>) { match self.lower_lit(expr) { - PatKind::AscribeUserType { ascription, subpattern: Pat { kind: box kind, .. } } => { + PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => { (kind, Some(ascription)) } kind => (kind, None), @@ -134,7 +134,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { match (end, cmp) { // `x..y` where `x < y`. // Non-empty because the range includes at least `x`. - (RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), + (RangeEnd::Excluded, Some(Ordering::Less)) => { + PatKind::Range(Box::new(PatRange { lo, hi, end })) + } // `x..y` where `x >= y`. The range is empty => error. (RangeEnd::Excluded, _) => { struct_span_err!( @@ -149,7 +151,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // `x..=y` where `x == y`. (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo }, // `x..=y` where `x < y`. - (RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), + (RangeEnd::Included, Some(Ordering::Less)) => { + PatKind::Range(Box::new(PatRange { lo, hi, end })) + } // `x..=y` where `x > y` hence the range is empty => error. (RangeEnd::Included, _) => { let mut err = struct_span_err!( @@ -196,8 +200,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } - fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + #[instrument(skip(self), level = "debug")] + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { let mut ty = self.typeck_results.node_type(pat.hir_id); + let mut span = pat.span; let kind = match pat.kind { hir::PatKind::Wild => PatKind::Wild, @@ -228,7 +234,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // constants somewhere. Have them on the range pattern. for end in &[lo, hi] { if let Some((_, Some(ascription))) = end { - let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) }; + let subpattern = Box::new(Pat { span: pat.span, ty, kind }); kind = PatKind::AscribeUserType { ascription: ascription.clone(), subpattern }; } @@ -258,6 +264,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Binding(_, id, ident, ref sub) => { + if let Some(ident_span) = ident.span.find_ancestor_inside(span) { + span = span.with_hi(ident_span.hi()); + } + let bm = *self .typeck_results .pat_binding_modes() @@ -322,14 +332,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, }; - Pat { span: pat.span, ty, kind: Box::new(kind) } + Box::new(Pat { span, ty, kind }) } fn lower_tuple_subpats( &mut self, pats: &'tcx [hir::Pat<'tcx>], expected_len: usize, - gap_pos: Option<usize>, + gap_pos: hir::DotDotPos, ) -> Vec<FieldPat<'tcx>> { pats.iter() .enumerate_and_adjust(expected_len, gap_pos) @@ -340,11 +350,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .collect() } - fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> { + fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Box<Pat<'tcx>>]> { pats.iter().map(|p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option<Pat<'tcx>> { + fn lower_opt_pattern( + &mut self, + pat: &'tcx Option<&'tcx hir::Pat<'tcx>>, + ) -> Option<Box<Pat<'tcx>>> { pat.as_ref().map(|p| self.lower_pattern(p)) } @@ -420,7 +433,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | DefKind::AssocTy, _, ) - | Res::SelfTy { .. } + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { let pattern_error = match res { @@ -436,12 +450,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) { debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); let annotation = CanonicalUserTypeAnnotation { - user_ty, + user_ty: Box::new(user_ty), span, inferred_ty: self.typeck_results.node_type(hir_id), }; kind = PatKind::AscribeUserType { - subpattern: Pat { span, ty, kind: Box::new(kind) }, + subpattern: Box::new(Pat { span, ty, kind }), ascription: Ascription { annotation, variance: ty::Variance::Covariant }, }; } @@ -453,11 +467,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. #[instrument(skip(self), level = "debug")] - fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { + fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Box<Pat<'tcx>> { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); - let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) }; + let pat_from_kind = |kind| Box::new(Pat { span, ty, kind }); let (def_id, is_associated_const) = match res { Res::Def(DefKind::Const, def_id) => (def_id, false), @@ -469,7 +483,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Use `Reveal::All` here because patterns are always monomorphic even if their function // isn't. let param_env_reveal_all = self.param_env.with_reveal_all_normalized(self.tcx); - let substs = self.typeck_results.node_substs(id); + // N.B. There is no guarantee that substs collected in typeck results are fully normalized, + // so they need to be normalized in order to pass to `Instance::resolve`, which will ICE + // if given unnormalized types. + let substs = self + .tcx + .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_substs(id)); let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { Ok(Some(i)) => i, Ok(None) => { @@ -505,13 +524,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let user_provided_types = self.typeck_results().user_provided_types(); if let Some(&user_ty) = user_provided_types.get(id) { let annotation = CanonicalUserTypeAnnotation { - user_ty, + user_ty: Box::new(user_ty), span, inferred_ty: self.typeck_results().node_type(id), }; - Pat { + Box::new(Pat { span, - kind: Box::new(PatKind::AscribeUserType { + kind: PatKind::AscribeUserType { subpattern: pattern, ascription: Ascription { annotation, @@ -519,9 +538,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// `variance` field documentation for details. variance: ty::Variance::Contravariant, }, - }), + }, ty: const_.ty(), - } + }) } else { pattern } @@ -553,23 +572,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let value = value.eval(self.tcx, self.param_env); match value { - mir::ConstantKind::Ty(c) => { - match c.kind() { - ConstKind::Param(_) => { - self.errors.push(PatternError::ConstParamInPattern(span)); - return PatKind::Wild; - } - ConstKind::Unevaluated(_) => { - // If we land here it means the const can't be evaluated because it's `TooGeneric`. - self.tcx - .sess - .span_err(span, "constant pattern depends on a generic parameter"); - return PatKind::Wild; - } - _ => bug!("Expected either ConstKind::Param or ConstKind::Unevaluated"), + mir::ConstantKind::Ty(c) => match c.kind() { + ConstKind::Param(_) => { + self.errors.push(PatternError::ConstParamInPattern(span)); + return PatKind::Wild; } + _ => bug!("Expected ConstKind::Param"), + }, + mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind, + mir::ConstantKind::Unevaluated(..) => { + // If we land here it means the const can't be evaluated because it's `TooGeneric`. + self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); + return PatKind::Wild; } - mir::ConstantKind::Val(_, _) => *self.const_to_pat(value, id, span, false).kind, } } @@ -580,7 +595,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { let (lit, neg) = match expr.kind { hir::ExprKind::Path(ref qpath) => { - return *self.lower_path(qpath, expr.hir_id, expr.span).kind; + return self.lower_path(qpath, expr.hir_id, expr.span).kind; } hir::ExprKind::ConstBlock(ref anon_const) => { return self.lower_inline_const(anon_const, expr.hir_id, expr.span); @@ -598,7 +613,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) { - Ok(constant) => *self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, + Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, Err(LitToConstError::Reported) => PatKind::Wild, Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } @@ -615,7 +630,7 @@ impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { } } -pub(crate) trait PatternFoldable<'tcx>: Sized { +trait PatternFoldable<'tcx>: Sized { fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { self.super_fold_with(folder) } @@ -623,7 +638,7 @@ pub(crate) trait PatternFoldable<'tcx>: Sized { fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self; } -pub(crate) trait PatternFolder<'tcx>: Sized { +trait PatternFolder<'tcx>: Sized { fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { pattern.super_fold_with(self) } @@ -646,6 +661,12 @@ impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec<T> { } } +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box<[T]> { + fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } +} + impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option<T> { fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self { self.as_ref().map(|t| t.fold_with(folder)) @@ -665,7 +686,7 @@ macro_rules! ClonePatternFoldableImpls { } ClonePatternFoldableImpls! { <'tcx> - Span, Field, Mutability, Symbol, LocalVarId, usize, ty::Const<'tcx>, + Span, Field, Mutability, Symbol, LocalVarId, usize, Region<'tcx>, Ty<'tcx>, BindingMode, AdtDef<'tcx>, SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, UserTypeProjection, CanonicalUserTypeAnnotation<'tcx> @@ -732,7 +753,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { PatKind::Deref { subpattern: subpattern.fold_with(folder) } } PatKind::Constant { value } => PatKind::Constant { value }, - PatKind::Range(range) => PatKind::Range(range), + PatKind::Range(ref range) => PatKind::Range(range.clone()), PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice { prefix: prefix.fold_with(folder), slice: slice.fold_with(folder), diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 0a660ef30..8dc9976ea 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -364,8 +364,8 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. #[derive(Clone)] -struct PatStack<'p, 'tcx> { - pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, +pub(crate) struct PatStack<'p, 'tcx> { + pub(crate) pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { @@ -403,6 +403,21 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { }) } + // Recursively expand all patterns into their subpatterns and push each `PatStack` to matrix. + fn expand_and_extend<'a>(&'a self, matrix: &mut Matrix<'p, 'tcx>) { + if !self.is_empty() && self.head().is_or_pat() { + for pat in self.head().iter_fields() { + let mut new_patstack = PatStack::from_pattern(pat); + new_patstack.pats.extend_from_slice(&self.pats[1..]); + if !new_patstack.is_empty() && new_patstack.head().is_or_pat() { + new_patstack.expand_and_extend(matrix); + } else if !new_patstack.is_empty() { + matrix.push(new_patstack); + } + } + } + } + /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations. /// /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing @@ -436,7 +451,7 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { /// A 2D matrix. #[derive(Clone)] pub(super) struct Matrix<'p, 'tcx> { - patterns: Vec<PatStack<'p, 'tcx>>, + pub patterns: Vec<PatStack<'p, 'tcx>>, } impl<'p, 'tcx> Matrix<'p, 'tcx> { @@ -453,7 +468,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// expands it. fn push(&mut self, row: PatStack<'p, 'tcx>) { if !row.is_empty() && row.head().is_or_pat() { - self.patterns.extend(row.expand_or_pat()); + row.expand_and_extend(self); } else { self.patterns.push(row); } @@ -731,7 +746,7 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { /// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` /// is not exhaustive enough. /// -/// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`. +/// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, scrut_ty: Ty<'tcx>, @@ -739,9 +754,8 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( hir_id: HirId, witnesses: Vec<DeconstructedPat<'p, 'tcx>>, ) { - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| { - let mut lint = build.build("some variants are not matched explicitly"); + cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| { + let joined_patterns = joined_uncovered_patterns(cx, &witnesses); lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); lint.help( "ensure that all variants are matched explicitly by adding the suggested match arms", @@ -750,7 +764,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", scrut_ty, )); - lint.emit(); + lint }); } @@ -776,7 +790,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( /// `is_under_guard` is used to inform if the pattern has a guard. If it /// has one it must not be inserted into the matrix. This shouldn't be /// relied on for soundness. -#[instrument(level = "debug", skip(cx, matrix, hir_id))] +#[instrument(level = "debug", skip(cx, matrix, hir_id), ret)] fn is_useful<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, @@ -827,7 +841,15 @@ fn is_useful<'p, 'tcx>( } } } else { - let ty = v.head().ty(); + let mut ty = v.head().ty(); + + // Opaque types can't get destructured/split, but the patterns can + // actually hint at hidden types, so we use the patterns' types instead. + if let ty::Opaque(..) = ty.kind() { + if let Some(row) = rows.first() { + ty = row.head().ty(); + } + } let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; @@ -863,7 +885,7 @@ fn is_useful<'p, 'tcx>( // that has the potential to trigger the `non_exhaustive_omitted_patterns` lint. // To understand the workings checkout `Constructor::split` and `SplitWildcard::new/into_ctors` if is_non_exhaustive_and_wild - // We check that the match has a wildcard pattern and that that wildcard is useful, + // We check that the match has a wildcard pattern and that wildcard is useful, // meaning there are variants that are covered by the wildcard. Without the check // for `witness_preference` the lint would trigger on `if let NonExhaustiveEnum::A = foo {}` && usefulness.is_useful() && matches!(witness_preference, RealArm) @@ -902,7 +924,6 @@ fn is_useful<'p, 'tcx>( v.head().set_reachable(); } - debug!(?ret); ret } |