Why does Rust store i64s captured by a closure as i64*s in the LLVM IR closure environment?

rust

Solution

Closures capture by reference by default. You can change that behavior to capture by value by adding the `move` keyword before the parameter list:

let f = move |x: i64| x + y + z;

This generates much leaner code:

define internal fastcc i64 @apply(i64 %.0.0.val, i64 %.0.1.val) unnamed_addr #0 personality i32 (i32, i32, i64, %"8.unwind::libunwind::_Unwind_Exception"*, %"8.unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality {
entry-block:
  %0 = add i64 %.0.0.val, 42
  %1 = add i64 %0, %.0.1.val
  ret i64 %1
}

Adding the `move` keyword means that any value that the closure uses will be moved into the closure's environment. In the case of integers, which are `Copy`, it doesn't make much difference, but in the case of other types like `String`, it means that you can't use the `String` anymore in the outer scope after creating the closure. It's an all-or-nothing deal, but you can manually take references to individual variables outside a `move` closure and have the closure use these references instead of the original values to get manual capture-by-reference behavior.

Can you observe the value vs ref difference somehow in this code?

If you take the address of the captured variable, you can observe the difference. Notice how the first and second output lines are the same, and the third is different.

Problem

In this simple example ``` #[inline(never)] fn apply<F, A, B>(f: F, x: A) -> B where F: FnOnce(A) -> B { f(x) } fn main() { let y: i64 = 1; let z: i64 = 2; let f = |x: i64| x + y + z; print!("{}", apply(f, 42)); } ``` the closure passed to `apply` is passed as a LLVM IR `{i64*, i64*}*`: ``` %closure = type { i64*, i64* } define internal fastcc i64 @apply(%closure* noalias nocapture readonly dereferenceable(16)) unnamed_addr #0 personality i32 (i32, i32, i64, %"8.unwind::libunwind::_Unwind_Exception"*, %"8.unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality { entry-block: %1 = getelementptr inbounds %closure, %closure* %0, i64 0, i32 1 %2 = getelementptr inbounds %closure, %closure* %0, i64 0, i32 0 %3 = load i64*, i64** %2, align 8 %4 = load i64*, i64** %1, align 8 %.idx.val.val.i = load i64, i64* %3, align 8, !noalias !1 %.idx1.val.val.i = load i64, i64* %4, align 8, !noalias !1 %5 = add i64 %.idx.val.val.i, 42 %6 = add i64 %5, %.idx1.val.val.i ret i64 %6 } ``` (`apply` actually has a more complicated name in the generated LLVM code.) This causes two loads to get to each of the captured variables. Why isn't `%closure` just `{i64, i64}` (which would make the argument to `apply` `{i64, i64}*`)?

Original source