Skip to content

Extracted function argument borrows

Sewen Thy edited this page Nov 29, 2022 · 9 revisions

Overview

Deciding how to pass values into the extracted function is non-trivial and requires analysis.

The end result of this algorithm is to decide for some value a: A, whether

  1. a Copy of a is passed into the extracted function;
  2. ownership of a is moved into the extracted function;
  3. an immutable reference of a, &a is passed into the extracted function;
  4. a mutable reference of a, &mut a is passed into the extracted function.

To decide this, the algorithm needs to consider the follwing metadata:

  1. does A implement Copy trait?
    Since Rust does not consider a type implementing the Copy trait a move i.e. a change of ownership, it can be passed into the extracted function directly.

  2. is a live after the extracted block?
    If the variable is not alive, then it is moved into the function--otherwise, it cannot be moved as the value will be dropped after the function call finished.

  3. is a defined with the mut keyword?
    If a is not and it is written to within the extracted block then this is an error and cannot be extracted.

  4. a is written to within the extracted block.

    • a: A where A is &mut B for some type B so a is passed as a: &mut B.

    • a: A where A is not a reference type so a new mutable reference is created &mut a: &mut A and passed from the caller as bar(&mut a).

  5. a is not written to within the extracted block.

    • a: A where A is &B or &mut B for some type B so a is passed as itself.
    • a: A where A is not a reference type so a new immutable reference is created &a: &A and passed from the caller as bar(&a).

Formalization

Algorithm FuncParams
Input: liveness CFG LIVE, reaching definition CFG REACH, type definitions variables in extracted block TV, extracted block statements BLK
Output: variables mapping between variable name and its type into the extracted function, and its passing in text
FuncParams

MakeMutableInput
Input: type definitions variables in extracted block TV, variable name V
Output: variable type and variable passing statment
MakeMutableInput

MakeImmutableInput
Input: type definitions variables in extracted block TV, variable name V
Output: variable type and variable passing statment
MakeImmutableInput

Complications

The pointer or the value?

We need to ensure that the reaching definition considers whether it's changing the reference to something like: z:&mut &i32, where *z = some_i32_ref so in this case, z definition itself is not reaching but what z is referencing perviously still is.

let x = 1;
let y = 1;
let mut z:&mut &i32 = &mut &x;
*z = &y;

Here, x definition on line 1 is still reaching after line 4, while z definition on line 3 is not reaching after line 4.

The implication here is in whether we should borrow the reference itself i.e. z or we should just pass z in.