Conversation
… union-type codegen issues
…erty/MapEntry types for correct native GEP offsets
…ion type alias unions
… @csyyjson_arr_get
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.
Semantic analysis passes and native codegen hardening
Summary
Adds pre-codegen semantic analysis passes that catch errors which would produce silently wrong native code, plus fixes latent codegen bugs that caused memory corruption and LLVM UB-driven code pruning.
Changes
New: Semantic analysis passes (
src/semantic/)Closure mutation checker (
closure-mutation-checker.ts) — ChadScript closures capture by value. Mutating a captured variable after the closure is created produces silently wrong results at runtime (the closure sees the old value). This pass walks the AST, tracks which variables are captured by arrow functions, and emits a compile error if any are reassigned after capture.Union type checker (
union-type-checker.ts) — The existing inline union check catchesparam: string | numberbut misses type alias unions liketype Mixed = string | number. When a type alias union has members with different LLVM representations (e.g.,i8*vsdouble), codegen emits the alias name literally as the LLVM param type, defaulting toi8*— causing a segfault if the caller passes adouble. This pass resolves aliases and rejects mixed-representation unions at compile time.Both passes are called from
LLVMGenerator.generateParts()before IR generation begins.Fix: Native self-hosting crashes
scanExprForCaptures— Object and Map expression cases used inlineas { key, value }type assertions that didn't match the real AST struct layout. Changed to use the namedObjectPropertyandMapEntrytypes fromast/types.tsfor correct native GEP offsets.Capture/destructuredNames casts — Added explicit casts to avoid union-type codegen issues in the native compiler where the type is used as a parameter.
Fix: Missing
setUsesJsonin JSON array index codegenIndexAccessGeneratoremittedcall @csyyjson_arr_getfor JSON array indexing without callingctx.setUsesJson(true). This meant thedeclarefor the yyjson C bridge functions was never emitted, causing an LLVM "undefined value" error.Consolidated
setUsesJsoncalls to fix a Stage 1 self-hosting crash where the flag was set too late.Fix:
LLVMGenerator.reset()missing 4 field resetsThe
reset()override was missing 4 fields thatBaseGenerator.reset()resets:allocaCounter— alloca naming drifts across function compilationsallocaInstructions— safety reset (normally cleared after hoisting)actualClassTypes— stale class-to-interface mappings can cause wrong GEP offsets (UB source)currentDebugLocId— stale debug metadataFix: Expression orchestrator null pointer generation → hard error
The orchestrator (
orchestrator.ts) silently generatedinttoptr i64 0 to i8*(null) for expression types it didn't recognize. When these nulls flowed into global variable initialization, LLVM-O2detected the null dereference as undefined behavior and was free to prune entire code paths — cascading to delete initialization of unrelated globals likeBUILTIN_TYPES(crash:mov 0x8,%eaxloading StringArray.length from null+8).Both fallback paths (empty type, unsupported type) now call
ctx.emitError()which returnsnever— stopping compilation immediately before any broken IR is emitted.Docs: rules.md updates
src/semantic/to Key Directories tableFiles changed
src/semantic/closure-mutation-checker.tssrc/semantic/union-type-checker.tssrc/codegen/llvm-generator.tsreset()(4 missing fields)src/codegen/expressions/orchestrator.tsemitErrorcallssrc/codegen/expressions/access/index.tssetUsesJsonfor JSON array index.claude/rules.mdtests/fixtures/closures/closure-capture-by-value-ok.tstests/fixtures/closures/closure-capture-mutation-error.tstests/fixtures/types/union-non-nullable-error.tstests/fixtures/types/union-type-alias-error.tsTest plan
npm run build— TypeScript compilation passesnpm test— 248/248 tests pass (includes 4 new fixture tests)bash scripts/self-hosting.sh— full 3-stage self-hosting passes (Stage 0 → Stage 1 → Stage 2)