Reproducer
type Point
x i32
fn make_point Point
let p Point . x 1
ret p
pub fn main Void world World !
let p make_point()
if == p.x 1
check world.out.write "ok\n"
bin/zero check passes silently. bin/zero build --target darwin-arm64 --out /tmp/p:
BLD004: direct backend call return type is unsupported
expected: direct AArch64 Mach-O object subset
actual: Point
help: choose a supported direct target or reduce the program to supported AArch64 direct-backend constructs
explain: zero explain BLD004
Same failure across all 8 targets listed in bin/zero --version --json:
darwin-arm64, darwin-x64
linux-musl-x64, linux-musl-arm64, linux-x64, linux-arm64
win32-x64.exe, win32-arm64.exe
Why it matters
Source-backed stdlib (and user code) can't return any user-defined type from a function and have it lower to the direct backends. Built-in record types — Maybe<String>, Maybe<Span<u8>>, etc. — work fine via specialized backend lowering. User-defined records don't have an equivalent path.
This blocks:
- Source-backed stdlib helpers that want to return typed handles (e.g., a hypothetical
Iterator<T>, Decoder, or fallibility wrapper)
- User code that wants to return small value types from functions
- The
assertDirectRuntimeOrUnsupported conformance path silently swallows this — so existing fixtures touching user records don't actually exercise runtime behavior; they only typecheck
I hit this while prototyping std.decimal (PR #308, now closed). The workaround there used a per-module DecimalResult { has Bool, value i64 } shape which type-checked cleanly but couldn't run on any backend.
Related but distinct gap
mutref<UserRecord> parameters hit a sibling restriction:
BLD004: direct backend call parameter type is unsupported
expected: direct AArch64 Mach-O object subset
actual: mutref<Point>
Listed here for completeness; may or may not share the fix.
Possible fix paths
- Generalize the existing record-lowering infrastructure used for
Maybe<X> and Span<X> to handle user-defined type declarations.
- Restrict to plain-by-value records under a size budget (e.g., ≤ 16 bytes / 2 registers) — covers the common cases without ABI complexity.
Both are non-trivial; this is the kind of language work that's clearly maintainer-scope, not contributor-scope.
Compiler version
v0.1.4 (bin/zero --version --json).
Reproducer
bin/zero checkpasses silently.bin/zero build --target darwin-arm64 --out /tmp/p:Same failure across all 8 targets listed in
bin/zero --version --json:darwin-arm64,darwin-x64linux-musl-x64,linux-musl-arm64,linux-x64,linux-arm64win32-x64.exe,win32-arm64.exeWhy it matters
Source-backed stdlib (and user code) can't return any user-defined
typefrom a function and have it lower to the direct backends. Built-in record types —Maybe<String>,Maybe<Span<u8>>, etc. — work fine via specialized backend lowering. User-defined records don't have an equivalent path.This blocks:
Iterator<T>,Decoder, or fallibility wrapper)assertDirectRuntimeOrUnsupportedconformance path silently swallows this — so existing fixtures touching user records don't actually exercise runtime behavior; they only typecheckI hit this while prototyping
std.decimal(PR #308, now closed). The workaround there used a per-moduleDecimalResult { has Bool, value i64 }shape which type-checked cleanly but couldn't run on any backend.Related but distinct gap
mutref<UserRecord>parameters hit a sibling restriction:Listed here for completeness; may or may not share the fix.
Possible fix paths
Maybe<X>andSpan<X>to handle user-definedtypedeclarations.Both are non-trivial; this is the kind of language work that's clearly maintainer-scope, not contributor-scope.
Compiler version
v0.1.4 (
bin/zero --version --json).