avoid suspend lambdas in var declaration statements

This commit is contained in:
Sergey Chernov 2026-01-24 19:04:22 +03:00
parent 062f9e7866
commit 9b580bafb6

View File

@ -3472,89 +3472,140 @@ class Compiler(
} }
} }
return statement(start) { context -> return object : Statement() {
if (extTypeName != null) { override val pos: Pos = start
val prop = if (getter != null || setter != null) { override suspend fun execute(context: Scope): Obj {
ObjProperty(name, getter, setter) if (extTypeName != null) {
} else { val prop = if (getter != null || setter != null) {
// Simple val extension with initializer ObjProperty(name, getter, setter)
val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized") } else {
ObjProperty(name, statement(initExpr.pos) { scp -> initExpr.execute(scp) }, null) // Simple val extension with initializer
val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized")
ObjProperty(
name,
object : Statement() {
override val pos: Pos = initExpr.pos
override suspend fun execute(scp: Scope): Obj = initExpr.execute(scp)
},
null
)
}
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
context.addExtension(
type,
name,
ObjRecord(
prop,
isMutable = false,
visibility = visibility,
writeVisibility = setterVisibility,
declaringClass = null,
type = ObjRecord.Type.Property
)
)
return prop
}
// In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions
// Do NOT infer declaring class from runtime thisObj here; only the compile-time captured
// ClassBody qualifies for class-field storage. Otherwise, this is a plain local.
isProperty = getter != null || setter != null
val declaringClassName = declaringClassNameCaptured
if (declaringClassName == null) {
if (context.containsLocal(name))
throw ScriptError(start, "Variable $name is already defined")
} }
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found") // Register the local name so subsequent identifiers can be emitted as fast locals
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance") if (!isStatic) declareLocalName(name)
context.addExtension(type, name, ObjRecord(prop, isMutable = false, visibility = visibility, writeVisibility = setterVisibility, declaringClass = null, type = ObjRecord.Type.Property)) if (isDelegate) {
val declaringClassName = declaringClassNameCaptured
return@statement prop if (declaringClassName != null) {
} val storageName = "$declaringClassName::$name"
// In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions val isClassScope = context.thisObj is ObjClass && (context.thisObj !is ObjInstance)
// Do NOT infer declaring class from runtime thisObj here; only the compile-time captured if (isClassScope) {
// ClassBody qualifies for class-field storage. Otherwise, this is a plain local. val cls = context.thisObj as ObjClass
isProperty = getter != null || setter != null cls.createField(
val declaringClassName = declaringClassNameCaptured name,
if (declaringClassName == null) { ObjUnset,
if (context.containsLocal(name)) isMutable,
throw ScriptError(start, "Variable $name is already defined") visibility,
} setterVisibility,
start,
// Register the local name so subsequent identifiers can be emitted as fast locals isTransient = isTransient,
if (!isStatic) declareLocalName(name) type = ObjRecord.Type.Delegated,
isAbstract = isAbstract,
if (isDelegate) { isClosed = isClosed,
val declaringClassName = declaringClassNameCaptured isOverride = isOverride
if (declaringClassName != null) { )
val storageName = "$declaringClassName::$name" cls.instanceInitializers += object : Statement() {
val isClassScope = context.thisObj is ObjClass && (context.thisObj !is ObjInstance) override val pos: Pos = start
if (isClassScope) { override suspend fun execute(scp: Scope): Obj {
val cls = context.thisObj as ObjClass val initValue = initialExpression!!.execute(scp)
cls.createField( val accessTypeStr = if (isMutable) "Var" else "Val"
name, val accessType = scp.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
ObjUnset, val finalDelegate = try {
isMutable, initValue.invokeInstanceMethod(
visibility, scp,
setterVisibility, "bind",
start, Arguments(ObjString(name), accessType, scp.thisObj)
isTransient = isTransient, )
type = ObjRecord.Type.Delegated, } catch (e: Exception) {
isAbstract = isAbstract, initValue
isClosed = isClosed, }
isOverride = isOverride scp.addItem(
) storageName, isMutable, ObjUnset, visibility, setterVisibility,
cls.instanceInitializers += statement(start) { scp -> recordType = ObjRecord.Type.Delegated,
val initValue = initialExpression!!.execute(scp) isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
).apply {
delegate = finalDelegate
}
return ObjVoid
}
}
return ObjVoid
} else {
val initValue = initialExpression!!.execute(context)
val accessTypeStr = if (isMutable) "Var" else "Val" val accessTypeStr = if (isMutable) "Var" else "Val"
val accessType = scp.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr") val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
val finalDelegate = try { val finalDelegate = try {
initValue.invokeInstanceMethod(scp, "bind", Arguments(ObjString(name), accessType, scp.thisObj)) initValue.invokeInstanceMethod(
context,
"bind",
Arguments(ObjString(name), accessType, context.thisObj)
)
} catch (e: Exception) { } catch (e: Exception) {
initValue initValue
} }
scp.addItem( val rec = context.addItem(
storageName, isMutable, ObjUnset, visibility, setterVisibility, storageName, isMutable, ObjUnset, visibility, setterVisibility,
recordType = ObjRecord.Type.Delegated, recordType = ObjRecord.Type.Delegated,
isAbstract = isAbstract, isAbstract = isAbstract,
isClosed = isClosed, isClosed = isClosed,
isOverride = isOverride, isOverride = isOverride,
isTransient = isTransient isTransient = isTransient
).apply { )
delegate = finalDelegate rec.delegate = finalDelegate
} return finalDelegate
ObjVoid
} }
return@statement ObjVoid
} else { } else {
val initValue = initialExpression!!.execute(context) val initValue = initialExpression!!.execute(context)
val accessTypeStr = if (isMutable) "Var" else "Val" val accessTypeStr = if (isMutable) "Var" else "Val"
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr") val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr")
val finalDelegate = try { val finalDelegate = try {
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, context.thisObj)) initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull))
} catch (e: Exception) { } catch (e: Exception) {
initValue initValue
} }
val rec = context.addItem( val rec = context.addItem(
storageName, isMutable, ObjUnset, visibility, setterVisibility, name, isMutable, ObjUnset, visibility, setterVisibility,
recordType = ObjRecord.Type.Delegated, recordType = ObjRecord.Type.Delegated,
isAbstract = isAbstract, isAbstract = isAbstract,
isClosed = isClosed, isClosed = isClosed,
@ -3562,95 +3613,78 @@ class Compiler(
isTransient = isTransient isTransient = isTransient
) )
rec.delegate = finalDelegate rec.delegate = finalDelegate
return@statement finalDelegate return finalDelegate
} }
} else { } else if (getter != null || setter != null) {
val initValue = initialExpression!!.execute(context) val declaringClassName = declaringClassNameCaptured!!
val accessTypeStr = if (isMutable) "Var" else "Val" val storageName = "$declaringClassName::$name"
val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr") val prop = ObjProperty(name, getter, setter)
val finalDelegate = try {
initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull))
} catch (e: Exception) {
initValue
}
val rec = context.addItem(
name, isMutable, ObjUnset, visibility, setterVisibility,
recordType = ObjRecord.Type.Delegated,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
)
rec.delegate = finalDelegate
return@statement finalDelegate
}
} else if (getter != null || setter != null) {
val declaringClassName = declaringClassNameCaptured!!
val storageName = "$declaringClassName::$name"
val prop = ObjProperty(name, getter, setter)
// If we are in class scope now (defining instance field), defer initialization to instance time // If we are in class scope now (defining instance field), defer initialization to instance time
val isClassScope = context.thisObj is ObjClass && (context.thisObj !is ObjInstance) val isClassScope = context.thisObj is ObjClass && (context.thisObj !is ObjInstance)
if (isClassScope) { if (isClassScope) {
val cls = context.thisObj as ObjClass val cls = context.thisObj as ObjClass
// Register in class members for reflection/MRO/satisfaction checks // Register in class members for reflection/MRO/satisfaction checks
if (isProperty) { if (isProperty) {
cls.addProperty( cls.addProperty(
name, name,
visibility = visibility, visibility = visibility,
writeVisibility = setterVisibility, writeVisibility = setterVisibility,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
pos = start,
prop = prop
)
} else {
cls.createField(
name,
ObjNull,
isMutable = isMutable,
visibility = visibility,
writeVisibility = setterVisibility,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient,
type = ObjRecord.Type.Field
)
}
// Register the property/field initialization thunk
if (!isAbstract) {
cls.instanceInitializers += statement(start) { scp ->
scp.addItem(
storageName,
isMutable,
prop,
visibility,
setterVisibility,
recordType = ObjRecord.Type.Property,
isAbstract = isAbstract, isAbstract = isAbstract,
isClosed = isClosed, isClosed = isClosed,
isOverride = isOverride isOverride = isOverride,
pos = start,
prop = prop
)
} else {
cls.createField(
name,
ObjNull,
isMutable = isMutable,
visibility = visibility,
writeVisibility = setterVisibility,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient,
type = ObjRecord.Type.Field
) )
ObjVoid
} }
// Register the property/field initialization thunk
if (!isAbstract) {
cls.instanceInitializers += object : Statement() {
override val pos: Pos = start
override suspend fun execute(scp: Scope): Obj {
scp.addItem(
storageName,
isMutable,
prop,
visibility,
setterVisibility,
recordType = ObjRecord.Type.Property,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride
)
return ObjVoid
}
}
}
return ObjVoid
} else {
// We are in instance scope already: perform initialization immediately
context.addItem(
storageName, isMutable, prop, visibility, setterVisibility,
recordType = ObjRecord.Type.Property,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
)
return prop
} }
ObjVoid
} else { } else {
// We are in instance scope already: perform initialization immediately
context.addItem(
storageName, isMutable, prop, visibility, setterVisibility,
recordType = ObjRecord.Type.Property,
isAbstract = isAbstract,
isClosed = isClosed,
isOverride = isOverride,
isTransient = isTransient
)
prop
}
} else {
val isLateInitVal = !isMutable && initialExpression == null val isLateInitVal = !isMutable && initialExpression == null
if (declaringClassName != null && !isStatic) { if (declaringClassName != null && !isStatic) {
val storageName = "$declaringClassName::$name" val storageName = "$declaringClassName::$name"
@ -3675,28 +3709,32 @@ class Compiler(
// Defer: at instance construction, evaluate initializer in instance scope and store under mangled name // Defer: at instance construction, evaluate initializer in instance scope and store under mangled name
if (!isAbstract) { if (!isAbstract) {
val initStmt = statement(start) { scp -> val initStmt = object : Statement() {
val initValue = override val pos: Pos = start
initialExpression?.execute(scp)?.byValueCopy() override suspend fun execute(scp: Scope): Obj {
?: if (isLateInitVal) ObjUnset else ObjNull val initValue =
// Preserve mutability of declaration: do NOT use addOrUpdateItem here, as it creates mutable records initialExpression?.execute(scp)?.byValueCopy()
scp.addItem( ?: if (isLateInitVal) ObjUnset else ObjNull
storageName, isMutable, initValue, visibility, setterVisibility, // Preserve mutability of declaration: do NOT use addOrUpdateItem here, as it creates mutable records
recordType = ObjRecord.Type.Field, scp.addItem(
isAbstract = isAbstract, storageName, isMutable, initValue, visibility, setterVisibility,
isClosed = isClosed, recordType = ObjRecord.Type.Field,
isOverride = isOverride, isAbstract = isAbstract,
isTransient = isTransient isClosed = isClosed,
) isOverride = isOverride,
ObjVoid isTransient = isTransient
)
return ObjVoid
}
} }
cls.instanceInitializers += initStmt cls.instanceInitializers += initStmt
} }
ObjVoid return ObjVoid
} else { } else {
// We are in instance scope already: perform initialization immediately // We are in instance scope already: perform initialization immediately
val initValue = val initValue =
initialExpression?.execute(context)?.byValueCopy() ?: if (isLateInitVal) ObjUnset else ObjNull initialExpression?.execute(context)?.byValueCopy()
?: if (isLateInitVal) ObjUnset else ObjNull
// Preserve mutability of declaration: create record with correct mutability // Preserve mutability of declaration: create record with correct mutability
context.addItem( context.addItem(
storageName, isMutable, initValue, visibility, setterVisibility, storageName, isMutable, initValue, visibility, setterVisibility,
@ -3706,14 +3744,15 @@ class Compiler(
isOverride = isOverride, isOverride = isOverride,
isTransient = isTransient isTransient = isTransient
) )
initValue return initValue
} }
} else { } else {
// Not in class body: regular local/var declaration // Not in class body: regular local/var declaration
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Other, isTransient = isTransient) context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Other, isTransient = isTransient)
initValue return initValue
} }
}
} }
} }
} }