diff options
author | Florian Diebold <[email protected]> | 2020-05-08 21:12:16 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-05-08 21:14:01 +0100 |
commit | a3d866e776f43c1ae717740bf0c507f4d9fe47cb (patch) | |
tree | e82b33ba64c1e0b36b33470d4d516200c2924d49 /crates | |
parent | f9ec7cebef732fbc9d4849d87d325efef5faadea (diff) |
Handle coercing function types to function pointers in match
E.g. in
```rust
match x {
1 => function1,
2 => function2,
}
```
we need to try coercing both to pointers. Turns out this is a special case in
rustc as well (see the link in the comment).
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_ty/src/infer/coerce.rs | 34 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 42 |
4 files changed, 72 insertions, 11 deletions
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 89200255a..173ec59ed 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs | |||
@@ -20,21 +20,35 @@ impl<'a> InferenceContext<'a> { | |||
20 | self.coerce_inner(from_ty, &to_ty) | 20 | self.coerce_inner(from_ty, &to_ty) |
21 | } | 21 | } |
22 | 22 | ||
23 | /// Merge two types from different branches, with possible implicit coerce. | 23 | /// Merge two types from different branches, with possible coercion. |
24 | /// | 24 | /// |
25 | /// Note that it is only possible that one type are coerced to another. | 25 | /// Mostly this means trying to coerce one to the other, but |
26 | /// Coercing both types to another least upper bound type is not possible in rustc, | 26 | /// - if we have two function types for different functions, we need to |
27 | /// which will simply result in "incompatible types" error. | 27 | /// coerce both to function pointers; |
28 | /// - if we were concerned with lifetime subtyping, we'd need to look for a | ||
29 | /// least upper bound. | ||
28 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { | 30 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { |
29 | if self.coerce(ty1, ty2) { | 31 | if self.coerce(ty1, ty2) { |
30 | ty2.clone() | 32 | ty2.clone() |
31 | } else if self.coerce(ty2, ty1) { | 33 | } else if self.coerce(ty2, ty1) { |
32 | ty1.clone() | 34 | ty1.clone() |
33 | } else { | 35 | } else { |
34 | tested_by!(coerce_merge_fail_fallback); | 36 | if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { |
35 | // For incompatible types, we use the latter one as result | 37 | tested_by!(coerce_fn_reification); |
36 | // to be better recovery for `if` without `else`. | 38 | // Special case: two function types. Try to coerce both to |
37 | ty2.clone() | 39 | // pointers to have a chance at getting a match. See |
40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | ||
41 | let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | ||
42 | let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); | ||
43 | let ptr_ty1 = Ty::fn_ptr(sig1); | ||
44 | let ptr_ty2 = Ty::fn_ptr(sig2); | ||
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | ||
46 | } else { | ||
47 | tested_by!(coerce_merge_fail_fallback); | ||
48 | // For incompatible types, we use the latter one as result | ||
49 | // to be better recovery for `if` without `else`. | ||
50 | ty2.clone() | ||
51 | } | ||
38 | } | 52 | } |
39 | } | 53 | } |
40 | 54 | ||
@@ -84,9 +98,7 @@ impl<'a> InferenceContext<'a> { | |||
84 | match from_ty.callable_sig(self.db) { | 98 | match from_ty.callable_sig(self.db) { |
85 | None => return false, | 99 | None => return false, |
86 | Some(sig) => { | 100 | Some(sig) => { |
87 | let num_args = sig.params_and_return.len() as u16 - 1; | 101 | from_ty = Ty::fn_ptr(sig); |
88 | from_ty = | ||
89 | Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return)); | ||
90 | } | 102 | } |
91 | } | 103 | } |
92 | } | 104 | } |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 3e5f38d0d..e8f3482fe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -683,6 +683,12 @@ impl Ty { | |||
683 | pub fn unit() -> Self { | 683 | pub fn unit() -> Self { |
684 | Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) | 684 | Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) |
685 | } | 685 | } |
686 | pub fn fn_ptr(sig: FnSig) -> Self { | ||
687 | Ty::apply( | ||
688 | TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, | ||
689 | Substs(sig.params_and_return), | ||
690 | ) | ||
691 | } | ||
686 | 692 | ||
687 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { | 693 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { |
688 | match self { | 694 | match self { |
diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index de5cb1d6b..a39740143 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs | |||
@@ -7,5 +7,6 @@ test_utils::marks!( | |||
7 | impl_self_type_match_without_receiver | 7 | impl_self_type_match_without_receiver |
8 | match_ergonomics_ref | 8 | match_ergonomics_ref |
9 | coerce_merge_fail_fallback | 9 | coerce_merge_fail_fallback |
10 | coerce_fn_reification | ||
10 | trait_self_implements_self | 11 | trait_self_implements_self |
11 | ); | 12 | ); |
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 0c3a833bd..6dc4b2cd1 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -546,6 +546,48 @@ fn test() { | |||
546 | } | 546 | } |
547 | 547 | ||
548 | #[test] | 548 | #[test] |
549 | fn coerce_fn_items_in_match_arms() { | ||
550 | covers!(coerce_fn_reification); | ||
551 | assert_snapshot!( | ||
552 | infer_with_mismatches(r#" | ||
553 | fn foo1(x: u32) -> isize { 1 } | ||
554 | fn foo2(x: u32) -> isize { 2 } | ||
555 | fn foo3(x: u32) -> isize { 3 } | ||
556 | fn test() { | ||
557 | let x = match 1 { | ||
558 | 1 => foo1, | ||
559 | 2 => foo2, | ||
560 | _ => foo3, | ||
561 | }; | ||
562 | } | ||
563 | "#, true), | ||
564 | @r###" | ||
565 | 9..10 'x': u32 | ||
566 | 26..31 '{ 1 }': isize | ||
567 | 28..29 '1': isize | ||
568 | 40..41 'x': u32 | ||
569 | 57..62 '{ 2 }': isize | ||
570 | 59..60 '2': isize | ||
571 | 71..72 'x': u32 | ||
572 | 88..93 '{ 3 }': isize | ||
573 | 90..91 '3': isize | ||
574 | 104..193 '{ ... }; }': () | ||
575 | 114..115 'x': fn(u32) -> isize | ||
576 | 118..190 'match ... }': fn(u32) -> isize | ||
577 | 124..125 '1': i32 | ||
578 | 136..137 '1': i32 | ||
579 | 136..137 '1': i32 | ||
580 | 141..145 'foo1': fn foo1(u32) -> isize | ||
581 | 155..156 '2': i32 | ||
582 | 155..156 '2': i32 | ||
583 | 160..164 'foo2': fn foo2(u32) -> isize | ||
584 | 174..175 '_': i32 | ||
585 | 179..183 'foo3': fn foo3(u32) -> isize | ||
586 | "### | ||
587 | ); | ||
588 | } | ||
589 | |||
590 | #[test] | ||
549 | fn coerce_closure_to_fn_ptr() { | 591 | fn coerce_closure_to_fn_ptr() { |
550 | assert_snapshot!( | 592 | assert_snapshot!( |
551 | infer_with_mismatches(r#" | 593 | infer_with_mismatches(r#" |