Skip to content

Commit 03f4b7e

Browse files
committed
Fix closure environment slot calculation and test coverage
Corrects the calculation of environment slot offsets for captured variables in closures, ensuring proper byte offset handling and consistent environment setup. Updates test WAT files to reflect the new closure environment layout and stack management, improving correctness and coverage for closure, function expression, return, ternary, and typealias scenarios.
1 parent 46c0026 commit 03f4b7e

11 files changed

+659
-169
lines changed

src/compiler.ts

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,40 @@ export class Compiler extends DiagnosticEmitter {
17791779
// This ensures we know which locals need to be stored in the environment
17801780
this.prescanForClosures(bodyNode, instance, flow);
17811781

1782+
// Mark captured parameters and set up environment if needed
1783+
let preCapturedNames = instance.preCapturedNames;
1784+
if (preCapturedNames && preCapturedNames.size > 0) {
1785+
// Check if any parameters are captured
1786+
let parameterTypes = instance.signature.parameterTypes;
1787+
for (let i = 0, k = parameterTypes.length; i < k; i++) {
1788+
let paramName = instance.getParameterName(i);
1789+
if (preCapturedNames.has(paramName)) {
1790+
let local = flow.lookupLocal(paramName);
1791+
if (local && !local.isCaptured) {
1792+
local.isCaptured = true;
1793+
// Ensure environment structures are set up
1794+
if (!instance.capturedLocals) {
1795+
instance.capturedLocals = new Map();
1796+
}
1797+
if (!instance.capturedLocals.has(local)) {
1798+
// Calculate proper byte offset
1799+
let currentOffset = 0;
1800+
for (let _keys = Map_keys(instance.capturedLocals), j = 0, m = _keys.length; j < m; ++j) {
1801+
let existingLocal = _keys[j];
1802+
currentOffset += existingLocal.type.byteSize;
1803+
}
1804+
local.envSlotIndex = currentOffset;
1805+
instance.capturedLocals.set(local, local.envSlotIndex);
1806+
}
1807+
if (!instance.envLocal) {
1808+
let envLocal = flow.addScopedLocal("$env", this.options.usizeType);
1809+
instance.envLocal = envLocal;
1810+
}
1811+
}
1812+
}
1813+
}
1814+
}
1815+
17821816
// compile statements
17831817
if (bodyNode.kind == NodeKind.Block) {
17841818
stmts = this.compileStatements((<BlockStatement>bodyNode).statements, stmts);
@@ -3239,12 +3273,12 @@ export class Compiler extends DiagnosticEmitter {
32393273
if (!sourceFunc.capturedLocals.has(local)) {
32403274
// Calculate proper byte offset based on current environment size
32413275
let currentOffset = 0;
3242-
for (let _values = Map_values(sourceFunc.capturedLocals), i = 0, k = _values.length; i < k; ++i) {
3243-
let existingLocal = _values[i];
3276+
for (let _keys = Map_keys(sourceFunc.capturedLocals), i = 0, k = _keys.length; i < k; ++i) {
3277+
let existingLocal = _keys[i];
32443278
currentOffset += existingLocal.type.byteSize;
32453279
}
32463280
local.envSlotIndex = currentOffset;
3247-
sourceFunc.capturedLocals.set(local, local);
3281+
sourceFunc.capturedLocals.set(local, local.envSlotIndex);
32483282
}
32493283
// Ensure we have an environment local
32503284
if (!sourceFunc.envLocal) {
@@ -7507,7 +7541,13 @@ export class Compiler extends DiagnosticEmitter {
75077541
}
75087542
if (local && !captures.has(local)) {
75097543
local.isCaptured = true;
7510-
local.envSlotIndex = captures.size;
7544+
// Calculate proper byte offset based on existing captures
7545+
let currentOffset = 0;
7546+
for (let _keys = Map_keys(captures), idx = 0, cnt = _keys.length; idx < cnt; ++idx) {
7547+
let existingLocal = _keys[idx];
7548+
currentOffset += existingLocal.type.byteSize;
7549+
}
7550+
local.envSlotIndex = currentOffset;
75117551
captures.set(local, local.envSlotIndex);
75127552
}
75137553
break;
@@ -7810,7 +7850,10 @@ export class Compiler extends DiagnosticEmitter {
78107850
this.collectDeclaredVariables(forStmt.body, vars);
78117851
break;
78127852
}
7813-
// Function expressions don't add to outer scope
7853+
case NodeKind.FunctionDeclaration:
7854+
case NodeKind.Function:
7855+
// Function declarations and expressions don't add their variables to outer scope
7856+
break;
78147857
}
78157858
}
78167859

@@ -8022,6 +8065,19 @@ export class Compiler extends DiagnosticEmitter {
80228065
}
80238066
break;
80248067
}
8068+
case NodeKind.Variable: {
8069+
// Add declared variables to inner names so they're not considered captures
8070+
let varStmt = <VariableStatement>node;
8071+
for (let i = 0, k = varStmt.declarations.length; i < k; i++) {
8072+
let decl = varStmt.declarations[i];
8073+
innerFunctionNames.add(decl.name.text);
8074+
// Scan initializers for captures
8075+
if (decl.initializer) {
8076+
this.collectCapturedNames(decl.initializer, innerFunctionNames, outerFlow, declaredVars, capturedNames);
8077+
}
8078+
}
8079+
break;
8080+
}
80258081
case NodeKind.Binary: {
80268082
let binary = <BinaryExpression>node;
80278083
this.collectCapturedNames(binary.left, innerFunctionNames, outerFlow, declaredVars, capturedNames);

tests/compiler/closure.debug.wat

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,28 +2352,28 @@
23522352
global.get $$~lib/__closure_env
23532353
i32.load
23542354
global.get $$~lib/__closure_env
2355-
i32.load offset=1
2355+
i32.load offset=4
23562356
i32.add
23572357
global.get $$~lib/__closure_env
2358-
i32.load offset=2
2358+
i32.load offset=8
23592359
i32.add
23602360
)
23612361
(func $closure/testMultipleCaptures (param $a i32) (param $b i32) (result i32)
2362-
(local $c i32)
23632362
(local $$env i32)
2363+
(local $c i32)
23642364
(local $4 i32)
23652365
i32.const 12
23662366
call $~lib/rt/tlsf/__alloc
23672367
local.set $$env
23682368
local.get $$env
23692369
local.get $a
2370-
i32.store offset=4
2370+
i32.store
23712371
local.get $$env
23722372
local.get $b
2373-
i32.store offset=8
2373+
i32.store offset=4
23742374
local.get $$env
23752375
i32.const 10
2376-
i32.store
2376+
i32.store offset=8
23772377
i32.const 8
23782378
i32.const 4
23792379
call $~lib/rt/itcms/__new

tests/compiler/closure.release.wat

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,11 +1549,11 @@
15491549
)
15501550
(func $closure/testMultipleCaptures~anonymous|0 (result i32)
15511551
global.get $$~lib/__closure_env
1552-
i32.load offset=2
1552+
i32.load offset=8
15531553
global.get $$~lib/__closure_env
15541554
i32.load
15551555
global.get $$~lib/__closure_env
1556-
i32.load offset=1
1556+
i32.load offset=4
15571557
i32.add
15581558
i32.add
15591559
)
@@ -1853,13 +1853,13 @@
18531853
i32.add
18541854
local.tee $1
18551855
i32.const 1
1856-
i32.store offset=4
1856+
i32.store
18571857
local.get $0
18581858
i32.const 2
1859-
i32.store offset=12
1859+
i32.store offset=8
18601860
local.get $0
18611861
i32.const 10
1862-
i32.store offset=4
1862+
i32.store offset=12
18631863
i32.const 4
18641864
call $~lib/rt/itcms/__new
18651865
local.tee $0

0 commit comments

Comments
 (0)