Skip to content

Commit f967076

Browse files
committed
Bail out of MIR construction if check_match fails
1 parent c1bb0e0 commit f967076

File tree

5 files changed

+175
-20
lines changed

5 files changed

+175
-20
lines changed

compiler/rustc_middle/src/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1022,7 +1022,7 @@ rustc_queries! {
10221022
desc { "converting literal to mir constant" }
10231023
}
10241024

1025-
query check_match(key: LocalDefId) {
1025+
query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
10261026
desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
10271027
cache_on_disk_if { true }
10281028
}

compiler/rustc_mir_build/src/build/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
4242
// Ensure unsafeck and abstract const building is ran before we steal the THIR.
4343
tcx.ensure_with_value().thir_check_unsafety(def);
4444
tcx.ensure_with_value().thir_abstract_const(def);
45-
tcx.ensure_with_value().check_match(def);
45+
if let Err(e) = tcx.check_match(def) {
46+
return construct_error(tcx, def, e);
47+
}
4648

4749
let body = match tcx.thir_body(def) {
4850
Err(error_reported) => construct_error(tcx, def, error_reported),

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

+35-18
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ use rustc_session::Session;
2626
use rustc_span::hygiene::DesugaringKind;
2727
use rustc_span::Span;
2828

29-
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
30-
let Ok((thir, expr)) = tcx.thir_body(def_id) else { return };
29+
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
30+
let (thir, expr) = match tcx.thir_body(def_id) {
31+
Ok((thir, expr)) => (thir, expr),
32+
Err(e) => return Err(e),
33+
};
3134
let thir = thir.borrow();
3235
let pattern_arena = TypedArena::default();
3336
let mut visitor = MatchVisitor {
@@ -37,13 +40,18 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
3740
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
3841
let_source: LetSource::None,
3942
pattern_arena: &pattern_arena,
43+
errors: vec![],
4044
};
4145
visitor.visit_expr(&thir[expr]);
46+
if let Some(e) = visitor.errors.first() {
47+
return Err(*e);
48+
}
4249
for param in thir.params.iter() {
4350
if let Some(box ref pattern) = param.pat {
44-
visitor.check_irrefutable(pattern, "function argument", None);
51+
visitor.check_irrefutable(pattern, "function argument", None)?;
4552
}
4653
}
54+
Ok(())
4755
}
4856

4957
fn create_e0004(
@@ -77,6 +85,7 @@ struct MatchVisitor<'a, 'p, 'tcx> {
7785
lint_level: HirId,
7886
let_source: LetSource,
7987
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
88+
errors: Vec<ErrorGuaranteed>,
8089
}
8190

8291
impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
@@ -139,7 +148,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
139148
Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar,
140149
_ => hir::MatchSource::Normal,
141150
};
142-
self.check_match(scrutinee, arms, source, ex.span);
151+
if let Err(e) = self.check_match(scrutinee, arms, source, ex.span) {
152+
self.errors.push(e)
153+
}
143154
}
144155
ExprKind::Let { box ref pat, expr } => {
145156
self.check_let(pat, expr, self.let_source, ex.span);
@@ -166,8 +177,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
166177
self.check_let(pattern, initializer, LetSource::LetElse, span);
167178
}
168179

169-
if else_block.is_none() {
170-
self.check_irrefutable(pattern, "local binding", Some(span));
180+
if else_block.is_none()
181+
&& let Err(e) = self.check_irrefutable(pattern, "local binding", Some(span)) {
182+
self.errors.push(e);
171183
}
172184
}
173185
_ => {}
@@ -226,7 +238,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
226238
arms: &[ArmId],
227239
source: hir::MatchSource,
228240
expr_span: Span,
229-
) {
241+
) -> Result<(), ErrorGuaranteed> {
230242
let mut cx = self.new_cx(self.lint_level, true);
231243

232244
for &arm in arms {
@@ -274,13 +286,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
274286
debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
275287
let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
276288
let [pat_field] = &subpatterns[..] else { bug!() };
277-
self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
289+
self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None)?;
278290
} else {
279-
non_exhaustive_match(
291+
return Err(non_exhaustive_match(
280292
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
281-
);
293+
));
282294
}
283295
}
296+
Ok(())
284297
}
285298

