[IR] Use erased types in backing field initializer in case of generic delegated property

- Since neither IrProperty nor IrField is Type Parameter container
 using of proprty's type parameter in IrField related code leads to
 creation of "hanging" type parameters which should be considered as
 incorrect IR.
 - Such code designed to be prohibited in LV 1.5
 - The fix makes use of erased type in such case
 where type parameter is expected.
This commit is contained in:
Roman Artemev
2020-03-12 15:31:44 +03:00
committed by romanart
parent 13a73b67de
commit d27954a6d4
17 changed files with 919 additions and 33 deletions
@@ -65,7 +65,7 @@ class DelegatedPropertyGenerator(declarationGenerator: DeclarationGenerator) : D
val irDelegate = irProperty.backingField!!
val thisClass = propertyDescriptor.containingDeclaration as? ClassDescriptor
val delegateReceiverValue = createBackingFieldValueForDelegate(irDelegate.symbol, thisClass, ktDelegate)
val delegateReceiverValue = createBackingFieldValueForDelegate(irDelegate.symbol, thisClass, ktDelegate, propertyDescriptor)
val getterDescriptor = propertyDescriptor.getter!!
irProperty.getter = generateDelegatedPropertyAccessor(ktProperty, ktDelegate, getterDescriptor) { irGetter ->
generateDelegatedPropertyGetterBody(
@@ -120,24 +120,26 @@ class DelegatedPropertyGenerator(declarationGenerator: DeclarationGenerator) : D
kPropertyType: KotlinType,
ktDelegate: KtPropertyDelegate
): IrField {
val delegateType = getDelegatedPropertyDelegateType(propertyDescriptor, ktDelegate)
val delegateDescriptor = createPropertyDelegateDescriptor(propertyDescriptor, delegateType, kPropertyType)
return context.typeTranslator.withTypeErasure(propertyDescriptor) {
val delegateType = getDelegatedPropertyDelegateType(propertyDescriptor, ktDelegate)
val delegateDescriptor = createPropertyDelegateDescriptor(propertyDescriptor, delegateType, kPropertyType)
val startOffset = ktDelegate.startOffsetSkippingComments
val endOffset = ktDelegate.endOffset
val origin = IrDeclarationOrigin.PROPERTY_DELEGATE
val type = delegateDescriptor.type.toIrType()
return context.symbolTable.declareField(
startOffset, endOffset, origin, delegateDescriptor, type
) {
IrFieldImpl(startOffset, endOffset, origin, it, type).apply {
metadata = MetadataSource.Property(propertyDescriptor)
val startOffset = ktDelegate.startOffsetSkippingComments
val endOffset = ktDelegate.endOffset
val origin = IrDeclarationOrigin.PROPERTY_DELEGATE
val type = delegateDescriptor.type.toIrType()
context.symbolTable.declareField(
startOffset, endOffset, origin, delegateDescriptor, type
) {
IrFieldImpl(startOffset, endOffset, origin, it, type).apply {
metadata = MetadataSource.Property(propertyDescriptor)
}
}.also { irDelegate ->
irDelegate.initializer = generateInitializerBodyForPropertyDelegate(
propertyDescriptor, kPropertyType, ktDelegate,
irDelegate.symbol
)
}
}.also { irDelegate ->
irDelegate.initializer = generateInitializerBodyForPropertyDelegate(
propertyDescriptor, kPropertyType, ktDelegate,
irDelegate.symbol
)
}
}
@@ -166,17 +168,21 @@ class DelegatedPropertyGenerator(declarationGenerator: DeclarationGenerator) : D
private fun createBackingFieldValueForDelegate(
irDelegateField: IrFieldSymbol,
thisClass: ClassDescriptor?,
ktDelegate: KtPropertyDelegate
ktDelegate: KtPropertyDelegate,
propertyDescriptor: PropertyDescriptor
): IntermediateValue {
val thisValue = createThisValueForDelegate(thisClass, ktDelegate)
return BackingFieldLValue(
context,
ktDelegate.startOffsetSkippingComments, ktDelegate.endOffset,
irDelegateField.descriptor.type.toIrType(),
irDelegateField,
thisValue,
null
)
return context.typeTranslator.withTypeErasure(propertyDescriptor) {
// TODO: do not erase type here
val thisValue = createThisValueForDelegate(thisClass, ktDelegate)
BackingFieldLValue(
context,
ktDelegate.startOffsetSkippingComments, ktDelegate.endOffset,
irDelegateField.descriptor.type.toIrType(),
irDelegateField,
thisValue,
null
)
}
}
private fun createThisValueForDelegate(thisClass: ClassDescriptor?, ktDelegate: KtPropertyDelegate): IntermediateValue? =
@@ -203,10 +203,13 @@ internal class InsertImplicitCasts(
initializer = initializer?.cast(declaration.descriptor.type)
}
override fun visitField(declaration: IrField): IrStatement =
declaration.transformPostfix {
initializer?.coerceInnerExpression(descriptor.type)
override fun visitField(declaration: IrField): IrStatement {
return typeTranslator.withTypeErasure(declaration.correspondingPropertySymbol?.descriptor ?: declaration.descriptor) {
declaration.transformPostfix {
initializer?.coerceInnerExpression(descriptor.type)
}
}
}
override fun visitFunction(declaration: IrFunction): IrStatement =
typeTranslator.buildWithScope(declaration) {
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.types.impl.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
import java.util.*
class TypeTranslator(
private val symbolTable: ReferenceSymbolTable,
@@ -31,6 +33,8 @@ class TypeTranslator(
private val enterTableScope: Boolean = false
) {
private val erasureStack = Stack<PropertyDescriptor>()
private val typeApproximatorForNI = TypeApproximator(builtIns)
lateinit var constantValueGenerator: ConstantValueGenerator
@@ -48,6 +52,15 @@ class TypeTranslator(
}
}
fun <T> withTypeErasure(propertyDescriptor: PropertyDescriptor, b: () -> T): T {
try {
erasureStack.push(propertyDescriptor)
return b()
} finally {
erasureStack.pop()
}
}
inline fun <T> buildWithScope(container: IrTypeParametersContainer, builder: () -> T): T {
enterScope(container)
val result = builder()
@@ -80,6 +93,17 @@ class TypeTranslator(
val ktTypeDescriptor = ktTypeConstructor.declarationDescriptor
?: throw AssertionError("No descriptor for type $approximatedType")
if (erasureStack.isNotEmpty()) {
if (ktTypeDescriptor is TypeParameterDescriptor) {
if (ktTypeDescriptor.containingDeclaration in erasureStack) {
// This hack is about type parameter leak in case of generic delegated property
// Such code has to be prohibited since LV 1.5
// For more details see commit message or KT-24643
return approximateUpperBounds(ktTypeDescriptor.upperBounds, variance)
}
}
}
return IrSimpleTypeBuilder().apply {
this.kotlinType = flexibleApproximatedType
this.hasQuestionMark = approximatedType.isMarkedNullable
@@ -103,6 +127,11 @@ class TypeTranslator(
}.buildTypeProjection()
}
private fun approximateUpperBounds(upperBounds: Collection<KotlinType>, variance: Variance): IrTypeProjection {
val commonSupertype = CommonSupertypes.commonSupertype(upperBounds)
return translateType(approximate(commonSupertype.replaceArgumentsWithStarProjections()), variance)
}
private fun SimpleType.toIrTypeAbbreviation(): IrTypeAbbreviation {
val typeAliasDescriptor = constructor.declarationDescriptor.let {
it as? TypeAliasDescriptor