diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-15 20:08:12 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-15 20:08:12 +0000 |
commit | 0ef8ace012b19b76ee99b283801d0d17a3b72b4b (patch) | |
tree | 911458abbefcddd06e131c410bd1e1710e77ca95 /crates | |
parent | 4e24b25c669965cf6a68c4b8e775cc83615d978a (diff) | |
parent | 91853590a9ee78406e892ca92305edef3a5b9213 (diff) |
Merge #2567
2567: Handle impl Trait more correctly r=flodiebold a=flodiebold
When calling a function, argument-position impl Trait is transparent; same for return-position impl Trait when inside the function.
So in these cases, we need to represent that type not by `Ty::Opaque`, but by a type variable that can be unified with whatever flows into there.
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 27 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/traits.rs | 65 |
4 files changed, 91 insertions, 3 deletions
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index af42854cc..98ba05fc2 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -32,6 +32,7 @@ use hir_def::{ | |||
32 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; | 32 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; |
33 | use ra_arena::map::ArenaMap; | 33 | use ra_arena::map::ArenaMap; |
34 | use ra_prof::profile; | 34 | use ra_prof::profile; |
35 | use test_utils::tested_by; | ||
35 | 36 | ||
36 | use super::{ | 37 | use super::{ |
37 | primitive::{FloatTy, IntTy}, | 38 | primitive::{FloatTy, IntTy}, |
@@ -274,6 +275,29 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
274 | self.normalize_associated_types_in(ty) | 275 | self.normalize_associated_types_in(ty) |
275 | } | 276 | } |
276 | 277 | ||
278 | /// Replaces `impl Trait` in `ty` by type variables and obligations for | ||
279 | /// those variables. This is done for function arguments when calling a | ||
280 | /// function, and for return types when inside the function body, i.e. in | ||
281 | /// the cases where the `impl Trait` is 'transparent'. In other cases, `impl | ||
282 | /// Trait` is represented by `Ty::Opaque`. | ||
283 | fn insert_vars_for_impl_trait(&mut self, ty: Ty) -> Ty { | ||
284 | ty.fold(&mut |ty| match ty { | ||
285 | Ty::Opaque(preds) => { | ||
286 | tested_by!(insert_vars_for_impl_trait); | ||
287 | let var = self.table.new_type_var(); | ||
288 | let var_subst = Substs::builder(1).push(var.clone()).build(); | ||
289 | self.obligations.extend( | ||
290 | preds | ||
291 | .iter() | ||
292 | .map(|pred| pred.clone().subst_bound_vars(&var_subst)) | ||
293 | .filter_map(Obligation::from_predicate), | ||
294 | ); | ||
295 | var | ||
296 | } | ||
297 | _ => ty, | ||
298 | }) | ||
299 | } | ||
300 | |||
277 | /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. | 301 | /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. |
278 | fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { | 302 | fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { |
279 | match ty { | 303 | match ty { |
@@ -414,7 +438,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
414 | 438 | ||
415 | self.infer_pat(*pat, &ty, BindingMode::default()); | 439 | self.infer_pat(*pat, &ty, BindingMode::default()); |
416 | } | 440 | } |
417 | self.return_ty = self.make_ty(&data.ret_type); | 441 | let return_ty = self.make_ty(&data.ret_type); |
442 | self.return_ty = self.insert_vars_for_impl_trait(return_ty); | ||
418 | } | 443 | } |
419 | 444 | ||
420 | fn infer_body(&mut self) { | 445 | fn infer_body(&mut self) { |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 2e3cdd53a..924ad3e81 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -613,6 +613,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
613 | continue; | 613 | continue; |
614 | } | 614 | } |
615 | 615 | ||
616 | let param_ty = self.insert_vars_for_impl_trait(param_ty); | ||
616 | let param_ty = self.normalize_associated_types_in(param_ty); | 617 | let param_ty = self.normalize_associated_types_in(param_ty); |
617 | self.infer_expr_coerce(arg, &Expectation::has_type(param_ty.clone())); | 618 | self.infer_expr_coerce(arg, &Expectation::has_type(param_ty.clone())); |
618 | } | 619 | } |
diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index 0f754eb9c..fe74acf11 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs | |||
@@ -6,4 +6,5 @@ test_utils::marks!( | |||
6 | type_var_resolves_to_int_var | 6 | type_var_resolves_to_int_var |
7 | match_ergonomics_ref | 7 | match_ergonomics_ref |
8 | coerce_merge_fail_fallback | 8 | coerce_merge_fail_fallback |
9 | insert_vars_for_impl_trait | ||
9 | ); | 10 | ); |
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 6139adb72..802937cb0 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | use super::{infer, type_at, type_at_pos}; | ||
2 | use crate::test_db::TestDB; | ||
3 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | |||
4 | use ra_db::fixture::WithFixture; | 3 | use ra_db::fixture::WithFixture; |
4 | use test_utils::covers; | ||
5 | |||
6 | use super::{infer, infer_with_mismatches, type_at, type_at_pos}; | ||
7 | use crate::test_db::TestDB; | ||
5 | 8 | ||
6 | #[test] | 9 | #[test] |
7 | fn infer_await() { | 10 | fn infer_await() { |
@@ -1486,3 +1489,61 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> { | |||
1486 | // this is a legitimate cycle | 1489 | // this is a legitimate cycle |
1487 | assert_eq!(t, "{unknown}"); | 1490 | assert_eq!(t, "{unknown}"); |
1488 | } | 1491 | } |
1492 | |||
1493 | #[test] | ||
1494 | fn unify_impl_trait() { | ||
1495 | covers!(insert_vars_for_impl_trait); | ||
1496 | assert_snapshot!( | ||
1497 | infer_with_mismatches(r#" | ||
1498 | trait Trait<T> {} | ||
1499 | |||
1500 | fn foo(x: impl Trait<u32>) { loop {} } | ||
1501 | fn bar<T>(x: impl Trait<T>) -> T { loop {} } | ||
1502 | |||
1503 | struct S<T>(T); | ||
1504 | impl<T> Trait<T> for S<T> {} | ||
1505 | |||
1506 | fn default<T>() -> T { loop {} } | ||
1507 | |||
1508 | fn test() -> impl Trait<i32> { | ||
1509 | let s1 = S(default()); | ||
1510 | foo(s1); | ||
1511 | let x: i32 = bar(S(default())); | ||
1512 | S(default()) | ||
1513 | } | ||
1514 | "#, true), | ||
1515 | @r###" | ||
1516 | [27; 28) 'x': impl Trait<u32> | ||
1517 | [47; 58) '{ loop {} }': () | ||
1518 | [49; 56) 'loop {}': ! | ||
1519 | [54; 56) '{}': () | ||
1520 | [69; 70) 'x': impl Trait<T> | ||
1521 | [92; 103) '{ loop {} }': T | ||
1522 | [94; 101) 'loop {}': ! | ||
1523 | [99; 101) '{}': () | ||
1524 | [172; 183) '{ loop {} }': T | ||
1525 | [174; 181) 'loop {}': ! | ||
1526 | [179; 181) '{}': () | ||
1527 | [214; 310) '{ ...t()) }': S<i32> | ||
1528 | [224; 226) 's1': S<u32> | ||
1529 | [229; 230) 'S': S<u32>(T) -> S<T> | ||
1530 | [229; 241) 'S(default())': S<u32> | ||
1531 | [231; 238) 'default': fn default<u32>() -> T | ||
1532 | [231; 240) 'default()': u32 | ||
1533 | [247; 250) 'foo': fn foo(impl Trait<u32>) -> () | ||
1534 | [247; 254) 'foo(s1)': () | ||
1535 | [251; 253) 's1': S<u32> | ||
1536 | [264; 265) 'x': i32 | ||
1537 | [273; 276) 'bar': fn bar<i32>(impl Trait<T>) -> T | ||
1538 | [273; 290) 'bar(S(...lt()))': i32 | ||
1539 | [277; 278) 'S': S<i32>(T) -> S<T> | ||
1540 | [277; 289) 'S(default())': S<i32> | ||
1541 | [279; 286) 'default': fn default<i32>() -> T | ||
1542 | [279; 288) 'default()': i32 | ||
1543 | [296; 297) 'S': S<i32>(T) -> S<T> | ||
1544 | [296; 308) 'S(default())': S<i32> | ||
1545 | [298; 305) 'default': fn default<i32>() -> T | ||
1546 | [298; 307) 'default()': i32 | ||
1547 | "### | ||
1548 | ); | ||
1549 | } | ||