Fix block capture sync for bytecode locals

This commit is contained in:
Sergey Chernov 2026-01-30 15:39:03 +03:00
parent 89cf2c1612
commit ecf64dcbc3
3 changed files with 17 additions and 15 deletions

View File

@ -2116,6 +2116,7 @@ class BytecodeCompiler(
compileRefWithFallback(ref, null, target.pos) compileRefWithFallback(ref, null, target.pos)
} }
} }
is VarDeclStatement -> emitVarDecl(target)
is IfStatement -> compileIfStatement(target) is IfStatement -> compileIfStatement(target)
is net.sergeych.lyng.ForInStatement -> { is net.sergeych.lyng.ForInStatement -> {
val resultSlot = emitForIn(target, false) ?: return null val resultSlot = emitForIn(target, false) ?: return null

View File

@ -1522,6 +1522,19 @@ class CmdFrame(
} }
} }
private fun shouldSyncLocalCaptures(captures: List<String>): Boolean {
if (captures.isEmpty()) return false
val localNames = fn.localSlotNames
if (localNames.isEmpty()) return false
for (capture in captures) {
for (local in localNames) {
if (local == null) continue
if (local == capture) return true
}
}
return false
}
private fun resolveModuleScope(scope: Scope): Scope { private fun resolveModuleScope(scope: Scope): Scope {
var current: Scope? = scope var current: Scope? = scope
var last: Scope = scope var last: Scope = scope
@ -1545,7 +1558,7 @@ class CmdFrame(
} else { } else {
emptyList() emptyList()
} }
if (captures.isNotEmpty() && fn.localSlotNames.isNotEmpty()) { if (shouldSyncLocalCaptures(captures)) {
syncFrameToScope() syncFrameToScope()
} }
if (scope.skipScopeCreation) { if (scope.skipScopeCreation) {
@ -1584,8 +1597,8 @@ class CmdFrame(
?: error("Scope stack underflow in POP_SCOPE") ?: error("Scope stack underflow in POP_SCOPE")
val captures = captureStack.removeLastOrNull() ?: emptyList() val captures = captureStack.removeLastOrNull() ?: emptyList()
scopeDepth -= 1 scopeDepth -= 1
if (captures.isNotEmpty() && fn.localSlotNames.isNotEmpty()) { if (shouldSyncLocalCaptures(captures)) {
syncScopeToFrame() syncFrameToScope()
} }
} }

View File

@ -131,7 +131,6 @@ class ScriptTest {
} }
// --- Helpers to test iterator cancellation semantics --- // --- Helpers to test iterator cancellation semantics ---
@Ignore
class ObjTestIterable : Obj() { class ObjTestIterable : Obj() {
var cancelCount: Int = 0 var cancelCount: Int = 0
@ -148,7 +147,6 @@ class ScriptTest {
} }
} }
@Ignore
class ObjTestIterator(private val owner: ObjTestIterable) : Obj() { class ObjTestIterator(private val owner: ObjTestIterable) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass = type
private var i = 0 private var i = 0
@ -171,7 +169,6 @@ class ScriptTest {
} }
} }
@Ignore("Scope.eval should seed compile-time symbols from current scope")
@Test @Test
fun testForLoopDoesNotCancelOnNaturalCompletion() = runTest { fun testForLoopDoesNotCancelOnNaturalCompletion() = runTest {
val scope = Script.newScope() val scope = Script.newScope()
@ -189,7 +186,6 @@ class ScriptTest {
assertEquals(0, ti.cancelCount) assertEquals(0, ti.cancelCount)
} }
@Ignore("incremental enable")
@Test @Test
fun testForLoopCancelsOnBreak() = runTest { fun testForLoopCancelsOnBreak() = runTest {
val scope = Script.newScope() val scope = Script.newScope()
@ -205,7 +201,6 @@ class ScriptTest {
assertEquals(1, ti.cancelCount) assertEquals(1, ti.cancelCount)
} }
@Ignore("incremental enable")
@Test @Test
fun testForLoopCancelsOnException() = runTest { fun testForLoopCancelsOnException() = runTest {
val scope = Script.newScope() val scope = Script.newScope()
@ -384,7 +379,6 @@ class ScriptTest {
assertTrue(eval("sin(π)").toDouble() - 1 < 0.000001) assertTrue(eval("sin(π)").toDouble() - 1 < 0.000001)
} }
@Ignore("Scope.eval should seed compile-time symbols from current scope")
@Test @Test
fun varsAndConstsTest() = runTest { fun varsAndConstsTest() = runTest {
val scope = Scope(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
@ -406,7 +400,6 @@ class ScriptTest {
assertEquals(5, scope.eval("b").toInt()) assertEquals(5, scope.eval("b").toInt())
} }
@Ignore("incremental enable")
@Test @Test
fun functionTest() = runTest { fun functionTest() = runTest {
val scope = Scope(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
@ -433,7 +426,6 @@ class ScriptTest {
assertEquals(14, scope.eval("bar(3)").toInt()) assertEquals(14, scope.eval("bar(3)").toInt())
} }
@Ignore("incremental enable")
@Test @Test
fun simpleClosureTest() = runTest { fun simpleClosureTest() = runTest {
val scope = Scope(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
@ -559,7 +551,6 @@ class ScriptTest {
assertFalse { eval("4 <= 3").toBool() } assertFalse { eval("4 <= 3").toBool() }
} }
@Ignore("incremental enable")
@Test @Test
fun ifTest() = runTest { fun ifTest() = runTest {
// if - single line // if - single line
@ -759,7 +750,6 @@ class ScriptTest {
assertEquals(ObjInt(5), c["c"]?.value) assertEquals(ObjInt(5), c["c"]?.value)
} }
@Ignore("Scope.eval should seed compile-time symbols from current scope")
@Test @Test
fun testAssignArgumentsEndEllipsis() = runTest { fun testAssignArgumentsEndEllipsis() = runTest {
// equal args, // equal args,
@ -787,7 +777,6 @@ class ScriptTest {
c.eval("assert( b == [] )") c.eval("assert( b == [] )")
} }
@Ignore("incremental enable")
@Test @Test
fun testAssignArgumentsStartEllipsis() = runTest { fun testAssignArgumentsStartEllipsis() = runTest {
val ttEnd = Token.Type.RBRACE val ttEnd = Token.Type.RBRACE
@ -822,7 +811,6 @@ class ScriptTest {
} }
} }
@Ignore("incremental enable")
@Test @Test
fun testAssignArgumentsMiddleEllipsis() = runTest { fun testAssignArgumentsMiddleEllipsis() = runTest {
val ttEnd = Token.Type.RBRACE val ttEnd = Token.Type.RBRACE