Skip to content

Diagnostics

Andrew Lind edited this page Jul 2, 2026 · 5 revisions

Hatchet fails loudly rather than guessing. When it cannot resolve a type — a typo, a missing import, or a type declared outside the --src scope — it reports an error and does not generate that module, instead of silently emitting wrong C++ (e.g. a class rendered by value instead of as a pointer). Errors are collected across all files and reported together; modules that transpiled cleanly are still written, and the run exits non-zero:

error: Widget.hx:14: unresolved type `Button` in parameter `b` of `new` — is it declared and within the --src scope?

Generated 6 file(s); 1 module(s) skipped due to errors.
hatchet: 1 error(s); 1 module(s) were not generated

Unsupported idioms

The same discipline applies to unsupported Haxe idioms: valid Haxe that Hatchet does not yet transpile fails with an invitation to contribute upstream (the repository URL is in src/diag.rs). This distinguishes "your input is wrong" (fix the Haxe) from "Hatchet doesn't do this yet" (raise a PR).

Currently flagged as unsupported:

  • a lambda used outside a top-level final binding or an Array.map(...) argument;
  • Haxe macros (a macro function, or the macro AST type Expr);
  • regular expressions (both the ~/pattern/flags literal and the EReg type);
  • using static extensions;
  • function types as values (var cb:Int->Int in a field, parameter, return, or typedef — first-class function values have no C++98 lowering; the one lowered position is a top-level final lambda binding, which becomes a free function);
  • rest parameters (...vals:Int and the haxe.Rest type, Haxe 4.2 varargs);
  • recursive enum payloads (Node(child:Tree) — a by-value tagged class cannot contain itself);
  • the is runtime type-check operator (Haxe 4.2);
  • generics (a type-parameterized class Box<T>, interface I<T>, enum Tree<T>, generic method first<T>(…), or typedef Pair<T> — type parameters have no C++98 template lowering, so each is flagged rather than emitted with T as a bare unknown type);
  • non-constant switch patterns (a capture case x:, a literal or nested payload sub-pattern in case Add(0, b):, or a destructuring pattern combined with other alternatives in one case);
  • (get, default) properties and dynamic access (every other accessor pair is lowered — see Members & Access — and a stray get_x/set_x whose field does not declare the matching access kind is flagged rather than silently dropped);
  • generic / @:multiType abstracts (a plain abstract X(T) newtype is supported — see Value Types & Abstracts — but a type-parameterized or multitype one is not);
  • an anonymous struct as a container element (Array<{x:Int}>) — it would lower to a useless std::vector<void*>, so it is flagged; give the struct a typedef and use that named type as the element;
  • and @:op forms with no C++98 operator (the two-arg @:op([]) write, @:op(a.b), @:op(a()), postfix).

These are parsed but not transpiled, so they are reported with a clean diagnostic rather than a parse error. Relatedly, iteration that cannot be lowered fails loudly: a for loop (or comprehension) over a value that is not a range, Array, Map, or a type implementing the Iterator/Iterable protocol on itself — including a protocol reached only through a typedef alias or inherited from a base class (which Hatchet does not consult), and key => value over a value-only custom iterator — is a hard error rather than a guess, each with a message naming the specific cause.

Likewise, comparing a value String to null is a hard error (a value std::string is never null): test != "" for emptiness, or use Null<String> for a nullable string (see Types & Nullability). An optional ?s:String parameter is exempt — it defaults to "", so its null check legitimately lowers to s.empty().

Assigning through a value-struct parameter is a hard error: a value-struct parameter lowers to const T&, so a write through it is reported up front rather than emitted as non-compiling C++.

Warnings

Non-fatal warnings flag a lowering that still compiles but is deprecated:

  • the empty structure {} used as a void* type is deprecated — use Dynamic (opaque value) or cpp.RawPointer<cpp.Void> (opaque pointer). See Raw-Pointer Interop.

Clone this wiki locally