Skip to content

Commit aa7cc5d

Browse files
committed
loop match: run exhaustiveness check
1 parent f46ce66 commit aa7cc5d

File tree

9 files changed

+242
-153
lines changed

9 files changed

+242
-153
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,11 @@ pub enum ExprKind<'tcx> {
380380
},
381381
/// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression.
382382
LoopMatch {
383-
/// The state variable that is updated, and also the scrutinee of the match.
383+
/// The state variable that is updated.
384+
/// The `match_data.scrutinee` is the same variable, but with a different span.
384385
state: ExprId,
385386
region_scope: region::Scope,
386-
arms: Box<[ArmId]>,
387-
match_span: Span,
387+
match_data: Box<LoopMatchMatchData>,
388388
},
389389
/// Special expression representing the `let` part of an `if let` or similar construct
390390
/// (including `if let` guards in match arms, and let-chains formed by `&&`).
@@ -599,6 +599,14 @@ pub struct Arm<'tcx> {
599599
pub span: Span,
600600
}
601601

602+
/// The `match` part of a `#[loop_match]`
603+
#[derive(Clone, Debug, HashStable)]
604+
pub struct LoopMatchMatchData {
605+
pub scrutinee: ExprId,
606+
pub arms: Box<[ArmId]>,
607+
pub span: Span,
608+
}
609+
602610
#[derive(Copy, Clone, Debug, HashStable)]
603611
pub enum LogicalOp {
604612
/// The `&&` operator.

compiler/rustc_middle/src/thir/visit.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::{
22
AdtExpr, AdtExprBase, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand,
33
Pat, PatKind, Stmt, StmtKind, Thir,
44
};
5+
use crate::thir::LoopMatchMatchData;
56

67
/// Every `walk_*` method uses deconstruction to access fields of structs and
78
/// enums. This will result in a compile error if a field is added, which makes
@@ -83,7 +84,8 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
8384
visitor.visit_pat(pat);
8485
}
8586
Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
86-
LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => {
87+
LoopMatch { match_data: box LoopMatchMatchData { scrutinee, ref arms, .. }, .. }
88+
| Match { scrutinee, ref arms, .. } => {
8789
visitor.visit_expr(&visitor.thir()[scrutinee]);
8890
for &arm in &**arms {
8991
visitor.visit_arm(&visitor.thir()[arm]);

compiler/rustc_mir_build/src/builder/expr/into.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
245245
None
246246
})
247247
}
248-
ExprKind::LoopMatch { state, region_scope, match_span, ref arms } => {
248+
ExprKind::LoopMatch {
249+
state,
250+
region_scope,
251+
match_data: box LoopMatchMatchData { box ref arms, span: match_span, scrutinee },
252+
} => {
249253
// Intuitively, this is a combination of a loop containing a labeled block
250254
// containing a match.
251255
//
@@ -292,8 +296,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
292296

293297
// Logic for `match`.
294298
let scrutinee_place_builder =
295-
unpack!(body_block = this.as_place_builder(body_block, state));
296-
let scrutinee_span = this.thir.exprs[state].span;
299+
unpack!(body_block = this.as_place_builder(body_block, scrutinee));
300+
let scrutinee_span = this.thir.exprs[scrutinee].span;
297301
let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
298302

299303
let mut patterns = Vec::with_capacity(arms.len());
@@ -335,7 +339,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
335339
move |this| {
336340
this.in_breakable_scope(None, state_place, expr_span, |this| {
337341
Some(this.in_const_continuable_scope(
338-
arms.clone(),
342+
Box::from(arms),
339343
built_tree.clone(),
340344
state_place,
341345
expr_span,

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,11 @@ impl<'tcx> ThirBuildCx<'tcx> {
983983
data: region::ScopeData::Node,
984984
},
985985

986-
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
987-
match_span: block_body_expr.span,
986+
match_data: Box::new(LoopMatchMatchData {
987+
scrutinee: self.mirror_expr(scrutinee),
988+
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
989+
span: block_body_expr.span,
990+
}),
988991
}
989992
} else {
990993
let block_ty = self.typeck_results.node_type(body.hir_id);

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_errors::codes::*;
66
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err};
77
use rustc_hir::def::*;
88
use rustc_hir::def_id::LocalDefId;
9-
use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
9+
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
1010
use rustc_infer::infer::TyCtxtInferExt;
1111
use rustc_lint::Level;
1212
use rustc_middle::bug;
@@ -154,6 +154,12 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
154154
ExprKind::Match { scrutinee, box ref arms, match_source } => {
155155
self.check_match(scrutinee, arms, match_source, ex.span);
156156
}
157+
ExprKind::LoopMatch {
158+
match_data: box LoopMatchMatchData { scrutinee, box ref arms, span },
159+
..
160+
} => {
161+
self.check_match(scrutinee, arms, MatchSource::Normal, span);
162+
}
157163
ExprKind::Let { box ref pat, expr } => {
158164
self.check_let(pat, Some(expr), ex.span);
159165
}

compiler/rustc_mir_build/src/thir/print.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,18 +318,23 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
318318
self.print_expr(*body, depth_lvl + 2);
319319
print_indented!(self, ")", depth_lvl);
320320
}
321-
LoopMatch { state, region_scope, match_span, arms } => {
321+
LoopMatch { state, region_scope, match_data } => {
322322
print_indented!(self, "LoopMatch {", depth_lvl);
323323
print_indented!(self, "state:", depth_lvl + 1);
324324
self.print_expr(*state, depth_lvl + 2);
325325
print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
326-
print_indented!(self, format!("match_span: {:?}", match_span), depth_lvl + 1);
327-
328-
print_indented!(self, "arms: [", depth_lvl + 1);
329-
for arm_id in arms.iter() {
330-
self.print_arm(*arm_id, depth_lvl + 2);
326+
print_indented!(self, "match_data:", depth_lvl + 1);
327+
print_indented!(self, "LoopMatchMatchData {", depth_lvl + 2);
328+
print_indented!(self, format!("span: {:?}", match_data.span), depth_lvl + 3);
329+
print_indented!(self, "scrutinee:", depth_lvl + 3);
330+
self.print_expr(match_data.scrutinee, depth_lvl + 4);
331+
332+
print_indented!(self, "arms: [", depth_lvl + 3);
333+
for arm_id in match_data.arms.iter() {
334+
self.print_arm(*arm_id, depth_lvl + 4);
331335
}
332-
print_indented!(self, "]", depth_lvl + 1);
336+
print_indented!(self, "]", depth_lvl + 3);
337+
print_indented!(self, "}", depth_lvl + 2);
333338
print_indented!(self, "}", depth_lvl);
334339
}
335340
Let { expr, pat } => {

tests/ui/loop-match/invalid.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,16 @@ fn arm_has_guard(cond: bool) {
159159
}
160160
}
161161
}
162+
163+
fn non_exhaustive() {
164+
let mut state = State::A;
165+
#[loop_match]
166+
loop {
167+
state = 'blk: {
168+
match state {
169+
//~^ ERROR non-exhaustive patterns: `State::B` and `State::C` not covered
170+
State::A => State::B,
171+
}
172+
}
173+
}
174+
}

tests/ui/loop-match/invalid.stderr

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,30 @@ error: match arms that are part of a `#[loop_match]` cannot have guards
8686
LL | State::B if cond => break 'a,
8787
| ^^^^
8888

89-
error: aborting due to 12 previous errors
89+
error[E0004]: non-exhaustive patterns: `State::B` and `State::C` not covered
90+
--> $DIR/invalid.rs:168:19
91+
|
92+
LL | match state {
93+
| ^^^^^ patterns `State::B` and `State::C` not covered
94+
|
95+
note: `State` defined here
96+
--> $DIR/invalid.rs:7:6
97+
|
98+
LL | enum State {
99+
| ^^^^^
100+
LL | A,
101+
LL | B,
102+
| - not covered
103+
LL | C,
104+
| - not covered
105+
= note: the matched value is of type `State`
106+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
107+
|
108+
LL ~ State::A => State::B,
109+
LL ~ State::B | State::C => todo!(),
110+
|
111+
112+
error: aborting due to 13 previous errors
90113

91-
For more information about this error, try `rustc --explain E0308`.
114+
Some errors have detailed explanations: E0004, E0308.
115+
For more information about an error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)