Auto merge of #88280 - sexxi-goose:non-exhaustive, r=nikomatsakis
Handle match statements with non exhaustive variants in closures This PR ensures that the behavior for match statements with non exhaustive variants is the same inside and outside closures. If we have a non-exhaustive SingleVariant which is defined in a different crate, then we should handle the case the same way we would handle a MultiVariant: borrow the match discriminant. Closes https://github.com/rust-lang/project-rfc-2229/issues/59 r? `@nikomatsakis`
This commit is contained in:
commit
2f662b1403
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
@ -0,0 +1,37 @@
|
||||
// edition:2021
|
||||
|
||||
enum SingleVariant {
|
||||
A
|
||||
}
|
||||
|
||||
struct TestStruct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
fn edge_case_if() {
|
||||
let sv = SingleVariant::A;
|
||||
let condition = true;
|
||||
// sv should not be captured as it is a SingleVariant
|
||||
let _a = || {
|
||||
match sv {
|
||||
SingleVariant::A if condition => (),
|
||||
_ => ()
|
||||
}
|
||||
};
|
||||
let mut mut_sv = sv;
|
||||
_a();
|
||||
|
||||
// ts should be captured
|
||||
let ts = TestStruct { x: 1, y: 1, z: 1 };
|
||||
let _b = || { match ts {
|
||||
TestStruct{ x: 1, .. } => (),
|
||||
_ => ()
|
||||
}};
|
||||
let mut mut_ts = ts;
|
||||
//~^ ERROR: cannot move out of `ts` because it is borrowed
|
||||
_b();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,17 @@
|
||||
error[E0505]: cannot move out of `ts` because it is borrowed
|
||||
--> $DIR/match-edge-cases_2.rs:32:22
|
||||
|
|
||||
LL | let _b = || { match ts {
|
||||
| -- -- borrow occurs due to use in closure
|
||||
| |
|
||||
| borrow of `ts` occurs here
|
||||
...
|
||||
LL | let mut mut_ts = ts;
|
||||
| ^^ move out of `ts` occurs here
|
||||
LL |
|
||||
LL | _b();
|
||||
| -- borrow later used here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0505`.
|
@ -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();
|
||||
}
|
@ -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`.
|
Loading…
x
Reference in New Issue
Block a user