aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2020-05-08 21:12:16 +0100
committerFlorian Diebold <[email protected]>2020-05-08 21:14:01 +0100
commita3d866e776f43c1ae717740bf0c507f4d9fe47cb (patch)
treee82b33ba64c1e0b36b33470d4d516200c2924d49 /crates
parentf9ec7cebef732fbc9d4849d87d325efef5faadea (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.rs34
-rw-r--r--crates/ra_hir_ty/src/lib.rs6
-rw-r--r--crates/ra_hir_ty/src/marks.rs1
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs42
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]
549fn coerce_fn_items_in_match_arms() {
550 covers!(coerce_fn_reification);
551 assert_snapshot!(
552 infer_with_mismatches(r#"
553fn foo1(x: u32) -> isize { 1 }
554fn foo2(x: u32) -> isize { 2 }
555fn foo3(x: u32) -> isize { 3 }
556fn 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]
549fn coerce_closure_to_fn_ptr() { 591fn coerce_closure_to_fn_ptr() {
550 assert_snapshot!( 592 assert_snapshot!(
551 infer_with_mismatches(r#" 593 infer_with_mismatches(r#"