Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix try/catch tests, JSON.parse on macOS, and require native compiler in test suite
Summary
TryStatementAST node back to direct fields (catchParam,catchBody) instead of nestedcatchClause: { param, body }— fixes a self-hosting bug where the native compiler couldn't handle chained inline struct field access (tryStmt.catchClause.body), causing catch block bodies to be silently dropped from generated IRSet<string>/ string+=accumulation injson.tsJSON.parse codegen — fixes a self-hosting crash on macOS ARM64 where the native compiler segfaulted when compilingJSON.parse<T>()callsnpm testbuild.build/chadcautomatically if missing, and fail loudly instead of silently falling back to the Node.js interpreterProblem
Try/catch self-hosting bug: Commit 5271d53 changed
TryStatementfrom flat fields to a nested struct, relying on inline struct field access working in the native compiler. The two-level chained accesstryStmt.catchClause.bodysilently produced no code in the native binary, so catch blocks were completely empty in the generated LLVM IR.JSON.parse crash on macOS:
json.tsused multiple patterns that the native compiler can't handle reliably on ARM64:JsonInterfaceDefcontaining{ name: string; type: string }[](array-of-objects) — undefined behavior on ARM64 due to strict alignmentSet<string>for dedup tracking —Setinternals may have alignment/hashing issues in the native compiler on ARM64let parserIR = ...; parserIR += ...; parserIR += ...;(30+ string+=accumulations) — creates many intermediate heap-allocated C strings; the only codegen path that built strings this way instead of usingctx.emit()line-by-linename.endsWith("?")/name.slice()— higher-level string methods that may not be fully reliable in the native compilerSilent test fallback:
compiler.test.tsfell back tonode dist/chadc-node.jsif.build/chadcwas missing. CI never had the native binary, so it always tested with the Node.js interpreter, masking both bugs above.Inconsistent CI setup: Linux glibc CI ran tests before building the native compiler or tree-sitter objects, so it never tested with the native compiler even after fixing the fallback. Only macOS and musl had the native compiler available during tests.
Changes
src/ast/types.ts—TryStatementusescatchParam: string | null+catchBody: BlockStatement | nullinstead ofcatchClause: { param, body } | nullsrc/codegen/statements/control-flow.ts— access flat fields in codegensrc/parser-native/transformer.ts— produce flat fields from tree-sitter parsesrc/parser-ts/handlers/statements.ts— produce flat fields from TS API parsesrc/analysis/semantic-analyzer.ts— access flat fieldssrc/ast/visitor.ts— access flat fieldssrc/codegen/infrastructure/closure-analyzer.ts— access flat fieldssrc/codegen/stdlib/json.ts— removeJsonInterfaceDefandgetInterfaceFields(), use delegate methods directly; replaceSet<string>withstring[]+ manual lookup; replace string+=accumulation withstring[]lines pushed individually viapushGlobalString; replaceendsWith/slicewithcharAt/substringscripts/test.js— build native compiler before running tests; re-run compiler tests with Node.js compiler as second passtests/compiler.test.ts— configurable viaCHADC_COMPILERenv var, defaults to.build/chadc.github/workflows/ci.yml— all 3 platforms now build tree-sitter objects + native compiler + smoke test before running tests; macOS signs compiler before testsTest plan
npm test— 299 native + 161 node passingnpm run verify:quick— self-hosting passes.build/chadcnow throws a clear error instead of silent fallbacknpm testFollow-up: root causes and prevention
These are underlying issues in the native compiler that caused the regressions. Fixing them would prevent future self-hosting bugs from slipping in.
Root cause: string
+=is unsafe on ARM64+=by allocating a new C string, copying both halves, and discarding the old one. On ARM64, this crashes after many iterations — likely an alignment or allocator bug in howGC_malloc_atomicinteracts withstrlen/memcpyfor large accumulated strings.+=in a loop, verify it passes on both Linux and macOS native compilers. If it crashes, the bug is in the string concatenation codegen itself (src/codegen/types/collections/string/). Fix the underlying IR generation for string concat.Root cause:
Setmay not work correctly on ARM64Set<string>uses a hash table internally. If the hash function or bucket storage has alignment issues in the compiled output,.has()/.add()could return wrong values or crash.Set<string>, adds 10+ entries, and checks.has()/.size. Run with native compiler on macOS to verify.Root cause: array-of-objects field access (
arr[i].name) is broken in stage 0.claude/rules.mdbut there's no automated enforcement.src/codegen/for patterns like[i].fieldNameand warns. Alternatively, fix the underlying GEP codegen for array-of-struct element access soarr[i].fieldworks correctly.Root cause: chained inline struct field access (
a.b.c) silently drops codea.bfine buta.b.c(two levels of inline struct GEP) silently produces no code.loadFieldValueinsrc/codegen/expressions/member.tsto handle chained struct access. Add a test fixture with 2-level and 3-level chained struct access.Root cause: tests didn't catch self-hosting bugs
npm run verify) on all platforms, not just macOS/musl. Currently onlybuild-linux-muslandbuild-macosrunself-hosting.test.ts.