aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-12-05 22:02:31 +0000
committerFlorian Diebold <[email protected]>2019-12-06 14:15:26 +0000
commitf86fe3d891ab295e9e394a1338da86524a6205d3 (patch)
tree891cf4be4893c79598800928e7c0b669d9d2a9c2 /crates/ra_hir_ty
parentd2b210a02e3e8ee1cf38909411fa8945aec99f4e (diff)
Don't unify within a reference
If we are expecting a `&Foo` and get a `&something`, when checking the `something`, we are *expecting* a `Foo`, but we shouldn't try to unify whatever we get with that expectation, because it could actually be a `&Foo`, and `&&Foo` coerces to `&Foo`. So this fixes quite a few false type mismatches.
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs21
-rw-r--r--crates/ra_hir_ty/src/tests.rs26
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs36
3 files changed, 72 insertions, 11 deletions
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 1e78f6efd..b8df27706 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -201,7 +201,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
201 } 201 }
202 Expr::Return { expr } => { 202 Expr::Return { expr } => {
203 if let Some(expr) = expr { 203 if let Some(expr) = expr {
204 self.infer_expr(*expr, &Expectation::has_type(self.return_ty.clone())); 204 self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
205 } 205 }
206 Ty::simple(TypeCtor::Never) 206 Ty::simple(TypeCtor::Never)
207 } 207 }
@@ -245,7 +245,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
245 ty 245 ty
246 } 246 }
247 Expr::Field { expr, name } => { 247 Expr::Field { expr, name } => {
248 let receiver_ty = self.infer_expr(*expr, &Expectation::none()); 248 let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
249 let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); 249 let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
250 let ty = autoderef::autoderef( 250 let ty = autoderef::autoderef(
251 self.db, 251 self.db,
@@ -280,7 +280,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
280 self.normalize_associated_types_in(ty) 280 self.normalize_associated_types_in(ty)
281 } 281 }
282 Expr::Await { expr } => { 282 Expr::Await { expr } => {
283 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 283 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
284 let ty = match self.resolve_future_future_output() { 284 let ty = match self.resolve_future_future_output() {
285 Some(future_future_output_alias) => { 285 Some(future_future_output_alias) => {
286 let ty = self.table.new_type_var(); 286 let ty = self.table.new_type_var();
@@ -299,7 +299,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
299 ty 299 ty
300 } 300 }
301 Expr::Try { expr } => { 301 Expr::Try { expr } => {
302 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 302 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
303 let ty = match self.resolve_ops_try_ok() { 303 let ty = match self.resolve_ops_try_ok() {
304 Some(ops_try_ok_alias) => { 304 Some(ops_try_ok_alias) => {
305 let ty = self.table.new_type_var(); 305 let ty = self.table.new_type_var();
@@ -318,7 +318,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
318 ty 318 ty
319 } 319 }
320 Expr::Cast { expr, type_ref } => { 320 Expr::Cast { expr, type_ref } => {
321 let _inner_ty = self.infer_expr(*expr, &Expectation::none()); 321 let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
322 let cast_ty = self.make_ty(type_ref); 322 let cast_ty = self.make_ty(type_ref);
323 // FIXME check the cast... 323 // FIXME check the cast...
324 cast_ty 324 cast_ty
@@ -334,12 +334,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
334 } else { 334 } else {
335 Expectation::none() 335 Expectation::none()
336 }; 336 };
337 // FIXME reference coercions etc. 337 let inner_ty = self.infer_expr_inner(*expr, &expectation);
338 let inner_ty = self.infer_expr(*expr, &expectation);
339 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) 338 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
340 } 339 }
341 Expr::Box { expr } => { 340 Expr::Box { expr } => {
342 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 341 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
343 if let Some(box_) = self.resolve_boxed_box() { 342 if let Some(box_) = self.resolve_boxed_box() {
344 Ty::apply_one(TypeCtor::Adt(box_), inner_ty) 343 Ty::apply_one(TypeCtor::Adt(box_), inner_ty)
345 } else { 344 } else {
@@ -347,7 +346,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
347 } 346 }
348 } 347 }
349 Expr::UnaryOp { expr, op } => { 348 Expr::UnaryOp { expr, op } => {
350 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 349 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
351 match op { 350 match op {
352 UnaryOp::Deref => match self.resolver.krate() { 351 UnaryOp::Deref => match self.resolver.krate() {
353 Some(krate) => { 352 Some(krate) => {
@@ -417,7 +416,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
417 _ => Ty::Unknown, 416 _ => Ty::Unknown,
418 }, 417 },
419 Expr::Range { lhs, rhs, range_type } => { 418 Expr::Range { lhs, rhs, range_type } => {
420 let lhs_ty = lhs.map(|e| self.infer_expr(e, &Expectation::none())); 419 let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none()));
421 let rhs_expect = lhs_ty 420 let rhs_expect = lhs_ty
422 .as_ref() 421 .as_ref()
423 .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); 422 .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
@@ -455,7 +454,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
455 } 454 }
456 } 455 }
457 Expr::Index { base, index } => { 456 Expr::Index { base, index } => {
458 let _base_ty = self.infer_expr(*base, &Expectation::none()); 457 let _base_ty = self.infer_expr_inner(*base, &Expectation::none());
459 let _index_ty = self.infer_expr(*index, &Expectation::none()); 458 let _index_ty = self.infer_expr(*index, &Expectation::none());
460 // FIXME: use `std::ops::Index::Output` to figure out the real return type 459 // FIXME: use `std::ops::Index::Output` to figure out the real return type
461 Ty::Unknown 460 Ty::Unknown
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 9f373a8f4..f1b67555f 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -50,6 +50,10 @@ fn type_at(content: &str) -> String {
50} 50}
51 51
52fn infer(content: &str) -> String { 52fn infer(content: &str) -> String {
53 infer_with_mismatches(content, false)
54}
55
56fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
53 let (db, file_id) = TestDB::with_single_file(content); 57 let (db, file_id) = TestDB::with_single_file(content);
54 58
55 let mut acc = String::new(); 59 let mut acc = String::new();
@@ -57,6 +61,7 @@ fn infer(content: &str) -> String {
57 let mut infer_def = |inference_result: Arc<InferenceResult>, 61 let mut infer_def = |inference_result: Arc<InferenceResult>,
58 body_source_map: Arc<BodySourceMap>| { 62 body_source_map: Arc<BodySourceMap>| {
59 let mut types = Vec::new(); 63 let mut types = Vec::new();
64 let mut mismatches = Vec::new();
60 65
61 for (pat, ty) in inference_result.type_of_pat.iter() { 66 for (pat, ty) in inference_result.type_of_pat.iter() {
62 let syntax_ptr = match body_source_map.pat_syntax(pat) { 67 let syntax_ptr = match body_source_map.pat_syntax(pat) {
@@ -76,6 +81,9 @@ fn infer(content: &str) -> String {
76 None => continue, 81 None => continue,
77 }; 82 };
78 types.push((syntax_ptr, ty)); 83 types.push((syntax_ptr, ty));
84 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
85 mismatches.push((syntax_ptr, mismatch));
86 }
79 } 87 }
80 88
81 // sort ranges for consistency 89 // sort ranges for consistency
@@ -101,6 +109,24 @@ fn infer(content: &str) -> String {
101 ) 109 )
102 .unwrap(); 110 .unwrap();
103 } 111 }
112 if include_mismatches {
113 mismatches.sort_by_key(|(src_ptr, _)| {
114 (src_ptr.value.range().start(), src_ptr.value.range().end())
115 });
116 for (src_ptr, mismatch) in &mismatches {
117 let range = src_ptr.value.range();
118 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" };
119 write!(
120 acc,
121 "{}{}: expected {}, got {}\n",
122 macro_prefix,
123 range,
124 mismatch.expected.display(&db),
125 mismatch.actual.display(&db),
126 )
127 .unwrap();
128 }
129 }
104 }; 130 };
105 131
106 let module = db.module_for_file(file_id); 132 let module = db.module_for_file(file_id);
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 1530fcc63..58b22396f 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -1,3 +1,4 @@
1use super::infer_with_mismatches;
1use insta::assert_snapshot; 2use insta::assert_snapshot;
2use test_utils::covers; 3use test_utils::covers;
3 4
@@ -367,3 +368,38 @@ fn test() {
367 "### 368 "###
368 ); 369 );
369} 370}
371
372#[test]
373fn coerce_autoderef() {
374 assert_snapshot!(
375 infer_with_mismatches(r#"
376struct Foo;
377fn takes_ref_foo(x: &Foo) {}
378fn test() {
379 takes_ref_foo(&Foo);
380 takes_ref_foo(&&Foo);
381 takes_ref_foo(&&&Foo);
382}
383"#, true),
384 @r###"
385 [30; 31) 'x': &Foo
386 [39; 41) '{}': ()
387 [52; 133) '{ ...oo); }': ()
388 [58; 71) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
389 [58; 77) 'takes_...(&Foo)': ()
390 [72; 76) '&Foo': &Foo
391 [73; 76) 'Foo': Foo
392 [83; 96) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
393 [83; 103) 'takes_...&&Foo)': ()
394 [97; 102) '&&Foo': &&Foo
395 [98; 102) '&Foo': &Foo
396 [99; 102) 'Foo': Foo
397 [109; 122) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
398 [109; 130) 'takes_...&&Foo)': ()
399 [123; 129) '&&&Foo': &&&Foo
400 [124; 129) '&&Foo': &&Foo
401 [125; 129) '&Foo': &Foo
402 [126; 129) 'Foo': Foo
403 "###
404 );
405}