Handle labeled break and catch locals in ScriptTest

This commit is contained in:
Sergey Chernov 2026-01-30 17:03:51 +03:00
parent d6e1e74b48
commit 4b66454bf3
3 changed files with 24 additions and 8 deletions

View File

@ -712,7 +712,8 @@ class Compiler(
private fun containsLoopControl(stmt: Statement, inLoop: Boolean = false): Boolean { private fun containsLoopControl(stmt: Statement, inLoop: Boolean = false): Boolean {
val target = if (stmt is BytecodeStatement) stmt.original else stmt val target = if (stmt is BytecodeStatement) stmt.original else stmt
return when (target) { return when (target) {
is BreakStatement, is ContinueStatement -> !inLoop is BreakStatement -> target.label != null || !inLoop
is ContinueStatement -> target.label != null || !inLoop
is IfStatement -> { is IfStatement -> {
containsLoopControl(target.ifBody, inLoop) || containsLoopControl(target.ifBody, inLoop) ||
(target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false) (target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false)
@ -2571,7 +2572,7 @@ class Compiler(
val block = try { val block = try {
resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null) resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null)
resolutionSink?.declareSymbol(catchVar.value, SymbolKind.LOCAL, isMutable = false, pos = catchVar.pos) resolutionSink?.declareSymbol(catchVar.value, SymbolKind.LOCAL, isMutable = false, pos = catchVar.pos)
withCatchSlot(unwrapBytecodeDeep(parseBlock()), catchVar.value) withCatchSlot(unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(catchVar.value to false))), catchVar.value)
} finally { } finally {
resolutionSink?.exitScope(cc.currentPos()) resolutionSink?.exitScope(cc.currentPos())
} }
@ -2585,7 +2586,10 @@ class Compiler(
val block = try { val block = try {
resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null) resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null)
resolutionSink?.declareSymbol(itToken.value, SymbolKind.LOCAL, isMutable = false, pos = itToken.pos) resolutionSink?.declareSymbol(itToken.value, SymbolKind.LOCAL, isMutable = false, pos = itToken.pos)
withCatchSlot(unwrapBytecodeDeep(parseBlock(true)), itToken.value) withCatchSlot(
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(itToken.value to false), skipLeadingBrace = true)),
itToken.value
)
} finally { } finally {
resolutionSink?.exitScope(cc.currentPos()) resolutionSink?.exitScope(cc.currentPos())
} }
@ -3765,6 +3769,13 @@ class Compiler(
} }
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement { private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
return parseBlockWithPredeclared(emptyList(), skipLeadingBrace)
}
private suspend fun parseBlockWithPredeclared(
predeclared: List<Pair<String, Boolean>>,
skipLeadingBrace: Boolean = false
): Statement {
val startPos = cc.currentPos() val startPos = cc.currentPos()
if (!skipLeadingBrace) { if (!skipLeadingBrace) {
val t = cc.next() val t = cc.next()
@ -3773,6 +3784,9 @@ class Compiler(
} }
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null) resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++) val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
for ((name, isMutable) in predeclared) {
declareSlotNameIn(blockSlotPlan, name, isMutable, isDelegated = false)
}
slotPlanStack.add(blockSlotPlan) slotPlanStack.add(blockSlotPlan)
val capturePlan = CapturePlan(blockSlotPlan) val capturePlan = CapturePlan(blockSlotPlan)
capturePlanStack.add(capturePlan) capturePlanStack.add(capturePlan)

View File

@ -2942,7 +2942,13 @@ class BytecodeCompiler(
private fun compileBreak(stmt: net.sergeych.lyng.BreakStatement): CompiledValue? { private fun compileBreak(stmt: net.sergeych.lyng.BreakStatement): CompiledValue? {
val stack = loopStack.toList() val stack = loopStack.toList()
val targetIndex = findLoopContextIndex(stmt.label) ?: return null val targetIndex = findLoopContextIndex(stmt.label) ?: run {
val labels = stack.joinToString(prefix = "[", postfix = "]") { it.label ?: "<unlabeled>" }
throw BytecodeFallbackException(
"Bytecode fallback: break label '${stmt.label}' not found in $labels",
stmt.pos
)
}
val ctx = stack[targetIndex] val ctx = stack[targetIndex]
val value = stmt.resultExpr?.let { compileStatementValueOrFallback(it) } val value = stmt.resultExpr?.let { compileStatementValueOrFallback(it) }
if (ctx.resultSlot != null) { if (ctx.resultSlot != null) {

View File

@ -2092,7 +2092,6 @@ class ScriptTest {
) )
} }
@Ignore("incremental enable")
@Test @Test
fun testForLabelNreakTest() = runTest { fun testForLabelNreakTest() = runTest {
eval( eval(
@ -2115,7 +2114,6 @@ class ScriptTest {
) )
} }
@Ignore("incremental enable")
@Test @Test
fun testThrowExisting() = runTest { fun testThrowExisting() = runTest {
eval( eval(
@ -2145,7 +2143,6 @@ class ScriptTest {
) )
} }
@Ignore("incremental enable")
@Test @Test
fun testCatchShort1() = runTest { fun testCatchShort1() = runTest {
eval( eval(
@ -2170,7 +2167,6 @@ class ScriptTest {
) )
} }
@Ignore("incremental enable")
@Test @Test
fun testCatchShort2() = runTest { fun testCatchShort2() = runTest {
eval( eval(