Returning values
A macro gives a value back to its caller with @return:
macro max(a: int, b: int) -> int {
@if a > b {
@return a
}
@return b
}
macro show(value: int) {
@emit value
}
show max(3, 9)
9
-> int declares the return type. It’s optional, but if it’s there, it’s
enforced. It’s also needed when the macro is passed as an argument (see
Macros as parameters) or used as a
conversion.
The returned value must have the declared type. As with arguments, nothing is converted automatically:
from std.binary import bits
macro byte() -> bits<8> {
@return 5
}
macro emit_byte(b: bits<8>) {
@emit b
}
emit_byte byte()
`byte` returned `int`, but its signature declares `-> bits<8>`
Write @return 5 as bits<8> instead.
-> is only about returning. A macro that declares -> T must return a
T, so declaring a return type on a macro that only emits is an error:
from std.binary import bits
macro nop() -> bits<8> {
@emit 0x90 as bits<8>
}
nop
`nop` returned nothing, but its signature declares `-> bits<8>`
To declare what a macro emits, use the
emits facet
instead: macro nop() | emits bits<8> { ... }.
Returning versus emitting
Every macro has two separate outputs:
@return | @emit | |
|---|---|---|
| Goes to | The caller, as the call’s value | The program’s output |
| How many | One value, or none | Any number |
| Ends the macro | Yes | No |
A macro may do both. A macro that only emits is like an instruction; a macro that only returns is like a function.
Where emitted values can go
A statement call puts the macro’s emitted values in the output where the call appears. A call used as an expression has nowhere to put emitted values unless it’s the whole of one of these:
- a
const’s value:const x = f() @return’s value:@return f()
In both cases, the emitted values are kept, in order, where that statement
appears. The same goes for @fold in expression position.
macro emit_and_return(x: int) -> int {
@emit x
@return x * 10
}
macro caller() {
const y = emit_and_return(1)
@emit y
}
caller
1 10
Anywhere else, such as inside a larger expression, calling a macro that emits is an error, because its values would be lost:
macro emit_and_return(x: int) -> int {
@emit x
@return x * 10
}
macro caller() {
@emit 1 + emit_and_return(1)
}
caller
`emit_and_return` emits values, so it can only be used as a statement
Macros that return nothing
Using a macro that returns nothing as a value is an error. A bare @return
ends a macro early without a value.