aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2021-05-15 15:00:24 +0100
committerFlorian Diebold <[email protected]>2021-05-21 16:48:34 +0100
commita09079f27aa631b011f6c0703200862d28af81f4 (patch)
tree863ab0fbdb3dafed13cdbe1b6b066072c012f1b4 /crates/hir_ty
parentafa6be243587e523d5a2fc218db78568041ff296 (diff)
Fix coercion of two closures to a function pointer
Fixes #8604.
Diffstat (limited to 'crates/hir_ty')
-rw-r--r--crates/hir_ty/src/infer/coerce.rs50
-rw-r--r--crates/hir_ty/src/lib.rs1
-rw-r--r--crates/hir_ty/src/tests/coercion.rs17
-rw-r--r--crates/hir_ty/src/tests/simple.rs36
4 files changed, 48 insertions, 56 deletions
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 8467ea056..27f59c8bb 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -40,32 +40,45 @@ impl<'a> InferenceContext<'a> {
40 /// Merge two types from different branches, with possible coercion. 40 /// Merge two types from different branches, with possible coercion.
41 /// 41 ///
42 /// Mostly this means trying to coerce one to the other, but 42 /// Mostly this means trying to coerce one to the other, but
43 /// - if we have two function types for different functions, we need to 43 /// - if we have two function types for different functions or closures, we need to
44 /// coerce both to function pointers; 44 /// coerce both to function pointers;
45 /// - if we were concerned with lifetime subtyping, we'd need to look for a 45 /// - if we were concerned with lifetime subtyping, we'd need to look for a
46 /// least upper bound. 46 /// least upper bound.
47 pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { 47 pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
48 // Special case: two function types. Try to coerce both to
49 // pointers to have a chance at getting a match. See
50 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
51 let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) {
52 (TyKind::FnDef(..), TyKind::FnDef(..))
53 | (TyKind::Closure(..), TyKind::FnDef(..))
54 | (TyKind::FnDef(..), TyKind::Closure(..))
55 | (TyKind::Closure(..), TyKind::Closure(..)) => {
56 // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
57 // we should be coercing the closure to a fn pointer of the safety of the FnDef
58 cov_mark::hit!(coerce_fn_reification);
59 let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig");
60 Some(sig)
61 }
62 _ => None,
63 };
64 if let Some(sig) = sig {
65 let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner);
66 let result1 = self.coerce_inner(ty1.clone(), &target_ty);
67 let result2 = self.coerce_inner(ty2.clone(), &target_ty);
68 if let (Ok(_result1), Ok(_result2)) = (result1, result2) {
69 // TODO deal with the goals
70 return target_ty;
71 }
72 }
73
48 if self.coerce(ty1, ty2) { 74 if self.coerce(ty1, ty2) {
49 ty2.clone() 75 ty2.clone()
50 } else if self.coerce(ty2, ty1) { 76 } else if self.coerce(ty2, ty1) {
51 ty1.clone() 77 ty1.clone()
52 } else { 78 } else {
53 if let (TyKind::FnDef(..), TyKind::FnDef(..)) = 79 // FIXME record a type mismatch
54 (ty1.kind(&Interner), ty2.kind(&Interner)) 80 cov_mark::hit!(coerce_merge_fail_fallback);
55 { 81 ty1.clone()
56 cov_mark::hit!(coerce_fn_reification);
57 // Special case: two function types. Try to coerce both to
58 // pointers to have a chance at getting a match. See
59 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
60 let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig");
61 let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig");
62 let ptr_ty1 = TyBuilder::fn_ptr(sig1);
63 let ptr_ty2 = TyBuilder::fn_ptr(sig2);
64 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
65 } else {
66 cov_mark::hit!(coerce_merge_fail_fallback);
67 ty1.clone()
68 }
69 } 82 }
70 } 83 }
71 84
@@ -236,8 +249,7 @@ impl<'a> InferenceContext<'a> {
236 Ok(result) 249 Ok(result)
237 } 250 }
238 251
239 /// Attempts to coerce from the type of a Rust function item into a closure 252 /// Attempts to coerce from the type of a Rust function item into a function pointer.
240 /// or a function pointer.
241 fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { 253 fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
242 match to_ty.kind(&Interner) { 254 match to_ty.kind(&Interner) {
243 TyKind::Function(_) => { 255 TyKind::Function(_) => {
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 179a27763..06d5cd0b6 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -167,6 +167,7 @@ pub fn make_canonical<T: HasInterner<Interner = Interner>>(
167 Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } 167 Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
168} 168}
169 169
170// FIXME: get rid of this, just replace it by FnPointer
170/// A function signature as seen by type inference: Several parameter types and 171/// A function signature as seen by type inference: Several parameter types and
171/// one return type. 172/// one return type.
172#[derive(Clone, PartialEq, Eq, Debug)] 173#[derive(Clone, PartialEq, Eq, Debug)]
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 190471069..67295b663 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_block_expr_type_mismatch() { 6fn infer_block_expr_type_mismatch() {
@@ -858,3 +858,18 @@ fn coerce_unsize_generic() {
858 "]], 858 "]],
859 ); 859 );
860} 860}
861
862#[test]
863fn infer_two_closures_lub() {
864 check_types(
865 r#"
866fn foo(c: i32) {
867 let add = |a: i32, b: i32| a + b;
868 let sub = |a, b| a - b;
869 //^ |i32, i32| -> i32
870 if c > 42 { add } else { sub };
871 //^ fn(i32, i32) -> i32
872}
873 "#,
874 )
875}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a9cd42186..5c70a1fc0 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1040,42 +1040,6 @@ fn infer_in_elseif() {
1040} 1040}
1041 1041
1042#[test] 1042#[test]
1043fn infer_closure_unify() {
1044 check_infer(
1045 r#"
1046 fn foo(f: bool) {
1047 let a = |x| x;
1048 let b = |x| x;
1049 let id = if f { a } else { b };
1050 id(123);
1051 }
1052 "#,
1053 expect![[r#"
1054 7..8 'f': bool
1055 16..106 '{ ...23); }': ()
1056 26..27 'a': |i32| -> i32
1057 30..35 '|x| x': |i32| -> i32
1058 31..32 'x': i32
1059 34..35 'x': i32
1060 45..46 'b': |i32| -> i32
1061 49..54 '|x| x': |i32| -> i32
1062 50..51 'x': i32
1063 53..54 'x': i32
1064 64..66 'id': |i32| -> i32
1065 69..90 'if f {... { b }': |i32| -> i32
1066 72..73 'f': bool
1067 74..79 '{ a }': |i32| -> i32
1068 76..77 'a': |i32| -> i32
1069 85..90 '{ b }': |i32| -> i32
1070 87..88 'b': |i32| -> i32
1071 96..98 'id': |i32| -> i32
1072 96..103 'id(123)': i32
1073 99..102 '123': i32
1074 "#]],
1075 )
1076}
1077
1078#[test]
1079fn infer_if_match_with_return() { 1043fn infer_if_match_with_return() {
1080 check_infer( 1044 check_infer(
1081 r#" 1045 r#"