diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-05-08 19:09:25 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-05-08 19:09:25 +0100 |
commit | f9ec7cebef732fbc9d4849d87d325efef5faadea (patch) | |
tree | 4a40dbed04c432dee54f8a8d016c89c7411b1e6d /crates/ra_hir_ty/src/infer | |
parent | f1fa9aa4c4d4fcfe7d6e90ba9cefca90bc7c4998 (diff) | |
parent | d0129c4ddba3b72e7b26e94e9c25546d37dbf166 (diff) |
Merge #4377
4377: Implement better handling of divergence r=matklad a=flodiebold
Divergence here means that for some reason, the end of a block will not be reached. We tried to model this just using the never type, but that doesn't work fully (e.g. in `let x = { loop {}; "foo" };` x should still have type `&str`); so this introduces a `diverges` flag that the type checker keeps track of, like rustc does. We also add some checking for `break`, but no support for break-with-value or labeled breaks yet.
Co-authored-by: Florian Diebold <[email protected]>
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_ty/src/infer')
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 84 |
1 files changed, 69 insertions, 15 deletions
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 614c352a0..0b67d216a 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Type inference for expressions. | 1 | //! Type inference for expressions. |
2 | 2 | ||
3 | use std::iter::{repeat, repeat_with}; | 3 | use std::iter::{repeat, repeat_with}; |
4 | use std::sync::Arc; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | builtin_type::Signedness, | 7 | builtin_type::Signedness, |
@@ -21,11 +21,18 @@ use crate::{ | |||
21 | Ty, TypeCtor, Uncertain, | 21 | Ty, TypeCtor, Uncertain, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; | 24 | use super::{ |
25 | BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, | ||
26 | TypeMismatch, | ||
27 | }; | ||
25 | 28 | ||
26 | impl<'a> InferenceContext<'a> { | 29 | impl<'a> InferenceContext<'a> { |
27 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 30 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
28 | let ty = self.infer_expr_inner(tgt_expr, expected); | 31 | let ty = self.infer_expr_inner(tgt_expr, expected); |
32 | if ty.is_never() { | ||
33 | // Any expression that produces a value of type `!` must have diverged | ||
34 | self.diverges = Diverges::Always; | ||
35 | } | ||
29 | let could_unify = self.unify(&ty, &expected.ty); | 36 | let could_unify = self.unify(&ty, &expected.ty); |
30 | if !could_unify { | 37 | if !could_unify { |
31 | self.result.type_mismatches.insert( | 38 | self.result.type_mismatches.insert( |
@@ -64,11 +71,18 @@ impl<'a> InferenceContext<'a> { | |||
64 | // if let is desugared to match, so this is always simple if | 71 | // if let is desugared to match, so this is always simple if |
65 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 72 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
66 | 73 | ||
74 | let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | ||
75 | let mut both_arms_diverge = Diverges::Always; | ||
76 | |||
67 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 77 | let then_ty = self.infer_expr_inner(*then_branch, &expected); |
78 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | ||
68 | let else_ty = match else_branch { | 79 | let else_ty = match else_branch { |
69 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 80 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), |
70 | None => Ty::unit(), | 81 | None => Ty::unit(), |
71 | }; | 82 | }; |
83 | both_arms_diverge &= self.diverges; | ||
84 | |||
85 | self.diverges = condition_diverges | both_arms_diverge; | ||
72 | 86 | ||
73 | self.coerce_merge_branch(&then_ty, &else_ty) | 87 | self.coerce_merge_branch(&then_ty, &else_ty) |
74 | } | 88 | } |
@@ -79,24 +93,43 @@ impl<'a> InferenceContext<'a> { | |||
79 | Ty::Unknown | 93 | Ty::Unknown |
80 | } | 94 | } |
81 | Expr::Loop { body } => { | 95 | Expr::Loop { body } => { |
96 | self.breakables.push(BreakableContext { may_break: false }); | ||
82 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 97 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
98 | |||
99 | let ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
100 | if ctxt.may_break { | ||
101 | self.diverges = Diverges::Maybe; | ||
102 | } | ||
83 | // FIXME handle break with value | 103 | // FIXME handle break with value |
84 | Ty::simple(TypeCtor::Never) | 104 | if ctxt.may_break { |
105 | Ty::unit() | ||
106 | } else { | ||
107 | Ty::simple(TypeCtor::Never) | ||
108 | } | ||
85 | } | 109 | } |
86 | Expr::While { condition, body } => { | 110 | Expr::While { condition, body } => { |
111 | self.breakables.push(BreakableContext { may_break: false }); | ||
87 | // 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 |
88 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 113 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
89 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 114 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
115 | let _ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
116 | // the body may not run, so it diverging doesn't mean we diverge | ||
117 | self.diverges = Diverges::Maybe; | ||
90 | Ty::unit() | 118 | Ty::unit() |
91 | } | 119 | } |
92 | Expr::For { iterable, body, pat } => { | 120 | Expr::For { iterable, body, pat } => { |
93 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 121 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
94 | 122 | ||
123 | self.breakables.push(BreakableContext { may_break: false }); | ||
95 | let pat_ty = | 124 | let pat_ty = |
96 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 125 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
97 | 126 | ||
98 | self.infer_pat(*pat, &pat_ty, BindingMode::default()); | 127 | self.infer_pat(*pat, &pat_ty, BindingMode::default()); |
128 | |||
99 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 129 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
130 | let _ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
131 | // the body may not run, so it diverging doesn't mean we diverge | ||
132 | self.diverges = Diverges::Maybe; | ||
100 | Ty::unit() | 133 | Ty::unit() |
101 | } | 134 | } |
102 | Expr::Lambda { body, args, ret_type, arg_types } => { | 135 | Expr::Lambda { body, args, ret_type, arg_types } => { |
@@ -132,10 +165,12 @@ impl<'a> InferenceContext<'a> { | |||
132 | // infer the body. | 165 | // infer the body. |
133 | self.coerce(&closure_ty, &expected.ty); | 166 | self.coerce(&closure_ty, &expected.ty); |
134 | 167 | ||
135 | let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); | 168 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); |
169 | let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); | ||
136 | 170 | ||
137 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); | 171 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); |
138 | 172 | ||
173 | self.diverges = prev_diverges; | ||
139 | self.return_ty = prev_ret_ty; | 174 | self.return_ty = prev_ret_ty; |
140 | 175 | ||
141 | closure_ty | 176 | closure_ty |
@@ -165,7 +200,11 @@ impl<'a> InferenceContext<'a> { | |||
165 | self.table.new_type_var() | 200 | self.table.new_type_var() |
166 | }; | 201 | }; |
167 | 202 | ||
203 | let matchee_diverges = self.diverges; | ||
204 | let mut all_arms_diverge = Diverges::Always; | ||
205 | |||
168 | for arm in arms { | 206 | for arm in arms { |
207 | self.diverges = Diverges::Maybe; | ||
169 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); | 208 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); |
170 | if let Some(guard_expr) = arm.guard { | 209 | if let Some(guard_expr) = arm.guard { |
171 | self.infer_expr( | 210 | self.infer_expr( |
@@ -175,9 +214,12 @@ impl<'a> InferenceContext<'a> { | |||
175 | } | 214 | } |
176 | 215 | ||
177 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); | 216 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); |
217 | all_arms_diverge &= self.diverges; | ||
178 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); | 218 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); |
179 | } | 219 | } |
180 | 220 | ||
221 | self.diverges = matchee_diverges | all_arms_diverge; | ||
222 | |||
181 | result_ty | 223 | result_ty |
182 | } | 224 | } |
183 | Expr::Path(p) => { | 225 | Expr::Path(p) => { |
@@ -191,6 +233,13 @@ impl<'a> InferenceContext<'a> { | |||
191 | // FIXME handle break with value | 233 | // FIXME handle break with value |
192 | self.infer_expr(*expr, &Expectation::none()); | 234 | self.infer_expr(*expr, &Expectation::none()); |
193 | } | 235 | } |
236 | if let Some(ctxt) = self.breakables.last_mut() { | ||
237 | ctxt.may_break = true; | ||
238 | } else { | ||
239 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { | ||
240 | expr: tgt_expr, | ||
241 | }); | ||
242 | } | ||
194 | Ty::simple(TypeCtor::Never) | 243 | Ty::simple(TypeCtor::Never) |
195 | } | 244 | } |
196 | Expr::Return { expr } => { | 245 | Expr::Return { expr } => { |
@@ -522,7 +571,6 @@ impl<'a> InferenceContext<'a> { | |||
522 | tail: Option<ExprId>, | 571 | tail: Option<ExprId>, |
523 | expected: &Expectation, | 572 | expected: &Expectation, |
524 | ) -> Ty { | 573 | ) -> Ty { |
525 | let mut diverges = false; | ||
526 | for stmt in statements { | 574 | for stmt in statements { |
527 | match stmt { | 575 | match stmt { |
528 | Statement::Let { pat, type_ref, initializer } => { | 576 | Statement::Let { pat, type_ref, initializer } => { |
@@ -544,9 +592,7 @@ impl<'a> InferenceContext<'a> { | |||
544 | self.infer_pat(*pat, &ty, BindingMode::default()); | 592 | self.infer_pat(*pat, &ty, BindingMode::default()); |
545 | } | 593 | } |
546 | Statement::Expr(expr) => { | 594 | Statement::Expr(expr) => { |
547 | if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { | 595 | self.infer_expr(*expr, &Expectation::none()); |
548 | diverges = true; | ||
549 | } | ||
550 | } | 596 | } |
551 | } | 597 | } |
552 | } | 598 | } |
@@ -554,14 +600,22 @@ impl<'a> InferenceContext<'a> { | |||
554 | let ty = if let Some(expr) = tail { | 600 | let ty = if let Some(expr) = tail { |
555 | self.infer_expr_coerce(expr, expected) | 601 | self.infer_expr_coerce(expr, expected) |
556 | } else { | 602 | } else { |
557 | self.coerce(&Ty::unit(), expected.coercion_target()); | 603 | // Citing rustc: if there is no explicit tail expression, |
558 | Ty::unit() | 604 | // that is typically equivalent to a tail expression |
605 | // of `()` -- except if the block diverges. In that | ||
606 | // case, there is no value supplied from the tail | ||
607 | // expression (assuming there are no other breaks, | ||
608 | // this implies that the type of the block will be | ||
609 | // `!`). | ||
610 | if self.diverges.is_always() { | ||
611 | // we don't even make an attempt at coercion | ||
612 | self.table.new_maybe_never_type_var() | ||
613 | } else { | ||
614 | self.coerce(&Ty::unit(), expected.coercion_target()); | ||
615 | Ty::unit() | ||
616 | } | ||
559 | }; | 617 | }; |
560 | if diverges { | 618 | ty |
561 | Ty::simple(TypeCtor::Never) | ||
562 | } else { | ||
563 | ty | ||
564 | } | ||
565 | } | 619 | } |
566 | 620 | ||
567 | fn infer_method_call( | 621 | fn infer_method_call( |