about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/expr_use_visitor.rs52
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-88331.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-88331.stderr27
3 files changed, 93 insertions, 19 deletions
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 024370f8d37..a8be207dbb3 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -2,6 +2,7 @@
 //! normal visitor, which just walks the entire body in one shot, the
 //! `ExprUseVisitor` determines how expressions are being used.
 
+use hir::def::DefKind;
 // Export these here so that Clippy can use them.
 pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
 
@@ -14,7 +15,7 @@ use rustc_index::vec::Idx;
 use rustc_infer::infer::InferCtxt;
 use rustc_middle::hir::place::ProjectionKind;
 use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty::{self, adjustment, TyCtxt};
+use rustc_middle::ty::{self, adjustment, Ty, TyCtxt};
 use rustc_target::abi::VariantIdx;
 use std::iter;
 
@@ -251,28 +252,37 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                                     needs_to_be_read = true;
                                 }
                             }
-                            PatKind::TupleStruct(..)
-                            | PatKind::Path(..)
-                            | PatKind::Struct(..)
-                            | PatKind::Tuple(..) => {
-                                // If the PatKind is a TupleStruct, Path, Struct or Tuple then we want to check
-                                // whether the Variant is a MultiVariant or a SingleVariant. We only want
-                                // to borrow discr if it is a MultiVariant.
-                                // If it is a SingleVariant and creates a binding we will handle that when
-                                // this callback gets called again.
-
-                                // Get the type of the Place after all projections have been applied
-                                let place_ty = place.place.ty();
-
-                                if let ty::Adt(def, _) = place_ty.kind() {
-                                    if def.variants.len() > 1 {
+                            PatKind::Path(qpath) => {
+                                // A `Path` pattern is just a name like `Foo`. This is either a
+                                // named constant or else it refers to an ADT variant
+
+                                let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id);
+                                match res {
+                                    Res::Def(DefKind::Const, _)
+                                    | Res::Def(DefKind::AssocConst, _) => {
+                                        // Named constants have to be equated with the value
+                                        // being matched, so that's a read of the value being matched.
+                                        //
+                                        // FIXME: We don't actually  reads for ZSTs.
                                         needs_to_be_read = true;
                                     }
-                                } else {
-                                    // If it is not ty::Adt, then it should be read
-                                    needs_to_be_read = true;
+                                    _ => {
+                                        // Otherwise, this is a struct/enum variant, and so it's
+                                        // only a read if we need to read the discriminant.
+                                        needs_to_be_read |= is_multivariant_adt(place.place.ty());
+                                    }
                                 }
                             }
+                            PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
+                                // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching
+                                // against a multivariant enum or struct. In that case, we have to read
+                                // the discriminant. Otherwise this kind of pattern doesn't actually
+                                // read anything (we'll get invoked for the `...`, which may indeed
+                                // perform some reads).
+
+                                let place_ty = place.place.ty();
+                                needs_to_be_read |= is_multivariant_adt(place_ty);
+                            }
                             PatKind::Lit(_) | PatKind::Range(..) => {
                                 // If the PatKind is a Lit or a Range then we want
                                 // to borrow discr.
@@ -833,3 +843,7 @@ fn delegate_consume<'a, 'tcx>(
         }
     }
 }
+
+fn is_multivariant_adt(ty: Ty<'tcx>) -> bool {
+    if let ty::Adt(def, _) = ty.kind() { def.variants.len() > 1 } else { false }
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88331.rs b/src/test/ui/closures/2229_closure_analysis/issue-88331.rs
new file mode 100644
index 00000000000..0a6d71c68ae
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-88331.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode(pub u8);
+
+impl Opcode {
+    pub const OP1: Opcode = Opcode(0x1);
+}
+
+pub fn example1(msg_type: Opcode) -> impl FnMut(&[u8]) {
+    move |i| match msg_type {
+    //~^ ERROR: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+        Opcode::OP1 => unimplemented!(),
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode2(Opcode);
+
+impl Opcode2 {
+    pub const OP2: Opcode2 = Opcode2(Opcode(0x1));
+}
+
+
+pub fn example2(msg_type: Opcode2) -> impl FnMut(&[u8]) {
+
+    move |i| match msg_type {
+    //~^ ERROR: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+        Opcode2::OP2=> unimplemented!(),
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88331.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88331.stderr
new file mode 100644
index 00000000000..f02d23464f1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-88331.stderr
@@ -0,0 +1,27 @@
+error[E0004]: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+  --> $DIR/issue-88331.rs:11:20
+   |
+LL | pub struct Opcode(pub u8);
+   | -------------------------- `Opcode` defined here
+...
+LL |     move |i| match msg_type {
+   |                    ^^^^^^^^ patterns `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Opcode`
+
+error[E0004]: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+  --> $DIR/issue-88331.rs:27:20
+   |
+LL | pub struct Opcode2(Opcode);
+   | --------------------------- `Opcode2` defined here
+...
+LL |     move |i| match msg_type {
+   |                    ^^^^^^^^ patterns `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Opcode2`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.