From cd23e139138e42c130e5dd8e4c565392776d0272 Mon Sep 17 00:00:00 2001 From: cs01 Date: Mon, 13 Apr 2026 22:22:14 -0700 Subject: [PATCH] fix: native compiler segfault on closure-mutation error inside functions --- package-lock.json | 4 ++-- src/semantic/closure-mutation-checker.ts | 6 +++++- .../closures/closure-mutation-inside-arrow-body.ts | 10 ++++++++++ .../closures/closure-mutation-inside-function.ts | 9 +++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/closures/closure-mutation-inside-arrow-body.ts create mode 100644 tests/fixtures/closures/closure-mutation-inside-function.ts diff --git a/package-lock.json b/package-lock.json index 6ec5684e..003b1558 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chadscript", - "version": "0.2.0-beta", + "version": "0.3.0-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chadscript", - "version": "0.2.0-beta", + "version": "0.3.0-beta", "license": "MIT", "dependencies": { "express": "^5.2.1", diff --git a/src/semantic/closure-mutation-checker.ts b/src/semantic/closure-mutation-checker.ts index aab83463..6657800c 100644 --- a/src/semantic/closure-mutation-checker.ts +++ b/src/semantic/closure-mutation-checker.ts @@ -97,7 +97,11 @@ class ClosureMutationChecker { // Simple-name reassignment after capture is the error we're looking for. // Member-access assignments (obj.x = y) don't reassign the binding itself. if (capturedNames.indexOf(assign.name) !== -1) { - this.reportError(assign.name, assign.loc); + // NOTE: `assign.loc` is intentionally not passed here. No AssignmentStatement + // creation site in parser-ts/parser-native sets `loc`, so reading `assign.loc` + // in the native self-hosted compiler GEPs past the struct and segfaults. + // See the PR that introduced this comment for the full root cause. + this.reportError(assign.name, undefined); } this.scanExprForCaptures(assign.value, scopeVarNames, capturedNames); } else if (stype === "if") { diff --git a/tests/fixtures/closures/closure-mutation-inside-arrow-body.ts b/tests/fixtures/closures/closure-mutation-inside-arrow-body.ts new file mode 100644 index 00000000..1a67b6a7 --- /dev/null +++ b/tests/fixtures/closures/closure-mutation-inside-arrow-body.ts @@ -0,0 +1,10 @@ +// @test-compile-error: variable 'x' is captured by a closure but reassigned after capture +// @test-description: closure mutation detected when assignment is inside the arrow body itself +function outer() { + let x = 0; + const g = () => { + x = 1; + }; + g(); +} +outer(); diff --git a/tests/fixtures/closures/closure-mutation-inside-function.ts b/tests/fixtures/closures/closure-mutation-inside-function.ts new file mode 100644 index 00000000..411915b0 --- /dev/null +++ b/tests/fixtures/closures/closure-mutation-inside-function.ts @@ -0,0 +1,9 @@ +// @test-compile-error: variable 'x' is captured by a closure but reassigned after capture +// @test-description: closure mutation detected inside a function body (not just top-level) +function outer() { + let x = 0; + const g = () => x; + x = 1; + g(); +} +outer();