Skip to content

Commit 18ac1ea

Browse files
committed
use proper errors in mir building
1 parent 37d1a28 commit 18ac1ea

File tree

6 files changed

+357
-29
lines changed

6 files changed

+357
-29
lines changed

compiler/rustc_mir_build/messages.ftl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,23 @@ mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `
478478
mir_build_variant_defined_here = not covered
479479
480480
mir_build_wrap_suggestion = consider wrapping the function body in an unsafe block
481+
482+
mir_build_loop_match_invalid_update =
483+
invalid update of the `loop_match` state
484+
.label = the assignment must update this variable
485+
486+
mir_build_loop_match_invalid_match =
487+
invalid match on `loop_match` state
488+
.note = only matches on local variables are valid
489+
490+
mir_build_loop_match_bad_statements =
491+
statements are not allowed in this position within a `loop_match`
492+
493+
mir_build_loop_match_bad_rhs =
494+
this expression must be a single `match` wrapped in a labelled block
495+
496+
mir_build_loop_match_missing_assignment =
497+
expected a single assignment expression
498+
499+
mir_build_const_continue_missing_value =
500+
a `const_continue` must break to a label with a value

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ use rustc_middle::thir::*;
1111
use rustc_middle::ty::CanonicalUserTypeAnnotation;
1212
use rustc_middle::ty::util::Discr;
1313
use rustc_pattern_analysis::constructor::Constructor;
14-
use rustc_pattern_analysis::rustc::DeconstructedPat;
15-
use rustc_pattern_analysis::rustc::RustcPatCtxt;
14+
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
1615
use rustc_span::source_map::Spanned;
1716
use tracing::{debug, instrument};
1817

@@ -300,7 +299,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
300299
let discr_ty = match state_ty {
301300
ty if ty.is_enum() => ty.discriminant_ty(this.tcx),
302301
ty if ty.is_integral() => ty,
303-
_ => todo!(),
302+
other => todo!("{other:?}"),
304303
};
305304

306305
let rvalue = match state_ty {

compiler/rustc_mir_build/src/errors.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,3 +1161,48 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
11611161
}
11621162
}
11631163
}
1164+
1165+
#[derive(Diagnostic)]
1166+
#[diag(mir_build_loop_match_invalid_update)]
1167+
pub(crate) struct LoopMatchInvalidUpdate {
1168+
#[primary_span]
1169+
pub lhs: Span,
1170+
#[label]
1171+
pub scrutinee: Span,
1172+
}
1173+
1174+
#[derive(Diagnostic)]
1175+
#[diag(mir_build_loop_match_invalid_match)]
1176+
#[note]
1177+
pub(crate) struct LoopMatchInvalidMatch {
1178+
#[primary_span]
1179+
pub span: Span,
1180+
}
1181+
1182+
#[derive(Diagnostic)]
1183+
#[diag(mir_build_loop_match_bad_statements)]
1184+
pub(crate) struct LoopMatchBadStatements {
1185+
#[primary_span]
1186+
pub span: Span,
1187+
}
1188+
1189+
#[derive(Diagnostic)]
1190+
#[diag(mir_build_loop_match_bad_rhs)]
1191+
pub(crate) struct LoopMatchBadRhs {
1192+
#[primary_span]
1193+
pub span: Span,
1194+
}
1195+
1196+
#[derive(Diagnostic)]
1197+
#[diag(mir_build_loop_match_missing_assignment)]
1198+
pub(crate) struct LoopMatchMissingAssignment {
1199+
#[primary_span]
1200+
pub span: Span,
1201+
}
1202+
1203+
#[derive(Diagnostic)]
1204+
#[diag(mir_build_const_continue_missing_value)]
1205+
pub(crate) struct ConstContinueMissingValue {
1206+
#[primary_span]
1207+
pub span: Span,
1208+
}

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

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_middle::{bug, span_bug};
2121
use rustc_span::{Span, sym};
2222
use tracing::{debug, info, instrument, trace};
2323

24+
use crate::errors::*;
2425
use crate::thir::cx::ThirBuildCx;
2526

2627
impl<'tcx> ThirBuildCx<'tcx> {
@@ -798,15 +799,20 @@ impl<'tcx> ThirBuildCx<'tcx> {
798799
.any(|attr| attr.has_name(sym::const_continue));
799800
if is_const_continue {
800801
match dest.target_id {
801-
Ok(target_id) => ExprKind::ConstContinue {
802-
label: region::Scope {
803-
local_id: target_id.local_id,
804-
data: region::ScopeData::Node,
805-
},
806-
value: self.mirror_expr(
807-
value.expect("missing value for #[const_continue] break"),
808-
),
809-
},
802+
Ok(target_id) => {
803+
let Some(value) = value else {
804+
let span = expr.span;
805+
self.tcx.dcx().emit_fatal(ConstContinueMissingValue { span })
806+
};
807+
808+
ExprKind::ConstContinue {
809+
label: region::Scope {
810+
local_id: target_id.local_id,
811+
data: region::ScopeData::Node,
812+
},
813+
value: self.mirror_expr(value),
814+
}
815+
}
810816
Err(err) => bug!("invalid loop id for break: {}", err),
811817
}
812818
} else {
@@ -863,27 +869,58 @@ impl<'tcx> ThirBuildCx<'tcx> {
863869
.iter()
864870
.any(|attr| attr.has_name(sym::loop_match));
865871
if is_loop_match {
866-
assert!(body.stmts.is_empty());
867-
let hir::ExprKind::Assign(state, block_expr, _) = body.expr.unwrap().kind
868-
else {
869-
panic!();
872+
let dcx = self.tcx.dcx();
873+
874+
if let Some(first) = body.stmts.first() {
875+
let span = first.span.to(body.stmts.last().unwrap().span);
876+
dcx.emit_fatal(LoopMatchBadStatements { span })
877+
}
878+
879+
let Some(loop_body_expr) = body.expr else {
880+
dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span })
870881
};
871-
let hir::ExprKind::Block(block_body, _) = block_expr.kind else {
872-
panic!();
882+
883+
let hir::ExprKind::Assign(state, rhs_expr, _) = loop_body_expr.kind else {
884+
dcx.emit_fatal(LoopMatchMissingAssignment { span: loop_body_expr.span })
873885
};
874-
assert!(block_body.stmts.is_empty());
875-
let hir::ExprKind::Match(discr, arms, match_source) =
876-
block_body.expr.unwrap().kind
886+
887+
let hir::ExprKind::Block(block_body, _) = rhs_expr.kind else {
888+
dcx.emit_fatal(LoopMatchBadRhs { span: rhs_expr.span })
889+
};
890+
891+
if let Some(first) = block_body.stmts.first() {
892+
let span = first.span.to(block_body.stmts.last().unwrap().span);
893+
dcx.emit_fatal(LoopMatchBadStatements { span })
894+
}
895+
896+
let Some(block_body_expr) = block_body.expr else {
897+
dcx.emit_fatal(LoopMatchBadRhs { span: block_body.span })
898+
};
899+
900+
let hir::ExprKind::Match(scrutinee, arms, match_source) = block_body_expr.kind
877901
else {
878-
panic!();
902+
dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
903+
};
904+
905+
fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> {
906+
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind {
907+
if let Res::Local(hir_id) = path.res {
908+
return Some(hir_id);
909+
}
910+
}
911+
912+
None
913+
}
914+
915+
let Some(scrutinee_hir_id) = local(scrutinee) else {
916+
dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span })
879917
};
880-
match (state.kind, discr.kind) {
881-
(
882-
hir::ExprKind::Path(hir::QPath::Resolved(_, state)),
883-
hir::ExprKind::Path(hir::QPath::Resolved(_, discr)),
884-
) if state.segments.iter().map(|seg| seg.ident).collect::<Vec<_>>()
885-
== discr.segments.iter().map(|seg| seg.ident).collect::<Vec<_>>() => {}
886-
_ => panic!(),
918+
919+
if local(state) != Some(scrutinee_hir_id) {
920+
dcx.emit_fatal(LoopMatchInvalidUpdate {
921+
scrutinee: scrutinee.span,
922+
lhs: state.span,
923+
})
887924
}
888925

889926
ExprKind::LoopMatch {

tests/ui/loop-match/invalid.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#![feature(loop_match)]
2+
#![crate_type = "lib"]
3+
4+
enum State {
5+
A,
6+
B,
7+
C,
8+
}
9+
10+
fn invalid_update() {
11+
let mut fake = State::A;
12+
let state = State::A;
13+
#[loop_match]
14+
loop {
15+
fake = 'blk: {
16+
//~^ ERROR: invalid update of the `loop_match` state
17+
match state {
18+
_ => State::B,
19+
}
20+
}
21+
}
22+
}
23+
24+
fn invalid_scrutinee() {
25+
let state = State::A;
26+
#[loop_match]
27+
loop {
28+
state = 'blk: {
29+
match State::A {
30+
//~^ ERROR: invalid match on `loop_match` state
31+
_ => State::B,
32+
}
33+
}
34+
}
35+
}
36+
37+
fn bad_statements_1() {
38+
let state = State::A;
39+
#[loop_match]
40+
loop {
41+
1;
42+
//~^ ERROR: statements are not allowed in this position within a `loop_match`
43+
state = 'blk: {
44+
match State::A {
45+
_ => State::B,
46+
}
47+
}
48+
}
49+
}
50+
51+
fn bad_statements_2() {
52+
let state = State::A;
53+
#[loop_match]
54+
loop {
55+
state = 'blk: {
56+
1;
57+
//~^ ERROR: statements are not allowed in this position within a `loop_match`
58+
match State::A {
59+
_ => State::B,
60+
}
61+
}
62+
}
63+
}
64+
65+
fn bad_rhs_1() {
66+
let state = State::A;
67+
#[loop_match]
68+
loop {
69+
state = State::B
70+
//~^ ERROR: this expression must be a single `match` wrapped in a labelled block
71+
}
72+
}
73+
74+
fn bad_rhs_2() {
75+
let state = State::A;
76+
#[loop_match]
77+
loop {
78+
state = 'blk: {
79+
State::B
80+
//~^ ERROR: this expression must be a single `match` wrapped in a labelled block
81+
}
82+
}
83+
}
84+
85+
fn bad_rhs_3() {
86+
let state = ();
87+
#[loop_match]
88+
loop {
89+
state = 'blk: {
90+
//~^ ERROR: this expression must be a single `match` wrapped in a labelled block
91+
}
92+
}
93+
}
94+
95+
fn missing_assignment() {
96+
let state = State::A;
97+
#[loop_match]
98+
loop {
99+
() //~ ERROR: expected a single assignment expression
100+
}
101+
}
102+
103+
fn empty_loop_body() {
104+
let state = State::A;
105+
#[loop_match]
106+
loop {
107+
//~^ ERROR: expected a single assignment expression
108+
}
109+
}
110+
111+
fn break_without_value() {
112+
let state = State::A;
113+
#[loop_match]
114+
'a: loop {
115+
state = 'blk: {
116+
match state {
117+
State::A => {
118+
#[const_continue]
119+
break 'blk;
120+
//~^ ERROR: mismatched types
121+
}
122+
_ => break 'a,
123+
}
124+
}
125+
}
126+
}
127+
128+
fn break_without_value_unit() {
129+
let state = ();
130+
#[loop_match]
131+
'a: loop {
132+
state = 'blk: {
133+
match state {
134+
() => {
135+
#[const_continue]
136+
break 'blk;
137+
//~^ ERROR: a `const_continue` must break to a label with a value
138+
}
139+
}
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)