Step 21: union mismatch bytecode throw
This commit is contained in:
parent
b49f291bff
commit
9d508e219f
@ -70,9 +70,9 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Allow `NopStatement` in `containsUnsupportedForBytecode`.
|
- [x] Allow `NopStatement` in `containsUnsupportedForBytecode`.
|
||||||
- [x] Emit `ObjVoid` directly in bytecode for `NopStatement` in statement/value contexts.
|
- [x] Emit `ObjVoid` directly in bytecode for `NopStatement` in statement/value contexts.
|
||||||
- [x] Add a JVM test that exercises a code path returning `NopStatement` in bytecode (e.g., static class member decl in class body).
|
- [x] Add a JVM test that exercises a code path returning `NopStatement` in bytecode (e.g., static class member decl in class body).
|
||||||
- [ ] Step 21: Union mismatch path in bytecode.
|
- [x] Step 21: Union mismatch path in bytecode.
|
||||||
- [ ] Replace `UnionTypeMismatchStatement` branch with a bytecode-compilable throw path (no custom `StatementRef` that blocks bytecode).
|
- [x] Replace `UnionTypeMismatchStatement` branch with a bytecode-compilable throw path (no custom `StatementRef` that blocks bytecode).
|
||||||
- [ ] Add a JVM test that forces the union mismatch at runtime and asserts the error message.
|
- [x] Add a JVM test that forces the union mismatch at runtime and asserts the error message.
|
||||||
- [ ] Step 22: Delegated local slots in bytecode.
|
- [ ] Step 22: Delegated local slots in bytecode.
|
||||||
- [ ] Support reads/writes/assign-ops/inc/dec for delegated locals (`LocalSlotRef.isDelegated`) in `BytecodeCompiler`.
|
- [ ] Support reads/writes/assign-ops/inc/dec for delegated locals (`LocalSlotRef.isDelegated`) in `BytecodeCompiler`.
|
||||||
- [ ] Remove `containsDelegatedRefs` guard once delegated locals are bytecode-safe.
|
- [ ] Remove `containsDelegatedRefs` guard once delegated locals are bytecode-safe.
|
||||||
|
|||||||
@ -4341,15 +4341,6 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UnionTypeMismatchStatement(
|
|
||||||
private val message: String,
|
|
||||||
override val pos: Pos
|
|
||||||
) : Statement() {
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
throw ScriptError(pos, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveMemberHostForUnion(typeDecl: TypeDecl, memberName: String, pos: Pos): ObjClass {
|
private fun resolveMemberHostForUnion(typeDecl: TypeDecl, memberName: String, pos: Pos): ObjClass {
|
||||||
val receiverClass = when (typeDecl) {
|
val receiverClass = when (typeDecl) {
|
||||||
TypeDecl.TypeAny, TypeDecl.TypeNullableAny -> Obj.rootObjectType
|
TypeDecl.TypeAny, TypeDecl.TypeNullableAny -> Obj.rootObjectType
|
||||||
@ -4392,8 +4383,10 @@ class Compiler(
|
|||||||
resolveMemberHostForUnion(option, memberName, pos)
|
resolveMemberHostForUnion(option, memberName, pos)
|
||||||
}
|
}
|
||||||
val unionName = typeDeclName(union)
|
val unionName = typeDeclName(union)
|
||||||
val failStmt = UnionTypeMismatchStatement("value is not $unionName", pos)
|
val errorMessage = ObjString("value is not $unionName").asReadonly
|
||||||
var current: ObjRef = net.sergeych.lyng.obj.StatementRef(failStmt)
|
val throwExpr = ExpressionStatement(ConstRef(errorMessage), pos)
|
||||||
|
val throwStmt = ThrowStatement(throwExpr, pos)
|
||||||
|
var current: ObjRef = net.sergeych.lyng.obj.StatementRef(throwStmt)
|
||||||
for (option in options.asReversed()) {
|
for (option in options.asReversed()) {
|
||||||
val typeRef = net.sergeych.lyng.obj.TypeDeclRef(option, pos)
|
val typeRef = net.sergeych.lyng.obj.TypeDeclRef(option, pos)
|
||||||
val cond = BinaryOpRef(BinOp.IS, left, typeRef)
|
val cond = BinaryOpRef(BinOp.IS, left, typeRef)
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.Compiler
|
import net.sergeych.lyng.Compiler
|
||||||
|
import net.sergeych.lyng.ExecutionError
|
||||||
import net.sergeych.lyng.Script
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.ScriptError
|
import net.sergeych.lyng.ScriptError
|
||||||
import net.sergeych.lyng.Source
|
import net.sergeych.lyng.Source
|
||||||
@ -24,6 +25,7 @@ import net.sergeych.lyng.eval
|
|||||||
import net.sergeych.lyng.obj.toInt
|
import net.sergeych.lyng.obj.toInt
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class BytecodeRecentOpsTest {
|
class BytecodeRecentOpsTest {
|
||||||
@ -221,6 +223,21 @@ class BytecodeRecentOpsTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unionMemberDispatchMismatch() = runTest {
|
||||||
|
val err = assertFailsWith<ExecutionError> {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
class A { fun who() = "A" }
|
||||||
|
class B { fun who() = "B" }
|
||||||
|
val x: A | B = 1
|
||||||
|
x.who()
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assertTrue(err.message?.contains("value is not A | B") == true)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun objectReceiverMemberError() = runTest {
|
fun objectReceiverMemberError() = runTest {
|
||||||
val failed = try {
|
val failed = try {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user