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)] #[derive(Clone, Debug)]
struct BreakableContext { struct BreakableContext {
pub may_break: bool, pub may_break: bool,
pub break_ty: Ty,
} }
impl<'a> InferenceContext<'a> { impl<'a> InferenceContext<'a> {

View file

@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> {
Ty::Unknown Ty::Unknown
} }
Expr::Loop { body } => { 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())); self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
let ctxt = self.breakables.pop().expect("breakable stack broken"); let ctxt = self.breakables.pop().expect("breakable stack broken");
@ -102,13 +102,13 @@ impl<'a> InferenceContext<'a> {
} }
// FIXME handle break with value // FIXME handle break with value
if ctxt.may_break { if ctxt.may_break {
Ty::unit() ctxt.break_ty
} else { } else {
Ty::simple(TypeCtor::Never) Ty::simple(TypeCtor::Never)
} }
} }
Expr::While { condition, body } => { 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 // 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(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
self.infer_expr(*body, &Expectation::has_type(Ty::unit())); self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@ -120,7 +120,7 @@ impl<'a> InferenceContext<'a> {
Expr::For { iterable, body, pat } => { Expr::For { iterable, body, pat } => {
let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); 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 = let pat_ty =
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
@ -229,12 +229,21 @@ impl<'a> InferenceContext<'a> {
} }
Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Continue => Ty::simple(TypeCtor::Never),
Expr::Break { expr } => { Expr::Break { expr } => {
let mut has_val_ty = None;
if let Some(expr) = expr { if let Some(expr) = expr {
// FIXME handle break with value has_val_ty = Some(self.infer_expr(*expr, &Expectation::none()));
self.infer_expr(*expr, &Expectation::none());
} }
if let Some(ctxt) = self.breakables.last_mut() { if let Some(ctxt) = self.breakables.last_mut() {
ctxt.may_break = true; 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 { } else {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr, expr: tgt_expr,