Skip to content

Commit b60970f

Browse files
committed
Handle break somewhat better
Still no break-with-value or labels, but at least we know that `loop { break; }` doesn't diverge.
1 parent fe7bf99 commit b60970f

File tree

3 files changed

+105
-3
lines changed

3 files changed

+105
-3
lines changed

crates/ra_hir_ty/src/infer.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,12 @@ struct InferenceContext<'a> {
211211
/// so it doesn't make sense.
212212
return_ty: Ty,
213213
diverges: Diverges,
214+
breakables: Vec<BreakableContext>,
215+
}
216+
217+
#[derive(Clone, Debug)]
218+
struct BreakableContext {
219+
pub may_break: bool,
214220
}
215221

216222
impl<'a> InferenceContext<'a> {
@@ -226,6 +232,7 @@ impl<'a> InferenceContext<'a> {
226232
body: db.body(owner),
227233
resolver,
228234
diverges: Diverges::Maybe,
235+
breakables: Vec::new(),
229236
}
230237
}
231238

crates/ra_hir_ty/src/infer/expr.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ use crate::{
2121
Ty, TypeCtor, Uncertain,
2222
};
2323

24-
use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges};
24+
use super::{
25+
BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
26+
TypeMismatch,
27+
};
2528

2629
impl<'a> InferenceContext<'a> {
2730
pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
@@ -90,24 +93,43 @@ impl<'a> InferenceContext<'a> {
9093
Ty::Unknown
9194
}
9295
Expr::Loop { body } => {
96+
self.breakables.push(BreakableContext { may_break: false });
9397
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
98+
99+
let ctxt = self.breakables.pop().expect("breakable stack broken");
100+
if ctxt.may_break {
101+
self.diverges = Diverges::Maybe;
102+
}
94103
// FIXME handle break with value
95-
Ty::simple(TypeCtor::Never)
104+
if ctxt.may_break {
105+
Ty::unit()
106+
} else {
107+
Ty::simple(TypeCtor::Never)
108+
}
96109
}
97110
Expr::While { condition, body } => {
111+
self.breakables.push(BreakableContext { may_break: false });
98112
// while let is desugared to a match loop, so this is always simple while
99113
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
100114
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
115+
let _ctxt = self.breakables.pop().expect("breakable stack broken");
116+
// the body may not run, so it diverging doesn't mean we diverge
117+
self.diverges = Diverges::Maybe;
101118
Ty::unit()
102119
}
103120
Expr::For { iterable, body, pat } => {
104121
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
105122

123+
self.breakables.push(BreakableContext { may_break: false });
106124
let pat_ty =
107125
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
108126

109127
self.infer_pat(*pat, &pat_ty, BindingMode::default());
128+
110129
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
130+
let _ctxt = self.breakables.pop().expect("breakable stack broken");
131+
// the body may not run, so it diverging doesn't mean we diverge
132+
self.diverges = Diverges::Maybe;
111133
Ty::unit()
112134
}
113135
Expr::Lambda { body, args, ret_type, arg_types } => {
@@ -211,6 +233,9 @@ impl<'a> InferenceContext<'a> {
211233
// FIXME handle break with value
212234
self.infer_expr(*expr, &Expectation::none());
213235
}
236+
if let Some(ctxt) = self.breakables.last_mut() {
237+
ctxt.may_break = true;
238+
}
214239
Ty::simple(TypeCtor::Never)
215240
}
216241
Expr::Return { expr } => {

crates/ra_hir_ty/src/tests/never_type.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,78 @@ fn test1() {
361361
// should give type mismatch
362362
let x: u32 = { loop { break; } };
363363
}
364+
fn test2() {
365+
// should give type mismatch
366+
let x: u32 = { for a in b { break; }; };
367+
// should give type mismatch as well
368+
let x: u32 = { for a in b {}; };
369+
// should give type mismatch as well
370+
let x: u32 = { for a in b { return; }; };
371+
}
372+
fn test3() {
373+
// should give type mismatch
374+
let x: u32 = { while true { break; }; };
375+
// should give type mismatch as well -- there's an implicit break, even if it's never hit
376+
let x: u32 = { while true {}; };
377+
// should give type mismatch as well
378+
let x: u32 = { while true { return; }; };
379+
}
364380
"#,
365381
true,
366382
);
367-
assert_snapshot!(t, @r###""###);
383+
assert_snapshot!(t, @r###"
384+
25..99 '{ ...} }; }': ()
385+
68..69 'x': u32
386+
77..96 '{ loop...k; } }': ()
387+
79..94 'loop { break; }': ()
388+
84..94 '{ break; }': ()
389+
86..91 'break': !
390+
77..96: expected u32, got ()
391+
79..94: expected u32, got ()
392+
111..357 '{ ...; }; }': ()
393+
154..155 'x': u32
394+
163..189 '{ for ...; }; }': ()
395+
165..186 'for a ...eak; }': ()
396+
169..170 'a': {unknown}
397+
174..175 'b': {unknown}
398+
176..186 '{ break; }': ()
399+
178..183 'break': !
400+
240..241 'x': u32
401+
249..267 '{ for ... {}; }': ()
402+
251..264 'for a in b {}': ()
403+
255..256 'a': {unknown}
404+
260..261 'b': {unknown}
405+
262..264 '{}': ()
406+
318..319 'x': u32
407+
327..354 '{ for ...; }; }': ()
408+
329..351 'for a ...urn; }': ()
409+
333..334 'a': {unknown}
410+
338..339 'b': {unknown}
411+
340..351 '{ return; }': ()
412+
342..348 'return': !
413+
163..189: expected u32, got ()
414+
249..267: expected u32, got ()
415+
327..354: expected u32, got ()
416+
369..668 '{ ...; }; }': ()
417+
412..413 'x': u32
418+
421..447 '{ whil...; }; }': ()
419+
423..444 'while ...eak; }': ()
420+
429..433 'true': bool
421+
434..444 '{ break; }': ()
422+
436..441 'break': !
423+
551..552 'x': u32
424+
560..578 '{ whil... {}; }': ()
425+
562..575 'while true {}': ()
426+
568..572 'true': bool
427+
573..575 '{}': ()
428+
629..630 'x': u32
429+
638..665 '{ whil...; }; }': ()
430+
640..662 'while ...urn; }': ()
431+
646..650 'true': bool
432+
651..662 '{ return; }': ()
433+
653..659 'return': !
434+
421..447: expected u32, got ()
435+
560..578: expected u32, got ()
436+
638..665: expected u32, got ()
437+
"###);
368438
}

0 commit comments

Comments
 (0)