Skip to content

Commit 1cd78a3

Browse files
committed
correctly infer labelled breaks
1 parent fb469c3 commit 1cd78a3

File tree

9 files changed

+117
-46
lines changed

9 files changed

+117
-46
lines changed

crates/ra_hir_def/src/body/lower.rs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl ExprCollector<'_> {
134134
self.make_expr(expr, Err(SyntheticSyntax))
135135
}
136136
fn empty_block(&mut self) -> ExprId {
137-
self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
137+
self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
138138
}
139139
fn missing_expr(&mut self) -> ExprId {
140140
self.alloc_expr_desugared(Expr::Missing)
@@ -215,7 +215,13 @@ impl ExprCollector<'_> {
215215
ast::Expr::BlockExpr(e) => self.collect_block(e),
216216
ast::Expr::LoopExpr(e) => {
217217
let body = self.collect_block_opt(e.loop_body());
218-
self.alloc_expr(Expr::Loop { body }, syntax_ptr)
218+
self.alloc_expr(
219+
Expr::Loop {
220+
body,
221+
label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
222+
},
223+
syntax_ptr,
224+
)
219225
}
220226
ast::Expr::WhileExpr(e) => {
221227
let body = self.collect_block_opt(e.loop_body());
@@ -230,25 +236,47 @@ impl ExprCollector<'_> {
230236
let pat = self.collect_pat(pat);
231237
let match_expr = self.collect_expr_opt(condition.expr());
232238
let placeholder_pat = self.missing_pat();
233-
let break_ = self.alloc_expr_desugared(Expr::Break { expr: None });
239+
let break_ =
240+
self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
234241
let arms = vec![
235242
MatchArm { pat, expr: body, guard: None },
236243
MatchArm { pat: placeholder_pat, expr: break_, guard: None },
237244
];
238245
let match_expr =
239246
self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
240-
return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr);
247+
return self.alloc_expr(
248+
Expr::Loop {
249+
body: match_expr,
250+
label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
251+
},
252+
syntax_ptr,
253+
);
241254
}
242255
},
243256
};
244257

245-
self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
258+
self.alloc_expr(
259+
Expr::While {
260+
condition,
261+
body,
262+
label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
263+
},
264+
syntax_ptr,
265+
)
246266
}
247267
ast::Expr::ForExpr(e) => {
248268
let iterable = self.collect_expr_opt(e.iterable());
249269
let pat = self.collect_pat_opt(e.pat());
250270
let body = self.collect_block_opt(e.loop_body());
251-
self.alloc_expr(Expr::For { iterable, pat, body }, syntax_ptr)
271+
self.alloc_expr(
272+
Expr::For {
273+
iterable,
274+
pat,
275+
body,
276+
label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
277+
},
278+
syntax_ptr,
279+
)
252280
}
253281
ast::Expr::CallExpr(e) => {
254282
let callee = self.collect_expr_opt(e.expr());
@@ -301,13 +329,18 @@ impl ExprCollector<'_> {
301329
.unwrap_or(Expr::Missing);
302330
self.alloc_expr(path, syntax_ptr)
303331
}
304-
ast::Expr::ContinueExpr(_e) => {
305-
// FIXME: labels
306-
self.alloc_expr(Expr::Continue, syntax_ptr)
332+
ast::Expr::ContinueExpr(e) => {
333+
self.alloc_expr(
334+
Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
335+
syntax_ptr,
336+
)
307337
}
308338
ast::Expr::BreakExpr(e) => {
309339
let expr = e.expr().map(|e| self.collect_expr(e));
310-
self.alloc_expr(Expr::Break { expr }, syntax_ptr)
340+
self.alloc_expr(
341+
Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
342+
syntax_ptr,
343+
)
311344
}
312345
ast::Expr::ParenExpr(e) => {
313346
let inner = self.collect_expr_opt(e.expr());
@@ -529,7 +562,8 @@ impl ExprCollector<'_> {
529562
})
530563
.collect();
531564
let tail = block.expr().map(|e| self.collect_expr(e));
532-
self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr)
565+
let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t));
566+
self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr)
533567
}
534568

