JVM_IR: fix NPE in interface companion initializers

* When referencing the companion itself, they should use the $$INSTANCE
  field, not the (null until <clinit> returns) Companion field of the
  interface.

* Interface companion init blocks should be made static.
This commit is contained in:
pyos
2019-11-05 13:23:29 +01:00
committed by max-kammerer
parent 7c015564ce
commit 939a9ff53e
11 changed files with 83 additions and 129 deletions
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.backend.common.lower package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -32,8 +31,6 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -213,30 +210,3 @@ fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index
) )
else -> throw Error("Unexpected parameter descriptor: $this") else -> throw Error("Unexpected parameter descriptor: $this")
} }
fun IrBody.replaceThisByStaticReference(
context: CommonBackendContext,
irClass: IrClass,
oldThisReceiverParameter: IrValueParameter
): IrBody =
transform(ReplaceThisByStaticReference(context, irClass, oldThisReceiverParameter), null)
private class ReplaceThisByStaticReference(
val context: CommonBackendContext,
val irClass: IrClass,
val oldThisReceiverParameter: IrValueParameter
) : IrElementTransformer<Nothing?> {
override fun visitGetValue(expression: IrGetValue, data: Nothing?): IrExpression {
val irGetValue = expression
if (irGetValue.symbol == oldThisReceiverParameter.symbol) {
val instanceField = context.declarationFactory.getFieldForObjectInstance(irClass)
return IrGetFieldImpl(
expression.startOffset,
expression.endOffset,
instanceField.symbol,
irClass.defaultType
)
}
return super.visitGetValue(irGetValue, data)
}
}
@@ -46,5 +46,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true) object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true) object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true)
object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW") object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW")
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true) object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true)
} }
@@ -34,6 +34,7 @@ class JvmDeclarationFactory(
private val methodSignatureMapper: MethodSignatureMapper private val methodSignatureMapper: MethodSignatureMapper
) : DeclarationFactory { ) : DeclarationFactory {
private val singletonFieldDeclarations = HashMap<IrSymbolOwner, IrField>() private val singletonFieldDeclarations = HashMap<IrSymbolOwner, IrField>()
private val interfaceCompanionFieldDeclarations = HashMap<IrSymbolOwner, IrField>()
private val outerThisDeclarations = HashMap<IrClass, IrField>() private val outerThisDeclarations = HashMap<IrClass, IrField>()
private val innerClassConstructors = HashMap<IrConstructor, IrConstructor>() private val innerClassConstructors = HashMap<IrConstructor, IrConstructor>()
@@ -134,6 +135,23 @@ class JvmDeclarationFactory(
} }
} }
fun getPrivateFieldForObjectInstance(singleton: IrClass): IrField =
if (singleton.isCompanion && singleton.parentAsClass.isJvmInterface)
interfaceCompanionFieldDeclarations.getOrPut(singleton) {
buildField {
name = Name.identifier("\$\$INSTANCE")
type = singleton.defaultType
origin = JvmLoweredDeclarationOrigin.INTERFACE_COMPANION_PRIVATE_INSTANCE
isFinal = true
isStatic = true
visibility = JavaVisibilities.PACKAGE_VISIBILITY
}.apply {
parent = singleton
}
}
else
getFieldForObjectInstance(singleton)
fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction { fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction {
val parent = interfaceFun.parentAsClass val parent = interfaceFun.parentAsClass
assert(parent.isJvmInterface) { "Parent of ${interfaceFun.dump()} should be interface" } assert(parent.isJvmInterface) { "Parent of ${interfaceFun.dump()} should be interface" }
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME
@@ -148,3 +150,23 @@ fun IrExpression.isSmartcastFromHigherThanNullable(context: JvmBackendContext) =
this is IrTypeOperatorCall && this is IrTypeOperatorCall &&
operator == IrTypeOperator.IMPLICIT_CAST && operator == IrTypeOperator.IMPLICIT_CAST &&
!this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns) !this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns)
fun IrBody.replaceThisByStaticReference(
context: JvmBackendContext,
irClass: IrClass,
oldThisReceiverParameter: IrValueParameter
): IrBody =
transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.symbol == oldThisReceiverParameter.symbol) {
val instanceField = context.declarationFactory.getPrivateFieldForObjectInstance(irClass)
return IrGetFieldImpl(
expression.startOffset,
expression.endOffset,
instanceField.symbol,
irClass.defaultType
)
}
return super.visitGetValue(expression)
}
}, null)
@@ -10,12 +10,12 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.lower.replaceThisByStaticReference
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
@@ -6,10 +6,10 @@
package org.jetbrains.kotlin.backend.jvm.lower package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.replaceThisByStaticReference
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isObject import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME
@@ -40,7 +41,7 @@ internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
description = "Move and/or copy companion object fields to static fields of companion's owner" description = "Move and/or copy companion object fields to static fields of companion's owner"
) )
private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackendContext) : ClassLoweringPass { private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
val fieldReplacementMap = mutableMapOf<IrFieldSymbol, IrFieldSymbol>() val fieldReplacementMap = mutableMapOf<IrFieldSymbol, IrFieldSymbol>()
if (irClass.isObject && !irClass.isCompanion && irClass.visibility != Visibilities.LOCAL) { if (irClass.isObject && !irClass.isCompanion && irClass.visibility != Visibilities.LOCAL) {
@@ -76,7 +77,7 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackend
// It is an error to annotate only some of the fields of an interface companion with @JvmField. // It is an error to annotate only some of the fields of an interface companion with @JvmField.
val newParent = if (irClass.isJvmInterface && !companion.allFieldsAreJvmField()) companion else irClass val newParent = if (irClass.isJvmInterface && !companion.allFieldsAreJvmField()) companion else irClass
val newDeclarations = companion.declarations.mapNotNull { val newDeclarations = companion.declarations.map {
when (it) { when (it) {
is IrProperty -> is IrProperty ->
movePropertyFieldToStaticParent(it, companion, newParent, fieldReplacementMap) movePropertyFieldToStaticParent(it, companion, newParent, fieldReplacementMap)
@@ -87,10 +88,16 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackend
} }
} }
// Move declarations to parent if required if (newParent === companion) {
if (newParent !== companion) { // Keep fields as children of `IrProperty`, but replace anonymous initializers with static ones,
// preserving the relative ordering of anonymous initializers and property initializers.
for ((i, declaration) in newDeclarations.withIndex())
if (declaration is IrAnonymousInitializer)
companion.declarations[i] = declaration
} else {
// Move all touched declarations to the parent.
companion.declarations.removeAll { it is IrAnonymousInitializer } companion.declarations.removeAll { it is IrAnonymousInitializer }
newParent.declarations += newDeclarations newDeclarations.filterNotNullTo(newParent.declarations)
} }
} }
@@ -153,57 +160,12 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: CommonBackend
isStatic = true isStatic = true
).apply { ).apply {
parent = newParent parent = newParent
body = oldInitializer.body.transferToNewParent(oldParent, newParent) body = oldInitializer.body
.replaceThisByStaticReference(context, oldParent, oldParent.thisReceiver!!)
.patchDeclarationParents(newParent) as IrBlockBody
} }
} }
private fun IrBlockBody.transferToNewParent(oldParent: IrClass, newParent: IrClass): IrBlockBody {
val objectInstanceField = context.declarationFactory.getFieldForObjectInstance(oldParent)
return transform(
data = null,
transformer = object : IrElementTransformerVoid() {
val variableMap = mutableMapOf<IrVariable, IrVariable>()
override fun visitVariable(declaration: IrVariable): IrStatement {
if (declaration.parent == oldParent) {
val newDescriptor = WrappedVariableDescriptor(declaration.descriptor.annotations, declaration.descriptor.source)
val newVariable = IrVariableImpl(
declaration.startOffset, declaration.endOffset,
declaration.origin, IrVariableSymbolImpl(newDescriptor),
declaration.name, declaration.type, declaration.isVar, declaration.isConst, declaration.isLateinit
).apply {
newDescriptor.bind(this)
parent = newParent
initializer = declaration.initializer
annotations.addAll(declaration.annotations)
}
variableMap[declaration] = newVariable
return super.visitVariable(newVariable)
}
return super.visitVariable(declaration)
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.symbol.owner == oldParent.thisReceiver) {
return IrGetFieldImpl(
expression.startOffset, expression.endOffset,
objectInstanceField.symbol,
expression.type
)
}
variableMap[expression.symbol.owner]?.let { newVariable ->
return IrGetValueImpl(
expression.startOffset, expression.endOffset,
expression.type,
newVariable.symbol,
expression.origin
)
}
return super.visitGetValue(expression)
}
}) as IrBlockBody
}
private fun createStaticBackingField(oldField: IrField, propertyParent: IrClass, fieldParent: IrClass): IrField { private fun createStaticBackingField(oldField: IrField, propertyParent: IrClass, fieldParent: IrClass): IrField {
val descriptor = WrappedFieldDescriptor(oldField.descriptor.annotations, oldField.descriptor.source) val descriptor = WrappedFieldDescriptor(oldField.descriptor.annotations, oldField.descriptor.source)
val field = IrFieldImpl( val field = IrFieldImpl(
@@ -10,17 +10,11 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irExprBody import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.builders.irGetField import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
internal val objectClassPhase = makeIrFilePhase( internal val objectClassPhase = makeIrFilePhase(
@@ -49,39 +43,20 @@ private class ObjectClassLowering(val context: JvmBackendContext) : IrElementTra
if (!irClass.isObject) return if (!irClass.isObject) return
val publicInstanceField = context.declarationFactory.getFieldForObjectInstance(irClass) val publicInstanceField = context.declarationFactory.getFieldForObjectInstance(irClass)
val privateInstanceField = context.declarationFactory.getPrivateFieldForObjectInstance(irClass)
val constructor = irClass.constructors.find { it.isPrimary } val constructor = irClass.constructors.find { it.isPrimary }
?: throw AssertionError("Object should have a primary constructor: ${irClass.name}") ?: throw AssertionError("Object should have a primary constructor: ${irClass.name}")
val publicInstanceOwner = if (irClass.isCompanion) parentScope!!.irElement as IrDeclarationContainer else irClass if (privateInstanceField != publicInstanceField) {
if (irClass.isCompanion && irClass.parentAsClass.isJvmInterface) { with(context.createIrBuilder(privateInstanceField.symbol)) {
// TODO rename to $$INSTANCE privateInstanceField.initializer = irExprBody(irCall(constructor.symbol))
// TODO: mark as synthesized
val privateFieldDescriptor = WrappedFieldDescriptor()
val privateField = IrFieldImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE,
IrFieldSymbolImpl(privateFieldDescriptor),
publicInstanceField.name,
irClass.defaultType,
Visibilities.PROTECTED/* TODO package local */,
isFinal = true,
isExternal = false,
isStatic = true,
isFakeOverride = false
).apply {
privateFieldDescriptor.bind(this)
parent = irClass
with(context.createIrBuilder(symbol)) {
initializer = irExprBody(
irCall(constructor)
)
}
pendingTransformations.add { parentAsClass.declarations.add(this) }
} }
with(context.createIrBuilder(publicInstanceField.symbol)) { with(context.createIrBuilder(publicInstanceField.symbol)) {
publicInstanceField.initializer = irExprBody(irGetField(null, privateField)) publicInstanceField.initializer = irExprBody(irGetField(null, privateInstanceField))
}
pendingTransformations.add {
(privateInstanceField.parent as IrDeclarationContainer).declarations.add(0, privateInstanceField)
} }
} else { } else {
with(context.createIrBuilder(publicInstanceField.symbol)) { with(context.createIrBuilder(publicInstanceField.symbol)) {
@@ -89,9 +64,8 @@ private class ObjectClassLowering(val context: JvmBackendContext) : IrElementTra
} }
} }
publicInstanceField.parent = publicInstanceOwner
pendingTransformations.add { pendingTransformations.add {
publicInstanceOwner.declarations.add(publicInstanceField) (publicInstanceField.parent as IrDeclarationContainer).declarations.add(0, publicInstanceField)
} }
} }
} }
@@ -52,18 +52,28 @@ private class SingletonReferencesLowering(val context: JvmBackendContext) : File
} }
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
val instanceField = context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner) // When in an instance method of the object, use the dispatch receiver. Do not use the dispatch
// receiver in the constructor, as in the case of an `object` the only way it can be used is
// in a super constructor call argument (=> it has JVM type "uninitialized this" and is not usable)
// while for a `companion object` all initializers are in `<clinit>` and have no receiver at all.
thisOfClass(expression.symbol.owner, false)?.let {
return IrGetValueImpl(expression.startOffset, expression.endOffset, it.symbol)
}
val instanceField = if (allScopes.any { it.irElement == expression.symbol.owner })
context.declarationFactory.getPrivateFieldForObjectInstance(expression.symbol.owner) // Constructor or static method.
else
context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner) // Not in object scope at all.
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type) return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
} }
// `this` is generally available while the reference is within the lexical scope of the containing enum entry. // `this` is generally available while the reference is within the lexical scope of the containing class.
private fun thisOfClass(declaration: IrClass?): IrValueParameter? { private fun thisOfClass(declaration: IrClass?, allowConstructorReceiver: Boolean = true): IrValueParameter? {
if (declaration == null) return null if (declaration == null) return null
for (scope in allScopes.reversed()) { for (scope in allScopes.reversed()) {
when (val element = scope.irElement) { when (val element = scope.irElement) {
is IrFunction -> is IrFunction ->
element.dispatchReceiverParameter?.let { if (it.type.classOrNull == declaration.symbol) return it } element.dispatchReceiverParameter?.let { if (it.type.classOrNull == declaration.symbol) return it }
is IrClass -> if (element == declaration) return element.thisReceiver is IrClass -> if (allowConstructorReceiver && element == declaration) return element.thisReceiver
} }
} }
return null return null
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// Inside of the companion we have to access the instance through the local Companion field, // Inside of the companion we have to access the instance through the local Companion field,
// not by indirection through the Companion field of the enclosing class. // not by indirection through the Companion field of the enclosing class.
// Class initialization might not have finished yet. // Class initialization might not have finished yet.
@@ -1,6 +1,5 @@
// !LANGUAGE: +NestedClassesInAnnotations // !LANGUAGE: +NestedClassesInAnnotations
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
interface Test { interface Test {
companion object { companion object {
fun ok() = "OK" fun ok() = "OK"