From 56ad09153405b8540542b83a143d58733ac3e504 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 13 Aug 2018 14:59:00 +0300 Subject: [PATCH] Generate synthetic inline class methods in psi2ir --- .../psi2ir/generators/ClassGenerator.kt | 11 +++- .../generators/DataClassMembersGenerator.kt | 26 ++++++--- .../ir/declarations/IrDeclarationOrigin.kt | 1 + .../ir/irText/classes/inlineClass.txt | 54 +++++++++++++++++++ 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt index e63d69602cb..de551bdce82 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt @@ -47,7 +47,6 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.newHashMapWithExpectedSize -import java.lang.AssertionError class ClassGenerator( declarationGenerator: DeclarationGenerator @@ -86,6 +85,10 @@ class ClassGenerator( generateFakeOverrideMemberDeclarations(irClass, ktClassOrObject) + if (irClass.isInline && ktClassOrObject is KtClassOrObject) { + generateAdditionalMembersForInlineClasses(irClass, ktClassOrObject) + } + if (irClass.isData && ktClassOrObject is KtClassOrObject) { generateAdditionalMembersForDataClass(irClass, ktClassOrObject) } @@ -316,8 +319,12 @@ class ClassGenerator( return typeArguments } + private fun generateAdditionalMembersForInlineClasses(irClass: IrClass, ktClassOrObject: KtClassOrObject) { + DataClassMembersGenerator(declarationGenerator).generateInlineClassMembers(ktClassOrObject, irClass) + } + private fun generateAdditionalMembersForDataClass(irClass: IrClass, ktClassOrObject: KtClassOrObject) { - DataClassMembersGenerator(declarationGenerator).generate(ktClassOrObject, irClass) + DataClassMembersGenerator(declarationGenerator).generateDataClassMembers(ktClassOrObject, irClass) } private fun generateAdditionalMembersForEnumClass(irClass: IrClass) { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt index c0d552abfb3..fd2976dc29d 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt @@ -43,14 +43,17 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -import java.lang.AssertionError class DataClassMembersGenerator( declarationGenerator: DeclarationGenerator ) : DeclarationGeneratorExtension(declarationGenerator) { - fun generate(ktClassOrObject: KtClassOrObject, irClass: IrClass) { - MyDataClassMethodGenerator(ktClassOrObject, irClass).generate() + fun generateInlineClassMembers(ktClassOrObject: KtClassOrObject, irClass: IrClass) { + MyDataClassMethodGenerator(ktClassOrObject, irClass, IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER).generate() + } + + fun generateDataClassMembers(ktClassOrObject: KtClassOrObject, irClass: IrClass) { + MyDataClassMethodGenerator(ktClassOrObject, irClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate() } private fun declareSimpleFunction(startOffset: Int, endOffset: Int, origin: IrDeclarationOrigin, function: FunctionDescriptor) = @@ -104,7 +107,8 @@ class DataClassMembersGenerator( private inner class MyDataClassMethodGenerator( ktClassOrObject: KtClassOrObject, - val irClass: IrClass + val irClass: IrClass, + val origin: IrDeclarationOrigin ) : DataClassMethodGenerator(ktClassOrObject, declarationGenerator.context.bindingContext) { private inline fun buildMember( function: FunctionDescriptor, @@ -112,7 +116,7 @@ class DataClassMembersGenerator( body: MemberFunctionBuilder.(IrFunction) -> Unit ) { MemberFunctionBuilder( - irClass, function, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER, + irClass, function, origin, psiElement.startOffsetOrUndefined, psiElement.endOffsetOrUndefined ).addToClass { irFunction -> irFunction.buildWithScope { @@ -123,8 +127,10 @@ class DataClassMembersGenerator( } override fun generateComponentFunction(function: FunctionDescriptor, parameter: ValueParameterDescriptor) { + if (!irClass.isData) return + val ktParameter = DescriptorToSourceUtils.descriptorToDeclaration(parameter) - ?: throw AssertionError("No definition for data class constructor parameter $parameter") + ?: throw AssertionError("No definition for data class constructor parameter $parameter") buildMember(function, ktParameter) { +irReturn(irGet(function.returnType!!.toIrType(), irThis(), getPropertyGetterSymbol(parameter))) @@ -140,8 +146,10 @@ class DataClassMembersGenerator( context.symbolTable.referenceFunction(property.getter!!) override fun generateCopyFunction(function: FunctionDescriptor, constructorParameters: List) { + if (!irClass.isData) return + val dataClassConstructor = classDescriptor.unsubstitutedPrimaryConstructor - ?: throw AssertionError("Data class should have a primary constructor: $classDescriptor") + ?: throw AssertionError("Data class should have a primary constructor: $classDescriptor") val constructorSymbol = context.symbolTable.referenceConstructor(dataClassConstructor) buildMember(function, declaration) { irFunction -> @@ -167,7 +175,9 @@ class DataClassMembersGenerator( buildMember(function, declaration) { val irType = classDescriptor.defaultType.toIrType() - +irIfThenReturnTrue(irEqeqeq(irThis(), irOther())) + if (!irClass.isInline) { + +irIfThenReturnTrue(irEqeqeq(irThis(), irOther())) + } +irIfThenReturnFalse(irNotIs(irOther(), irType)) val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast") for (property in properties) { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt index dbdb3ceee47..396c2a2726a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt @@ -29,6 +29,7 @@ interface IrDeclarationOrigin { object DELEGATED_MEMBER : IrDeclarationOriginImpl("DELEGATED_MEMBER") object ENUM_CLASS_SPECIAL_MEMBER : IrDeclarationOriginImpl("ENUM_CLASS_SPECIAL_MEMBER") object GENERATED_DATA_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_DATA_CLASS_MEMBER") + object GENERATED_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("GENERATED_INLINE_CLASS_MEMBER") object LOCAL_FUNCTION_FOR_LAMBDA : IrDeclarationOriginImpl("LOCAL_FUNCTION_FOR_LAMBDA") object CATCH_PARAMETER : IrDeclarationOriginImpl("CATCH_PARAMETER") object INSTANCE_RECEIVER : IrDeclarationOriginImpl("INSTANCE_RECEIVER") diff --git a/compiler/testData/ir/irText/classes/inlineClass.txt b/compiler/testData/ir/irText/classes/inlineClass.txt index b8ebb160bb7..cfabb2769bb 100644 --- a/compiler/testData/ir/irText/classes/inlineClass.txt +++ b/compiler/testData/ir/irText/classes/inlineClass.txt @@ -17,3 +17,57 @@ FILE fqName: fileName:/inlineClass.kt RETURN type=kotlin.Nothing from='(): Int' GET_FIELD 'x: Int' type=kotlin.Int origin=null receiver: GET_VAR 'this@Test: Test' type=Test origin=null + FUN GENERATED_INLINE_CLASS_MEMBER name:toString visibility:public modality:OPEN <> ($this:Test) returnType:kotlin.String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags: + $this: VALUE_PARAMETER name: type:Test flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='toString(): String' + STRING_CONCATENATION type=kotlin.String + CONST String type=kotlin.String value=Test( + CONST String type=kotlin.String value=x= + CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: GET_VAR 'this@Test: Test' type=Test origin=null + CONST String type=kotlin.String value=) + FUN GENERATED_INLINE_CLASS_MEMBER name:hashCode visibility:public modality:OPEN <> ($this:Test) returnType:kotlin.Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags: + $this: VALUE_PARAMETER name: type:Test flags: + BLOCK_BODY + VAR IR_TEMPORARY_VARIABLE name:tmp0_result type:kotlin.Int flags:var + CONST Int type=kotlin.Int value=0 + SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ + CALL 'hashCode(): Int' type=kotlin.Int origin=null + $this: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: GET_VAR 'this@Test: Test' type=Test origin=null + RETURN type=kotlin.Nothing from='hashCode(): Int' + GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null + FUN GENERATED_INLINE_CLASS_MEMBER name:equals visibility:public modality:OPEN <> ($this:Test, other:kotlin.Any?) returnType:kotlin.Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags: + $this: VALUE_PARAMETER name: type:Test flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=Test + typeOperand: CLASS CLASS name:Test modality:FINAL visibility:public flags:inline superTypes:[kotlin.Any] + GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value=false + VAR IR_TEMPORARY_VARIABLE name:tmp0_other_with_cast type:Test flags:val + TYPE_OP type=Test origin=CAST typeOperand=Test + typeOperand: CLASS CLASS name:Test modality:FINAL visibility:public flags:inline superTypes:[kotlin.Any] + GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Unit origin=null + BRANCH + if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: GET_VAR 'this@Test: Test' type=Test origin=null + arg1: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: GET_VAR 'tmp0_other_with_cast: Test' type=Test origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value=false + RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value=true