1
Fork 0

Handle match with non axhaustive variants in closures

This commit is contained in:
Roxane 2021-08-23 22:41:03 -04:00
parent 926f069950
commit 20de556a26
4 changed files with 131 additions and 2 deletions

View file

@ -15,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, 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
}
}

View file

@ -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 }

View file

@ -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();
}

View file

@ -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`.