diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index bd632fcbf5d..455663de5a4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -268,13 +268,6 @@ internal val testProcessorPhase = makeKonanFileOpPhase( description = "Unit test processor" ) -internal val enumClassPhase = makeKonanFileOpPhase( - { context, irFile -> EnumClassLowering(context).run(irFile) }, - name = "Enums", - description = "Enum classes lowering", - prerequisite = setOf(enumConstructorsPhase) // TODO: make weak dependency on `testProcessorPhase` -) - internal val delegationPhase = makeKonanFileLoweringPhase( ::PropertyDelegationLowering, name = "Delegation", @@ -288,6 +281,13 @@ internal val functionReferencePhase = makeKonanFileLoweringPhase( prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase` ) +internal val enumClassPhase = makeKonanFileOpPhase( + { context, irFile -> EnumClassLowering(context).run(irFile) }, + name = "Enums", + description = "Enum classes lowering", + prerequisite = setOf(enumConstructorsPhase, functionReferencePhase) // TODO: make weak dependency on `testProcessorPhase` +) + internal val singleAbstractMethodPhase = makeKonanFileLoweringPhase( ::NativeSingleAbstractMethodLowering, name = "SingleAbstractMethod", diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt index db9f7a3479b..18de0410495 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt @@ -45,6 +45,8 @@ class KonanReflectionTypes(module: ModuleDescriptor, internalPackage: FqName) { val kMutableProperty1: ClassDescriptor by ClassLookup(kotlinReflectScope) val kMutableProperty2: ClassDescriptor by ClassLookup(kotlinReflectScope) val kTypeProjection: ClassDescriptor by ClassLookup(kotlinReflectScope) + val kType: ClassDescriptor by ClassLookup(kotlinReflectScope) + val kVariance: ClassDescriptor by ClassLookup(kotlinReflectScope) val kFunctionImpl: ClassDescriptor by ClassLookup(internalScope) val kSuspendFunctionImpl: ClassDescriptor by ClassLookup(internalScope) @@ -56,4 +58,6 @@ class KonanReflectionTypes(module: ModuleDescriptor, internalPackage: FqName) { val kMutableProperty2Impl: ClassDescriptor by ClassLookup(internalScope) val kLocalDelegatedPropertyImpl: ClassDescriptor by ClassLookup(internalScope) val kLocalDelegatedMutablePropertyImpl: ClassDescriptor by ClassLookup(internalScope) + + val typeOf = kotlinReflectScope.getContributedFunctions(Name.identifier("typeOf"), NoLookupLocation.FROM_REFLECTION).single() } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 4fb3485e615..a4b38853084 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -450,14 +450,21 @@ internal class KonanSymbols( val kLocalDelegatedPropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedPropertyImpl) val kLocalDelegatedMutablePropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedMutablePropertyImpl) + + val typeOf = symbolTable.referenceSimpleFunction(context.reflectionTypes.typeOf) + + val kType = symbolTable.referenceClass(context.reflectionTypes.kType) + val kVariance = symbolTable.referenceClass(context.reflectionTypes.kVariance) + val getClassTypeInfo = internalFunction("getClassTypeInfo") val getObjectTypeInfo = internalFunction("getObjectTypeInfo") val kClassImpl = internalClass("KClassImpl") val kClassImplConstructor by lazy { kClassImpl.constructors.single() } val kClassUnsupportedImpl = internalClass("KClassUnsupportedImpl") val kClassUnsupportedImplConstructor by lazy { kClassUnsupportedImpl.constructors.single() } + val kTypeParameterImpl = internalClass("KTypeParameterImpl") val kTypeImpl = internalClass("KTypeImpl") - val kTypeImplForGenerics = internalClass("KTypeImplForGenerics") + val kTypeImplForTypeParametersWithRecursiveBounds = internalClass("KTypeImplForTypeParametersWithRecursiveBounds") val kTypeProjection = symbolTable.referenceClass(context.reflectionTypes.kTypeProjection) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt index 5def9a329ad..3cc6ea68d6f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.ir.util.isSuspend import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.library.uniqueName +import org.jetbrains.kotlin.name.Name // This file describes the ABI for Kotlin descriptors of exported declarations. @@ -112,6 +113,7 @@ internal val IrClass.kotlinObjCClassInfoSymbolName: String } val IrFunction.functionName get() = with(KonanBinaryInterface) { functionName } +val IrFunction.fullName get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString() val IrFunction.symbolName get() = with(KonanBinaryInterface) { symbolName } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt index d796ed79c32..ccfde9d4e9b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -33,8 +33,6 @@ import org.jetbrains.kotlin.name.Name internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass { private var tempIndex = 0 - private val kTypeGenerator = KTypeGenerator(context) - private fun getKPropertyImplConstructor(receiverTypes: List, returnType: IrType, isLocal: Boolean, @@ -109,19 +107,20 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa override fun visitPropertyReference(expression: IrPropertyReference): IrExpression { expression.transformChildrenVoid(this) + val kTypeGenerator = KTypeGenerator(context, irFile, expression) val startOffset = expression.startOffset val endOffset = expression.endOffset val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset) irBuilder.run { val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null } return when (receiversCount) { - 1 -> createKProperty(expression, this) // Has receiver. + 1 -> createKProperty(expression, kTypeGenerator, this) // Has receiver. 2 -> error("Callable reference to properties with two receivers is not allowed: ${expression.symbol.owner.name}") else -> { // Cache KProperties with no arguments. val field = kProperties.getOrPut(expression.symbol.owner) { - createKProperty(expression, this) to kProperties.size + createKProperty(expression, kTypeGenerator, this) to kProperties.size } irCall(arrayItemGetter).apply { @@ -149,6 +148,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa createLocalKProperty( expression.symbol.owner.name.asString(), expression.getter.owner.returnType, + KTypeGenerator(this@PropertyDelegationLowering.context, irFile, expression), this ) to kProperties.size } @@ -172,7 +172,11 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa } } - private fun createKProperty(expression: IrPropertyReference, irBuilder: IrBuilderWithScope): IrExpression { + private fun createKProperty( + expression: IrPropertyReference, + kTypeGenerator: KTypeGenerator, + irBuilder: IrBuilderWithScope + ): IrExpression { val startOffset = expression.startOffset val endOffset = expression.endOffset return irBuilder.irBlock(expression) { @@ -263,6 +267,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa private fun createLocalKProperty(propertyName: String, propertyType: IrType, + kTypeGenerator: KTypeGenerator, irBuilder: IrBuilderWithScope): IrExpression { irBuilder.run { val (symbol, constructorTypeArguments) = getKPropertyImplConstructor( diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt index 0b1f5253f8a..51ec2a03f98 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName -import org.jetbrains.kotlin.backend.konan.llvm.functionName +import org.jetbrains.kotlin.backend.konan.llvm.fullName import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor @@ -49,8 +49,6 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass declaration.origin == DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL } - private val kTypeGenerator = KTypeGenerator(context) - override fun lower(irFile: IrFile) { var generatedClasses = mutableListOf() irFile.transform(object: IrElementTransformerVoidWithContext() { @@ -151,7 +149,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass fun transformFunctionReference(expression: IrFunctionReference, samSuperType: IrType? = null): IrExpression { val parent: IrDeclarationContainer = (currentClass?.irElement as? IrClass) ?: irFile - val loweredFunctionReference = FunctionReferenceBuilder(parent, expression, samSuperType).build() + val loweredFunctionReference = FunctionReferenceBuilder(irFile, parent, expression, samSuperType).build() generatedClasses.add(loweredFunctionReference.functionReferenceClass) val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset) @@ -177,10 +175,12 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass private val getContinuationSymbol = symbols.getContinuation private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol - private inner class FunctionReferenceBuilder(val parent: IrDeclarationParent, - val functionReference: IrFunctionReference, - val samSuperType: IrType?) { - + private inner class FunctionReferenceBuilder( + val irFile: IrFile, + val parent: IrDeclarationParent, + val functionReference: IrFunctionReference, + val samSuperType: IrType? + ) { private val startOffset = functionReference.startOffset private val endOffset = functionReference.endOffset private val referencedFunction = functionReference.symbol.owner @@ -359,6 +359,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass type = parameter.type.substitute(typeArgumentsMap)) } + val kTypeGenerator = KTypeGenerator(context, irFile, functionReference) body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody { val superConstructor = when { isKSuspendFunction -> kSuspendFunctionImplConstructorSymbol.owner @@ -388,9 +389,6 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass } } - private val IrFunction.fullName: String - get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString() - private fun getFlags() = (if (referencedFunction.isSuspend) 1 else 0) + getAdaptedCallableReferenceFlags() shl 1 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt index 0ff3ab339dd..c20174f7938 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt @@ -6,18 +6,16 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.ir.Symbols +import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer +import org.jetbrains.kotlin.backend.common.lower.at import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.error import org.jetbrains.kotlin.backend.konan.renderCompilerError -import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.util.irCall -import org.jetbrains.kotlin.ir.util.isTypeOfIntrinsic import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid /** @@ -29,41 +27,31 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass { private val symbols get() = context.ir.symbols - private val kTypeGenerator = KTypeGenerator( - context, - eraseTypeParameters = true // Mimic JVM BE behaviour until proper type parameter impl is ready. - ) - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { + irFile.transformChildrenVoid(object : IrBuildingTransformer(context) { override fun visitClassReference(expression: IrClassReference): IrExpression { expression.transformChildrenVoid() - val builder = createIrBuilder(expression) - - val symbol = expression.symbol - return if (symbol is IrClassSymbol) { - builder.irKClass(context, symbol) - } else { - // E.g. for `T::class` in a body of an inline function itself. - builder.irCall(context.ir.symbols.throwNullPointerException.owner) + return builder.at(expression).run { + (expression.symbol as? IrClassSymbol)?.let { irKClass(this@PostInlineLowering.context, it) } + ?: + // E.g. for `T::class` in a body of an inline function itself. + irCall(symbols.throwNullPointerException.owner) } } override fun visitGetClass(expression: IrGetClass): IrExpression { expression.transformChildrenVoid() - val builder = createIrBuilder(expression) + return builder.at(expression).run { + irCall(symbols.kClassImplConstructor, listOf(expression.argument.type)).apply { + val typeInfo = irCall(symbols.getObjectTypeInfo).apply { + putValueArgument(0, expression.argument) + } - val typeArgument = expression.argument.type - - return builder.irCall(symbols.kClassImplConstructor, listOf(typeArgument)).apply { - val typeInfo = builder.irCall(symbols.getObjectTypeInfo).apply { - putValueArgument(0, expression.argument) + putValueArgument(0, typeInfo) } - - putValueArgument(0, typeInfo) } } @@ -94,20 +82,14 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass { expression.startOffset, expression.endOffset, context.irBuiltIns.stringType, IrConstKind.String, builder.toString())) - } else if (expression.symbol.owner.isTypeOfIntrinsic()) { - val type = expression.getTypeArgument(0) - ?: error(irFile, expression, "missing type argument") - return with (kTypeGenerator) { createIrBuilder(expression).irKType(type) } + } else if (Symbols.isTypeOfIntrinsic(expression.symbol)) { + return with (KTypeGenerator(context, irFile, expression, needExactTypeParameters = true)) { + builder.at(expression).irKType(expression.getTypeArgument(0)!!, leaveReifiedForLater = false) + } } return expression } - - private fun createIrBuilder(element: IrElement) = context.createIrBuilder( - currentScope!!.scope.scopeOwnerSymbol, - element.startOffset, - element.endOffset - ) }) } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt index 6954dfabf75..2e0b6127443 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PreInlineLowering.kt @@ -6,20 +6,27 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.BodyLoweringPass -import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer +import org.jetbrains.kotlin.backend.common.ir.Symbols +import org.jetbrains.kotlin.backend.common.lower.at +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanConfigKeys +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase +import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.types.isUnit -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer /** * This pass runs before inlining and performs the following additional transformations over some operations: * - Assertion call removal. + * - First phase of typeOf intrinsic lowering. */ internal class PreInlineLowering(val context: Context) : BodyLoweringPass { @@ -29,19 +36,30 @@ internal class PreInlineLowering(val context: Context) : BodyLoweringPass { private val enableAssertions = context.config.configuration.getBoolean(KonanConfigKeys.ENABLE_ASSERTIONS) override fun lower(irBody: IrBody, container: IrDeclaration) { - irBody.transformChildrenVoid(object : IrBuildingTransformer(context) { + irBody.transformChildren(object : IrElementTransformer { + override fun visitDeclaration(declaration: IrDeclarationBase, data: IrBuilderWithScope) = + super.visitDeclaration(declaration, + data = (declaration as? IrSymbolOwner)?.let { context.createIrBuilder(it.symbol, it.startOffset, it.endOffset) } + ?: data + ) - override fun visitCall(expression: IrCall): IrExpression { - expression.transformChildrenVoid(this) + override fun visitCall(expression: IrCall, data: IrBuilderWithScope): IrExpression { + expression.transformChildren(this, data) - // Replace assert() call with an empty composite if assertions are not enabled. - if (!enableAssertions && expression.symbol in asserts) { - assert(expression.type.isUnit()) - return IrCompositeImpl(expression.startOffset, expression.endOffset, expression.type) + return when { + !enableAssertions && expression.symbol in asserts -> { + // Replace assert() call with an empty composite if assertions are not enabled. + require(expression.type.isUnit()) + IrCompositeImpl(expression.startOffset, expression.endOffset, expression.type) + } + Symbols.isTypeOfIntrinsic(expression.symbol) -> { + with (KTypeGenerator(context, container.file, expression, needExactTypeParameters = true)) { + data.at(expression).irKType(expression.getTypeArgument(0)!!, leaveReifiedForLater = true) + } + } + else -> expression } - - return expression } - }) + }, data = context.createIrBuilder((container as IrSymbolOwner).symbol, irBody.startOffset, irBody.endOffset)) } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt index b4e819ad9ed..088766b8151 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt @@ -10,94 +10,184 @@ import org.jetbrains.kotlin.backend.konan.KonanBackendContext import org.jetbrains.kotlin.backend.konan.ir.typeWithStarProjections import org.jetbrains.kotlin.backend.konan.ir.typeWithoutArguments import org.jetbrains.kotlin.backend.konan.isObjCClass +import org.jetbrains.kotlin.backend.konan.llvm.fullName +import org.jetbrains.kotlin.backend.konan.reportCompilationError import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrEnumEntry +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers +import org.jetbrains.kotlin.types.Variance internal class KTypeGenerator( - private val context: KonanBackendContext, - private val eraseTypeParameters: Boolean = false + val context: KonanBackendContext, + val irFile: IrFile, + val irElement: IrElement, + val needExactTypeParameters: Boolean = false ) { private val symbols = context.ir.symbols - fun IrBuilderWithScope.irKType(type: IrType): IrExpression = if (type !is IrSimpleType) { - // Represent as non-denotable type: - irKTypeImpl( - kClassifier = irNull(), - irTypeArguments = emptyList(), - isMarkedNullable = false - ) - } else { - val classifier = type.classifier + fun IrBuilderWithScope.irKType(type: IrType, leaveReifiedForLater: Boolean = false) = + irKType(type, leaveReifiedForLater, mutableSetOf()) - if (classifier is IrClassSymbol) { - irKTypeImpl( - kClassifier = irKClass(classifier), - irTypeArguments = type.arguments, - isMarkedNullable = type.hasQuestionMark + private class RecursiveBoundsException(message: String) : Throwable(message) + + private fun IrBuilderWithScope.irKType( + type: IrType, + leaveReifiedForLater: Boolean, + seenTypeParameters: MutableSet + ): IrExpression { + if (type !is IrSimpleType) { + // Represent as non-denotable type: + return irKTypeImpl( + kClassifier = irNull(), + irTypeArguments = emptyList(), + isMarkedNullable = false, + leaveReifiedForLater = leaveReifiedForLater, + seenTypeParameters = seenTypeParameters ) - } else { - if (eraseTypeParameters) { - irKType(context.irBuiltIns.anyNType) - } else { - irCall(symbols.kTypeImplForGenerics.constructors.single()) + } + try { + val kClassifier = when (val classifier = type.classifier) { + is IrClassSymbol -> irKClass(classifier) + is IrTypeParameterSymbol -> { + if (classifier.owner.isReified && leaveReifiedForLater) { + // Leave as is for reification. + return irCall(symbols.typeOf).apply { putTypeArgument(0, type) } + } + + // Leave upper bounds of non-reified type parameters as is, even if they are reified themselves. + irKTypeParameter(classifier.owner, leaveReifiedForLater = false, seenTypeParameters = seenTypeParameters) + } + else -> TODO("Unexpected classifier: $classifier") } + + return irKTypeImpl( + kClassifier = kClassifier, + irTypeArguments = type.arguments, + isMarkedNullable = type.hasQuestionMark, + leaveReifiedForLater = leaveReifiedForLater, + seenTypeParameters = seenTypeParameters + ) + } catch (t: RecursiveBoundsException) { + if (needExactTypeParameters) + this@KTypeGenerator.context.reportCompilationError(t.message!!, irFile, irElement) + return irCall(symbols.kTypeImplForTypeParametersWithRecursiveBounds.constructors.single()) } } private fun IrBuilderWithScope.irKTypeImpl( kClassifier: IrExpression, irTypeArguments: List, - isMarkedNullable: Boolean + isMarkedNullable: Boolean, + leaveReifiedForLater: Boolean, + seenTypeParameters: MutableSet ): IrExpression = irCall(symbols.kTypeImpl.constructors.single()).apply { putValueArgument(0, kClassifier) - putValueArgument(1, irKTypeProjectionsList(irTypeArguments)) + putValueArgument(1, irKTypeProjectionsList(irTypeArguments, leaveReifiedForLater, seenTypeParameters)) putValueArgument(2, irBoolean(isMarkedNullable)) } private fun IrBuilderWithScope.irKClass(symbol: IrClassSymbol) = irKClass(this@KTypeGenerator.context, symbol) - private fun IrBuilderWithScope.irKTypeProjectionsList( - irTypeArguments: List + private fun IrBuilderWithScope.irKTypeParameter( + typeParameter: IrTypeParameter, + leaveReifiedForLater: Boolean, + seenTypeParameters: MutableSet ): IrMemberAccessExpression<*> { - val kTypeProjectionType = symbols.kTypeProjection.typeWithoutArguments + if (!seenTypeParameters.add(typeParameter)) + throw RecursiveBoundsException("Non-reified type parameters with recursive bounds are not supported yet: ${typeParameter.render()}") + val result = irCall(symbols.kTypeParameterImpl.constructors.single()).apply { + putValueArgument(0, irString(typeParameter.name.asString())) + putValueArgument(1, irString(typeParameter.parentUniqueName)) + putValueArgument(2, irKTypeList(typeParameter.superTypes, leaveReifiedForLater, seenTypeParameters)) + putValueArgument(3, irKVariance(typeParameter.variance)) + putValueArgument(4, irBoolean(typeParameter.isReified)) + } + seenTypeParameters.remove(typeParameter) + return result + } - return if (irTypeArguments.isEmpty()) { - irCall(symbols.emptyList, listOf(kTypeProjectionType)) - } else { - irCall(symbols.listOf, listOf(kTypeProjectionType)).apply { - putValueArgument(0, IrVarargImpl( - startOffset, - endOffset, - type = symbols.array.typeWith(kTypeProjectionType), - varargElementType = kTypeProjectionType, - elements = irTypeArguments.map { irKTypeProjection(it) } - )) - } + private val IrTypeParameter.parentUniqueName get() = when (val parent = parent) { + is IrFunction -> parent.fullName + else -> parent.fqNameForIrSerialization.asString() + } + + private val Variance.kVarianceName: String + get() = when (this) { + Variance.INVARIANT -> "INVARIANT" + Variance.IN_VARIANCE -> "IN" + Variance.OUT_VARIANCE -> "OUT" + } + + private fun IrBuilderWithScope.irKVariance(variance: Variance) = + IrGetEnumValueImpl( + startOffset, endOffset, + symbols.kVariance.defaultType, + symbols.kVariance.owner.declarations + .filterIsInstance() + .single { it.name.asString() == variance.kVarianceName }.symbol + ) + + private fun IrBuilderWithScope.irKTypeLikeList( + types: List, + itemType: IrType, + itemBuilder: (T) -> IrExpression + ) = if (types.isEmpty()) { + irCall(symbols.emptyList, listOf(itemType)) + } else { + irCall(symbols.listOf, listOf(itemType)).apply { + putValueArgument(0, IrVarargImpl( + startOffset, endOffset, + type = symbols.array.typeWith(itemType), + varargElementType = itemType, + elements = types.map { itemBuilder(it) } + )) } } - private fun IrBuilderWithScope.irKTypeProjection(argument: IrTypeArgument): IrExpression { - return when (argument) { - is IrTypeProjection -> irCall(symbols.kTypeProjectionFactories.getValue(argument.variance)).apply { - dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion) - putValueArgument(0, irKType(argument.type)) - } + private fun IrBuilderWithScope.irKTypeList( + types: List, + leaveReifiedForLater: Boolean, + seenTypeParameters: MutableSet + ) = irKTypeLikeList(types, symbols.kType.defaultType) { irKType(it, leaveReifiedForLater, seenTypeParameters) } - is IrStarProjection -> irCall(symbols.kTypeProjectionStar.owner.getter!!).apply { - dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion) - } + private fun IrBuilderWithScope.irKTypeProjectionsList( + irTypeArguments: List, + leaveReifiedForLater: Boolean, + seenTypeParameters: MutableSet + ) = irKTypeLikeList(irTypeArguments, symbols.kTypeProjection.typeWithoutArguments) { + irKTypeProjection(it, leaveReifiedForLater, seenTypeParameters) + } - else -> error("Unexpected IrTypeArgument: $argument (${argument::class})") + private fun IrBuilderWithScope.irKTypeProjection( + argument: IrTypeArgument, + leaveReifiedForLater: Boolean, + seenTypeParameters: MutableSet + ) = when (argument) { + is IrTypeProjection -> irCall(symbols.kTypeProjectionFactories.getValue(argument.variance)).apply { + dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion) + putValueArgument(0, irKType(argument.type, leaveReifiedForLater, seenTypeParameters)) } + + is IrStarProjection -> irCall(symbols.kTypeProjectionStar.owner.getter!!).apply { + dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion) + } + + else -> error("Unexpected IrTypeArgument: $argument (${argument::class})") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt index 18cb42775ba..ec64e94faf8 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SpecialBackendChecksTraversal.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.allParameters import org.jetbrains.kotlin.backend.common.lower.Closure import org.jetbrains.kotlin.backend.common.lower.ClosureAnnotator @@ -21,9 +22,11 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.isFinalClass import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -428,11 +431,11 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme getUnboundReferencedFunction(expression.getValueArgument(2)!!) ?: reportError(expression, "${callee.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda") } - else -> when (callee.symbol) { - symbols.createCleaner -> + else -> when { + callee.symbol == symbols.createCleaner -> getUnboundReferencedFunction(expression.getValueArgument(1)!!) ?: reportError(expression, "${callee.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda") - symbols.immutableBlobOf -> { + callee.symbol == symbols.immutableBlobOf -> { val args = expression.getValueArgument(0) ?: reportError(expression, "expected at least one element") val elements = (args as IrVararg).elements @@ -446,6 +449,8 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme reportError(it, "incorrect value for binary data: $value") } } + Symbols.isTypeOfIntrinsic(callee.symbol) -> + checkIrKType(expression, expression.getTypeArgument(0)!!) } } } @@ -486,6 +491,34 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme val typeParameter = symbol.owner.typeParameters.single() return getTypeArgument(typeParameter.index)!! } + + private fun checkIrKType( + irElement: IrElement, + type: IrType, + seenTypeParameters: MutableSet = mutableSetOf() + ) { + if (type !is IrSimpleType) + return + val classifier = type.classifier + if (classifier is IrTypeParameterSymbol) + checkIrKTypeParameter(irElement, classifier.owner, seenTypeParameters) + + type.arguments.forEach { + if (it is IrTypeProjection) + checkIrKType(irElement, it.type, seenTypeParameters) + } + } + + private fun checkIrKTypeParameter( + irElement: IrElement, + typeParameter: IrTypeParameter, + seenTypeParameters: MutableSet + ) { + if (!seenTypeParameters.add(typeParameter)) + reportError(irElement, "Non-reified type parameters with recursive bounds are not supported yet: ${typeParameter.render()}") + typeParameter.superTypes.forEach { checkIrKType(irElement, it, seenTypeParameters) } + seenTypeParameters.remove(typeParameter) + } } private fun BackendChecker.checkCanGenerateCCall(expression: IrCall, isInvoke: Boolean) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt index 7b0c9993b47..1527fd5f480 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt @@ -387,10 +387,5 @@ fun IrClass.defaultOrNullableType(hasQuestionMark: Boolean) = fun IrFunction.isRestrictedSuspendFunction(languageVersionSettings: LanguageVersionSettings): Boolean = this.descriptor.extensionReceiverParameter?.type?.isRestrictsSuspensionReceiver(languageVersionSettings) == true -fun IrFunction.isTypeOfIntrinsic(): Boolean = - this.name.asString() == "typeOf" && - this.valueParameters.isEmpty() && - (this.parent as? IrPackageFragment)?.fqName == StandardNames.KOTLIN_REFLECT_FQ_NAME - fun IrBuilderWithScope.irByte(value: Byte) = IrConstImpl.byte(startOffset, endOffset, context.irBuiltIns.byteType, value) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index fa035b5ba89..f887c460182 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -1732,6 +1732,10 @@ task ktype1(type: KonanLocalTest) { source = "codegen/ktype/ktype1.kt" } +task ktype_nonReified(type: KonanLocalTest) { + source = "codegen/ktype/nonReified.kt" +} + task associatedObjects1(type: KonanLocalTest) { source = "codegen/associatedObjects/associatedObjects1.kt" } diff --git a/backend.native/tests/codegen/ktype/ktype1.kt b/backend.native/tests/codegen/ktype/ktype1.kt index c5649433f82..d7a841c9093 100644 --- a/backend.native/tests/codegen/ktype/ktype1.kt +++ b/backend.native/tests/codegen/ktype/ktype1.kt @@ -32,7 +32,7 @@ fun testBasics1() { assertEquals("$pkg.C", kType>().toString()) assertEquals("$pkg.C<$pkg.C>", kType>>().toString()) - assertEquals("$pkg.C", kTypeForCWithTypeParameter().toString()) + assertEquals("$pkg.C", kTypeForCWithTypeParameter().toString()) assertEquals("$pkg.Object", kType().toString()) assertEquals("$pkg.Outer.Friend", kType().toString()) } diff --git a/backend.native/tests/codegen/ktype/nonReified.kt b/backend.native/tests/codegen/ktype/nonReified.kt new file mode 100644 index 00000000000..5dc5670251e --- /dev/null +++ b/backend.native/tests/codegen/ktype/nonReified.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package codegen.ktype.nonReified + +import kotlin.test.* +import kotlin.reflect.* + +@OptIn(kotlin.ExperimentalStdlibApi::class) +fun foo() = typeOf>() + +@Test +fun test_fun() { + val l = foo() + assertEquals(List::class, l.classifier) + val t = l.arguments.single().type!!.classifier + assertTrue(t is KTypeParameter) + assertFalse((t as KTypeParameter).isReified) + assertEquals("T", (t as KTypeParameter).name) +} + +class C { + @OptIn(kotlin.ExperimentalStdlibApi::class) + fun foo() = typeOf>() +} + +@Test +fun test_class() { + val l = C().foo() + assertEquals(List::class, l.classifier) + val t = l.arguments.single().type!!.classifier + assertTrue(t is KTypeParameter) + assertFalse((t as KTypeParameter).isReified) + assertEquals("T", (t as KTypeParameter).name) +} + +@OptIn(kotlin.ExperimentalStdlibApi::class) +fun bar1() = typeOf>() + +@OptIn(kotlin.ExperimentalStdlibApi::class) +fun bar2() = typeOf>() + +class D { + @OptIn(kotlin.ExperimentalStdlibApi::class) + fun bar1() = typeOf>() +} + +@Test +fun test_equality() { + val t1 = bar1().arguments.single().type!!.classifier + val t2 = bar2().arguments.single().type!!.classifier + val t3 = D().bar1().arguments.single().type!!.classifier + assertNotEquals(t1, t2) + assertNotEquals(t1, t3) + assertNotEquals(t2, t3) + assertEquals(t1, bar1().arguments.single().type!!.classifier) + assertEquals(t2, bar2().arguments.single().type!!.classifier) + assertEquals(t3, D().bar1().arguments.single().type!!.classifier) +} + +@OptIn(kotlin.ExperimentalStdlibApi::class) +inline fun reifiedUpperBound() = typeOf>() + +@Test +fun test_reifiedUpperBound() { + val l = reifiedUpperBound() + assertEquals(List::class, l.classifier) + val r = l.arguments.single().type!!.classifier + assertTrue(r is KTypeParameter) + assertFalse((r as KTypeParameter).isReified) + assertEquals("R", (r as KTypeParameter).name) + val t = (r as KTypeParameter).upperBounds.single().classifier + assertTrue(t is KTypeParameter) + assertTrue((t as KTypeParameter).isReified) + assertEquals("T", (t as KTypeParameter).name) +} diff --git a/backend.native/tests/compilerChecks/t58.kt b/backend.native/tests/compilerChecks/t58.kt new file mode 100644 index 00000000000..e1a9ec73ef0 --- /dev/null +++ b/backend.native/tests/compilerChecks/t58.kt @@ -0,0 +1,7 @@ +import kotlin.reflect.* + +@OptIn(kotlin.ExperimentalStdlibApi::class) +fun > foo() { + typeOf>() +} + diff --git a/backend.native/tests/compilerChecks/t59.kt b/backend.native/tests/compilerChecks/t59.kt new file mode 100644 index 00000000000..66d180a17d0 --- /dev/null +++ b/backend.native/tests/compilerChecks/t59.kt @@ -0,0 +1,7 @@ +import kotlin.reflect.* + +@OptIn(kotlin.ExperimentalStdlibApi::class) +inline fun > foo() { + typeOf>() +} + diff --git a/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt b/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt index f5bc895dd7f..cf3185cd44d 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt @@ -25,6 +25,7 @@ internal class KTypeImpl( override fun toString(): String { val classifierString = when (classifier) { is KClass<*> -> classifier.qualifiedName ?: classifier.simpleName + is KTypeParameter -> classifier.name else -> null } ?: return "(non-denotable type)" @@ -37,16 +38,7 @@ internal class KTypeImpl( arguments.forEachIndexed { index, argument -> if (index > 0) append(", ") - if (argument.variance == null) { - append('*') - } else { - append(when (argument.variance) { - KVariance.INVARIANT -> "" - KVariance.IN -> "in " - KVariance.OUT -> "out " - }) - append(argument.type) - } + append(argument) } append('>') @@ -57,18 +49,18 @@ internal class KTypeImpl( } } -internal class KTypeImplForGenerics : KType { +internal class KTypeImplForTypeParametersWithRecursiveBounds : KType { override val classifier: KClassifier? - get() = error("Generic types are not yet supported in reflection") + get() = error("Type parameters with recursive bounds are not yet supported in reflection") override val arguments: List get() = emptyList() override val isMarkedNullable: Boolean - get() = error("Generic types are not yet supported in reflection") + get() = error("Type parameters with recursive bounds are not yet supported in reflection") override fun equals(other: Any?) = - error("Generic types are not yet supported in reflection") + error("Type parameters with recursive bounds are not yet supported in reflection") override fun hashCode(): Int = - error("Generic types are not yet supported in reflection") -} \ No newline at end of file + error("Type parameters with recursive bounds are not yet supported in reflection") +} diff --git a/runtime/src/main/kotlin/kotlin/native/internal/KTypeParameterImpl.kt b/runtime/src/main/kotlin/kotlin/native/internal/KTypeParameterImpl.kt new file mode 100644 index 00000000000..f7fe3b74877 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/native/internal/KTypeParameterImpl.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package kotlin.native.internal + +import kotlin.reflect.* + +internal class KTypeParameterImpl( + override val name: String, + private val containerFqName: String, + override val upperBounds: List, + override val variance: KVariance, + override val isReified: Boolean +) : KTypeParameter { + override fun toString(): String = when (variance) { + KVariance.INVARIANT -> "" + KVariance.IN -> "in " + KVariance.OUT -> "out " + } + name + + override fun equals(other: Any?) = + other is KTypeParameterImpl && name == other.name && containerFqName == other.containerFqName + + override fun hashCode() = containerFqName.hashCode() * 31 + name.hashCode() +} \ No newline at end of file