From 0fe876925e59aad4765b415d9caaf262a6d43c4c Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Mon, 18 May 2020 23:39:10 +0200 Subject: Infer return type of loops with value breaks. --- crates/ra_hir_ty/src/infer.rs | 1 + crates/ra_hir_ty/src/infer/expr.rs | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2876cb141..957d6e0b5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -218,6 +218,7 @@ struct InferenceContext<'a> { #[derive(Clone, Debug)] struct BreakableContext { pub may_break: bool, + pub break_ty: Ty, } impl<'a> InferenceContext<'a> { diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 0b67d216a..c7aa67fbe 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { 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 @@ impl<'a> InferenceContext<'a> { } // 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 @@ impl<'a> InferenceContext<'a> { 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 @@ impl<'a> InferenceContext<'a> { } 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 => Option) + } + } } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, -- cgit v1.2.3