From deb05930ef3764b03582459d001fa3717d9b63f7 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Thu, 7 Oct 2021 23:33:41 +0500 Subject: [PATCH 1/4] internal: Sync match checking algorithm with rustc Original version: rust-lang/rust 68b76a483 2021-10-01 --- Cargo.lock | 7 + crates/hir_ty/Cargo.toml | 1 + crates/hir_ty/src/diagnostics/expr.rs | 47 +- crates/hir_ty/src/diagnostics/match_check.rs | 27 +- .../match_check/deconstruct_pat.rs | 635 ++++++++------- .../src/diagnostics/match_check/usefulness.rs | 731 +++++------------- .../src/handlers/missing_match_arms.rs | 1 - 7 files changed, 583 insertions(+), 866 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd4324cf3c4f..7d6446ea6b86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -559,6 +559,7 @@ dependencies = [ "tracing", "tracing-subscriber", "tracing-tree", + "typed-arena", ] [[package]] @@ -1775,6 +1776,12 @@ dependencies = [ "stdx", ] +[[package]] +name = "typed-arena" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" + [[package]] name = "ungrammar" version = "1.14.9" diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index a1a7063d48e0..f5e557570713 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml @@ -23,6 +23,7 @@ chalk-ir = "0.75" chalk-recursive = { version = "0.75", default-features = false } la-arena = { version = "0.3.0", path = "../../lib/arena" } once_cell = { version = "1.5.0" } +typed-arena = "2.0.1" stdx = { path = "../stdx", version = "0.0.0" } hir_def = { path = "../hir_def", version = "0.0.0" } diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index d3a010012376..04a0125fd772 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs @@ -2,7 +2,7 @@ //! through the body using inference results: mismatched arg counts, missing //! fields, etc. -use std::{cell::RefCell, sync::Arc}; +use std::sync::Arc; use hir_def::{ expr::Statement, path::path, resolver::HasResolver, type_ref::Mutability, AssocItemId, @@ -11,12 +11,14 @@ use hir_def::{ use hir_expand::name; use itertools::Either; use rustc_hash::FxHashSet; +use typed_arena::Arena; use crate::{ db::HirDatabase, diagnostics::match_check::{ self, - usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena}, + deconstruct_pat::DeconstructedPat, + usefulness::{compute_match_usefulness, MatchCheckCtx}, }, AdtId, InferenceResult, Interner, Ty, TyExt, TyKind, }; @@ -275,15 +277,19 @@ impl ExprValidator { ) { let body = db.body(self.owner); - let match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() { + let match_expr_ty = &infer[match_expr]; + if match_expr_ty.is_unknown() { return; - } else { - &infer.type_of_expr[match_expr] - }; + } - let pattern_arena = RefCell::new(PatternArena::new()); + let pattern_arena = Arena::new(); + let cx = MatchCheckCtx { + module: self.owner.module(db.upcast()), + db, + pattern_arena: &pattern_arena, + }; - let mut m_arms = Vec::new(); + let mut m_arms = Vec::with_capacity(arms.len()); let mut has_lowering_errors = false; for arm in arms { if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) { @@ -308,13 +314,7 @@ impl ExprValidator { // check the usefulness of each pattern as we added it // to the matrix here. let m_arm = match_check::MatchArm { - pat: self.lower_pattern( - arm.pat, - &mut pattern_arena.borrow_mut(), - db, - &body, - &mut has_lowering_errors, - ), + pat: self.lower_pattern(&cx, arm.pat, db, &body, &mut has_lowering_errors), has_guard: arm.guard.is_some(), }; m_arms.push(m_arm); @@ -332,14 +332,7 @@ impl ExprValidator { return; } - let cx = MatchCheckCtx { - module: self.owner.module(db.upcast()), - match_expr, - infer: &infer, - db, - pattern_arena: &pattern_arena, - }; - let report = compute_match_usefulness(&cx, &m_arms); + let report = compute_match_usefulness(&cx, &m_arms, match_expr_ty); // FIXME Report unreacheble arms // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201 @@ -352,17 +345,17 @@ impl ExprValidator { } } - fn lower_pattern( + fn lower_pattern<'p>( &self, + cx: &MatchCheckCtx<'_, 'p>, pat: PatId, - pattern_arena: &mut PatternArena, db: &dyn HirDatabase, body: &Body, have_errors: &mut bool, - ) -> match_check::PatId { + ) -> &'p DeconstructedPat<'p> { let mut patcx = match_check::PatCtxt::new(db, &self.infer, body); let pattern = patcx.lower_pattern(pat); - let pattern = pattern_arena.alloc(expand_pattern(pattern)); + let pattern = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern)); if !patcx.errors.is_empty() { *have_errors = true; } diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index b3cc83b34040..fdf448f5e119 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs @@ -5,13 +5,12 @@ //! //! It is modeled on the rustc module `rustc_mir_build::thir::pattern`. -mod deconstruct_pat; mod pat_util; +pub(crate) mod deconstruct_pat; pub(crate) mod usefulness; -use hir_def::{body::Body, EnumVariantId, LocalFieldId, VariantId}; -use la_arena::Idx; +use hir_def::{body::Body, expr::PatId, EnumVariantId, LocalFieldId, VariantId}; use crate::{db::HirDatabase, InferenceResult, Interner, Substitution, Ty, TyKind}; @@ -19,8 +18,6 @@ use self::pat_util::EnumerateAndAdjustIterator; pub(crate) use self::usefulness::MatchArm; -pub(crate) type PatId = Idx; - #[derive(Clone, Debug)] pub(crate) enum PatternError { Unimplemented, @@ -41,12 +38,6 @@ pub(crate) struct Pat { pub(crate) kind: Box, } -impl Pat { - pub(crate) fn wildcard_from_ty(ty: Ty) -> Self { - Pat { ty, kind: Box::new(PatKind::Wild) } - } -} - /// Close relative to `rustc_mir_build::thir::pattern::PatKind` #[derive(Clone, Debug, PartialEq)] pub(crate) enum PatKind { @@ -100,7 +91,7 @@ impl<'a> PatCtxt<'a> { Self { db, infer, body, errors: Vec::new() } } - pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { + pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat { // XXX(iDawer): Collecting pattern adjustments feels imprecise to me. // When lowering of & and box patterns are implemented this should be tested // in a manner of `match_ergonomics_issue_9095` test. @@ -116,7 +107,7 @@ impl<'a> PatCtxt<'a> { ) } - fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { + fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat { let mut ty = &self.infer[pat]; let variant = self.infer.variant_resolution_for_pat(pat); @@ -189,7 +180,7 @@ impl<'a> PatCtxt<'a> { fn lower_tuple_subpats( &mut self, - pats: &[hir_def::expr::PatId], + pats: &[PatId], expected_len: usize, ellipsis: Option, ) -> Vec { @@ -207,17 +198,17 @@ impl<'a> PatCtxt<'a> { .collect() } - fn lower_patterns(&mut self, pats: &[hir_def::expr::PatId]) -> Vec { + fn lower_patterns(&mut self, pats: &[PatId]) -> Vec { pats.iter().map(|&p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern(&mut self, pat: Option) -> Option { + fn lower_opt_pattern(&mut self, pat: Option) -> Option { pat.map(|p| self.lower_pattern(p)) } fn lower_variant_or_leaf( &mut self, - pat: hir_def::expr::PatId, + pat: PatId, ty: &Ty, subpatterns: Vec, ) -> PatKind { @@ -244,7 +235,7 @@ impl<'a> PatCtxt<'a> { kind } - fn lower_path(&mut self, pat: hir_def::expr::PatId, _path: &hir_def::path::Path) -> Pat { + fn lower_path(&mut self, pat: PatId, _path: &hir_def::path::Path) -> Pat { let ty = &self.infer[pat]; let pat_from_kind = |kind| Pat { ty: ty.clone(), kind: Box::new(kind) }; diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index eb7d5e5e482e..218b48680fa4 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -42,6 +42,7 @@ //! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]. use std::{ + cell::Cell, cmp::{max, min}, iter::once, ops::RangeInclusive, @@ -55,12 +56,29 @@ use syntax::SmolStr; use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; use super::{ - usefulness::{MatchCheckCtx, PatCtxt}, - FieldPat, Pat, PatId, PatKind, + usefulness::{helper::Captures, MatchCheckCtx, PatCtxt}, + Pat, PatKind, }; use self::Constructor::*; +/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. +fn expand_or_pat(pat: &Pat) -> Vec<&Pat> { + fn expand<'p>(pat: &'p Pat, vec: &mut Vec<&'p Pat>) { + 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); + pats +} + /// [Constructor] uses this in umimplemented variants. /// It allows porting match expressions from upstream algorithm without losing semantics. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -241,6 +259,10 @@ pub(super) struct Slice { } impl Slice { + fn arity(self) -> usize { + unimplemented!() + } + /// See `Constructor::is_covered_by` fn is_covered_by(self, _other: Self) -> bool { unimplemented!() // never called as Slice contains Void @@ -278,10 +300,13 @@ pub(super) enum Constructor { /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. NonExhaustive, /// Stands for constructors that are not seen in the matrix, as explained in the documentation - /// for [`SplitWildcard`]. - Missing, + /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns` + /// lint. + Missing { nonexhaustive_enum_missing_real_variants: bool }, /// Wildcard pattern. Wildcard, + /// Or-pattern. + Or, } impl Constructor { @@ -289,6 +314,10 @@ impl Constructor { matches!(self, Wildcard) } + pub(super) fn is_non_exhaustive(&self) -> bool { + matches!(self, NonExhaustive) + } + fn as_int_range(&self) -> Option<&IntRange> { match self { IntRange(range) => Some(range), @@ -318,16 +347,39 @@ impl Constructor { } } - /// Determines the constructor that the given pattern can be specialized to. - pub(super) fn from_pat(cx: &MatchCheckCtx<'_>, pat: PatId) -> Self { - match cx.pattern_arena.borrow()[pat].kind.as_ref() { - PatKind::Binding { .. } | PatKind::Wild => Wildcard, - PatKind::Leaf { .. } | PatKind::Deref { .. } => Single, - &PatKind::Variant { enum_variant, .. } => Variant(enum_variant), - &PatKind::LiteralBool { value } => IntRange(IntRange::from_bool(value)), - PatKind::Or { .. } => { - never!("Or-pattern should have been expanded earlier on."); - Wildcard + /// The number of fields for this constructor. This must be kept in sync with + /// `Fields::wildcards`. + pub(super) fn arity(&self, pcx: PatCtxt<'_, '_>) -> usize { + match self { + Single | Variant(_) => match *pcx.ty.kind(Interner) { + TyKind::Tuple(arity, ..) => arity, + TyKind::Ref(..) => 1, + TyKind::Adt(adt, ..) => { + if adt_is_box(adt.0, pcx.cx) { + // 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 = self.variant_id_for_adt(adt.0); + Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() + } + } + _ => { + never!("Unexpected type for `Single` constructor: {:?}", pcx.ty); + 0 + } + }, + Slice(slice) => slice.arity(), + Str(..) + | FloatRange(..) + | IntRange(..) + | NonExhaustive + | Opaque + | Missing { .. } + | Wildcard => 0, + Or => { + never!("The `Or` constructor doesn't have a fixed arity"); + 0 } } } @@ -347,7 +399,7 @@ impl Constructor { /// matrix, unless all of them are. pub(super) fn split<'a>( &self, - pcx: PatCtxt<'_>, + pcx: PatCtxt<'_, '_>, ctors: impl Iterator + Clone, ) -> SmallVec<[Self; 1]> { match self { @@ -375,13 +427,13 @@ impl Constructor { /// this checks for inclusion. // We inline because this has a single call site in `Matrix::specialize_constructor`. #[inline] - pub(super) fn is_covered_by(&self, _pcx: PatCtxt<'_>, other: &Self) -> bool { + pub(super) fn is_covered_by(&self, _pcx: PatCtxt<'_, '_>, other: &Self) -> bool { // This must be kept in sync with `is_covered_by_any`. match (self, other) { // Wildcards cover anything (_, Wildcard) => true, // The missing ctors are not covered by anything in the matrix except wildcards. - (Missing | Wildcard, _) => false, + (Missing { .. } | Wildcard, _) => false, (Single, Single) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, @@ -411,7 +463,7 @@ impl Constructor { /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is /// assumed to have been split from a wildcard. - fn is_covered_by_any(&self, _pcx: PatCtxt<'_>, used_ctors: &[Constructor]) -> bool { + fn is_covered_by_any(&self, _pcx: PatCtxt<'_, '_>, used_ctors: &[Constructor]) -> bool { if used_ctors.is_empty() { return false; } @@ -431,7 +483,7 @@ impl Constructor { .any(|other| slice.is_covered_by(other)), // This constructor is never covered by anything else NonExhaustive => false, - Str(..) | FloatRange(..) | Opaque | Missing | Wildcard => { + Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => { never!("found unexpected ctor in all_ctors: {:?}", self); true } @@ -463,7 +515,7 @@ pub(super) struct SplitWildcard { } impl SplitWildcard { - pub(super) fn new(pcx: PatCtxt<'_>) -> Self { + pub(super) fn new(pcx: PatCtxt<'_, '_>) -> Self { let cx = pcx.cx; let make_range = |start, end, scalar| IntRange(IntRange::from_range(start, end, scalar)); @@ -483,7 +535,7 @@ impl SplitWildcard { TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)], // TyKind::Array(..) if ... => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(), - &TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref _substs) => { + &TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ..) => { let enum_data = cx.db.enum_data(enum_id); // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an @@ -502,7 +554,7 @@ impl SplitWildcard { // // 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(enum_id); + 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 @@ -512,8 +564,15 @@ impl SplitWildcard { && !cx.feature_exhaustive_patterns() && !pcx.is_top_level; - if is_secretly_empty || is_declared_nonexhaustive { + if is_secretly_empty { smallvec![NonExhaustive] + } else if is_declared_nonexhaustive { + enum_data + .variants + .iter() + .map(|(local_id, ..)| Variant(EnumVariantId { parent: enum_id, local_id })) + .chain(Some(NonExhaustive)) + .collect() } else if cx.feature_exhaustive_patterns() { unimplemented!() // see MatchCheckCtx.feature_exhaustive_patterns() } else { @@ -535,6 +594,7 @@ impl SplitWildcard { // This type is one for which we cannot list constructors, like `str` or `f64`. _ => smallvec![NonExhaustive], }; + SplitWildcard { matrix_ctors: Vec::new(), all_ctors } } @@ -542,7 +602,7 @@ impl SplitWildcard { /// do what you want. pub(super) fn split<'a>( &mut self, - pcx: PatCtxt<'_>, + pcx: PatCtxt<'_, '_>, ctors: impl Iterator + Clone, ) { // Since `all_ctors` never contains wildcards, this won't recurse further. @@ -552,21 +612,21 @@ impl SplitWildcard { } /// Whether there are any value constructors for this type that are not present in the matrix. - fn any_missing(&self, pcx: PatCtxt<'_>) -> bool { + fn any_missing(&self, pcx: PatCtxt<'_, '_>) -> 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>( + pub(super) fn iter_missing<'a, 'p>( &'a self, - pcx: PatCtxt<'a>, - ) -> impl Iterator { + pcx: PatCtxt<'a, 'p>, + ) -> 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<'_>) -> SmallVec<[Constructor; 1]> { + fn into_ctors(self, pcx: PatCtxt<'_, '_>) -> 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, @@ -597,7 +657,17 @@ impl SplitWildcard { // 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 ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { - Missing + if pcx.is_non_exhaustive { + Missing { + nonexhaustive_enum_missing_real_variants: self + .iter_missing(pcx) + .filter(|c| !c.is_non_exhaustive()) + .next() + .is_some(), + } + } else { + Missing { nonexhaustive_enum_missing_real_variants: false } + } } else { Wildcard }; @@ -611,291 +681,334 @@ impl SplitWildcard { /// A value can be decomposed into a constructor applied to some fields. This struct represents /// those fields, generalized to allow patterns in each field. See also `Constructor`. -/// This is constructed from a constructor using [`Fields::wildcards()`]. /// -/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is -/// uninhabited. For that, we filter these fields out of the matrix. This is handled automatically -/// in `Fields`. This filtering is uncommon in practice, because uninhabited fields are rarely used, -/// so we avoid it when possible to preserve performance. -#[derive(Debug, Clone)] -pub(super) enum Fields { - /// Lists of patterns that don't contain any filtered fields. - /// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and - /// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril) - /// have not measured if it really made a difference. - Vec(SmallVec<[PatId; 2]>), +/// This is constructed for a constructor using [`Fields::wildcards()`]. The idea is that +/// [`Fields::wildcards()`] constructs a list of fields where all entries are wildcards, and then +/// given a pattern we fill some of the fields with its subpatterns. +/// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in +/// `extract_pattern_arguments` we fill some of the entries, and the result is +/// `[Some(0), _, _, _]`. +/// ```rust +/// let x: [Option; 4] = foo(); +/// match x { +/// [Some(0), ..] => {} +/// } +/// ``` +/// +/// Note that the number of fields of a constructor may not match the fields declared in the +/// original struct/variant. This happens if a private or `non_exhaustive` field is uninhabited, +/// because the code mustn't observe that it is uninhabited. In that case that field is not +/// included in `fields`. For that reason, when you have a `mir::Field` you must use +/// `index_with_declared_idx`. +#[derive(Clone, Copy)] +pub(super) struct Fields<'p> { + fields: &'p [DeconstructedPat<'p>], } -impl Fields { - /// Internal use. Use `Fields::wildcards()` instead. - /// Must not be used if the pattern is a field of a struct/tuple/variant. - fn from_single_pattern(pat: PatId) -> Self { - Fields::Vec(smallvec![pat]) +impl<'p> Fields<'p> { + fn empty() -> Self { + Fields { fields: &[] } } - /// Convenience; internal use. - fn wildcards_from_tys(cx: &MatchCheckCtx<'_>, tys: impl IntoIterator) -> Self { - let wilds = tys.into_iter().map(Pat::wildcard_from_ty); - let pats = wilds.map(|pat| cx.alloc_pat(pat)).collect(); - Fields::Vec(pats) + fn singleton(cx: &MatchCheckCtx<'_, 'p>, field: DeconstructedPat<'p>) -> Self { + let field = cx.pattern_arena.alloc(field); + Fields { fields: std::slice::from_ref(field) } } - /// Creates a new list of wildcard fields for a given constructor. - pub(crate) fn wildcards(pcx: PatCtxt<'_>, constructor: &Constructor) -> Self { - let ty = pcx.ty; - let cx = pcx.cx; - let wildcard_from_ty = |ty: &Ty| cx.alloc_pat(Pat::wildcard_from_ty(ty.clone())); + pub(super) fn from_iter( + cx: &MatchCheckCtx<'_, 'p>, + fields: impl IntoIterator>, + ) -> Self { + let fields: &[_] = cx.pattern_arena.alloc_extend(fields); + Fields { fields } + } + + fn wildcards_from_tys(cx: &MatchCheckCtx<'_, 'p>, 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 MatchCheckCtx<'a, 'p>, + ty: &'a Ty, + variant: VariantId, + ) -> impl Iterator + Captures<'a> + Captures<'p> { + let (adt, substs) = ty.as_adt().unwrap(); + + let adt_is_local = variant.module(cx.db.upcast()).krate() == cx.module.krate(); + // Whether we must not match the fields of this variant exhaustively. + let is_non_exhaustive = is_field_list_non_exhaustive(variant, cx) && !adt_is_local; + + let visibility = cx.db.field_visibilities(variant); + let field_ty = cx.db.field_types(variant); + let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32; + + (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { + // TODO check ty has been normalized + let ty = field_ty[fid].clone().substitute(Interner, substs); + let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) + || visibility[fid].is_visible_from(cx.db.upcast(), cx.module); + let is_uninhabited = cx.is_uninhabited(&ty); + + if is_uninhabited && (!is_visible || is_non_exhaustive) { + None + } else { + Some((fid, ty)) + } + }) + } + + /// Creates a new list of wildcard fields for a given constructor. The result must have a + /// length of `constructor.arity()`. + pub(crate) fn wildcards( + cx: &MatchCheckCtx<'_, 'p>, + ty: &Ty, + constructor: &Constructor, + ) -> Self { let ret = match constructor { Single | Variant(_) => match ty.kind(Interner) { TyKind::Tuple(_, substs) => { let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner)); Fields::wildcards_from_tys(cx, tys.cloned()) } - TyKind::Ref(.., rty) => Fields::from_single_pattern(wildcard_from_ty(rty)), + TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())), &TyKind::Adt(AdtId(adt), ref substs) => { if adt_is_box(adt, cx) { - // Use T as the sub pattern type of Box. - let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner); - Fields::from_single_pattern(wildcard_from_ty(subst_ty)) + // 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. + let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); + Fields::wildcards_from_tys(cx, once(subst_ty)) } else { - let variant_id = constructor.variant_id_for_adt(adt); - let adt_is_local = - variant_id.module(cx.db.upcast()).krate() == cx.module.krate(); - // Whether we must not match the fields of this variant exhaustively. - let is_non_exhaustive = - is_field_list_non_exhaustive(variant_id, cx) && !adt_is_local; - - cov_mark::hit!(match_check_wildcard_expanded_to_substitutions); - let field_ty_data = cx.db.field_types(variant_id); - let field_tys = || { - field_ty_data - .iter() - .map(|(_, binders)| binders.clone().substitute(Interner, substs)) - }; - - // In the following cases, we don't need to filter out any fields. This is - // the vast majority of real cases, since uninhabited fields are uncommon. - let has_no_hidden_fields = (matches!(adt, hir_def::AdtId::EnumId(_)) - && !is_non_exhaustive) - || !field_tys().any(|ty| cx.is_uninhabited(&ty)); - - if has_no_hidden_fields { - Fields::wildcards_from_tys(cx, field_tys()) - } else { - //FIXME(iDawer): see MatchCheckCtx::is_uninhabited, has_no_hidden_fields is always true - unimplemented!("exhaustive_patterns feature") - } + let variant = constructor.variant_id_for_adt(adt); + let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) + .map(|(_, ty)| ty); + Fields::wildcards_from_tys(cx, tys) } } ty_kind => { never!("Unexpected type for `Single` constructor: {:?}", ty_kind); - Fields::from_single_pattern(wildcard_from_ty(ty)) + Fields::wildcards_from_tys(cx, once(ty.clone())) } }, Slice(..) => { unimplemented!() } - Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing - | Wildcard => Fields::Vec(Default::default()), + Str(..) + | FloatRange(..) + | IntRange(..) + | NonExhaustive + | Opaque + | Missing { .. } + | Wildcard => Fields::empty(), + Or => { + never!("called `Fields::wildcards` on an `Or` ctor"); + Fields::empty() + } }; ret } - /// Apply a constructor to a list of patterns, yielding a new pattern. `self` - /// must have as many elements as this constructor's arity. - /// - /// This is roughly the inverse of `specialize_constructor`. - /// - /// Examples: - /// `ctor`: `Constructor::Single` - /// `ty`: `Foo(u32, u32, u32)` - /// `self`: `[10, 20, _]` - /// returns `Foo(10, 20, _)` - /// - /// `ctor`: `Constructor::Variant(Option::Some)` - /// `ty`: `Option` - /// `self`: `[false]` - /// returns `Some(false)` - pub(super) fn apply(self, pcx: PatCtxt<'_>, ctor: &Constructor) -> Pat { - let subpatterns_and_indices = self.patterns_and_indices(); - let mut subpatterns = - subpatterns_and_indices.iter().map(|&(_, p)| pcx.cx.pattern_arena.borrow()[p].clone()); - // FIXME(iDawer) witnesses are not yet used - const UNHANDLED: PatKind = PatKind::Wild; - - let pat = match ctor { - Single | Variant(_) => match pcx.ty.kind(Interner) { - TyKind::Adt(..) | TyKind::Tuple(..) => { - // We want the real indices here. - let subpatterns = subpatterns_and_indices - .iter() - .map(|&(field, pat)| FieldPat { - field, - pattern: pcx.cx.pattern_arena.borrow()[pat].clone(), - }) - .collect(); - - if let Some((hir_def::AdtId::EnumId(_), substs)) = pcx.ty.as_adt() { - let enum_variant = match ctor { - &Variant(id) => id, - _ => unreachable!(), - }; - PatKind::Variant { substs: substs.clone(), enum_variant, 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 - // can ignore this issue. - TyKind::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - TyKind::Slice(..) | TyKind::Array(..) => { - never!("bad slice pattern {:?} {:?}", ctor, pcx.ty); - PatKind::Wild - } - _ => PatKind::Wild, - }, - Constructor::Slice(_) => UNHANDLED, - Str(_) => UNHANDLED, - FloatRange(..) => UNHANDLED, - Constructor::IntRange(_) => UNHANDLED, - NonExhaustive => PatKind::Wild, - Wildcard => return Pat::wildcard_from_ty(pcx.ty.clone()), - Opaque => { - never!("we should not try to apply an opaque constructor"); - PatKind::Wild - } - Missing => { - never!( - "trying to apply the `Missing` constructor; \ - this should have been done in `apply_constructors`", - ); - PatKind::Wild - } - }; - - Pat { ty: pcx.ty.clone(), kind: Box::new(pat) } + /// Returns the list of patterns. + pub(super) fn iter_patterns<'a>( + &'a self, + ) -> impl Iterator> + Captures<'a> { + self.fields.iter() } +} - /// Returns the number of patterns. This is the same as the arity of the constructor used to - /// construct `self`. - pub(super) fn len(&self) -> usize { - match self { - Fields::Vec(pats) => pats.len(), - } - } +/// Values and patterns can be represented as a constructor applied to some fields. This represents +/// a pattern in this form. +/// 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> { + ctor: Constructor, + fields: Fields<'p>, + ty: Ty, + reachable: Cell, +} - /// Returns the list of patterns along with the corresponding field indices. - fn patterns_and_indices(&self) -> SmallVec<[(LocalFieldId, PatId); 2]> { - match self { - Fields::Vec(pats) => pats - .iter() - .copied() - .enumerate() - .map(|(i, p)| (LocalFieldId::from_raw((i as u32).into()), p)) - .collect(), - } +impl<'p> DeconstructedPat<'p> { + pub(super) fn wildcard(ty: Ty) -> Self { + Self::new(Wildcard, Fields::empty(), ty) } - pub(super) fn into_patterns(self) -> SmallVec<[PatId; 2]> { - match self { - Fields::Vec(pats) => pats, - } + pub(super) fn new(ctor: Constructor, fields: Fields<'p>, ty: Ty) -> Self { + DeconstructedPat { ctor, fields, ty, reachable: Cell::new(false) } } - /// Overrides some of the fields with the provided patterns. Exactly like - /// `replace_fields_indexed`, except that it takes `FieldPat`s as input. - fn replace_with_fieldpats( - &self, - new_pats: impl IntoIterator, - ) -> Self { - self.replace_fields_indexed( - new_pats.into_iter().map(|(field, pat)| (u32::from(field.into_raw()) as usize, pat)), - ) + /// 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>, ctor: Constructor) -> Self { + let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); + DeconstructedPat::new(ctor, fields, pcx.ty.clone()) } - /// Overrides some of the fields with the provided patterns. This is used when a pattern - /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start - /// with a `Fields` that is just one wildcard per field of the `Foo` struct, and override the - /// entry corresponding to `field1` with the pattern `Some(_)`. This is also used for slice - /// patterns for the same reason. - fn replace_fields_indexed(&self, new_pats: impl IntoIterator) -> Self { - let mut fields = self.clone(); + /// Clone this value. This method emphasizes that cloning loses reachability information and + /// should be done carefully. + pub(super) fn clone_and_forget_reachability(&self) -> Self { + DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty.clone()) + } - match &mut fields { - Fields::Vec(pats) => { - for (i, pat) in new_pats { - if let Some(p) = pats.get_mut(i) { - *p = pat; + pub(crate) fn from_pat(cx: &MatchCheckCtx<'_, 'p>, pat: &Pat) -> Self { + let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); + let ctor; + let fields; + match pat.kind.as_ref() { + 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(Interner) { + TyKind::Tuple(_, substs) => { + ctor = Single; + let mut wilds: SmallVec<[_; 2]> = substs + .iter(Interner) + .map(|arg| arg.assert_ty_ref(Interner).clone()) + .map(DeconstructedPat::wildcard) + .collect(); + for pat in subpatterns { + let idx: u32 = pat.field.into_raw().into(); + wilds[idx as usize] = mkpat(&pat.pattern); + } + fields = Fields::from_iter(cx, wilds) + } + TyKind::Adt(adt, substs) if adt_is_box(adt.0, cx) => { + // 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.iter().find(|pat| pat.field.into_raw() == 0u32.into()); + let field = if let Some(pat) = pat { + mkpat(&pat.pattern) + } else { + let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); + DeconstructedPat::wildcard(ty) + }; + ctor = Single; + fields = Fields::singleton(cx, field) + } + &TyKind::Adt(adt, _) => { + ctor = match pat.kind.as_ref() { + PatKind::Leaf { .. } => Single, + PatKind::Variant { enum_variant, .. } => Variant(*enum_variant), + _ => { + never!(); + Wildcard + } + }; + let variant = ctor.variant_id_for_adt(adt.0); + let fields_len = variant.variant_data(cx.db.upcast()).fields().len(); + // For each field in the variant, we store the relevant index into `self.fields` if any. + let mut field_id_to_id: Vec> = vec![None; fields_len]; + let tys = Fields::list_variant_nonhidden_fields(cx, &pat.ty, variant) + .enumerate() + .map(|(i, (fid, ty))| { + let field_idx: u32 = fid.into_raw().into(); + field_id_to_id[field_idx as usize] = Some(i); + ty + }); + let mut wilds: SmallVec<[_; 2]> = + tys.map(DeconstructedPat::wildcard).collect(); + for pat in subpatterns { + let field_idx: u32 = pat.field.into_raw().into(); + if let Some(i) = field_id_to_id[field_idx as usize] { + wilds[i] = mkpat(&pat.pattern); + } + } + fields = Fields::from_iter(cx, wilds); + } + _ => { + never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); + ctor = Wildcard; + fields = Fields::empty(); } } } + &PatKind::LiteralBool { value } => { + ctor = IntRange(IntRange::from_bool(value)); + fields = Fields::empty(); + } + PatKind::Or { .. } => { + ctor = Or; + let pats: SmallVec<[_; 2]> = expand_or_pat(pat).into_iter().map(mkpat).collect(); + fields = Fields::from_iter(cx, pats) + } } - fields + DeconstructedPat::new(ctor, fields, pat.ty.clone()) } - /// Replaces contained fields with the given list of patterns. There must be `len()` patterns - /// in `pats`. - pub(super) fn replace_fields( - &self, - cx: &MatchCheckCtx<'_>, - pats: impl IntoIterator, - ) -> Self { - let pats = pats.into_iter().map(|pat| cx.alloc_pat(pat)).collect(); + // // FIXME(iDawer): implement reporting of noncovered patterns + // pub(crate) fn to_pat(&self, _cx: &MatchCheckCtx<'_, 'p>) -> Pat { + // Pat { ty: self.ty.clone(), kind: PatKind::Wild.into() } + // } - match self { - Fields::Vec(_) => Fields::Vec(pats), - } + pub(super) fn is_or_pat(&self) -> bool { + matches!(self.ctor, Or) } - /// Replaces contained fields with the arguments of the given pattern. Only use on a pattern - /// that is compatible with the constructor used to build `self`. - /// This is meant to be used on the result of `Fields::wildcards()`. The idea is that - /// `wildcards` constructs a list of fields where all entries are wildcards, and the pattern - /// provided to this function fills some of the fields with non-wildcards. - /// In the following example `Fields::wildcards` would return `[_, _, _, _]`. If we call - /// `replace_with_pattern_arguments` on it with the pattern, the result will be `[Some(0), _, - /// _, _]`. - /// ```rust - /// let x: [Option; 4] = foo(); - /// match x { - /// [Some(0), ..] => {} - /// } - /// ``` - /// This is guaranteed to preserve the number of patterns in `self`. - pub(super) fn replace_with_pattern_arguments( - &self, - pat: PatId, - cx: &MatchCheckCtx<'_>, - ) -> Self { - // FIXME(iDawer): Factor out pattern deep cloning. See discussion: - // https://github.com/rust-analyzer/rust-analyzer/pull/8717#discussion_r633086640 - let mut arena = cx.pattern_arena.borrow_mut(); - match arena[pat].kind.as_ref() { - PatKind::Deref { subpattern } => { - assert_eq!(self.len(), 1); - let subpattern = subpattern.clone(); - Fields::from_single_pattern(arena.alloc(subpattern)) + pub(super) fn ctor(&self) -> &Constructor { + &self.ctor + } + + pub(super) fn ty(&self) -> &Ty { + &self.ty + } + + pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator> + 'a { + self.fields.iter_patterns() + } + + /// Specialize this pattern with a constructor. + /// `other_ctor` can be different from `self.ctor`, but must be covered by it. + pub(super) fn specialize<'a>( + &'a self, + cx: &MatchCheckCtx<'_, 'p>, + other_ctor: &Constructor, + ) -> SmallVec<[&'p DeconstructedPat<'p>; 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() } - PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { - let subpatterns = subpatterns.clone(); - let subpatterns = subpatterns - .iter() - .map(|field_pat| (field_pat.field, arena.alloc(field_pat.pattern.clone()))); - self.replace_with_fieldpats(subpatterns) + (Slice(self_slice), Slice(other_slice)) + if self_slice.arity() != other_slice.arity() => + { + unimplemented!() } - - PatKind::Wild - | PatKind::Binding { .. } - | PatKind::LiteralBool { .. } - | PatKind::Or { .. } => self.clone(), + _ => self.fields.iter_patterns().collect(), } } + + /// We keep track for each pattern if it was ever reachable during the analysis. This is used + /// with `unreachable_spans` to report unreachable subpatterns arising from or patterns. + pub(super) fn set_reachable(&self) { + self.reachable.set(true) + } + pub(super) fn is_reachable(&self) -> bool { + self.reachable.get() + } } -fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_>) -> bool { +fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_, '_>) -> bool { let attr_def_id = match variant_id { VariantId::EnumVariantId(id) => id.into(), VariantId::StructId(id) => id.into(), @@ -904,7 +1017,7 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_>) - cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists() } -fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_>) -> bool { +fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_, '_>) -> bool { use hir_def::lang_item::LangItemTarget; match cx.db.lang_item(cx.module.krate(), SmolStr::new_inline("owned_box")) { Some(LangItemTarget::StructId(box_id)) => adt == box_id.into(), diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index 43545708e5fe..f519f79c7628 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs @@ -1,5 +1,5 @@ -//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) -//! +//! Based on rust-lang/rust (last sync 68b76a483 2021-10-01) +//! //! //! ----- //! @@ -271,33 +271,26 @@ //! The details are not necessary to understand this file, so we explain them in //! [`super::deconstruct_pat`]. Splitting is done by the [`Constructor::split`] function. -use std::{cell::RefCell, iter::FromIterator}; +use std::iter::once; -use hir_def::{expr::ExprId, HasModule, ModuleId}; -use la_arena::Arena; -use once_cell::unsync::OnceCell; -use rustc_hash::FxHashMap; +use hir_def::{AdtId, HasModule, ModuleId}; use smallvec::{smallvec, SmallVec}; +use typed_arena::Arena; -use crate::{db::HirDatabase, InferenceResult, Interner, Ty}; +use crate::{db::HirDatabase, Ty, TyExt}; -use super::{ - deconstruct_pat::{Constructor, Fields, SplitWildcard}, - Pat, PatId, PatKind, PatternFoldable, PatternFolder, -}; +use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; -use self::{helper::PatIdExt, Usefulness::*, WitnessPreference::*}; +use self::{helper::Captures, ArmType::*, Usefulness::*}; -pub(crate) struct MatchCheckCtx<'a> { +pub(crate) struct MatchCheckCtx<'a, 'p> { pub(crate) module: ModuleId, - pub(crate) match_expr: ExprId, - pub(crate) infer: &'a InferenceResult, pub(crate) db: &'a dyn HirDatabase, /// Lowered patterns from arms plus generated by the check. - pub(crate) pattern_arena: &'a RefCell, + pub(crate) pattern_arena: &'p Arena>, } -impl<'a> MatchCheckCtx<'a> { +impl<'a, 'p> MatchCheckCtx<'a, 'p> { pub(super) fn is_uninhabited(&self, _ty: &Ty) -> bool { // FIXME(iDawer) implement exhaustive_patterns feature. More info in: // Tracking issue for RFC 1872: exhaustive_patterns feature https://github.com/rust-lang/rust/issues/51085 @@ -305,12 +298,16 @@ impl<'a> MatchCheckCtx<'a> { } /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - pub(super) fn is_foreign_non_exhaustive_enum(&self, enum_id: hir_def::EnumId) -> bool { - let has_non_exhaustive_attr = - self.db.attrs(enum_id.into()).by_key("non_exhaustive").exists(); - let is_local = - hir_def::AdtId::from(enum_id).module(self.db.upcast()).krate() == self.module.krate(); - has_non_exhaustive_attr && !is_local + pub(super) fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool { + match ty.as_adt() { + Some((adt @ AdtId::EnumId(_), _)) => { + let has_non_exhaustive_attr = + self.db.attrs(adt.into()).by_key("non_exhaustive").exists(); + let is_local = adt.module(self.db.upcast()).krate() == self.module.krate(); + has_non_exhaustive_attr && !is_local + } + _ => false, + } } // Rust feature described as "Allows exhaustive pattern matching on types that contain uninhabited types." @@ -318,92 +315,34 @@ impl<'a> MatchCheckCtx<'a> { // FIXME see MatchCheckCtx::is_uninhabited false } - - pub(super) fn alloc_pat(&self, pat: Pat) -> PatId { - self.pattern_arena.borrow_mut().alloc(pat) - } - - /// Get type of a pattern. Handles expanded patterns. - pub(super) fn type_of(&self, pat: PatId) -> Ty { - self.pattern_arena.borrow()[pat].ty.clone() - } } #[derive(Copy, Clone)] -pub(super) struct PatCtxt<'a> { - pub(super) cx: &'a MatchCheckCtx<'a>, +pub(super) struct PatCtxt<'a, 'p> { + pub(super) cx: &'a MatchCheckCtx<'a, 'p>, /// Type of the current column under investigation. pub(super) ty: &'a Ty, /// 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, -} - -pub(crate) fn expand_pattern(pat: Pat) -> Pat { - LiteralExpander.fold_pattern(&pat) -} - -struct LiteralExpander; - -impl PatternFolder for LiteralExpander { - fn fold_pattern(&mut self, pat: &Pat) -> Pat { - match (pat.ty.kind(Interner), pat.kind.as_ref()) { - (_, PatKind::Binding { subpattern: Some(s), .. }) => s.fold_with(self), - _ => pat.super_fold_with(self), - } - } -} - -impl Pat { - fn _is_wildcard(&self) -> bool { - matches!(*self.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) - } -} - -impl PatIdExt for PatId { - fn is_or_pat(self, cx: &MatchCheckCtx<'_>) -> bool { - matches!(*cx.pattern_arena.borrow()[self].kind, PatKind::Or { .. }) - } - - /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. - fn expand_or_pat(self, cx: &MatchCheckCtx<'_>) -> Vec { - fn expand(pat: PatId, vec: &mut Vec, pat_arena: &mut PatternArena) { - if let PatKind::Or { pats } = pat_arena[pat].kind.as_ref() { - // FIXME(iDawer): Factor out pattern deep cloning. See discussion: - // https://github.com/rust-analyzer/rust-analyzer/pull/8717#discussion_r633086640 - let pats = pats.clone(); - for pat in pats { - let pat = pat_arena.alloc(pat.clone()); - expand(pat, vec, pat_arena); - } - } else { - vec.push(pat) - } - } - - let mut pat_arena = cx.pattern_arena.borrow_mut(); - let mut pats = Vec::new(); - expand(self, &mut pats, &mut pat_arena); - pats - } + /// Wether the current pattern is from a `non_exhaustive` enum. + pub(super) is_non_exhaustive: bool, } /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. #[derive(Clone)] -pub(super) struct PatStack { - pats: SmallVec<[PatId; 2]>, - /// Cache for the constructor of the head - head_ctor: OnceCell, +pub(super) struct PatStack<'p> { + pats: SmallVec<[&'p DeconstructedPat<'p>; 2]>, } -impl PatStack { - fn from_pattern(pat: PatId) -> Self { +impl<'p> PatStack<'p> { + fn from_pattern(pat: &'p DeconstructedPat<'p>) -> Self { Self::from_vec(smallvec![pat]) } - fn from_vec(vec: SmallVec<[PatId; 2]>) -> Self { - PatStack { pats: vec, head_ctor: OnceCell::new() } + fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p>; 2]>) -> Self { + PatStack { pats: vec } } fn is_empty(&self) -> bool { @@ -414,73 +353,42 @@ impl PatStack { self.pats.len() } - fn head(&self) -> PatId { + fn head(&self) -> &'p DeconstructedPat<'p> { self.pats[0] } - #[inline] - fn head_ctor(&self, cx: &MatchCheckCtx<'_>) -> &Constructor { - self.head_ctor.get_or_init(|| Constructor::from_pat(cx, self.head())) - } - // 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(&self, cx: &MatchCheckCtx<'_>) -> impl Iterator + '_ { - self.head().expand_or_pat(cx).into_iter().map(move |pat| { + fn expand_or_pat(&self) -> impl Iterator> + Captures<'_> { + self.head().iter_fields().map(move |pat| { let mut new_patstack = PatStack::from_pattern(pat); new_patstack.pats.extend_from_slice(&self.pats[1..]); new_patstack }) } - /// This computes `S(self.head_ctor(), self)`. See top of the file for explanations. + /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations. /// /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. /// /// This is roughly the inverse of `Constructor::apply`. - fn pop_head_constructor( - &self, - ctor_wild_subpatterns: &Fields, - cx: &MatchCheckCtx<'_>, - ) -> PatStack { + fn pop_head_constructor(&self, cx: &MatchCheckCtx<'_, 'p>, ctor: &Constructor) -> PatStack<'p> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_fields = - ctor_wild_subpatterns.replace_with_pattern_arguments(self.head(), cx).into_patterns(); + let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } } -impl Default for PatStack { - fn default() -> Self { - Self::from_vec(smallvec![]) - } -} - -impl PartialEq for PatStack { - fn eq(&self, other: &Self) -> bool { - self.pats == other.pats - } -} - -impl FromIterator for PatStack { - fn from_iter(iter: T) -> Self - where - T: IntoIterator, - { - Self::from_vec(iter.into_iter().collect()) - } -} - /// A 2D matrix. #[derive(Clone)] -pub(super) struct Matrix { - patterns: Vec, +pub(super) struct Matrix<'p> { + patterns: Vec>, } -impl Matrix { +impl<'p> Matrix<'p> { fn empty() -> Self { Matrix { patterns: vec![] } } @@ -492,9 +400,9 @@ impl Matrix { /// 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, cx: &MatchCheckCtx<'_>) { - if !row.is_empty() && row.head().is_or_pat(cx) { - for row in row.expand_or_pat(cx) { + fn push(&mut self, row: PatStack<'p>) { + if !row.is_empty() && row.head().is_or_pat() { + for row in row.expand_or_pat() { self.patterns.push(row); } } else { @@ -503,284 +411,20 @@ impl Matrix { } /// Iterate over the first component of each row - fn heads(&self) -> impl Iterator + '_ { + fn heads(&self) -> impl Iterator> + Clone + Captures<'_> { self.patterns.iter().map(|r| r.head()) } - /// Iterate over the first constructor of each row. - fn head_ctors<'a>( - &'a self, - cx: &'a MatchCheckCtx<'_>, - ) -> impl Iterator + Clone { - self.patterns.iter().map(move |r| r.head_ctor(cx)) - } - /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor( - &self, - pcx: PatCtxt<'_>, - ctor: &Constructor, - ctor_wild_subpatterns: &Fields, - ) -> Matrix { - let rows = self - .patterns - .iter() - .filter(|r| ctor.is_covered_by(pcx, r.head_ctor(pcx.cx))) - .map(|r| r.pop_head_constructor(ctor_wild_subpatterns, pcx.cx)); - Matrix::from_iter(rows, pcx.cx) - } - - fn from_iter(rows: impl IntoIterator, cx: &MatchCheckCtx<'_>) -> Matrix { + fn specialize_constructor(&self, pcx: PatCtxt<'_, 'p>, ctor: &Constructor) -> Matrix<'p> { let mut matrix = Matrix::empty(); - for x in rows { - // Using `push` ensures we correctly expand or-patterns. - matrix.push(x, cx); - } - matrix - } -} - -/// Given a pattern or a pattern-stack, this struct captures a set of its subpatterns. We use that -/// to track reachable sub-patterns arising from or-patterns. In the absence of or-patterns this -/// will always be either `Empty` (the whole pattern is unreachable) or `Full` (the whole pattern -/// is reachable). When there are or-patterns, some subpatterns may be reachable while others -/// aren't. In this case the whole pattern still counts as reachable, but we will lint the -/// unreachable subpatterns. -/// -/// This supports a limited set of operations, so not all possible sets of subpatterns can be -/// represented. That's ok, we only want the ones that make sense for our usage. -/// -/// What we're doing is illustrated by this: -/// ``` -/// match (true, 0) { -/// (true, 0) => {} -/// (_, 1) => {} -/// (true | false, 0 | 1) => {} -/// } -/// ``` -/// When we try the alternatives of the `true | false` or-pattern, the last `0` is reachable in the -/// `false` alternative but not the `true`. So overall it is reachable. By contrast, the last `1` -/// is not reachable in either alternative, so we want to signal this to the user. -/// Therefore we take the union of sets of reachable patterns coming from different alternatives in -/// order to figure out which subpatterns are overall reachable. -/// -/// Invariant: we try to construct the smallest representation we can. In particular if -/// `self.is_empty()` we ensure that `self` is `Empty`, and same with `Full`. This is not important -/// for correctness currently. -#[derive(Debug, Clone)] -enum SubPatSet { - /// The empty set. This means the pattern is unreachable. - Empty, - /// The set containing the full pattern. - Full, - /// If the pattern is a pattern with a constructor or a pattern-stack, we store a set for each - /// of its subpatterns. Missing entries in the map are implicitly full, because that's the - /// common case. - Seq { subpats: FxHashMap }, - /// If the pattern is an or-pattern, we store a set for each of its alternatives. Missing - /// entries in the map are implicitly empty. Note: we always flatten nested or-patterns. - Alt { - subpats: FxHashMap, - /// Counts the total number of alternatives in the pattern - alt_count: usize, - /// We keep the pattern around to retrieve spans. - pat: PatId, - }, -} - -impl SubPatSet { - fn full() -> Self { - SubPatSet::Full - } - - fn empty() -> Self { - SubPatSet::Empty - } - - fn is_empty(&self) -> bool { - match self { - SubPatSet::Empty => true, - SubPatSet::Full => false, - // If any subpattern in a sequence is unreachable, the whole pattern is unreachable. - SubPatSet::Seq { subpats } => subpats.values().any(|set| set.is_empty()), - // An or-pattern is reachable if any of its alternatives is. - SubPatSet::Alt { subpats, .. } => subpats.values().all(|set| set.is_empty()), - } - } - - fn is_full(&self) -> bool { - match self { - SubPatSet::Empty => false, - SubPatSet::Full => true, - // The whole pattern is reachable only when all its alternatives are. - SubPatSet::Seq { subpats } => subpats.values().all(|sub_set| sub_set.is_full()), - // The whole or-pattern is reachable only when all its alternatives are. - SubPatSet::Alt { subpats, alt_count, .. } => { - subpats.len() == *alt_count && subpats.values().all(|set| set.is_full()) + for row in &self.patterns { + if ctor.is_covered_by(pcx, row.head().ctor()) { + let new_row = row.pop_head_constructor(pcx.cx, ctor); + matrix.push(new_row); } } - } - - /// Union `self` with `other`, mutating `self`. - fn union(&mut self, other: Self) { - use SubPatSet::*; - // Union with full stays full; union with empty changes nothing. - if self.is_full() || other.is_empty() { - return; - } else if self.is_empty() { - *self = other; - return; - } else if other.is_full() { - *self = Full; - return; - } - - match (&mut *self, other) { - (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { - s_set.retain(|i, s_sub_set| { - // Missing entries count as full. - let o_sub_set = o_set.remove(i).unwrap_or(Full); - s_sub_set.union(o_sub_set); - // We drop full entries. - !s_sub_set.is_full() - }); - // Everything left in `o_set` is missing from `s_set`, i.e. counts as full. Since - // unioning with full returns full, we can drop those entries. - } - (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { - s_set.retain(|i, s_sub_set| { - // Missing entries count as empty. - let o_sub_set = o_set.remove(i).unwrap_or(Empty); - s_sub_set.union(o_sub_set); - // We drop empty entries. - !s_sub_set.is_empty() - }); - // Everything left in `o_set` is missing from `s_set`, i.e. counts as empty. Since - // unioning with empty changes nothing, we can take those entries as is. - s_set.extend(o_set); - } - _ => panic!("bug"), - } - - if self.is_full() { - *self = Full; - } - } - - /// Returns a list of the unreachable subpatterns. If `self` is empty (i.e. the - /// whole pattern is unreachable) we return `None`. - fn list_unreachable_subpatterns(&self, cx: &MatchCheckCtx<'_>) -> Option> { - /// Panics if `set.is_empty()`. - fn fill_subpats( - set: &SubPatSet, - unreachable_pats: &mut Vec, - cx: &MatchCheckCtx<'_>, - ) { - match set { - SubPatSet::Empty => panic!("bug"), - SubPatSet::Full => {} - SubPatSet::Seq { subpats } => { - for sub_set in subpats.values() { - fill_subpats(sub_set, unreachable_pats, cx); - } - } - SubPatSet::Alt { subpats, pat, alt_count, .. } => { - let expanded = pat.expand_or_pat(cx); - for (i, &expanded) in expanded.iter().enumerate().take(*alt_count) { - let sub_set = subpats.get(&i).unwrap_or(&SubPatSet::Empty); - if sub_set.is_empty() { - // Found an unreachable subpattern. - unreachable_pats.push(expanded); - } else { - fill_subpats(sub_set, unreachable_pats, cx); - } - } - } - } - } - - if self.is_empty() { - return None; - } - if self.is_full() { - // No subpatterns are unreachable. - return Some(Vec::new()); - } - let mut unreachable_pats = Vec::new(); - fill_subpats(self, &mut unreachable_pats, cx); - Some(unreachable_pats) - } - - /// When `self` refers to a patstack that was obtained from specialization, after running - /// `unspecialize` it will refer to the original patstack before specialization. - fn unspecialize(self, arity: usize) -> Self { - use SubPatSet::*; - match self { - Full => Full, - Empty => Empty, - Seq { subpats } => { - // We gather the first `arity` subpatterns together and shift the remaining ones. - let mut new_subpats = FxHashMap::default(); - let mut new_subpats_first_col = FxHashMap::default(); - for (i, sub_set) in subpats { - if i < arity { - // The first `arity` indices are now part of the pattern in the first - // column. - new_subpats_first_col.insert(i, sub_set); - } else { - // Indices after `arity` are simply shifted - new_subpats.insert(i - arity + 1, sub_set); - } - } - // If `new_subpats_first_col` has no entries it counts as full, so we can omit it. - if !new_subpats_first_col.is_empty() { - new_subpats.insert(0, Seq { subpats: new_subpats_first_col }); - } - Seq { subpats: new_subpats } - } - Alt { .. } => panic!("bug"), // `self` is a patstack - } - } - - /// When `self` refers to a patstack that was obtained from splitting an or-pattern, after - /// running `unspecialize` it will refer to the original patstack before splitting. - /// - /// For example: - /// ``` - /// match Some(true) { - /// Some(true) => {} - /// None | Some(true | false) => {} - /// } - /// ``` - /// Here `None` would return the full set and `Some(true | false)` would return the set - /// containing `false`. After `unsplit_or_pat`, we want the set to contain `None` and `false`. - /// This is what this function does. - fn unsplit_or_pat(mut self, alt_id: usize, alt_count: usize, pat: PatId) -> Self { - use SubPatSet::*; - if self.is_empty() { - return Empty; - } - - // Subpatterns coming from inside the or-pattern alternative itself, e.g. in `None | Some(0 - // | 1)`. - let set_first_col = match &mut self { - Full => Full, - Seq { subpats } => subpats.remove(&0).unwrap_or(Full), - Empty => unreachable!(), - Alt { .. } => panic!("bug"), // `self` is a patstack - }; - let mut subpats_first_col = FxHashMap::default(); - subpats_first_col.insert(alt_id, set_first_col); - let set_first_col = Alt { subpats: subpats_first_col, pat, alt_count }; - - let mut subpats = match self { - Full => FxHashMap::default(), - Seq { subpats } => subpats, - Empty => unreachable!(), - Alt { .. } => panic!("bug"), // `self` is a patstack - }; - subpats.insert(0, set_first_col); - Seq { subpats } + matrix } } @@ -789,31 +433,34 @@ impl SubPatSet { /// of potential unreachable sub-patterns (in the presence of or-patterns). When checking /// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of /// witnesses of non-exhaustiveness when there are any. -/// Which variant to use is dictated by `WitnessPreference`. -#[derive(Clone, Debug)] -enum Usefulness { - /// Carries a set of subpatterns that have been found to be reachable. If empty, this indicates - /// the whole pattern is unreachable. If not, this indicates that the pattern is reachable but - /// that some sub-patterns may be unreachable (due to or-patterns). In the absence of - /// or-patterns this will always be either `Empty` (the whole pattern is unreachable) or `Full` - /// (the whole pattern is reachable). - NoWitnesses(SubPatSet), +/// Which variant to use is dictated by `ArmType`. +enum Usefulness<'p> { + /// 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 Usefulness { - fn new_useful(preference: WitnessPreference) -> Self { +impl<'p> Usefulness<'p> { + fn new_useful(preference: ArmType) -> Self { match preference { - ConstructWitness => WithWitnesses(vec![Witness(vec![])]), - LeaveOutWitness => NoWitnesses(SubPatSet::full()), + // A single (empty) witness of reachability. + FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]), + RealArm => NoWitnesses { useful: true }, } } - fn new_not_useful(preference: WitnessPreference) -> Self { + fn new_not_useful(preference: ArmType) -> Self { match preference { - ConstructWitness => WithWitnesses(vec![]), - LeaveOutWitness => NoWitnesses(SubPatSet::empty()), + FakeExtraWildcard => WithWitnesses(vec![]), + RealArm => NoWitnesses { useful: false }, + } + } + + fn is_useful(&self) -> bool { + match self { + Usefulness::NoWitnesses { useful } => *useful, + Usefulness::WithWitnesses(witnesses) => !witnesses.is_empty(), } } @@ -823,89 +470,78 @@ impl Usefulness { (WithWitnesses(_), WithWitnesses(o)) if o.is_empty() => {} (WithWitnesses(s), WithWitnesses(o)) if s.is_empty() => *self = WithWitnesses(o), (WithWitnesses(s), WithWitnesses(o)) => s.extend(o), - (NoWitnesses(s), NoWitnesses(o)) => s.union(o), - _ => unreachable!(), - } - } - - /// When trying several branches and each returns a `Usefulness`, we need to combine the - /// results together. - fn merge(pref: WitnessPreference, usefulnesses: impl Iterator) -> Self { - let mut ret = Self::new_not_useful(pref); - for u in usefulnesses { - ret.extend(u); - if let NoWitnesses(subpats) = &ret { - if subpats.is_full() { - // Once we reach the full set, more unions won't change the result. - return ret; - } + (NoWitnesses { useful: s_useful }, NoWitnesses { useful: o_useful }) => { + *s_useful = *s_useful || o_useful } - } - ret - } - - /// After calculating the usefulness for a branch of an or-pattern, call this to make this - /// usefulness mergeable with those from the other branches. - fn unsplit_or_pat(self, alt_id: usize, alt_count: usize, pat: PatId) -> Self { - match self { - NoWitnesses(subpats) => NoWitnesses(subpats.unsplit_or_pat(alt_id, alt_count, pat)), - WithWitnesses(_) => panic!("bug"), + _ => unreachable!(), } } - /// After calculating usefulness after a specialization, call this to recontruct a usefulness + /// After calculating usefulness after a specialization, call this to reconstruct a usefulness /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged /// with the results of specializing with the other constructors. fn apply_constructor( self, - pcx: PatCtxt<'_>, - matrix: &Matrix, + pcx: PatCtxt<'_, 'p>, + matrix: &Matrix<'p>, ctor: &Constructor, - ctor_wild_subpatterns: &Fields, ) -> Self { match self { - WithWitnesses(witnesses) if witnesses.is_empty() => WithWitnesses(witnesses), + NoWitnesses { .. } => self, + WithWitnesses(ref witnesses) if witnesses.is_empty() => self, WithWitnesses(witnesses) => { - let new_witnesses = if matches!(ctor, Constructor::Missing) { - let mut split_wildcard = SplitWildcard::new(pcx); - split_wildcard.split(pcx, matrix.head_ctors(pcx.cx)); - // 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 new_patterns: Vec<_> = split_wildcard - .iter_missing(pcx) - .map(|missing_ctor| { - Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor) - }) - .collect(); + let new_witnesses = if let Constructor::Missing { .. } = ctor { + // We got the special `Missing` constructor, so each of the missing constructors + // gives a new pattern that is not caught by the match. We list those patterns. + let new_patterns = if pcx.is_non_exhaustive { + // Here we don't want the user to try to list all variants, we want them to add + // a wildcard, so we only suggest that. + vec![DeconstructedPat::wildcard(pcx.ty.clone())] + } else { + 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) + .cloned() + .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) + .collect() + }; + witnesses .into_iter() .flat_map(|witness| { new_patterns.iter().map(move |pat| { - let mut witness = witness.clone(); - witness.0.push(pat.clone()); - witness + Witness( + witness + .0 + .iter() + .chain(once(pat)) + .map(DeconstructedPat::clone_and_forget_reachability) + .collect(), + ) }) }) .collect() } else { witnesses .into_iter() - .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns)) + .map(|witness| witness.apply_constructor(pcx, ctor)) .collect() }; WithWitnesses(new_witnesses) } - NoWitnesses(subpats) => NoWitnesses(subpats.unspecialize(ctor_wild_subpatterns.len())), } } } #[derive(Copy, Clone, Debug)] -enum WitnessPreference { - ConstructWitness, - LeaveOutWitness, +enum ArmType { + FakeExtraWildcard, + RealArm, } /// A witness of non-exhaustiveness for error reporting, represented @@ -941,12 +577,11 @@ enum WitnessPreference { /// `Witness(vec![Pair(Some(_), true)])` /// /// The final `Pair(Some(_), true)` is then the resulting witness. -#[derive(Clone, Debug)] -pub(crate) struct Witness(Vec); +pub(crate) struct Witness<'p>(Vec>); -impl Witness { +impl<'p> Witness<'p> { /// Asserts that the witness contains a single pattern, and returns it. - fn single_pattern(self) -> Pat { + fn single_pattern(self) -> DeconstructedPat<'p> { assert_eq!(self.0.len(), 1); self.0.into_iter().next().unwrap() } @@ -964,17 +599,13 @@ impl Witness { /// /// 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<'_>, - ctor: &Constructor, - ctor_wild_subpatterns: &Fields, - ) -> Self { + fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p>, ctor: &Constructor) -> Self { let pat = { let len = self.0.len(); - let arity = ctor_wild_subpatterns.len(); + let arity = ctor.arity(pcx); let pats = self.0.drain((len - arity)..).rev(); - ctor_wild_subpatterns.replace_fields(pcx.cx, pats).apply(pcx, ctor) + let fields = Fields::from_iter(pcx.cx, pats); + DeconstructedPat::new(ctor.clone(), fields, pcx.ty.clone()) }; self.0.push(pat); @@ -1005,14 +636,14 @@ impl Witness { /// `is_under_guard` is used to inform if the pattern has a guard. If it /// has one it must not be inserted into the matrix. This shouldn't be /// relied on for soundness. -fn is_useful( - cx: &MatchCheckCtx<'_>, - matrix: &Matrix, - v: &PatStack, - witness_preference: WitnessPreference, +fn is_useful<'p>( + cx: &MatchCheckCtx<'_, 'p>, + matrix: &Matrix<'p>, + v: &PatStack<'p>, + witness_preference: ArmType, is_under_guard: bool, is_top_level: bool, -) -> Usefulness { +) -> Usefulness<'p> { let Matrix { patterns: rows, .. } = matrix; // The base case. We are pattern-matching on () and the return value is @@ -1031,67 +662,60 @@ fn is_useful( assert!(rows.iter().all(|r| r.len() == v.len())); - // FIXME(Nadrieril): Hack to work around type normalization issues (see rust-lang/rust#72476). - let ty = matrix.heads().next().map_or(cx.type_of(v.head()), |r| cx.type_of(r)); - let pcx = PatCtxt { cx, ty: &ty, is_top_level }; + let ty = v.head().ty(); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); + let pcx = PatCtxt { cx, ty, is_top_level, is_non_exhaustive }; // If the first pattern is an or-pattern, expand it. - let ret = if v.head().is_or_pat(cx) { - //expanding or-pattern - let v_head = v.head(); - let vs: Vec<_> = v.expand_or_pat(cx).collect(); - let alt_count = vs.len(); + let mut ret = Usefulness::new_not_useful(witness_preference); + if v.head().is_or_pat() { // We try each or-pattern branch in turn. let mut matrix = matrix.clone(); - let usefulnesses = vs.into_iter().enumerate().map(|(i, v)| { + for v in v.expand_or_pat() { let usefulness = is_useful(cx, &matrix, &v, witness_preference, is_under_guard, false); + ret.extend(usefulness); // If pattern has a guard don't add it to the matrix. if !is_under_guard { // We push the already-seen patterns into the matrix in order to detect redundant // branches like `Some(_) | Some(0)`. - matrix.push(v, cx); + matrix.push(v); } - usefulness.unsplit_or_pat(i, alt_count, v_head) - }); - Usefulness::merge(witness_preference, usefulnesses) + } } else { - let v_ctor = v.head_ctor(cx); - // if let Constructor::IntRange(ctor_range) = v_ctor { - // // Lint on likely incorrect range patterns (#63987) - // ctor_range.lint_overlapping_range_endpoints( - // pcx, - // matrix.head_ctors_and_spans(cx), - // matrix.column_count().unwrap_or(0), - // hir_id, - // ) - // } + let v_ctor = v.head().ctor(); + + // FIXME: implement `overlapping_range_endpoints` lint // We split the head constructor of `v`. - let split_ctors = v_ctor.split(pcx, matrix.head_ctors(cx)); + let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); // For each constructor, we compute whether there's a value that starts with it that would // witness the usefulness of `v`. let start_matrix = matrix; - let usefulnesses = split_ctors.into_iter().map(|ctor| { - // debug!("specialize({:?})", ctor); + for ctor in split_ctors { // We cache the result of `Fields::wildcards` because it is used a lot. - let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor); - let spec_matrix = - start_matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns); - let v = v.pop_head_constructor(&ctor_wild_subpatterns, cx); + 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, is_under_guard, false); - usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns) - }); - Usefulness::merge(witness_preference, usefulnesses) + let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); + + // FIXME: implement `non_exhaustive_omitted_patterns` lint + + ret.extend(usefulness); + } }; + if ret.is_useful() { + v.head().set_reachable(); + } + ret } /// The arm of a match expression. #[derive(Clone, Copy)] -pub(crate) struct MatchArm { - pub(crate) pat: PatId, +pub(crate) struct MatchArm<'p> { + pub(crate) pat: &'p DeconstructedPat<'p>, pub(crate) has_guard: bool, } @@ -1101,18 +725,19 @@ pub(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), + // FIXME: store ureachable subpattern IDs + Reachable, /// The arm is unreachable. Unreachable, } /// The output of checking a match for exhaustiveness and arm reachability. -pub(crate) struct UsefulnessReport { +pub(crate) struct UsefulnessReport<'p> { /// For each arm of the input, whether that arm is reachable after the arms above it. - pub(crate) _arm_usefulness: Vec<(MatchArm, Reachability)>, + pub(crate) _arm_usefulness: Vec<(MatchArm<'p>, Reachability)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. - pub(crate) non_exhaustiveness_witnesses: Vec, + pub(crate) non_exhaustiveness_witnesses: Vec>, } /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which @@ -1120,53 +745,41 @@ pub(crate) struct UsefulnessReport { /// /// Note: the input patterns must have been lowered through /// `check_match::MatchVisitor::lower_pattern`. -pub(crate) fn compute_match_usefulness( - cx: &MatchCheckCtx<'_>, - arms: &[MatchArm], -) -> UsefulnessReport { +pub(crate) fn compute_match_usefulness<'p>( + cx: &MatchCheckCtx<'_, 'p>, + arms: &[MatchArm<'p>], + scrut_ty: &Ty, +) -> UsefulnessReport<'p> { let mut matrix = Matrix::empty(); let arm_usefulness = arms .iter() .copied() .map(|arm| { let v = PatStack::from_pattern(arm.pat); - let usefulness = is_useful(cx, &matrix, &v, LeaveOutWitness, arm.has_guard, true); + is_useful(cx, &matrix, &v, RealArm, arm.has_guard, true); if !arm.has_guard { - matrix.push(v, cx); + matrix.push(v); } - let reachability = match usefulness { - NoWitnesses(subpats) if subpats.is_empty() => Reachability::Unreachable, - NoWitnesses(subpats) => { - Reachability::Reachable(subpats.list_unreachable_subpatterns(cx).unwrap()) - } - WithWitnesses(..) => panic!("bug"), + let reachability = if arm.pat.is_reachable() { + Reachability::Reachable + } else { + Reachability::Unreachable }; (arm, reachability) }) .collect(); - let wild_pattern = - cx.pattern_arena.borrow_mut().alloc(Pat::wildcard_from_ty(cx.infer[cx.match_expr].clone())); + let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty.clone())); let v = PatStack::from_pattern(wild_pattern); - let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, false, true); + let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, false, true); let non_exhaustiveness_witnesses = match usefulness { WithWitnesses(pats) => pats.into_iter().map(Witness::single_pattern).collect(), - NoWitnesses(_) => panic!("bug"), + NoWitnesses { .. } => panic!("bug"), }; UsefulnessReport { _arm_usefulness: arm_usefulness, non_exhaustiveness_witnesses } } -pub(crate) type PatternArena = Arena; - -mod helper { - use super::MatchCheckCtx; - - pub(super) trait PatIdExt: Sized { - // fn is_wildcard(self, cx: &MatchCheckCtx<'_>) -> bool; - fn is_or_pat(self, cx: &MatchCheckCtx<'_>) -> bool; - fn expand_or_pat(self, cx: &MatchCheckCtx<'_>) -> Vec; - } - +pub(crate) mod helper { // Copy-pasted from rust/compiler/rustc_data_structures/src/captures.rs /// "Signaling" trait used in impl trait to tag lifetimes that you may /// need to capture but don't really need for other reasons. diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs index 947b0f2e2e62..5a3570469700 100644 --- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs @@ -821,7 +821,6 @@ fn main() { #[test] fn pattern_type_is_of_substitution() { - cov_mark::check!(match_check_wildcard_expanded_to_substitutions); check_diagnostics_no_bails( r#" struct Foo(T); From 1280961b51fb0b80867758dc5062bca3b9f0f6fe Mon Sep 17 00:00:00 2001 From: iDawer Date: Sun, 21 Nov 2021 18:15:21 +0500 Subject: [PATCH 2/4] internal: sync match checking with rust-lang/rust f31622a50 2021-11-12 --- crates/hir_ty/src/diagnostics/expr.rs | 2 +- .../match_check/deconstruct_pat.rs | 53 +++++++++++-------- .../src/diagnostics/match_check/usefulness.rs | 36 +++++++++---- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 04a0125fd772..00c7b952157a 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs @@ -335,7 +335,7 @@ impl ExprValidator { let report = compute_match_usefulness(&cx, &m_arms, match_expr_ty); // FIXME Report unreacheble arms - // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201 + // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200 let witnesses = report.non_exhaustiveness_witnesses; // FIXME Report witnesses diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index 218b48680fa4..acb3280329f5 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -332,6 +332,14 @@ impl Constructor { } } + pub(super) fn is_unstable_variant(&self, _pcx: PatCtxt<'_, '_>) -> bool { + false //FIXME: implement this + } + + pub(super) fn is_doc_hidden_variant(&self, _pcx: PatCtxt<'_, '_>) -> bool { + false //FIXME: implement this + } + fn variant_id_for_adt(&self, adt: hir_def::AdtId) -> VariantId { match *self { Variant(id) => id.into(), @@ -556,32 +564,33 @@ impl SplitWildcard { // witness. let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); + let is_exhaustive_pat_feature = cx.feature_exhaustive_patterns(); + // 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 = enum_data.variants.is_empty() - && !cx.feature_exhaustive_patterns() + && !is_exhaustive_pat_feature && !pcx.is_top_level; - if is_secretly_empty { - smallvec![NonExhaustive] - } else if is_declared_nonexhaustive { - enum_data - .variants - .iter() - .map(|(local_id, ..)| Variant(EnumVariantId { parent: enum_id, local_id })) - .chain(Some(NonExhaustive)) - .collect() - } else if cx.feature_exhaustive_patterns() { - unimplemented!() // see MatchCheckCtx.feature_exhaustive_patterns() - } else { - enum_data - .variants - .iter() - .map(|(local_id, ..)| Variant(EnumVariantId { parent: enum_id, local_id })) - .collect() + let mut ctors: SmallVec<[_; 1]> = enum_data + .variants + .iter() + .filter(|&(_, _v)| { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + let is_uninhabited = is_exhaustive_pat_feature + && unimplemented!("after MatchCheckCtx.feature_exhaustive_patterns()"); + !is_uninhabited + }) + .map(|(local_id, _)| Variant(EnumVariantId { parent: enum_id, local_id })) + .collect(); + + if is_secretly_empty || is_declared_nonexhaustive { + ctors.push(NonExhaustive); } + ctors } TyKind::Scalar(Scalar::Char) => unhandled(), TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), @@ -661,9 +670,7 @@ impl SplitWildcard { Missing { nonexhaustive_enum_missing_real_variants: self .iter_missing(pcx) - .filter(|c| !c.is_non_exhaustive()) - .next() - .is_some(), + .any(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx))), } } else { Missing { nonexhaustive_enum_missing_real_variants: false } @@ -820,9 +827,9 @@ impl<'p> Fields<'p> { /// Values and patterns can be represented as a constructor applied to some fields. This represents /// a pattern in this form. -/// This also keeps track of whether the pattern has been foundreachable during analysis. For this +/// This also keeps track of whether the pattern has been found reachable 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. +/// `clone_and_forget_reachability` if you're sure. pub(crate) struct DeconstructedPat<'p> { ctor: Constructor, fields: Fields<'p>, diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index f519f79c7628..9b00c4dccf30 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs @@ -1,5 +1,5 @@ -//! Based on rust-lang/rust (last sync 68b76a483 2021-10-01) -//! +//! Based on rust-lang/rust (last sync f31622a50 2021-11-12) +//! //! //! ----- //! @@ -402,9 +402,7 @@ impl<'p> Matrix<'p> { /// expands it. fn push(&mut self, row: PatStack<'p>) { if !row.is_empty() && row.head().is_or_pat() { - for row in row.expand_or_pat() { - self.patterns.push(row); - } + self.patterns.extend(row.expand_or_pat()); } else { self.patterns.push(row); } @@ -500,15 +498,33 @@ impl<'p> Usefulness<'p> { } 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)` or they are unstable feature gate (only stdlib types). + let mut hide_variant_show_wild = 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_map(|missing_ctor| { + // Check if this variant is marked `doc(hidden)` + if missing_ctor.is_doc_hidden_variant(pcx) + || missing_ctor.is_unstable_variant(pcx) + { + hide_variant_show_wild = true; + return None; + } + Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())) + }) + .collect(); + + if hide_variant_show_wild { + new.push(DeconstructedPat::wildcard(pcx.ty.clone())) + } + + new }; witnesses @@ -660,7 +676,7 @@ fn is_useful<'p>( return ret; } - assert!(rows.iter().all(|r| r.len() == v.len())); + debug_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); From b17aefb83a09e9e298d560f9a270b7ca89790203 Mon Sep 17 00:00:00 2001 From: iDawer Date: Sat, 27 Nov 2021 18:13:47 +0500 Subject: [PATCH 3/4] internal: Normalize field type after substituting --- crates/hir_ty/src/diagnostics/expr.rs | 1 + .../match_check/deconstruct_pat.rs | 4 ++-- .../src/diagnostics/match_check/usefulness.rs | 3 ++- crates/hir_ty/src/infer.rs | 22 ++++++++++++++++- .../src/handlers/missing_match_arms.rs | 24 +++++++++++++++++++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 00c7b952157a..a8c4026e31f3 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs @@ -285,6 +285,7 @@ impl ExprValidator { let pattern_arena = Arena::new(); let cx = MatchCheckCtx { module: self.owner.module(db.upcast()), + body: self.owner, db, pattern_arena: &pattern_arena, }; diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index acb3280329f5..84ded517ba7a 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -53,7 +53,7 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use syntax::SmolStr; -use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; +use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind}; use super::{ usefulness::{helper::Captures, MatchCheckCtx, PatCtxt}, @@ -753,8 +753,8 @@ impl<'p> Fields<'p> { let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { - // TODO check ty has been normalized let ty = field_ty[fid].clone().substitute(Interner, substs); + let ty = normalize(cx.db, cx.body, ty); let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) || visibility[fid].is_visible_from(cx.db.upcast(), cx.module); let is_uninhabited = cx.is_uninhabited(&ty); diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index 9b00c4dccf30..e8a13955d21c 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs @@ -273,7 +273,7 @@ use std::iter::once; -use hir_def::{AdtId, HasModule, ModuleId}; +use hir_def::{AdtId, DefWithBodyId, HasModule, ModuleId}; use smallvec::{smallvec, SmallVec}; use typed_arena::Arena; @@ -285,6 +285,7 @@ use self::{helper::Captures, ArmType::*, Usefulness::*}; pub(crate) struct MatchCheckCtx<'a, 'p> { pub(crate) module: ModuleId, + pub(crate) body: DefWithBodyId, pub(crate) db: &'a dyn HirDatabase, /// Lowered patterns from arms plus generated by the check. pub(crate) pattern_arena: &'p Arena>, diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 1bc19323da97..95d9a6735a34 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -16,7 +16,7 @@ use std::ops::Index; use std::sync::Arc; -use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar}; +use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use hir_def::{ body::Body, data::{ConstData, FunctionData, StaticData}, @@ -70,6 +70,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc Ty { + if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) { + return ty; + } + let krate = owner.module(db.upcast()).krate(); + let trait_env = owner + .as_generic_def_id() + .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); + let mut table = unify::InferenceTable::new(db, trait_env.clone()); + + let ty_with_vars = table.normalize_associated_types_in(ty); + table.resolve_obligations_as_possible(); + table.propagate_diverging_flag(); + table.resolve_completely(ty_with_vars) +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] enum ExprOrPatId { ExprId(ExprId), diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs index 5a3570469700..5ef4d2a22666 100644 --- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs @@ -863,6 +863,30 @@ fn main() { ); } + #[test] + fn normalize_field_ty() { + check_diagnostics_no_bails( + r" +trait Trait { type Projection; } +enum E {Foo, Bar} +struct A; +impl Trait for A { type Projection = E; } +struct Next(T::Projection); +static __: () = { + let n: Next = Next(E::Foo); + match n { Next(E::Foo) => {} } + // ^ error: missing match arm + match n { Next(E::Foo | E::Bar) => {} } + match n { Next(E::Foo | _ ) => {} } + match n { Next(_ | E::Bar) => {} } + match n { _ | Next(E::Bar) => {} } + match &n { Next(E::Foo | E::Bar) => {} } + match &n { _ | Next(E::Bar) => {} } +};", + + ); + } + mod false_negatives { //! The implementation of match checking here is a work in progress. As we roll this out, we //! prefer false negatives to false positives (ideally there would be no false positives). This From a9ad7be748d4bd146f05ae4dd9cbb0acb9431440 Mon Sep 17 00:00:00 2001 From: iDawer Date: Sun, 12 Dec 2021 14:24:10 +0500 Subject: [PATCH 4/4] Respect binding mode of a binding pattern for exhaustiveness check --- crates/hir_ty/src/diagnostics/match_check.rs | 19 +++++++++++++++---- crates/hir_ty/src/infer.rs | 3 ++- crates/hir_ty/src/infer/pat.rs | 2 ++ .../src/handlers/missing_match_arms.rs | 13 +++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index fdf448f5e119..87a85797613b 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs @@ -11,8 +11,11 @@ pub(crate) mod deconstruct_pat; pub(crate) mod usefulness; use hir_def::{body::Body, expr::PatId, EnumVariantId, LocalFieldId, VariantId}; +use stdx::never; -use crate::{db::HirDatabase, InferenceResult, Interner, Substitution, Ty, TyKind}; +use crate::{ + db::HirDatabase, infer::BindingMode, InferenceResult, Interner, Substitution, Ty, TyKind, +}; use self::pat_util::EnumerateAndAdjustIterator; @@ -21,6 +24,7 @@ pub(crate) use self::usefulness::MatchArm; #[derive(Clone, Debug)] pub(crate) enum PatternError { Unimplemented, + UnexpectedType, UnresolvedVariant, MissingField, ExtraFields, @@ -129,9 +133,16 @@ impl<'a> PatCtxt<'a> { PatKind::Leaf { subpatterns } } - hir_def::expr::Pat::Bind { subpat, .. } => { - if let TyKind::Ref(.., rty) = ty.kind(Interner) { - ty = rty; + hir_def::expr::Pat::Bind { ref name, subpat, .. } => { + let bm = self.infer.pat_binding_modes[&pat]; + match (bm, ty.kind(Interner)) { + (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty, + (BindingMode::Ref(_), _) => { + never!("`ref {}` has wrong type {:?}", name, ty); + self.errors.push(PatternError::UnexpectedType); + return Pat { ty: ty.clone(), kind: PatKind::Wild.into() }; + } + _ => (), } PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) } } diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 95d9a6735a34..54c3590f04bc 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -100,7 +100,7 @@ impl_from!(ExprId, PatId for ExprOrPatId); /// Binding modes inferred for patterns. /// #[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum BindingMode { +pub enum BindingMode { Move, Ref(Mutability), } @@ -292,6 +292,7 @@ pub struct InferenceResult { standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap>, + pub pat_binding_modes: FxHashMap, pub expr_adjustments: FxHashMap>, } diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 64e72abf0f3f..50fd2dd74949 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs @@ -204,6 +204,8 @@ impl<'a> InferenceContext<'a> { } else { BindingMode::convert(*mode) }; + self.result.pat_binding_modes.insert(pat, mode); + let inner_ty = match subpat { Some(subpat) => self.infer_pat(*subpat, &expected, default_bm), None => expected, diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs index 5ef4d2a22666..6e2764e59ff8 100644 --- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs @@ -883,7 +883,20 @@ static __: () = { match &n { Next(E::Foo | E::Bar) => {} } match &n { _ | Next(E::Bar) => {} } };", + ); + } + #[test] + fn binding_mode_by_ref() { + check_diagnostics_no_bails( + r" +enum E{ A, B } +fn foo() { + match &E::A { + E::A => {} + x => {} + } +}", ); }