Two function declarations nested inside another function that call each other abort at runtime. The build succeeds with no diagnostic, and the binary produces no output and no error message — just exit 133 (SIGTRAP).
The same pair of functions at module top level works correctly, so the trigger is the nesting.
Repro
export {}
function outer(n: number): number {
function even(k: number): number {
if (k === 0) return 1
return odd(k - 1)
}
function odd(k: number): number {
if (k === 0) return 0
return even(k - 1)
}
return even(n)
}
function main() {
console.log(`r=${outer(4)}`)
}
main()
$ bun recA.ts
r=1
$ scriptc build recA.ts -o recA # no diagnostic
$ ./recA
$ echo $?
133
Nothing is printed on stdout or stderr.
Narrowing
| shape |
result |
| mutual recursion between nested declarations |
abort, exit 133 |
| the same mutual recursion at top level |
works (r=1) |
nested, plain forward reference (first() calls second(), no cycle) |
works |
| nested, backward reference only |
works |
So it is specifically the cycle among nested declarations, not a forward reference on its own:
// works
function outer(): number {
function first(): number { return second() + 1 }
function second(): number { return 41 }
return first()
}
Where I hit it
A recursive-descent parser written the conventional way — primary/comparison/and/or nested inside parse(), with primary() calling or() for a parenthesised sub-expression. Compiled clean, aborted on every input. Restructuring to top-level functions over an explicit cursor object fixed it.
Impact
Silent, message-free abort is the worst failure mode: there is nothing to search for and nothing to point at. Even if the fix takes a while, a compile-time refusal naming the cycle would make this tractable.
Environment
- scriptc 0.0.17,
@scriptc/compiler 0.0.17
- macOS 26.5.2, arm64, Node v24.18.0
- Static build (no
--dynamic), default backend
Two function declarations nested inside another function that call each other abort at runtime. The build succeeds with no diagnostic, and the binary produces no output and no error message — just
exit 133(SIGTRAP).The same pair of functions at module top level works correctly, so the trigger is the nesting.
Repro
Nothing is printed on stdout or stderr.
Narrowing
r=1)first()callssecond(), no cycle)So it is specifically the cycle among nested declarations, not a forward reference on its own:
Where I hit it
A recursive-descent parser written the conventional way —
primary/comparison/and/ornested insideparse(), withprimary()callingor()for a parenthesised sub-expression. Compiled clean, aborted on every input. Restructuring to top-level functions over an explicit cursor object fixed it.Impact
Silent, message-free abort is the worst failure mode: there is nothing to search for and nothing to point at. Even if the fix takes a while, a compile-time refusal naming the cycle would make this tractable.
Environment
@scriptc/compiler0.0.17--dynamic), default backend