diff options
author | Roland Ruckerbauer <[email protected]> | 2020-05-18 22:39:10 +0100 |
---|---|---|
committer | Roland Ruckerbauer <[email protected]> | 2020-05-18 22:39:10 +0100 |
commit | 0fe876925e59aad4765b415d9caaf262a6d43c4c (patch) | |
tree | 2196f6884fe99a580b03f8891ef91c7065ff92cf | |
parent | 38e8f35855efac144373c1b5aab3af050e47e594 (diff) |
Infer return type of loops with value breaks.
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 21 |
2 files changed, 16 insertions, 6 deletions
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> { | |||
218 | #[derive(Clone, Debug)] | 218 | #[derive(Clone, Debug)] |
219 | struct BreakableContext { | 219 | struct BreakableContext { |
220 | pub may_break: bool, | 220 | pub may_break: bool, |
221 | pub break_ty: Ty, | ||
221 | } | 222 | } |
222 | 223 | ||
223 | impl<'a> InferenceContext<'a> { | 224 | 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> { | |||
93 | Ty::Unknown | 93 | Ty::Unknown |
94 | } | 94 | } |
95 | Expr::Loop { body } => { | 95 | Expr::Loop { body } => { |
96 | self.breakables.push(BreakableContext { may_break: false }); | 96 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); |
97 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 97 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
98 | 98 | ||
99 | let ctxt = self.breakables.pop().expect("breakable stack broken"); | 99 | let ctxt = self.breakables.pop().expect("breakable stack broken"); |
@@ -102,13 +102,13 @@ impl<'a> InferenceContext<'a> { | |||
102 | } | 102 | } |
103 | // FIXME handle break with value | 103 | // FIXME handle break with value |
104 | if ctxt.may_break { | 104 | if ctxt.may_break { |
105 | Ty::unit() | 105 | ctxt.break_ty |
106 | } else { | 106 | } else { |
107 | Ty::simple(TypeCtor::Never) | 107 | Ty::simple(TypeCtor::Never) |
108 | } | 108 | } |
109 | } | 109 | } |
110 | Expr::While { condition, body } => { | 110 | Expr::While { condition, body } => { |
111 | self.breakables.push(BreakableContext { may_break: false }); | 111 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); |
112 | // while let is desugared to a match loop, so this is always simple while | 112 | // while let is desugared to a match loop, so this is always simple while |
113 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 113 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
114 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 114 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
@@ -120,7 +120,7 @@ impl<'a> InferenceContext<'a> { | |||
120 | Expr::For { iterable, body, pat } => { | 120 | Expr::For { iterable, body, pat } => { |
121 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 121 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
122 | 122 | ||
123 | self.breakables.push(BreakableContext { may_break: false }); | 123 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); |
124 | let pat_ty = | 124 | let pat_ty = |
125 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 125 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
126 | 126 | ||
@@ -229,12 +229,21 @@ impl<'a> InferenceContext<'a> { | |||
229 | } | 229 | } |
230 | Expr::Continue => Ty::simple(TypeCtor::Never), | 230 | Expr::Continue => Ty::simple(TypeCtor::Never), |
231 | Expr::Break { expr } => { | 231 | Expr::Break { expr } => { |
232 | let mut has_val_ty = None; | ||
233 | |||
232 | if let Some(expr) = expr { | 234 | if let Some(expr) = expr { |
233 | // FIXME handle break with value | 235 | has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); |
234 | self.infer_expr(*expr, &Expectation::none()); | ||
235 | } | 236 | } |
237 | |||
236 | if let Some(ctxt) = self.breakables.last_mut() { | 238 | if let Some(ctxt) = self.breakables.last_mut() { |
237 | ctxt.may_break = true; | 239 | 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 | } | ||
238 | } else { | 247 | } else { |
239 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { | 248 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { |
240 | expr: tgt_expr, | 249 | expr: tgt_expr, |