Skip to content

Commit a063f00

Browse files
committed
Support function pointer MIR lowering
1 parent 1b85b43 commit a063f00

File tree

14 files changed

+467
-163
lines changed

14 files changed

+467
-163
lines changed

crates/hir-expand/src/name.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ pub mod known {
343343
feature,
344344
// known methods of lang items
345345
call_once,
346+
call_mut,
347+
call,
346348
eq,
347349
ne,
348350
ge,

crates/hir-ty/src/consteval/tests.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,108 @@ fn or_pattern() {
906906
);
907907
}
908908

909+
#[test]
910+
fn function_pointer() {
911+
check_number(
912+
r#"
913+
fn add2(x: u8) -> u8 {
914+
x + 2
915+
}
916+
const GOAL: u8 = {
917+
let plus2 = add2;
918+
plus2(3)
919+
};
920+
"#,
921+
5,
922+
);
923+
check_number(
924+
r#"
925+
fn add2(x: u8) -> u8 {
926+
x + 2
927+
}
928+
const GOAL: u8 = {
929+
let plus2: fn(u8) -> u8 = add2;
930+
plus2(3)
931+
};
932+
"#,
933+
5,
934+
);
935+
check_number(
936+
r#"
937+
//- minicore: coerce_unsized, index, slice
938+
fn add2(x: u8) -> u8 {
939+
x + 2
940+
}
941+
fn mult3(x: u8) -> u8 {
942+
x * 3
943+
}
944+
const GOAL: u8 = {
945+
let x = [add2, mult3];
946+
x[0](1) + x[1](5)
947+
};
948+
"#,
949+
18,
950+
);
951+
}
952+
953+
#[test]
954+
fn function_traits() {
955+
check_number(
956+
r#"
957+
//- minicore: fn
958+
fn add2(x: u8) -> u8 {
959+
x + 2
960+
}
961+
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
962+
f(x)
963+
}
964+
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
965+
f(x)
966+
}
967+
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
968+
f(x)
969+
}
970+
const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
971+
"#,
972+
15,
973+
);
974+
check_number(
975+
r#"
976+
//- minicore: fn
977+
fn add2(x: u8) -> u8 {
978+
x + 2
979+
}
980+
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
981+
f(x)
982+
}
983+
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
984+
f(x)
985+
}
986+
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
987+
f(x)
988+
}
989+
const GOAL: u8 = {
990+
let add2: fn(u8) -> u8 = add2;
991+
call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
992+
};
993+
"#,
994+
15,
995+
);
996+
check_number(
997+
r#"
998+
//- minicore: fn
999+
fn add2(x: u8) -> u8 {
1000+
x + 2
1001+
}
1002+
fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
1003+
f(x)
1004+
}
1005+
const GOAL: u8 = call(&&&&&add2, 3);
1006+
"#,
1007+
5,
1008+
);
1009+
}
1010+
9091011
#[test]
9101012
fn array_and_index() {
9111013
check_number(

crates/hir-ty/src/infer.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ use stdx::{always, never};
3838

3939
use crate::{
4040
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
41-
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
42-
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
43-
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
41+
lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
42+
DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
43+
Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
4444
};
4545

4646
// This lint has a false positive here. See the link below for details.
@@ -273,6 +273,13 @@ pub struct Adjustment {
273273
pub target: Ty,
274274
}
275275

276+
impl Adjustment {
277+
pub fn borrow(m: Mutability, ty: Ty) -> Self {
278+
let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
279+
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
280+
}
281+
}
282+
276283
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
277284
pub enum Adjust {
278285
/// Go from ! to any type.

crates/hir-ty/src/infer/coerce.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ fn success(
5151
pub(super) struct CoerceMany {
5252
expected_ty: Ty,
5353
final_ty: Option<Ty>,
54+
expressions: Vec<ExprId>,
5455
}
5556

5657
impl CoerceMany {
5758
pub(super) fn new(expected: Ty) -> Self {
58-
CoerceMany { expected_ty: expected, final_ty: None }
59+
CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
5960
}
6061

6162
/// Returns the "expected type" with which this coercion was
@@ -125,8 +126,15 @@ impl CoerceMany {
125126
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
126127
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
127128
if let (Ok(result1), Ok(result2)) = (result1, result2) {
128-
ctx.table.register_infer_ok(result1);
129-
ctx.table.register_infer_ok(result2);
129+
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
130+
for &e in &self.expressions {
131+
ctx.write_expr_adj(e, result1.value.0.clone());
132+
}
133+
ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
134+
if let Some(expr) = expr {
135+
ctx.write_expr_adj(expr, result2.value.0);
136+
self.expressions.push(expr);
137+
}
130138
return self.final_ty = Some(target_ty);
131139
}
132140
}
@@ -148,6 +156,9 @@ impl CoerceMany {
148156
}
149157
cov_mark::hit!(coerce_merge_fail_fallback);
150158
}
159+
if let Some(expr) = expr {
160+
self.expressions.push(expr);
161+
}
151162
}
152163
}
153164

crates/hir-ty/src/infer/expr.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::{
3434
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
3535
primitive::{self, UintTy},
3636
static_lifetime, to_chalk_trait_id,
37+
traits::FnTrait,
3738
utils::{generics, Generics},
3839
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
3940
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
@@ -385,16 +386,32 @@ impl<'a> InferenceContext<'a> {
385386
|| res.is_none();
386387
let (param_tys, ret_ty) = match res {
387388
Some((func, params, ret_ty)) => {
388-
let adjustments = auto_deref_adjust_steps(&derefs);
389-
// FIXME: Handle call adjustments for Fn/FnMut
390-
self.write_expr_adj(*callee, adjustments);
391-
if let Some((trait_, func)) = func {
392-
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
393-
.push(callee_ty.clone())
394-
.push(TyBuilder::tuple_with(params.iter().cloned()))
395-
.build();
396-
self.write_method_resolution(tgt_expr, func, subst.clone());
389+
let mut adjustments = auto_deref_adjust_steps(&derefs);
390+
if let Some(fn_x) = func {
391+
match fn_x {
392+
FnTrait::FnOnce => (),
393+
FnTrait::FnMut => adjustments.push(Adjustment::borrow(
394+
Mutability::Mut,
395+
derefed_callee.clone(),
396+
)),
397+
FnTrait::Fn => adjustments.push(Adjustment::borrow(
398+
Mutability::Not,
399+
derefed_callee.clone(),
400+
)),
401+
}
402+
let trait_ = fn_x
403+
.get_id(self.db, self.trait_env.krate)
404+
.expect("We just used it");
405+
let trait_data = self.db.trait_data(trait_);
406+
if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
407+
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
408+
.push(callee_ty.clone())
409+
.push(TyBuilder::tuple_with(params.iter().cloned()))
410+
.build();
411+
self.write_method_resolution(tgt_expr, func, subst.clone());
412+
}
397413
}
414+
self.write_expr_adj(*callee, adjustments);
398415
(params, ret_ty)
399416
}
400417
None => {

crates/hir-ty/src/infer/unify.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ use chalk_ir::{
88
};
99
use chalk_solve::infer::ParameterEnaVariableExt;
1010
use ena::unify::UnifyKey;
11-
use hir_def::{FunctionId, TraitId};
1211
use hir_expand::name;
1312
use stdx::never;
1413

1514
use super::{InferOk, InferResult, InferenceContext, TypeError};
1615
use crate::{
17-
db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
18-
Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
19-
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
20-
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
16+
db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
17+
AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
18+
InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
19+
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
2120
};
2221

2322
impl<'a> InferenceContext<'a> {
@@ -631,7 +630,7 @@ impl<'a> InferenceTable<'a> {
631630
&mut self,
632631
ty: &Ty,
633632
num_args: usize,
634-
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
633+
) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
635634
match ty.callable_sig(self.db) {
636635
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
637636
None => self.callable_sig_from_fn_trait(ty, num_args),
@@ -642,7 +641,7 @@ impl<'a> InferenceTable<'a> {
642641
&mut self,
643642
ty: &Ty,
644643
num_args: usize,
645-
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
644+
) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
646645
let krate = self.trait_env.krate;
647646
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
648647
let trait_data = self.db.trait_data(fn_once_trait);
@@ -676,19 +675,28 @@ impl<'a> InferenceTable<'a> {
676675
};
677676

678677
let trait_env = self.trait_env.env.clone();
678+
let mut trait_ref = projection.trait_ref(self.db);
679679
let obligation = InEnvironment {
680-
goal: projection.trait_ref(self.db).cast(Interner),
681-
environment: trait_env,
680+
goal: trait_ref.clone().cast(Interner),
681+
environment: trait_env.clone(),
682682
};
683683
let canonical = self.canonicalize(obligation.clone());
684684
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
685685
self.register_obligation(obligation.goal);
686686
let return_ty = self.normalize_projection_ty(projection);
687-
Some((
688-
Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
689-
arg_tys,
690-
return_ty,
691-
))
687+
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
688+
let fn_x_trait = fn_x.get_id(self.db, krate)?;
689+
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
690+
let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
691+
goal: trait_ref.clone().cast(Interner),
692+
environment: trait_env.clone(),
693+
};
694+
let canonical = self.canonicalize(obligation.clone());
695+
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
696+
return Some((Some(fn_x), arg_tys, return_ty));
697+
}
698+
}
699+
unreachable!("It should at least implement FnOnce at this point");
692700
} else {
693701
None
694702
}

crates/hir-ty/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,10 +576,14 @@ where
576576
}
577577

578578
pub fn callable_sig_from_fnonce(
579-
self_ty: &Ty,
579+
mut self_ty: &Ty,
580580
env: Arc<TraitEnvironment>,
581581
db: &dyn HirDatabase,
582582
) -> Option<CallableSig> {
583+
if let Some((ty, _, _)) = self_ty.as_reference() {
584+
// This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
585+
self_ty = ty;
586+
}
583587
let krate = env.krate;
584588
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
585589
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;

crates/hir-ty/src/method_resolution.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
autoderef::{self, AutoderefKind},
2121
db::HirDatabase,
2222
from_chalk_trait_id, from_foreign_def_id,
23-
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
23+
infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
2424
primitive::{FloatTy, IntTy, UintTy},
2525
static_lifetime, to_chalk_trait_id,
2626
utils::all_super_traits,
@@ -600,9 +600,9 @@ impl ReceiverAdjustments {
600600
}
601601
}
602602
if let Some(m) = self.autoref {
603-
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
604-
adjust
605-
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
603+
let a = Adjustment::borrow(m, ty);
604+
ty = a.target.clone();
605+
adjust.push(a);
606606
}
607607
if self.unsize_array {
608608
ty = 'x: {

0 commit comments

Comments
 (0)