Skip to content

Commit 4d8d72f

Browse files
committed
Handle match with non axhaustive variants in closures
1 parent 0fa3190 commit 4d8d72f

File tree

4 files changed

+129
-2
lines changed

4 files changed

+129
-2
lines changed

compiler/rustc_typeck/src/expr_use_visitor.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_index::vec::Idx;
1414
use rustc_infer::infer::InferCtxt;
1515
use rustc_middle::hir::place::ProjectionKind;
1616
use rustc_middle::mir::FakeReadCause;
17-
use rustc_middle::ty::{self, adjustment, TyCtxt};
17+
use rustc_middle::ty::{self, adjustment, AdtKind, TyCtxt};
1818
use rustc_target::abi::VariantIdx;
1919
use std::iter;
2020

@@ -262,7 +262,20 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
262262
let place_ty = place.place.ty();
263263

264264
if let ty::Adt(def, _) = place_ty.kind() {
265-
if def.variants.len() > 1 {
265+
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
266+
// to assume that more cases will be added to the variant in the future. This mean
267+
// that we should handle non-exhaustive SingleVariant the same way we would handle
268+
// a MultiVariant.
269+
// If the variant is not local it must be defined in another crate.
270+
let is_non_exhaustive = match def.adt_kind() {
271+
AdtKind::Struct | AdtKind::Union => {
272+
def.non_enum_variant().is_field_list_non_exhaustive()
273+
}
274+
AdtKind::Enum => def.is_variant_list_non_exhaustive(),
275+
};
276+
if def.variants.len() > 1
277+
|| (!def.did.is_local() && is_non_exhaustive)
278+
{
266279
needs_to_be_read = true;
267280
}
268281
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#[non_exhaustive]
2+
pub enum E1 {}
3+
4+
#[non_exhaustive]
5+
pub enum E2 { A, B }
6+
7+
#[non_exhaustive]
8+
pub enum E3 { C }
9+
10+
pub enum E4 { D }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// edition:2021
2+
3+
// aux-build:match_non_exhaustive_lib.rs
4+
5+
/* The error message for non-exhaustive matches on non-local enums
6+
* marked as non-exhaustive should mention the fact that the enum
7+
* is marked as non-exhaustive (issue #85227).
8+
*/
9+
10+
// Ignore non_exhaustive in the same crate
11+
#[non_exhaustive]
12+
enum L1 { A, B }
13+
enum L2 { C }
14+
15+
extern crate match_non_exhaustive_lib;
16+
use match_non_exhaustive_lib::{E1, E2, E3, E4};
17+
18+
fn foo() -> (L1, L2) {todo!()}
19+
fn bar() -> (E1, E2, E3, E4) {todo!()}
20+
21+
fn main() {
22+
let (l1, l2) = foo();
23+
// No error for enums defined in this crate
24+
let _a = || { match l1 { L1::A => (), L1::B => () } };
25+
// (except if the match is already non-exhaustive)
26+
let _b = || { match l1 { L1::A => () } };
27+
//~^ ERROR: non-exhaustive patterns: `B` not covered [E0004]
28+
29+
// l2 should not be captured as it is a non-exhaustive SingleVariant
30+
// defined in this crate
31+
let _c = || { match l2 { L2::C => (), _ => () } };
32+
let mut mut_l2 = l2;
33+
_c();
34+
35+
// E1 is not visibly uninhabited from here
36+
let (e1, e2, e3, e4) = bar();
37+
let _d = || { match e1 {} };
38+
//~^ ERROR: non-exhaustive patterns: type `E1` is non-empty [E0004]
39+
let _e = || { match e2 { E2::A => (), E2::B => () } };
40+
//~^ ERROR: non-exhaustive patterns: `_` not covered [E0004]
41+
let _f = || { match e2 { E2::A => (), E2::B => (), _ => () } };
42+
43+
// e3 should be captured as it is a non-exhaustive SingleVariant
44+
// defined in another crate
45+
let _g = || { match e3 { E3::C => (), _ => () } };
46+
let mut mut_e3 = e3;
47+
//~^ ERROR: cannot move out of `e3` because it is borrowed
48+
_g();
49+
50+
// e4 should not be captured as it is a SingleVariant
51+
let _h = || { match e4 { E4::D => (), _ => () } };
52+
let mut mut_e4 = e4;
53+
_h();
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
error[E0004]: non-exhaustive patterns: `B` not covered
2+
--> $DIR/non-exhaustive-match.rs:26:25
3+
|
4+
LL | enum L1 { A, B }
5+
| ----------------
6+
| | |
7+
| | not covered
8+
| `L1` defined here
9+
...
10+
LL | let _b = || { match l1 { L1::A => () } };
11+
| ^^ pattern `B` not covered
12+
|
13+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
14+
= note: the matched value is of type `L1`
15+
16+
error[E0004]: non-exhaustive patterns: type `E1` is non-empty
17+
--> $DIR/non-exhaustive-match.rs:37:25
18+
|
19+
LL | let _d = || { match e1 {} };
20+
| ^^
21+
|
22+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
23+
= note: the matched value is of type `E1`, which is marked as non-exhaustive
24+
25+
error[E0004]: non-exhaustive patterns: `_` not covered
26+
--> $DIR/non-exhaustive-match.rs:39:25
27+
|
28+
LL | let _e = || { match e2 { E2::A => (), E2::B => () } };
29+
| ^^ pattern `_` not covered
30+
|
31+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
32+
= note: the matched value is of type `E2`, which is marked as non-exhaustive
33+
34+
error[E0505]: cannot move out of `e3` because it is borrowed
35+
--> $DIR/non-exhaustive-match.rs:46:22
36+
|
37+
LL | let _g = || { match e3 { E3::C => (), _ => () } };
38+
| -- -- borrow occurs due to use in closure
39+
| |
40+
| borrow of `e3` occurs here
41+
LL | let mut mut_e3 = e3;
42+
| ^^ move out of `e3` occurs here
43+
LL |
44+
LL | _g();
45+
| -- borrow later used here
46+
47+
error: aborting due to 4 previous errors
48+
49+
Some errors have detailed explanations: E0004, E0505.
50+
For more information about an error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)