Validity Conditions
We associate borrow PCG edges with validity conditions to identify the control-flow conditions under which they are valid. Consider the following program:
fn main(){ // BB0 let mut x = 1; let mut y = 2; let mut z = 3; let mut r = &mut x; if rand() { // BB1 r = &mut y; } // BB2 if rand2() { // BB3 r = &mut z; } // BB4 *r = 5; }
We represent control flow using a primitive called a branch choice
of the form . A branch choice represents the flow of
control from one basic block to another. In the above code there are two
possible branch choices from bb0
: and
and one branch choice from bb1
(to
bb2
).
For any given basic block in the program, an execution path leading to
is an ordered list of branch choices. For
example, one path of execution to bb4
can be described by: .
Validity conditions conceptually define a predicate on execution paths.
For example, the validity conditions describing the
control flow where r
borrows from z
at bb3
require that the path contain
.
We represent validity conditions as a partial function mapping relevant blocks to sets of allowed target blocks.
An execution path is valid for validity conditions iff, for each , either:
- is not a relevant block in , or
- is in the set of allowed target blocks of in
TODO: Explain how validity conditions are initially determined when creating a new edge at a basic block (inherent VCs). Describe the join informally.
Formal Definition
Validity conditions is a map describing control-flow conditions. For each block , is a strict subset of the real successors of .
The join of validity conditions and is defined as:
Validity conditions are valid for a path iff:
The intuition behind the first case of the join is that if whether a borrow is live does not depend on what successor is taken from some basic block , then it is not necessary to store a predicate about .
This is kind of an optimization, actually removing this case for the join would not have any difference on whether the resulting validity conditions are valid for any block. In the implementation this is useful so we can have a canonical representation (and also reduces the size of the data structure).