mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33: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,16 +20,29 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.coerce_inner(from_ty, &to_ty)
|
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.
|
/// Mostly this means trying to coerce one to the other, but
|
||||||
/// Coercing both types to another least upper bound type is not possible in rustc,
|
/// - if we have two function types for different functions, we need to
|
||||||
/// which will simply result in "incompatible types" error.
|
/// 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 {
|
pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
|
||||||
if self.coerce(ty1, ty2) {
|
if self.coerce(ty1, ty2) {
|
||||||
ty2.clone()
|
ty2.clone()
|
||||||
} else if self.coerce(ty2, ty1) {
|
} else if self.coerce(ty2, ty1) {
|
||||||
ty1.clone()
|
ty1.clone()
|
||||||
|
} else {
|
||||||
|
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 {
|
} else {
|
||||||
tested_by!(coerce_merge_fail_fallback);
|
tested_by!(coerce_merge_fail_fallback);
|
||||||
// For incompatible types, we use the latter one as result
|
// For incompatible types, we use the latter one as result
|
||||||
|
@ -37,6 +50,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
ty2.clone()
|
ty2.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
|
fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
|
||||||
match (&from_ty, to_ty) {
|
match (&from_ty, to_ty) {
|
||||||
|
@ -84,9 +98,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
match from_ty.callable_sig(self.db) {
|
match from_ty.callable_sig(self.db) {
|
||||||
None => return false,
|
None => return false,
|
||||||
Some(sig) => {
|
Some(sig) => {
|
||||||
let num_args = sig.params_and_return.len() as u16 - 1;
|
from_ty = Ty::fn_ptr(sig);
|
||||||
from_ty =
|
|
||||||
Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -683,6 +683,12 @@ impl Ty {
|
||||||
pub fn unit() -> Self {
|
pub fn unit() -> Self {
|
||||||
Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty())
|
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)> {
|
pub fn as_reference(&self) -> Option<(&Ty, Mutability)> {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -7,5 +7,6 @@ test_utils::marks!(
|
||||||
impl_self_type_match_without_receiver
|
impl_self_type_match_without_receiver
|
||||||
match_ergonomics_ref
|
match_ergonomics_ref
|
||||||
coerce_merge_fail_fallback
|
coerce_merge_fail_fallback
|
||||||
|
coerce_fn_reification
|
||||||
trait_self_implements_self
|
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]
|
#[test]
|
||||||
fn coerce_closure_to_fn_ptr() {
|
fn coerce_closure_to_fn_ptr() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
|
Loading…
Reference in a new issue