Explain the unpack! macro
This commit is contained in:
parent
a6843376d8
commit
d8c2025b02
|
|
@ -40,20 +40,48 @@ writes the result into the `RETURN_PLACE`.
|
||||||
|
|
||||||
## `unpack!` all the things
|
## `unpack!` all the things
|
||||||
|
|
||||||
One important thing of note is the `unpack!` macro, which accompanies all recursive
|
Functions that generate MIR tend to fall into one of two patterns.
|
||||||
calls. The macro ensures that you get the result of the recursive call while updating
|
First, if the function generates only statements, then it will take a
|
||||||
the basic block that you are now in. As an example: lowering `a + b` will need to do
|
basic block as argument onto which those statements should be appended.
|
||||||
three somewhat independent things:
|
It can then return a result as normal:
|
||||||
|
|
||||||
* give you an `Rvalue` referring to the result of the operation
|
```rust
|
||||||
* insert an assertion ensuring that the operation does not overflow
|
fn generate_some_mir(&mut self, block: BasicBlock) -> ResultType {
|
||||||
* tell you in which basic block you should write further operations into, because
|
...
|
||||||
the basic block has changed due to the inserted assertion (assertions terminate
|
}
|
||||||
blocks and jump either to a panic block or a newly created block, the latter being
|
```
|
||||||
the one you get back).
|
|
||||||
|
|
||||||
The `unpack!` macro will call the recursive function you pass it, return the `Rvalue`
|
But there are other functions that may generate new basic blocks as well.
|
||||||
and update the basic block by mutating the basic block variable you pass to it.
|
For example, lowering an expression like `if foo { 22 } else { 44 }`
|
||||||
|
requires generating a small "diamond-shaped graph".
|
||||||
|
In this case, the functions take a basic block where their code starts
|
||||||
|
and return a (potentially) new basic block where the code generation ends.
|
||||||
|
The `BlockAnd` type is used to represent this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn generate_more_mir(&mut self, block: BasicBlock) -> BlockAnd<ResultType> {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you invoke these functions, it is common to have a local variable `block` that is effectively a "cursor". It represents the point at which we are adding new MIR. When you invoke `generate_more_mir`, you want to update this cursor. You can do this manually, but it's tedious:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut block;
|
||||||
|
let v = match self.generate_more_mir(..) {
|
||||||
|
BlockAnd { block: new_block, value: v } => {
|
||||||
|
block = new_block;
|
||||||
|
v
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
For this reason, we offer a macro that lets you write
|
||||||
|
`let v = unpack!(block = self.generate_more_mir(...))`.
|
||||||
|
It simply extracts the new block and overwrites the
|
||||||
|
variable `block` that you named in the `unpack!`.
|
||||||
|
MIR functions that generate statements always take as argument a
|
||||||
|
basic block onto which the statements should be appended.
|
||||||
|
|
||||||
## Lowering expressions into the desired MIR
|
## Lowering expressions into the desired MIR
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue