Lowering turns the checked program into the compiler's intermediate language: the same program, with every construct that's there to make Refingere pleasant to write taken apart into a few simple ones. A match becomes a block of tests, a for loop a loop over an iterator, !! a test and an early return, a format string a list of parts. What comes out is what compile-time code runs from, and what the backends generate code from.
The program's own macros are lowered too: each implement macro becomes a function, with its kind, for compile-time code to run, and each derive or attribute macro put on a definition is recorded - see Macros.
Given every file's tree, links and types. Gives the program in the intermediate language, each file's part on its own. In bootstrapping/5-intermediate; --ir prints it. Every node, and exactly what each construct becomes, is on Intermediate Language - this page is about the step itself.
Lowering comes right after type checking, before macros are expanded. Macros and #comptime(...) code run while compiling, and they run from the intermediate language - so they behave exactly as the same code does once compiled, and there's one set of rules for what each construct does rather than two. A use of one of the program's own macros can't be lowered yet, since what it stands for is only known once it has run; it's kept as a macro-use, and replaced once it has. The rest of the code to run while compiling - each #comptime(...) - is lowered like any other, inside a comptime node that marks it, and replaced by its value the same way.
Each definition is lowered on its own. A function gets a fresh set of numbered locals - its parameters first - and its body is walked statement by statement, each written out as statements of the intermediate language. An expression that needs a statement first - !!, ??, an and whose right side has statements - writes it out before itself, and gives a temporary local holding its value. So the intermediate language's expressions never hide statements, and can always be worked out in one go. An await in an @async function is one of those too: a statement of its own, so the function only ever pauses between statements.
What a block does with context values is worked out here as well. An inject, reject or defer takes the rest of its block as its body, and a reject knows how many of its own block's injections it takes away - none means it's left out altogether. A let, a loop's variable and a pattern's name are marked as new bindings, so a closure made in one round of a loop keeps that round's.
Lowering reads what the earlier steps found, and decides nothing they didn't. Analysis's links say which local or definition each name is - a local is its number, a definition its file and name (pricing/price.rfn#total). Type checking's types say whether Shape.Circle is an enum's case or a field of a value, and are written on each local and literal. A builtin macro's placeholders come from the parser, and the builtin macros' names from core.rfn.
Some choices are left for the running program: which method value.name(...) calls, and what an operator does, depend on the values' types, and the backends choose them while the program runs, as they do today. A call keeps its arguments' names, and the called function binds them.
Each file's structs and enums, its consts, its functions - a statement a line, an expression in brackets - and which methods its functions are. See the Intermediate Language page's Reading it.
What's wrong with macros as they're written: a macro name that isn't a builtin's or the program's, a builtin macro written the wrong way (#format {{{ ... }}}, #SQL"...", a namespace against a string), and one of the program's macros without exactly one kind, or with a builtin macro's name. Everything else has already been checked. A ... between a list's elements, standing for ones left out of the source, is lowered to a panic - it has no value to run.
There's no intermediate language: src/6-compile-wasm and src/6-compile-js each generate code from the syntax tree directly, and each does its own taking apart of the constructs.
Next: Macros.