Skip to content

Commit c8557b9

Browse files
bors[bot]adamrk
andauthored
Merge #4958
4958: Infer FnSig via Fn traits r=flodiebold a=adamrk Addresses #4481. When inferring types check if the callee implements one of the builtin `Fn` traits. Also autoderef the callee before trying to figure out it's `FnSig`. Co-authored-by: adamrk <ark.email@gmail.com>
2 parents 0f7961d + 1629fb7 commit c8557b9

File tree

4 files changed

+312
-24
lines changed

4 files changed

+312
-24
lines changed

crates/ra_hir_ty/src/infer/expr.rs

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ use hir_def::{
1010
resolver::resolver_for_expr,
1111
AdtId, AssocContainerId, FieldId, Lookup,
1212
};
13-
use hir_expand::name::Name;
13+
use hir_expand::name::{name, Name};
1414
use ra_syntax::ast::RangeOp;
1515

1616
use crate::{
1717
autoderef, method_resolution, op,
18-
traits::InEnvironment,
18+
traits::{FnTrait, InEnvironment},
1919
utils::{generics, variant_data, Generics},
2020
ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
2121
TraitRef, Ty, TypeCtor,
@@ -63,6 +63,58 @@ impl<'a> InferenceContext<'a> {
6363
self.resolve_ty_as_possible(ty)
6464
}
6565

66+
fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
67+
let krate = self.resolver.krate()?;
68+
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
69+
let output_assoc_type =
70+
self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
71+
let generic_params = generics(self.db.upcast(), fn_once_trait.into());
72+
if generic_params.len() != 2 {
73+
return None;
74+
}
75+
76+
let mut param_builder = Substs::builder(num_args);
77+
let mut arg_tys = vec![];
78+
for _ in 0..num_args {
79+
let arg = self.table.new_type_var();
80+
param_builder = param_builder.push(arg.clone());
81+
arg_tys.push(arg);
82+
}
83+
let parameters = param_builder.build();
84+
let arg_ty = Ty::Apply(ApplicationTy {
85+
ctor: TypeCtor::Tuple { cardinality: num_args as u16 },
86+
parameters,
87+
});
88+
let substs = Substs::build_for_generics(&generic_params)
89+
.push(ty.clone())
90+
.push(arg_ty.clone())
91+
.build();
92+
93+
let trait_env = Arc::clone(&self.trait_env);
94+
let implements_fn_trait =
95+
Obligation::Trait(TraitRef { trait_: fn_once_trait, substs: substs.clone() });
96+
let goal = self.canonicalizer().canonicalize_obligation(InEnvironment {
97+
value: implements_fn_trait.clone(),
98+
environment: trait_env,
99+
});
100+
if self.db.trait_solve(krate, goal.value).is_some() {
101+
self.obligations.push(implements_fn_trait);
102+
let output_proj_ty =
103+
crate::ProjectionTy { associated_ty: output_assoc_type, parameters: substs };
104+
let return_ty = self.normalize_projection_ty(output_proj_ty);
105+
Some((arg_tys, return_ty))
106+
} else {
107+
None
108+
}
109+
}
110+
111+
pub fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
112+
match ty.callable_sig(self.db) {
113+
Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
114+
None => self.callable_sig_from_fn_trait(ty, num_args),
115+
}
116+
}
117+
66118
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
67119
let body = Arc::clone(&self.body); // avoid borrow checker problem
68120
let ty = match &body[tgt_expr] {
@@ -198,14 +250,23 @@ impl<'a> InferenceContext<'a> {
198250
}
199251
Expr::Call { callee, args } => {
200252
let callee_ty = self.infer_expr(*callee, &Expectation::none());
201-
let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) {
202-
Some(sig) => (sig.params().to_vec(), sig.ret().clone()),
203-
None => {
204-
// Not callable
205-
// FIXME: report an error
206-
(Vec::new(), Ty::Unknown)
207-
}
208-
};
253+
let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone());
254+
let mut derefs = autoderef(
255+
self.db,
256+
self.resolver.krate(),
257+
InEnvironment {
258+
value: canonicalized.value.clone(),
259+
environment: self.trait_env.clone(),
260+
},
261+
);
262+
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
263+
.find_map(|callee_deref_ty| {
264+
self.callable_sig(
265+
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
266+
args.len(),
267+
)
268+
})
269+
.unwrap_or((Vec::new(), Ty::Unknown));
209270
self.register_obligations_for_call(&callee_ty);
210271
self.check_call_arguments(args, &param_tys);
211272
self.normalize_associated_types_in(ret_ty)

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2888,3 +2888,226 @@ impl<A: Step> iter::Iterator for ops::Range<A> {
28882888
);
28892889
assert_eq!(t, "i32");
28902890
}
2891+
2892+
#[test]
2893+
fn infer_closure_arg() {
2894+
assert_snapshot!(
2895+
infer(
2896+
r#"
2897+
//- /lib.rs
2898+
2899+
enum Option<T> {
2900+
None,
2901+
Some(T)
2902+
}
2903+
2904+
fn foo() {
2905+
let s = Option::None;
2906+
let f = |x: Option<i32>| {};
2907+
(&f)(s)
2908+
}
2909+
"#
2910+
),
2911+
@r###"
2912+
137..259 '{ ... }': ()
2913+
159..160 's': Option<i32>
2914+
163..175 'Option::None': Option<i32>
2915+
197..198 'f': |Option<i32>| -> ()
2916+
201..220 '|x: Op...2>| {}': |Option<i32>| -> ()
2917+
202..203 'x': Option<i32>
2918+
218..220 '{}': ()
2919+
238..245 '(&f)(s)': ()
2920+
239..241 '&f': &|Option<i32>| -> ()
2921+
240..241 'f': |Option<i32>| -> ()
2922+
243..244 's': Option<i32>
2923+
"###
2924+
);
2925+
}
2926+
2927+
#[test]
2928+
fn infer_fn_trait_arg() {
2929+
assert_snapshot!(
2930+
infer(
2931+
r#"
2932+
//- /lib.rs deps:std
2933+
2934+
#[lang = "fn_once"]
2935+
pub trait FnOnce<Args> {
2936+
type Output;
2937+
2938+
extern "rust-call" fn call_once(&self, args: Args) -> Self::Output;
2939+
}
2940+
2941+
#[lang = "fn"]
2942+
pub trait Fn<Args>:FnOnce<Args> {
2943+
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
2944+
}
2945+
2946+
enum Option<T> {
2947+
None,
2948+
Some(T)
2949+
}
2950+
2951+
fn foo<F, T>(f: F) -> T
2952+
where
2953+
F: Fn(Option<i32>) -> T,
2954+
{
2955+
let s = None;
2956+
f(s)
2957+
}
2958+
"#
2959+
),
2960+
@r###"
2961+
183..187 'self': &Self
2962+
189..193 'args': Args
2963+
350..354 'self': &Self
2964+
356..360 'args': Args
2965+
515..516 'f': F
2966+
597..663 '{ ... }': T
2967+
619..620 's': Option<i32>
2968+
623..627 'None': Option<i32>
2969+
645..646 'f': F
2970+
645..649 'f(s)': T
2971+
647..648 's': Option<i32>
2972+
"###
2973+
);
2974+
}
2975+
2976+
#[test]
2977+
fn infer_box_fn_arg() {
2978+
assert_snapshot!(
2979+
infer(
2980+
r#"
2981+
//- /lib.rs deps:std
2982+
2983+
#[lang = "fn_once"]
2984+
pub trait FnOnce<Args> {
2985+
type Output;
2986+
2987+
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
2988+
}
2989+
2990+
#[lang = "deref"]
2991+
pub trait Deref {
2992+
type Target: ?Sized;
2993+
2994+
fn deref(&self) -> &Self::Target;
2995+
}
2996+
2997+
#[lang = "owned_box"]
2998+
pub struct Box<T: ?Sized> {
2999+
inner: *mut T,
3000+
}
3001+
3002+
impl<T: ?Sized> Deref for Box<T> {
3003+
type Target = T;
3004+
3005+
fn deref(&self) -> &T {
3006+
&self.inner
3007+
}
3008+
}
3009+
3010+
enum Option<T> {
3011+
None,
3012+
Some(T)
3013+
}
3014+
3015+
fn foo() {
3016+
let s = Option::None;
3017+
let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
3018+
f(&s)
3019+
}
3020+
"#
3021+
),
3022+
@r###"
3023+
182..186 'self': Self
3024+
188..192 'args': Args
3025+
356..360 'self': &Self
3026+
622..626 'self': &Box<T>
3027+
634..685 '{ ... }': &T
3028+
656..667 '&self.inner': &*mut T
3029+
657..661 'self': &Box<T>
3030+
657..667 'self.inner': *mut T
3031+
812..957 '{ ... }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)>
3032+
834..835 's': Option<i32>
3033+
838..850 'Option::None': Option<i32>
3034+
872..873 'f': Box<dyn FnOnce<(&Option<i32>,)>>
3035+
907..920 'box (|ps| {})': Box<|{unknown}| -> ()>
3036+
912..919 '|ps| {}': |{unknown}| -> ()
3037+
913..915 'ps': {unknown}
3038+
917..919 '{}': ()
3039+
938..939 'f': Box<dyn FnOnce<(&Option<i32>,)>>
3040+
938..943 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)>
3041+
940..942 '&s': &Option<i32>
3042+
941..942 's': Option<i32>
3043+
"###
3044+
);
3045+
}
3046+
3047+
#[test]
3048+
fn infer_dyn_fn_output() {
3049+
assert_snapshot!(
3050+
infer(
3051+
r#"
3052+
//- /lib.rs deps:std
3053+
3054+
#[lang = "fn_once"]
3055+
pub trait FnOnce<Args> {
3056+
type Output;
3057+
3058+
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3059+
}
3060+
3061+
#[lang = "fn"]
3062+
pub trait Fn<Args>:FnOnce<Args> {
3063+
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
3064+
}
3065+
3066+
#[lang = "deref"]
3067+
pub trait Deref {
3068+
type Target: ?Sized;
3069+
3070+
fn deref(&self) -> &Self::Target;
3071+
}
3072+
3073+
#[lang = "owned_box"]
3074+
pub struct Box<T: ?Sized> {
3075+
inner: *mut T,
3076+
}
3077+
3078+
impl<T: ?Sized> Deref for Box<T> {
3079+
type Target = T;
3080+
3081+
fn deref(&self) -> &T {
3082+
&self.inner
3083+
}
3084+
}
3085+
3086+
fn foo() {
3087+
let f: Box<dyn Fn() -> i32> = box(|| 5);
3088+
let x = f();
3089+
}
3090+
"#
3091+
),
3092+
@r###"
3093+
182..186 'self': Self
3094+
188..192 'args': Args
3095+
349..353 'self': &Self
3096+
355..359 'args': Args
3097+
523..527 'self': &Self
3098+
789..793 'self': &Box<T>
3099+
801..852 '{ ... }': &T
3100+
823..834 '&self.inner': &*mut T
3101+
824..828 'self': &Box<T>
3102+
824..834 'self.inner': *mut T
3103+
889..990 '{ ... }': ()
3104+
911..912 'f': Box<dyn Fn<(), Output = i32>>
3105+
937..946 'box(|| 5)': Box<|| -> i32>
3106+
941..945 '|| 5': || -> i32
3107+
944..945 '5': i32
3108+
968..969 'x': FnOnce::Output<dyn Fn<(), Output = i32>, ()>
3109+
972..973 'f': Box<dyn Fn<(), Output = i32>>
3110+
972..975 'f()': FnOnce::Output<dyn Fn<(), Output = i32>, ()>
3111+
"###
3112+
);
3113+
}

crates/ra_hir_ty/src/traits.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
use std::{panic, sync::Arc};
33

44
use chalk_ir::cast::Cast;
5-
use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId};
5+
use hir_def::{
6+
expr::ExprId, lang_item::LangItemTarget, DefWithBodyId, ImplId, TraitId, TypeAliasId,
7+
};
68
use ra_db::{impl_intern_key, salsa, CrateId};
79
use ra_prof::profile;
810
use rustc_hash::FxHashSet;
@@ -298,6 +300,14 @@ impl FnTrait {
298300
FnTrait::Fn => "fn",
299301
}
300302
}
303+
304+
pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
305+
let target = db.lang_item(krate, self.lang_item_name().into())?;
306+
match target {
307+
LangItemTarget::TraitId(t) => Some(t),
308+
_ => None,
309+
}
310+
}
301311
}
302312

303313
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

0 commit comments

Comments
 (0)