Skip to content

Commit 0fe8769

Browse files
committed
Infer return type of loops with value breaks.
1 parent 38e8f35 commit 0fe8769

File tree

2 files changed

+16
-6
lines changed

2 files changed

+16
-6
lines changed

crates/ra_hir_ty/src/infer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ struct InferenceContext<'a> {
218218
#[derive(Clone, Debug)]
219219
struct BreakableContext {
220220
pub may_break: bool,
221+
pub break_ty: Ty,
221222
}
222223

223224
impl<'a> InferenceContext<'a> {

crates/ra_hir_ty/src/infer/expr.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> {
9393
Ty::Unknown
9494
}
9595
Expr::Loop { body } => {
96-
self.breakables.push(BreakableContext { may_break: false });
96+
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
9797
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
9898

9999
let ctxt = self.breakables.pop().expect("breakable stack broken");
@@ -102,13 +102,13 @@ impl<'a> InferenceContext<'a> {
102102
}
103103
// FIXME handle break with value
104104
if ctxt.may_break {
105-
Ty::unit()
105+
ctxt.break_ty
106106
} else {
107107
Ty::simple(TypeCtor::Never)
108108
}
109109
}
110110
Expr::While { condition, body } => {
111-
self.breakables.push(BreakableContext { may_break: false });
111+
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
112112
// while let is desugared to a match loop, so this is always simple while
113113
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
114114
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@@ -120,7 +120,7 @@ impl<'a> InferenceContext<'a> {
120120
Expr::For { iterable, body, pat } => {
121121
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
122122

123-
self.breakables.push(BreakableContext { may_break: false });
123+
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
124124
let pat_ty =
125125
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
126126

@@ -229,12 +229,21 @@ impl<'a> InferenceContext<'a> {
229229
}
230230
Expr::Continue => Ty::simple(TypeCtor::Never),
231231
Expr::Break { expr } => {
232+
let mut has_val_ty = None;
233+
232234
if let Some(expr) = expr {
233-
// FIXME handle break with value
234-
self.infer_expr(*expr, &Expectation::none());
235+
has_val_ty = Some(self.infer_expr(*expr, &Expectation::none()));
235236
}
237+
236238
if let Some(ctxt) = self.breakables.last_mut() {
237239
ctxt.may_break = true;
240+
if let Some(val_ty) = has_val_ty {
241+
if ctxt.break_ty == Ty::Unknown {
242+
ctxt.break_ty = val_ty;
243+
} else if ctxt.break_ty != val_ty {
244+
// TODO: Unify partially matching type information (Option<{unknown}> + Option<i32> => Option<i32>)
245+
}
246+
}
238247
} else {
239248
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
240249
expr: tgt_expr,

0 commit comments

Comments
 (0)