Infer FnSig from Fn traits
This commit is contained in:
@@ -38,8 +38,8 @@ impl<'a> InferenceContext<'a> {
|
||||
// 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 sig1 = self.callable_sig(ty1).expect("FnDef without callable sig");
|
||||
let sig2 = self.callable_sig(ty2).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)
|
||||
@@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> {
|
||||
|
||||
// `{function_type}` -> `fn()`
|
||||
(ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => {
|
||||
match from_ty.callable_sig(self.db) {
|
||||
match self.callable_sig(&from_ty) {
|
||||
None => return false,
|
||||
Some(sig) => {
|
||||
from_ty = Ty::fn_ptr(sig);
|
||||
|
||||
@@ -15,15 +15,15 @@ use ra_syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
autoderef, method_resolution, op,
|
||||
traits::InEnvironment,
|
||||
traits::{builtin::get_fn_trait, FnTrait, InEnvironment, SolutionVariables},
|
||||
utils::{generics, variant_data, Generics},
|
||||
ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
|
||||
TraitRef, Ty, TypeCtor,
|
||||
ApplicationTy, Binders, CallableDef, FnSig, InferTy, IntTy, Mutability, Obligation, Rawness,
|
||||
Substs, TraitRef, Ty, TypeCtor,
|
||||
};
|
||||
|
||||
use super::{
|
||||
find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
|
||||
InferenceDiagnostic, TypeMismatch,
|
||||
InferenceDiagnostic, Solution, TypeMismatch,
|
||||
};
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
@@ -63,6 +63,75 @@ impl<'a> InferenceContext<'a> {
|
||||
self.resolve_ty_as_possible(ty)
|
||||
}
|
||||
|
||||
fn callable_sig_from_fn_trait(&mut self, ty: &Ty) -> Option<FnSig> {
|
||||
if let Some(krate) = self.resolver.krate() {
|
||||
let fn_traits: Vec<crate::TraitId> = [FnTrait::FnOnce, FnTrait::FnMut, FnTrait::Fn]
|
||||
.iter()
|
||||
.filter_map(|f| get_fn_trait(self.db, krate, *f))
|
||||
.collect();
|
||||
for fn_trait in fn_traits {
|
||||
let fn_trait_data = self.db.trait_data(fn_trait);
|
||||
let generic_params = generics(self.db.upcast(), fn_trait.into());
|
||||
if generic_params.len() != 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let arg_ty = self.table.new_type_var();
|
||||
let substs = Substs::build_for_generics(&generic_params)
|
||||
.push(ty.clone())
|
||||
.push(arg_ty.clone())
|
||||
.build();
|
||||
|
||||
let trait_ref = TraitRef { trait_: fn_trait, substs: substs.clone() };
|
||||
let trait_env = Arc::clone(&self.trait_env);
|
||||
let implements_fn_goal =
|
||||
self.canonicalizer().canonicalize_obligation(InEnvironment {
|
||||
value: Obligation::Trait(trait_ref),
|
||||
environment: trait_env,
|
||||
});
|
||||
if let Some(Solution::Unique(SolutionVariables(solution))) =
|
||||
self.db.trait_solve(krate, implements_fn_goal.value.clone())
|
||||
{
|
||||
match solution.value.as_slice() {
|
||||
[Ty::Apply(ApplicationTy {
|
||||
ctor: TypeCtor::Tuple { cardinality: _ },
|
||||
parameters,
|
||||
})] => {
|
||||
let output_assoc_type = match fn_trait_data
|
||||
.associated_types()
|
||||
.collect::<Vec<hir_def::TypeAliasId>>()
|
||||
.as_slice()
|
||||
{
|
||||
[output] => *output,
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let output_proj_ty = crate::ProjectionTy {
|
||||
associated_ty: output_assoc_type,
|
||||
parameters: substs,
|
||||
};
|
||||
let return_ty = self.normalize_projection_ty(output_proj_ty);
|
||||
return Some(FnSig::from_params_and_return(
|
||||
parameters.into_iter().map(|ty| ty.clone()).collect(),
|
||||
return_ty,
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
pub fn callable_sig(&mut self, ty: &Ty) -> Option<FnSig> {
|
||||
match ty.callable_sig(self.db) {
|
||||
result @ Some(_) => result,
|
||||
None => self.callable_sig_from_fn_trait(ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let body = Arc::clone(&self.body); // avoid borrow checker problem
|
||||
let ty = match &body[tgt_expr] {
|
||||
@@ -198,14 +267,21 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
Expr::Call { callee, args } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||
let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) {
|
||||
Some(sig) => (sig.params().to_vec(), sig.ret().clone()),
|
||||
None => {
|
||||
// Not callable
|
||||
// FIXME: report an error
|
||||
(Vec::new(), Ty::Unknown)
|
||||
}
|
||||
};
|
||||
let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone());
|
||||
let mut derefs = autoderef(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
value: canonicalized.value.clone(),
|
||||
environment: self.trait_env.clone(),
|
||||
},
|
||||
);
|
||||
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
|
||||
.find_map(|callee_deref_ty| {
|
||||
self.callable_sig(&canonicalized.decanonicalize_ty(callee_deref_ty.value))
|
||||
.map(|sig| (sig.params().to_vec(), sig.ret().clone()))
|
||||
})
|
||||
.unwrap_or((Vec::new(), Ty::Unknown));
|
||||
self.register_obligations_for_call(&callee_ty);
|
||||
self.check_call_arguments(args, ¶m_tys);
|
||||
self.normalize_associated_types_in(ret_ty)
|
||||
@@ -692,7 +768,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let method_ty = method_ty.subst(&substs);
|
||||
let method_ty = self.insert_type_vars(method_ty);
|
||||
self.register_obligations_for_call(&method_ty);
|
||||
let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) {
|
||||
let (expected_receiver_ty, param_tys, ret_ty) = match self.callable_sig(&method_ty) {
|
||||
Some(sig) => {
|
||||
if !sig.params().is_empty() {
|
||||
(sig.params()[0].clone(), sig.params()[1..].to_vec(), sig.ret().clone())
|
||||
|
||||
@@ -14,7 +14,7 @@ use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty,
|
||||
use self::chalk::{from_chalk, Interner, ToChalk};
|
||||
|
||||
pub(crate) mod chalk;
|
||||
mod builtin;
|
||||
pub(crate) mod builtin;
|
||||
|
||||
// This controls the maximum size of types Chalk considers. If we set this too
|
||||
// high, we can run into slow edge cases; if we set it too low, Chalk won't
|
||||
|
||||
@@ -360,7 +360,11 @@ fn super_trait_object_unsize_impl_datum(
|
||||
BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() }
|
||||
}
|
||||
|
||||
fn get_fn_trait(db: &dyn HirDatabase, krate: CrateId, fn_trait: super::FnTrait) -> Option<TraitId> {
|
||||
pub fn get_fn_trait(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
fn_trait: super::FnTrait,
|
||||
) -> Option<TraitId> {
|
||||
let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
|
||||
match target {
|
||||
LangItemTarget::TraitId(t) => Some(t),
|
||||
|
||||
@@ -2410,4 +2410,103 @@ fn func(foo: i32) { if true { <|>foo; }; }
|
||||
]
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_closure_arg() {
|
||||
check_hover_result(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
|
||||
enum Option<T> {
|
||||
None,
|
||||
Some(T)
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let s<|> = Option::None;
|
||||
let f = |x: Option<i32>| {};
|
||||
(&f)(s)
|
||||
}
|
||||
"#,
|
||||
&["Option<i32>"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_fn_trait_arg() {
|
||||
check_hover_result(
|
||||
r#"
|
||||
//- /lib.rs deps:std
|
||||
|
||||
#[lang = "fn"]
|
||||
pub trait Fn<Args> {
|
||||
type Output;
|
||||
|
||||
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
enum Option<T> {
|
||||
None,
|
||||
Some(T)
|
||||
}
|
||||
|
||||
fn foo<F, T>(f: F) -> T
|
||||
where
|
||||
F: Fn(Option<i32>) -> T,
|
||||
{
|
||||
let s<|> = None;
|
||||
f(s)
|
||||
}
|
||||
"#,
|
||||
&["Option<i32>"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_box_fn_arg() {
|
||||
check_hover_result(
|
||||
r#"
|
||||
//- /lib.rs deps:std
|
||||
|
||||
#[lang = "fn_once"]
|
||||
pub trait FnOnce<Args> {
|
||||
type Output;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
#[lang = "deref"]
|
||||
pub trait Deref {
|
||||
type Target: ?Sized;
|
||||
|
||||
fn deref(&self) -> &Self::Target;
|
||||
}
|
||||
|
||||
#[lang = "owned_box"]
|
||||
pub struct Box<T: ?Sized> {
|
||||
inner: *mut T,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for Box<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
enum Option<T> {
|
||||
None,
|
||||
Some(T)
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let s<|> = Option::None;
|
||||
let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
|
||||
f(&s)
|
||||
}
|
||||
"#,
|
||||
&["Option<i32>"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user