286299
fn check_let_reachability(
@@ -409,7 +422,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
409422
}
410423

411424
#[instrument(level = "trace", skip(self))]
412-
fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
425+
fn check_irrefutable(
426+
&self,
427+
pat: &Pat<'tcx>,
428+
origin: &str,
429+
sp: Option<Span>,
430+
) -> Result<(), ErrorGuaranteed> {
413431
let mut cx = self.new_cx(self.lint_level, false);
414432

415433
let pattern = self.lower_pattern(&mut cx, pat);
@@ -423,7 +441,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
423441
if witnesses.is_empty() {
424442
// The pattern is irrefutable.
425443
self.check_patterns(pat, Irrefutable);
426-
return;
444+
return Ok(());
427445
}
428446

429447
let inform = sp.is_some().then_some(Inform);
@@ -478,7 +496,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
478496
AdtDefinedHere { adt_def_span, ty, variants }
479497
};
480498

481-
self.tcx.sess.emit_err(PatternNotCovered {
499+
Err(self.tcx.sess.emit_err(PatternNotCovered {
482500
span: pat.span,
483501
origin,
484502
uncovered: Uncovered::new(pat.span, &cx, witnesses),
@@ -489,7 +507,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
489507
let_suggestion,
490508
misc_suggestion,
491509
adt_defined_here,
492-
});
510+
}))
493511
}
494512
}
495513

