diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index cd00d181ed046..1c60ef7bda0d1 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. @@ -836,3 +846,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 0000000000000..0a6d71c68ae84 --- /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 0000000000000..f02d23464f168 --- /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`.