]> git.lizzy.rs Git - rust.git/commitdiff
Handle match with non axhaustive variants in closures
authorRoxane <roxane.fruytier@hotmail.com>
Tue, 24 Aug 2021 02:41:03 +0000 (22:41 -0400)
committerRoxane <roxane.fruytier@hotmail.com>
Sat, 28 Aug 2021 23:27:32 +0000 (19:27 -0400)
compiler/rustc_typeck/src/expr_use_visitor.rs
src/test/ui/closures/2229_closure_analysis/auxiliary/match_non_exhaustive_lib.rs [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/non-exhaustive-match.rs [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/non-exhaustive-match.stderr [new file with mode: 0644]

index a8be207dbb363cce1148deea796abde7d220b4c8..3d483e322a8510dc379acf2f383745d33fbb359b 100644 (file)
@@ -15,7 +15,7 @@
 use rustc_infer::infer::InferCtxt;
 use rustc_middle::hir::place::ProjectionKind;
 use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty::{self, adjustment, Ty, TyCtxt};
+use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt};
 use rustc_target::abi::VariantIdx;
 use std::iter;
 
@@ -845,5 +845,20 @@ 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 }
+    if let ty::Adt(def, _) = ty.kind() {
+        // Note that if a non-exhaustive SingleVariant is defined in another crate, we need
+        // to assume that more cases will be added to the variant in the future. This mean
+        // that we should handle non-exhaustive SingleVariant the same way we would handle
+        // a MultiVariant.
+        // If the variant is not local it must be defined in another crate.
+        let is_non_exhaustive = match def.adt_kind() {
+            AdtKind::Struct | AdtKind::Union => {
+                def.non_enum_variant().is_field_list_non_exhaustive()
+            }
+            AdtKind::Enum => def.is_variant_list_non_exhaustive(),
+        };
+        def.variants.len() > 1 || (!def.did.is_local() && is_non_exhaustive)
+    } else {
+        false
+    }
 }
diff --git a/src/test/ui/closures/2229_closure_analysis/auxiliary/match_non_exhaustive_lib.rs b/src/test/ui/closures/2229_closure_analysis/auxiliary/match_non_exhaustive_lib.rs
new file mode 100644 (file)
index 0000000..4060c40
--- /dev/null
@@ -0,0 +1,10 @@
+#[non_exhaustive]
+pub enum E1 {}
+
+#[non_exhaustive]
+pub enum E2 { A, B }
+
+#[non_exhaustive]
+pub enum E3 { C }
+
+pub enum E4 { D }
diff --git a/src/test/ui/closures/2229_closure_analysis/non-exhaustive-match.rs b/src/test/ui/closures/2229_closure_analysis/non-exhaustive-match.rs
new file mode 100644 (file)
index 0000000..318673e
--- /dev/null
@@ -0,0 +1,54 @@
+// edition:2021
+
+// aux-build:match_non_exhaustive_lib.rs
+
+/* The error message for non-exhaustive matches on non-local enums
+ * marked as non-exhaustive should mention the fact that the enum
+ * is marked as non-exhaustive (issue #85227).
+ */
+
+// Ignore non_exhaustive in the same crate
+#[non_exhaustive]
+enum L1 { A, B }
+enum L2 { C }
+
+extern crate match_non_exhaustive_lib;
+use match_non_exhaustive_lib::{E1, E2, E3, E4};
+
+fn foo() -> (L1, L2) {todo!()}
+fn bar() -> (E1, E2, E3, E4) {todo!()}
+
+fn main() {
+    let (l1, l2) = foo();
+    // No error for enums defined in this crate
+    let _a = || { match l1 { L1::A => (), L1::B => () } };
+    // (except if the match is already non-exhaustive)
+    let _b = || { match l1 { L1::A => () } };
+    //~^ ERROR: non-exhaustive patterns: `B` not covered [E0004]
+
+    // l2 should not be captured as it is a non-exhaustive SingleVariant
+    // defined in this crate
+    let _c = || { match l2 { L2::C => (), _ => () }  };
+    let mut mut_l2 = l2;
+    _c();
+
+    // E1 is not visibly uninhabited from here
+    let (e1, e2, e3, e4) = bar();
+    let _d = || { match e1 {} };
+    //~^ ERROR: non-exhaustive patterns: type `E1` is non-empty [E0004]
+    let _e = || { match e2 { E2::A => (), E2::B => () } };
+    //~^ ERROR: non-exhaustive patterns: `_` not covered [E0004]
+    let _f = || { match e2 { E2::A => (), E2::B => (), _ => () }  };
+
+    // e3 should be captured as it is a non-exhaustive SingleVariant
+    // defined in another crate
+    let _g = || { match e3 { E3::C => (), _ => () }  };
+    let mut mut_e3 = e3;
+    //~^ ERROR: cannot move out of `e3` because it is borrowed
+    _g();
+
+    // e4 should not be captured as it is a SingleVariant
+    let _h = || { match e4 { E4::D => (), _ => () }  };
+    let mut mut_e4 = e4;
+    _h();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/non-exhaustive-match.stderr b/src/test/ui/closures/2229_closure_analysis/non-exhaustive-match.stderr
new file mode 100644 (file)
index 0000000..91ffe1a
--- /dev/null
@@ -0,0 +1,50 @@
+error[E0004]: non-exhaustive patterns: `B` not covered
+  --> $DIR/non-exhaustive-match.rs:26:25
+   |
+LL | enum L1 { A, B }
+   | ----------------
+   | |            |
+   | |            not covered
+   | `L1` defined here
+...
+LL |     let _b = || { match l1 { L1::A => () } };
+   |                         ^^ pattern `B` 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 `L1`
+
+error[E0004]: non-exhaustive patterns: type `E1` is non-empty
+  --> $DIR/non-exhaustive-match.rs:37:25
+   |
+LL |     let _d = || { match e1 {} };
+   |                         ^^
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `E1`, which is marked as non-exhaustive
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/non-exhaustive-match.rs:39:25
+   |
+LL |     let _e = || { match e2 { E2::A => (), E2::B => () } };
+   |                         ^^ pattern `_` 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 `E2`, which is marked as non-exhaustive
+
+error[E0505]: cannot move out of `e3` because it is borrowed
+  --> $DIR/non-exhaustive-match.rs:46:22
+   |
+LL |     let _g = || { match e3 { E3::C => (), _ => () }  };
+   |              --         -- borrow occurs due to use in closure
+   |              |
+   |              borrow of `e3` occurs here
+LL |     let mut mut_e3 = e3;
+   |                      ^^ move out of `e3` occurs here
+LL |
+LL |     _g();
+   |     -- borrow later used here
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0004, E0505.
+For more information about an error, try `rustc --explain E0004`.