Repository: rust-lang/rust-analyzer
Pull request #15112 (2023-06-22 18:45:47)

Support manual impl of fn traits in mir interpreter
Increases passed tests from 48 to 49 :)

HKalbasi: @bors r+
bors: :pushpin: Commit b9752680c2c99c683ef82ee8e021965528826c17 has been approved by `HKalbasi`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust-analyzer) for this repository.

<!-- @bors r=HKalbasi b9752680c2c99c683ef82ee8e021965528826c17 -->
<!-- homu: {"type":"Approved","sha":"b9752680c2c99c683ef82ee8e021965528826c17","approver":"HKalbasi","queue":"https://bors.rust-lang.org/homu/queue/rust-analyzer"} -->
bors: :hourglass: Testing commit b9752680c2c99c683ef82ee8e021965528826c17 with merge e3ea4028a1a38346496294f4f0f3a6bb6b04f1a0...
<!-- homu: {"type":"BuildStarted","head_sha":"b9752680c2c99c683ef82ee8e021965528826c17","merge_sha":"e3ea4028a1a38346496294f4f0f3a6bb6b04f1a0"} -->

Patch:
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 28b6066c19..0c7669cd54 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1799,7 +1799,7 @@ impl Evaluator<'_> {
         match def {
             CallableDefId::FunctionId(def) => {
                 if let Some(_) = self.detect_fn_trait(def) {
-                    self.exec_fn_trait(&args, destination, locals, span)?;
+                    self.exec_fn_trait(def, args, generic_args, locals, destination, span)?;
                     return Ok(());
                 }
                 self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?;
@@ -1921,9 +1921,11 @@ impl Evaluator<'_> {
 
     fn exec_fn_trait(
         &mut self,
+        def: FunctionId,
         args: &[IntervalAndTy],
-        destination: Interval,
+        generic_args: Substitution,
         locals: &Locals<'_>,
+        destination: Interval,
         span: MirSpan,
     ) -> Result<()> {
         let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
@@ -1958,7 +1960,38 @@ impl Evaluator<'_> {
                     span,
                 )?;
             }
-            x => not_supported!("Call FnTrait methods with type {x:?}"),
+            _ => {
+                // try to execute the manual impl of `FnTrait` for structs (nightly feature used in std)
+                let arg0 = func;
+                let args = &args[1..];
+                let arg1 = {
+                    let ty = TyKind::Tuple(
+                        args.len(),
+                        Substitution::from_iter(Interner, args.iter().map(|x| x.ty.clone())),
+                    )
+                    .intern(Interner);
+                    let layout = self.layout(&ty)?;
+                    let result = self.make_by_layout(
+                        layout.size.bytes_usize(),
+                        &layout,
+                        None,
+                        args.iter().map(|x| IntervalOrOwned::Borrowed(x.interval)),
+                    )?;
+                    // FIXME: there is some leak here
+                    let size = layout.size.bytes_usize();
+                    let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize);
+                    self.write_memory(addr, &result)?;
+                    IntervalAndTy { interval: Interval { addr, size }, ty }
+                };
+                return self.exec_fn_with_args(
+                    def,
+                    &[arg0.clone(), arg1],
+                    generic_args,
+                    locals,
+                    destination,
+                    span,
+                );
+            }
         }
         Ok(())
     }
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 3b9ef03c36..3ed26d0e9b 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -681,7 +681,23 @@ impl Evaluator<'_> {
                     let addr = tuple.interval.addr.offset(offset);
                     args.push(IntervalAndTy::new(addr, field, self, locals)?);
                 }
-                self.exec_fn_trait(&args, destination, locals, span)
+                if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) {
+                    if let Some(def) = target
+                        .as_trait()
+                        .and_then(|x| self.db.trait_data(x).method_by_name(&name![call_once]))
+                    {
+                        return self.exec_fn_trait(
+                            def,
+                            &args,
+                            // FIXME: wrong for manual impls of `FnOnce`
+                            Substitution::empty(Interner),
+                            locals,
+                            destination,
+                            span,
+                        );
+                    }
+                }
+                not_supported!("FnOnce was not available for executing const_eval_select");
             }
             _ => not_supported!("unknown intrinsic {name}"),
         }


Test patch:
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 1e241500c2..b487d7d049 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1550,6 +1550,30 @@ fn closures() {
     );
 }
 
+#[test]
+fn manual_fn_trait_impl() {
+    check_number(
+        r#"
+//- minicore: fn, copy
+struct S(i32);
+
+impl FnOnce<(i32, i32)> for S {
+    type Output = i32;
+
+    extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 {
+        arg.0 + arg.1 + self.0
+    }
+}
+
+const GOAL: i32 = {
+    let s = S(1);
+    s(2, 3)
+};
+"#,
+        6,
+    );
+}
+
 #[test]
 fn closure_and_impl_fn() {
     check_number(
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index e05d824dba..24a463d714 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -32,6 +32,7 @@ fn transmute() {
 fn const_eval_select() {
     check_number(
         r#"
+        //- minicore: fn
         extern "rust-intrinsic" {
             pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
             where