about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs49
-rw-r--r--tests/ui/pattern/deref-patterns/implicit-const-deref.rs19
-rw-r--r--tests/ui/pattern/deref-patterns/implicit-const-deref.stderr16
-rw-r--r--tests/ui/pattern/deref-patterns/implicit-cow-deref.rs44
4 files changed, 114 insertions, 14 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index da5153bed12..46916f22d0e 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -9,6 +9,7 @@ use rustc_errors::{
     Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err,
 };
 use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def_id::DefId;
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
 use rustc_hir::{
     self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr,
@@ -179,8 +180,16 @@ enum PeelKind {
     /// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
     /// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
     /// don't peel it. See [`ResolvedPat`] for more information.
-    // TODO: add `ResolvedPat` and `until_adt`.
-    Implicit,
+    Implicit { until_adt: Option<DefId> },
+}
+
+impl AdjustMode {
+    const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode {
+        AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } }
+    }
+    const fn peel_all() -> AdjustMode {
+        AdjustMode::peel_until_adt(None)
+    }
 }
 
 /// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
@@ -294,7 +303,7 @@ impl<'tcx> ResolvedPat<'tcx> {
             // The remaining possible resolutions for path, struct, and tuple struct patterns are
             // ADT constructors. As such, we may peel references freely, but we must not peel the
             // ADT itself from the scrutinee if it's a smart pointer.
-            AdjustMode::Peel { kind: PeelKind::Implicit }
+            AdjustMode::peel_until_adt(self.ty.ty_adt_def().map(|adt| adt.did()))
         }
     }
 }
@@ -521,14 +530,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
             // examples in `tests/ui/pattern/deref_patterns/`.
             _ if self.tcx.features().deref_patterns()
-                && let AdjustMode::Peel { kind: PeelKind::Implicit } = adjust_mode
+                && let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode
                 && pat.default_binding_modes
                 // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
                 // FIXME(deref_patterns): we'll get better diagnostics for users trying to
                 // implicitly deref generics if we allow them here, but primitives, tuples, and
                 // inference vars definitely should be stopped. Figure out what makes most sense.
-                // TODO: stop peeling if the pattern is a constructor for the scrutinee type
-                && expected.is_adt()
+                && let ty::Adt(scrutinee_adt, _) = *expected.kind()
+                // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
+                // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
+                && until_adt != Some(scrutinee_adt.did())
                 // At this point, the pattern isn't able to match `expected` without peeling. Check
                 // that it implements `Deref` before assuming it's a smart pointer, to get a normal
                 // type error instead of a missing impl error if not. This only checks for `Deref`,
@@ -637,22 +648,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match &pat.kind {
             // Type checking these product-like types successfully always require
             // that the expected type be of those types and not reference types.
-            PatKind::Struct(..)
-            | PatKind::TupleStruct(..)
-            | PatKind::Tuple(..)
+            PatKind::Tuple(..)
             | PatKind::Range(..)
-            | PatKind::Slice(..) => AdjustMode::Peel { kind: PeelKind::Implicit },
+            | PatKind::Slice(..) => AdjustMode::peel_all(),
             // When checking an explicit deref pattern, only peel reference types.
             // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
             // patterns may want `PeelKind::Implicit`, stopping on encountering a box.
             | PatKind::Box(_)
             | PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
             // A never pattern behaves somewhat like a literal or unit variant.
-            PatKind::Never => AdjustMode::Peel { kind: PeelKind::Implicit },
+            PatKind::Never => AdjustMode::peel_all(),
             // For patterns with paths, how we peel the scrutinee depends on the path's resolution.
-            PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
+            PatKind::Struct(..)
+            | PatKind::TupleStruct(..)
+            | PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
                 // If there was an error resolving the path, default to peeling everything.
-                opt_path_res.unwrap().map_or(AdjustMode::Peel { kind: PeelKind::Implicit }, |pr| pr.adjust_mode())
+                opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode())
             }
 
             // String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -662,7 +673,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Call `resolve_vars_if_possible` here for inline const blocks.
             PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
                 ty::Ref(..) => AdjustMode::Pass,
-                _ => AdjustMode::Peel { kind: PeelKind::Implicit },
+                _ => {
+                    // Path patterns have already been handled, and inline const blocks currently
+                    // aren't possible to write, so any handling for them would be untested.
+                    if cfg!(debug_assertions)
+                        && self.tcx.features().deref_patterns()
+                        && !matches!(lt.kind, PatExprKind::Lit { .. })
+                    {
+                        span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
+                    }
+                    AdjustMode::peel_all()
+                }
             },
 
             // Ref patterns are complicated, we handle them in `check_pat_ref`.
diff --git a/tests/ui/pattern/deref-patterns/implicit-const-deref.rs b/tests/ui/pattern/deref-patterns/implicit-const-deref.rs
new file mode 100644
index 00000000000..70f89629bc2
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/implicit-const-deref.rs
@@ -0,0 +1,19 @@
+//! Test that we get an error about structural equality rather than a type error when attempting to
+//! use const patterns of library pointer types. Currently there aren't any smart pointers that can
+//! be used in constant patterns, but we still need to make sure we don't implicitly dereference the
+//! scrutinee and end up with a type error; this would prevent us from reporting that only constants
+//! supporting structural equality can be used as patterns.
+#![feature(deref_patterns)]
+#![allow(incomplete_features)]
+
+const EMPTY: Vec<()> = Vec::new();
+
+fn main() {
+    // FIXME(inline_const_pat): if `inline_const_pat` is reinstated, there should be a case here for
+    // inline const block patterns as well; they're checked differently than named constants.
+    match vec![()] {
+        EMPTY => {}
+        //~^ ERROR: constant of non-structural type `Vec<()>` in a pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr b/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr
new file mode 100644
index 00000000000..21d09ec44c4
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr
@@ -0,0 +1,16 @@
+error: constant of non-structural type `Vec<()>` in a pattern
+  --> $DIR/implicit-const-deref.rs:15:9
+   |
+LL | const EMPTY: Vec<()> = Vec::new();
+   | -------------------- constant defined here
+...
+LL |         EMPTY => {}
+   |         ^^^^^ constant of non-structural type
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+   = note: `Vec<()>` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
+   |
+   = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs
new file mode 100644
index 00000000000..6a363c58b63
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs
@@ -0,0 +1,44 @@
+//@ run-pass
+//! Test that implicit deref patterns interact as expected with `Cow` constructor patterns.
+#![feature(deref_patterns)]
+#![allow(incomplete_features)]
+
+use std::borrow::Cow;
+
+fn main() {
+    let cow: Cow<'static, [u8]> = Cow::from(&[1, 2, 3]);
+
+    match cow {
+        [..] => {}
+        _ => unreachable!(),
+    }
+
+    match cow {
+        Cow::Borrowed(_) => {}
+        Cow::Owned(_) => unreachable!(),
+    }
+
+    match Box::new(&cow) {
+        Cow::Borrowed { 0: _ } => {}
+        Cow::Owned { 0: _ } => unreachable!(),
+        _ => unreachable!(),
+    }
+
+    let cow_of_cow: Cow<'_, Cow<'static, [u8]>> = Cow::Owned(cow);
+
+    match cow_of_cow {
+        [..] => {}
+        _ => unreachable!(),
+    }
+
+    match cow_of_cow {
+        Cow::Borrowed(_) => unreachable!(),
+        Cow::Owned(_) => {}
+    }
+
+    match Box::new(&cow_of_cow) {
+        Cow::Borrowed { 0: _ } => unreachable!(),
+        Cow::Owned { 0: _ } => {}
+        _ => unreachable!(),
+    }
+}