Infer return type of loops with value breaks.

This commit is contained in:
Roland Ruckerbauer 2020-05-18 23:39:10 +02:00
parent 38e8f35855
commit 0fe876925e
2 changed files with 16 additions and 6 deletions

View File

@ -218,6 +218,7 @@ struct InferenceContext<'a> {
#[derive(Clone, Debug)]
struct BreakableContext {
pub may_break: bool,
pub break_ty: Ty,
}
impl<'a> InferenceContext<'a> {

View File

@ -93,7 +93,7 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
Ty::Unknown
}
Expr::Loop { body } => {
self.breakables.push(BreakableContext { may_break: false });
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
let ctxt = self.breakables.pop().expect("breakable stack broken");
@ -102,13 +102,13 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
}
// FIXME handle break with value
if ctxt.may_break {
Ty::unit()
ctxt.break_ty
} else {
Ty::simple(TypeCtor::Never)
}
}
Expr::While { condition, body } => {
self.breakables.push(BreakableContext { may_break: false });
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
// while let is desugared to a match loop, so this is always simple while
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@ -120,7 +120,7 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
Expr::For { iterable, body, pat } => {
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
self.breakables.push(BreakableContext { may_break: false });
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
let pat_ty =
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
@ -229,12 +229,21 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
}
Expr::Continue => Ty::simple(TypeCtor::Never),
Expr::Break { expr } => {
let mut has_val_ty = None;
if let Some(expr) = expr {
// FIXME handle break with value
self.infer_expr(*expr, &Expectation::none());
has_val_ty = Some(self.infer_expr(*expr, &Expectation::none()));
}
if let Some(ctxt) = self.breakables.last_mut() {
ctxt.may_break = true;
if let Some(val_ty) = has_val_ty {
if ctxt.break_ty == Ty::Unknown {
ctxt.break_ty = val_ty;
} else if ctxt.break_ty != val_ty {
// TODO: Unify partially matching type information (Option<{unknown}> + Option<i32> => Option<i32>)
}
}
} else {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,