diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt new file mode 100644 index 00000000000..88b0bc74d5f --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrCatch +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.resolve.DescriptorUtils + +// TODO: synchronize with JVM BE +// TODO: rename the file. +class Closure(val capturedValues: List = emptyList()) + +class ClosureAnnotator { + private val closureBuilders = mutableMapOf() + + constructor(declaration: IrDeclaration) { + // Collect all closures for classes and functions. Collect call graph + declaration.acceptChildrenVoid(ClosureCollectorVisitor()) + } + + fun getFunctionClosure(descriptor: FunctionDescriptor) = getClosure(descriptor) + fun getClassClosure(descriptor: ClassDescriptor) = getClosure(descriptor) + + private fun getClosure(descriptor: DeclarationDescriptor) : Closure { + closureBuilders.values.forEach { it.processed = false } + return closureBuilders + .getOrElse(descriptor) { throw AssertionError("No closure builder for passed descriptor.") } + .buildClosure() + } + + private class ClosureBuilder(val owner: DeclarationDescriptor) { + val capturedValues = mutableSetOf() + private val declaredValues = mutableSetOf() + private val includes = mutableSetOf() + + var processed = false + + /* + * Node's closure = variables captured by the node + + * closure of all included nodes - + * variables declared in the node. + */ + fun buildClosure(): Closure { + val result = mutableSetOf().apply { addAll(capturedValues) } + includes.forEach { + if (!it.processed) { + it.processed = true + it.buildClosure().capturedValues.filterTo(result) { isExternal(it.descriptor) } + } + } + // TODO: We can save the closure and reuse it. + return Closure(result.toList()) + } + + + fun include(includingBuilder: ClosureBuilder) { + includes.add(includingBuilder) + } + + fun declareVariable(valueDescriptor: ValueDescriptor?) { + if (valueDescriptor != null) + declaredValues.add(valueDescriptor) + } + + fun seeVariable(value: IrValueSymbol) { + if (isExternal(value.descriptor)) + capturedValues.add(value) + } + + fun isExternal(valueDescriptor: ValueDescriptor): Boolean { + return !declaredValues.contains(valueDescriptor) + } + + } + + private inner class ClosureCollectorVisitor : IrElementVisitorVoid { + + val closuresStack = mutableListOf() + + fun includeInParent(builder: ClosureBuilder) { + // We don't include functions or classes in a parent function when they are declared. + // Instead we will include them when are is used (use = call for a function or constructor call for a class). + val parentBuilder = closuresStack.peek() + if (parentBuilder != null && parentBuilder.owner !is FunctionDescriptor) { + parentBuilder.include(builder) + } + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + val classDescriptor = declaration.descriptor + val closureBuilder = ClosureBuilder(classDescriptor) + closureBuilders[declaration.descriptor] = closureBuilder + + closureBuilder.declareVariable(classDescriptor.thisAsReceiverParameter) + if (classDescriptor.isInner) { + closureBuilder.declareVariable((classDescriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter) + includeInParent(closureBuilder) + } + + closuresStack.push(closureBuilder) + declaration.acceptChildrenVoid(this) + closuresStack.pop() + } + + override fun visitFunction(declaration: IrFunction) { + val functionDescriptor = declaration.descriptor + val closureBuilder = ClosureBuilder(functionDescriptor) + closureBuilders[functionDescriptor] = closureBuilder + + functionDescriptor.valueParameters.forEach { closureBuilder.declareVariable(it) } + closureBuilder.declareVariable(functionDescriptor.dispatchReceiverParameter) + closureBuilder.declareVariable(functionDescriptor.extensionReceiverParameter) + if (functionDescriptor is ConstructorDescriptor) { + closureBuilder.declareVariable(functionDescriptor.constructedClass.thisAsReceiverParameter) + // Include closure of the class in the constructor closure. + val classBuilder = closuresStack.peek() + classBuilder?.let { + assert(classBuilder.owner == functionDescriptor.constructedClass) + closureBuilder.include(classBuilder) + } + } + + closuresStack.push(closureBuilder) + declaration.acceptChildrenVoid(this) + closuresStack.pop() + + includeInParent(closureBuilder) + } + + override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) { + // Getter and setter of local delegated properties are special generated functions and don't have closure. + declaration.delegate.initializer?.acceptVoid(this) + } + + override fun visitVariableAccess(expression: IrValueAccessExpression) { + closuresStack.peek()?.seeVariable(expression.symbol) + super.visitVariableAccess(expression) + } + + override fun visitVariable(declaration: IrVariable) { + closuresStack.peek()?.declareVariable(declaration.descriptor) + super.visitVariable(declaration) + } + + override fun visitCatch(aCatch: IrCatch) { + closuresStack.peek()?.declareVariable(aCatch.parameter) + super.visitCatch(aCatch) + } + + // Process delegating constructor calls, enum constructor calls, calls and callable references. + override fun visitMemberAccess(expression: IrMemberAccessExpression) { + expression.acceptChildrenVoid(this) + val descriptor = expression.descriptor + if (DescriptorUtils.isLocal(descriptor)) { + val builder = closureBuilders[descriptor] + builder?.let { + closuresStack.peek()?.include(builder) + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 00000000000..288aa714638 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt @@ -0,0 +1,463 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.common.ir.ir2string +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.* +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.IrVariableImpl +import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl +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.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.builtIns + +open class DefaultArgumentStubGenerator constructor(val context: CommonBackendContext): DeclarationContainerLoweringPass { + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + irDeclarationContainer.declarations.transformFlat { memberDeclaration -> + if (memberDeclaration is IrFunction) + lower(memberDeclaration) + else + null + } + + } + + private val symbols = context.ir.symbols + + private fun lower(irFunction: IrFunction): List { + val functionDescriptor = irFunction.descriptor + + if (!functionDescriptor.needsDefaultArgumentsLowering) + return listOf(irFunction) + + val bodies = functionDescriptor.valueParameters + .mapNotNull{irFunction.getDefault(it)} + + + log("detected ${functionDescriptor.name.asString()} has got #${bodies.size} default expressions") + functionDescriptor.overriddenDescriptors.forEach { context.log{"DEFAULT-REPLACER: $it"} } + if (bodies.isNotEmpty()) { + val newIrFunction = functionDescriptor.generateDefaultsFunction(context) + val descriptor = newIrFunction.descriptor + log("$functionDescriptor -> $descriptor") + val builder = context.createIrBuilder(newIrFunction.symbol) + val body = builder.irBlockBody(irFunction) { + val params = mutableListOf() + val variables = mutableMapOf() + if (descriptor.extensionReceiverParameter != null) { + variables[functionDescriptor.extensionReceiverParameter!!] = + newIrFunction.extensionReceiverParameter!!.symbol + } + + for (valueParameter in functionDescriptor.valueParameters) { + val parameterSymbol = newIrFunction.valueParameters[valueParameter.index].symbol + val temporaryVariableSymbol = + IrVariableSymbolImpl(scope.createTemporaryVariableDescriptor(parameterSymbol.descriptor)) + params.add(temporaryVariableSymbol) + variables.put(valueParameter, temporaryVariableSymbol) + if (valueParameter.hasDefaultValue()) { + val kIntAnd = symbols.intAnd + val condition = irNotEquals(irCall(kIntAnd).apply { + dispatchReceiver = irGet(maskParameterSymbol(newIrFunction, valueParameter.index / 32)) + putValueArgument(0, irInt(1 shl (valueParameter.index % 32))) + }, irInt(0)) + val expressionBody = getDefaultParameterExpressionBody(irFunction, valueParameter) + + /* Use previously calculated values in next expression. */ + expressionBody.transformChildrenVoid(object:IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue): IrExpression { + log("GetValue: ${expression.descriptor}") + val valueSymbol = variables[expression.descriptor] ?: return expression + return irGet(valueSymbol) + } + }) + val variableInitialization = irIfThenElse( + type = temporaryVariableSymbol.descriptor.type, + condition = condition, + thenPart = expressionBody.expression, + elsePart = irGet(parameterSymbol)) + + scope.createTemporaryVariable( + symbol = temporaryVariableSymbol, + initializer = variableInitialization) + /* Mapping calculated values with its origin variables. */ + } else { + + scope.createTemporaryVariable( + symbol = temporaryVariableSymbol, + initializer = irGet(parameterSymbol)) + } + } + if (irFunction is IrConstructor) { + + IrDelegatingConstructorCallImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + symbol = irFunction.symbol, descriptor = irFunction.symbol.descriptor + ).apply { + params.forEachIndexed { i, variable -> + putValueArgument(i, irGet(variable)) + } + if (functionDescriptor.dispatchReceiverParameter != null) { + dispatchReceiver = irGet(newIrFunction.dispatchReceiverParameter!!.symbol) + } + } + } else { + +irReturn(irCall(irFunction.symbol).apply { + if (functionDescriptor.dispatchReceiverParameter != null) { + dispatchReceiver = irGet(newIrFunction.dispatchReceiverParameter!!.symbol) + } + if (functionDescriptor.extensionReceiverParameter != null) { + extensionReceiver = irGet(variables[functionDescriptor.extensionReceiverParameter!!]!!) + } + params.forEachIndexed { i, variable -> + putValueArgument(i, irGet(variable)) + } + }) + } + } + // Remove default argument initializers. + irFunction.valueParameters.forEach { + it.defaultValue = null + } + return if (functionDescriptor is ClassConstructorDescriptor) + listOf(irFunction, IrConstructorImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + descriptor = descriptor as ClassConstructorDescriptor, + origin = DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER, + body = body).apply { createParameterDeclarations() }) + else + listOf(irFunction, IrFunctionImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + descriptor = descriptor, + origin = DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER, + body = body).apply { createParameterDeclarations() }) + } + return listOf(irFunction) + } + + + private fun log(msg:String) = context.log{"DEFAULT-REPLACER: $msg"} +} + +private fun Scope.createTemporaryVariableDescriptor(parameterDescriptor: ParameterDescriptor?): VariableDescriptor = + IrTemporaryVariableDescriptorImpl( + containingDeclaration = this.scopeOwner, + name = parameterDescriptor!!.name.asString().synthesizedName, + outType = parameterDescriptor.type, + isMutable = false) + +private fun Scope.createTemporaryVariable(symbol: IrVariableSymbol, initializer: IrExpression) = + IrVariableImpl( + startOffset = initializer.startOffset, + endOffset = initializer.endOffset, + origin = IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, + symbol = symbol).apply { + + this.initializer = initializer + } + +private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody { + return irFunction.getDefault(valueParameter) as? IrExpressionBody ?: TODO("FIXME!!!") +} + +private fun maskParameterDescriptor(function: IrFunction, number: Int) = + maskParameterSymbol(function, number).descriptor as ValueParameterDescriptor +private fun maskParameterSymbol(function: IrFunction, number: Int) = + function.valueParameters.single { it.descriptor.name == parameterMaskName(number) }.symbol + +private fun markerParameterDescriptor(descriptor: FunctionDescriptor) = descriptor.valueParameters.single { it.name == kConstructorMarkerName } + +private fun nullConst(expression: IrElement, type: KotlinType): IrExpression? { + when { + KotlinBuiltIns.isFloat(type) -> return IrConstImpl.float (expression.startOffset, expression.endOffset, type, 0.0F) + KotlinBuiltIns.isDouble(type) -> return IrConstImpl.double (expression.startOffset, expression.endOffset, type, 0.0) + KotlinBuiltIns.isBoolean(type) -> return IrConstImpl.boolean (expression.startOffset, expression.endOffset, type, false) + KotlinBuiltIns.isByte(type) -> return IrConstImpl.byte (expression.startOffset, expression.endOffset, type, 0) + KotlinBuiltIns.isChar(type) -> return IrConstImpl.char (expression.startOffset, expression.endOffset, type, 0.toChar()) + KotlinBuiltIns.isShort(type) -> return IrConstImpl.short (expression.startOffset, expression.endOffset, type, 0) + KotlinBuiltIns.isInt(type) -> return IrConstImpl.int (expression.startOffset, expression.endOffset, type, 0) + KotlinBuiltIns.isLong(type) -> return IrConstImpl.long (expression.startOffset, expression.endOffset, type, 0) + else -> return IrConstImpl.constNull (expression.startOffset, expression.endOffset, type.builtIns.nullableNothingType) + } +} + +class DefaultParameterInjector constructor(val context: CommonBackendContext): BodyLoweringPass { + override fun lower(irBody: IrBody) { + + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { + super.visitDelegatingConstructorCall(expression) + val descriptor = expression.descriptor + if (!descriptor.needsDefaultArgumentsLowering) + return expression + val argumentsCount = argumentCount(expression) + if (argumentsCount == descriptor.valueParameters.size) + return expression + val (symbolForCall, params) = parametersForCall(expression) + return IrDelegatingConstructorCallImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + symbol = symbolForCall as IrConstructorSymbol, + descriptor = symbolForCall.descriptor) + .apply { + params.forEach { + log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}") + putValueArgument(it.first.index, it.second) + } + dispatchReceiver = expression.dispatchReceiver + } + + } + + override fun visitCall(expression: IrCall): IrExpression { + super.visitCall(expression) + val functionDescriptor = expression.descriptor + + if (!functionDescriptor.needsDefaultArgumentsLowering) + return expression + + val argumentsCount = argumentCount(expression) + if (argumentsCount == functionDescriptor.valueParameters.size) + return expression + val (symbol, params) = parametersForCall(expression) + val descriptor = symbol.descriptor + descriptor.typeParameters.forEach { log("$descriptor [${it.index}]: $it") } + descriptor.original.typeParameters.forEach { log("${descriptor.original}[${it.index}] : $it") } + return IrCallImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + symbol = symbol, + descriptor = descriptor, + typeArguments = expression.descriptor.typeParameters.map{it to (expression.getTypeArgument(it) ?: it.defaultType) }.toMap()) + .apply { + params.forEach { + log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}") + putValueArgument(it.first.index, it.second) + } + expression.extensionReceiver?.apply{ + extensionReceiver = expression.extensionReceiver + } + expression.dispatchReceiver?.apply { + dispatchReceiver = expression.dispatchReceiver + } + log("call::extension@: ${ir2string(expression.extensionReceiver)}") + log("call::dispatch@: ${ir2string(expression.dispatchReceiver)}") + } + } + + private fun parametersForCall(expression: IrMemberAccessExpression): Pair>> { + val descriptor = expression.descriptor as FunctionDescriptor + val keyDescriptor = if (DescriptorUtils.isOverride(descriptor)) + DescriptorUtils.getAllOverriddenDescriptors(descriptor).first() + else + descriptor.original + val realFunction = keyDescriptor.generateDefaultsFunction(context) + val realDescriptor = realFunction.descriptor + + log("$descriptor -> $realDescriptor") + val maskValues = Array(descriptor.valueParameters.size / 32 + 1, {0}) + val params = mutableListOf>() + params.addAll(descriptor.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 valueParameterDescriptor = realDescriptor.valueParameters[i] + val pair = valueParameterDescriptor to (valueArgument ?: nullConst(expression, valueParameterDescriptor.type)) + return@mapIndexed pair + }) + maskValues.forEachIndexed { i, maskValue -> + params += maskParameterDescriptor(realFunction, i) to IrConstImpl.int( + startOffset = irBody.startOffset, + endOffset = irBody.endOffset, + type = descriptor.builtIns.intType, + value = maskValue) + } + if (expression.descriptor is ClassConstructorDescriptor) { + val defaultArgumentMarker = context.ir.symbols.defaultConstructorMarker + params += markerParameterDescriptor(realDescriptor) to IrGetObjectValueImpl( + startOffset = irBody.startOffset, + endOffset = irBody.endOffset, + type = defaultArgumentMarker.owner.defaultType, + symbol = defaultArgumentMarker) + } + else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) { + params += realDescriptor.valueParameters.last() to + IrConstImpl.constNull(irBody.startOffset, irBody.endOffset, context.builtIns.any.defaultType) + } + params.forEach { + log("descriptor::${realDescriptor.name.asString()}#${it.first.index}: ${it.first.name.asString()}") + } + return Pair(realFunction.symbol, params) + } + + private fun argumentCount(expression: IrMemberAccessExpression) = + expression.descriptor.valueParameters.count { expression.getValueArgument(it) != null } + }) + } + + private fun log(msg: String) = context.log{"DEFAULT-INJECTOR: $msg"} +} + +private val CallableMemberDescriptor.needsDefaultArgumentsLowering + get() = valueParameters.any { it.hasDefaultValue() } && !(this is FunctionDescriptor && isInline) + +private fun FunctionDescriptor.generateDefaultsFunction(context: CommonBackendContext): IrFunction { + return context.ir.defaultParameterDeclarationsCache.getOrPut(this) { + val descriptor = when (this) { + is ClassConstructorDescriptor -> + ClassConstructorDescriptorImpl.create( + /* containingDeclaration = */ containingDeclaration, + /* annotations = */ annotations, + /* isPrimary = */ false, + /* source = */ source) + is FunctionDescriptor -> { + val name = Name.identifier("$name\$default") + + SimpleFunctionDescriptorImpl.create( + /* containingDeclaration = */ containingDeclaration, + /* annotations = */ annotations, + /* name = */ name, + /* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED, + /* source = */ source) + } + else -> TODO("FIXME: $this") + } + + val syntheticParameters = MutableList(valueParameters.size / 32 + 1) { i -> + valueParameter(descriptor, valueParameters.size + i, parameterMaskName(i), descriptor.builtIns.intType) + } + if (this is ClassConstructorDescriptor) { + syntheticParameters += valueParameter(descriptor, syntheticParameters.last().index + 1, + kConstructorMarkerName, + context.ir.symbols.defaultConstructorMarker.owner.defaultType) + } + else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) { + syntheticParameters += valueParameter(descriptor, syntheticParameters.last().index + 1, + "handler".synthesizedName, + context.ir.symbols.any.owner.defaultType) + } + + descriptor.initialize( + /* receiverParameterType = */ extensionReceiverParameter?.type, + /* dispatchReceiverParameter = */ dispatchReceiverParameter, + /* typeParameters = */ typeParameters.map { + TypeParameterDescriptorImpl.createForFurtherModification( + /* containingDeclaration = */ descriptor, + /* annotations = */ it.annotations, + /* reified = */ it.isReified, + /* variance = */ it.variance, + /* name = */ it.name, + /* index = */ it.index, + /* source = */ it.source, + /* reportCycleError = */ null, + /* supertypeLoopsChecker = */ SupertypeLoopChecker.EMPTY + ).apply { + it.upperBounds.forEach { addUpperBound(it) } + setInitialized() + } + }, + /* unsubstitutedValueParameters = */ valueParameters.map { + ValueParameterDescriptorImpl( + containingDeclaration = descriptor, + original = null, /* ValueParameterDescriptorImpl::copy do not save original. */ + index = it.index, + annotations = it.annotations, + name = it.name, + outType = it.type, + declaresDefaultValue = false, + isCrossinline = it.isCrossinline, + isNoinline = it.isNoinline, + varargElementType = it.varargElementType, + source = it.source) + } + syntheticParameters, + /* unsubstitutedReturnType = */ returnType, + /* modality = */ Modality.FINAL, + /* visibility = */ this.visibility) + descriptor.isSuspend = this.isSuspend + context.log{"adds to cache[$this] = $descriptor"} + + val startOffset = this.startOffsetOrUndefined + val endOffset = this.endOffsetOrUndefined + + val result: IrFunction = when (descriptor) { + is ClassConstructorDescriptor -> IrConstructorImpl( + startOffset, endOffset, + DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER, + descriptor + ) + + else -> IrFunctionImpl( + startOffset, endOffset, + DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER, + descriptor + ) + } + + result.createParameterDeclarations() + + result + } +} + +object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER : + IrDeclarationOriginImpl("DEFAULT_PARAMETER_EXTENT") + +private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: Name, type: KotlinType):ValueParameterDescriptor { + return ValueParameterDescriptorImpl( + containingDeclaration = descriptor, + original = null, + index = index, + annotations = Annotations.EMPTY, + name = name, + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = SourceElement.NO_SOURCE + ) +} + +internal val kConstructorMarkerName = "marker".synthesizedName + +private fun parameterMaskName(number: Int) = "mask$number".synthesizedName diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IrBuildUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IrBuildUtils.kt new file mode 100644 index 00000000000..841b6d11649 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IrBuildUtils.kt @@ -0,0 +1,216 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl +import org.jetbrains.kotlin.ir.util.createParameterDeclarations +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType + +abstract class SymbolWithIrBuilder { + + protected abstract fun buildSymbol(): S + + protected open fun doInitialize() { } + + protected abstract fun buildIr(): D + + val symbol by lazy { buildSymbol() } + + private val builtIr by lazy { buildIr() } + private var initialized: Boolean = false + + fun initialize() { + doInitialize() + initialized = true + } + + val ir: D + get() { + if (!initialized) + throw Error("Access to IR before initialization") + return builtIr + } +} + +fun BackendContext.createPropertyGetterBuilder(startOffset: Int, endOffset: Int, origin: IrDeclarationOrigin, + fieldSymbol: IrFieldSymbol, type: KotlinType) + = object: SymbolWithIrBuilder() { + + override fun buildSymbol() = IrSimpleFunctionSymbolImpl( + PropertyGetterDescriptorImpl( + /* correspondingProperty = */ fieldSymbol.descriptor, + /* annotations = */ Annotations.EMPTY, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE, + /* isDefault = */ false, + /* isExternal = */ false, + /* isInline = */ false, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* original = */ null, + /* source = */ SourceElement.NO_SOURCE + ) + ) + + override fun doInitialize() { + val descriptor = symbol.descriptor as PropertyGetterDescriptorImpl + descriptor.apply { + initialize(type) + } + } + + override fun buildIr() = IrFunctionImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = origin, + symbol = symbol).apply { + + createParameterDeclarations() + + body = createIrBuilder(this.symbol, startOffset, endOffset).irBlockBody { + +irReturn(irGetField(irGet(this@apply.dispatchReceiverParameter!!.symbol), fieldSymbol)) + } + } +} + +private fun BackendContext.createPropertySetterBuilder(startOffset: Int, endOffset: Int, origin: IrDeclarationOrigin, + fieldSymbol: IrFieldSymbol, type: KotlinType) + = object: SymbolWithIrBuilder() { + + override fun buildSymbol() = IrSimpleFunctionSymbolImpl( + PropertySetterDescriptorImpl( + /* correspondingProperty = */ fieldSymbol.descriptor, + /* annotations = */ Annotations.EMPTY, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE, + /* isDefault = */ false, + /* isExternal = */ false, + /* isInline = */ false, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* original = */ null, + /* source = */ SourceElement.NO_SOURCE + ) + ) + + lateinit var valueParameterDescriptor: ValueParameterDescriptor + + override fun doInitialize() { + val descriptor = symbol.descriptor as PropertySetterDescriptorImpl + descriptor.apply { + valueParameterDescriptor = ValueParameterDescriptorImpl( + containingDeclaration = this, + original = null, + index = 0, + annotations = Annotations.EMPTY, + name = Name.identifier("value"), + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = SourceElement.NO_SOURCE + ) + + initialize(valueParameterDescriptor) + } + } + + override fun buildIr() = IrFunctionImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = origin, + symbol = symbol).apply { + + createParameterDeclarations() + + body = createIrBuilder(this.symbol, startOffset, endOffset).irBlockBody { + +irSetField(irGet(this@apply.dispatchReceiverParameter!!.symbol), fieldSymbol, irGet(this@apply.valueParameters.single().symbol)) + } + } +} + +fun BackendContext.createPropertyWithBackingFieldBuilder(startOffset: Int, endOffset: Int, origin: IrDeclarationOrigin, + owner: ClassDescriptor, name: Name, type: KotlinType, isMutable: Boolean) + = object: SymbolWithIrBuilder() { + + private lateinit var getterBuilder: SymbolWithIrBuilder + private var setterBuilder: SymbolWithIrBuilder? = null + + override fun buildSymbol() = IrFieldSymbolImpl( + PropertyDescriptorImpl.create( + /* containingDeclaration = */ owner, + /* annotations = */ Annotations.EMPTY, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE, + /* isVar = */ isMutable, + /* name = */ name, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* source = */ SourceElement.NO_SOURCE, + /* lateInit = */ false, + /* isConst = */ false, + /* isHeader = */ false, + /* isImpl = */ false, + /* isExternal = */ false, + /* isDelegated = */ false + ) + ) + + override fun doInitialize() { + val descriptor = symbol.descriptor as PropertyDescriptorImpl + getterBuilder = createPropertyGetterBuilder(startOffset, endOffset, origin, symbol, type).apply { initialize() } + if (isMutable) + setterBuilder = createPropertySetterBuilder(startOffset, endOffset, origin, symbol, type).apply { initialize() } + descriptor.initialize( + /* getter = */ getterBuilder.symbol.descriptor as PropertyGetterDescriptorImpl, + /* setter = */ setterBuilder?.symbol?.descriptor as? PropertySetterDescriptorImpl) + val receiverType: KotlinType? = null + descriptor.setType(type, emptyList(), owner.thisAsReceiverParameter, receiverType) + } + + override fun buildIr(): IrProperty { + val backingField = IrFieldImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = origin, + symbol = symbol) + return IrPropertyImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = origin, + isDelegated = false, + descriptor = symbol.descriptor, + backingField = backingField, + getter = getterBuilder.ir, + setter = setterBuilder?.ir) + } +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt new file mode 100644 index 00000000000..739755a10a1 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.expressions.IrBlock +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.types.KotlinType + +class LateinitLowering(val context: CommonBackendContext): FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(object: IrElementTransformerVoid() { + override fun visitProperty(declaration: IrProperty): IrStatement { + if (declaration.descriptor.isLateInit && declaration.descriptor.kind.isReal) + transformGetter(declaration.backingField!!.symbol, declaration.getter!!) + return declaration + } + + private fun transformGetter(backingFieldSymbol: IrFieldSymbol, getter: IrFunction) { + val type = backingFieldSymbol.descriptor.type + assert (!KotlinBuiltIns.isPrimitiveType(type), { "'lateinit' modifier is not allowed on primitive types" }) + val startOffset = getter.startOffset + val endOffset = getter.endOffset + val irBuilder = context.createIrBuilder(getter.symbol, startOffset, endOffset) + irBuilder.run { + val block = irBlock(type) + val resultVar = scope.createTemporaryVariable( + irGetField(irGet(getter.dispatchReceiverParameter!!.symbol), backingFieldSymbol)) + block.statements.add(resultVar) + val throwIfNull = irIfThenElse(context.builtIns.nothingType, + irNotEquals(irGet(resultVar.symbol), irNull()), + irReturn(irGet(resultVar.symbol)), + irCall(throwErrorFunction)) + block.statements.add(throwIfNull) + getter.body = IrExpressionBodyImpl(startOffset, endOffset, block) + } + } + }) + } + + private val throwErrorFunction = context.ir.symbols.ThrowUninitializedPropertyAccessException + + private fun IrBuilderWithScope.irBlock(type: KotlinType): IrBlock + = IrBlockImpl(startOffset, endOffset, type) + +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt new file mode 100644 index 00000000000..b2652a8f521 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -0,0 +1,729 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.* +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.util.createParameterDeclarations +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.parents +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.types.KotlinType +import java.util.* + +class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContainerLoweringPass { + + private object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE : + IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE") {} + + private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE : + IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE") {} + + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + if (irDeclarationContainer is IrDeclaration && + irDeclarationContainer.descriptor.parents.any { it is CallableDescriptor }) { + + // Lowering of non-local declarations handles all local declarations inside. + // This declaration is local and shouldn't be considered. + return + } + + // Continuous numbering across all declarations in the container. + lambdasCount = 0 + + irDeclarationContainer.declarations.transformFlat { memberDeclaration -> + // TODO: may be do the opposite - specify the list of IR elements which need not to be transformed + when (memberDeclaration) { + is IrFunction -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations() + is IrProperty -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations() + is IrField -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations() + is IrAnonymousInitializer -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations() + else -> null + } + } + } + + private var lambdasCount = 0 + + private abstract class LocalContext { + /** + * @return the expression to get the value for given descriptor, or `null` if [IrGetValue] should be used. + */ + abstract fun irGet(startOffset: Int, endOffset: Int, descriptor: ValueDescriptor): IrExpression? + } + + private abstract class LocalContextWithClosureAsParameters : LocalContext() { + + abstract val declaration: IrFunction + open val descriptor: FunctionDescriptor + get() = declaration.descriptor + + abstract val transformedDescriptor: FunctionDescriptor + abstract val transformedDeclaration: IrFunction + + val capturedValueToParameter: MutableMap = HashMap() + + override fun irGet(startOffset: Int, endOffset: Int, descriptor: ValueDescriptor): IrExpression? { + val newSymbol = capturedValueToParameter[descriptor] ?: return null + + return IrGetValueImpl(startOffset, endOffset, newSymbol) + } + } + + private class LocalFunctionContext(override val declaration: IrFunction) : LocalContextWithClosureAsParameters() { + lateinit var closure: Closure + + override lateinit var transformedDescriptor: FunctionDescriptor + override lateinit var transformedDeclaration: IrSimpleFunction + + var index: Int = -1 + + override fun toString(): String = + "LocalFunctionContext for $descriptor" + } + + private class LocalClassConstructorContext(override val declaration: IrConstructor) : LocalContextWithClosureAsParameters() { + override val descriptor: ClassConstructorDescriptor + get() = declaration.descriptor + + override lateinit var transformedDescriptor: ClassConstructorDescriptor + override lateinit var transformedDeclaration: IrConstructor + + override fun toString(): String = + "LocalClassConstructorContext for $descriptor" + } + + private class LocalClassContext(val declaration: IrClass) : LocalContext() { + val descriptor: ClassDescriptor + get() = declaration.descriptor + + lateinit var closure: Closure + + val capturedValueToField: MutableMap = HashMap() + + override fun irGet(startOffset: Int, endOffset: Int, descriptor: ValueDescriptor): IrExpression? { + val field = capturedValueToField[descriptor] ?: return null + + return IrGetFieldImpl(startOffset, endOffset, field.symbol, + receiver = IrGetValueImpl(startOffset, endOffset, declaration.thisReceiver!!.symbol) + ) + } + + override fun toString(): String = + "LocalClassContext for ${descriptor}" + } + + private inner class LocalDeclarationsTransformer(val memberDeclaration: IrDeclaration) { + val localFunctions: MutableMap = LinkedHashMap() + val localClasses: MutableMap = LinkedHashMap() + val localClassConstructors: MutableMap = LinkedHashMap() + + val transformedDeclarations = mutableMapOf() + + val FunctionDescriptor.transformed: IrFunctionSymbol? + get() = transformedDeclarations[this] as IrFunctionSymbol? + + val oldParameterToNew: MutableMap = HashMap() + val newParameterToOld: MutableMap = HashMap() + val newParameterToCaptured: MutableMap = HashMap() + + fun lowerLocalDeclarations(): List? { + collectLocalDeclarations() + if (localFunctions.isEmpty() && localClasses.isEmpty()) return null + + collectClosures() + + transformDescriptors() + + rewriteDeclarations() + + val result = collectRewrittenDeclarations() + return result + } + + private fun collectRewrittenDeclarations(): ArrayList = + ArrayList(localFunctions.size + localClasses.size + 1).apply { + add(memberDeclaration) + + localFunctions.values.mapTo(this) { + val original = it.declaration + it.transformedDeclaration.apply { + this.body = original.body + + original.descriptor.valueParameters.filter { it.declaresDefaultValue() }.forEach { argument -> + val body = original.getDefault(argument)!! + oldParameterToNew[argument]!!.owner.defaultValue = body + } + } + } + + localClasses.values.mapTo(this) { + it.declaration + } + } + + private inner class FunctionBodiesRewriter(val localContext: LocalContext?) : IrElementTransformerVoid() { + + override fun visitClass(declaration: IrClass): IrStatement { + if (declaration.descriptor in localClasses) { + // Replace local class definition with an empty composite. + return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType) + } else { + return super.visitClass(declaration) + } + } + + override fun visitFunction(declaration: IrFunction): IrStatement { + if (declaration.descriptor in localFunctions) { + // Replace local function definition with an empty composite. + return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType) + } else { + return super.visitFunction(declaration) + } + } + + override fun visitConstructor(declaration: IrConstructor): IrStatement { + // Body is transformed separately. See loop over constructors in rewriteDeclarations(). + + val constructorContext = localClassConstructors[declaration.descriptor] + if (constructorContext != null) { + return constructorContext.transformedDeclaration.apply { + this.body = declaration.body!! + + declaration.descriptor.valueParameters.filter { it.declaresDefaultValue() }.forEach { argument -> + val body = declaration.getDefault(argument)!! + oldParameterToNew[argument]!!.owner.defaultValue = body + } + } + } else { + return super.visitConstructor(declaration) + } + } + + override fun visitGetValue(expression: IrGetValue): IrExpression { + val descriptor = expression.descriptor + + localContext?.irGet(expression.startOffset, expression.endOffset, descriptor)?.let { + return it + } + + oldParameterToNew[descriptor]?.let { + return IrGetValueImpl(expression.startOffset, expression.endOffset, it) + } + + return expression + } + + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + + val oldCallee = expression.descriptor.original + val newCallee = oldCallee.transformed ?: return expression + + val newCall = createNewCall(expression, newCallee).fillArguments(expression) + + return newCall + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { + expression.transformChildrenVoid(this) + + val oldCallee = expression.descriptor.original + val newCallee = transformedDeclarations[oldCallee] as IrConstructorSymbol? ?: return expression + + val newExpression = IrDelegatingConstructorCallImpl( + expression.startOffset, expression.endOffset, + newCallee, + newCallee.descriptor, + remapTypeArguments(expression, newCallee.descriptor) + ).fillArguments(expression) + + return newExpression + } + + private fun T.fillArguments(oldExpression: IrMemberAccessExpression): T { + + mapValueParameters { newValueParameterDescriptor -> + val oldParameter = newParameterToOld[newValueParameterDescriptor] + + if (oldParameter != null) { + oldExpression.getValueArgument(oldParameter as ValueParameterDescriptor) + } else { + // The callee expects captured value as argument. + val capturedValueSymbol = + newParameterToCaptured[newValueParameterDescriptor] ?: + throw AssertionError("Non-mapped parameter $newValueParameterDescriptor") + + val capturedValueDescriptor = capturedValueSymbol.descriptor + localContext?.irGet( + oldExpression.startOffset, oldExpression.endOffset, + capturedValueDescriptor + ) ?: + // Captured value is directly available for the caller. + IrGetValueImpl(oldExpression.startOffset, oldExpression.endOffset, + oldParameterToNew[capturedValueDescriptor] ?: capturedValueSymbol) + } + + } + + dispatchReceiver = oldExpression.dispatchReceiver + extensionReceiver = oldExpression.extensionReceiver + + return this + } + + override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { + expression.transformChildrenVoid(this) + + val oldCallee = expression.descriptor.original + val newCallee = oldCallee.transformed ?: return expression + + val newCallableReference = IrFunctionReferenceImpl( + expression.startOffset, expression.endOffset, + expression.type, // TODO functional type for transformed descriptor + newCallee, + newCallee.descriptor, + remapTypeArguments(expression, newCallee.descriptor), + expression.origin + ).fillArguments(expression) + + return newCallableReference + } + + override fun visitReturn(expression: IrReturn): IrExpression { + expression.transformChildrenVoid(this) + + val oldReturnTarget = expression.returnTarget + val newReturnTarget = oldReturnTarget.transformed ?: return expression + + return IrReturnImpl(expression.startOffset, expression.endOffset, newReturnTarget, expression.value) + } + + override fun visitDeclarationReference(expression: IrDeclarationReference): IrExpression { + if (expression.descriptor in transformedDeclarations) { + TODO() + } + return super.visitDeclarationReference(expression) + } + + override fun visitDeclaration(declaration: IrDeclaration): IrStatement { + if (declaration.descriptor in transformedDeclarations) { + TODO() + } + return super.visitDeclaration(declaration) + } + } + + private fun rewriteFunctionBody(irDeclaration: IrDeclaration, localContext: LocalContext?) { + irDeclaration.transformChildrenVoid(FunctionBodiesRewriter(localContext)) + } + + private fun rewriteClassMembers(irClass: IrClass, localClassContext: LocalClassContext) { + irClass.transformChildrenVoid(FunctionBodiesRewriter(localClassContext)) + + val classDescriptor = irClass.descriptor + val constructorsCallingSuper = classDescriptor.constructors + .map { localClassConstructors[it]!! } + .filter { it.declaration.callsSuper() } + assert(constructorsCallingSuper.any(), { "Expected at least one constructor calling super; class: $classDescriptor" }) + + localClassContext.capturedValueToField.forEach { capturedValue, field -> + val startOffset = irClass.startOffset + val endOffset = irClass.endOffset + irClass.declarations.add(field) + + for (constructorContext in constructorsCallingSuper) { + val blockBody = constructorContext.declaration.body as? IrBlockBody + ?: throw AssertionError("Unexpected constructor body: ${constructorContext.declaration.body}") + val capturedValueExpression = constructorContext.irGet(startOffset, endOffset, capturedValue)!! + blockBody.statements.add(0, + IrSetFieldImpl(startOffset, endOffset, field.symbol, + IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol), + capturedValueExpression, STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE)) + } + } + } + + private fun rewriteDeclarations() { + localFunctions.values.forEach { + rewriteFunctionBody(it.declaration, it) + } + + localClassConstructors.values.forEach { + rewriteFunctionBody(it.declaration, it) + } + + localClasses.values.forEach { + rewriteClassMembers(it.declaration, it) + } + + rewriteFunctionBody(memberDeclaration, null) + } + + private fun createNewCall(oldCall: IrCall, newCallee: IrFunctionSymbol) = + if (oldCall is IrCallWithShallowCopy) + oldCall.shallowCopy(oldCall.origin, newCallee, oldCall.superQualifierSymbol) + else + IrCallImpl( + oldCall.startOffset, oldCall.endOffset, + newCallee, + newCallee.descriptor, + remapTypeArguments(oldCall, newCallee.descriptor), + oldCall.origin, oldCall.superQualifierSymbol + ) + + private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: CallableDescriptor): Map? { + val oldCallee = oldExpression.descriptor.original + + return if (oldCallee.typeParameters.isEmpty()) + null + else oldCallee.typeParameters.associateBy( + { newCallee.typeParameters[it.index] }, + { oldExpression.getTypeArgumentOrDefault(it) } + ) + } + + private fun transformDescriptors() { + localFunctions.values.forEach { + createLiftedDescriptor(it) + } + + localClasses.values.forEach { + createFieldsForCapturedValues(it) + } + + localClassConstructors.values.forEach { + createTransformedConstructorDescriptor(it) + } + } + + private fun suggestLocalName(descriptor: DeclarationDescriptor): String { + localFunctions[descriptor]?.let { + if (it.index >= 0) + return "lambda-${it.index}" + } + + return descriptor.name.asString() + } + + private fun generateNameForLiftedDeclaration(descriptor: DeclarationDescriptor, + newOwner: DeclarationDescriptor): Name = + Name.identifier( + descriptor.parentsWithSelf + .takeWhile { it != newOwner } + .toList().reversed() + .map { suggestLocalName(it) } + .joinToString(separator = "$") + ) + + private fun createLiftedDescriptor(localFunctionContext: LocalFunctionContext) { + val oldDescriptor = localFunctionContext.descriptor + + val memberOwner = memberDeclaration.descriptor.containingDeclaration!! + val newDescriptor = SimpleFunctionDescriptorImpl.create( + memberOwner, + oldDescriptor.annotations, + generateNameForLiftedDeclaration(oldDescriptor, memberOwner), + CallableMemberDescriptor.Kind.SYNTHESIZED, + oldDescriptor.source + ).apply { + isTailrec = oldDescriptor.isTailrec + isSuspend = oldDescriptor.isSuspend + // TODO: copy other properties or consider using `FunctionDescriptor.CopyBuilder`. + } + + localFunctionContext.transformedDescriptor = newDescriptor + + if (oldDescriptor.dispatchReceiverParameter != null) { + throw AssertionError("local functions must not have dispatch receiver") + } + + val newDispatchReceiverParameter = null + + // Do not substitute type parameters for now. + val newTypeParameters = oldDescriptor.typeParameters + + // TODO: consider using fields to access the closure of enclosing class. + val capturedValues = localFunctionContext.closure.capturedValues + + val newValueParameters = createTransformedValueParameters(localFunctionContext, capturedValues) + + newDescriptor.initialize( + oldDescriptor.extensionReceiverParameter?.type, + newDispatchReceiverParameter, + newTypeParameters, + newValueParameters, + oldDescriptor.returnType, + Modality.FINAL, + Visibilities.PRIVATE + ) + + oldDescriptor.extensionReceiverParameter?.let { + newParameterToOld.putAbsentOrSame(newDescriptor.extensionReceiverParameter!!, it) + } + + localFunctionContext.transformedDeclaration = with(localFunctionContext.declaration) { + IrFunctionImpl(startOffset, endOffset, origin, newDescriptor) + }.apply { + createParameterDeclarations() + recordTransformedValueParameters(localFunctionContext) + transformedDeclarations[oldDescriptor] = this.symbol + } + } + + private fun createTransformedValueParameters(localContext: LocalContextWithClosureAsParameters, + capturedValues: List) + : List { + + val oldDescriptor = localContext.descriptor + val newDescriptor = localContext.transformedDescriptor + + val closureParametersCount = capturedValues.size + val newValueParametersCount = closureParametersCount + oldDescriptor.valueParameters.size + + val newValueParameters = ArrayList(newValueParametersCount).apply { + capturedValues.mapIndexedTo(this) { i, capturedValue -> + createUnsubstitutedCapturedValueParameter(newDescriptor, capturedValue.descriptor, i).apply { + newParameterToCaptured[this] = capturedValue + } + } + + oldDescriptor.valueParameters.mapIndexedTo(this) { i, oldValueParameterDescriptor -> + createUnsubstitutedParameter(newDescriptor, oldValueParameterDescriptor, closureParametersCount + i).apply { + newParameterToOld.putAbsentOrSame(this, oldValueParameterDescriptor) + } + } + } + return newValueParameters + } + + private fun IrFunction.recordTransformedValueParameters(localContext: LocalContextWithClosureAsParameters) { + + valueParameters.forEach { + val capturedValue = newParameterToCaptured[it.descriptor] + if (capturedValue != null) { + localContext.capturedValueToParameter[capturedValue.descriptor] = it.symbol + } + } + + (listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters).forEach { + val oldParameter = newParameterToOld[it.descriptor] + if (oldParameter != null) { + oldParameterToNew.putAbsentOrSame(oldParameter, it.symbol) + } + } + + } + + private fun createTransformedConstructorDescriptor(constructorContext: LocalClassConstructorContext) { + val oldDescriptor = constructorContext.descriptor + val localClassContext = localClasses[oldDescriptor.containingDeclaration]!! + val newDescriptor = ClassConstructorDescriptorImpl.create( + localClassContext.descriptor, + Annotations.EMPTY, oldDescriptor.isPrimary, oldDescriptor.source) + + constructorContext.transformedDescriptor = newDescriptor + + // Do not substitute type parameters for now. + val newTypeParameters = oldDescriptor.typeParameters + + val capturedValues = localClasses[oldDescriptor.containingDeclaration]!!.closure.capturedValues + + val newValueParameters = createTransformedValueParameters(constructorContext, capturedValues) + + newDescriptor.initialize( + newValueParameters, + Visibilities.PRIVATE, + newTypeParameters + ) + newDescriptor.returnType = oldDescriptor.returnType + + oldDescriptor.dispatchReceiverParameter?.let { + newParameterToOld.putAbsentOrSame(newDescriptor.dispatchReceiverParameter!!, it) + } + + oldDescriptor.extensionReceiverParameter?.let { + throw AssertionError("constructors can't have extension receiver") + } + + constructorContext.transformedDeclaration = with(constructorContext.declaration) { + IrConstructorImpl(startOffset, endOffset, origin, newDescriptor) + }.apply { + createParameterDeclarations() + recordTransformedValueParameters(constructorContext) + transformedDeclarations[oldDescriptor] = this.symbol + } + } + + private fun createFieldsForCapturedValues(localClassContext: LocalClassContext) { + val classDescriptor = localClassContext.descriptor + + localClassContext.closure.capturedValues.forEach { capturedValue -> + val fieldDescriptor = PropertyDescriptorImpl.create( + classDescriptor, + Annotations.EMPTY, + Modality.FINAL, + Visibilities.PRIVATE, + /* isVar = */ false, + suggestNameForCapturedValue(capturedValue.descriptor), + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE, + /* lateInit = */ false, + /* isConst = */ false, + /* isHeader = */ false, + /* isImpl = */ false, + /* isExternal = */ false, + /* isDelegated = */ false) + + fieldDescriptor.initialize(/* getter = */ null, /* setter = */ null) + + val extensionReceiverParameter: ReceiverParameterDescriptor? = null + + fieldDescriptor.setType( + capturedValue.descriptor.type, + emptyList(), + classDescriptor.thisAsReceiverParameter, + extensionReceiverParameter) + + localClassContext.capturedValueToField[capturedValue.descriptor] = IrFieldImpl( + localClassContext.declaration.startOffset, localClassContext.declaration.endOffset, + DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE, + fieldDescriptor + ) + } + } + + private fun MutableMap.putAbsentOrSame(key: K, value: V) { + val current = this.getOrPut(key, { value }) + + if (current != value) { + error("$current != $value") + } + } + + private fun suggestNameForCapturedValue(valueDescriptor: ValueDescriptor): Name = + if (valueDescriptor.name.isSpecial) { + val oldNameStr = valueDescriptor.name.asString() + oldNameStr.substring(1, oldNameStr.length - 1).synthesizedName + } else + valueDescriptor.name + + private fun createUnsubstitutedCapturedValueParameter( + newParameterOwner: CallableMemberDescriptor, + valueDescriptor: ValueDescriptor, + index: Int + ): ValueParameterDescriptor = + ValueParameterDescriptorImpl( + newParameterOwner, null, index, + valueDescriptor.annotations, + suggestNameForCapturedValue(valueDescriptor), + valueDescriptor.type, + false, false, false, null, valueDescriptor.source + ) + + private fun createUnsubstitutedParameter( + newParameterOwner: CallableMemberDescriptor, + valueParameterDescriptor: ValueParameterDescriptor, + newIndex: Int + ): ValueParameterDescriptor = + valueParameterDescriptor.copy(newParameterOwner, valueParameterDescriptor.name, newIndex) + + + private fun collectClosures() { + val annotator = ClosureAnnotator(memberDeclaration) + localFunctions.forEach { descriptor, context -> + context.closure = annotator.getFunctionClosure(descriptor) + } + + localClasses.forEach { descriptor, context -> + context.closure = annotator.getClassClosure(descriptor) + } + } + + private fun collectLocalDeclarations() { + memberDeclaration.acceptChildrenVoid(object : IrElementVisitorVoid { + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + private fun DeclarationDescriptor.declaredInFunction() = when (this.containingDeclaration) { + is CallableDescriptor -> true + is ClassDescriptor -> false + is PackageFragmentDescriptor -> false + else -> TODO(this.toString() + "\n" + this.containingDeclaration.toString()) + } + + override fun visitFunction(declaration: IrFunction) { + declaration.acceptChildrenVoid(this) + + val descriptor = declaration.descriptor + + if (descriptor.declaredInFunction()) { + val localFunctionContext = LocalFunctionContext(declaration) + + localFunctions[descriptor] = localFunctionContext + + if (descriptor.name.isSpecial) { + localFunctionContext.index = lambdasCount++ + } + } + + } + + override fun visitConstructor(declaration: IrConstructor) { + declaration.acceptChildrenVoid(this) + + val descriptor = declaration.descriptor + assert(!descriptor.declaredInFunction()) + + if (descriptor.constructedClass.isInner) return + + localClassConstructors[descriptor] = LocalClassConstructorContext(declaration) + } + + override fun visitClass(declaration: IrClass) { + declaration.acceptChildrenVoid(this) + + val descriptor = declaration.descriptor + + if (descriptor.isInner) return + + // Local nested classes can only be inner. + assert(descriptor.declaredInFunction()) + + val localClassContext = LocalClassContext(declaration) + localClasses[descriptor] = localClassContext + } + }) + } + } + +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt new file mode 100644 index 00000000000..de849c39836 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -0,0 +1,239 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.atMostOne +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.IrTypeOperator +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy +import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.Printer + +class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns) + +class DeclarationIrBuilder( + backendContext: BackendContext, + symbol: IrSymbol, + startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET +) : IrBuilderWithScope( + IrLoweringContext(backendContext), + Scope(symbol), + startOffset, + endOffset +) + +fun BackendContext.createIrBuilder(symbol: IrSymbol, + startOffset: Int = UNDEFINED_OFFSET, + endOffset: Int = UNDEFINED_OFFSET) = + DeclarationIrBuilder(this, symbol, startOffset, endOffset) + + +fun T.at(element: IrElement) = this.at(element.startOffset, element.endOffset) + +/** + * Builds [IrBlock] to be used instead of given expression. + */ +inline fun IrGeneratorWithScope.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null, + resultType: KotlinType? = expression.type, + body: IrBlockBuilder.() -> Unit) = + this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body) + +inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) = + this.irBlockBody(irElement.startOffset, irElement.endOffset, body) + +fun IrBuilderWithScope.irIfThen(condition: IrExpression, thenPart: IrExpression) = + IrIfThenElseImpl(startOffset, endOffset, context.builtIns.unitType, condition, thenPart, null) + +fun IrBuilderWithScope.irNot(arg: IrExpression) = + primitiveOp1(startOffset, endOffset, context.irBuiltIns.booleanNotSymbol, IrStatementOrigin.EXCL, arg) + +fun IrBuilderWithScope.irThrow(arg: IrExpression) = + IrThrowImpl(startOffset, endOffset, context.builtIns.nothingType, arg) + +fun IrBuilderWithScope.irCatch(catchParameter: IrVariable) = + IrCatchImpl( + startOffset, endOffset, + catchParameter + ) + +fun IrBuilderWithScope.irCast(arg: IrExpression, type: KotlinType, typeOperand: KotlinType) = + IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.CAST, typeOperand, arg) + +fun IrBuilderWithScope.irImplicitCoercionToUnit(arg: IrExpression) = + IrTypeOperatorCallImpl(startOffset, endOffset, context.builtIns.unitType, + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, context.builtIns.unitType, arg) + +fun IrBuilderWithScope.irGetField(receiver: IrExpression, symbol: IrFieldSymbol) = + IrGetFieldImpl(startOffset, endOffset, symbol, receiver) + +fun IrBuilderWithScope.irSetField(receiver: IrExpression, symbol: IrFieldSymbol, value: IrExpression) = + IrSetFieldImpl(startOffset, endOffset, symbol, receiver, value) + +open class IrBuildingTransformer(private val context: BackendContext) : IrElementTransformerVoid() { + private var currentBuilder: IrBuilderWithScope? = null + + protected val builder: IrBuilderWithScope + get() = currentBuilder!! + + private inline fun withBuilder(symbol: IrSymbol, block: () -> T): T { + val oldBuilder = currentBuilder + currentBuilder = context.createIrBuilder(symbol) + return try { + block() + } finally { + currentBuilder = oldBuilder + } + } + + override fun visitFunction(declaration: IrFunction): IrStatement { + withBuilder(declaration.symbol) { + return super.visitFunction(declaration) + } + } + + override fun visitField(declaration: IrField): IrStatement { + withBuilder(declaration.symbol) { + // Transforms initializer: + return super.visitField(declaration) + } + } + + override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer): IrStatement { + withBuilder(declaration.symbol) { + return super.visitAnonymousInitializer(declaration) + } + } +} + +fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List): List { + + val result = mutableListOf() + + val allSuperDescriptors = current.typeConstructor.supertypes + .flatMap { it.memberScope.getContributedDescriptors() } + .filterIsInstance() + + for ((name, group) in allSuperDescriptors.groupBy { it.name }) { + OverridingUtil.generateOverridesInFunctionGroup( + name, + /* membersFromSupertypes = */ group, + /* membersFromCurrent = */ functionsFromCurrent.filter { it.name == name }, + current, + object : NonReportingOverrideStrategy() { + override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) { + result.add(fakeOverride) + } + + override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) { + error("Conflict in scope of $current: $fromSuper vs $fromCurrent") + } + } + ) + } + + return result +} + +class SimpleMemberScope(val members: List) : MemberScopeImpl() { + + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = + members.filterIsInstance() + .atMostOne { it.name == name } + + override fun getContributedVariables(name: Name, location: LookupLocation): Collection = + members.filterIsInstance() + .filter { it.name == name } + + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = + members.filterIsInstance() + .filter { it.name == name } + + override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean): Collection = + members.filter { kindFilter.accepts(it) && nameFilter(it.name) } + + override fun printScopeStructure(p: Printer) = TODO("not implemented") + +} + +fun IrConstructor.callsSuper(): Boolean { + val constructedClass = descriptor.constructedClass + val superClass = constructedClass.getSuperClassOrAny() + var callsSuper = false + var numberOfCalls = 0 + acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + // Skip nested + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) { + assert(++numberOfCalls == 1, { "More than one delegating constructor call: $descriptor" }) + if (expression.descriptor.constructedClass == superClass) + callsSuper = true + else if (expression.descriptor.constructedClass != constructedClass) + throw AssertionError("Expected either call to another constructor of the class being constructed or" + + " call to super class constructor. But was: ${expression.descriptor.constructedClass}") + } + }) + assert(numberOfCalls == 1, { "Expected exactly one delegating constructor call but none encountered: $descriptor" }) + return callsSuper +} + +fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index: Int) + = when (this) { + is ValueParameterDescriptor -> this.copy(newOwner, name, index) + is ReceiverParameterDescriptor -> ValueParameterDescriptorImpl( + containingDeclaration = newOwner, + original = null, + index = index, + annotations = annotations, + name = name, + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = source + ) + else -> throw Error("Unexpected parameter descriptor: $this") +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt new file mode 100644 index 00000000000..2f0e3fa6702 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/TailrecLowering.kt @@ -0,0 +1,163 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol +import org.jetbrains.kotlin.ir.util.explicitParameters +import org.jetbrains.kotlin.ir.util.getArgumentsWithSymbols +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +/** + * This pass lowers tail recursion calls in `tailrec` functions. + * + * Note: it currently can't handle local functions and classes declared in default arguments. + * See [deepCopyWithVariables]. + */ +class TailrecLowering(val context: BackendContext) : FunctionLoweringPass { + override fun lower(irFunction: IrFunction) { + lowerTailRecursionCalls(context, irFunction) + } +} + +private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunction) { + val tailRecursionCalls = collectTailRecursionCalls(irFunction) + if (tailRecursionCalls.isEmpty()) { + return + } + + val oldBody = irFunction.body as IrBlockBody + val builder = context.createIrBuilder(irFunction.symbol).at(oldBody) + + val parameters = irFunction.explicitParameters + + irFunction.body = builder.irBlockBody { + // Define variables containing current values of parameters: + val parameterToVariable = parameters.associate { + it to irTemporaryVar(irGet(it), nameHint = it.suggestVariableName()).symbol + } + // (these variables are to be updated on any tail call). + + +irWhile().apply { + val loop = this + condition = irTrue() + + body = irBlock(startOffset, endOffset, resultType = context.builtIns.unitType) { + // Read variables containing current values of parameters: + val parameterToNew = parameters.associate { + val variable = parameterToVariable[it]!! + it to irTemporary(irGet(variable), nameHint = it.suggestVariableName()).symbol + } + + val transformer = BodyTransformer(builder, irFunction, loop, + parameterToNew, parameterToVariable, tailRecursionCalls) + + oldBody.statements.forEach { + +it.transform(transformer, null) + } + + +irBreak(loop) + } + } + } +} + +private class BodyTransformer( + val builder: IrBuilderWithScope, + val irFunction: IrFunction, + val loop: IrLoop, + val parameterToNew: Map, + val parameterToVariable: Map, + val tailRecursionCalls: Set +) : IrElementTransformerVoid() { + + val parameters = irFunction.explicitParameters + + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid(this) + val value = parameterToNew[expression.symbol] ?: return expression + return builder.at(expression).irGet(value) + } + + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + if (expression !in tailRecursionCalls) { + return expression + } + + return builder.at(expression).genTailCall(expression) + } + + private fun IrBuilderWithScope.genTailCall(expression: IrCall) = this.irBlock(expression) { + // Get all specified arguments: + val parameterToArgument = expression.getArgumentsWithSymbols().map { (parameter, argument) -> + parameter to argument + } + + // For each specified argument set the corresponding variable to it in the correct order: + parameterToArgument.forEach { (parameter, argument) -> + at(argument) + // Note that argument can use values of parameters, so it is important that + // references to parameters are mapped using `parameterToNew`, not `parameterToVariable`. + +irSetVar(parameterToVariable[parameter]!!, argument) + } + + val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet() + + // For each unspecified argument set the corresponding variable to default: + parameters.filter { it !in specifiedParameters }.forEach { parameter -> + + val originalDefaultValue = parameter.owner.defaultValue?.expression ?: + throw Error("no argument specified for $parameter") + + // Copy default value, mapping parameters to variables containing freshly computed arguments: + val defaultValue = originalDefaultValue + .deepCopyWithVariables() + .transform(object : IrElementTransformerVoid() { + + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid(this) + + val variableSymbol = parameterToVariable[expression.symbol] ?: return expression + return IrGetValueImpl( + expression.startOffset, expression.endOffset, + variableSymbol, expression.origin + ) + } + }, data = null) + + +irSetVar(parameterToVariable[parameter]!!, defaultValue) + } + + // Jump to the entry: + +irContinue(loop) + } +} + +private fun IrValueParameterSymbol.suggestVariableName(): String = if (descriptor.name.isSpecial) { + val oldNameStr = descriptor.name.asString() + "$" + oldNameStr.substring(1, oldNameStr.length - 1) +} else { + descriptor.name.identifier +} \ No newline at end of file