The compiler must finish a constant before runtime code begins.
base #Int = 4
larger #Int = base + 2
A constant can use another constant that is already available.
It cannot use a runtime variable or call a runtime function.
When a constant expression is invalid, the compiler reports it during the build instead of leaving the failure for runtime.
Static Bool if specialisation is implemented for ordinary if syntax, including conditions from #Config of Bool values:
enabled #Config of Bool = false
if enabled:
perform_optional_work()
;
A constant initializer must fully evaluate during AST construction.
base #Int = 4
larger #Int = base + 2
label #= [: size [larger]]
Allowed dependency shape
A constant may depend on:
- literals
- earlier same-file constants
- imported constants
- foldable operators
- foldable casts
- foldable struct construction
- foldable templates
- other compile-time values accepted by the current language surface
It may not depend on:
- runtime bindings
- runtime function calls
- host calls
- runtime-only template content
- mutable state
Ordering
Same-file constant evaluation follows source order.
A same-file forward reference is invalid.
Cross-file constant dependencies are represented in the top-level dependency graph and evaluated in dependency order. A direct .mtf or .md file value creates one of these without a top-level clause, because it consumes that file's content constant.
Circular constant dependencies are compilation errors. That includes a cycle formed through content-file values.
Structural values fold without final text
A folded String is not always plain text. A file path, and the bare site root @/, fold to a String that carries a structural anchor rather than characters, because the final URL depends on output placement the frontend has not chosen yet.
logo #= @assets/logo.svg
image #= [: <img src="[logo]">]
Both are complete constants. The compiler knows the whole semantic value, so composition, storage, export and template insertion all fold normally.
What cannot fold is an operation that needs the final characters while an anchor is unresolved, such as comparing one of these strings against a literal URL. That is diagnosed rather than folded against a guessed path. The restriction is about placement being undecided, not about the value being a file: once a builder resolves each anchor, ordinary runtime string behaviour applies.
See File values.
Compile-time and runtime agreement
Folding must preserve runtime language semantics.
Examples:
- a statically known checked-numeric failure is a compile-time diagnostic
String -> Int and Float -> Int enforce the signed 32-bit rangeString -> Float rejects non-finite materializationFloat -> String uses Moth's stable formatter- compile-time and runtime template interpolation use the same Float formatting
The compiler must not accept one result during folding and produce another at runtime.
Advisory const facts
The compiler may discover private const facts for optimisation.
Those facts do not:
- change source semantics
- create dependency-bindable declarations
- participate in header dependency sorting
- turn a runtime binding into a source constant
Static Bool control flow
An ordinary if condition may fold to a known Bool, including an implemented #Config of Bool value. Both branches first parse, resolve and type-check as normal source. Stage 4 then selects one branch, preserving the selected branch's lexical scope; inactive executable work does not reach HIR or later analysis. Runtime conditions remain runtime branches.
This is not target conditional compilation. #Config of Bool uses the same general folded constant and ordinary-if mechanism. Advisory const facts do not create source constants or structural conditional compilation.
Stage 4 performs this selection after final constant values and full branch validation, then derives terminality and executable facts from the selected AST.