about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-expand/src/name.rs2
-rw-r--r--crates/hir-ty/src/consteval/tests.rs102
-rw-r--r--crates/hir-ty/src/infer.rs13
-rw-r--r--crates/hir-ty/src/infer/coerce.rs17
-rw-r--r--crates/hir-ty/src/infer/expr.rs35
-rw-r--r--crates/hir-ty/src/infer/unify.rs36
-rw-r--r--crates/hir-ty/src/lib.rs6
-rw-r--r--crates/hir-ty/src/method_resolution.rs8
-rw-r--r--crates/hir-ty/src/mir/eval.rs297
-rw-r--r--crates/hir-ty/src/mir/lower.rs46
-rw-r--r--crates/hir-ty/src/mir/pretty.rs4
-rw-r--r--crates/hir-ty/src/tests/coercion.rs30
-rw-r--r--crates/hir-ty/src/traits.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs25
14 files changed, 467 insertions, 163 deletions
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index c3462beac73..71eb35d9df8 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -343,6 +343,8 @@ pub mod known {
         feature,
         // known methods of lang items
         call_once,
+        call_mut,
+        call,
         eq,
         ne,
         ge,
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 3bec2ee88bf..8a9a5d254df 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -907,6 +907,108 @@ fn or_pattern() {
 }
 
 #[test]
+fn function_pointer() {
+    check_number(
+        r#"
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    const GOAL: u8 = {
+        let plus2 = add2;
+        plus2(3)
+    };
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    const GOAL: u8 = {
+        let plus2: fn(u8) -> u8 = add2;
+        plus2(3)
+    };
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    fn mult3(x: u8) -> u8 {
+        x * 3
+    }
+    const GOAL: u8 = {
+        let x = [add2, mult3];
+        x[0](1) + x[1](5)
+    };
+        "#,
+        18,
+    );
+}
+
+#[test]
+fn function_traits() {
+    check_number(
+        r#"
+    //- minicore: fn
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
+        "#,
+        15,
+    );
+    check_number(
+        r#"
+    //- minicore: fn
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    const GOAL: u8 = {
+        let add2: fn(u8) -> u8 = add2;
+        call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
+    };
+        "#,
+        15,
+    );
+    check_number(
+        r#"
+    //- minicore: fn
+    fn add2(x: u8) -> u8 {
+        x + 2
+    }
+    fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
+        f(x)
+    }
+    const GOAL: u8 = call(&&&&&add2, 3);
+        "#,
+        5,
+    );
+}
+
+#[test]
 fn array_and_index() {
     check_number(
         r#"
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 00b5f7948ac..06d74215fff 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -38,9 +38,9 @@ use stdx::{always, never};
 
 use crate::{
     db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
-    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
-    GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
-    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+    lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
+    DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
+    Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -273,6 +273,13 @@ pub struct Adjustment {
     pub target: Ty,
 }
 
+impl Adjustment {
+    pub fn borrow(m: Mutability, ty: Ty) -> Self {
+        let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
+        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum Adjust {
     /// Go from ! to any type.
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 48c91530266..6e899249b69 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -51,11 +51,12 @@ fn success(
 pub(super) struct CoerceMany {
     expected_ty: Ty,
     final_ty: Option<Ty>,
+    expressions: Vec<ExprId>,
 }
 
 impl CoerceMany {
     pub(super) fn new(expected: Ty) -> Self {
-        CoerceMany { expected_ty: expected, final_ty: None }
+        CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
     }
 
     /// Returns the "expected type" with which this coercion was
@@ -125,8 +126,15 @@ impl CoerceMany {
             let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
             let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
             if let (Ok(result1), Ok(result2)) = (result1, result2) {
-                ctx.table.register_infer_ok(result1);
-                ctx.table.register_infer_ok(result2);
+                ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
+                for &e in &self.expressions {
+                    ctx.write_expr_adj(e, result1.value.0.clone());
+                }
+                ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
+                if let Some(expr) = expr {
+                    ctx.write_expr_adj(expr, result2.value.0);
+                    self.expressions.push(expr);
+                }
                 return self.final_ty = Some(target_ty);
             }
         }
@@ -148,6 +156,9 @@ impl CoerceMany {
             }
             cov_mark::hit!(coerce_merge_fail_fallback);
         }
+        if let Some(expr) = expr {
+            self.expressions.push(expr);
+        }
     }
 }
 
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 7bf227a27f2..ca285f2fec0 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -34,6 +34,7 @@ use crate::{
     method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
     primitive::{self, UintTy},
     static_lifetime, to_chalk_trait_id,
+    traits::FnTrait,
     utils::{generics, Generics},
     Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
     Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
@@ -385,16 +386,32 @@ impl<'a> InferenceContext<'a> {
                         || res.is_none();
                 let (param_tys, ret_ty) = match res {
                     Some((func, params, ret_ty)) => {
-                        let adjustments = auto_deref_adjust_steps(&derefs);
-                        // FIXME: Handle call adjustments for Fn/FnMut
-                        self.write_expr_adj(*callee, adjustments);
-                        if let Some((trait_, func)) = func {
-                            let subst = TyBuilder::subst_for_def(self.db, trait_, None)
-                                .push(callee_ty.clone())
-                                .push(TyBuilder::tuple_with(params.iter().cloned()))
-                                .build();
-                            self.write_method_resolution(tgt_expr, func, subst.clone());
+                        let mut adjustments = auto_deref_adjust_steps(&derefs);
+                        if let Some(fn_x) = func {
+                            match fn_x {
+                                FnTrait::FnOnce => (),
+                                FnTrait::FnMut => adjustments.push(Adjustment::borrow(
+                                    Mutability::Mut,
+                                    derefed_callee.clone(),
+                                )),
+                                FnTrait::Fn => adjustments.push(Adjustment::borrow(
+                                    Mutability::Not,
+                                    derefed_callee.clone(),
+                                )),
+                            }
+                            let trait_ = fn_x
+                                .get_id(self.db, self.trait_env.krate)
+                                .expect("We just used it");
+                            let trait_data = self.db.trait_data(trait_);
+                            if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
+                                let subst = TyBuilder::subst_for_def(self.db, trait_, None)
+                                    .push(callee_ty.clone())
+                                    .push(TyBuilder::tuple_with(params.iter().cloned()))
+                                    .build();
+                                self.write_method_resolution(tgt_expr, func, subst.clone());
+                            }
                         }
+                        self.write_expr_adj(*callee, adjustments);
                         (params, ret_ty)
                     }
                     None => {
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 504f0743aa9..2a07dd708c2 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -8,16 +8,15 @@ use chalk_ir::{
 };
 use chalk_solve::infer::ParameterEnaVariableExt;
 use ena::unify::UnifyKey;
-use hir_def::{FunctionId, TraitId};
 use hir_expand::name;
 use stdx::never;
 
 use super::{InferOk, InferResult, InferenceContext, TypeError};
 use crate::{
-    db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
-    Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
-    InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
-    Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+    db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
+    AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
+    InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
+    Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -631,7 +630,7 @@ impl<'a> InferenceTable<'a> {
         &mut self,
         ty: &Ty,
         num_args: usize,
-    ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+    ) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
         match ty.callable_sig(self.db) {
             Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
             None => self.callable_sig_from_fn_trait(ty, num_args),
@@ -642,7 +641,7 @@ impl<'a> InferenceTable<'a> {
         &mut self,
         ty: &Ty,
         num_args: usize,
-    ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+    ) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
         let krate = self.trait_env.krate;
         let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
         let trait_data = self.db.trait_data(fn_once_trait);
@@ -676,19 +675,28 @@ impl<'a> InferenceTable<'a> {
         };
 
         let trait_env = self.trait_env.env.clone();
+        let mut trait_ref = projection.trait_ref(self.db);
         let obligation = InEnvironment {
-            goal: projection.trait_ref(self.db).cast(Interner),
-            environment: trait_env,
+            goal: trait_ref.clone().cast(Interner),
+            environment: trait_env.clone(),
         };
         let canonical = self.canonicalize(obligation.clone());
         if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
             self.register_obligation(obligation.goal);
             let return_ty = self.normalize_projection_ty(projection);
-            Some((
-                Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
-                arg_tys,
-                return_ty,
-            ))
+            for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
+                let fn_x_trait = fn_x.get_id(self.db, krate)?;
+                trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
+                let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
+                    goal: trait_ref.clone().cast(Interner),
+                    environment: trait_env.clone(),
+                };
+                let canonical = self.canonicalize(obligation.clone());
+                if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
+                    return Some((Some(fn_x), arg_tys, return_ty));
+                }
+            }
+            unreachable!("It should at least implement FnOnce at this point");
         } else {
             None
         }
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 9c63d67ab19..782a8ab4aa2 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -576,10 +576,14 @@ where
 }
 
 pub fn callable_sig_from_fnonce(
-    self_ty: &Ty,
+    mut self_ty: &Ty,
     env: Arc<TraitEnvironment>,
     db: &dyn HirDatabase,
 ) -> Option<CallableSig> {
+    if let Some((ty, _, _)) = self_ty.as_reference() {
+        // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
+        self_ty = ty;
+    }
     let krate = env.krate;
     let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
     let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index f3a27632bf5..f105c94086c 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -20,7 +20,7 @@ use crate::{
     autoderef::{self, AutoderefKind},
     db::HirDatabase,
     from_chalk_trait_id, from_foreign_def_id,
-    infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
+    infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
     primitive::{FloatTy, IntTy, UintTy},
     static_lifetime, to_chalk_trait_id,
     utils::all_super_traits,
@@ -600,9 +600,9 @@ impl ReceiverAdjustments {
             }
         }
         if let Some(m) = self.autoref {
-            ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
-            adjust
-                .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
+            let a = Adjustment::borrow(m, ty);
+            ty = a.target.clone();
+            adjust.push(a);
         }
         if self.unsize_array {
             ty = 'x: {
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 1ff6e106021..88ef92a4ae6 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -11,7 +11,7 @@ use hir_def::{
     builtin_type::BuiltinType,
     lang_item::{lang_attr, LangItem},
     layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
-    AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
+    AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, VariantId,
 };
 use intern::Interned;
 use la_arena::ArenaMap;
@@ -24,8 +24,9 @@ use crate::{
     layout::layout_of_ty,
     mapping::from_chalk,
     method_resolution::lookup_impl_method,
-    CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, Ty,
-    TyBuilder, TyExt,
+    traits::FnTrait,
+    CallableDefId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution,
+    TraitEnvironment, Ty, TyBuilder, TyExt,
 };
 
 use super::{
@@ -33,11 +34,37 @@ use super::{
     Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
 };
 
+#[derive(Debug, Default)]
+struct VTableMap {
+    ty_to_id: HashMap<Ty, usize>,
+    id_to_ty: Vec<Ty>,
+}
+
+impl VTableMap {
+    fn id(&mut self, ty: Ty) -> usize {
+        if let Some(x) = self.ty_to_id.get(&ty) {
+            return *x;
+        }
+        let id = self.id_to_ty.len();
+        self.id_to_ty.push(ty.clone());
+        self.ty_to_id.insert(ty, id);
+        id
+    }
+
+    fn ty(&self, id: usize) -> Result<&Ty> {
+        self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
+    }
+}
+
 pub struct Evaluator<'a> {
     db: &'a dyn HirDatabase,
     trait_env: Arc<TraitEnvironment>,
     stack: Vec<u8>,
     heap: Vec<u8>,
+    /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we
+    /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the
+    /// time of use.
+    vtable_map: VTableMap,
     crate_id: CrateId,
     // FIXME: This is a workaround, see the comment on `interpret_mir`
     assert_placeholder_ty_is_unused: bool,
@@ -147,6 +174,7 @@ pub enum MirEvalError {
     ExecutionLimitExceeded,
     StackOverflow,
     TargetDataLayoutNotAvailable,
+    InvalidVTableId(usize),
 }
 
 impl std::fmt::Debug for MirEvalError {
@@ -168,6 +196,7 @@ impl std::fmt::Debug for MirEvalError {
             Self::MirLowerError(arg0, arg1) => {
                 f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
             }
+            Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
             Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
             Self::InvalidConst(arg0) => {
                 let data = &arg0.data(Interner);
@@ -240,6 +269,7 @@ impl Evaluator<'_> {
         Evaluator {
             stack: vec![0],
             heap: vec![0],
+            vtable_map: VTableMap::default(),
             db,
             trait_env,
             crate_id,
@@ -461,108 +491,16 @@ impl Evaluator<'_> {
                 } => {
                     let fn_ty = self.operand_ty(func, &locals)?;
                     match &fn_ty.data(Interner).kind {
+                        TyKind::Function(_) => {
+                            let bytes = self.eval_operand(func, &locals)?;
+                            self.exec_fn_pointer(bytes, destination, args, &locals)?;
+                        }
                         TyKind::FnDef(def, generic_args) => {
-                            let def: CallableDefId = from_chalk(self.db, *def);
-                            let generic_args = self.subst_filler(generic_args, &locals);
-                            match def {
-                                CallableDefId::FunctionId(def) => {
-                                    let arg_bytes = args
-                                        .iter()
-                                        .map(|x| {
-                                            Ok(self
-                                                .eval_operand(x, &locals)?
-                                                .get(&self)?
-                                                .to_owned())
-                                        })
-                                        .collect::<Result<Vec<_>>>()?
-                                        .into_iter();
-                                    let function_data = self.db.function_data(def);
-                                    let is_intrinsic = match &function_data.abi {
-                                        Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
-                                        None => match def.lookup(self.db.upcast()).container {
-                                            hir_def::ItemContainerId::ExternBlockId(block) => {
-                                                let id = block.lookup(self.db.upcast()).id;
-                                                id.item_tree(self.db.upcast())[id.value]
-                                                    .abi
-                                                    .as_deref()
-                                                    == Some("rust-intrinsic")
-                                            }
-                                            _ => false,
-                                        },
-                                    };
-                                    let result = if is_intrinsic {
-                                        self.exec_intrinsic(
-                                            function_data
-                                                .name
-                                                .as_text()
-                                                .unwrap_or_default()
-                                                .as_str(),
-                                            arg_bytes,
-                                            generic_args,
-                                            &locals,
-                                        )?
-                                    } else if let Some(x) = self.detect_lang_function(def) {
-                                        self.exec_lang_item(x, arg_bytes)?
-                                    } else {
-                                        let (imp, generic_args) = lookup_impl_method(
-                                            self.db,
-                                            self.trait_env.clone(),
-                                            def,
-                                            generic_args.clone(),
-                                        );
-                                        let generic_args =
-                                            self.subst_filler(&generic_args, &locals);
-                                        let def = imp.into();
-                                        let mir_body = self
-                                            .db
-                                            .mir_body(def)
-                                            .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
-                                        self.interpret_mir(&mir_body, arg_bytes, generic_args)
-                                            .map_err(|e| {
-                                                MirEvalError::InFunction(imp, Box::new(e))
-                                            })?
-                                    };
-                                    let dest_addr = self.place_addr(destination, &locals)?;
-                                    self.write_memory(dest_addr, &result)?;
-                                }
-                                CallableDefId::StructId(id) => {
-                                    let (size, variant_layout, tag) = self.layout_of_variant(
-                                        id.into(),
-                                        generic_args.clone(),
-                                        &locals,
-                                    )?;
-                                    let result = self.make_by_layout(
-                                        size,
-                                        &variant_layout,
-                                        tag,
-                                        args,
-                                        &locals,
-                                    )?;
-                                    let dest_addr = self.place_addr(destination, &locals)?;
-                                    self.write_memory(dest_addr, &result)?;
-                                }
-                                CallableDefId::EnumVariantId(id) => {
-                                    let (size, variant_layout, tag) = self.layout_of_variant(
-                                        id.into(),
-                                        generic_args.clone(),
-                                        &locals,
-                                    )?;
-                                    let result = self.make_by_layout(
-                                        size,
-                                        &variant_layout,
-                                        tag,
-                                        args,
-                                        &locals,
-                                    )?;
-                                    let dest_addr = self.place_addr(destination, &locals)?;
-                                    self.write_memory(dest_addr, &result)?;
-                                }
-                            }
-                            current_block_idx =
-                                target.expect("broken mir, function without target");
+                            self.exec_fn_def(*def, generic_args, destination, args, &locals)?;
                         }
-                        _ => not_supported!("unknown function type"),
+                        x => not_supported!("unknown function type {x:?}"),
                     }
+                    current_block_idx = target.expect("broken mir, function without target");
                 }
                 Terminator::SwitchInt { discr, targets } => {
                     let val = u128::from_le_bytes(pad16(
@@ -808,6 +746,16 @@ impl Evaluator<'_> {
                     not_supported!("creating pointer from exposed address")
                 }
                 CastKind::Pointer(cast) => match cast {
+                    PointerCast::ReifyFnPointer => {
+                        let current_ty = self.operand_ty(operand, locals)?;
+                        if let TyKind::FnDef(_, _) = &current_ty.data(Interner).kind {
+                            let id = self.vtable_map.id(current_ty);
+                            let ptr_size = self.ptr_size();
+                            Owned(id.to_le_bytes()[0..ptr_size].to_vec())
+                        } else {
+                            not_supported!("ReifyFnPointer cast of a non FnDef type");
+                        }
+                    }
                     PointerCast::Unsize => {
                         let current_ty = self.operand_ty(operand, locals)?;
                         match &target_ty.data(Interner).kind {
@@ -920,7 +868,7 @@ impl Evaluator<'_> {
         size: usize, // Not neccessarily equal to variant_layout.size
         variant_layout: &Layout,
         tag: Option<(usize, usize, i128)>,
-        values: &Vec<Operand>,
+        values: &[Operand],
         locals: &Locals<'_>,
     ) -> Result<Vec<u8>> {
         let mut result = vec![0; size];
@@ -1140,6 +1088,20 @@ impl Evaluator<'_> {
         None
     }
 
+    fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
+        use LangItem::*;
+        let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
+            return None;
+        };
+        let l = lang_attr(self.db.upcast(), parent)?;
+        match l {
+            FnOnce => Some(FnTrait::FnOnce),
+            FnMut => Some(FnTrait::FnMut),
+            Fn => Some(FnTrait::Fn),
+            _ => None,
+        }
+    }
+
     fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
         // FIXME: support indirect references
         let mut mm = MemoryMap::default();
@@ -1228,7 +1190,134 @@ impl Evaluator<'_> {
         }
     }
 
-    pub(crate) fn exec_lang_item(
+    fn exec_fn_pointer(
+        &mut self,
+        bytes: Interval,
+        destination: &Place,
+        args: &[Operand],
+        locals: &Locals<'_>,
+    ) -> Result<()> {
+        let id = from_bytes!(usize, bytes.get(self)?);
+        let next_ty = self.vtable_map.ty(id)?.clone();
+        if let TyKind::FnDef(def, generic_args) = &next_ty.data(Interner).kind {
+            self.exec_fn_def(*def, generic_args, destination, args, &locals)?;
+        } else {
+            return Err(MirEvalError::TypeError("function pointer to non function"));
+        }
+        Ok(())
+    }
+
+    fn exec_fn_def(
+        &mut self,
+        def: FnDefId,
+        generic_args: &Substitution,
+        destination: &Place,
+        args: &[Operand],
+        locals: &Locals<'_>,
+    ) -> Result<()> {
+        let def: CallableDefId = from_chalk(self.db, def);
+        let generic_args = self.subst_filler(generic_args, &locals);
+        match def {
+            CallableDefId::FunctionId(def) => {
+                let dest_addr = self.place_addr(destination, &locals)?;
+                if let Some(x) = self.detect_fn_trait(def) {
+                    self.exec_fn_trait(x, &args, destination, locals)?;
+                    return Ok(());
+                }
+                let arg_bytes = args
+                    .iter()
+                    .map(|x| Ok(self.eval_operand(x, &locals)?.get(&self)?.to_owned()))
+                    .collect::<Result<Vec<_>>>()?
+                    .into_iter();
+                let function_data = self.db.function_data(def);
+                let is_intrinsic = match &function_data.abi {
+                    Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
+                    None => match def.lookup(self.db.upcast()).container {
+                        hir_def::ItemContainerId::ExternBlockId(block) => {
+                            let id = block.lookup(self.db.upcast()).id;
+                            id.item_tree(self.db.upcast())[id.value].abi.as_deref()
+                                == Some("rust-intrinsic")
+                        }
+                        _ => false,
+                    },
+                };
+                let result = if is_intrinsic {
+                    self.exec_intrinsic(
+                        function_data.name.as_text().unwrap_or_default().as_str(),
+                        arg_bytes,
+                        generic_args,
+                        &locals,
+                    )?
+                } else if let Some(x) = self.detect_lang_function(def) {
+                    self.exec_lang_item(x, arg_bytes)?
+                } else {
+                    let (imp, generic_args) = lookup_impl_method(
+                        self.db,
+                        self.trait_env.clone(),
+                        def,
+                        generic_args.clone(),
+                    );
+                    let generic_args = self.subst_filler(&generic_args, &locals);
+                    let def = imp.into();
+                    let mir_body =
+                        self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?;
+                    self.interpret_mir(&mir_body, arg_bytes, generic_args)
+                        .map_err(|e| MirEvalError::InFunction(imp, Box::new(e)))?
+                };
+                self.write_memory(dest_addr, &result)?;
+            }
+            CallableDefId::StructId(id) => {
+                let (size, variant_layout, tag) =
+                    self.layout_of_variant(id.into(), generic_args.clone(), &locals)?;
+                let result = self.make_by_layout(size, &variant_layout, tag, args, &locals)?;
+                let dest_addr = self.place_addr(destination, &locals)?;
+                self.write_memory(dest_addr, &result)?;
+            }
+            CallableDefId::EnumVariantId(id) => {
+                let (size, variant_layout, tag) =
+                    self.layout_of_variant(id.into(), generic_args.clone(), &locals)?;
+                let result = self.make_by_layout(size, &variant_layout, tag, args, &locals)?;
+                let dest_addr = self.place_addr(destination, &locals)?;
+                self.write_memory(dest_addr, &result)?;
+            }
+        }
+        Ok(())
+    }
+
+    fn exec_fn_trait(
+        &mut self,
+        ft: FnTrait,
+        args: &[Operand],
+        destination: &Place,
+        locals: &Locals<'_>,
+    ) -> Result<()> {
+        let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
+        let ref_func_ty = self.operand_ty(func, locals)?;
+        let func_ty = match ft {
+            FnTrait::FnOnce => ref_func_ty,
+            FnTrait::FnMut | FnTrait::Fn => match ref_func_ty.as_reference() {
+                Some(x) => x.0.clone(),
+                None => return Err(MirEvalError::TypeError("fn trait with non-reference arg")),
+            },
+        };
+        match &func_ty.data(Interner).kind {
+            TyKind::FnDef(def, subst) => {
+                self.exec_fn_def(*def, subst, destination, &args[1..], locals)?;
+            }
+            TyKind::Function(_) => {
+                let mut func_data = self.eval_operand(func, locals)?;
+                if let FnTrait::FnMut | FnTrait::Fn = ft {
+                    let addr = Address::from_bytes(func_data.get(self)?)?;
+                    func_data = Interval { addr, size: self.ptr_size() };
+                }
+                self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
+            }
+            x => not_supported!("Call {ft:?} trait methods with type {x:?}"),
+        }
+        Ok(())
+    }
+
+    fn exec_lang_item(
         &self,
         x: LangItem,
         mut args: std::vec::IntoIter<Vec<u8>>,
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 3b9a31c772a..7a5ca089420 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -298,7 +298,7 @@ impl MirLowerCtx<'_> {
                         );
                         Ok(Some(current))
                     }
-                    ValueNs::StructId(_) => {
+                    ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
                         // It's probably a unit struct or a zero sized function, so no action is needed.
                         Ok(Some(current))
                     }
@@ -445,36 +445,36 @@ impl MirLowerCtx<'_> {
                 })
             },
             Expr::Call { callee, args, .. } => {
+                if let Some((func_id, generic_args)) =
+                    self.infer.method_resolution(expr_id) {
+                    let ty = chalk_ir::TyKind::FnDef(
+                        CallableDefId::FunctionId(func_id).to_chalk(self.db),
+                        generic_args,
+                    )
+                    .intern(Interner);
+                    let func = Operand::from_bytes(vec![], ty);
+                    return self.lower_call_and_args(
+                        func,
+                        iter::once(*callee).chain(args.iter().copied()),
+                        place,
+                        current,
+                        self.is_uninhabited(expr_id),
+                    );
+                }
                 let callee_ty = self.expr_ty_after_adjustments(*callee);
                 match &callee_ty.data(Interner).kind {
                     chalk_ir::TyKind::FnDef(..) => {
                         let func = Operand::from_bytes(vec![], callee_ty.clone());
                         self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
                     }
-                    TyKind::Scalar(_)
-                    | TyKind::Tuple(_, _)
-                    | TyKind::Array(_, _)
-                    | TyKind::Adt(_, _)
-                    | TyKind::Str
-                    | TyKind::Foreign(_)
-                    | TyKind::Slice(_) => {
-                        return Err(MirLowerError::TypeError("function call on data type"))
+                    chalk_ir::TyKind::Function(_) => {
+                        let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
+                            return Ok(None);
+                        };
+                        self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
                     }
                     TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
-                    TyKind::AssociatedType(_, _)
-                    | TyKind::Raw(_, _)
-                    | TyKind::Ref(_, _, _)
-                    | TyKind::OpaqueType(_, _)
-                    | TyKind::Never
-                    | TyKind::Closure(_, _)
-                    | TyKind::Generator(_, _)
-                    | TyKind::GeneratorWitness(_, _)
-                    | TyKind::Placeholder(_)
-                    | TyKind::Dyn(_)
-                    | TyKind::Alias(_)
-                    | TyKind::Function(_)
-                    | TyKind::BoundVar(_)
-                    | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
+                    _ => return Err(MirLowerError::TypeError("function call on bad type")),
                 }
             }
             Expr::MethodCall { receiver, args, .. } => {
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 70364d08823..eb0002266dc 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -301,9 +301,9 @@ impl<'a> MirPrettyCtx<'a> {
                 w!(self, ")");
             }
             Rvalue::Cast(ck, op, ty) => {
-                w!(self, "Discriminant({ck:?}");
+                w!(self, "Cast({ck:?}, ");
                 self.operand(op);
-                w!(self, "{})", ty.display(self.db));
+                w!(self, ", {})", ty.display(self.db));
             }
             Rvalue::CheckedBinaryOp(b, o1, o2) => {
                 self.operand(o1);
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index b524922b6cf..696bdef03fc 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -397,9 +397,39 @@ fn test() {
 }
 
 #[test]
+fn coerce_fn_item_to_fn_ptr_in_array() {
+    check_no_mismatches(
+        r"
+fn foo(x: u32) -> isize { 1 }
+fn bar(x: u32) -> isize { 1 }
+fn test() {
+    let f = [foo, bar];
+          // ^^^ adjustments: Pointer(ReifyFnPointer)
+}",
+    );
+}
+
+#[test]
 fn coerce_fn_items_in_match_arms() {
     cov_mark::check!(coerce_fn_reification);
 
+    check_no_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,
+          // ^^^^ adjustments: Pointer(ReifyFnPointer)
+        2 => foo2,
+          // ^^^^ adjustments: Pointer(ReifyFnPointer)
+        _ => foo3,
+          // ^^^^ adjustments: Pointer(ReifyFnPointer)
+    };
+    x;
+}",
+    );
     check_types(
         r"
 fn foo1(x: u32) -> isize { 1 }
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 3ab85c68f5b..aebf59f3152 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -11,6 +11,7 @@ use hir_def::{
     lang_item::{LangItem, LangItemTarget},
     TraitId,
 };
+use hir_expand::name::{name, Name};
 use stdx::panic_context;
 
 use crate::{
@@ -187,6 +188,14 @@ impl FnTrait {
         }
     }
 
+    pub fn method_name(&self) -> Name {
+        match self {
+            FnTrait::FnOnce => name!(call_once),
+            FnTrait::FnMut => name!(call_mut),
+            FnTrait::Fn => name!(call),
+        }
+    }
+
     pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
         let target = db.lang_item(krate, self.lang_item())?;
         match target {
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 96470265d11..03951ea2bf5 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -632,6 +632,31 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
     }
 
     #[test]
+    fn fn_traits() {
+        check_diagnostics(
+            r#"
+//- minicore: fn
+fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
+        //^^^^^ 💡 weak: variable does not need to be mutable
+    x(2)
+}
+fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
+    x(2)
+  //^ 💡 error: cannot mutate immutable variable `x`
+}
+fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
+               //^^^^^ 💡 weak: variable does not need to be mutable
+    x(2)
+}
+fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
+         //^^^^^ 💡 weak: variable does not need to be mutable
+    x(2)
+}
+"#,
+        );
+    }
+
+    #[test]
     fn respect_allow_unused_mut() {
         // FIXME: respect
         check_diagnostics(