Add receiver-stack function types

This commit is contained in:
sergeych committed 2026-04-29 12:59:24 +03:00
1 parent 1bababa058
commit e107296bca
11 files changed
+415 -46

No files matched your search

@@ -473,6 +473,29 @@ class MiniAstTest {
assertEquals("b", fn.params[1].name)
}
@Test
fun miniAst_captures_context_receiver_function_type() = runTest {
val code = """
val block: context(Html, Head) Body.()->String = { "ok" }
""".trimIndent()
val (_, sink) = compileWithMini(code)
val mini = sink.build()
assertNotNull(mini)
val vd = mini.declarations.filterIsInstance<MiniValDecl>().firstOrNull { it.name == "block" }
assertNotNull(vd)
val type = vd.type as MiniFunctionType
val receiver = type.receiver as MiniTypeName
assertEquals(listOf("Body"), receiver.segments.map { it.name })
assertEquals(2, type.contextReceivers.size)
val ctx0 = type.contextReceivers[0] as MiniTypeName
val ctx1 = type.contextReceivers[1] as MiniTypeName
assertEquals(listOf("Html"), ctx0.segments.map { it.name })
assertEquals(listOf("Head"), ctx1.segments.map { it.name })
assertTrue(type.params.isEmpty())
val ret = type.returnType as MiniTypeName
assertEquals(listOf("String"), ret.segments.map { it.name })
}
@Test
fun miniAst_captures_dokka_tags() = runTest {
val code = """
@@ -154,4 +154,132 @@ class OptTest {
assert( cnt == 4 )
""".trimIndent())
}
@Test
fun testReceivers1() = runTest {
eval("""
class RA {
fun a() { println("a") }
}
class RB {
fun b() { println("b") }
}
fun ta( f: RA.()->Unit ) {
val instance = RA()
with(instance) { f(this) }
}
fun tb( f: RB.()->Unit ) {
val b = RB()
with(b) { f(this) }
}
ta {
a()
tb {
b()
// but important: a() must still be accessible
// because it is inner block, sort of closure:
a()
}
}
""".trimIndent())
}
@Test
fun testContextReceiverFunctionType() = runTest {
eval("""
class RA {
fun value(): Int = 10
}
class RB {
fun value(): Int = 20
}
fun ta(f: RA.()->Int): Int {
val instance = RA()
return with(instance) { f(this) }
}
fun tb(f: context(RA) RB.()->Int): Int {
val instance = RB()
return with(instance) { f(this) }
}
val result = ta {
val block: context(RA) RB.()->Int = {
value() + this@RA.value()
}
tb(block)
}
assertEquals(30, result)
""".trimIndent())
}
@Test
fun testNestedReceiverQualifiedThis() = runTest {
eval("""
class RA {
fun value(): Int = 1
}
class RB {
fun value(): Int = 2
}
fun ta(f: RA.()->Int): Int {
val instance = RA()
return with(instance) { f(this) }
}
fun tb(f: RB.()->Int): Int {
val instance = RB()
return with(instance) { f(this) }
}
val result = ta {
tb {
value() + this@RA.value()
}
}
assertEquals(3, result)
""".trimIndent())
}
@Test
fun testReceiverAmbiguityRequiresQualifiedThis() = runTest {
val ex = assertFailsWith<ScriptError> {
eval("""
class RA {
fun shared(): Int = 10
}
class RC {
fun shared(): Int = 30
}
class RB
fun ta(f: RA.()->Int): Int {
val instance = RA()
return with(instance) { f(this) }
}
fun tc(f: RC.()->Int): Int {
val instance = RC()
return with(instance) { f(this) }
}
fun tb(f: context(RA, RC) RB.()->Int): Int {
val instance = RB()
return with(instance) { f(this) }
}
ta {
tc {
val block: context(RA, RC) RB.()->Int = {
shared()
}
tb(block)
}
}
""".trimIndent())
}
assertContains(ex.message ?: "", "ambiguous between receivers RA, RC")
}
}