We want a phase that eliminates explicit control flow (say a return or a break) by moving or duplicating code.
We want to rewrite the control flow so that every return, break or continue always appears in a return position in our AST. Returns in return positions can be dropped: e.g. if ... then {return 3;} else {return 5;} can be simplified in if ... then {3} else {5}.
See Details below for a lengthy explanation.
Details
Question marks (but also return and friends) used to be transformed in a monadic style.
In this style, computations are promoted to a Result or Option monad, and pure and bind nodes are inserted heavily.
For instance, let's consider the following function:
fn test(x: Option<u8>) -> Option<u8> {
Some(f(x?) + 4)
}
This would be transformed to this monadic F* function, where (let*) is basically a bind node in the option monad:
let test (x: option u8) : option u8 =
run_monad (
let* hoist1:u8 = x in
Some
(let hoist2:u8 = f hoist1 in
let hoist3:u8 = hoist2 + 4 in
Some hoist3 <: option u8)
<:
option (option u8))
While this is a correct translation, lifting everything to a monad and running the monad is a bit heavy.
Instead, the control flow of the Rust function test can be rewritten into:
match x {
Some(x) => Some(f(x) + 4),
None => None
}
Rewriting control flow is about pushing ?s to return positions. When ? appears deep in a if/match node itself nested in a sequential expression (e.g. a sequence or a function call), then such rewriting can result in code duplication. Example:
fn fun(cond: bool, x: Option<u8>) -> Option<u8> {
let b = x + 10;
let a = if b > 20 { x? + long_code } else { 0 };
let b = other_long_code;
Some(a + b + 3)
}
fn fun_rewrite_step(x: Option<u8>) -> Option<u8> {
let b = x + 10;
if b > 20 {
let a = x? + long_code;
let b = other_long_code;
Some(a + b + 3)
} else {
let b = other_long_code;
Some(a + b + 3)
};
}
fn fun_rewrited(x: Option<u8>) -> Option<u8> {
let b = x + 10;
if b > 20 {
match x {
Some(x) => {
let a = x? + long_code;
let b = other_long_code;
Some(a + b + 3)
}
None => None,
}
} else {
let b = other_long_code;
Some(a + b + 3)
}
}
We want a phase that eliminates explicit control flow (say a
returnor abreak) by moving or duplicating code.We want to rewrite the control flow so that every
return,breakorcontinuealways appears in a return position in our AST. Returns in return positions can be dropped: e.g.if ... then {return 3;} else {return 5;}can be simplified inif ... then {3} else {5}.returns appearing outside of a loop (this was PR Add RewriteControlFlow phase. #907);return,breakandcontinuewithin a loop.See Details below for a lengthy explanation.
Details
Question marks (but also
returnand friends) used to be transformed in a monadic style.In this style, computations are promoted to a
ResultorOptionmonad, andpureandbindnodes are inserted heavily.For instance, let's consider the following function:
This would be transformed to this monadic F* function, where
(let*)is basically abindnode in the option monad:While this is a correct translation, lifting everything to a monad and running the monad is a bit heavy.
Instead, the control flow of the Rust function
testcan be rewritten into:Rewriting control flow is about pushing
?s to return positions. When?appears deep in aif/matchnode itself nested in a sequential expression (e.g. a sequence or a function call), then such rewriting can result in code duplication. Example: