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 e28fd2c50814f..334e97f9b0a31 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,8 +1,8 @@ use super::deconstruct_pat::{Constructor, DeconstructedPat}; use super::usefulness::{ - compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, + compute_match_usefulness, MatchArm, Reachability, UsefulnessCtxt, UsefulnessReport, }; -use super::{PatCtxt, PatternError}; +use super::{deconstructed_to_pat, pat_to_deconstructed, MatchCheckCtxt, PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; @@ -14,7 +14,8 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{HirId, Pat}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_session::lint::builtin::{ - BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, + BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, NON_EXHAUSTIVE_OMITTED_PATTERNS, + OVERLAPPING_RANGE_ENDPOINTS, UNREACHABLE_PATTERNS, }; use rustc_session::Session; use rustc_span::{DesugaringKind, ExpnKind, Span}; @@ -50,7 +51,7 @@ struct MatchVisitor<'a, 'p, 'tcx> { tcx: TyCtxt<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, - pattern_arena: &'p TypedArena>, + pattern_arena: &'p TypedArena>>, } impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { @@ -127,14 +128,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { fn lower_pattern( &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, + cx: &UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>>, pat: &'tcx hir::Pat<'tcx>, have_errors: &mut bool, - ) -> &'p DeconstructedPat<'p, 'tcx> { + ) -> &'p DeconstructedPat<'p, MatchCheckCtxt<'tcx>> { let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results); patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); - let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern)); + let pattern: &_ = cx.pattern_arena.alloc(pat_to_deconstructed(cx, &pattern)); if !patcx.errors.is_empty() { *have_errors = true; patcx.report_inlining_errors(); @@ -142,13 +143,13 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { pattern } - fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> { - MatchCheckCtxt { + fn new_cx(&self, hir_id: HirId) -> UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>> { + let cx = MatchCheckCtxt { tcx: self.tcx, param_env: self.param_env, module: self.tcx.parent_module(hir_id).to_def_id(), - pattern_arena: &self.pattern_arena, - } + }; + UsefulnessCtxt { cx, pattern_arena: &self.pattern_arena } } fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) { @@ -164,15 +165,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { arms: &'tcx [hir::Arm<'tcx>], source: hir::MatchSource, ) { - let mut cx = self.new_cx(scrut.hir_id); + let ucx = self.new_cx(scrut.hir_id); + let cx = &ucx.cx; for arm in arms { // Check the arm for some things unrelated to exhaustiveness. self.check_patterns(&arm.pat, Refutable); if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { self.check_patterns(pat, Refutable); - let tpat = self.lower_pattern(&mut cx, pat, &mut false); - check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span()); + let tpat = self.lower_pattern(&ucx, pat, &mut false); + check_let_reachability(&ucx, pat.hir_id, tpat, tpat.span()); } } @@ -181,7 +183,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let arms: Vec<_> = arms .iter() .map(|hir::Arm { pat, guard, .. }| MatchArm { - pat: self.lower_pattern(&mut cx, pat, &mut have_errors), + pat: self.lower_pattern(&ucx, pat, &mut have_errors), hir_id: pat.hir_id, has_guard: guard.is_some(), }) @@ -193,11 +195,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut); - let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty); + let report = compute_match_usefulness(&ucx, &arms, scrut.hir_id, scrut_ty); match source { hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { - report_arm_reachability(&cx, &report) + report_exhaustiveness_lints(&cx, &report); + report_arm_reachability(&cx, &report); } // Unreachable patterns in try and await expressions occur when one of // the arms are an uninhabited type. Which is OK. @@ -213,12 +216,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option) { - let mut cx = self.new_cx(pat.hir_id); + let ucx = self.new_cx(pat.hir_id); + let cx = &ucx.cx; - let pattern = self.lower_pattern(&mut cx, pat, &mut false); + let pattern = self.lower_pattern(&ucx, pat, &mut false); let pattern_ty = pattern.ty(); let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + let report = compute_match_usefulness(&ucx, &arms, pat.hir_id, pattern_ty); + report_exhaustiveness_lints(&cx, &report); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -356,11 +361,11 @@ fn check_for_bindings_named_same_as_variants( } /// Checks for common cases of "catchall" patterns that may not be intended as such. -fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { +fn pat_is_catchall(pat: &DeconstructedPat<'_, MatchCheckCtxt<'_>>) -> bool { use Constructor::*; match pat.ctor() { Wildcard => true, - Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Single | Tuple(_) | Ref(_) | BoxPat(_) => pat.iter_fields().all(|pat| pat_is_catchall(pat)), _ => false, } } @@ -437,17 +442,17 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) { } fn check_let_reachability<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, + ucx: &UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>>, pat_id: HirId, - pat: &'p DeconstructedPat<'p, 'tcx>, + pat: &'p DeconstructedPat<'p, MatchCheckCtxt<'tcx>>, span: Span, ) { + let cx = &ucx.cx; let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty()); + let report = compute_match_usefulness(&ucx, &arms, pat_id, pat.ty()); + report_exhaustiveness_lints(&cx, &report); // Report if the pattern is unreachable, which can only occur when the type is uninhabited. - // This also reports unreachable sub-patterns though, so we can't just replace it with an - // `is_uninhabited` check. report_arm_reachability(&cx, &report); if report.non_exhaustiveness_witnesses.is_empty() { @@ -458,24 +463,16 @@ fn check_let_reachability<'p, 'tcx>( /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - report: &UsefulnessReport<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, + report: &UsefulnessReport<'p, MatchCheckCtxt<'tcx>>, ) { - use Reachability::*; let mut catchall = None; - for (arm, is_useful) in report.arm_usefulness.iter() { - match is_useful { - Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall), - Reachable(unreachables) if unreachables.is_empty() => {} - // The arm is reachable, but contains unreachable subpatterns (from or-patterns). - Reachable(unreachables) => { - let mut unreachables = unreachables.clone(); - // Emit lints in the order in which they occur in the file. - unreachables.sort_unstable(); - for span in unreachables { - unreachable_pattern(cx.tcx, span, arm.hir_id, None); - } + for (arm, reachability) in report.arm_usefulness.iter() { + match reachability { + Reachability::Unreachable => { + unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall) } + Reachability::Reachable => {} } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { catchall = Some(arm.pat.span()); @@ -483,12 +480,81 @@ fn report_arm_reachability<'p, 'tcx>( } } +/// Report various things detected during exhaustiveness checking. +fn report_exhaustiveness_lints<'p, 'tcx>( + cx: &MatchCheckCtxt<'tcx>, + report: &UsefulnessReport<'p, MatchCheckCtxt<'tcx>>, +) { + if !report.unreachable_subpatterns.is_empty() { + let mut unreachables = report.unreachable_subpatterns.clone(); + // Emit lints in the order in which they occur in the file. + unreachables.sort_unstable(); + for (span, hir_id) in unreachables { + unreachable_pattern(cx.tcx, span, hir_id, None); + } + } + for (ty, span, hir_id, patterns) in &report.non_exhaustive_omitted_patterns { + lint_non_exhaustive_omitted_patterns(cx, ty, *span, *hir_id, patterns.as_slice()); + } + for (span, hir_id, overlaps) in &report.overlapping_range_endpoints { + lint_overlapping_range_endpoints(cx, *span, *hir_id, overlaps.as_slice()); + } +} + +/// Report when some variants of a `non_exhaustive` enum failed to be listed explicitly. +/// +/// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`. +pub(super) fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( + cx: &MatchCheckCtxt<'tcx>, + scrut_ty: Ty<'tcx>, + span: Span, + hir_id: HirId, + witnesses: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], +) { + let joined_patterns = joined_uncovered_patterns(cx, &witnesses); + cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, span, |build| { + let mut lint = build.build("some variants are not matched explicitly"); + lint.span_label(span, pattern_not_covered_label(witnesses, &joined_patterns)); + lint.help( + "ensure that all variants are matched explicitly by adding the suggested match arms", + ); + lint.note(&format!( + "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", + scrut_ty, + )); + lint.emit(); + }); +} + +/// Lint on likely incorrect range patterns (#63987) +pub(super) fn lint_overlapping_range_endpoints<'p, 'tcx>( + cx: &MatchCheckCtxt<'tcx>, + span: Span, + hir_id: HirId, + overlaps: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], +) { + if !overlaps.is_empty() { + cx.tcx.struct_span_lint_hir(OVERLAPPING_RANGE_ENDPOINTS, hir_id, span, |lint| { + let mut err = lint.build("multiple patterns overlap on their endpoints"); + for pat in overlaps { + err.span_label( + pat.span(), + &format!("this range overlaps on `{}`...", deconstructed_to_pat(cx, pat)), + ); + } + err.span_label(span, "... with this range"); + err.note("you likely meant to write mutually exclusive ranges"); + err.emit(); + }); + } +} + /// Report that a match is not exhaustive. fn non_exhaustive_match<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - witnesses: Vec>, + witnesses: Vec>>, is_empty_match: bool, ) { let non_empty_enum = match scrut_ty.kind() { @@ -556,17 +622,17 @@ fn non_exhaustive_match<'p, 'tcx>( } crate fn joined_uncovered_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - witnesses: &[DeconstructedPat<'p, 'tcx>], + cx: &MatchCheckCtxt<'tcx>, + witnesses: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], ) -> String { const LIMIT: usize = 3; - let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string(); + let pat_to_str = |pat| deconstructed_to_pat(cx, pat).to_string(); match witnesses { [] => bug!(), - [witness] => format!("`{}`", witness.to_pat(cx)), + [witness] => format!("`{}`", deconstructed_to_pat(cx, witness)), [head @ .., tail] if head.len() < LIMIT => { let head: Vec<_> = head.iter().map(pat_to_str).collect(); - format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx)) + format!("`{}` and `{}`", head.join("`, `"), deconstructed_to_pat(cx, tail)) } _ => { let (head, tail) = witnesses.split_at(LIMIT); @@ -577,7 +643,7 @@ crate fn joined_uncovered_patterns<'p, 'tcx>( } crate fn pattern_not_covered_label( - witnesses: &[DeconstructedPat<'_, '_>], + witnesses: &[DeconstructedPat<'_, MatchCheckCtxt<'_>>], joined_patterns: &str, ) -> String { format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns) @@ -585,10 +651,10 @@ crate fn pattern_not_covered_label( /// Point at the definition of non-covered `enum` variants. fn adt_defined_here<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>, ty: Ty<'tcx>, - witnesses: &[DeconstructedPat<'p, 'tcx>], + witnesses: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], ) { let ty = ty.peel_refs(); if let ty::Adt(def, _) = ty.kind() { @@ -605,10 +671,13 @@ fn adt_defined_here<'p, 'tcx>( } fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, def: &AdtDef, - patterns: impl Iterator>, -) -> Vec { + patterns: impl Iterator>>, +) -> Vec +where + MatchCheckCtxt<'tcx>: 'p, +{ use Constructor::*; let mut covered = vec![]; for pattern in patterns { 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 dfcbd0da3a6e1..aa88d6b68a80a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -45,21 +45,9 @@ use self::Constructor::*; use self::SliceKind::*; -use super::compare_const_vals; -use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use super::usefulness::{Context, PatCtxt, UsefulnessCtxt}; use rustc_data_structures::captures::Captures; -use rustc_index::vec::Idx; - -use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::mir::Field; -use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; -use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef}; -use rustc_session::lint; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; use std::cell::Cell; @@ -68,21 +56,25 @@ use std::fmt; use std::iter::{once, IntoIterator}; 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); - } - } else { - vec.push(pat) - } - } +macro_rules! span_bug { + ($span:expr, $($rest:tt)*) => { + Cx::span_bug($span, || format!($($rest)*)) + }; +} - let mut pats = Vec::new(); - expand(pat, &mut pats); - pats +#[derive(Copy, Clone, PartialEq, Debug)] +pub(crate) enum RangeEnd { + Included, + Excluded, +} + +impl fmt::Display for RangeEnd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + RangeEnd::Included => "..=", + RangeEnd::Excluded => "..", + }) + } } /// An inclusive interval, used for precise integer exhaustiveness checking. @@ -92,11 +84,12 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { /// For example, the pattern `-128..=127i8` is encoded as `0..=255`. /// This makes comparisons and arithmetic on interval endpoints much more /// straightforward. See `signed_bias` for details. +/// Only use for chars, ints and uints. /// /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. #[derive(Clone, PartialEq, Eq)] -pub(super) struct IntRange { +pub(crate) struct IntRange { range: RangeInclusive, /// Keeps the bias used for encoding the range. It depends on the type of the range and /// possibly the pointer size of the current architecture. The algorithm ensures we never @@ -105,11 +98,6 @@ pub(super) struct IntRange { } impl IntRange { - #[inline] - fn is_integral(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) - } - fn is_singleton(&self) -> bool { self.range.start() == self.range.end() } @@ -119,80 +107,30 @@ impl IntRange { } #[inline] - fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> { - match *ty.kind() { - ty::Bool => Some((Size::from_bytes(1), 0)), - ty::Char => Some((Size::from_bytes(4), 0)), - ty::Int(ity) => { - let size = Integer::from_int_ty(&tcx, ity).size(); - Some((size, 1u128 << (size.bits() as u128 - 1))) - } - ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)), - _ => None, - } - } - - #[inline] - fn from_const<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &Const<'tcx>, - ) -> Option { - if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { - let ty = value.ty; - let val = (|| { - if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just fall through, which - // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr_internal(target_size) { - return Some(bits); - } - } - // This is a more general form of the previous case. - value.try_eval_bits(tcx, param_env, ty) - })()?; - let val = val ^ bias; - Some(IntRange { range: val..=val, bias }) - } else { - None - } - } - - #[inline] - fn from_range<'tcx>( - tcx: TyCtxt<'tcx>, + pub(super) fn from_bits( + ty_size: u64, + is_signed_int: bool, lo: u128, hi: u128, - ty: Ty<'tcx>, end: &RangeEnd, - ) -> Option { - if Self::is_integral(ty) { - // Perform a shift if the underlying types are signed, - // which makes the interval arithmetic simpler. - let bias = IntRange::signed_bias(tcx, ty); - let (lo, hi) = (lo ^ bias, hi ^ bias); - let offset = (*end == RangeEnd::Excluded) as u128; - if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { - // This should have been caught earlier by E0030. - bug!("malformed range pattern: {}..={}", lo, (hi - offset)); - } - Some(IntRange { range: lo..=(hi - offset), bias }) - } else { - None + ) -> IntRange { + let bias = if is_signed_int { 1u128 << (ty_size as u128 - 1) } else { 0 }; + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let (lo, hi) = (lo ^ bias, hi ^ bias); + let offset = (*end == RangeEnd::Excluded) as u128; + if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { + // This should have been caught earlier by E0030. + bug!("malformed range pattern: {}..={}", lo, (hi - offset)); } + IntRange { range: lo..=(hi - offset), bias } } - // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. - fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 { - match *ty.kind() { - ty::Int(ity) => { - let bits = Integer::from_int_ty(&tcx, ity).size().bits() as u128; - 1u128 << (bits - 1) - } - _ => 0, - } + /// The reverse of `Self::from_bits`. + pub(super) fn to_bits(&self) -> (u128, u128, RangeEnd) { + let (lo, hi) = self.boundaries(); + let bias = self.bias; + (lo ^ bias, hi ^ bias, RangeEnd::Included) } fn is_subrange(&self, other: &Self) -> bool { @@ -209,7 +147,11 @@ impl IntRange { } } - fn suspicious_intersection(&self, other: &Self) -> bool { + /// Check whether two non-singleton ranges touch exactly on on of their endpoints. + fn suspicious_intersection(&self, other: &Self) -> Option { + if self.is_singleton() || other.is_singleton() { + return None; + } // `false` in the following cases: // 1 ---- // 1 ---------- // 1 ---- // 1 ---- // 2 ---------- // 2 ---- // 2 ---- // 2 ---- @@ -223,36 +165,21 @@ impl IntRange { // 2 -------- // 2 ------- let (lo, hi) = self.boundaries(); let (other_lo, other_hi) = other.boundaries(); - (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton() - } - - /// Only used for displaying the range properly. - fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { - let (lo, hi) = self.boundaries(); - - let bias = self.bias; - let (lo, hi) = (lo ^ bias, hi ^ bias); - - let env = ty::ParamEnv::empty().and(ty); - let lo_const = ty::Const::from_bits(tcx, lo, env); - let hi_const = ty::Const::from_bits(tcx, hi, env); - - let kind = if lo == hi { - PatKind::Constant { value: lo_const } + if lo == other_hi || hi == other_lo { + Some(self.intersection(other).unwrap()) } else { - PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) - }; - - Pat { ty, span: DUMMY_SP, kind: Box::new(kind) } + None + } } /// Lint on likely incorrect range patterns (#63987) - pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( + pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, Cx: Context + 'a>( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - pats: impl Iterator>, + pcx: PatCtxt<'_, 'p, Cx>, + report: &mut Vec<(Cx::Span, Cx::HirId, Vec>)>, + pats: impl Iterator>, column_count: usize, - hir_id: HirId, + hir_id: Cx::HirId, ) { if self.is_singleton() { return; @@ -272,33 +199,22 @@ impl IntRange { return; } - let overlaps: Vec<_> = pats - .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) - .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| (self.intersection(&range).unwrap(), span)) - .collect(); - + let mut overlaps = Vec::new(); + for pat in pats { + if let IntRange(range) = pat.ctor() { + if let Some(intersection) = self.suspicious_intersection(range) { + let intersection = DeconstructedPat::new( + IntRange(intersection), + Fields::empty(), + pcx.ty, + pat.span(), + ); + overlaps.push(intersection); + } + } + } if !overlaps.is_empty() { - pcx.cx.tcx.struct_span_lint_hir( - lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - hir_id, - pcx.span, - |lint| { - let mut err = lint.build("multiple patterns overlap on their endpoints"); - for (int_range, span) in overlaps { - err.span_label( - span, - &format!( - "this range overlaps on `{}`...", - int_range.to_pat(pcx.cx.tcx, pcx.ty) - ), - ); - } - err.span_label(pcx.span, "... with this range"); - err.note("you likely meant to write mutually exclusive ranges"); - err.emit(); - }, - ); + report.push((pcx.span, hir_id, overlaps)); } } @@ -315,8 +231,8 @@ impl IntRange { } } -/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and -/// would be displayed as such. To render properly, convert to a pattern first. +/// Note: this is missing some information. It will render chars as numbers among other things. To +/// render properly, convert to a pattern first. impl fmt::Debug for IntRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (lo, hi) = self.boundaries(); @@ -424,7 +340,7 @@ impl SplitIntRange { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum SliceKind { +pub(super) enum SliceKind { /// Patterns of length `n` (`[x, y]`). FixedLen(usize), /// Patterns using the `..` notation (`[x, .., y]`). @@ -454,15 +370,15 @@ impl SliceKind { /// A constructor for array and slice patterns. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(super) struct Slice { +pub(crate) struct Slice { /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`. - array_len: Option, + pub(super) array_len: Option, /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`. - kind: SliceKind, + pub(super) kind: SliceKind, } impl Slice { - fn new(array_len: Option, kind: SliceKind) -> Self { + pub(super) fn new(array_len: Option, kind: SliceKind) -> Self { let kind = match (array_len, kind) { // If the middle `..` is empty, we effectively have a fixed-length pattern. (Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len), @@ -621,20 +537,28 @@ impl SplitVarLenSlice { /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and /// `Fields`. #[derive(Clone, Debug, PartialEq)] -pub(super) enum Constructor<'tcx> { - /// The constructor for patterns that have a single constructor, like tuples, struct patterns - /// and fixed-length arrays. +pub(crate) enum Constructor { + /// Structs and unions. Single, /// Enum variants. - Variant(VariantIdx), + Variant(Cx::VariantIdx), + /// Tuple patterns. + Tuple(Cx::TyList), + /// Ref patterns (`&_` and `&mut _`). + Ref(Cx::Ty), + /// Box patterns. + BoxPat(Cx::Ty), + /// Booleans. + Bool(bool), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + F32Range(Cx::F32Float, Cx::F32Float, RangeEnd), + F64Range(Cx::F64Float, Cx::F64Float, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(&'tcx ty::Const<'tcx>), + Str(Cx::StringLit), /// Array and slice patterns. - Slice(Slice), + Slice(Slice, Cx::Ty), /// Constants that must not be matched structurally. They are treated as black /// boxes for the purposes of exhaustiveness: we must not inspect them, and they /// don't count towards making a match exhaustive. @@ -645,14 +569,16 @@ pub(super) enum Constructor<'tcx> { /// Stands for constructors that are not seen in the matrix, as explained in the documentation /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns` /// lint. - Missing { nonexhaustive_enum_missing_real_variants: bool }, + Missing { + nonexhaustive_enum_missing_real_variants: bool, + }, /// Wildcard pattern. Wildcard, /// Or-pattern. Or, } -impl<'tcx> Constructor<'tcx> { +impl Constructor { pub(super) fn is_wildcard(&self) -> bool { matches!(self, Wildcard) } @@ -670,45 +596,24 @@ impl<'tcx> Constructor<'tcx> { fn as_slice(&self) -> Option { match self { - Slice(slice) => Some(*slice), + Slice(slice, _) => Some(*slice), _ => None, } } - fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { - match *self { - Variant(idx) => idx, - Single => { - assert!(!adt.is_enum()); - VariantIdx::new(0) - } - _ => bug!("bad constructor {:?} for adt {:?}", self, adt), - } - } - /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. - pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> usize { + pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, Cx>) -> usize { match self { - Single | Variant(_) => match pcx.ty.kind() { - ty::Tuple(fs) => fs.len(), - ty::Ref(..) => 1, - ty::Adt(adt, ..) => { - if adt.is_box() { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - 1 - } else { - let variant = &adt.variants[self.variant_index_for_adt(adt)]; - Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() - } - } - _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty), - }, - Slice(slice) => slice.arity(), + Single | Variant(_) => pcx.ucx.cx.list_variant_nonhidden_fields(pcx.ty, self).len(), + Tuple(fs) => Cx::ty_list_len(fs), + Ref(_) | BoxPat(_) => 1, + Slice(slice, _) => slice.arity(), Str(..) - | FloatRange(..) + | Bool(..) | IntRange(..) + | F32Range(..) + | F64Range(..) | NonExhaustive | Opaque | Missing { .. } @@ -732,11 +637,11 @@ impl<'tcx> Constructor<'tcx> { /// matrix, unless all of them are. pub(super) fn split<'a>( &self, - pcx: PatCtxt<'_, '_, 'tcx>, - ctors: impl Iterator> + Clone, + pcx: PatCtxt<'_, '_, Cx>, + ctors: impl Iterator> + Clone, ) -> SmallVec<[Self; 1]> where - 'tcx: 'a, + Cx: 'a, { match self { Wildcard => { @@ -752,11 +657,11 @@ impl<'tcx> Constructor<'tcx> { split_range.split(int_ranges.cloned()); split_range.iter().map(IntRange).collect() } - &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }) => { + &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }, ty) => { let mut split_self = SplitVarLenSlice::new(self_prefix, self_suffix, array_len); let slices = ctors.filter_map(|c| c.as_slice()).map(|s| s.kind); split_self.split(slices); - split_self.iter().map(Slice).collect() + split_self.iter().map(|s| Slice(s, ty)).collect() } // Any other constructor can be used unchanged. _ => smallvec![self.clone()], @@ -768,7 +673,7 @@ impl<'tcx> Constructor<'tcx> { /// this checks for inclusion. // We inline because this has a single call site in `Matrix::specialize_constructor`. #[inline] - pub(super) fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool { + pub(super) fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, Cx>, other: &Self) -> bool { // This must be kept in sync with `is_covered_by_any`. match (self, other) { // Wildcards cover anything @@ -777,17 +682,15 @@ impl<'tcx> Constructor<'tcx> { (Missing { .. } | Wildcard, _) => false, (Single, Single) => true, + (Tuple(_), Tuple(_)) => true, + (Ref(_), Ref(_)) => true, + (BoxPat(_), BoxPat(_)) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, + (Bool(self_b), Bool(other_b)) => self_b == other_b, (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), - ( - FloatRange(self_from, self_to, self_end), - FloatRange(other_from, other_to, other_end), - ) => { - match ( - compare_const_vals(pcx.cx.tcx, self_to, other_to, pcx.cx.param_env, pcx.ty), - compare_const_vals(pcx.cx.tcx, self_from, other_from, pcx.cx.param_env, pcx.ty), - ) { + (F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => { + match (self_to.partial_cmp(other_to), self_from.partial_cmp(other_from)) { (Some(to), Some(from)) => { (from == Ordering::Greater || from == Ordering::Equal) && (to == Ordering::Less @@ -796,15 +699,18 @@ impl<'tcx> Constructor<'tcx> { _ => false, } } - (Str(self_val), Str(other_val)) => { - // FIXME: there's probably a more direct way of comparing for equality - match compare_const_vals(pcx.cx.tcx, self_val, other_val, pcx.cx.param_env, pcx.ty) - { - Some(comparison) => comparison == Ordering::Equal, - None => false, + (F64Range(self_from, self_to, self_end), F64Range(other_from, other_to, other_end)) => { + match (self_to.partial_cmp(other_to), self_from.partial_cmp(other_from)) { + (Some(to), Some(from)) => { + (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less + || (other_end == self_end && to == Ordering::Equal)) + } + _ => false, } } - (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), + (Str(self_val), Str(other_val)) => self_val == other_val, + (Slice(self_slice, _), Slice(other_slice, _)) => self_slice.is_covered_by(*other_slice), // We are trying to inspect an opaque constant. Thus we skip the row. (Opaque, _) | (_, Opaque) => false, @@ -825,8 +731,8 @@ impl<'tcx> Constructor<'tcx> { /// assumed to have been split from a wildcard. fn is_covered_by_any<'p>( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - used_ctors: &[Constructor<'tcx>], + pcx: PatCtxt<'_, 'p, Cx>, + used_ctors: &[Constructor], ) -> bool { if used_ctors.is_empty() { return false; @@ -834,20 +740,24 @@ impl<'tcx> Constructor<'tcx> { // This must be kept in sync with `is_covered_by`. match self { - // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s. - Single => !used_ctors.is_empty(), + // If `self` is one of these the type has exactly one constructor so the check is + // simpler. + Single | Tuple(_) | Ref(_) | BoxPat(_) => !used_ctors.is_empty(), Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)), + Bool(self_b) => { + used_ctors.iter().any(|c| matches!(c, Bool(other_b) if self_b == other_b)) + } IntRange(range) => used_ctors .iter() .filter_map(|c| c.as_int_range()) .any(|other| range.is_covered_by(other)), - Slice(slice) => used_ctors + Slice(slice, _) => used_ctors .iter() .filter_map(|c| c.as_slice()) .any(|other| slice.is_covered_by(other)), // This constructor is never covered by anything else NonExhaustive => false, - Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => { + Str(..) | F32Range(..) | F64Range(..) | Opaque | Missing { .. } | Wildcard | Or => { span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self) } } @@ -870,136 +780,16 @@ impl<'tcx> Constructor<'tcx> { /// or not. In fact this is quite natural from the point of view of diagnostics too. This is done /// in `to_ctors`: in some cases we only return `Missing`. #[derive(Debug)] -pub(super) struct SplitWildcard<'tcx> { +pub(super) struct SplitWildcard { /// Constructors seen in the matrix. - matrix_ctors: Vec>, + matrix_ctors: Vec>, /// All the constructors for this type - all_ctors: SmallVec<[Constructor<'tcx>; 1]>, + all_ctors: SmallVec<[Constructor; 1]>, } -impl<'tcx> SplitWildcard<'tcx> { - pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { - debug!("SplitWildcard::new({:?})", pcx.ty); - let cx = pcx.cx; - let make_range = |start, end| { - IntRange( - // `unwrap()` is ok because we know the type is an integer. - IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(), - ) - }; - // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, - // arrays and slices we use ranges and variable-length slices when appropriate. - // - // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that - // are statically impossible. E.g., for `Option`, we do not include `Some(_)` in the - // returned list of constructors. - // Invariant: this is empty if and only if the type is uninhabited (as determined by - // `cx.is_uninhabited()`). - let all_ctors = match pcx.ty.kind() { - ty::Bool => smallvec![make_range(0, 1)], - ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { - let len = len.eval_usize(cx.tcx, cx.param_env) as usize; - if len != 0 && cx.is_uninhabited(sub_ty) { - smallvec![] - } else { - smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))] - } - } - // Treat arrays of a constant but unknown length like slices. - ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { - let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(Slice::new(None, kind))] - } - ty::Adt(def, substs) if def.is_enum() => { - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additional "unknown" constructor. - // There is no point in enumerating all possible variants, because the user can't - // actually match against them all themselves. So we always return only the fictitious - // constructor. - // E.g., in an example like: - // - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // - // we don't want to show every possible IO error, but instead have only `_` as the - // witness. - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); - - // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it - // as though it had an "unknown" constructor to avoid exposing its emptiness. The - // exception is if the pattern is at the top level, because we want empty matches to be - // considered exhaustive. - let is_secretly_empty = def.variants.is_empty() - && !cx.tcx.features().exhaustive_patterns - && !pcx.is_top_level; - - if is_secretly_empty { - smallvec![NonExhaustive] - } else if is_declared_nonexhaustive { - def.variants - .indices() - .map(|idx| Variant(idx)) - .chain(Some(NonExhaustive)) - .collect() - } else if cx.tcx.features().exhaustive_patterns { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - def.variants - .iter_enumerated() - .filter(|(_, v)| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module) - }) - .map(|(idx, _)| Variant(idx)) - .collect() - } else { - def.variants.indices().map(|idx| Variant(idx)).collect() - } - } - ty::Char => { - smallvec![ - // The valid Unicode Scalar Value ranges. - make_range('\u{0000}' as u128, '\u{D7FF}' as u128), - make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), - ] - } - ty::Int(_) | ty::Uint(_) - if pcx.ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching => - { - // `usize`/`isize` are not allowed to be matched exhaustively unless the - // `precise_pointer_size_matching` feature is enabled. So we treat those types like - // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. - smallvec![NonExhaustive] - } - &ty::Int(ity) => { - let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128; - let min = 1u128 << (bits - 1); - let max = min - 1; - smallvec![make_range(min, max)] - } - &ty::Uint(uty) => { - let size = Integer::from_uint_ty(&cx.tcx, uty).size(); - let max = size.truncate(u128::MAX); - smallvec![make_range(0, max)] - } - // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot - // expose its emptiness. The exception is if the pattern is at the top level, because we - // want empty matches to be considered exhaustive. - ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => { - smallvec![NonExhaustive] - } - ty::Never => smallvec![], - _ if cx.is_uninhabited(pcx.ty) => smallvec![], - ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single], - // This type is one for which we cannot list constructors, like `str` or `f64`. - _ => smallvec![NonExhaustive], - }; - +impl SplitWildcard { + pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, Cx>) -> Self { + let all_ctors = pcx.ucx.cx.list_constructors_for_type(pcx.ty, pcx.is_top_level); SplitWildcard { matrix_ctors: Vec::new(), all_ctors } } @@ -1007,10 +797,10 @@ impl<'tcx> SplitWildcard<'tcx> { /// do what you want. pub(super) fn split<'a>( &mut self, - pcx: PatCtxt<'_, '_, 'tcx>, - ctors: impl Iterator> + Clone, + pcx: PatCtxt<'_, '_, Cx>, + ctors: impl Iterator> + Clone, ) where - 'tcx: 'a, + Cx: 'a, { // Since `all_ctors` never contains wildcards, this won't recurse further. self.all_ctors = @@ -1019,21 +809,21 @@ impl<'tcx> SplitWildcard<'tcx> { } /// Whether there are any value constructors for this type that are not present in the matrix. - fn any_missing(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> bool { + fn any_missing(&self, pcx: PatCtxt<'_, '_, Cx>) -> bool { self.iter_missing(pcx).next().is_some() } /// Iterate over the constructors for this type that are not present in the matrix. pub(super) fn iter_missing<'a, 'p>( &'a self, - pcx: PatCtxt<'a, 'p, 'tcx>, - ) -> impl Iterator> + Captures<'p> { + pcx: PatCtxt<'a, 'p, Cx>, + ) -> impl Iterator> + Captures<'p> { self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.matrix_ctors)) } /// Return the set of constructors resulting from splitting the wildcard. As explained at the /// top of the file, if any constructors are missing we can ignore the present ones. - fn into_ctors(self, pcx: PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { + fn into_ctors(self, pcx: PatCtxt<'_, '_, Cx>) -> SmallVec<[Constructor; 1]> { if self.any_missing(pcx) { // Some constructors are missing, thus we can specialize with the special `Missing` // constructor, which stands for those constructors that are not seen in the matrix, @@ -1062,7 +852,7 @@ impl<'tcx> SplitWildcard<'tcx> { // // The exception is: if we are at the top-level, for example in an empty match, we // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); + let report_when_all_missing = pcx.is_top_level && !Cx::is_numeric(pcx.ty); let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { if pcx.is_non_exhaustive { Missing { @@ -1108,100 +898,58 @@ impl<'tcx> SplitWildcard<'tcx> { /// included in `fields`. For that reason, when you have a `mir::Field` you must use /// `index_with_declared_idx`. #[derive(Debug, Clone, Copy)] -pub(super) struct Fields<'p, 'tcx> { - fields: &'p [DeconstructedPat<'p, 'tcx>], +pub(super) struct Fields<'p, Cx: Context> { + fields: &'p [DeconstructedPat<'p, Cx>], } -impl<'p, 'tcx> Fields<'p, 'tcx> { - fn empty() -> Self { +impl<'p, Cx: Context> Fields<'p, Cx> { + pub(super) fn empty() -> Self { Fields { fields: &[] } } - fn singleton(cx: &MatchCheckCtxt<'p, 'tcx>, field: DeconstructedPat<'p, 'tcx>) -> Self { - let field: &_ = cx.pattern_arena.alloc(field); + pub(super) fn singleton(ucx: &UsefulnessCtxt<'p, Cx>, field: DeconstructedPat<'p, Cx>) -> Self { + let field: &_ = ucx.pattern_arena.alloc(field); Fields { fields: std::slice::from_ref(field) } } pub(super) fn from_iter( - cx: &MatchCheckCtxt<'p, 'tcx>, - fields: impl IntoIterator>, + ucx: &UsefulnessCtxt<'p, Cx>, + fields: impl IntoIterator>, ) -> Self { - let fields: &[_] = cx.pattern_arena.alloc_from_iter(fields); + let fields: &[_] = ucx.pattern_arena.alloc_from_iter(fields); Fields { fields } } fn wildcards_from_tys( - cx: &MatchCheckCtxt<'p, 'tcx>, - tys: impl IntoIterator>, + ucx: &UsefulnessCtxt<'p, Cx>, + tys: impl IntoIterator, ) -> Self { - Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard)) - } - - // 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. - fn list_variant_nonhidden_fields<'a>( - cx: &'a MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - variant: &'a VariantDef, - ) -> impl Iterator)> + Captures<'a> + Captures<'p> { - let (adt, substs) = match ty.kind() { - ty::Adt(adt, substs) => (adt, substs), - _ => bug!(), - }; - // Whether we must not match the fields of this variant exhaustively. - let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local(); - - variant.fields.iter().enumerate().filter_map(move |(i, field)| { - let ty = field.ty(cx.tcx, substs); - // `field.ty()` doesn't normalize after substituting. - let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = cx.is_uninhabited(ty); - - if is_uninhabited && (!is_visible || is_non_exhaustive) { - None - } else { - Some((Field::new(i), ty)) - } - }) + Fields::from_iter(ucx, tys.into_iter().map(DeconstructedPat::wildcard)) } /// Creates a new list of wildcard fields for a given constructor. The result must have a /// length of `constructor.arity()`. pub(super) fn wildcards( - cx: &MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - constructor: &Constructor<'tcx>, + ucx: &UsefulnessCtxt<'p, Cx>, + ty: Cx::Ty, + constructor: &Constructor, ) -> Self { let ret = match constructor { - Single | Variant(_) => match ty.kind() { - ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), - ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), - ty::Adt(adt, substs) => { - if adt.is_box() { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - Fields::wildcards_from_tys(cx, once(substs.type_at(0))) - } else { - let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; - let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) - .map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) - } - } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), - }, - Slice(slice) => match *ty.kind() { - ty::Slice(ty) | ty::Array(ty, _) => { - let arity = slice.arity(); - Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) - } - _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), - }, + Single | Variant(_) => { + let tys = ucx.cx.list_variant_nonhidden_fields(ty, constructor); + Fields::wildcards_from_tys(ucx, tys) + } + Tuple(fs) => Fields::wildcards_from_tys(ucx, Cx::ty_list_iter(fs)), + Ref(ty) | BoxPat(ty) => Fields::wildcards_from_tys(ucx, once(*ty)), + Slice(slice, ty) => { + let arity = slice.arity(); + Fields::wildcards_from_tys(ucx, (0..arity).map(|_| *ty)) + } Str(..) - | FloatRange(..) + | Bool(..) | IntRange(..) + | F32Range(..) + | F64Range(..) | NonExhaustive | Opaque | Missing { .. } @@ -1217,7 +965,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// Returns the list of patterns. pub(super) fn iter_patterns<'a>( &'a self, - ) -> impl Iterator> + Captures<'a> { + ) -> impl Iterator> + Captures<'a> { self.fields.iter() } } @@ -1227,24 +975,24 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// This also keeps track of whether the pattern has been foundreachable during analysis. For this /// reason we should be careful not to clone patterns for which we care about that. Use /// `clone_and_forget_reachability` is you're sure. -pub(crate) struct DeconstructedPat<'p, 'tcx> { - ctor: Constructor<'tcx>, - fields: Fields<'p, 'tcx>, - ty: Ty<'tcx>, - span: Span, +pub(crate) struct DeconstructedPat<'p, Cx: Context> { + ctor: Constructor, + fields: Fields<'p, Cx>, + ty: Cx::Ty, + span: Cx::Span, reachable: Cell, } -impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { - pub(super) fn wildcard(ty: Ty<'tcx>) -> Self { - Self::new(Wildcard, Fields::empty(), ty, DUMMY_SP) +impl<'p, Cx: Context> DeconstructedPat<'p, Cx> { + pub(super) fn wildcard(ty: Cx::Ty) -> Self { + Self::new(Wildcard, Fields::empty(), ty, Cx::Span::default()) } pub(super) fn new( - ctor: Constructor<'tcx>, - fields: Fields<'p, 'tcx>, - ty: Ty<'tcx>, - span: Span, + ctor: Constructor, + fields: Fields<'p, Cx>, + ty: Cx::Ty, + span: Cx::Span, ) -> Self { DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) } } @@ -1252,9 +1000,9 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. - pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); - DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP) + pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, Cx>, ctor: Constructor) -> Self { + let fields = Fields::wildcards(pcx.ucx, pcx.ty, &ctor); + DeconstructedPat::new(ctor, fields, pcx.ty, Cx::Span::default()) } /// Clone this value. This method emphasizes that cloning loses reachability information and @@ -1263,259 +1011,23 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span) } - pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self { - let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); - let ctor; - let fields; - match pat.kind.as_ref() { - PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern), - PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), - PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { - ctor = Wildcard; - fields = Fields::empty(); - } - PatKind::Deref { subpattern } => { - ctor = Single; - fields = Fields::singleton(cx, mkpat(subpattern)); - } - PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { - match pat.ty.kind() { - ty::Tuple(fs) => { - ctor = Single; - let mut wilds: SmallVec<[_; 2]> = fs - .iter() - .map(|ty| ty.expect_ty()) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - wilds[pat.field.index()] = mkpat(&pat.pattern); - } - fields = Fields::from_iter(cx, wilds); - } - ty::Adt(adt, substs) if adt.is_box() => { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, - // _)` or a box pattern. As a hack to avoid an ICE with the former, we - // ignore other fields than the first one. This will trigger an error later - // anyway. - // See https://github.com/rust-lang/rust/issues/82772 , - // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 - // The problem is that we can't know from the type whether we'll match - // normally or through box-patterns. We'll have to figure out a proper - // solution when we introduce generalized deref patterns. Also need to - // prevent mixing of those two options. - let pat = subpatterns.into_iter().find(|pat| pat.field.index() == 0); - let pat = if let Some(pat) = pat { - mkpat(&pat.pattern) - } else { - DeconstructedPat::wildcard(substs.type_at(0)) - }; - ctor = Single; - fields = Fields::singleton(cx, pat); - } - ty::Adt(adt, _) => { - ctor = match pat.kind.as_ref() { - PatKind::Leaf { .. } => Single, - PatKind::Variant { variant_index, .. } => Variant(*variant_index), - _ => bug!(), - }; - let variant = &adt.variants[ctor.variant_index_for_adt(adt)]; - // For each field in the variant, we store the relevant index into `self.fields` if any. - let mut field_id_to_id: Vec> = - (0..variant.fields.len()).map(|_| None).collect(); - let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant) - .enumerate() - .map(|(i, (field, ty))| { - field_id_to_id[field.index()] = Some(i); - ty - }); - let mut wilds: SmallVec<[_; 2]> = - tys.map(DeconstructedPat::wildcard).collect(); - for pat in subpatterns { - if let Some(i) = field_id_to_id[pat.field.index()] { - wilds[i] = mkpat(&pat.pattern); - } - } - fields = Fields::from_iter(cx, wilds); - } - _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), - } - } - PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) { - ctor = IntRange(int_range); - fields = Fields::empty(); - } else { - match pat.ty.kind() { - ty::Float(_) => { - ctor = FloatRange(value, value, RangeEnd::Included); - fields = Fields::empty(); - } - ty::Ref(_, t, _) if t.is_str() => { - // We want a `&str` constant to behave like a `Deref` pattern, to be compatible - // with other `Deref` patterns. This could have been done in `const_to_pat`, - // but that causes issues with the rest of the matching code. - // So here, the constructor for a `"foo"` pattern is `&` (represented by - // `Single`), and has one field. That field has constructor `Str(value)` and no - // fields. - // Note: `t` is `str`, not `&str`. - let subpattern = - DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span); - ctor = Single; - fields = Fields::singleton(cx, subpattern) - } - // All constants that can be structurally matched have already been expanded - // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are - // opaque. - _ => { - ctor = Opaque; - fields = Fields::empty(); - } - } - } - } - &PatKind::Range(PatRange { lo, hi, end }) => { - let ty = lo.ty; - ctor = if let Some(int_range) = IntRange::from_range( - cx.tcx, - lo.eval_bits(cx.tcx, cx.param_env, lo.ty), - hi.eval_bits(cx.tcx, cx.param_env, hi.ty), - ty, - &end, - ) { - IntRange(int_range) - } else { - FloatRange(lo, hi, end) - }; - fields = Fields::empty(); - } - PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { - let array_len = match pat.ty.kind() { - ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize), - ty::Slice(_) => None, - _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), - }; - let kind = if slice.is_some() { - VarLen(prefix.len(), suffix.len()) - } else { - FixedLen(prefix.len() + suffix.len()) - }; - ctor = Slice(Slice::new(array_len, kind)); - fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); - } - PatKind::Or { .. } => { - ctor = Or; - let pats = expand_or_pat(pat); - fields = Fields::from_iter(cx, pats.into_iter().map(mkpat)); - } - } - DeconstructedPat::new(ctor, fields, pat.ty, pat.span) - } - - 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) - }; - let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); - let pat = 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 }) - .collect(), - }, - ty::Adt(adt_def, _) if adt_def.is_box() => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - PatKind::Deref { subpattern: subpatterns.next().unwrap() } - } - ty::Adt(adt_def, substs) => { - let variant_index = self.ctor.variant_index_for_adt(adt_def); - let variant = &adt_def.variants[variant_index]; - let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant) - .zip(subpatterns) - .map(|((field, _ty), pattern)| FieldPat { field, pattern }) - .collect(); - - if adt_def.is_enum() { - PatKind::Variant { adt_def, substs, variant_index, subpatterns } - } else { - PatKind::Leaf { subpatterns } - } - } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to reconstruct the correct constant pattern here. However a string - // literal pattern will never be reported as a non-exhaustiveness witness, so we - // ignore this issue. - ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty), - }, - Slice(slice) => { - match slice.kind { - FixedLen(_) => PatKind::Slice { - prefix: subpatterns.collect(), - slice: None, - suffix: vec![], - }, - VarLen(prefix, _) => { - let mut subpatterns = subpatterns.peekable(); - let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); - if slice.array_len.is_some() { - // Improves diagnostics a bit: if the type is a known-size array, instead - // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. - // This is incorrect if the size is not known, since `[_, ..]` captures - // arrays of lengths `>= 1` whereas `[..]` captures any length. - while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { - prefix.pop(); - } - while subpatterns.peek().is_some() - && is_wildcard(subpatterns.peek().unwrap()) - { - subpatterns.next(); - } - } - let suffix: Vec<_> = subpatterns.collect(); - let wild = Pat::wildcard_from_ty(self.ty); - PatKind::Slice { prefix, slice: Some(wild), suffix } - } - } - } - &Str(value) => PatKind::Constant { value }, - &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), - IntRange(range) => return range.to_pat(cx.tcx, self.ty), - Wildcard | NonExhaustive => PatKind::Wild, - Missing { .. } => bug!( - "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, - `Missing` should have been processed in `apply_constructors`" - ), - Opaque | Or => { - bug!("can't convert to pattern: {:?}", self) - } - }; - - Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) } - } - pub(super) fn is_or_pat(&self) -> bool { matches!(self.ctor, Or) } - pub(super) fn ctor(&self) -> &Constructor<'tcx> { + pub(super) fn ctor(&self) -> &Constructor { &self.ctor } - pub(super) fn ty(&self) -> Ty<'tcx> { + pub(super) fn ty(&self) -> Cx::Ty { self.ty } - pub(super) fn span(&self) -> Span { + pub(super) fn span(&self) -> Cx::Span { self.span } pub(super) fn iter_fields<'a>( &'a self, - ) -> impl Iterator> + Captures<'a> { + ) -> impl Iterator> + Captures<'a> { self.fields.iter_patterns() } @@ -1523,15 +1035,15 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(super) fn specialize<'a>( &'a self, - cx: &MatchCheckCtxt<'p, 'tcx>, - other_ctor: &Constructor<'tcx>, - ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { + ucx: &UsefulnessCtxt<'p, Cx>, + other_ctor: &Constructor, + ) -> SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]> { match (&self.ctor, other_ctor) { (Wildcard, _) => { // We return a wildcard for each field of `other_ctor`. - Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() + Fields::wildcards(ucx, self.ty, other_ctor).iter_patterns().collect() } - (Slice(self_slice), Slice(other_slice)) + (Slice(self_slice, inner_ty), Slice(other_slice, _)) if self_slice.arity() != other_slice.arity() => { // The only tricky case: two slices of different arity. Since `self_slice` covers @@ -1542,14 +1054,10 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { match self_slice.kind { FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice), VarLen(prefix, suffix) => { - let inner_ty = match *self.ty.kind() { - ty::Slice(ty) | ty::Array(ty, _) => ty, - _ => bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty), - }; let prefix = &self.fields.fields[..prefix]; let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let wildcard: &_ = - cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); + ucx.pattern_arena.alloc(DeconstructedPat::wildcard(*inner_ty)); let extra_wildcards = other_slice.arity() - self_slice.arity(); let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); prefix.iter().chain(extra_wildcards).chain(suffix).collect() @@ -1570,13 +1078,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } /// Report the spans of subpatterns that were not reachable, if any. - pub(super) fn unreachable_spans(&self) -> Vec { + pub(super) fn unreachable_spans(&self) -> Vec { let mut spans = Vec::new(); self.collect_unreachable_spans(&mut spans); spans } - fn collect_unreachable_spans(&self, spans: &mut Vec) { + fn collect_unreachable_spans(&self, spans: &mut Vec) { // We don't look at subpatterns if we already reported the whole pattern as unreachable. if !self.is_reachable() { spans.push(self.span); @@ -1590,7 +1098,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a /// `Display` impl. -impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { +impl<'p, Cx: Context> fmt::Debug for DeconstructedPat<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Printing lists is a chore. let mut first = true; @@ -1605,47 +1113,30 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { let mut start_or_comma = || start_or_continue(", "); match &self.ctor { - Single | Variant(_) => match self.ty.kind() { - ty::Adt(def, _) if def.is_box() => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - let subpattern = self.iter_fields().next().unwrap(); - write!(f, "box {:?}", subpattern) + Single | Variant(_) | Tuple(_) => { + if let Some(ident) = Cx::variant_ident(self.ty(), self.ctor()) { + write!(f, "{}", ident)?; } - ty::Adt(..) | ty::Tuple(..) => { - let variant = match self.ty.kind() { - ty::Adt(adt, _) => { - Some(&adt.variants[self.ctor.variant_index_for_adt(adt)]) - } - ty::Tuple(_) => None, - _ => unreachable!(), - }; - - if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; - } - // Without `cx`, we can't know which field corresponds to which, so we can't - // get the names of the fields. Instead we just display everything as a suple - // struct, which should be good enough. - write!(f, "(")?; - for p in self.iter_fields() { - write!(f, "{}", start_or_comma())?; - write!(f, "{:?}", p)?; - } - write!(f, ")") - } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to detect strings here. However a string literal pattern will never - // be reported as a non-exhaustiveness witness, so we can ignore this issue. - ty::Ref(_, _, mutbl) => { - let subpattern = self.iter_fields().next().unwrap(); - write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern) + // Without `cx`, we can't know which field corresponds to which, so we can't + // get the names of the fields. Instead we just display everything as a suple + // struct, which should be good enough. + write!(f, "(")?; + for p in self.iter_fields() { + write!(f, "{}", start_or_comma())?; + write!(f, "{:?}", p)?; } - _ => write!(f, "_"), - }, - Slice(slice) => { + write!(f, ")") + } + Ref(_) => { + let subpattern = self.iter_fields().next().unwrap(); + write!(f, "&{:?}", subpattern) + } + BoxPat(_) => { + let subpattern = self.iter_fields().next().unwrap(); + write!(f, "box {:?}", subpattern) + } + Slice(slice, _) => { let mut subpatterns = self.fields.iter_patterns(); write!(f, "[")?; match slice.kind { @@ -1667,12 +1158,18 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } write!(f, "]") } - &FloatRange(lo, hi, end) => { + Bool(b) => write!(f, "{}", b), + IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render chars as ranges etc. + F32Range(lo, hi, end) => { + write!(f, "{}", lo)?; + write!(f, "{}", end)?; + write!(f, "{}", hi) + } + F64Range(lo, hi, end) => { write!(f, "{}", lo)?; write!(f, "{}", end)?; write!(f, "{}", hi) } - IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0` Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty), Or => { for pat in self.iter_fields() { @@ -1680,7 +1177,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } Ok(()) } - Str(value) => write!(f, "{}", value), + Str(_) => write!(f, ""), Opaque => write!(f, ""), } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index cb74ae4df2ef8..c25fcaac3e859 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -6,25 +6,34 @@ mod deconstruct_pat; mod usefulness; pub(crate) use self::check_match::check_match; - +use self::deconstruct_pat::{Constructor, DeconstructedPat, Fields, IntRange, Slice, SliceKind}; +use self::usefulness::UsefulnessCtxt; use crate::thir::util::UserAnnotatedTyHelpers; +use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; +use rustc_data_structures::captures::Captures; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::RangeEnd; +use rustc_hir::{HirId, RangeEnd}; use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue}; -use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; -use rustc_middle::mir::UserTypeProjection; +use rustc_middle::middle::stability::EvalResult; +use rustc_middle::mir::interpret::{ + get_slice_bytes, ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar, +}; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; -use rustc_middle::ty::subst::{GenericArg, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, ConstKind, DefIdTree, Region, Ty, TyCtxt, UserType}; -use rustc_span::{Span, Symbol}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_target::abi::{Size, VariantIdx}; +use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; +use std::fmt; #[derive(Clone, Debug)] crate enum PatternError { @@ -246,7 +255,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Tuple(ref pats, ddpos) => { let tys = match ty.kind() { - ty::Tuple(ref tys) => tys, + ty::Tuple(tys) => tys, _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty), }; let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos); @@ -584,143 +593,6 @@ impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { } } -crate trait PatternFoldable<'tcx>: Sized { - fn fold_with>(&self, folder: &mut F) -> Self { - self.super_fold_with(folder) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self; -} - -crate trait PatternFolder<'tcx>: Sized { - fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { - pattern.super_fold_with(self) - } - - fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> { - kind.super_fold_with(self) - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - Box::new(content) - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.as_ref().map(|t| t.fold_with(folder)) - } -} - -macro_rules! CloneImpls { - (<$lt_tcx:tt> $($ty:ty),+) => { - $( - impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { - fn super_fold_with>(&self, _: &mut F) -> Self { - Clone::clone(self) - } - } - )+ - } -} - -CloneImpls! { <'tcx> - Span, Field, Mutability, Symbol, hir::HirId, usize, ty::Const<'tcx>, - Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef, - SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, - UserTypeProjection, PatTyProj<'tcx> -} - -impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) } - } -} - -impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - Pat { - ty: self.ty.fold_with(folder), - span: self.span.fold_with(folder), - kind: self.kind.fold_with(folder), - } - } -} - -impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern_kind(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - PatKind::Wild => PatKind::Wild, - PatKind::AscribeUserType { - ref subpattern, - ascription: Ascription { variance, ref user_ty, user_ty_span }, - } => PatKind::AscribeUserType { - subpattern: subpattern.fold_with(folder), - ascription: Ascription { - user_ty: user_ty.fold_with(folder), - variance, - user_ty_span, - }, - }, - PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => { - PatKind::Binding { - mutability: mutability.fold_with(folder), - name: name.fold_with(folder), - mode: mode.fold_with(folder), - var: var.fold_with(folder), - ty: ty.fold_with(folder), - subpattern: subpattern.fold_with(folder), - is_primary, - } - } - PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { - PatKind::Variant { - adt_def: adt_def.fold_with(folder), - substs: substs.fold_with(folder), - variant_index, - subpatterns: subpatterns.fold_with(folder), - } - } - PatKind::Leaf { ref subpatterns } => { - PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) } - } - PatKind::Deref { ref subpattern } => { - PatKind::Deref { subpattern: subpattern.fold_with(folder) } - } - PatKind::Constant { value } => PatKind::Constant { value }, - PatKind::Range(range) => PatKind::Range(range), - PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder), - }, - PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder), - }, - PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) }, - } - } -} - crate fn compare_const_vals<'tcx>( tcx: TyCtxt<'tcx>, a: &'tcx ty::Const<'tcx>, @@ -785,3 +657,641 @@ crate fn compare_const_vals<'tcx>( } fallback() } + +/// Return the size of the corresponding number type. +#[inline] +fn number_type_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Size { + use rustc_middle::ty::layout::IntegerExt; + use rustc_target::abi::{Integer, Primitive}; + match ty.kind() { + ty::Bool => Size::from_bytes(1), + ty::Char => Size::from_bytes(4), + ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), + ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), + ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), + ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), + _ => bug!("unexpected type: {}", ty), + } +} + +/// Evaluate an int constant, with a faster branch for a common case. +#[inline] +fn fast_try_eval_bits<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty_size: Size, + value: &ty::Const<'tcx>, +) -> Option { + let int = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) = value.val { + // If the constant is already evaluated, we shortcut here. + int + } else { + // This is a more general but slower form of the previous case. + value.val.eval(tcx, param_env).try_to_scalar_int()? + }; + int.to_bits(ty_size).ok() +} + +fn pat_to_deconstructed<'p, 'tcx>( + ucx: &UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>>, + pat: &Pat<'tcx>, +) -> DeconstructedPat<'p, MatchCheckCtxt<'tcx>> { + use deconstruct_pat::RangeEnd; + let cx = &ucx.cx; + let mkpat = |pat| pat_to_deconstructed(ucx, pat); + let ctor; + let fields; + match pat.kind.as_ref() { + PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern), + PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { + ctor = Constructor::Wildcard; + fields = Fields::empty(); + } + PatKind::Deref { subpattern } => { + ctor = match pat.ty.kind() { + ty::Adt(adt, _) if adt.is_box() => Constructor::BoxPat(subpattern.ty), + _ => Constructor::Ref(subpattern.ty), + }; + fields = Fields::singleton(ucx, mkpat(subpattern)); + } + PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + match pat.ty.kind() { + ty::Tuple(fs) => { + ctor = Constructor::Tuple(fs.as_ref()); + let mut wilds: SmallVec<[_; 2]> = fs + .iter() + .map(|ty| ty.expect_ty()) + .map(DeconstructedPat::wildcard) + .collect(); + for pat in subpatterns { + wilds[pat.field.index()] = mkpat(&pat.pattern); + } + fields = Fields::from_iter(ucx, wilds); + } + ty::Adt(adt, substs) if adt.is_box() => { + // If we're here, the pattern is using the private `Box(_, _)` constructor. + // Since this is private, we can ignore the subpatterns here and pretend it's a + // `box _`. There'll be an error later anyways. This prevents an ICE. + // See https://github.com/rust-lang/rust/issues/82772 , + // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 + let inner_ty = substs.type_at(0); + ctor = Constructor::BoxPat(inner_ty); + fields = Fields::singleton(ucx, DeconstructedPat::wildcard(inner_ty)); + } + ty::Adt(adt, _) => { + let variant = match *pat.kind { + PatKind::Leaf { .. } => { + ctor = Constructor::Single; + adt.non_enum_variant() + } + PatKind::Variant { variant_index, .. } => { + ctor = Constructor::Variant(variant_index); + &adt.variants[variant_index] + } + _ => bug!(), + }; + // For each field in the variant, we store the relevant index into `self.fields` if any. + let mut field_id_to_id: Vec> = + (0..variant.fields.len()).map(|_| None).collect(); + let tys = cx.list_variant_nonhidden_fields(pat.ty, &ctor).enumerate().map( + |(i, (field, ty))| { + field_id_to_id[field.index()] = Some(i); + ty + }, + ); + let mut wilds: SmallVec<[_; 2]> = tys.map(DeconstructedPat::wildcard).collect(); + for pat in subpatterns { + if let Some(i) = field_id_to_id[pat.field.index()] { + wilds[i] = mkpat(&pat.pattern); + } + } + fields = Fields::from_iter(ucx, wilds); + } + _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), + } + } + PatKind::Constant { value } => { + match pat.ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + use rustc_apfloat::Float; + let size = number_type_size(cx.tcx, value.ty); + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, size, value) { + Some(bits) => match pat.ty.kind() { + ty::Bool => Constructor::Bool(bits != 0), + ty::Char | ty::Int(_) | ty::Uint(_) => { + Constructor::IntRange(IntRange::from_bits( + size.bits(), + MatchCheckCtxt::is_signed_int(pat.ty), + bits, + bits, + &RangeEnd::Included, + )) + } + ty::Float(ty::FloatTy::F32) => { + let value = rustc_apfloat::ieee::Single::from_bits(bits); + Constructor::F32Range(value, value, RangeEnd::Included) + } + ty::Float(ty::FloatTy::F64) => { + let value = rustc_apfloat::ieee::Double::from_bits(bits); + Constructor::F64Range(value, value, RangeEnd::Included) + } + _ => unreachable!(), + }, + None => Constructor::Opaque, + }; + fields = Fields::empty(); + } + &ty::Ref(_, t, _) if t.is_str() => { + if let ty::ConstKind::Value(val @ ConstValue::Slice { .. }) = value.val { + let bytes = get_slice_bytes(&cx.tcx, val); + // We want a `&str` constant to behave like a `Deref` pattern, to be compatible + // with other `Deref` patterns. This could have been done in `const_to_pat`, + // but that causes issues with the rest of the matching code. + // So here, the constructor for a `"foo"` pattern is `&` (represented by + // `Single`), and has one field. That field has constructor `Str(value)` and no + // fields. + // Note: `t` is `str`, not `&str`. + let subpattern = DeconstructedPat::new( + Constructor::Str(bytes), + Fields::empty(), + t, + pat.span, + ); + ctor = Constructor::Ref(t); + fields = Fields::singleton(ucx, subpattern) + } else { + // FIXME(Nadrieril): Does this ever happen? + ctor = Constructor::Opaque; + fields = Fields::empty(); + } + } + // All constants that can be structurally matched have already been expanded + // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are + // opaque. + _ => { + ctor = Constructor::Opaque; + fields = Fields::empty(); + } + } + } + &PatKind::Range(PatRange { lo, hi, end }) => { + use rustc_apfloat::Float; + let ty = lo.ty; + let end = match end { + rustc_hir::RangeEnd::Included => RangeEnd::Included, + rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded, + }; + let size = number_type_size(cx.tcx, ty); + let lo = fast_try_eval_bits(cx.tcx, cx.param_env, size, lo) + .unwrap_or_else(|| bug!("expected bits of {:?}, got {:?}", ty, lo)); + let hi = fast_try_eval_bits(cx.tcx, cx.param_env, size, hi) + .unwrap_or_else(|| bug!("expected bits of {:?}, got {:?}", ty, hi)); + ctor = match ty.kind() { + ty::Char | ty::Int(_) | ty::Uint(_) => Constructor::IntRange(IntRange::from_bits( + size.bits(), + MatchCheckCtxt::is_signed_int(ty), + lo, + hi, + &end, + )), + ty::Float(ty::FloatTy::F32) => { + let lo = rustc_apfloat::ieee::Single::from_bits(lo); + let hi = rustc_apfloat::ieee::Single::from_bits(hi); + Constructor::F32Range(lo, hi, RangeEnd::Included) + } + ty::Float(ty::FloatTy::F64) => { + let lo = rustc_apfloat::ieee::Double::from_bits(lo); + let hi = rustc_apfloat::ieee::Double::from_bits(hi); + Constructor::F64Range(lo, hi, RangeEnd::Included) + } + _ => bug!("invalid type for range pattern: {}", ty), + }; + fields = Fields::empty(); + } + PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { + let (ty, array_len) = match pat.ty.kind() { + ty::Array(ty, length) => { + (ty, Some(length.eval_usize(cx.tcx, cx.param_env) as usize)) + } + ty::Slice(ty) => (ty, None), + _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), + }; + let kind = if slice.is_some() { + SliceKind::VarLen(prefix.len(), suffix.len()) + } else { + SliceKind::FixedLen(prefix.len() + suffix.len()) + }; + ctor = Constructor::Slice(Slice::new(array_len, kind), *ty); + fields = Fields::from_iter(ucx, prefix.iter().chain(suffix).map(mkpat)); + } + PatKind::Or { .. } => { + /// Recursively expand this pattern into its subpatterns. + 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); + } + } else { + vec.push(pat) + } + } + + let mut pats = Vec::new(); + expand(pat, &mut pats); + ctor = Constructor::Or; + fields = Fields::from_iter(ucx, pats.into_iter().map(mkpat)); + } + } + DeconstructedPat::new(ctor, fields, pat.ty, pat.span) +} + +fn deconstructed_to_pat<'p, 'tcx>( + cx: &MatchCheckCtxt<'tcx>, + pat: &DeconstructedPat<'p, MatchCheckCtxt<'tcx>>, +) -> Pat<'tcx> { + use Constructor::*; + let is_wildcard = |pat: &Pat<'_>| { + matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) + }; + let mut subpatterns = pat.iter_fields().map(|pat| deconstructed_to_pat(cx, pat)); + let kind = match pat.ctor() { + Single | Variant(_) => match pat.ty().kind() { + ty::Adt(adt_def, substs) => { + let variant_index = match pat.ctor() { + Variant(idx) => *idx, + Single => VariantIdx::new(0), + _ => bug!("bad constructor {:?} for adt", pat.ctor()), + }; + let subpatterns = cx + .list_variant_nonhidden_fields(pat.ty(), pat.ctor()) + .zip(subpatterns) + .map(|((field, _ty), pattern)| FieldPat { field, pattern }) + .collect(); + + if adt_def.is_enum() { + PatKind::Variant { adt_def, substs, variant_index, subpatterns } + } else { + PatKind::Leaf { subpatterns } + } + } + _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()), + }, + Tuple(..) => PatKind::Leaf { + subpatterns: subpatterns + .enumerate() + .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .collect(), + }, + // Note: given the expansion of `&str` patterns done in `DeconstructedPat::from_pat`, + // we should be careful to reconstruct the correct constant pattern here. However a + // string literal pattern will never be reported as a non-exhaustiveness witness, so we + // ignore this issue. + Ref(_) | BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + Slice(slice, _) => { + match slice.kind { + SliceKind::FixedLen(_) => { + PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } + } + SliceKind::VarLen(prefix, _) => { + let mut subpatterns = subpatterns.peekable(); + let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); + if slice.array_len.is_some() { + // Improves diagnostics a bit: if the type is a known-size array, instead + // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. + // This is incorrect if the size is not known, since `[_, ..]` captures + // arrays of lengths `>= 1` whereas `[..]` captures any length. + while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { + prefix.pop(); + } + while subpatterns.peek().is_some() + && is_wildcard(subpatterns.peek().unwrap()) + { + subpatterns.next(); + } + } + let suffix: Vec<_> = subpatterns.collect(); + let wild = Pat::wildcard_from_ty(pat.ty()); + PatKind::Slice { prefix, slice: Some(wild), suffix } + } + } + } + Bool(b) => PatKind::Constant { value: ty::Const::from_bool(cx.tcx, *b) }, + IntRange(range) => { + let (lo, hi, end) = range.to_bits(); + let env = ty::ParamEnv::empty().and(pat.ty()); + let lo_const = ty::Const::from_bits(cx.tcx, lo, env); + let hi_const = ty::Const::from_bits(cx.tcx, hi, env); + + if lo == hi { + PatKind::Constant { value: lo_const } + } else { + let end = match end { + deconstruct_pat::RangeEnd::Included => rustc_hir::RangeEnd::Included, + deconstruct_pat::RangeEnd::Excluded => rustc_hir::RangeEnd::Excluded, + }; + PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end }) + } + } + Wildcard | NonExhaustive => PatKind::Wild, + Missing { .. } => bug!( + "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, + `Missing` should have been processed in `apply_constructors`" + ), + // These will never be converted because we don't emit them as non-exhaustiveness + // witnesses. And that's good because we're missing the relevant `&Const`. + F32Range(..) | F64Range(..) | Str(_) | Opaque | Or => { + bug!("can't convert to pattern: {:?}", pat) + } + }; + + Pat { ty: pat.ty(), span: DUMMY_SP, kind: Box::new(kind) } +} + +#[derive(Copy, Clone)] +crate struct MatchCheckCtxt<'tcx> { + crate tcx: TyCtxt<'tcx>, + crate param_env: ty::ParamEnv<'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 + /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty + /// outside its module and should not be matchable with an empty match statement. + crate module: DefId, +} + +impl<'tcx> fmt::Debug for MatchCheckCtxt<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MatchCheckCtxt").field("module", &self.module).finish() + } +} + +impl<'tcx> usefulness::Context for MatchCheckCtxt<'tcx> { + type Ty = Ty<'tcx>; + type TyList = &'tcx [GenericArg<'tcx>]; + type VariantIdx = VariantIdx; + type F32Float = IeeeFloat; + type F64Float = IeeeFloat; + type StringLit = &'tcx [u8]; + type HirId = HirId; + type Span = Span; + type Ident = Ident; + + fn is_numeric(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)) + } + + fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(def, ..) => { + def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() + } + _ => false, + } + } + + fn ty_list_len(ty_list: &Self::TyList) -> usize { + ty_list.len() + } + + type TyListIter = std::iter::Map< + std::iter::Copied>>, + fn(GenericArg<'tcx>) -> Ty<'tcx>, + >; + fn ty_list_iter(ty_list: &Self::TyList) -> Self::TyListIter { + ty_list.iter().copied().map(GenericArg::expect_ty) + } + + fn list_constructors_for_type( + &self, + ty: Ty<'tcx>, + is_top_level_pattern: bool, + ) -> SmallVec<[Constructor; 1]> { + use Constructor::*; + use SliceKind::*; + let cx = self; + + debug!("list_constructors_for_type({:?})", ty); + let make_range = |size: Size, start, end| { + IntRange(self::IntRange::from_bits( + size.bits(), + Self::is_signed_int(ty), + start, + end, + &deconstruct_pat::RangeEnd::Included, + )) + }; + match ty.kind() { + _ if cx.is_uninhabited(ty) => smallvec![], + ty::Bool => smallvec![Bool(false), Bool(true)], + ty::Char => { + let size = number_type_size(cx.tcx, ty); + smallvec![ + // The valid Unicode Scalar Value ranges. + make_range(size, '\u{0000}' as u128, '\u{D7FF}' as u128), + make_range(size, '\u{E000}' as u128, '\u{10FFFF}' as u128), + ] + } + ty::Int(_) | ty::Uint(_) + if ty.is_ptr_sized_integral() + && !cx.tcx.features().precise_pointer_size_matching => + { + // `usize`/`isize` are not allowed to be matched exhaustively unless the + // `precise_pointer_size_matching` feature is enabled. So we treat those types like + // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. + smallvec![NonExhaustive] + } + &ty::Int(_) => { + let size = number_type_size(cx.tcx, ty); + let bits = size.bits() as u128; + let min = 1u128 << (bits - 1); + let max = min - 1; + smallvec![make_range(size, min, max)] + } + &ty::Uint(_) => { + let size = number_type_size(cx.tcx, ty); + let max = size.truncate(u128::MAX); + smallvec![make_range(size, 0, max)] + } + ty::Adt(def, substs) if def.is_enum() => { + let exhaustive_patterns_feature_enabled = cx.tcx.features().exhaustive_patterns; + let mut ctors: SmallVec<[_; 1]> = def + .variants + .iter_enumerated() + .filter(|(_, v)| { + // Filter variants that depend on a disabled unstable feature. + let is_enabled = !matches!( + cx.tcx.eval_stability(v.def_id, None, DUMMY_SP, None), + EvalResult::Deny { .. } + ); + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + let is_uninhabited = exhaustive_patterns_feature_enabled + && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) + .contains(cx.tcx, cx.module); + is_enabled && !is_uninhabited + }) + .map(|(idx, _)| Variant(idx)) + .collect(); + + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an + // additional "unknown" constructor. + // There is no point in enumerating all possible variants, because the user can't + // actually match against them all themselves. So we always return only the fictitious + // constructor. + // E.g., in an example like: + // + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // + // we don't want to show every possible IO error, but instead have only `_` as the + // witness. + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); + + // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it + // as though it had an "unknown" constructor to avoid exposing its emptiness. The + // exception is if the pattern is at the top level, because we want empty matches to be + // considered exhaustive. + let is_secretly_empty = def.variants.is_empty() + && !exhaustive_patterns_feature_enabled + && !is_top_level_pattern; + + if is_declared_nonexhaustive || is_secretly_empty { + ctors.push(NonExhaustive); + } + ctors + } + // The only legal non-wildcard patterns of type `Box` are box patterns, so we emit + // that. + ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], + ty::Adt(..) => smallvec![Single], + ty::Ref(_, ty, _) => smallvec![Ref(*ty)], + ty::Tuple(fs) => smallvec![Tuple(fs.as_ref())], + ty::Array(sub_ty, len) => { + let slice = match len.try_eval_usize(cx.tcx, cx.param_env) { + Some(len) => { + // The uninhabited case is already handled. + self::Slice::new(Some(len as usize), VarLen(0, 0)) + } + None => { + // Treat arrays of a constant but unknown length like slices. + let kind = + if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + self::Slice::new(None, kind) + } + }; + smallvec![Slice(slice, *sub_ty)] + } + ty::Slice(sub_ty) => { + let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + smallvec![Slice(self::Slice::new(None, kind), *sub_ty)] + } + // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we don't + // expose its emptiness and return `NonExhaustive` below. The exception is if the + // pattern is at the top level, because we want empty matches to be considered + // exhaustive. + ty::Never if is_top_level_pattern => smallvec![], + // This type is one for which we cannot list constructors, like `str` or `f64`, or is + // uninhabited but `exhaustive_patterns` is disabled. + _ => smallvec![NonExhaustive], + } + } + + fn variant_ident(ty: Ty<'tcx>, ctor: &Constructor) -> Option { + match ty.kind() { + ty::Adt(adt, _) => { + let variant = match ctor { + Constructor::Variant(idx) => &adt.variants[*idx], + Constructor::Single => adt.non_enum_variant(), + _ => return None, + }; + Some(variant.ident) + } + _ => None, + } + } + + fn is_doc_hidden_variant(&self, ty: Ty<'tcx>, idx: Self::VariantIdx) -> bool { + if let ty::Adt(adt, _) = ty.kind() { + let variant = &adt.variants[idx]; + let attrs = self.tcx.get_attrs(variant.def_id); + for attr in attrs.iter() { + if attr.has_name(sym::doc) { + let items = attr.meta_item_list().unwrap_or_default(); + if items.iter().find(|item| item.has_name(sym::hidden)).is_some() { + return true; + } + } + } + } + false + } + + fn list_variant_nonhidden_fields( + &self, + ty: Ty<'tcx>, + ctor: &Constructor, + ) -> Vec> { + self.list_variant_nonhidden_fields(ty, ctor).map(|(_, ty)| ty).collect() + } + + fn span_bug(span: Self::Span, f: impl Fn() -> String) -> ! { + let error = f(); + span_bug!(span, "{}", error) + } +} + +impl<'tcx> MatchCheckCtxt<'tcx> { + fn is_signed_int(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Int(_)) + } + + fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + if self.tcx.features().exhaustive_patterns { + self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) + } else { + false + } + } + + /// List the fields for this variant. + /// 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. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. + fn list_variant_nonhidden_fields<'a>( + &'a self, + ty: Ty<'tcx>, + ctor: &Constructor, + ) -> impl Iterator)> + Captures<'a> { + let cx = self; + let (adt, substs) = match ty.kind() { + ty::Adt(adt, substs) => (adt, substs), + _ => bug!(), + }; + let variant = match ctor { + Constructor::Variant(variant_index) => &adt.variants[*variant_index], + Constructor::Single => adt.non_enum_variant(), + _ => bug!("bad constructor {:?} for adt {:?}", ctor, ty), + }; + // Whether we must not match the fields of this variant exhaustively. + let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local(); + + variant.fields.iter().enumerate().filter_map(move |(i, field)| { + let ty = field.ty(cx.tcx, substs); + // `field.ty()` doesn't normalize after substituting. + let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = cx.is_uninhabited(ty); + + if is_uninhabited && (!is_visible || is_non_exhaustive) { + None + } else { + Some((Field::new(i), ty)) + } + }) + } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 43adef3d03bed..39b7713f582cc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -283,61 +283,90 @@ use self::ArmType::*; use self::Usefulness::*; -use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label}; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; -use rustc_data_structures::captures::Captures; - use rustc_arena::TypedArena; -use rustc_hir::def_id::DefId; -use rustc_hir::HirId; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_span::{Span, DUMMY_SP}; +use rustc_data_structures::captures::Captures; use smallvec::{smallvec, SmallVec}; -use std::fmt; +use std::fmt::Display; +use std::fmt::{self, Debug}; use std::iter::once; -crate struct MatchCheckCtxt<'p, 'tcx> { - crate tcx: TyCtxt<'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 - /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty - /// outside its module and should not be matchable with an empty match statement. - crate module: DefId, - crate param_env: ty::ParamEnv<'tcx>, - crate pattern_arena: &'p TypedArena>, -} - -impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { - pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - if self.tcx.features().exhaustive_patterns { - self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) - } else { - false - } - } +/// Context for running the algorithm. This decouples the algorithm from specifics of types and the +/// like. +/// The `Copy` and `Debug` bounds are not actually used, but are needed because derived impls add +/// extra useless bounds on `Cx`. +pub(crate) trait Context: Debug + Copy { + type Ty: Debug + Copy; + type TyList: Debug + Clone; + type TyListIter: Iterator; + type VariantIdx: Debug + Copy + Eq; + type F32Float: Debug + Display + Clone + PartialOrd; + type F64Float: Debug + Display + Clone + PartialOrd; + type StringLit: Debug + Clone + Ord; + type HirId: Debug + Copy; + type Span: Debug + Copy + Default; + type Ident: Display; + + fn is_numeric(ty: Self::Ty) -> bool; /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - pub(super) fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { - match ty.kind() { - ty::Adt(def, ..) => { - def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() - } - _ => false, - } - } + fn is_foreign_non_exhaustive_enum(&self, ty: Self::Ty) -> bool; + + fn ty_list_len(ty_list: &Self::TyList) -> usize; + fn ty_list_iter(ty_list: &Self::TyList) -> Self::TyListIter; + + /// List the set of possible constructors for the type `ty`. For numbers, arrays and slices we use + /// ranges and variable-length slices when appropriate. + /// + /// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that are + /// statically impossible. E.g., for `Option`, we do not include `Some(_)` in the returned list + /// of constructors. + /// Even if `exhaustive_patterns` is disabled, we also omit constructors for empty enums and the + /// never type if we are at the top-level in a match, because we want to allow empty matches to be + /// valid for those. + /// Invariant: this is only empty when the type is uninhabited, and the reverse holds if + /// `exhaustive_patterns` is enabled. + fn list_constructors_for_type( + &self, + ty: Self::Ty, + is_top_level_pattern: bool, + ) -> SmallVec<[Constructor; 1]>; + + /// Show the name of the variant corresponding to this constructor. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. + fn variant_ident(ty: Self::Ty, ctor: &Constructor) -> Option; + + /// Whether the variant should be hidden from lints. + /// `ty` must be `ty::Adt` for an enum, and `idx` must be the index of a variant for that type. + fn is_doc_hidden_variant(&self, ty: Self::Ty, idx: Self::VariantIdx) -> bool; + + /// List the fields for this variant. + /// 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. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. + fn list_variant_nonhidden_fields( + &self, + ty: Self::Ty, + ctor: &Constructor, + ) -> Vec; + + fn span_bug(span: Self::Span, f: impl Fn() -> String) -> !; +} + +crate struct UsefulnessCtxt<'p, Cx: Context> { + crate cx: Cx, + crate pattern_arena: &'p TypedArena>, } #[derive(Copy, Clone)] -pub(super) struct PatCtxt<'a, 'p, 'tcx> { - pub(super) cx: &'a MatchCheckCtxt<'p, 'tcx>, +pub(super) struct PatCtxt<'a, 'p, Cx: Context> { + pub(super) ucx: &'a UsefulnessCtxt<'p, Cx>, /// Type of the current column under investigation. - pub(super) ty: Ty<'tcx>, + pub(super) ty: Cx::Ty, /// Span of the current pattern under investigation. - pub(super) span: Span, + pub(super) span: Cx::Span, /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a /// subpattern. pub(super) is_top_level: bool, @@ -345,7 +374,7 @@ pub(super) struct PatCtxt<'a, 'p, 'tcx> { pub(super) is_non_exhaustive: bool, } -impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: Context> Debug for PatCtxt<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PatCtxt").field("ty", &self.ty).finish() } @@ -354,16 +383,16 @@ 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]>, +struct PatStack<'p, Cx: Context> { + pats: SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]>, } -impl<'p, 'tcx> PatStack<'p, 'tcx> { - fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self { +impl<'p, Cx: Context> PatStack<'p, Cx> { + fn from_pattern(pat: &'p DeconstructedPat<'p, Cx>) -> Self { Self::from_vec(smallvec![pat]) } - fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>) -> Self { + fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]>) -> Self { PatStack { pats: vec } } @@ -375,17 +404,17 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { self.pats.len() } - fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> { + fn head(&self) -> &'p DeconstructedPat<'p, Cx> { self.pats[0] } - fn iter(&self) -> impl Iterator> { + fn iter(&self) -> impl Iterator> { self.pats.iter().copied() } // Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an // or-pattern. Panics if `self` is empty. - fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Captures<'a> { + fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Captures<'a> { self.head().iter_fields().map(move |pat| { let mut new_patstack = PatStack::from_pattern(pat); new_patstack.pats.extend_from_slice(&self.pats[1..]); @@ -401,19 +430,19 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { /// This is roughly the inverse of `Constructor::apply`. fn pop_head_constructor( &self, - cx: &MatchCheckCtxt<'p, 'tcx>, - ctor: &Constructor<'tcx>, - ) -> PatStack<'p, 'tcx> { + ucx: &UsefulnessCtxt<'p, Cx>, + ctor: &Constructor, + ) -> PatStack<'p, Cx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); + let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(ucx, ctor); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } } /// Pretty-printing for matrix row. -impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { +impl<'p, Cx: Context> Debug for PatStack<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "+")?; for pat in self.iter() { @@ -425,11 +454,11 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { /// A 2D matrix. #[derive(Clone)] -pub(super) struct Matrix<'p, 'tcx> { - patterns: Vec>, +pub(super) struct Matrix<'p, Cx: Context> { + patterns: Vec>, } -impl<'p, 'tcx> Matrix<'p, 'tcx> { +impl<'p, Cx: Context> Matrix<'p, Cx> { fn empty() -> Self { Matrix { patterns: vec![] } } @@ -441,7 +470,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. - fn push(&mut self, row: PatStack<'p, 'tcx>) { + fn push(&mut self, row: PatStack<'p, Cx>) { if !row.is_empty() && row.head().is_or_pat() { for row in row.expand_or_pat() { self.patterns.push(row); @@ -454,20 +483,20 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Iterate over the first component of each row fn heads<'a>( &'a self, - ) -> impl Iterator> + Clone + Captures<'a> { + ) -> impl Iterator> + Clone + Captures<'a> { self.patterns.iter().map(|r| r.head()) } /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - ctor: &Constructor<'tcx>, - ) -> Matrix<'p, 'tcx> { + pcx: PatCtxt<'_, 'p, Cx>, + ctor: &Constructor, + ) -> Matrix<'p, Cx> { let mut matrix = Matrix::empty(); for row in &self.patterns { if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx.cx, ctor); + let new_row = row.pop_head_constructor(pcx.ucx, ctor); matrix.push(new_row); } } @@ -484,7 +513,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// + false + [_] + /// + _ + [_, _, tail @ ..] + /// ``` -impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { +impl<'p, Cx: Context> Debug for Matrix<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -518,15 +547,15 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { /// witnesses of non-exhaustiveness when there are any. /// Which variant to use is dictated by `ArmType`. #[derive(Debug)] -enum Usefulness<'p, 'tcx> { +enum Usefulness<'p, Cx: Context> { /// If we don't care about witnesses, simply remember if the pattern was useful. NoWitnesses { useful: bool }, /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole /// pattern is unreachable. - WithWitnesses(Vec>), + WithWitnesses(Vec>), } -impl<'p, 'tcx> Usefulness<'p, 'tcx> { +impl<'p, Cx: Context> Usefulness<'p, Cx> { fn new_useful(preference: ArmType) -> Self { match preference { // A single (empty) witness of reachability. @@ -567,9 +596,9 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { /// with the results of specializing with the other constructors. fn apply_constructor( self, - pcx: PatCtxt<'_, 'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors - ctor: &Constructor<'tcx>, + pcx: PatCtxt<'_, 'p, Cx>, + matrix: &Matrix<'p, Cx>, // used to compute missing ctors + ctor: &Constructor, ) -> Self { match self { NoWitnesses { .. } => self, @@ -585,15 +614,35 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { } else { let mut split_wildcard = SplitWildcard::new(pcx); split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); + + // This lets us know if we skipped any variants because they are marked + // `doc(hidden)`. + let mut hid_some_variants = false; // Construct for each missing constructor a "wild" version of this // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for // `Option::Some`, we get the pattern `Some(_)`. - split_wildcard + let mut new: Vec> = split_wildcard .iter_missing(pcx) - .cloned() - .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) - .collect() + .filter(|missing_ctor| { + if let Constructor::Variant(idx) = missing_ctor { + if pcx.ucx.cx.is_doc_hidden_variant(pcx.ty, *idx) { + hid_some_variants = true; + return false; + } + } + true + }) + .map(|missing_ctor| { + DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()) + }) + .collect(); + + if hid_some_variants { + new.push(DeconstructedPat::wildcard(pcx.ty)); + } + + new }; witnesses @@ -663,11 +712,11 @@ enum ArmType { /// /// The final `Pair(Some(_), true)` is then the resulting witness. #[derive(Debug)] -crate struct Witness<'p, 'tcx>(Vec>); +crate struct Witness<'p, Cx: Context>(Vec>); -impl<'p, 'tcx> Witness<'p, 'tcx> { +impl<'p, Cx: Context> Witness<'p, Cx> { /// Asserts that the witness contains a single pattern, and returns it. - fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> { + fn single_pattern(self) -> DeconstructedPat<'p, Cx> { assert_eq!(self.0.len(), 1); self.0.into_iter().next().unwrap() } @@ -685,13 +734,13 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { /// /// left_ty: struct X { a: (bool, &'static str), b: usize} /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } - fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self { + fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p, Cx>, ctor: &Constructor) -> Self { let pat = { let len = self.0.len(); let arity = ctor.arity(pcx); let pats = self.0.drain((len - arity)..).rev(); - let fields = Fields::from_iter(pcx.cx, pats); - DeconstructedPat::new(ctor.clone(), fields, pcx.ty, DUMMY_SP) + let fields = Fields::from_iter(pcx.ucx, pats); + DeconstructedPat::new(ctor.clone(), fields, pcx.ty, Cx::Span::default()) }; self.0.push(pat); @@ -700,32 +749,6 @@ 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`. -fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - scrut_ty: Ty<'tcx>, - sp: Span, - hir_id: HirId, - witnesses: Vec>, -) { - 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"); - 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", - ); - lint.note(&format!( - "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - scrut_ty, - )); - lint.emit(); - }); -} - /// Algorithm from . /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -750,17 +773,18 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( /// relied on for soundness. #[instrument( level = "debug", - skip(cx, matrix, witness_preference, hir_id, is_under_guard, is_top_level) + skip(ucx, matrix, report, witness_preference, hir_id, is_under_guard, is_top_level) )] -fn is_useful<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'p, 'tcx>, +fn is_useful<'p, Cx: Context>( + ucx: &UsefulnessCtxt<'p, Cx>, + matrix: &Matrix<'p, Cx>, + v: &PatStack<'p, Cx>, + report: &mut UsefulnessReport<'p, Cx>, witness_preference: ArmType, - hir_id: HirId, + hir_id: Cx::HirId, is_under_guard: bool, is_top_level: bool, -) -> Usefulness<'p, 'tcx> { +) -> Usefulness<'p, Cx> { debug!("matrix,v={:?}{:?}", matrix, v); let Matrix { patterns: rows, .. } = matrix; @@ -782,8 +806,8 @@ fn is_useful<'p, 'tcx>( assert!(rows.iter().all(|r| r.len() == v.len())); let ty = v.head().ty(); - let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); - let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; + let is_non_exhaustive = ucx.cx.is_foreign_non_exhaustive_enum(ty); + let pcx = PatCtxt { ucx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; // If the first pattern is an or-pattern, expand it. let mut ret = Usefulness::new_not_useful(witness_preference); @@ -792,8 +816,16 @@ fn is_useful<'p, 'tcx>( // We try each or-pattern branch in turn. let mut matrix = matrix.clone(); for v in v.expand_or_pat() { - let usefulness = - is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + let usefulness = is_useful( + ucx, + &matrix, + &v, + report, + witness_preference, + hir_id, + is_under_guard, + false, + ); ret.extend(usefulness); // If pattern has a guard don't add it to the matrix. if !is_under_guard { @@ -808,6 +840,7 @@ fn is_useful<'p, 'tcx>( // Lint on likely incorrect range patterns (#63987) ctor_range.lint_overlapping_range_endpoints( pcx, + &mut report.overlapping_range_endpoints, matrix.heads(), matrix.column_count().unwrap_or(0), hir_id, @@ -821,11 +854,18 @@ fn is_useful<'p, 'tcx>( let start_matrix = &matrix; for ctor in split_ctors { debug!("specialize({:?})", ctor); - // We cache the result of `Fields::wildcards` because it is used a lot. let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); - let v = v.pop_head_constructor(cx, &ctor); - let usefulness = - is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false); + let v = v.pop_head_constructor(ucx, &ctor); + let usefulness = is_useful( + ucx, + &spec_matrix, + &v, + report, + witness_preference, + hir_id, + is_under_guard, + false, + ); let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); // When all the conditions are met we have a match with a `non_exhaustive` enum @@ -841,24 +881,20 @@ fn is_useful<'p, 'tcx>( Constructor::Missing { nonexhaustive_enum_missing_real_variants: true } ) { - let patterns = { - let mut split_wildcard = SplitWildcard::new(pcx); - split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - split_wildcard - .iter_missing(pcx) - // Filter out the `NonExhaustive` because we want to list only real - // variants. - .filter(|c| !c.is_non_exhaustive()) - .cloned() - .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) - .collect::>() - }; - - lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns); + let mut split_wildcard = SplitWildcard::new(pcx); + split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); + // Construct for each missing constructor a "wild" version of this constructor, + // that matches everything that can be built with it. For example, if `ctor` is a + // `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`. + let patterns: Vec<_> = split_wildcard + .iter_missing(pcx) + // Filter out `NonExhaustive` because we want to list only real variants. + .filter(|c| !c.is_non_exhaustive()) + .cloned() + .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) + .collect(); + + report.non_exhaustive_omitted_patterns.push((pcx.ty, pcx.span, hir_id, patterns)); } ret.extend(usefulness); @@ -875,31 +911,47 @@ fn is_useful<'p, 'tcx>( /// The arm of a match expression. #[derive(Clone, Copy)] -crate struct MatchArm<'p, 'tcx> { +crate struct MatchArm<'p, Cx: Context> { /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. - crate pat: &'p DeconstructedPat<'p, 'tcx>, - crate hir_id: HirId, + crate pat: &'p DeconstructedPat<'p, Cx>, + crate hir_id: Cx::HirId, crate has_guard: bool, } /// Indicates whether or not a given arm is reachable. #[derive(Clone, Debug)] crate enum Reachability { - /// The arm is reachable. This additionally carries a set of or-pattern branches that have been - /// found to be unreachable despite the overall arm being reachable. Used only in the presence - /// of or-patterns, otherwise it stays empty. - Reachable(Vec), - /// The arm is unreachable. + Reachable, Unreachable, } /// The output of checking a match for exhaustiveness and arm reachability. -crate struct UsefulnessReport<'p, 'tcx> { +crate struct UsefulnessReport<'p, Cx: Context> { /// For each arm of the input, whether that arm is reachable after the arms above it. - crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>, + crate arm_usefulness: Vec<(MatchArm<'p, Cx>, Reachability)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. - crate non_exhaustiveness_witnesses: Vec>, + crate non_exhaustiveness_witnesses: Vec>, + /// Reports any unreachable sub-or-patterns. + crate unreachable_subpatterns: Vec<(Cx::Span, Cx::HirId)>, + /// Report when some variants of a `non_exhaustive` enum failed to be listed explicitly. + crate non_exhaustive_omitted_patterns: + Vec<(Cx::Ty, Cx::Span, Cx::HirId, Vec>)>, + /// Report likely incorrect range patterns (#63987) + crate overlapping_range_endpoints: Vec<(Cx::Span, Cx::HirId, Vec>)>, +} + +// Can't derive because it adds a bound on `Cx`. +impl<'p, Cx: Context> Default for UsefulnessReport<'p, Cx> { + fn default() -> Self { + Self { + arm_usefulness: Default::default(), + non_exhaustiveness_witnesses: Default::default(), + unreachable_subpatterns: Default::default(), + non_exhaustive_omitted_patterns: Default::default(), + overlapping_range_endpoints: Default::default(), + } + } } /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which @@ -907,37 +959,38 @@ crate struct UsefulnessReport<'p, 'tcx> { /// /// Note: the input patterns must have been lowered through /// `check_match::MatchVisitor::lower_pattern`. -crate fn compute_match_usefulness<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - arms: &[MatchArm<'p, 'tcx>], - scrut_hir_id: HirId, - scrut_ty: Ty<'tcx>, -) -> UsefulnessReport<'p, 'tcx> { +crate fn compute_match_usefulness<'p, Cx: Context>( + ucx: &UsefulnessCtxt<'p, Cx>, + arms: &[MatchArm<'p, Cx>], + scrut_hir_id: Cx::HirId, + scrut_ty: Cx::Ty, +) -> UsefulnessReport<'p, Cx> { + let mut report = UsefulnessReport::default(); let mut matrix = Matrix::empty(); - let arm_usefulness: Vec<_> = arms - .iter() - .copied() - .map(|arm| { - let v = PatStack::from_pattern(arm.pat); - is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true); - if !arm.has_guard { - matrix.push(v); + for arm in arms { + let v = PatStack::from_pattern(arm.pat); + is_useful(ucx, &matrix, &v, &mut report, RealArm, arm.hir_id, arm.has_guard, true); + if !arm.has_guard { + matrix.push(v); + } + let reachability = if arm.pat.is_reachable() { + for span in arm.pat.unreachable_spans() { + report.unreachable_subpatterns.push((span, arm.hir_id)); } - let reachability = if arm.pat.is_reachable() { - Reachability::Reachable(arm.pat.unreachable_spans()) - } else { - Reachability::Unreachable - }; - (arm, reachability) - }) - .collect(); + Reachability::Reachable + } else { + Reachability::Unreachable + }; + report.arm_usefulness.push((*arm, reachability)); + } - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); + let wild_pattern = ucx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); let v = PatStack::from_pattern(wild_pattern); - let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); - let non_exhaustiveness_witnesses = match usefulness { + let usefulness = + is_useful(ucx, &matrix, &v, &mut report, FakeExtraWildcard, scrut_hir_id, false, true); + report.non_exhaustiveness_witnesses = match usefulness { WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(), NoWitnesses { .. } => bug!(), }; - UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } + report } diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 8429799cabf15..2b9281e0ba34f 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -61,7 +61,7 @@ fn main() { } match None { // There is only one error that correctly points to the whole subpattern - Some(0) | + Some(0) | //xx Some( //~ ERROR unreachable 0 | 0) => {} _ => {} @@ -131,7 +131,7 @@ fn main() { // https://github.com/rust-lang/rust/issues/76836 match None { Some(false) => {} - None | Some(true + None | Some(true //xx | false) => {} //~ ERROR unreachable } @@ -139,14 +139,17 @@ fn main() { match (true, true) { (false, true) => {} (true, true) => {} - (false | true, false + (false | true, false //xx | true) => {} //~ ERROR unreachable } match (true, true) { (true, false) => {} (true, true) => {} - (false + (false //xx | true, //~ ERROR unreachable false | true) => {} } + + let (true | false | true, _) = (true, true); + //~^ ERROR unreachable pattern } diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index 3f7d47dcb8ceb..b7cd2151ec7ea 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -166,5 +166,11 @@ error: unreachable pattern LL | | true, | ^^^^ -error: aborting due to 26 previous errors +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:153:25 + | +LL | let (true | false | true, _) = (true, true); + | ^^^^ + +error: aborting due to 27 previous errors diff --git a/src/test/ui/pattern/usefulness/auxiliary/hidden.rs b/src/test/ui/pattern/usefulness/auxiliary/hidden.rs new file mode 100644 index 0000000000000..742b7e82c16b0 --- /dev/null +++ b/src/test/ui/pattern/usefulness/auxiliary/hidden.rs @@ -0,0 +1,6 @@ +pub enum Foo { + A, + B, + #[doc(hidden)] + C, +} diff --git a/src/test/ui/pattern/usefulness/auxiliary/unstable.rs b/src/test/ui/pattern/usefulness/auxiliary/unstable.rs new file mode 100644 index 0000000000000..3142489c86103 --- /dev/null +++ b/src/test/ui/pattern/usefulness/auxiliary/unstable.rs @@ -0,0 +1,12 @@ +#![feature(staged_api)] +#![stable(feature = "stable_test_feature", since = "1.0.0")] + +#[stable(feature = "stable_test_feature", since = "1.0.0")] +pub enum Foo { + #[stable(feature = "stable_test_feature", since = "1.0.0")] + Stable, + #[stable(feature = "stable_test_feature", since = "1.0.0")] + Stable2, + #[unstable(feature = "unstable_test_feature", issue = "none")] + Unstable, +} diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs new file mode 100644 index 0000000000000..a1dcab0931423 --- /dev/null +++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs @@ -0,0 +1,30 @@ +// aux-build:hidden.rs + +extern crate hidden; + +use hidden::Foo; + +fn main() { + match Foo::A { + Foo::A => {} + Foo::B => {} + } + //~^^^^ non-exhaustive patterns: `_` not covered + + match Foo::A { + Foo::A => {} + Foo::C => {} + } + //~^^^^ non-exhaustive patterns: `B` not covered + + match Foo::A { + Foo::A => {} + } + //~^^^ non-exhaustive patterns: `B` and `_` not covered + + match None { + None => {} + Some(Foo::A) => {} + } + //~^^^^ non-exhaustive patterns: `Some(B)` and `Some(_)` not covered +} diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr new file mode 100644 index 0000000000000..6c9539822b3dd --- /dev/null +++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr @@ -0,0 +1,54 @@ +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:8:11 + | +LL | match Foo::A { + | ^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error[E0004]: non-exhaustive patterns: `B` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:14:11 + | +LL | match Foo::A { + | ^^^^^^ pattern `B` not covered + | + ::: $DIR/auxiliary/hidden.rs:3:5 + | +LL | B, + | - not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error[E0004]: non-exhaustive patterns: `B` and `_` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:20:11 + | +LL | match Foo::A { + | ^^^^^^ patterns `B` and `_` not covered + | + ::: $DIR/auxiliary/hidden.rs:3:5 + | +LL | B, + | - not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error[E0004]: non-exhaustive patterns: `Some(B)` and `Some(_)` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:25:11 + | +LL | match None { + | ^^^^ patterns `Some(B)` and `Some(_)` not covered + | + ::: $SRC_DIR/core/src/option.rs:LL:COL + | +LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ---- not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Option` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/stable-gated-patterns.rs b/src/test/ui/pattern/usefulness/stable-gated-patterns.rs new file mode 100644 index 0000000000000..4a87eea11795b --- /dev/null +++ b/src/test/ui/pattern/usefulness/stable-gated-patterns.rs @@ -0,0 +1,18 @@ +// aux-build:unstable.rs + +extern crate unstable; + +use unstable::Foo; + +fn main() { + match Foo::Stable { + Foo::Stable => {} + } + //~^^^ non-exhaustive patterns: `Stable2` not covered + + // Ok: all variants are explicitly matched + match Foo::Stable { + Foo::Stable => {} + Foo::Stable2 => {} + } +} diff --git a/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr b/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr new file mode 100644 index 0000000000000..b2729ebab6994 --- /dev/null +++ b/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr @@ -0,0 +1,17 @@ +error[E0004]: non-exhaustive patterns: `Stable2` not covered + --> $DIR/stable-gated-patterns.rs:8:11 + | +LL | match Foo::Stable { + | ^^^^^^^^^^^ pattern `Stable2` not covered + | + ::: $DIR/auxiliary/unstable.rs:9:5 + | +LL | Stable2, + | ------- not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs b/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs new file mode 100644 index 0000000000000..b9804b0ffe77a --- /dev/null +++ b/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs @@ -0,0 +1,22 @@ +#![feature(unstable_test_feature)] + +// aux-build:unstable.rs + +extern crate unstable; + +use unstable::Foo; + +fn main() { + match Foo::Stable { + Foo::Stable => {} + Foo::Stable2 => {} + } + //~^^^^ non-exhaustive patterns: `Unstable` not covered + + // Ok: all variants are explicitly matched + match Foo::Stable { + Foo::Stable => {} + Foo::Stable2 => {} + Foo::Unstable => {} + } +} diff --git a/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr b/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr new file mode 100644 index 0000000000000..f9c0196b76598 --- /dev/null +++ b/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr @@ -0,0 +1,17 @@ +error[E0004]: non-exhaustive patterns: `Unstable` not covered + --> $DIR/unstable-gated-patterns.rs:10:11 + | +LL | match Foo::Stable { + | ^^^^^^^^^^^ pattern `Unstable` not covered + | + ::: $DIR/auxiliary/unstable.rs:11:5 + | +LL | Unstable, + | -------- not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs index c196ded404ddc..21c9dc28994f3 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs @@ -92,35 +92,6 @@ fn main() { //~^^ some variants are not matched explicitly //~^^^^^ some variants are not matched explicitly - // The io::ErrorKind has many `unstable` fields how do they interact with this - // lint - #[deny(non_exhaustive_omitted_patterns)] - match std::io::ErrorKind::Other { - std::io::ErrorKind::NotFound => {} - std::io::ErrorKind::PermissionDenied => {} - std::io::ErrorKind::ConnectionRefused => {} - std::io::ErrorKind::ConnectionReset => {} - std::io::ErrorKind::ConnectionAborted => {} - std::io::ErrorKind::NotConnected => {} - std::io::ErrorKind::AddrInUse => {} - std::io::ErrorKind::AddrNotAvailable => {} - std::io::ErrorKind::BrokenPipe => {} - std::io::ErrorKind::AlreadyExists => {} - std::io::ErrorKind::WouldBlock => {} - std::io::ErrorKind::InvalidInput => {} - std::io::ErrorKind::InvalidData => {} - std::io::ErrorKind::TimedOut => {} - std::io::ErrorKind::WriteZero => {} - std::io::ErrorKind::Interrupted => {} - std::io::ErrorKind::Other => {} - std::io::ErrorKind::UnexpectedEof => {} - std::io::ErrorKind::Unsupported => {} - std::io::ErrorKind::OutOfMemory => {} - // All stable variants are above and unstable in `_` - _ => {} - } - //~^^ some variants are not matched explicitly - #[warn(non_exhaustive_omitted_patterns)] match VariantNonExhaustive::Baz(1, 2) { VariantNonExhaustive::Baz(_, _) => {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr index e66fd8008a10b..9457ab2cced82 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr @@ -1,11 +1,11 @@ warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:127:9 + --> $DIR/reachable-patterns.rs:98:9 | LL | VariantNonExhaustive::Bar { x, .. } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:124:12 + --> $DIR/reachable-patterns.rs:95:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,13 +13,13 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:132:9 + --> $DIR/reachable-patterns.rs:103:9 | LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:131:12 + --> $DIR/reachable-patterns.rs:102:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,13 +27,13 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:140:29 + --> $DIR/reachable-patterns.rs:111:29 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:139:12 + --> $DIR/reachable-patterns.rs:110:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:140:9 + --> $DIR/reachable-patterns.rs:111:9 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed @@ -115,32 +115,18 @@ LL | _ => {} = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/reachable-patterns.rs:120:9 - | -LL | _ => {} - | ^ patterns `HostUnreachable`, `NetworkUnreachable`, `NetworkDown` and 18 more not covered - | -note: the lint level is defined here - --> $DIR/reachable-patterns.rs:97:12 - | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `ErrorKind` and the `non_exhaustive_omitted_patterns` attribute was found - -error: some variants are not matched explicitly - --> $DIR/reachable-patterns.rs:157:9 + --> $DIR/reachable-patterns.rs:128:9 | LL | _ => {} | ^ pattern `A(_)` not covered | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:155:12 + --> $DIR/reachable-patterns.rs:126:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found -error: aborting due to 7 previous errors; 4 warnings emitted +error: aborting due to 6 previous errors; 4 warnings emitted