FIR2IR: find & use delegate-to members properly

This commit is contained in:
Mikhail Glukhikh
2021-12-03 15:34:06 +03:00
parent a919351d07
commit 7cfec0d846
6 changed files with 128 additions and 76 deletions
@@ -10,14 +10,17 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
import org.jetbrains.kotlin.fir.scopes.processAllProperties
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.lowerBoundIfFlexible
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
@@ -31,6 +34,7 @@ import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.JvmNames.JVM_DEFAULT_CLASS_ID
import org.jetbrains.kotlin.name.Name
/**
* A generator for delegated members from implementation by delegation.
@@ -43,34 +47,31 @@ class DelegatedMemberGenerator(
private val components: Fir2IrComponents
) : Fir2IrComponents by components {
companion object {
private val PLATFORM_DEPENDENT_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.PlatformDependent"))
}
private val baseFunctionSymbols = mutableMapOf<IrFunction, List<FirNamedFunctionSymbol>>()
private val basePropertySymbols = mutableMapOf<IrProperty, List<FirPropertySymbol>>()
private data class DeclarationBodyInfo(
val declaration: IrDeclaration,
val field: IrField,
val delegateToSymbol: FirCallableSymbol<*>
val delegateToSymbol: FirCallableSymbol<*>,
val delegateToLookupTag: ConeClassLikeLookupTag?
)
private val bodiesInfo = mutableListOf<DeclarationBodyInfo>()
fun generateBodies() {
for ((declaration, irField, delegateToSymbol) in bodiesInfo) {
for ((declaration, irField, delegateToSymbol, delegateToLookupTag) in bodiesInfo) {
when (declaration) {
is IrSimpleFunction -> {
val member = declarationStorage.getIrFunctionSymbol(
delegateToSymbol as FirNamedFunctionSymbol
delegateToSymbol as FirNamedFunctionSymbol, delegateToLookupTag
).owner as? IrSimpleFunction ?: continue
val body = createDelegateBody(irField, declaration, member)
declaration.body = body
}
is IrProperty -> {
val member = declarationStorage.getIrPropertySymbol(
delegateToSymbol as FirPropertySymbol
delegateToSymbol as FirPropertySymbol, delegateToLookupTag
).owner as? IrProperty ?: continue
val getter = declaration.getter!!
getter.body = createDelegateBody(irField, getter, member.getter!!)
@@ -84,25 +85,43 @@ class DelegatedMemberGenerator(
bodiesInfo.clear()
}
private fun FirTypeParameter.boundClass(): FirClass {
val boundType = bounds.first().coneType.fullyExpandedType(session).lowerBoundIfFlexible()
val boundClassifier = boundType.toSymbol(session)!!.fir
if (boundClassifier is FirClass) return boundClassifier
return (boundClassifier as FirTypeParameter).boundClass()
}
// Generate delegated members for [subClass]. The synthetic field [irField] has the super interface type.
fun generate(irField: IrField, firField: FirField, firSubClass: FirClass, subClass: IrClass) {
val subClassLookupTag = firSubClass.symbol.toLookupTag()
val subClassScope = firSubClass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = true)
val subClassScope = firSubClass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false)
val delegateToType = firField.initializer!!.typeRef.coneType.fullyExpandedType(session).lowerBoundIfFlexible()
val delegateToClass = when (val fir = delegateToType.toSymbol(session)?.fir) {
is FirClass -> fir
is FirTypeParameter -> fir.boundClass()
else -> throw AssertionError("${fir?.render()}")
}
val delegateToScope = delegateToClass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false)
val delegateToLookupTag = (delegateToType as? ConeClassLikeType)?.lookupTag
subClassScope.processAllFunctions { functionSymbol ->
val unwrapped =
functionSymbol
.unwrapDelegateTarget(subClassLookupTag, firField)
functionSymbol.unwrapDelegateTarget(subClassLookupTag, firField)
?: return@processAllFunctions
if (shouldSkipDelegationFor(unwrapped)) {
return@processAllFunctions
}
val delegateToSymbol = findDelegateToSymbol(
unwrapped.symbol,
delegateToScope::processFunctionsByName,
delegateToScope::processOverriddenFunctions
) ?: return@processAllFunctions
val irSubFunction = generateDelegatedFunction(
subClass, firSubClass, functionSymbol.fir
)
bodiesInfo += DeclarationBodyInfo(irSubFunction, irField, unwrapped.symbol)
bodiesInfo += DeclarationBodyInfo(irSubFunction, irField, delegateToSymbol, delegateToLookupTag)
declarationStorage.cacheDelegationFunction(functionSymbol.fir, irSubFunction)
subClass.addMember(irSubFunction)
}
@@ -111,42 +130,54 @@ class DelegatedMemberGenerator(
if (propertySymbol !is FirPropertySymbol) return@processAllProperties
val unwrapped =
propertySymbol
.unwrapDelegateTarget(subClassLookupTag, firField)
propertySymbol.unwrapDelegateTarget(subClassLookupTag, firField)
?: return@processAllProperties
if (shouldSkipDelegationFor(unwrapped)) {
return@processAllProperties
}
val delegateToSymbol = findDelegateToSymbol(
unwrapped.symbol,
{ name, processor ->
delegateToScope.processPropertiesByName(name) {
if (it !is FirPropertySymbol) return@processPropertiesByName
processor(it)
}
},
delegateToScope::processOverriddenProperties
) ?: return@processAllProperties
val irSubProperty = generateDelegatedProperty(
subClass, firSubClass, propertySymbol.fir
)
bodiesInfo += DeclarationBodyInfo(irSubProperty, irField, unwrapped.symbol)
bodiesInfo += DeclarationBodyInfo(irSubProperty, irField, delegateToSymbol, delegateToLookupTag)
declarationStorage.cacheDelegatedProperty(propertySymbol.fir, irSubProperty)
subClass.addMember(irSubProperty)
}
}
private fun shouldSkipDelegationFor(unwrapped: FirCallableDeclaration): Boolean {
// See org.jetbrains.kotlin.resolve.jvm.JvmDelegationFilter
return (unwrapped is FirSimpleFunction && unwrapped.isDefaultJavaMethod()) ||
unwrapped.hasAnnotation(JVM_DEFAULT_CLASS_ID) ||
unwrapped.hasAnnotation(PLATFORM_DEPENDENT_CLASS_ID)
}
private fun FirSimpleFunction.isDefaultJavaMethod(): Boolean =
when {
isIntersectionOverride ->
baseForIntersectionOverride!!.isDefaultJavaMethod()
isSubstitutionOverride ->
originalForSubstitutionOverride!!.isDefaultJavaMethod()
else -> {
// Check that we have a non-abstract method from Java interface
origin == FirDeclarationOrigin.Enhancement && modality == Modality.OPEN
private inline fun <reified S : FirCallableSymbol<*>> findDelegateToSymbol(
unwrappedSymbol: S,
processCallables: (name: Name, processor: (S) -> Unit) -> Unit,
crossinline processOverridden: (base: S, processor: (S) -> ProcessorAction) -> ProcessorAction
): S? {
var result: S? = null
// The purpose of this code is to find member in delegate-to scope
// which matches or overrides unwrappedSymbol (which is in turn taken from subclass scope).
processCallables(unwrappedSymbol.name) { candidateSymbol ->
if (result != null) return@processCallables
if (candidateSymbol === unwrappedSymbol) {
result = candidateSymbol
return@processCallables
}
processOverridden(candidateSymbol) {
if (it === unwrappedSymbol) {
result = candidateSymbol
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
}
return result
}
fun bindDelegatedMembersOverriddenSymbols(irClass: IrClass) {
val superClasses by lazy(LazyThreadSafetyMode.NONE) {
@@ -275,36 +306,58 @@ class DelegatedMemberGenerator(
declarationStorage,
fakeOverrideGenerator
)
annotationGenerator.generate(delegateProperty.getter!!, firDelegateProperty)
annotationGenerator.generate(getter, firDelegateProperty)
if (delegateProperty.isVar) {
val setter = delegateProperty.setter!!
setter.overriddenSymbols =
firDelegateProperty.generateOverriddenAccessorSymbols(
firSubClass, isGetter = false, session, scopeSession, declarationStorage, fakeOverrideGenerator
)
annotationGenerator.generate(delegateProperty.setter!!, firDelegateProperty)
annotationGenerator.generate(setter, firDelegateProperty)
}
return delegateProperty
}
companion object {
private val PLATFORM_DEPENDENT_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.PlatformDependent"))
private fun <S : FirCallableSymbol<D>, D : FirCallableDeclaration> S.unwrapDelegateTarget(
subClassLookupTag: ConeClassLikeLookupTag,
firField: FirField,
): D? {
val callable = this.fir as? D ?: return null
val delegatedWrapperData = callable.delegatedWrapperData ?: return null
if (delegatedWrapperData.containingClass != subClassLookupTag) return null
if (delegatedWrapperData.delegateField != firField) return null
val wrapped = delegatedWrapperData.wrapped as? D ?: return null
@Suppress("UNCHECKED_CAST")
val wrappedSymbol = wrapped.symbol as? S ?: return null
@Suppress("UNCHECKED_CAST")
return (wrappedSymbol.unwrapCallRepresentative().fir as D).takeIf { !shouldSkipDelegationFor(it) }
}
private fun shouldSkipDelegationFor(unwrapped: FirCallableDeclaration): Boolean {
// See org.jetbrains.kotlin.resolve.jvm.JvmDelegationFilter
return (unwrapped is FirSimpleFunction && unwrapped.isDefaultJavaMethod()) ||
unwrapped.hasAnnotation(JVM_DEFAULT_CLASS_ID) ||
unwrapped.hasAnnotation(PLATFORM_DEPENDENT_CLASS_ID)
}
private fun FirSimpleFunction.isDefaultJavaMethod(): Boolean =
when {
isIntersectionOverride ->
baseForIntersectionOverride!!.isDefaultJavaMethod()
isSubstitutionOverride ->
originalForSubstitutionOverride!!.isDefaultJavaMethod()
else -> {
// Check that we have a non-abstract method from Java interface
origin == FirDeclarationOrigin.Enhancement && modality == Modality.OPEN
}
}
}
}
private fun <S : FirCallableSymbol<D>, D : FirCallableDeclaration> S.unwrapDelegateTarget(
subClassLookupTag: ConeClassLikeLookupTag,
firField: FirField,
): D? {
val callable = this.fir as? D ?: return null
val delegatedWrapperData = callable.delegatedWrapperData ?: return null
if (delegatedWrapperData.containingClass != subClassLookupTag) return null
if (delegatedWrapperData.delegateField != firField) return null
val wrapped = delegatedWrapperData.wrapped as? D ?: return null
@Suppress("UNCHECKED_CAST")
val wrappedSymbol = wrapped.symbol as? S ?: return null
@Suppress("UNCHECKED_CAST")
return wrappedSymbol.unwrapCallRepresentative().fir as D
}
@@ -215,7 +215,7 @@ FILE fqName:<root> fileName:/delegatedImplementation.kt
VALUE_PARAMETER name:x index:0 type:kotlin.Int
VALUE_PARAMETER name:s index:1 type:kotlin.String
BLOCK_BODY
CALL 'public abstract fun foo (x: kotlin.Int, s: kotlin.String): kotlin.Unit declared in <root>.IBase' type=kotlin.Unit origin=null
CALL 'public final fun foo (x: kotlin.Int, s: kotlin.String): kotlin.Unit declared in <root>.BaseImpl' type=kotlin.Unit origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.BaseImpl visibility:local [final]' type=<root>.BaseImpl origin=null
receiver: GET_VAR '<this>: <root>.Test1 declared in <root>.Test1.foo' type=<root>.Test1 origin=null
x: GET_VAR 'x: kotlin.Int declared in <root>.Test1.foo' type=kotlin.Int origin=null
@@ -226,7 +226,7 @@ FILE fqName:<root> fileName:/delegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Test1
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun bar (): kotlin.Int declared in <root>.Test1'
CALL 'public abstract fun bar (): kotlin.Int declared in <root>.IBase' type=kotlin.Int origin=null
CALL 'public final fun bar (): kotlin.Int declared in <root>.BaseImpl' type=kotlin.Int origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.BaseImpl visibility:local [final]' type=<root>.BaseImpl origin=null
receiver: GET_VAR '<this>: <root>.Test1 declared in <root>.Test1.bar' type=<root>.Test1 origin=null
FUN DELEGATED_MEMBER name:qux visibility:public modality:OPEN <> ($this:<root>.Test1, $receiver:kotlin.String) returnType:kotlin.Unit
@@ -235,7 +235,7 @@ FILE fqName:<root> fileName:/delegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Test1
$receiver: VALUE_PARAMETER name:<this> type:kotlin.String
BLOCK_BODY
CALL 'public abstract fun qux (): kotlin.Unit declared in <root>.IBase' type=kotlin.Unit origin=null
CALL 'public final fun qux (): kotlin.Unit declared in <root>.BaseImpl' type=kotlin.Unit origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.BaseImpl visibility:local [final]' type=<root>.BaseImpl origin=null
receiver: GET_VAR '<this>: <root>.Test1 declared in <root>.Test1.qux' type=<root>.Test1 origin=null
$receiver: GET_VAR '<this>: kotlin.String declared in <root>.Test1.qux' type=kotlin.String origin=null
@@ -268,7 +268,7 @@ FILE fqName:<root> fileName:/delegatedImplementation.kt
VALUE_PARAMETER name:x index:0 type:kotlin.Int
VALUE_PARAMETER name:s index:1 type:kotlin.String
BLOCK_BODY
CALL 'public abstract fun foo (x: kotlin.Int, s: kotlin.String): kotlin.Unit declared in <root>.IBase' type=kotlin.Unit origin=null
CALL 'public final fun foo (x: kotlin.Int, s: kotlin.String): kotlin.Unit declared in <root>.BaseImpl' type=kotlin.Unit origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.BaseImpl visibility:local [final]' type=<root>.BaseImpl origin=null
receiver: GET_VAR '<this>: <root>.Test2 declared in <root>.Test2.foo' type=<root>.Test2 origin=null
x: GET_VAR 'x: kotlin.Int declared in <root>.Test2.foo' type=kotlin.Int origin=null
@@ -279,7 +279,7 @@ FILE fqName:<root> fileName:/delegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Test2
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun bar (): kotlin.Int declared in <root>.Test2'
CALL 'public abstract fun bar (): kotlin.Int declared in <root>.IBase' type=kotlin.Int origin=null
CALL 'public final fun bar (): kotlin.Int declared in <root>.BaseImpl' type=kotlin.Int origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.BaseImpl visibility:local [final]' type=<root>.BaseImpl origin=null
receiver: GET_VAR '<this>: <root>.Test2 declared in <root>.Test2.bar' type=<root>.Test2 origin=null
FUN DELEGATED_MEMBER name:qux visibility:public modality:OPEN <> ($this:<root>.Test2, $receiver:kotlin.String) returnType:kotlin.Unit
@@ -288,7 +288,7 @@ FILE fqName:<root> fileName:/delegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Test2
$receiver: VALUE_PARAMETER name:<this> type:kotlin.String
BLOCK_BODY
CALL 'public abstract fun qux (): kotlin.Unit declared in <root>.IBase' type=kotlin.Unit origin=null
CALL 'public final fun qux (): kotlin.Unit declared in <root>.BaseImpl' type=kotlin.Unit origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.BaseImpl visibility:local [final]' type=<root>.BaseImpl origin=null
receiver: GET_VAR '<this>: <root>.Test2 declared in <root>.Test2.qux' type=<root>.Test2 origin=null
$receiver: GET_VAR '<this>: kotlin.String declared in <root>.Test2.qux' type=kotlin.String origin=null
@@ -58,7 +58,7 @@ FILE fqName:<root> fileName:/delegatedImplementationWithExplicitOverride.kt
public abstract fun foo (): kotlin.Unit declared in <root>.IFooBar
$this: VALUE_PARAMETER name:<this> type:<root>.C
BLOCK_BODY
CALL 'public abstract fun foo (): kotlin.Unit declared in <root>.IFooBar' type=kotlin.Unit origin=null
CALL 'public final fun foo (): kotlin.Unit declared in <root>.FooBarImpl' type=kotlin.Unit origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.FooBarImpl visibility:local [final]' type=<root>.FooBarImpl origin=null
receiver: GET_VAR '<this>: <root>.C declared in <root>.C.foo' type=<root>.C origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.FooBarImpl visibility:local [final]
@@ -137,7 +137,7 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestJFoo
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestJFoo'
CALL 'public abstract fun foo (): kotlin.String declared in <root>.IFoo' type=kotlin.String origin=null
CALL 'public open fun foo (): @[EnhancedNullability] kotlin.String declared in <root>.JFoo' type=kotlin.String origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.JFoo visibility:local [final]' type=<root>.JFoo origin=null
receiver: GET_VAR '<this>: <root>.TestJFoo declared in <root>.TestJFoo.foo' type=<root>.TestJFoo origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.JFoo visibility:local [final]
@@ -168,7 +168,7 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestK1
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK1'
CALL 'public abstract fun foo (): kotlin.String declared in <root>.IFoo' type=kotlin.String origin=null
CALL 'public open fun foo (): @[EnhancedNullability] kotlin.String [fake_override] declared in <root>.K1' type=kotlin.String origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.K1 visibility:local [final]' type=<root>.K1 origin=null
receiver: GET_VAR '<this>: <root>.TestK1 declared in <root>.TestK1.foo' type=<root>.TestK1 origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.K1 visibility:local [final]
@@ -199,7 +199,7 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestK2
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK2'
CALL 'public abstract fun foo (): kotlin.String declared in <root>.IFoo' type=kotlin.String origin=null
CALL 'public final fun foo (): kotlin.String declared in <root>.K2' type=kotlin.String origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.K2 visibility:local [final]' type=<root>.K2 origin=null
receiver: GET_VAR '<this>: <root>.TestK2 declared in <root>.TestK2.foo' type=<root>.TestK2 origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.K2 visibility:local [final]
@@ -230,7 +230,7 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestK3
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK3'
CALL 'public abstract fun foo (): kotlin.String declared in <root>.IFoo' type=kotlin.String origin=null
CALL 'public open fun foo (): kotlin.String [fake_override] declared in <root>.K3' type=kotlin.String origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.K3 visibility:local [final]' type=<root>.K3 origin=null
receiver: GET_VAR '<this>: <root>.TestK3 declared in <root>.TestK3.foo' type=<root>.TestK3 origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.K3 visibility:local [final]
@@ -261,7 +261,7 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestK4
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK4'
CALL 'public abstract fun foo (): kotlin.String declared in <root>.IFoo' type=kotlin.String origin=null
CALL 'public final fun foo (): @[FlexibleNullability] kotlin.String? declared in <root>.K4' type=kotlin.String origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.K4 visibility:local [final]' type=<root>.K4 origin=null
receiver: GET_VAR '<this>: <root>.TestK4 declared in <root>.TestK4.foo' type=<root>.TestK4 origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.K4 visibility:local [final]
+1 -2
View File
@@ -13,8 +13,7 @@ FILE fqName:<root> fileName:/kt45934.kt
$this: VALUE_PARAMETER name:<this> type:<root>.C
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo <C> (): @[FlexibleNullability] kotlin.collections.List<@[FlexibleNullability] C of <root>.C.foo?>? declared in <root>.C'
CALL 'public abstract fun foo <C> (): @[FlexibleNullability] kotlin.collections.List<@[FlexibleNullability] C of <root>.I.foo?>? declared in <root>.I' type=@[FlexibleNullability] kotlin.collections.List<@[FlexibleNullability] C of <root>.C.foo?>? origin=null
<C>: C of <root>.C.foo
CALL 'public open fun foo (): @[FlexibleNullability] kotlin.collections.List<@[FlexibleNullability] kotlin.String?>? declared in <root>.J' type=@[FlexibleNullability] kotlin.collections.List<@[FlexibleNullability] C of <root>.C.foo?>? origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.J visibility:local [final]' type=<root>.J origin=null
receiver: GET_VAR '<this>: <root>.C declared in <root>.C.foo' type=<root>.C origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.J visibility:local [final]
@@ -61,7 +61,7 @@ FILE fqName:<root> fileName:/nullCheckOnInterfaceDelegation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Delegated
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.Delegated'
CALL 'public abstract fun foo (): kotlin.String declared in <root>.IFoo' type=kotlin.String origin=null
CALL 'public final fun foo (): @[FlexibleNullability] kotlin.String? declared in <root>.Derived' type=kotlin.String origin=null
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.Derived visibility:local [final]' type=<root>.Derived origin=null
receiver: GET_VAR '<this>: <root>.Delegated declared in <root>.Delegated.foo' type=<root>.Delegated origin=null
FIELD DELEGATE name:<$$delegate_0> type:<root>.Derived visibility:local [final]