From 924fa6f9d3d16ef8e615ca639ba97d5646493aaa Mon Sep 17 00:00:00 2001 From: Georgy Bronnikov Date: Fri, 5 Jul 2019 19:21:38 +0300 Subject: [PATCH] JVM_IR: use DefaultParameterInjector --- .../common/descriptors/WrappedDescriptors.kt | 7 + .../kotlin/backend/common/ir/IrUtils.kt | 20 +- .../lower/DefaultArgumentStubGenerator.kt | 409 ++++++++++-------- .../kotlin/backend/jvm/JvmBackendContext.kt | 3 + .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 11 +- .../jvm/lower/InterfaceDelegationLowering.kt | 28 ++ .../jvm/lower/JvmDefaultParameterInjector.kt | 42 ++ .../lower/StaticDefaultFunctionLowering.kt | 73 +++- .../jvm/lower/SyntheticAccessorLowering.kt | 2 +- 9 files changed, 395 insertions(+), 200 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt index c1ca78317af..a3e2ec98bdd 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt @@ -975,6 +975,13 @@ open class WrappedFieldDescriptor( override fun getUserData(key: CallableDescriptor.UserDataKey?): V? = null } +fun wrappedSimpleFunctionDescriptorBasedOn(descriptor: SimpleFunctionDescriptor) = + if (descriptor is DescriptorWithContainerSource) + // TODO: Do we ever need annotations and source for these? + WrappedFunctionDescriptorWithContainerSource(descriptor.containerSource) + else + WrappedSimpleFunctionDescriptor(descriptor.annotations, descriptor.source) + private fun getContainingDeclaration(declaration: IrDeclarationWithName): DeclarationDescriptor { val parent = declaration.parent return if (parent is IrClass && parent.origin == IrDeclarationOrigin.FILE_CLASS && parent.parent is IrExternalPackageFragment) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt index 4974735fa39..c9d70d55624 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt @@ -549,15 +549,23 @@ fun createStaticFunctionWithReceivers( oldFunction.valueParameters.map { it.copyTo(this, index = it.index + offset) } ) - val mapping: Map = - (listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters) - .zip(valueParameters).toMap() if (copyBody) { - body = oldFunction.body - ?.transform(VariableRemapper(mapping), null) - ?.patchDeclarationParents(this) + copyBodyToStatic(oldFunction, this) } metadata = oldFunction.metadata } } + +fun copyBodyToStatic(oldFunction: IrFunction, staticFunction: IrFunction) { + val mapping: Map = + (listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters) + .zip(staticFunction.valueParameters).toMap() + staticFunction.body = oldFunction.body + ?.transform(VariableRemapper(mapping), null) + ?.patchDeclarationParents(staticFunction) +} + +fun IrClass.underlyingType() = if (isInline) + constructors.single { it.isPrimary }.valueParameters[0].type +else defaultType diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt index 639aff30605..5f5ba5c51da 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt @@ -5,19 +5,11 @@ package org.jetbrains.kotlin.backend.common.lower -import org.jetbrains.kotlin.backend.common.CommonBackendContext -import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.FunctionLoweringPass -import org.jetbrains.kotlin.backend.common.descriptors.WrappedClassConstructorDescriptor -import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor -import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor -import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName -import org.jetbrains.kotlin.backend.common.ir.copyTo -import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom -import org.jetbrains.kotlin.backend.common.ir.ir2string -import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom +import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.backend.common.descriptors.* +import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* @@ -25,8 +17,10 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl @@ -37,6 +31,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.safeAs // TODO: fix expect/actual default parameters @@ -206,173 +201,202 @@ private fun markerParameterDeclaration(function: IrFunction) = val DEFAULT_DISPATCH_CALL = object : IrStatementOriginImpl("DEFAULT_DISPATCH_CALL") {} open class DefaultParameterInjector( - val context: CommonBackendContext, + open val context: CommonBackendContext, private val skipInline: Boolean = true, - private val skipExternalMethods: Boolean = false -) : FileLoweringPass { + private val skipExternalMethods: Boolean = false, + private val shiftMaskForExtraArgs: Boolean = false +) : IrElementTransformerVoid(), BodyLoweringPass, FileLoweringPass { override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrElementTransformerVoid() { - private fun visitFunctionAccessExpression( - expression: IrFunctionAccessExpression, - builder: (IrFunctionSymbol) -> IrFunctionAccessExpression - ): IrExpression { - val functionDeclaration = expression.symbol.owner - - if (!functionDeclaration.needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) - return expression - - val argumentsCount = argumentCount(expression) - if (argumentsCount == functionDeclaration.valueParameters.size) - return expression - - val (symbol, params) = parametersForCall(expression) - val descriptor = symbol.descriptor - val declaration = symbol.owner - - for (i in 0 until expression.typeArgumentsCount) { - log { "$descriptor [$i]: $expression.getTypeArgument(i)" } - } - declaration.typeParameters.forEach { log { "$declaration[${it.index}] : $it" } } - - return builder(symbol).apply { - this.copyTypeArgumentsFrom(expression) - - params.forEach { - log { "call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}" } - putValueArgument(it.first.index, it.second) - } - - dispatchReceiver = expression.dispatchReceiver - extensionReceiver = expression.extensionReceiver - - log { "call::extension@: ${ir2string(expression.extensionReceiver)}" } - log { "call::dispatch@: ${ir2string(expression.dispatchReceiver)}" } - } - } - - override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { - super.visitDelegatingConstructorCall(expression) - - return visitFunctionAccessExpression(expression) { - IrDelegatingConstructorCallImpl( - startOffset = expression.startOffset, - endOffset = expression.endOffset, - type = context.irBuiltIns.unitType, - symbol = it as IrConstructorSymbol, - descriptor = it.descriptor, - typeArgumentsCount = expression.typeArgumentsCount - ) - } - } - - override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { - super.visitConstructorCall(expression) - - return visitFunctionAccessExpression(expression) { - IrConstructorCallImpl.fromSymbolOwner( - expression.startOffset, - expression.endOffset, - it.owner.returnType, - it as IrConstructorSymbol, - DEFAULT_DISPATCH_CALL - ) - } - } - - override fun visitCall(expression: IrCall): IrExpression { - super.visitCall(expression) - - return visitFunctionAccessExpression(expression) { - IrCallImpl( - startOffset = expression.startOffset, - endOffset = expression.endOffset, - type = it.owner.returnType, - symbol = it, - descriptor = it.descriptor, - typeArgumentsCount = expression.typeArgumentsCount, - origin = DEFAULT_DISPATCH_CALL, - superQualifierSymbol = expression.superQualifierSymbol - ) - } - } - - private fun IrFunction.findSuperMethodWithDefaultArguments(): IrFunction? { - if (!needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) return null - - if (this !is IrSimpleFunction) return this - - for (s in overriddenSymbols) { - s.owner.findSuperMethodWithDefaultArguments()?.let { return it } - } - - return this - } - - private fun parametersForCall(expression: IrFunctionAccessExpression): Pair>> { - val declaration = expression.symbol.owner - - val keyFunction = declaration.findSuperMethodWithDefaultArguments()!! - val realFunction = - keyFunction.generateDefaultsFunction(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, skipInline, skipExternalMethods) - - log { "$declaration -> $realFunction" } - val maskValues = Array((declaration.valueParameters.size + 31) / 32) { 0 } - val params = mutableListOf>() - params += declaration.valueParameters.mapIndexed { i, _ -> - val valueArgument = expression.getValueArgument(i) - if (valueArgument == null) { - val maskIndex = i / 32 - maskValues[maskIndex] = maskValues[maskIndex] or (1 shl (i % 32)) - } - val valueParameterDeclaration = realFunction.valueParameters[i] - val defaultValueArgument = if (valueParameterDeclaration.varargElementType != null) { - null - } else { - nullConst(expression, realFunction.valueParameters[i].type) - } - valueParameterDeclaration to (valueArgument ?: defaultValueArgument) - } - - val startOffset = expression.startOffset - val endOffset = expression.endOffset - maskValues.forEachIndexed { i, maskValue -> - params += maskParameterDeclaration(realFunction, i) to IrConstImpl.int( - startOffset = startOffset, - endOffset = endOffset, - type = context.irBuiltIns.intType, - value = maskValue - ) - } - if (expression.symbol is IrConstructorSymbol) { - val defaultArgumentMarker = context.ir.symbols.defaultConstructorMarker - params += markerParameterDeclaration(realFunction) to IrGetObjectValueImpl( - startOffset = startOffset, - endOffset = endOffset, - type = defaultArgumentMarker.owner.defaultType, - symbol = defaultArgumentMarker - ) - } else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) { - params += realFunction.valueParameters.last() to - IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType) - } - params.forEach { - log { "descriptor::${realFunction.name.asString()}#${it.first.index}: ${it.first.name.asString()}" } - } - return Pair(realFunction.symbol, params) - } - - private fun argumentCount(expression: IrMemberAccessExpression): Int { - var result = 0 - for (i in 0 until expression.valueArgumentsCount) { - expression.getValueArgument(i)?.run { ++result } - } - return result - } - }) + irFile.transformChildrenVoid(this) } - protected open fun nullConst(expression: IrElement, type: IrType) = when { + override fun lower(irBody: IrBody) { + irBody.transformChildrenVoid(this) + } + + private fun visitFunctionAccessExpression( + expression: IrFunctionAccessExpression, + builder: (IrFunctionSymbol) -> IrFunctionAccessExpression + ): IrExpression { + val functionDeclaration = expression.symbol.owner + + if (!functionDeclaration.needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) + return expression + + val argumentsCount = argumentCount(expression) + if (argumentsCount == functionDeclaration.valueParameters.size) + return expression + + val (symbol, params) = parametersForCall(expression, shiftMaskForExtraArgs) + val descriptor = symbol.descriptor + val declaration = symbol.owner + + for (i in 0 until expression.typeArgumentsCount) { + log { "$descriptor [$i]: $expression.getTypeArgument(i)" } + } + declaration.typeParameters.forEach { log { "$declaration[${it.index}] : $it" } } + + return builder(symbol).apply { + this.copyTypeArgumentsFrom(expression) + + params.forEach { + log { "call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}" } + putValueArgument(it.first.index, it.second) + } + + dispatchReceiver = expression.dispatchReceiver + extensionReceiver = expression.extensionReceiver + + log { "call::extension@: ${ir2string(expression.extensionReceiver)}" } + log { "call::dispatch@: ${ir2string(expression.dispatchReceiver)}" } + } + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { + super.visitDelegatingConstructorCall(expression) + + return visitFunctionAccessExpression(expression) { + IrDelegatingConstructorCallImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + type = context.irBuiltIns.unitType, + symbol = it as IrConstructorSymbol, + descriptor = it.descriptor, + typeArgumentsCount = expression.typeArgumentsCount + ) + } + } + + override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { + super.visitConstructorCall(expression) + + return visitFunctionAccessExpression(expression) { + IrConstructorCallImpl.fromSymbolOwner( + expression.startOffset, + expression.endOffset, + expression.type, + it as IrConstructorSymbol, + DEFAULT_DISPATCH_CALL + ) + } + } + + override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression { + super.visitEnumConstructorCall(expression) + + return visitFunctionAccessExpression(expression) { + IrEnumConstructorCallImpl( + expression.startOffset, + expression.endOffset, + expression.type, + it as IrConstructorSymbol + ) + } + } + + override fun visitCall(expression: IrCall): IrExpression { + super.visitCall(expression) + + return visitFunctionAccessExpression(expression) { + IrCallImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + type = expression.type, + symbol = it, + descriptor = it.descriptor, + typeArgumentsCount = expression.typeArgumentsCount, + origin = DEFAULT_DISPATCH_CALL, + superQualifierSymbol = expression.superQualifierSymbol + ) + } + } + + private fun IrFunction.findSuperMethodWithDefaultArguments(): IrFunction? { + if (!needsDefaultArgumentsLowering(skipInline, skipExternalMethods)) return null + + if (this !is IrSimpleFunction) return this + + for (s in overriddenSymbols) { + s.owner.findSuperMethodWithDefaultArguments()?.let { return it } + } + + return this + } + + private fun parametersForCall( + expression: IrFunctionAccessExpression, + shiftMaskForExtraArgs: Boolean + ): Pair>> { + val declaration = expression.symbol.owner + + val extraArgsShift = when { + shiftMaskForExtraArgs && declaration is IrConstructor && declaration.parentAsClass.isEnumClass -> 2 + shiftMaskForExtraArgs && declaration is IrConstructor && declaration.parentAsClass.isInner -> 1 // skip the `$outer` parameter + else -> 0 + } + + val keyFunction = declaration.findSuperMethodWithDefaultArguments()!! + val realFunction = + keyFunction.generateDefaultsFunction( + context, + IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, + skipInline, + skipExternalMethods + ) + + log { "$declaration -> $realFunction" } + val maskValues = Array((declaration.valueParameters.size - extraArgsShift + 31) / 32) { 0 } + val params = mutableListOf>() + params += declaration.valueParameters.mapIndexed { argIndex, _ -> + val valueArgument = expression.getValueArgument(argIndex) + val shiftedArgIndex = argIndex - extraArgsShift + if (valueArgument == null) { + val maskIndex = shiftedArgIndex / 32 + maskValues[maskIndex] = maskValues[maskIndex] or (1 shl (shiftedArgIndex % 32)) + } + val valueParameterDeclaration = realFunction.valueParameters[argIndex] + val defaultValueArgument = if (valueParameterDeclaration.varargElementType != null) { + null + } else { + nullConst(expression, realFunction.valueParameters[argIndex].type) + } + valueParameterDeclaration to (valueArgument ?: defaultValueArgument) + } + + val startOffset = expression.startOffset + val endOffset = expression.endOffset + maskValues.forEachIndexed { i, maskValue -> + params += maskParameterDeclaration(realFunction, i) to IrConstImpl.int( + startOffset = startOffset, + endOffset = endOffset, + type = context.irBuiltIns.intType, + value = maskValue + ) + } + if (expression.symbol is IrConstructorSymbol) { + val defaultArgumentMarker = context.ir.symbols.defaultConstructorMarker + params += markerParameterDeclaration(realFunction) to + IrConstImpl.constNull(startOffset, endOffset, defaultArgumentMarker.owner.defaultType) + } else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) { + params += realFunction.valueParameters.last() to + IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType) + } + params.forEach { + log { "descriptor::${realFunction.name.asString()}#${it.first.index}: ${it.first.name.asString()}" } + } + return Pair(realFunction.symbol, params) + } + + private fun argumentCount(expression: IrMemberAccessExpression): Int { + var result = 0 + for (i in 0 until expression.valueArgumentsCount) { + expression.getValueArgument(i)?.run { ++result } + } + return result + } + + protected open fun nullConst(expression: IrElement, type: IrType): IrExpression = when { type.isFloat() -> IrConstImpl.float(expression.startOffset, expression.endOffset, type, 0.0F) type.isDouble() -> IrConstImpl.double(expression.startOffset, expression.endOffset, type, 0.0) type.isBoolean() -> IrConstImpl.boolean(expression.startOffset, expression.endOffset, type, false) @@ -393,6 +417,16 @@ class DefaultParameterCleaner constructor(val context: CommonBackendContext) : F } } +// Some later lowerings produce bodies that need to undergo default parameter injection. +fun IrBody.insertCallsToDefaultArgumentStubs( + context: CommonBackendContext, + skipInline: Boolean = true, + skipExternalMethods: Boolean = false, + shiftMaskForExtraArgs: Boolean = false +) { + DefaultParameterInjector(context, skipInline, skipExternalMethods, shiftMaskForExtraArgs).lower(this) +} + // TODO this implementation is exponential private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean, skipExternalMethods: Boolean): Boolean { if (isInline && skipInlineMethods) return false @@ -434,7 +468,7 @@ private fun IrFunction.generateDefaultsFunctionImpl( } newFunction.copyTypeParametersFrom(this) - val newValueParameters = valueParameters.map { it.copyTo(newFunction) } + syntheticParameters + val newValueParameters = valueParameters.map { it.copyMaybeNullableTo(newFunction, context.irBuiltIns) } + syntheticParameters newValueParameters.forEach { it.defaultValue = null } @@ -485,7 +519,7 @@ private fun buildFunctionDeclaration(irFunction: IrFunction, origin: IrDeclarati } } is IrSimpleFunction -> { - val descriptor = WrappedSimpleFunctionDescriptor(irFunction.descriptor.annotations, irFunction.descriptor.source) + val descriptor = wrappedSimpleFunctionDescriptorBasedOn(irFunction.descriptor as SimpleFunctionDescriptor) val name = Name.identifier("${irFunction.name}\$default") return IrFunctionImpl( @@ -543,3 +577,26 @@ private fun IrFunction.valueParameter(index: Int, name: Name, type: IrType): IrV internal val kConstructorMarkerName = "marker".synthesizedName private fun parameterMaskName(number: Int) = "mask$number".synthesizedName + +private fun IrValueParameter.copyMaybeNullableTo(irFunction: IrFunction, irBuiltIns: IrBuiltIns): IrValueParameter { + if (defaultValue == null) return copyTo(irFunction) + val underlyingType = type.classOrNull?.owner?.underlyingType() ?: type + if (underlyingType in irBuiltIns.primitiveIrTypes) return copyTo(irFunction) + + val newType = type.remapTypeParameters( + (parent as IrTypeParametersContainer).classIfConstructor, + irFunction.classIfConstructor + ).makeNullable() + + val descriptor = WrappedValueParameterDescriptor(symbol.descriptor.annotations, symbol.descriptor.source) + return IrValueParameterImpl( + startOffset, endOffset, origin, IrValueParameterSymbolImpl(descriptor), + name, index, newType, varargElementType, isCrossinline, isNoinline + ).also { + descriptor.bind(it) + it.parent = irFunction + it.defaultValue = null + it.annotations.addAll(annotations.map { it.deepCopyWithSymbols() }) + } +} + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index f0d2e1d79ff..baef405506e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.name.FqName @@ -67,6 +68,8 @@ class JvmBackendContext( val suspendLambdaToOriginalFunctionMap = mutableMapOf() val continuationClassBuilders = mutableMapOf() + val staticDefaultStubs = mutableMapOf() + internal fun getTopLevelClass(fqName: FqName): IrClassSymbol { val descriptor = state.module.getPackage(fqName.parent()).memberScope.getContributedClassifier( fqName.shortName(), NoLookupLocation.FROM_BACKEND diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index cde3f211346..b4625bce8cb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -91,6 +91,13 @@ private val defaultArgumentStubPhase = makeIrFilePhase( prerequisite = setOf(localDeclarationsPhase) ) +private val defaultArgumentInjectorPhase = makeIrFilePhase( + ::JvmDefaultParameterInjector, + name = "DefaultParameterInjector", + description = "Transform calls with default arguments into calls to stubs", + prerequisite = setOf(defaultArgumentStubPhase) +) + private val innerClassesPhase = makeIrFilePhase( ::InnerClassesLowering, name = "InnerClasses", @@ -127,10 +134,12 @@ private val jvmFilePhases = callableReferencePhase then localDeclarationsPhase then defaultArgumentStubPhase then + defaultArgumentInjectorPhase then interfacePhase then interfaceDelegationPhase then interfaceSuperCallsPhase then + interfaceDefaultCallsPhase then singleAbstractMethodPhase then addContinuationPhase then @@ -145,13 +154,13 @@ private val jvmFilePhases = enumClassPhase then objectClassPhase then makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then - syntheticAccessorPhase then collectionStubMethodLowering then bridgePhase then jvmOverloadsAnnotationPhase then jvmDefaultConstructorPhase then jvmStaticAnnotationPhase then staticDefaultFunctionPhase then + syntheticAccessorPhase then toArrayPhase then flattenStringConcatenationPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt index bae840b4e1c..836a187d9f4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceDelegationLowering.kt @@ -211,6 +211,34 @@ private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrEl } } +internal val interfaceDefaultCallsPhase = makeIrFilePhase( + lowering = ::InterfaceDefaultCallsLowering, + name = "InterfaceDefaultCalls", + description = "Redirect interface calls with default arguments to DefaultImpls" +) + +private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass { + + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(this) + } + + override fun visitCall(expression: IrCall): IrExpression { + val callee = expression.symbol.owner + + if (callee.parent.safeAs()?.isInterface != true || + callee.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER + ) { + return super.visitCall(expression) + } + + val redirectTarget = context.declarationFactory.getDefaultImplsFunction(callee as IrSimpleFunction) + val newCall = irCall(expression, redirectTarget, receiversAsArguments = true) + + return super.visitCall(newCall) + } +} + private fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod() = resolveFakeOverride()?.let { origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB } == true || diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt new file mode 100644 index 00000000000..dc253602d7d --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultParameterInjector.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.ir.underlyingType +import org.jetbrains.kotlin.backend.common.lower.DefaultParameterInjector +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.types.* + +class JvmDefaultParameterInjector(context: JvmBackendContext) : + DefaultParameterInjector(context, skipInline = false, skipExternalMethods = false) { + + override val context: JvmBackendContext get() = super.context as JvmBackendContext + + override fun nullConst(expression: IrElement, type: IrType): IrExpression { + if (type !is IrSimpleType || + type.hasQuestionMark || + type.classOrNull?.owner?.isInline != true + ) { + return super.nullConst(expression, type) + } + val underlyingType = type.classOrNull!!.owner.underlyingType() + return IrCallImpl( + expression.startOffset, expression.endOffset, + type, + context.ir.symbols.unsafeCoerceIntrinsicSymbol, context.ir.symbols.unsafeCoerceIntrinsicSymbol.descriptor, + typeArgumentsCount = 2, + valueArgumentsCount = 1 + ).apply { + putTypeArgument(0, underlyingType) // from + putTypeArgument(1, type) // to + putValueArgument(0, super.nullConst(expression, underlyingType)) + } + } + +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt index 8cf0c8a7ea6..7ae05722410 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticDefaultFunctionLowering.kt @@ -16,30 +16,45 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.ir.copyBodyToStatic import org.jetbrains.kotlin.backend.common.ir.createStaticFunctionWithReceivers +import org.jetbrains.kotlin.backend.common.phaser.SameTypeNamedPhaseWrapper import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.common.phaser.then +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrReturn import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -internal val staticDefaultFunctionPhase = makeIrFilePhase( +private val functionDefinitionLoweringPhase = makeIrFilePhase( ::StaticDefaultFunctionLowering, - name = "StaticDefaultFunction", + name = "StaticDefaultFunctionDefinition", description = "Generate static functions for default parameters" ) -private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), ClassLoweringPass { - constructor(@Suppress("UNUSED_PARAMETER") context: BackendContext) : this() +private val callLoweringPhase = makeIrFilePhase( + ::StaticDefaultCallLowering, + name = "StaticDefaultCall", + description = "Generate calls of static functions for default parameters" +) - val updatedFunctions = hashMapOf() +internal val staticDefaultFunctionPhase = SameTypeNamedPhaseWrapper( + name = "StaticDefaultFunction", + description = "Make function adapters for default arguments static", + lower = functionDefinitionLoweringPhase then callLoweringPhase, + prerequisite = setOf(jvmStaticAnnotationPhase), + nlevels = 1 +) + +private class StaticDefaultFunctionLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), + ClassLoweringPass { override fun lower(irClass: IrClass) { irClass.accept(this, null) @@ -47,9 +62,9 @@ private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), Clas override fun visitFunction(declaration: IrFunction): IrStatement { return if (declaration.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && declaration.dispatchReceiverParameter != null) { - return createStaticFunctionWithReceivers(declaration.parent, declaration.name, declaration).also { - updatedFunctions[declaration.symbol] = it - super.visitFunction(declaration) + context.getStaticFunctionWithReceivers(declaration).also { + copyBodyToStatic(declaration, it) + super.visitFunction(it) } } else { super.visitFunction(declaration) @@ -58,9 +73,9 @@ private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), Clas override fun visitReturn(expression: IrReturn): IrExpression { return super.visitReturn( - if (updatedFunctions.containsKey(expression.returnTargetSymbol)) { + if (context.staticDefaultStubs.containsKey(expression.returnTargetSymbol)) { with(expression) { - val irFunction = updatedFunctions[expression.returnTargetSymbol]!! + val irFunction = context.staticDefaultStubs[expression.returnTargetSymbol]!! IrReturnImpl(startOffset, endOffset, expression.type, irFunction.symbol, expression.value) } } else { @@ -68,4 +83,30 @@ private class StaticDefaultFunctionLowering() : IrElementTransformerVoid(), Clas } ) } -} \ No newline at end of file +} + +private class StaticDefaultCallLowering( + val context: JvmBackendContext +) : IrElementTransformerVoid(), FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.accept(this, null) + } + + override fun visitCall(expression: IrCall): IrExpression { + val callee = expression.symbol.owner + if (callee.origin !== IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || callee.dispatchReceiverParameter == null) { + return super.visitCall(expression) + } + + val newCallee = context.getStaticFunctionWithReceivers(callee) + val newCall = irCall(expression, newCallee, receiversAsArguments = true) + + return super.visitCall(newCall) + } +} + +private fun JvmBackendContext.getStaticFunctionWithReceivers(function: IrFunction) = + staticDefaultStubs.getOrPut(function.symbol) { + createStaticFunctionWithReceivers(function.parent, function.name, function, copyBody = false) + } + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index fc78a600654..e0205c5be89 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -41,7 +41,7 @@ internal val syntheticAccessorPhase = makeIrFilePhase( ::SyntheticAccessorLowering, name = "SyntheticAccessor", description = "Introduce synthetic accessors", - prerequisite = setOf(objectClassPhase) + prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase) ) private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {