FIR2IR: insert implicit not-null casts in delegated calls

This commit is contained in:
Mikhail Glukhikh
2021-12-02 19:42:28 +03:00
parent 7cfec0d846
commit fa8441fb23
10 changed files with 100 additions and 80 deletions
@@ -99,7 +99,7 @@ class Fir2IrImplicitCastInserter(
override fun visitExpression(expression: FirExpression, data: IrElement): IrElement { override fun visitExpression(expression: FirExpression, data: IrElement): IrElement {
return when (expression) { return when (expression) {
is FirBlock -> (data as IrContainerExpression).insertImplicitCasts() is FirBlock -> (data as IrContainerExpression).insertImplicitCasts()
is FirUnitExpression -> (data as IrExpression).let { it.coerceToUnitIfNeeded(it.type, irBuiltIns) } is FirUnitExpression -> (data as IrExpression).let { coerceToUnitIfNeeded(it, irBuiltIns) }
else -> data else -> data
} }
} }
@@ -108,7 +108,7 @@ class Fir2IrImplicitCastInserter(
return when (statement) { return when (statement) {
is FirTypeAlias -> data is FirTypeAlias -> data
FirStubStatement -> data FirStubStatement -> data
is FirUnitExpression -> (data as IrExpression).let { it.coerceToUnitIfNeeded(it.type, irBuiltIns) } is FirUnitExpression -> (data as IrExpression).let { coerceToUnitIfNeeded(it, irBuiltIns) }
is FirBlock -> (data as IrContainerExpression).insertImplicitCasts() is FirBlock -> (data as IrContainerExpression).insertImplicitCasts()
else -> statement.accept(this, data) else -> statement.accept(this, data)
} }
@@ -203,12 +203,9 @@ class Fir2IrImplicitCastInserter(
insertImplicitCasts() insertImplicitCasts()
} }
expectedType.isUnit -> { expectedType.isUnit -> {
coerceToUnitIfNeeded(type, irBuiltIns) coerceToUnitIfNeeded(this, irBuiltIns)
} }
valueType.isNullabilityFlexible() && valueType.canBeNull && !expectedType.acceptsNullValues() -> { typeCanBeEnhancedOrFlexibleNullable(valueType) && !expectedType.acceptsNullValues() -> {
insertImplicitNotNullCastIfNeeded(expression)
}
valueType.hasEnhancedNullability() && !expectedType.acceptsNullValues() -> {
insertImplicitNotNullCastIfNeeded(expression) insertImplicitNotNullCastIfNeeded(expression)
} }
// TODO: coerceIntToAnotherIntegerType // TODO: coerceIntToAnotherIntegerType
@@ -217,11 +214,6 @@ class Fir2IrImplicitCastInserter(
} }
} }
private fun FirTypeRef.isNullabilityFlexible(): Boolean {
val flexibility = coneTypeSafe<ConeFlexibleType>() ?: return false
return flexibility.lowerBound.isMarkedNullable != flexibility.upperBound.isMarkedNullable
}
private fun FirTypeRef.acceptsNullValues(): Boolean = private fun FirTypeRef.acceptsNullValues(): Boolean =
canBeNull || hasEnhancedNullability() canBeNull || hasEnhancedNullability()
@@ -230,21 +222,7 @@ class Fir2IrImplicitCastInserter(
// [TypeOperatorLowering] will retrieve the source (from start offset to end offset) as an assertion message. // [TypeOperatorLowering] will retrieve the source (from start offset to end offset) as an assertion message.
// Avoid type casting if we can't determine the source for some reasons, e.g., implicit `this` receiver. // Avoid type casting if we can't determine the source for some reasons, e.g., implicit `this` receiver.
if (expression.source == null) return this if (expression.source == null) return this
// Cast type massage 1. Remove @EnhancedNullability return implicitNotNullCast(this)
// Cast type massage 2. Convert it to a non-null variant (in case of @FlexibleNullability)
val castType = type.removeAnnotations {
val classId = it.symbol.owner.parentAsClass.classId
classId == StandardClassIds.Annotations.EnhancedNullability ||
classId == StandardClassIds.Annotations.FlexibleNullability
}.withHasQuestionMark(false)
return IrTypeOperatorCallImpl(
this.startOffset,
this.endOffset,
castType,
IrTypeOperator.IMPLICIT_NOTNULL,
castType,
this
)
} }
private fun IrContainerExpression.insertImplicitCasts(): IrContainerExpression { private fun IrContainerExpression.insertImplicitCasts(): IrContainerExpression {
@@ -254,7 +232,7 @@ class Fir2IrImplicitCastInserter(
statements.forEachIndexed { i, irStatement -> statements.forEachIndexed { i, irStatement ->
if (irStatement !is IrErrorCallExpression && irStatement is IrExpression) { if (irStatement !is IrErrorCallExpression && irStatement is IrExpression) {
if (i != lastIndex) { if (i != lastIndex) {
statements[i] = irStatement.coerceToUnitIfNeeded(irStatement.type, irBuiltIns) statements[i] = coerceToUnitIfNeeded(irStatement, irBuiltIns)
} }
// TODO: for the last statement, need to cast to the return type if mismatched // TODO: for the last statement, need to cast to the return type if mismatched
} }
@@ -268,7 +246,7 @@ class Fir2IrImplicitCastInserter(
statements.forEachIndexed { i, irStatement -> statements.forEachIndexed { i, irStatement ->
if (irStatement !is IrErrorCallExpression && irStatement is IrExpression) { if (irStatement !is IrErrorCallExpression && irStatement is IrExpression) {
statements[i] = irStatement.coerceToUnitIfNeeded(irStatement.type, irBuiltIns) statements[i] = coerceToUnitIfNeeded(irStatement, irBuiltIns)
} }
} }
return this return this
@@ -347,27 +325,61 @@ class Fir2IrImplicitCastInserter(
return implicitCast(original, castType) return implicitCast(original, castType)
} }
private fun implicitCast(original: IrExpression, castType: IrType): IrExpression { companion object {
return IrTypeOperatorCallImpl( private fun implicitCast(original: IrExpression, castType: IrType): IrExpression {
original.startOffset, return IrTypeOperatorCallImpl(
original.endOffset, original.startOffset,
castType, original.endOffset,
IrTypeOperator.IMPLICIT_CAST, castType,
castType, IrTypeOperator.IMPLICIT_CAST,
original castType,
) original
}
private fun IrExpression.coerceToUnitIfNeeded(valueType: IrType, irBuiltIns: IrBuiltIns): IrExpression {
return if (valueType.isUnit() || valueType.isNothing())
this
else
IrTypeOperatorCallImpl(
startOffset, endOffset,
irBuiltIns.unitType,
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
irBuiltIns.unitType,
this
) )
}
private fun coerceToUnitIfNeeded(original: IrExpression, irBuiltIns: IrBuiltIns): IrExpression {
val valueType = original.type
return if (valueType.isUnit() || valueType.isNothing())
original
else
IrTypeOperatorCallImpl(
original.startOffset, original.endOffset,
irBuiltIns.unitType,
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
irBuiltIns.unitType,
original
)
}
internal fun implicitNotNullCast(original: IrExpression): IrTypeOperatorCall {
// Cast type massage 1. Remove @EnhancedNullability
// Cast type massage 2. Convert it to a non-null variant (in case of @FlexibleNullability)
val castType = original.type.removeAnnotations {
val classId = it.symbol.owner.parentAsClass.classId
classId == StandardClassIds.Annotations.EnhancedNullability ||
classId == StandardClassIds.Annotations.FlexibleNullability
}.withHasQuestionMark(false)
return IrTypeOperatorCallImpl(
original.startOffset,
original.endOffset,
castType,
IrTypeOperator.IMPLICIT_NOTNULL,
castType,
original
)
}
internal fun typeCanBeEnhancedOrFlexibleNullable(typeRef: FirTypeRef): Boolean {
return when {
typeRef.hasEnhancedNullability() -> true
typeRef.isNullabilityFlexible() && typeRef.canBeNull -> true
else -> false
}
}
private fun FirTypeRef.isNullabilityFlexible(): Boolean {
val flexibility = coneTypeSafe<ConeFlexibleType>() ?: return false
return flexibility.lowerBound.isMarkedNullable != flexibility.upperBound.isMarkedNullable
}
} }
} }
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -61,12 +62,13 @@ class DelegatedMemberGenerator(
fun generateBodies() { fun generateBodies() {
for ((declaration, irField, delegateToSymbol, delegateToLookupTag) in bodiesInfo) { for ((declaration, irField, delegateToSymbol, delegateToLookupTag) in bodiesInfo) {
val callTypeCanBeNullable = Fir2IrImplicitCastInserter.typeCanBeEnhancedOrFlexibleNullable(delegateToSymbol.fir.returnTypeRef)
when (declaration) { when (declaration) {
is IrSimpleFunction -> { is IrSimpleFunction -> {
val member = declarationStorage.getIrFunctionSymbol( val member = declarationStorage.getIrFunctionSymbol(
delegateToSymbol as FirNamedFunctionSymbol, delegateToLookupTag delegateToSymbol as FirNamedFunctionSymbol, delegateToLookupTag
).owner as? IrSimpleFunction ?: continue ).owner as? IrSimpleFunction ?: continue
val body = createDelegateBody(irField, declaration, member) val body = createDelegateBody(irField, declaration, member, callTypeCanBeNullable)
declaration.body = body declaration.body = body
} }
is IrProperty -> { is IrProperty -> {
@@ -74,10 +76,10 @@ class DelegatedMemberGenerator(
delegateToSymbol as FirPropertySymbol, delegateToLookupTag delegateToSymbol as FirPropertySymbol, delegateToLookupTag
).owner as? IrProperty ?: continue ).owner as? IrProperty ?: continue
val getter = declaration.getter!! val getter = declaration.getter!!
getter.body = createDelegateBody(irField, getter, member.getter!!) getter.body = createDelegateBody(irField, getter, member.getter!!, callTypeCanBeNullable)
if (declaration.isVar) { if (declaration.isVar) {
val setter = declaration.setter!! val setter = declaration.setter!!
setter.body = createDelegateBody(irField, setter, member.setter!!) setter.body = createDelegateBody(irField, setter, member.setter!!, false)
} }
} }
} }
@@ -226,7 +228,8 @@ class DelegatedMemberGenerator(
private fun createDelegateBody( private fun createDelegateBody(
irField: IrField, irField: IrField,
delegateFunction: IrSimpleFunction, delegateFunction: IrSimpleFunction,
superFunction: IrSimpleFunction superFunction: IrSimpleFunction,
callTypeCanBeNullable: Boolean
): IrBlockBody { ): IrBlockBody {
val startOffset = irField.startOffset val startOffset = irField.startOffset
val endOffset = irField.endOffset val endOffset = irField.endOffset
@@ -268,10 +271,14 @@ class DelegatedMemberGenerator(
) )
} }
} }
val resultType = delegateFunction.returnType
val irCastOrCall =
if (callTypeCanBeNullable && !resultType.isNullable()) Fir2IrImplicitCastInserter.implicitNotNullCast(irCall)
else irCall
if (superFunction.returnType.isUnit() || superFunction.returnType.isNothing()) { if (superFunction.returnType.isUnit() || superFunction.returnType.isNothing()) {
body.statements.add(irCall) body.statements.add(irCastOrCall)
} else { } else {
val irReturn = IrReturnImpl(startOffset, endOffset, irBuiltIns.nothingType, delegateFunction.symbol, irCall) val irReturn = IrReturnImpl(startOffset, endOffset, irBuiltIns.nothingType, delegateFunction.symbol, irCastOrCall)
body.statements.add(irReturn) body.statements.add(irReturn)
} }
return body return body
@@ -1,6 +1,4 @@
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: NPE expected on calling foo
// DISABLE_PARAM_ASSERTIONS // DISABLE_PARAM_ASSERTIONS
// MODULE: lib // MODULE: lib
@@ -1,6 +1,4 @@
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: Fail: should have been an exception
// MODULE: lib // MODULE: lib
// FILE: Delegation.java // FILE: Delegation.java
@@ -44,9 +44,10 @@ FILE fqName:<root> fileName:/delegatedImplementationOfJavaInterface.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Test $this: VALUE_PARAMETER name:<this> type:<root>.Test
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun returnNotNull (): @[EnhancedNullability] kotlin.String declared in <root>.Test' RETURN type=kotlin.Nothing from='public open fun returnNotNull (): @[EnhancedNullability] kotlin.String declared in <root>.Test'
CALL 'public abstract fun returnNotNull (): @[EnhancedNullability] kotlin.String declared in <root>.J' type=@[EnhancedNullability] kotlin.String origin=null TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.J visibility:local [final]' type=<root>.J origin=null CALL 'public abstract fun returnNotNull (): @[EnhancedNullability] kotlin.String declared in <root>.J' type=@[EnhancedNullability] kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.Test declared in <root>.Test.returnNotNull' type=<root>.Test 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>.Test declared in <root>.Test.returnNotNull' type=<root>.Test origin=null
FUN DELEGATED_MEMBER name:returnNullable visibility:public modality:OPEN <> ($this:<root>.Test) returnType:kotlin.String? FUN DELEGATED_MEMBER name:returnNullable visibility:public modality:OPEN <> ($this:<root>.Test) returnType:kotlin.String?
annotations: annotations:
Nullable(value = <null>) Nullable(value = <null>)
@@ -19,7 +19,7 @@ class Test : J {
@NotNull @NotNull
override fun returnNotNull(): @EnhancedNullability String { override fun returnNotNull(): @EnhancedNullability String {
return <this>.#<$$delegate_0>.returnNotNull() return <this>.#<$$delegate_0>.returnNotNull() /*!! String */
} }
@Nullable @Nullable
@@ -137,9 +137,10 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestJFoo $this: VALUE_PARAMETER name:<this> type:<root>.TestJFoo
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestJFoo' RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestJFoo'
CALL 'public open fun foo (): @[EnhancedNullability] kotlin.String declared in <root>.JFoo' type=kotlin.String origin=null TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.JFoo visibility:local [final]' type=<root>.JFoo origin=null CALL 'public open fun foo (): @[EnhancedNullability] kotlin.String declared in <root>.JFoo' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.TestJFoo declared in <root>.TestJFoo.foo' type=<root>.TestJFoo 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] FIELD DELEGATE name:<$$delegate_0> type:<root>.JFoo visibility:local [final]
EXPRESSION_BODY EXPRESSION_BODY
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.JFoo' type=<root>.JFoo origin=null CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.JFoo' type=<root>.JFoo origin=null
@@ -168,9 +169,10 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestK1 $this: VALUE_PARAMETER name:<this> type:<root>.TestK1
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK1' RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK1'
CALL 'public open fun foo (): @[EnhancedNullability] kotlin.String [fake_override] declared in <root>.K1' type=kotlin.String origin=null TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.K1 visibility:local [final]' type=<root>.K1 origin=null CALL 'public open fun foo (): @[EnhancedNullability] kotlin.String [fake_override] declared in <root>.K1' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.TestK1 declared in <root>.TestK1.foo' type=<root>.TestK1 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] FIELD DELEGATE name:<$$delegate_0> type:<root>.K1 visibility:local [final]
EXPRESSION_BODY EXPRESSION_BODY
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.K1' type=<root>.K1 origin=null CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.K1' type=<root>.K1 origin=null
@@ -261,9 +263,10 @@ FILE fqName:<root> fileName:/implicitNotNullOnDelegatedImplementation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.TestK4 $this: VALUE_PARAMETER name:<this> type:<root>.TestK4
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK4' RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.TestK4'
CALL 'public final fun foo (): @[FlexibleNullability] kotlin.String? declared in <root>.K4' type=kotlin.String origin=null TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.K4 visibility:local [final]' type=<root>.K4 origin=null CALL 'public final fun foo (): @[FlexibleNullability] kotlin.String? declared in <root>.K4' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.TestK4 declared in <root>.TestK4.foo' type=<root>.TestK4 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] FIELD DELEGATE name:<$$delegate_0> type:<root>.K4 visibility:local [final]
EXPRESSION_BODY EXPRESSION_BODY
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.K4' type=<root>.K4 origin=null CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.K4' type=<root>.K4 origin=null
@@ -55,7 +55,7 @@ class TestJFoo : IFoo {
} }
override fun foo(): String { override fun foo(): String {
return <this>.#<$$delegate_0>.foo() return <this>.#<$$delegate_0>.foo() /*!! String */
} }
local /* final field */ val <$$delegate_0>: JFoo = JFoo() local /* final field */ val <$$delegate_0>: JFoo = JFoo()
@@ -70,7 +70,7 @@ class TestK1 : IFoo {
} }
override fun foo(): String { override fun foo(): String {
return <this>.#<$$delegate_0>.foo() return <this>.#<$$delegate_0>.foo() /*!! String */
} }
local /* final field */ val <$$delegate_0>: K1 = K1() local /* final field */ val <$$delegate_0>: K1 = K1()
@@ -115,7 +115,7 @@ class TestK4 : IFoo {
} }
override fun foo(): String { override fun foo(): String {
return <this>.#<$$delegate_0>.foo() return <this>.#<$$delegate_0>.foo() /*!! String */
} }
local /* final field */ val <$$delegate_0>: K4 = K4() local /* final field */ val <$$delegate_0>: K4 = K4()
@@ -61,9 +61,10 @@ FILE fqName:<root> fileName:/nullCheckOnInterfaceDelegation.kt
$this: VALUE_PARAMETER name:<this> type:<root>.Delegated $this: VALUE_PARAMETER name:<this> type:<root>.Delegated
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.Delegated' RETURN type=kotlin.Nothing from='public open fun foo (): kotlin.String declared in <root>.Delegated'
CALL 'public final fun foo (): @[FlexibleNullability] kotlin.String? declared in <root>.Derived' type=kotlin.String origin=null TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
$this: GET_FIELD 'FIELD DELEGATE name:<$$delegate_0> type:<root>.Derived visibility:local [final]' type=<root>.Derived origin=null CALL 'public final fun foo (): @[FlexibleNullability] kotlin.String? declared in <root>.Derived' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.Delegated declared in <root>.Delegated.foo' type=<root>.Delegated 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] FIELD DELEGATE name:<$$delegate_0> type:<root>.Derived visibility:local [final]
EXPRESSION_BODY EXPRESSION_BODY
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.Derived' type=<root>.Derived origin=null CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.Derived' type=<root>.Derived origin=null
@@ -28,7 +28,7 @@ class Delegated : IFoo {
} }
override fun foo(): String { override fun foo(): String {
return <this>.#<$$delegate_0>.foo() return <this>.#<$$delegate_0>.foo() /*!! String */
} }
local /* final field */ val <$$delegate_0>: Derived = Derived() local /* final field */ val <$$delegate_0>: Derived = Derived()