diff options
author | Florian Diebold <[email protected]> | 2019-12-05 22:02:31 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-12-06 14:15:26 +0000 |
commit | f86fe3d891ab295e9e394a1338da86524a6205d3 (patch) | |
tree | 891cf4be4893c79598800928e7c0b669d9d2a9c2 /crates/ra_hir_ty | |
parent | d2b210a02e3e8ee1cf38909411fa8945aec99f4e (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.rs | 21 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 26 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 36 |
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 | ||
52 | fn infer(content: &str) -> String { | 52 | fn infer(content: &str) -> String { |
53 | infer_with_mismatches(content, false) | ||
54 | } | ||
55 | |||
56 | fn 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 @@ | |||
1 | use super::infer_with_mismatches; | ||
1 | use insta::assert_snapshot; | 2 | use insta::assert_snapshot; |
2 | use test_utils::covers; | 3 | use test_utils::covers; |
3 | 4 | ||
@@ -367,3 +368,38 @@ fn test() { | |||
367 | "### | 368 | "### |
368 | ); | 369 | ); |
369 | } | 370 | } |
371 | |||
372 | #[test] | ||
373 | fn coerce_autoderef() { | ||
374 | assert_snapshot!( | ||
375 | infer_with_mismatches(r#" | ||
376 | struct Foo; | ||
377 | fn takes_ref_foo(x: &Foo) {} | ||
378 | fn 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 | } | ||