diff options
author | Florian Diebold <[email protected]> | 2021-05-15 15:00:24 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2021-05-21 16:48:34 +0100 |
commit | a09079f27aa631b011f6c0703200862d28af81f4 (patch) | |
tree | 863ab0fbdb3dafed13cdbe1b6b066072c012f1b4 | |
parent | afa6be243587e523d5a2fc218db78568041ff296 (diff) |
Fix coercion of two closures to a function pointer
Fixes #8604.
-rw-r--r-- | crates/hir_ty/src/infer/coerce.rs | 50 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/coercion.rs | 17 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/simple.rs | 36 |
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 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_block_expr_type_mismatch() { | 6 | fn infer_block_expr_type_mismatch() { |
@@ -858,3 +858,18 @@ fn coerce_unsize_generic() { | |||
858 | "]], | 858 | "]], |
859 | ); | 859 | ); |
860 | } | 860 | } |
861 | |||
862 | #[test] | ||
863 | fn infer_two_closures_lub() { | ||
864 | check_types( | ||
865 | r#" | ||
866 | fn 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] |
1043 | fn 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] | ||
1079 | fn infer_if_match_with_return() { | 1043 | fn infer_if_match_with_return() { |
1080 | check_infer( | 1044 | check_infer( |
1081 | r#" | 1045 | r#" |