From 0a8c30a96fe09047da07a8e2980baa47a334a3d7 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Wed, 2 Jun 2021 01:29:07 +0500 Subject: [PATCH] internal: implement pattern adjustments. --- crates/hir_ty/src/diagnostics/match_check.rs | 28 ++++++++++++++++++-- crates/hir_ty/src/infer.rs | 2 ++ crates/hir_ty/src/infer/pat.rs | 7 +++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index a9a99f57a24..c8e1b23deee 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs @@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> { } pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { - // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") + // 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. + // Pattern adjustment is part of RFC 2005-match-ergonomics. // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 let unadjusted_pat = self.lower_pattern_unadjusted(pat); - unadjusted_pat + self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( + unadjusted_pat, + |subpattern, ref_ty| Pat { + ty: ref_ty.clone(), + kind: Box::new(PatKind::Deref { subpattern }), + }, + ) } fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { @@ -1236,6 +1245,21 @@ fn main(f: Foo) { ); } + #[test] + fn match_ergonomics_issue_9095() { + check_diagnostics( + r#" +enum Foo { A(T) } +fn main() { + match &Foo::A(true) { + _ => {} + Foo::A(_) => {} + } +} +"#, + ); + } + 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 diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 7a4268819b4..0e9f777dab0 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -150,6 +150,8 @@ pub struct InferenceResult { type_mismatches: FxHashMap, /// Interned Unknown to return references to. standard_types: InternedStandardTypes, + /// Stores the types which were implicitly dereferenced in pattern binding modes. + pub pat_adjustments: FxHashMap>, } impl InferenceResult { diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 83e0a7a9e95..a21f44d6ae1 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs @@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> { let mut expected = self.resolve_ty_shallow(expected); if is_non_ref_pat(&body, pat) { + let mut pat_adjustments = Vec::new(); while let Some((inner, _lifetime, mutability)) = expected.as_reference() { + pat_adjustments.push(expected.clone()); expected = self.resolve_ty_shallow(inner); default_bm = match default_bm { BindingMode::Move => BindingMode::Ref(mutability), @@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> { BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), } } + + if !pat_adjustments.is_empty() { + pat_adjustments.shrink_to_fit(); + self.result.pat_adjustments.insert(pat, pat_adjustments); + } } else if let Pat::Ref { .. } = &body[pat] { cov_mark::hit!(match_ergonomics_ref); // When you encounter a `&pat` pattern, reset to Move.