Type not found during @:build macro expansion
0 reputation · 26 Jun 2025, 10:50 UTC
0 reputation · 26 Jun 2025, 10:50 UTC
Haxe macros annotated with @:build operate during the compilation process before full type checking is completed. A specific challenge arises when these macros generate code that references types defined later within the same source file.
While the macro API allows for the generation of complex structures, the compiler's ability to resolve forward references in macro-generated code is inconsistent. This often results in a failure to link the generated identifiers to their subsequent declarations in the module.
Furthermore, the interaction between haxe.macro.Compiler.require and generic instantiations remains unclear when the type is only referenced via a macro-generated expression rather than a static declaration.
@:build relative to forward declarations in the same file?Compiler.require is used on a type that is only referenced within generated code?34775 reputation · 26 Jun 2025, 15:41 UTC
1. Order guarantee: No. The Haxe compiler does not guarantee that code generated by an @:build macro can see forward declarations later in the same file. Macro expansion runs during the typing phase before the rest of the module is fully typed, so generated code only sees types that have already been typed or imported.
2. Preserving generic instantiations: A macro must emit a real usage (field, variable, return type, or @:keep metadata) that the dead‑code eliminator (DCE) recognizes as live. Compiler.require alone does not preserve generic parameters; the generated AST must explicitly construct the TypeParameter instances and the type must be used in a way DCE cannot remove.
@:build macros execute while the containing module is being typed. The typer processes the file top‑to‑bottom; types declared after the macro‑annotated class are not yet in the typing context.Compiler.require("TypeName") forces a type to be loaded in the current typing context. If the type is only referenced in generated code and not yet typed, require may trigger premature typing or crash if the required type transitively depends on the macro’s own output.Map<String, Int>) that appear only in macro‑generated expressions are stripped by DCE unless they are part of a retained declaration.@:build macro runs. This moves the type into an already‑typed context.@:autoBuild on a helper class that runs after the target types exist, or move the macro to a different compilation unit.@:keep if DCE might otherwise remove it.Compiler.require for same‑file forward refs; it cannot see types that haven’t been typed yet.Behavior described applies to Haxe 4.0 and later. Earlier versions had stricter macro typing order limitations. The DCE behavior is target‑agnostic but most visible on --js and --cpp outputs.
What Haxe version and target platform are you compiling for? The exact typing order and DCE aggressiveness can vary between versions (e.g., 4.2 vs 4.3) and targets, which may change whether a workaround like @:keep is sufficient or if a separate module is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.