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 {
val target = if (stmt is BytecodeStatement) stmt.original else stmt
return when (target) {
is BreakStatement, is ContinueStatement -> !inLoop
is BreakStatement -> target.label != null || !inLoop
is ContinueStatement -> target.label != null || !inLoop
is IfStatement -> {
containsLoopControl(target.ifBody, inLoop) ||
(target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false)
@ -2571,7 +2572,7 @@ class Compiler(
val block = try {
resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null)
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 {
resolutionSink?.exitScope(cc.currentPos())
}
@ -2585,7 +2586,10 @@ class Compiler(
val block = try {
resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null)
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 {
resolutionSink?.exitScope(cc.currentPos())
}
@ -3765,6 +3769,13 @@ class Compiler(
}
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()
if (!skipLeadingBrace) {
val t = cc.next()
@ -3773,6 +3784,9 @@ class Compiler(
}
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
for ((name, isMutable) in predeclared) {
declareSlotNameIn(blockSlotPlan, name, isMutable, isDelegated = false)
}
slotPlanStack.add(blockSlotPlan)
val capturePlan = CapturePlan(blockSlotPlan)
capturePlanStack.add(capturePlan)

View File

@ -2942,7 +2942,13 @@ class BytecodeCompiler(
private fun compileBreak(stmt: net.sergeych.lyng.BreakStatement): CompiledValue? {
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 value = stmt.resultExpr?.let { compileStatementValueOrFallback(it) }
if (ctx.resultSlot != null) {

View File

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