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/expr.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'crates/ra_hir_ty/src/infer/expr.rs') 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 From 6eaa669da0c7b3730a309db5e320126653b88997 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:03:59 +0200 Subject: loop return value inference: coerce_merge branches --- crates/ra_hir_ty/src/infer/expr.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'crates/ra_hir_ty/src/infer/expr.rs') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index c7aa67fbe..83702ada0 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,14 +93,17 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); + self.breakables.push(BreakableContext { + may_break: false, + break_ty: self.table.new_type_var(), + }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); if ctxt.may_break { self.diverges = Diverges::Maybe; } - // FIXME handle break with value + if ctxt.may_break { ctxt.break_ty } else { @@ -229,26 +232,31 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { - let mut has_val_ty = None; + let val_ty = if let Some(expr) = expr { + self.infer_expr(*expr, &Expectation::none()) + } else { + Ty::unit() + }; - if let Some(expr) = expr { - has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); - } + let mut has_brkctx = false; - 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) - } - } + if self.breakables.last().is_some() { + has_brkctx = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } + + if has_brkctx { + let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + let ctxt = self.breakables.last_mut().expect("This is a bug"); + ctxt.may_break = true; + ctxt.break_ty = merged_type; + } + Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { -- cgit v1.2.3 From 45021cae551826727c32c7499c68ca48d046890f Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 22:52:15 +0200 Subject: Apply suggestion of @flodiebold: Get rid of multiple unwraps --- crates/ra_hir_ty/src/infer/expr.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir_ty/src/infer/expr.rs') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83702ada0..b28724f0e 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -238,25 +238,23 @@ impl<'a> InferenceContext<'a> { Ty::unit() }; - let mut has_brkctx = false; + let last_ty = if let Some(ctxt) = self.breakables.last() { + ctxt.break_ty.clone() + } else { + Ty::Unknown + }; - if self.breakables.last().is_some() { - has_brkctx = true; + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + if let Some(ctxt) = self.breakables.last_mut() { + ctxt.break_ty = merged_type; + ctxt.may_break = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } - if has_brkctx { - let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); - let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); - - let ctxt = self.breakables.last_mut().expect("This is a bug"); - ctxt.may_break = true; - ctxt.break_ty = merged_type; - } - Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { -- cgit v1.2.3