aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2020-05-08 16:36:11 +0100
committerFlorian Diebold <[email protected]>2020-05-08 17:15:24 +0100
commitfe7bf993aa8d64668707e348f2ea69918cfda9a4 (patch)
tree9d3fc4f969dbd06d982dda6dadc6325a668e7eb1 /crates/ra_hir_ty
parentd3eb9d8eafbebca7da95fa8a4813b92eb5080500 (diff)
Implement better handling of divergence
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.
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/infer.rs40
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs53
-rw-r--r--crates/ra_hir_ty/src/lib.rs7
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs2
-rw-r--r--crates/ra_hir_ty/src/tests/never_type.rs107
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs10
7 files changed, 200 insertions, 23 deletions
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index bd4ef69a0..d3a066268 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -210,6 +210,7 @@ struct InferenceContext<'a> {
210 /// closures, but currently this is the only field that will change there, 210 /// closures, but currently this is the only field that will change there,
211 /// so it doesn't make sense. 211 /// so it doesn't make sense.
212 return_ty: Ty, 212 return_ty: Ty,
213 diverges: Diverges,
213} 214}
214 215
215impl<'a> InferenceContext<'a> { 216impl<'a> InferenceContext<'a> {
@@ -224,6 +225,7 @@ impl<'a> InferenceContext<'a> {
224 owner, 225 owner,
225 body: db.body(owner), 226 body: db.body(owner),
226 resolver, 227 resolver,
228 diverges: Diverges::Maybe,
227 } 229 }
228 } 230 }
229 231
@@ -666,6 +668,44 @@ impl Expectation {
666 } 668 }
667} 669}
668 670
671#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
672enum Diverges {
673 Maybe,
674 Always,
675}
676
677impl Diverges {
678 fn is_always(self) -> bool {
679 self == Diverges::Always
680 }
681}
682
683impl std::ops::BitAnd for Diverges {
684 type Output = Self;
685 fn bitand(self, other: Self) -> Self {
686 std::cmp::min(self, other)
687 }
688}
689
690impl std::ops::BitOr for Diverges {
691 type Output = Self;
692 fn bitor(self, other: Self) -> Self {
693 std::cmp::max(self, other)
694 }
695}
696
697impl std::ops::BitAndAssign for Diverges {
698 fn bitand_assign(&mut self, other: Self) {
699 *self = *self & other;
700 }
701}
702
703impl std::ops::BitOrAssign for Diverges {
704 fn bitor_assign(&mut self, other: Self) {
705 *self = *self | other;
706 }
707}
708
669mod diagnostics { 709mod diagnostics {
670 use hir_def::{expr::ExprId, FunctionId}; 710 use hir_def::{expr::ExprId, FunctionId};
671 use hir_expand::diagnostics::DiagnosticSink; 711 use hir_expand::diagnostics::DiagnosticSink;
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 614c352a0..f2f9883b2 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
3use std::iter::{repeat, repeat_with}; 3use std::iter::{repeat, repeat_with};
4use std::sync::Arc; 4use std::{mem, sync::Arc};
5 5
6use hir_def::{ 6use hir_def::{
7 builtin_type::Signedness, 7 builtin_type::Signedness,
@@ -21,11 +21,15 @@ use crate::{
21 Ty, TypeCtor, Uncertain, 21 Ty, TypeCtor, Uncertain,
22}; 22};
23 23
24use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; 24use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges};
25 25
26impl<'a> InferenceContext<'a> { 26impl<'a> InferenceContext<'a> {
27 pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { 27 pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
28 let ty = self.infer_expr_inner(tgt_expr, expected); 28 let ty = self.infer_expr_inner(tgt_expr, expected);
29 if ty.is_never() {
30 // Any expression that produces a value of type `!` must have diverged
31 self.diverges = Diverges::Always;
32 }
29 let could_unify = self.unify(&ty, &expected.ty); 33 let could_unify = self.unify(&ty, &expected.ty);
30 if !could_unify { 34 if !could_unify {
31 self.result.type_mismatches.insert( 35 self.result.type_mismatches.insert(
@@ -64,11 +68,18 @@ impl<'a> InferenceContext<'a> {
64 // if let is desugared to match, so this is always simple if 68 // if let is desugared to match, so this is always simple if
65 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); 69 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
66 70
71 let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
72 let mut both_arms_diverge = Diverges::Always;
73
67 let then_ty = self.infer_expr_inner(*then_branch, &expected); 74 let then_ty = self.infer_expr_inner(*then_branch, &expected);
75 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
68 let else_ty = match else_branch { 76 let else_ty = match else_branch {
69 Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), 77 Some(else_branch) => self.infer_expr_inner(*else_branch, &expected),
70 None => Ty::unit(), 78 None => Ty::unit(),
71 }; 79 };
80 both_arms_diverge &= self.diverges;
81
82 self.diverges = condition_diverges | both_arms_diverge;
72 83
73 self.coerce_merge_branch(&then_ty, &else_ty) 84 self.coerce_merge_branch(&then_ty, &else_ty)
74 } 85 }
@@ -132,10 +143,12 @@ impl<'a> InferenceContext<'a> {
132 // infer the body. 143 // infer the body.
133 self.coerce(&closure_ty, &expected.ty); 144 self.coerce(&closure_ty, &expected.ty);
134 145
135 let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); 146 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
147 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
136 148
137 self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); 149 self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
138 150
151 self.diverges = prev_diverges;
139 self.return_ty = prev_ret_ty; 152 self.return_ty = prev_ret_ty;
140 153
141 closure_ty 154 closure_ty
@@ -165,7 +178,11 @@ impl<'a> InferenceContext<'a> {
165 self.table.new_type_var() 178 self.table.new_type_var()
166 }; 179 };
167 180
181 let matchee_diverges = self.diverges;
182 let mut all_arms_diverge = Diverges::Always;
183
168 for arm in arms { 184 for arm in arms {
185 self.diverges = Diverges::Maybe;
169 let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); 186 let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
170 if let Some(guard_expr) = arm.guard { 187 if let Some(guard_expr) = arm.guard {
171 self.infer_expr( 188 self.infer_expr(
@@ -175,9 +192,12 @@ impl<'a> InferenceContext<'a> {
175 } 192 }
176 193
177 let arm_ty = self.infer_expr_inner(arm.expr, &expected); 194 let arm_ty = self.infer_expr_inner(arm.expr, &expected);
195 all_arms_diverge &= self.diverges;
178 result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); 196 result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
179 } 197 }
180 198
199 self.diverges = matchee_diverges | all_arms_diverge;
200
181 result_ty 201 result_ty
182 } 202 }
183 Expr::Path(p) => { 203 Expr::Path(p) => {
@@ -522,7 +542,6 @@ impl<'a> InferenceContext<'a> {
522 tail: Option<ExprId>, 542 tail: Option<ExprId>,
523 expected: &Expectation, 543 expected: &Expectation,
524 ) -> Ty { 544 ) -> Ty {
525 let mut diverges = false;
526 for stmt in statements { 545 for stmt in statements {
527 match stmt { 546 match stmt {
528 Statement::Let { pat, type_ref, initializer } => { 547 Statement::Let { pat, type_ref, initializer } => {
@@ -544,9 +563,7 @@ impl<'a> InferenceContext<'a> {
544 self.infer_pat(*pat, &ty, BindingMode::default()); 563 self.infer_pat(*pat, &ty, BindingMode::default());
545 } 564 }
546 Statement::Expr(expr) => { 565 Statement::Expr(expr) => {
547 if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { 566 self.infer_expr(*expr, &Expectation::none());
548 diverges = true;
549 }
550 } 567 }
551 } 568 }
552 } 569 }
@@ -554,14 +571,22 @@ impl<'a> InferenceContext<'a> {
554 let ty = if let Some(expr) = tail { 571 let ty = if let Some(expr) = tail {
555 self.infer_expr_coerce(expr, expected) 572 self.infer_expr_coerce(expr, expected)
556 } else { 573 } else {
557 self.coerce(&Ty::unit(), expected.coercion_target()); 574 // Citing rustc: if there is no explicit tail expression,
558 Ty::unit() 575 // that is typically equivalent to a tail expression
576 // of `()` -- except if the block diverges. In that
577 // case, there is no value supplied from the tail
578 // expression (assuming there are no other breaks,
579 // this implies that the type of the block will be
580 // `!`).
581 if self.diverges.is_always() {
582 // we don't even make an attempt at coercion
583 self.table.new_maybe_never_type_var()
584 } else {
585 self.coerce(&Ty::unit(), expected.coercion_target());
586 Ty::unit()
587 }
559 }; 588 };
560 if diverges { 589 ty
561 Ty::simple(TypeCtor::Never)
562 } else {
563 ty
564 }
565 } 590 }
566 591
567 fn infer_method_call( 592 fn infer_method_call(
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index a6f56c661..ac0ef1bfe 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -730,6 +730,13 @@ impl Ty {
730 } 730 }
731 } 731 }
732 732
733 pub fn is_never(&self) -> bool {
734 match self {
735 Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => true,
736 _ => false,
737 }
738 }
739
733 /// If this is a `dyn Trait` type, this returns the `Trait` part. 740 /// If this is a `dyn Trait` type, this returns the `Trait` part.
734 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { 741 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
735 match self { 742 match self {
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index e6fb3e123..0c3a833bd 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -384,7 +384,7 @@ fn foo() -> u32 {
384} 384}
385"#, true), 385"#, true),
386 @r###" 386 @r###"
387 17..40 '{ ...own; }': ! 387 17..40 '{ ...own; }': u32
388 23..37 'return unknown': ! 388 23..37 'return unknown': !
389 30..37 'unknown': u32 389 30..37 'unknown': u32
390 "### 390 "###
@@ -514,7 +514,7 @@ fn foo() {
514 27..103 '{ ... }': &u32 514 27..103 '{ ... }': &u32
515 37..82 'if tru... }': () 515 37..82 'if tru... }': ()
516 40..44 'true': bool 516 40..44 'true': bool
517 45..82 '{ ... }': ! 517 45..82 '{ ... }': ()
518 59..71 'return &1u32': ! 518 59..71 'return &1u32': !
519 66..71 '&1u32': &u32 519 66..71 '&1u32': &u32
520 67..71 '1u32': u32 520 67..71 '1u32': u32
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 07398ddcc..4c6099aa2 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -197,7 +197,7 @@ fn spam() {
197 !0..6 '1isize': isize 197 !0..6 '1isize': isize
198 !0..6 '1isize': isize 198 !0..6 '1isize': isize
199 !0..6 '1isize': isize 199 !0..6 '1isize': isize
200 54..457 '{ ...!(); }': ! 200 54..457 '{ ...!(); }': ()
201 88..109 'spam!(...am!())': {unknown} 201 88..109 'spam!(...am!())': {unknown}
202 115..134 'for _ ...!() {}': () 202 115..134 'for _ ...!() {}': ()
203 119..120 '_': {unknown} 203 119..120 '_': {unknown}
diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs
index a77209480..1721f97c5 100644
--- a/crates/ra_hir_ty/src/tests/never_type.rs
+++ b/crates/ra_hir_ty/src/tests/never_type.rs
@@ -1,4 +1,6 @@
1use super::type_at; 1use insta::assert_snapshot;
2
3use super::{infer_with_mismatches, type_at};
2 4
3#[test] 5#[test]
4fn infer_never1() { 6fn infer_never1() {
@@ -261,3 +263,106 @@ fn test(a: i32) {
261 ); 263 );
262 assert_eq!(t, "f64"); 264 assert_eq!(t, "f64");
263} 265}
266
267#[test]
268fn diverging_expression_1() {
269 let t = infer_with_mismatches(
270 r#"
271//- /main.rs
272fn test1() {
273 let x: u32 = return;
274}
275fn test2() {
276 let x: u32 = { return; };
277}
278fn test3() {
279 let x: u32 = loop {};
280}
281fn test4() {
282 let x: u32 = { loop {} };
283}
284fn test5() {
285 let x: u32 = { if true { loop {}; } else { loop {}; } };
286}
287fn test6() {
288 let x: u32 = { let y: u32 = { loop {}; }; };
289}
290"#,
291 true,
292 );
293 assert_snapshot!(t, @r###"
294 25..53 '{ ...urn; }': ()
295 35..36 'x': u32
296 44..50 'return': !
297 65..98 '{ ...; }; }': ()
298 75..76 'x': u32
299 84..95 '{ return; }': u32
300 86..92 'return': !
301 110..139 '{ ... {}; }': ()
302 120..121 'x': u32
303 129..136 'loop {}': !
304 134..136 '{}': ()
305 151..184 '{ ...} }; }': ()
306 161..162 'x': u32
307 170..181 '{ loop {} }': u32
308 172..179 'loop {}': !
309 177..179 '{}': ()
310 196..260 '{ ...} }; }': ()
311 206..207 'x': u32
312 215..257 '{ if t...}; } }': u32
313 217..255 'if tru... {}; }': u32
314 220..224 'true': bool
315 225..237 '{ loop {}; }': u32
316 227..234 'loop {}': !
317 232..234 '{}': ()
318 243..255 '{ loop {}; }': u32
319 245..252 'loop {}': !
320 250..252 '{}': ()
321 272..324 '{ ...; }; }': ()
322 282..283 'x': u32
323 291..321 '{ let ...; }; }': u32
324 297..298 'y': u32
325 306..318 '{ loop {}; }': u32
326 308..315 'loop {}': !
327 313..315 '{}': ()
328 "###);
329}
330
331#[test]
332fn diverging_expression_2() {
333 let t = infer_with_mismatches(
334 r#"
335//- /main.rs
336fn test1() {
337 // should give type mismatch
338 let x: u32 = { loop {}; "foo" };
339}
340"#,
341 true,
342 );
343 assert_snapshot!(t, @r###"
344 25..98 '{ ..." }; }': ()
345 68..69 'x': u32
346 77..95 '{ loop...foo" }': &str
347 79..86 'loop {}': !
348 84..86 '{}': ()
349 88..93 '"foo"': &str
350 77..95: expected u32, got &str
351 88..93: expected u32, got &str
352 "###);
353}
354
355#[test]
356fn diverging_expression_3_break() {
357 let t = infer_with_mismatches(
358 r#"
359//- /main.rs
360fn test1() {
361 // should give type mismatch
362 let x: u32 = { loop { break; } };
363}
364"#,
365 true,
366 );
367 assert_snapshot!(t, @r###""###);
368}
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index e17a17900..3820175f6 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -179,7 +179,7 @@ fn test(a: u32, b: isize, c: !, d: &str) {
179 17..18 'b': isize 179 17..18 'b': isize
180 27..28 'c': ! 180 27..28 'c': !
181 33..34 'd': &str 181 33..34 'd': &str
182 42..121 '{ ...f32; }': ! 182 42..121 '{ ...f32; }': ()
183 48..49 'a': u32 183 48..49 'a': u32
184 55..56 'b': isize 184 55..56 'b': isize
185 62..63 'c': ! 185 62..63 'c': !
@@ -935,7 +935,7 @@ fn foo() {
935 29..33 'true': bool 935 29..33 'true': bool
936 34..51 '{ ... }': i32 936 34..51 '{ ... }': i32
937 44..45 '1': i32 937 44..45 '1': i32
938 57..80 '{ ... }': ! 938 57..80 '{ ... }': i32
939 67..73 'return': ! 939 67..73 'return': !
940 90..93 '_x2': i32 940 90..93 '_x2': i32
941 96..149 'if tru... }': i32 941 96..149 'if tru... }': i32
@@ -951,7 +951,7 @@ fn foo() {
951 186..190 'true': bool 951 186..190 'true': bool
952 194..195 '3': i32 952 194..195 '3': i32
953 205..206 '_': bool 953 205..206 '_': bool
954 210..241 '{ ... }': ! 954 210..241 '{ ... }': i32
955 224..230 'return': ! 955 224..230 'return': !
956 257..260 '_x4': i32 956 257..260 '_x4': i32
957 263..320 'match ... }': i32 957 263..320 'match ... }': i32
@@ -1687,7 +1687,7 @@ fn foo() -> u32 {
1687 17..59 '{ ...; }; }': () 1687 17..59 '{ ...; }; }': ()
1688 27..28 'x': || -> usize 1688 27..28 'x': || -> usize
1689 31..56 '|| -> ...n 1; }': || -> usize 1689 31..56 '|| -> ...n 1; }': || -> usize
1690 43..56 '{ return 1; }': ! 1690 43..56 '{ return 1; }': usize
1691 45..53 'return 1': ! 1691 45..53 'return 1': !
1692 52..53 '1': usize 1692 52..53 '1': usize
1693 "### 1693 "###
@@ -1706,7 +1706,7 @@ fn foo() -> u32 {
1706 17..48 '{ ...; }; }': () 1706 17..48 '{ ...; }; }': ()
1707 27..28 'x': || -> () 1707 27..28 'x': || -> ()
1708 31..45 '|| { return; }': || -> () 1708 31..45 '|| { return; }': || -> ()
1709 34..45 '{ return; }': ! 1709 34..45 '{ return; }': ()
1710 36..42 'return': ! 1710 36..42 'return': !
1711 "### 1711 "###
1712 ); 1712 );