535569
fn collect_block_items(&mut self, block: &ast::BlockExpr) {

crates/ra_hir_def/src/body/scope.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ fn compute_block_scopes(
138138
fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
139139
scopes.set_scope(expr, scope);
140140
match &body[expr] {
141-
Expr::Block { statements, tail } => {
141+
Expr::Block { statements, tail, .. } => {
142142
compute_block_scopes(&statements, *tail, body, scopes, scope);
143143
}
144-
Expr::For { iterable, pat, body: body_expr } => {
144+
Expr::For { iterable, pat, body: body_expr, .. } => {
145145
compute_expr_scopes(*iterable, body, scopes, scope);
146146
let scope = scopes.new_scope(scope);
147147
scopes.add_bindings(body, scope, *pat);

crates/ra_hir_def/src/expr.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,22 @@ pub enum Expr {
5252
Block {
5353
statements: Vec<Statement>,
5454
tail: Option<ExprId>,
55+
label: Option<Name>,
5556
},
5657
Loop {
5758
body: ExprId,
59+
label: Option<Name>,
5860
},
5961
While {
6062
condition: ExprId,
6163
body: ExprId,
64+
label: Option<Name>,
6265
},
6366
For {
6467
iterable: ExprId,
6568
pat: PatId,
6669
body: ExprId,
70+
label: Option<Name>,
6771
},
6872
Call {
6973
callee: ExprId,
@@ -79,9 +83,12 @@ pub enum Expr {
7983
expr: ExprId,
8084
arms: Vec<MatchArm>,
8185
},
82-
Continue,
86+
Continue {
87+
label: Option<Name>,
88+
},
8389
Break {
8490
expr: Option<ExprId>,
91+
label: Option<Name>,
8592
},
8693
Return {
8794
expr: Option<ExprId>,
@@ -225,7 +232,7 @@ impl Expr {
225232
f(*else_branch);
226233
}
227234
}
228-
Expr::Block { statements, tail } => {
235+
Expr::Block { statements, tail, .. } => {
229236
for stmt in statements {
230237
match stmt {
231238
Statement::Let { initializer, .. } => {
@@ -241,8 +248,8 @@ impl Expr {
241248
}
242249
}
243250
Expr::TryBlock { body } => f(*body),
244-
Expr::Loop { body } => f(*body),
245-
Expr::While { condition, body } => {
251+
Expr::Loop { body, .. } => f(*body),
252+
Expr::While { condition, body, .. } => {
246253
f(*condition);
247254
f(*body);
248255
}
@@ -268,8 +275,8 @@ impl Expr {
268275
f(arm.expr);
269276
}
270277
}
271-
Expr::Continue => {}
272-
Expr::Break { expr } | Expr::Return { expr } => {
278+
Expr::Continue { .. } => {},
279+
Expr::Break { expr, .. } | Expr::Return { expr } => {
273280
if let Some(expr) = expr {
274281
f(*expr);
275282
}

crates/ra_hir_expand/src/name.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ impl Name {
3737
Name(Repr::TupleField(idx))
3838
}
3939

40+
pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name {
41+
assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME);
42+
Name(Repr::Text(lt.text().clone()))
43+
}
44+
4045
/// Shortcut to create inline plain text name
4146
const fn new_inline_ascii(text: &[u8]) -> Name {
4247
Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))

crates/ra_hir_ty/src/infer.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ struct InferenceContext<'a> {
219219
struct BreakableContext {
220220
pub may_break: bool,
221221
pub break_ty: Ty,
222+
pub label: Option<name::Name>,
223+
}
224+
225+
fn find_breakable<'c>(
226+
ctxs: &'c mut [BreakableContext],
227+
label: Option<&name::Name>,
228+
) -> Option<&'c mut BreakableContext> {
229+
match label {
230+
Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label),
231+
None => ctxs.last_mut(),
232+
}
222233
}
223234

224235
impl<'a> InferenceContext<'a> {

crates/ra_hir_ty/src/infer/expr.rs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use crate::{
2222
};
2323

2424
use super::{
25-
BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
26-
TypeMismatch,
25+
find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
26+
InferenceDiagnostic, TypeMismatch,
2727
};
2828

2929
impl<'a> InferenceContext<'a> {
@@ -86,16 +86,20 @@ impl<'a> InferenceContext<'a> {
8686

8787
self.coerce_merge_branch(&then_ty, &else_ty)
8888
}
89-
Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
89+
Expr::Block { statements, tail, .. } => {
90+
// FIXME: Breakable block inference
91+
self.infer_block(statements, *tail, expected)
92+
}
9093
Expr::TryBlock { body } => {
9194
let _inner = self.infer_expr(*body, expected);
9295
// FIXME should be std::result::Result<{inner}, _>
9396
Ty::Unknown
9497
}
95-
Expr::Loop { body } => {
98+
Expr::Loop { body, label } => {
9699
self.breakables.push(BreakableContext {
97100
may_break: false,
98101
break_ty: self.table.new_type_var(),
102+
label: label.clone(),
99103
});
100104
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
101105

@@ -110,8 +114,12 @@ impl<'a> InferenceContext<'a> {
110114
Ty::simple(TypeCtor::Never)
111115
}
112116
}
113-
Expr::While { condition, body } => {
114-
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
117+
Expr::While { condition, body, label } => {
118+
self.breakables.push(BreakableContext {
119+
may_break: false,
120+
break_ty: Ty::Unknown,
121+
label: label.clone(),
122+
});
115123
// while let is desugared to a match loop, so this is always simple while
116124
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
117125
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@@ -120,10 +128,14 @@ impl<'a> InferenceContext<'a> {
120128
self.diverges = Diverges::Maybe;
121129
Ty::unit()
122130
}
123-
Expr::For { iterable, body, pat } => {
131+
Expr::For { iterable, body, pat, label } => {
124132
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
125133

126-
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
134+
self.breakables.push(BreakableContext {
135+
may_break: false,
136+
break_ty: Ty::Unknown,
137+
label: label.clone(),
138+
});
127139
let pat_ty =
128140
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
129141

@@ -236,23 +248,24 @@ impl<'a> InferenceContext<'a> {
236248
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
237249
self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
238250
}
239-
Expr::Continue => Ty::simple(TypeCtor::Never),
240-
Expr::Break { expr } => {
251+
Expr::Continue { .. } => Ty::simple(TypeCtor::Never),
252+
Expr::Break { expr, label } => {
241253
let val_ty = if let Some(expr) = expr {
242254
self.infer_expr(*expr, &Expectation::none())
243255
} else {
244256
Ty::unit()
245257
};
246258

247-
let last_ty = if let Some(ctxt) = self.breakables.last() {
248-
ctxt.break_ty.clone()
249-
} else {
250-
Ty::Unknown
251-
};
259+
let last_ty =
260+
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
261+
ctxt.break_ty.clone()
262+
} else {
263+
Ty::Unknown
264+
};
252265

253266
let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
254267

255-
if let Some(ctxt) = self.breakables.last_mut() {
268+
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
256269
ctxt.break_ty = merged_type;
257270
ctxt.may_break = true;
258271
} else {

crates/ra_hir_ty/src/tests/simple.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,31 +1969,31 @@ fn foo() {
19691969
25..333 '|| 'ou... }': || -> bool
19701970
28..333 ''outer... }': bool
19711971
41..333 '{ ... }': ()
1972-
55..60 'inner': i32
1973-
63..301 ''inner... }': i32
1972+
55..60 'inner': i8
1973+
63..301 ''inner... }': i8
19741974
76..301 '{ ... }': ()
1975-
94..95 'i': i32
1975+
94..95 'i': bool
19761976
98..114 'Defaul...efault': {unknown}
1977-
98..116 'Defaul...ault()': i32
1977+
98..116 'Defaul...ault()': bool
19781978
130..270 'if (br... }': ()
19791979
134..148 'break 'outer i': !
1980-
147..148 'i': i32
1980+
147..148 'i': bool
19811981
150..209 '{ ... }': ()
1982-
168..194 'loop {...5i8; }': i8
1982+
168..194 'loop {...5i8; }': !
19831983
173..194 '{ brea...5i8; }': ()
19841984
175..191 'break ...er 5i8': !
19851985
188..191 '5i8': i8
19861986
215..270 'if tru... }': ()
19871987
218..222 'true': bool
19881988
223..270 '{ ... }': ()
19891989
241..255 'break 'inner 6': !
1990-
254..255 '6': i32
1990+
254..255 '6': i8
19911991
283..290 'break 7': !
1992-
289..290 '7': i32
1992+
289..290 '7': i8
19931993
311..326 'break inner < 8': !
1994-
317..322 'inner': i32
1994+
317..322 'inner': i8
19951995
317..326 'inner < 8': bool
1996-
325..326 '8': i32
1996+
325..326 '8': i8
19971997
"###
19981998
);
19991999
}

crates/ra_syntax/src/ast/generated/nodes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,7 @@ pub struct BlockExpr {
10811081
impl ast::AttrsOwner for BlockExpr {}
10821082
impl ast::ModuleItemOwner for BlockExpr {}
10831083
impl BlockExpr {
1084+
pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
10841085
pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
10851086
pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
10861087
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }

xtask/src/ast_src.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
10581058
/// [Reference](https://doc.rust-lang.org/reference/expressions/block-expr.html)
10591059
/// [Labels for blocks RFC](https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md)
10601060
struct BlockExpr: AttrsOwner, ModuleItemOwner {
1061-
T!['{'], statements: [Stmt], Expr, T!['}'],
1061+
Label, T!['{'], statements: [Stmt], Expr, T!['}'],
10621062
}
10631063

10641064
/// Return expression.

0 commit comments

Comments
 (0)