From 10fa4de4fafa7f0d74e010a9781edd7a8abed73c Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 20 Apr 2026 18:48:28 +0300 Subject: [PATCH] regression fix --- .../net/sergeych/lyng/bytecode/CmdRuntime.kt | 28 +++++++++++++++---- lynglib/src/commonTest/kotlin/StdlibTest.kt | 24 ++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt index 485cf3b..5c986dd 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -4174,10 +4174,15 @@ class CmdFrame( } else { val value = record.value if (!record.isMutable && value is FrameSlotRef) { - val resolved = value.peekValue() + val resolved = value.resolvedCaptureValueOrNull() if (resolved != null) { if (value.refersTo(frame, localIndex)) continue - frame.setObj(localIndex, value.read()) + when (resolved) { + is ObjInt -> frame.setInt(localIndex, resolved.value) + is ObjReal -> frame.setReal(localIndex, resolved.value) + is ObjBool -> frame.setBool(localIndex, resolved.value) + else -> frame.setObj(localIndex, resolved) + } } else { frame.setObj(localIndex, value) } @@ -4213,9 +4218,14 @@ class CmdFrame( } else { val value = record.value if (!record.isMutable && value is FrameSlotRef) { - val resolved = value.peekValue() + val resolved = value.resolvedCaptureValueOrNull() if (resolved != null) { - frame.setObj(idx, value.read()) + when (resolved) { + is ObjInt -> frame.setInt(idx, resolved.value) + is ObjReal -> frame.setReal(idx, resolved.value) + is ObjBool -> frame.setBool(idx, resolved.value) + else -> frame.setObj(idx, resolved) + } } else { frame.setObj(idx, value) } @@ -4279,7 +4289,15 @@ class CmdFrame( } } else { val raw = frame.getRawObj(localIndex) - if (raw == null && name != null) { + // For primitive-typed slots (INT/REAL/BOOL), getRawObj returns null + // but the value exists in the typed slot array. Skip the scope name + // lookup to avoid finding a same-named symbol higher in the scope chain + // (e.g., a List.size property when capturing a `size: Int` parameter). + val isPrimitiveSlot = when (frame.getSlotTypeCode(localIndex)) { + SlotType.INT.code, SlotType.REAL.code, SlotType.BOOL.code -> true + else -> false + } + if (raw == null && name != null && !isPrimitiveSlot) { val record = findNamedExistingRecord(scope, name) if (record != null) { val value = record.value diff --git a/lynglib/src/commonTest/kotlin/StdlibTest.kt b/lynglib/src/commonTest/kotlin/StdlibTest.kt index 14bff4e..28de2b1 100644 --- a/lynglib/src/commonTest/kotlin/StdlibTest.kt +++ b/lynglib/src/commonTest/kotlin/StdlibTest.kt @@ -444,4 +444,28 @@ class StdlibTest { assertEquals([0, 10, 20, 30, 40], x) """.trimIndent()) } + @Test + fun testListFill2() = runTest { + eval(""" + val b: List = List.fill(5,10) { it } + println(b) + assertEquals(b, [0,1,2,3,4]) + """.trimIndent()) + } + + @Test + fun testTypedParamCaptureInClosure() = runTest { + // Regression: typed (Int) parameters lost their value when captured into a closure + // because peekValue() returned null for INT-typed frame slots, causing the capture + // to store a FrameSlotRef as OBJ while bytecode expected INT type. + eval(""" + fun makeList(n: Int): List { + List().also { + for( i in 0..