@@ -631,7 +649,7 @@ fn non_exhaustive_match<'p, 'tcx>(
631649
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
632650
arms: &[ArmId],
633651
expr_span: Span,
634-
) {
652+
) -> ErrorGuaranteed {
635653
let is_empty_match = arms.is_empty();
636654
let non_empty_enum = match scrut_ty.kind() {
637655
ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
@@ -643,13 +661,12 @@ fn non_exhaustive_match<'p, 'tcx>(
643661
let pattern;
644662
let patterns_len;
645663
if is_empty_match && !non_empty_enum {
646-
cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
664+
return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
647665
cx,
648666
expr_span,
649667
span: sp,
650668
ty: scrut_ty,
651669
});
652-
return;
653670
} else {
654671
// FIXME: migration of this diagnostic will require list support
655672
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
@@ -800,7 +817,7 @@ fn non_exhaustive_match<'p, 'tcx>(
800817
} else {
801818
err.help(&msg);
802819
}
803-
err.emit();
820+
err.emit()
804821
}
805822

806823
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// regression test for #108683
2+
// edition:2021
3+
4+
enum Refutable {
5+
A,
6+
B,
7+
}
8+
9+
fn example(v1: u32, v2: [u32; 4], v3: Refutable) {
10+
const PAT: u32 = 0;
11+
let v4 = &v2[..];
12+
|| {
13+
let 0 = v1; //~ ERROR refutable pattern in local binding
14+
let (0 | 1) = v1; //~ ERROR refutable pattern in local binding
15+
let 1.. = v1; //~ ERROR refutable pattern in local binding
16+
let [0, 0, 0, 0] = v2; //~ ERROR refutable pattern in local binding
17+
let [0] = v4; //~ ERROR refutable pattern in local binding
18+
let Refutable::A = v3; //~ ERROR refutable pattern in local binding
19+
let PAT = v1; //~ ERROR refutable pattern in local binding
20+
};
21+
}
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
error[E0005]: refutable pattern in local binding
2+
--> $DIR/bad-pattern.rs:13:13
3+
|
4+
LL | let 0 = v1;
5+
| ^ pattern `1_u32..=u32::MAX` not covered
6+
|
7+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
8+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
9+
= note: the matched value is of type `u32`
10+
help: you might want to use `if let` to ignore the variant that isn't matched
11+
|
12+
LL | if let 0 = v1 { todo!() };
13+
| ++ +++++++++++
14+
help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
15+
|
16+
LL | let _0 = v1;
17+
| +
18+
19+
error[E0005]: refutable pattern in local binding
20+
--> $DIR/bad-pattern.rs:14:14
21+
|
22+
LL | let (0 | 1) = v1;
23+
| ^^^^^ pattern `2_u32..=u32::MAX` not covered
24+
|
25+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
26+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
27+
= note: the matched value is of type `u32`
28+
help: you might want to use `if let` to ignore the variant that isn't matched
29+
|
30+
LL | if let (0 | 1) = v1 { todo!() };
31+
| ++ +++++++++++
32+
33+
error[E0005]: refutable pattern in local binding
34+
--> $DIR/bad-pattern.rs:15:13
35+
|
36+
LL | let 1.. = v1;
37+
| ^^^ pattern `0_u32` not covered
38+
|
39+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
40+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
41+
= note: the matched value is of type `u32`
42+
help: you might want to use `if let` to ignore the variant that isn't matched
43+
|
44+
LL | if let 1.. = v1 { todo!() };
45+
| ++ +++++++++++
46+
47+
error[E0005]: refutable pattern in local binding
48+
--> $DIR/bad-pattern.rs:16:13
49+
|
50+
LL | let [0, 0, 0, 0] = v2;
51+
| ^^^^^^^^^^^^ pattern `[1_u32..=u32::MAX, _, _, _]` not covered
52+
|
53+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
54+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
55+
= note: the matched value is of type `[u32; 4]`
56+
help: you might want to use `if let` to ignore the variant that isn't matched
57+
|
58+
LL | if let [0, 0, 0, 0] = v2 { todo!() };
59+
| ++ +++++++++++
60+
61+
error[E0005]: refutable pattern in local binding
62+
--> $DIR/bad-pattern.rs:17:13
63+
|
64+
LL | let [0] = v4;
65+
| ^^^ patterns `&[]` and `&[_, _, ..]` not covered
66+
|
67+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
68+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
69+
= note: the matched value is of type `&[u32]`
70+
help: you might want to use `if let` to ignore the variants that aren't matched
71+
|
72+
LL | if let [0] = v4 { todo!() };
73+
| ++ +++++++++++
74+
75+
error[E0005]: refutable pattern in local binding
76+
--> $DIR/bad-pattern.rs:18:13
77+
|
78+
LL | let Refutable::A = v3;
79+
| ^^^^^^^^^^^^ pattern `Refutable::B` not covered
80+
|
81+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
82+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
83+
note: `Refutable` defined here
84+
--> $DIR/bad-pattern.rs:4:6
85+
|
86+
LL | enum Refutable {
87+
| ^^^^^^^^^
88+
LL | A,
89+
LL | B,
90+
| - not covered
91+
= note: the matched value is of type `Refutable`
92+
help: you might want to use `if let` to ignore the variant that isn't matched
93+
|
94+
LL | if let Refutable::A = v3 { todo!() };
95+
| ++ +++++++++++
96+
97+
error[E0005]: refutable pattern in local binding
98+
--> $DIR/bad-pattern.rs:19:13
99+
|
100+
LL | let PAT = v1;
101+
| ^^^
102+
| |
103+
| pattern `1_u32..=u32::MAX` not covered
104+
| missing patterns are not covered because `PAT` is interpreted as a constant pattern, not a new variable
105+
| help: introduce a variable instead: `PAT_var`
106+
|
107+
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
108+
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
109+
= note: the matched value is of type `u32`
110+
111+
error: aborting due to 7 previous errors
112+
113+
For more information about this error, try `rustc --explain E0005`.

0 commit comments

Comments
 (0)