mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
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).
This commit is contained in:
parent
f9ec7cebef
commit
a3d866e776
4 changed files with 72 additions and 11 deletions
|
@ -20,21 +20,35 @@ impl<'a> InferenceContext<'a> {
|
|||
self.coerce_inner(from_ty, &to_ty)
|
||||
}
|
||||
|
||||
/// Merge two types from different branches, with possible implicit coerce.
|
||||
/// Merge two types from different branches, with possible coercion.
|
||||
///
|
||||
/// Note that it is only possible that one type are coerced to another.
|
||||
/// Coercing both types to another least upper bound type is not possible in rustc,
|
||||
/// which will simply result in "incompatible types" error.
|
||||
/// Mostly this means trying to coerce one to the other, but
|
||||
/// - if we have two function types for different functions, we need to
|
||||
/// coerce both to function pointers;
|
||||
/// - if we were concerned with lifetime subtyping, we'd need to look for a
|
||||
/// least upper bound.
|
||||
pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
|
||||
if self.coerce(ty1, ty2) {
|
||||
ty2.clone()
|
||||
} else if self.coerce(ty2, ty1) {
|
||||
ty1.clone()
|
||||
} else {
|
||||
tested_by!(coerce_merge_fail_fallback);
|
||||
// For incompatible types, we use the latter one as result
|
||||
// to be better recovery for `if` without `else`.
|
||||
ty2.clone()
|
||||
if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) {
|
||||
tested_by!(coerce_fn_reification);
|
||||
// Special case: two function types. Try to coerce both to
|
||||
// pointers to have a chance at getting a match. See
|
||||
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
|
||||
let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig");
|
||||
let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig");
|
||||
let ptr_ty1 = Ty::fn_ptr(sig1);
|
||||
let ptr_ty2 = Ty::fn_ptr(sig2);
|
||||
self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
|
||||
} else {
|
||||
tested_by!(coerce_merge_fail_fallback);
|
||||
// For incompatible types, we use the latter one as result
|
||||
// to be better recovery for `if` without `else`.
|
||||
ty2.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,9 +98,7 @@ impl<'a> InferenceContext<'a> {
|
|||
match from_ty.callable_sig(self.db) {
|
||||
None => return false,
|
||||
Some(sig) => {
|
||||
let num_args = sig.params_and_return.len() as u16 - 1;
|
||||
from_ty =
|
||||
Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return));
|
||||
from_ty = Ty::fn_ptr(sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -683,6 +683,12 @@ impl Ty {
|
|||
pub fn unit() -> Self {
|
||||
Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty())
|
||||
}
|
||||
pub fn fn_ptr(sig: FnSig) -> Self {
|
||||
Ty::apply(
|
||||
TypeCtor::FnPtr { num_args: sig.params().len() as u16 },
|
||||
Substs(sig.params_and_return),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_reference(&self) -> Option<(&Ty, Mutability)> {
|
||||
match self {
|
||||
|
|
|
@ -7,5 +7,6 @@ test_utils::marks!(
|
|||
impl_self_type_match_without_receiver
|
||||
match_ergonomics_ref
|
||||
coerce_merge_fail_fallback
|
||||
coerce_fn_reification
|
||||
trait_self_implements_self
|
||||
);
|
||||
|
|
|
@ -545,6 +545,48 @@ fn test() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_fn_items_in_match_arms() {
|
||||
covers!(coerce_fn_reification);
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
fn foo1(x: u32) -> isize { 1 }
|
||||
fn foo2(x: u32) -> isize { 2 }
|
||||
fn foo3(x: u32) -> isize { 3 }
|
||||
fn test() {
|
||||
let x = match 1 {
|
||||
1 => foo1,
|
||||
2 => foo2,
|
||||
_ => foo3,
|
||||
};
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
9..10 'x': u32
|
||||
26..31 '{ 1 }': isize
|
||||
28..29 '1': isize
|
||||
40..41 'x': u32
|
||||
57..62 '{ 2 }': isize
|
||||
59..60 '2': isize
|
||||
71..72 'x': u32
|
||||
88..93 '{ 3 }': isize
|
||||
90..91 '3': isize
|
||||
104..193 '{ ... }; }': ()
|
||||
114..115 'x': fn(u32) -> isize
|
||||
118..190 'match ... }': fn(u32) -> isize
|
||||
124..125 '1': i32
|
||||
136..137 '1': i32
|
||||
136..137 '1': i32
|
||||
141..145 'foo1': fn foo1(u32) -> isize
|
||||
155..156 '2': i32
|
||||
155..156 '2': i32
|
||||
160..164 'foo2': fn foo2(u32) -> isize
|
||||
174..175 '_': i32
|
||||
179..183 'foo3': fn foo3(u32) -> isize
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_closure_to_fn_ptr() {
|
||||
assert_snapshot!(
|
||||
|
|
Loading…
Reference in a new issue