From 594a553a04bc8a78f45c631e21c9c2996019a345 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 22 Aug 2016 18:13:21 +0300 Subject: [PATCH] Safe calls. --- .../org/jetbrains/kotlin/psi2ir/IrUtils.kt | 6 +- .../jetbrains/kotlin/psi2ir/KotlinUtils.kt | 58 +++++ .../kotlin/psi2ir/Psi2IrTranslator.kt | 9 +- .../psi2ir/generators/CallGeneration.kt | 112 +++++++++ .../kotlin/psi2ir/generators/CallGenerator.kt | 203 ++++++---------- .../psi2ir/generators/DeclarationGenerator.kt | 2 +- .../generators/ExpressionBodyGenerator.kt | 8 +- .../{IrGenerator.kt => Generator.kt} | 45 ++-- .../psi2ir/generators/IrBlockBuilder.kt | 97 -------- .../psi2ir/generators/IrBodyGenerator.kt | 68 ------ .../psi2ir/generators/ModuleGenerator.kt | 2 +- .../{operators => }/OperatorConventions.kt | 2 +- .../generators/OperatorExpressionGenerator.kt | 218 ++++++++++-------- .../kotlin/psi2ir/generators/Primitives.kt | 56 +++++ .../kotlin/psi2ir/generators/Scope.kt | 87 +------ .../psi2ir/generators/StatementGenerator.kt | 72 +++--- .../generators/WhenExpressionGenerator.kt | 39 ++-- .../psi2ir/generators/values/IndexedLValue.kt | 168 -------------- .../generators/values/PropertyLValue.kt | 57 ----- .../values/RematerializableValue.kt | 57 ----- .../kotlin/psi2ir/generators/values/Value.kt | 57 ----- .../ArrayAccessAssignmentReceiver.kt | 63 +++++ .../LValueWithGetterAndSetterCalls.kt | 59 +++++ .../psi2ir/intermediate/OnceCallValue.kt | 42 ++++ .../intermediate/OnceExpressionValue.kt | 39 ++++ .../psi2ir/intermediate/PregeneratedCall.kt | 79 +++++++ .../intermediate/RematerializableValue.kt | 53 +++++ .../psi2ir/intermediate/SafeCallReceiver.kt | 67 ++++++ .../SimpleCallReceiver.kt} | 17 +- .../intermediate/SimplePropertyLValue.kt | 90 ++++++++ .../kotlin/psi2ir/intermediate/Values.kt | 37 +++ .../values => intermediate}/VariableLValue.kt | 20 +- .../transformations/InsertImplicitCasts.kt | 118 ++++++++++ .../src/org/jetbrains/kotlin/ir/IrElement.kt | 9 + .../kotlin/ir/declarations/IrVariable.kt | 2 +- .../kotlin/ir/expressions/IrBlock.kt | 19 ++ .../jetbrains/kotlin/ir/expressions/IrCall.kt | 23 +- .../kotlin/ir/expressions/IrConst.kt | 7 +- .../ir/expressions/IrDummyExpression.kt | 5 +- .../kotlin/ir/expressions/IrExpression.kt | 4 + .../ir/expressions/IrGetExtensionReceiver.kt | 7 +- .../jetbrains/kotlin/ir/expressions/IrLoop.kt | 11 +- .../expressions/IrMemberAccessExpression.kt | 4 +- .../kotlin/ir/expressions/IrOperator.kt | 11 + .../ir/expressions/IrOperatorExpression.kt | 2 - .../kotlin/ir/expressions/IrThisReference.kt | 7 +- .../ir/expressions/IrTypeOperatorCall.kt | 1 + .../expressions/IrVariableAccessExpression.kt | 10 +- .../jetbrains/kotlin/ir/expressions/IrWhen.kt | 26 +-- .../kotlin/ir/util/RenderIrElement.kt | 4 +- .../kotlin/ir/visitors/IrElementVisitor.kt | 2 +- .../testData/ir/irText/arrayAssignment.txt | 28 ++- .../ir/irText/arrayAugmentedAssignment1.kt | 12 +- .../ir/irText/arrayAugmentedAssignment1.txt | 43 ++-- .../ir/irText/augmentedAssignment1.txt | 10 +- .../ir/irText/augmentedAssignment2.txt | 10 +- .../ir/irText/callWithReorderedArguments.txt | 4 +- .../testData/ir/irText/destructuring1.txt | 6 +- compiler/testData/ir/irText/dotQualified.txt | 11 +- compiler/testData/ir/irText/elvis.txt | 58 ++--- .../ir/irText/implicitCastOnPlatformType.txt | 13 +- .../testData/ir/irText/incrementDecrement.txt | 17 +- .../irText/safeCallWithIncrementDecrement.kt | 20 ++ .../irText/safeCallWithIncrementDecrement.txt | 53 +++++ compiler/testData/ir/irText/safeCalls.kt | 15 ++ compiler/testData/ir/irText/safeCalls.txt | 75 ++++++ .../ir/irText/smartCastsWithDestructuring.txt | 6 +- compiler/testData/ir/irText/when.txt | 6 +- .../kotlin/ir/IrTextTestCaseGenerated.java | 12 + 69 files changed, 1567 insertions(+), 1093 deletions(-) create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrGenerator.kt => Generator.kt} (66%) delete mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBlockBuilder.kt delete mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBodyGenerator.kt rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{operators => }/OperatorConventions.kt (98%) create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Primitives.kt delete mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt delete mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt delete mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/RematerializableValue.kt delete mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/Value.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/ArrayAccessAssignmentReceiver.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/LValueWithGetterAndSetterCalls.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceCallValue.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceExpressionValue.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PregeneratedCall.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/RematerializableValue.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/{KtElementUtils.kt => intermediate/SimpleCallReceiver.kt} (54%) create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/{generators/values => intermediate}/VariableLValue.kt (71%) create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt create mode 100644 compiler/testData/ir/irText/safeCallWithIncrementDecrement.kt create mode 100644 compiler/testData/ir/irText/safeCallWithIncrementDecrement.txt create mode 100644 compiler/testData/ir/irText/safeCalls.kt create mode 100644 compiler/testData/ir/irText/safeCalls.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt index bce1966c8fb..4a705e171b5 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt @@ -19,9 +19,13 @@ package org.jetbrains.kotlin.psi2ir import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi2ir.containsNull import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.isNullabilityFlexible +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable +import org.jetbrains.kotlin.types.upperIfFlexible fun IrVariable.defaultLoad(): IrExpression = - IrGetVariableImpl(startOffset, endOffset, descriptor) \ No newline at end of file + IrGetVariableImpl(startOffset, endOffset, descriptor) + diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt new file mode 100644 index 00000000000..0e6ba89d8a5 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2016 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.psi2ir + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.upperIfFlexible + +fun KotlinType.containsNull() = + KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.nullableNothingType, this.upperIfFlexible()) + +fun KtElement.deparenthesize(): KtElement = + if (this is KtExpression) KtPsiUtil.safeDeparenthesize(this) else this + +val CallableDescriptor.explicitReceiverType: KotlinType? + get() { + extensionReceiverParameter?.let { return it.type } + dispatchReceiverParameter?.let { return it.type } + return null + } + +fun ResolvedCall<*>.isValueArgumentReorderingRequired(): Boolean { + var lastValueParameterIndex = -1 + for (valueArgument in call.valueArguments) { + val argumentMapping = getArgumentMapping(valueArgument) + if (argumentMapping !is ArgumentMatch || argumentMapping.isError()) { + throw AssertionError("Value argument in function call is mapped with error") + } + val argumentIndex = argumentMapping.valueParameter.index + if (argumentIndex < lastValueParameterIndex) { + return true + } + lastValueParameterIndex = argumentIndex + } + return false +} + diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt index 06e311ba0e0..59936c48830 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt @@ -17,13 +17,13 @@ package org.jetbrains.kotlin.psi2ir import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrModule import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks +import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts import org.jetbrains.kotlin.resolve.BindingContext class Psi2IrTranslator(val configuration: Configuration = Configuration()) { @@ -39,8 +39,9 @@ class Psi2IrTranslator(val configuration: Configuration = Configuration()) { return irModule } - private fun postprocess(irElement: IrElement) { - if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irElement) - if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irElement) + private fun postprocess(irModule: IrModule) { + insertImplicitCasts(irModule.irBuiltins.builtIns, irModule) + if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irModule) + if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irModule) } } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt new file mode 100644 index 00000000000..5ce30c997f3 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2010-2016 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.psi2ir.generators + +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.psi2ir.intermediate.* +import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.scopes.receivers.* + +fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? = + receiver?.let { generateReceiver(ktDefaultElement, receiver) } + +fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): IntermediateValue { + val receiverExpression = when (receiver) { + is ImplicitClassReceiver -> + IrThisReferenceImpl(ktDefaultElement.startOffset, ktDefaultElement.startOffset, receiver.type, receiver.classDescriptor) + is ThisClassReceiver -> + (receiver as? ExpressionReceiver)?.expression?.let { receiverExpression -> + IrThisReferenceImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, + receiver.classDescriptor) + } ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver") + is ExpressionReceiver -> + generateExpression(receiver.expression) + is ClassValueReceiver -> + IrGetObjectValueImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type, + receiver.classQualifier.descriptor) + is ExtensionReceiver -> + IrGetExtensionReceiverImpl(ktDefaultElement.startOffset, ktDefaultElement.startOffset, receiver.type, + receiver.declarationDescriptor.extensionReceiverParameter!!) + else -> + TODO("Receiver: ${receiver.javaClass.simpleName}") + } + + return if (receiverExpression is IrExpressionWithCopy) + RematerializableValue(receiverExpression) + else + OnceExpressionValue(receiverExpression) +} + +fun StatementGenerator.generateCallReceiver( + ktDefaultElement: KtElement, + dispatchReceiver: ReceiverValue?, + extensionReceiver: ReceiverValue?, + isSafe: Boolean +) : CallReceiver { + val dispatchReceiverValue = generateReceiverOrNull(ktDefaultElement, dispatchReceiver) + val extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver) + + if (!isSafe) { + return SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue) + } + else if (extensionReceiverValue != null) { + return SafeCallReceiver(this, ktDefaultElement.startOffset, ktDefaultElement.endOffset, + extensionReceiverValue.load(), dispatchReceiverValue) + } + else if (dispatchReceiverValue != null) { + return SafeCallReceiver(this, ktDefaultElement.startOffset, ktDefaultElement.endOffset, + dispatchReceiverValue.load(), null) + } + else { + return throw AssertionError("Safe call should have an explicit receiver: ${ktDefaultElement.text}") + } +} + +fun StatementGenerator.generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = + when (valueArgument) { + is DefaultValueArgument -> + null + is ExpressionValueArgument -> + generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!) + is VarargValueArgument -> + createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg") + else -> + TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") + } + +fun StatementGenerator.pregenerateCall(resolvedCall: ResolvedCall<*>): PregeneratedCall { + val call = pregenerateCallReceivers(resolvedCall) + + resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument -> + call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument) + } + + return call +} + +fun StatementGenerator.pregenerateCallReceivers(resolvedCall: ResolvedCall<*>): PregeneratedCall { + val call = PregeneratedCall(resolvedCall) + + val ktDefaultCallElement = resolvedCall.call.callElement + call.callReceiver = generateCallReceiver(ktDefaultCallElement, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall()) + + return call +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 401a45fdce9..49d0139b448 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -16,109 +16,104 @@ package org.jetbrains.kotlin.psi2ir.generators -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall -import org.jetbrains.kotlin.resolve.calls.model.* -import org.jetbrains.kotlin.resolve.scopes.receivers.* +import org.jetbrains.kotlin.psi2ir.intermediate.PregeneratedCall +import org.jetbrains.kotlin.psi2ir.intermediate.getValueArgumentsInParameterOrder +import org.jetbrains.kotlin.psi2ir.intermediate.isValueArgumentReorderingRequired +import org.jetbrains.kotlin.psi2ir.intermediate.IntermediateValue +import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary +import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument import org.jetbrains.kotlin.types.KotlinType import java.util.* -class CallGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase(parentGenerator) { +class CallGenerator( + override val context: GeneratorContext, + override val scope: Scope +) : BodyGenerator { + + constructor(parent: BodyGenerator) : this(parent.context, parent.scope) + fun generateCall( - ktElement: KtElement, - resolvedCall: ResolvedCall, - operator: IrOperator? = null, - superQualifier: ClassDescriptor? = null + startOffset: Int, + endOffset: Int, + call: PregeneratedCall, + operator: IrOperator? = null ): IrExpression { - val descriptor = resolvedCall.resultingDescriptor + val descriptor = call.descriptor return when (descriptor) { is PropertyDescriptor -> - generatePropertyGetterCall(descriptor, ktElement, resolvedCall) + generatePropertyGetterCall(descriptor, startOffset, endOffset, call) is FunctionDescriptor -> - generateFunctionCall(descriptor, ktElement, operator, resolvedCall, superQualifier) + generateFunctionCall(descriptor, startOffset, endOffset, operator, call) else -> TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}") } } - private fun CallGenerator.generatePropertyGetterCall( + private fun generatePropertyGetterCall( descriptor: PropertyDescriptor, - ktElement: KtElement, - resolvedCall: ResolvedCall<*> - ): IrGetterCallImpl { - val returnType = getReturnType(resolvedCall) - val dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter) - val extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter) - return IrGetterCallImpl(ktElement.startOffset, ktElement.endOffset, - returnType, descriptor.getter!!, resolvedCall.call.isSafeCall(), - dispatchReceiver, extensionReceiver, IrOperator.GET_PROPERTY) - } - - private fun ResolvedCall<*>.requiresArgumentReordering(): Boolean { - var lastValueParameterIndex = -1 - for (valueArgument in call.valueArguments) { - val argumentMapping = getArgumentMapping(valueArgument) - if (argumentMapping !is ArgumentMatch || argumentMapping.isError()) { - error("Value argument in function call is mapped with error") - } - val argumentIndex = argumentMapping.valueParameter.index - if (argumentIndex < lastValueParameterIndex) return true - lastValueParameterIndex = argumentIndex + startOffset: Int, + endOffset: Int, + call: PregeneratedCall + ): IrExpression { + return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + IrGetterCallImpl(startOffset, endOffset, descriptor.getter!!, + dispatchReceiverValue?.load(), + extensionReceiverValue?.load(), + IrOperator.GET_PROPERTY) } - return false } private fun generateFunctionCall( descriptor: FunctionDescriptor, - ktElement: KtElement, + startOffset: Int, + endOffset: Int, operator: IrOperator?, - resolvedCall: ResolvedCall, - superQualifier: ClassDescriptor? + call: PregeneratedCall ): IrExpression { val returnType = descriptor.returnType - val irCall = IrCallImpl( - ktElement.startOffset, ktElement.endOffset, returnType, - descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier - ) - irCall.dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter) - irCall.extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter) + return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + val irCall = IrCallImpl(startOffset, endOffset, returnType, descriptor, operator, call.superQualifier) + irCall.dispatchReceiver = dispatchReceiverValue?.load() + irCall.extensionReceiver = extensionReceiverValue?.load() - return if (resolvedCall.requiresArgumentReordering()) { - generateCallWithArgumentReordering(irCall, ktElement, resolvedCall, returnType) - } - else { - val valueArguments = resolvedCall.valueArgumentsByIndex - for (index in valueArguments!!.indices) { - val valueArgument = valueArguments[index] - val valueParameter = descriptor.valueParameters[index] - val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue - irCall.putArgument(index, irArgument) - } - irCall + val irCallWithReordering = + if (call.isValueArgumentReorderingRequired()) { + generateCallWithArgumentReordering(irCall, startOffset, endOffset, call, returnType) + } + else { + val valueArguments = call.getValueArgumentsInParameterOrder() + for ((index, valueArgument) in valueArguments.withIndex()) { + irCall.putArgument(index, valueArgument) + } + irCall + } + + irCallWithReordering } } private fun generateCallWithArgumentReordering( irCall: IrCall, - ktElement: KtElement, - resolvedCall: ResolvedCall, + startOffset: Int, + endOffset: Int, + call: PregeneratedCall, resultType: KotlinType? ): IrExpression { - // TODO use IrLetExpression? + val resolvedCall = call.original val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values val valueParameters = resolvedCall.resultingDescriptor.valueParameters - val hasResult = isUsedAsExpression(ktElement) - val irBlock = IrBlockImpl(ktElement.startOffset, ktElement.endOffset, resultType, hasResult, - IrOperator.SYNTHETIC_BLOCK) + val irBlock = IrBlockImpl(startOffset, endOffset, resultType, true, IrOperator.SYNTHETIC_BLOCK) val valueArgumentsToValueParameters = HashMap() for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { @@ -126,86 +121,28 @@ class CallGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorB valueArgumentsToValueParameters[valueArgument] = valueParameter } - val reorderingScope = Scope(this.scope) + val irArgumentValues = HashMap() for (valueArgument in valueArgumentsInEvaluationOrder) { val valueParameter = valueArgumentsToValueParameters[valueArgument]!! - val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue - val irTmpArg = reorderingScope.introduceTemporary(valueParameter, irArgument) - irBlock.addIfNotNull(irTmpArg) + val irArgument = call.getValueArgument(valueParameter) ?: continue + val irArgumentValue = createRematerializableOrTemporary(scope, irArgument, irBlock, valueParameter.name.asString()) + irArgumentValues[valueParameter] = irArgumentValue } - for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { + resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument -> val valueParameter = valueParameters[index] - val irGetTemporary = reorderingScope.valueOf(valueParameter)!! - irCall.putArgument(index, toExpectedType(irGetTemporary, valueParameter.type)) + irCall.putArgument(index, irArgumentValues[valueParameter]?.load()) } irBlock.addStatement(irCall) return irBlock } - - fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?, receiverParameterDescriptor: ReceiverParameterDescriptor?) = - generateReceiver(ktElement, receiver, receiverParameterDescriptor?.type) - - fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?, expectedType: KotlinType?) = - toExpectedTypeOrNull(generateReceiver(ktElement, receiver), expectedType) - - fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?): IrExpression? = - if (receiver == null) - null - else - scope.valueOf(receiver) ?: doGenerateReceiver(ktElement, receiver) - - fun doGenerateReceiver(ktElement: KtElement, receiver: ReceiverValue?): IrExpression? = - when (receiver) { - is ImplicitClassReceiver -> - IrThisReferenceImpl(ktElement.startOffset, ktElement.startOffset, receiver.type, receiver.classDescriptor) - is ThisClassReceiver -> - (receiver as? ExpressionReceiver)?.expression?.let { receiverExpression -> - IrThisReferenceImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor) - } ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver") - is ExpressionReceiver -> - generateExpression(receiver.expression) - is ClassValueReceiver -> - IrGetObjectValueImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type, - receiver.classQualifier.descriptor) - is ExtensionReceiver -> - IrGetExtensionReceiverImpl(ktElement.startOffset, ktElement.startOffset, receiver.type, - receiver.declarationDescriptor.extensionReceiverParameter!!) - null -> - null - else -> - TODO("Receiver: ${receiver.javaClass.simpleName}") - } - - fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? = - generateValueArgument(valueArgument, valueParameterDescriptor, valueParameterDescriptor.type) - - fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor, expectedType: KotlinType): IrExpression? = - if (valueParameterDescriptor.varargElementType != null) - doGenerateValueArgument(valueArgument, valueParameterDescriptor) - else - toExpectedTypeOrNull(doGenerateValueArgument(valueArgument, valueParameterDescriptor), expectedType) - - private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? = - if (valueArgument is DefaultValueArgument) - null - else - scope.valueOf(valueParameterDescriptor) ?: doGenerateValueArgument(valueArgument) - - private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = - when (valueArgument) { - is ExpressionValueArgument -> - generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!) - is VarargValueArgument -> - createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg") - else -> - TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") - } - - private fun generateExpression(ktExpression: KtExpression): IrExpression = - scope.valueOf(ktExpression) ?: parentGenerator.generateExpression(ktExpression) - } + +fun CallGenerator.generateCall(ktElement: KtElement, call: PregeneratedCall, operator: IrOperator? = null) = + generateCall(ktElement.startOffset, ktElement.endOffset, call, operator) + +fun CallGenerator.generateCall(irExpression: IrExpression, call: PregeneratedCall, operator: IrOperator? = null) = + generateCall(irExpression.startOffset, irExpression.endOffset, call, operator) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt index becfb115d50..e1a4e0404ab 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext -class DeclarationGenerator(override val context: GeneratorContext) : IrGenerator { +class DeclarationGenerator(override val context: GeneratorContext) : Generator { fun generateAnnotationEntries(annotationEntries: List) { // TODO create IrAnnotation's for each KtAnnotationEntry } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ExpressionBodyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ExpressionBodyGenerator.kt index 87d4e4ece78..616d7253ff3 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ExpressionBodyGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ExpressionBodyGenerator.kt @@ -25,13 +25,13 @@ import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset -class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val context: GeneratorContext): IrBodyGenerator { - override val scope = Scope.rootScope(scopeOwner, this) +class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val context: GeneratorContext): BodyGenerator { + override val scope = Scope(scopeOwner) fun generateFunctionBody(ktBody: KtExpression): IrExpression { resetInternalContext() - val irBodyExpression = createStatementGenerator().generateExpressionWithExpectedType(ktBody, scopeOwner.returnType) + val irBodyExpression = createStatementGenerator().generateExpression(ktBody) val irBodyExpressionAsBlock = if (ktBody is KtBlockExpression) @@ -52,7 +52,7 @@ class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val c } fun generatePropertyInitializerBody(ktInitializer: KtExpression): IrExpression = - createStatementGenerator().generateExpressionWithExpectedType(ktInitializer, scopeOwner.returnType!!) + createStatementGenerator().generateExpression(ktInitializer) private fun createStatementGenerator() = StatementGenerator(context, scopeOwner, this, scope) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt similarity index 66% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt index 88775fcbec9..4a07e4bf39e 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt @@ -35,35 +35,38 @@ import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice import java.lang.AssertionError import java.lang.RuntimeException -interface IrGenerator { + +interface Generator { val context: GeneratorContext } -fun IrGenerator.get(slice: ReadOnlySlice, key: K): V? = +interface BodyGenerator : Generator { + val scope: Scope +} + + +fun Generator.get(slice: ReadOnlySlice, key: K): V? = context.bindingContext[slice, key] -fun IrGenerator.getOrFail(slice: ReadOnlySlice, key: K): V = +fun Generator.getOrFail(slice: ReadOnlySlice, key: K): V = context.bindingContext[slice, key] ?: throw RuntimeException("No $slice for $key") -inline fun IrGenerator.getOrFail(slice: ReadOnlySlice, key: K, message: (K) -> String): V = +inline fun Generator.getOrFail(slice: ReadOnlySlice, key: K, message: (K) -> String): V = context.bindingContext[slice, key] ?: throw RuntimeException(message(key)) -fun IrGenerator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? = +fun Generator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? = context.bindingContext.getType(key) -fun IrGenerator.getExpectedTypeForLastInferredCall(key: KtExpression): KotlinType? = - get(BindingContext.EXPECTED_EXPRESSION_TYPE, key) +fun Generator.getInferredTypeWithSmartcastsOrFail(key: KtExpression): KotlinType = + getInferredTypeWithSmartcasts(key) ?: throw AssertionError("No type for expression: ${key.text}") -fun IrGenerator.getInferredTypeWithSmarcastsOrFail(key: KtExpression): KotlinType = - getInferredTypeWithSmartcasts(key) ?: TODO("No type for expression: ${key.text}") - -fun IrGenerator.isUsedAsExpression(ktElement: KtElement) = +fun Generator.isUsedAsExpression(ktElement: KtElement) = get(BindingContext.USED_AS_EXPRESSION, ktElement) ?: false -fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall? = +fun Generator.getResolvedCall(key: KtExpression): ResolvedCall? = key.getResolvedCall(context.bindingContext) -fun IrGenerator.getReturnType(key: KtExpression): KotlinType? { +fun Generator.getReturnType(key: KtExpression): KotlinType? { val resolvedCall = getResolvedCall(key) if (resolvedCall != null) { return getReturnType(resolvedCall) @@ -78,21 +81,21 @@ fun IrGenerator.getReturnType(key: KtExpression): KotlinType? { } fun getReturnType(resolvedCall: ResolvedCall<*>): KotlinType { - val descriptor = resolvedCall.resultingDescriptor + val returnType = getReturnType(resolvedCall.resultingDescriptor) + return if (resolvedCall.call.isSafeCall()) returnType.makeNullable() else returnType +} + +fun getReturnType(descriptor: CallableDescriptor): KotlinType { return when (descriptor) { is ClassDescriptor -> descriptor.classValueType ?: throw AssertionError("Class descriptor without companion object: $descriptor") is CallableDescriptor -> { - val returnType = descriptor.returnType ?: throw AssertionError("Callable descriptor without return type: $descriptor") - if (resolvedCall.call.isSafeCall()) - returnType.makeNullable() - else - returnType + descriptor.returnType ?: throw AssertionError("Callable descriptor without return type: $descriptor") } else -> - throw AssertionError("Unexpected desciptor in resolved call: $descriptor") + throw AssertionError("Unexpected descriptor in resolved call: $descriptor") } } -fun IrGenerator.createDummyExpression(ktExpression: KtExpression, description: String): IrDummyExpression = +fun Generator.createDummyExpression(ktExpression: KtExpression, description: String): IrDummyExpression = IrDummyExpression(ktExpression.startOffset, ktExpression.endOffset, getInferredTypeWithSmartcasts(ktExpression), description) \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBlockBuilder.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBlockBuilder.kt deleted file mode 100644 index 243ffac9288..00000000000 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBlockBuilder.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2010-2016 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.psi2ir.generators - -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.assertCast -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.utils.SmartList - -class IrBlockBuilder(val startOffset: Int, val endOffset: Int, val irOperator: IrOperator, val generator: IrBodyGenerator) { - private val statements = SmartList() - private var resultType: KotlinType? = null - private var hasResult = false - val scope: Scope get() = generator.scope - - fun add(irStatement: T): T { - if (irStatement != null) { - statements.add(irStatement) - } - return irStatement - } - - fun result(irExpression: T): T { - resultType = irExpression.type - hasResult = true - statements.add(irExpression) - return irExpression - } - - fun build() = - if (statements.size == 1) - statements[0] - else { - val block = IrBlockImpl(startOffset, endOffset, resultType, hasResult, irOperator) - statements.forEach { block.addStatement(it) } - block - } -} - -fun IrBodyGenerator.block(ktElement: KtElement, irOperator: IrOperator, body: IrBlockBuilder.() -> Unit): IrExpression = - IrBlockBuilder(ktElement.startOffset, ktElement.endOffset, irOperator, this).apply(body).build().assertCast() - -fun IrBodyGenerator.block(irExpression: IrExpression, irOperator: IrOperator, body: IrBlockBuilder.() -> Unit): IrExpression = - IrBlockBuilder(irExpression.startOffset, irExpression.endOffset, irOperator, this).apply(body).build().assertCast() - -fun IrBlockBuilder.constType(constKind: IrConstKind<*>): KotlinType = - when (constKind) { - IrConstKind.Null -> generator.context.builtIns.nullableNothingType - IrConstKind.Boolean -> generator.context.builtIns.anyType - IrConstKind.Byte -> generator.context.builtIns.byteType - IrConstKind.Short -> generator.context.builtIns.shortType - IrConstKind.Int -> generator.context.builtIns.intType - IrConstKind.Long -> generator.context.builtIns.longType - IrConstKind.String -> generator.context.builtIns.stringType - IrConstKind.Float -> generator.context.builtIns.floatType - IrConstKind.Double -> generator.context.builtIns.doubleType - } - -fun IrBlockBuilder.const(constKind: IrConstKind, value: T) = - IrConstImpl(startOffset, endOffset, constType(constKind), constKind, value) - -fun IrBlockBuilder.constNull() = - const(IrConstKind.Null, null) - -fun IrBlockBuilder.op0(operatorDescriptor: CallableDescriptor) = - IrNullaryOperatorImpl(startOffset, endOffset, irOperator, operatorDescriptor) - -fun IrBlockBuilder.op1(operatorDescriptor: CallableDescriptor, argument: IrExpression) = - IrUnaryOperatorImpl(startOffset, endOffset, irOperator, operatorDescriptor, argument) - -fun IrBlockBuilder.op2(operatorDescriptor: CallableDescriptor, argument1: IrExpression, argument2: IrExpression) = - IrBinaryOperatorImpl(startOffset, endOffset, irOperator, operatorDescriptor, argument1, argument2) - -fun IrBlockBuilder.equalsNull(argument: IrExpression) = - op2(generator.context.irBuiltIns.eqeq, argument, constNull()) - -fun IrBlockBuilder.ifThenElse(type: KotlinType?, condition: IrExpression, thenBranch: IrExpression, elseBranch: IrExpression) = - IrIfThenElseImpl(startOffset, endOffset, type, condition, thenBranch, elseBranch, irOperator) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBodyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBodyGenerator.kt deleted file mode 100644 index db3c06fc274..00000000000 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrBodyGenerator.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010-2016 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.psi2ir.generators - -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -import org.jetbrains.kotlin.types.isNullabilityFlexible - -interface IrBodyGenerator : IrGenerator { - val scope: Scope -} - -abstract class IrChildBodyGeneratorBase( - val parentGenerator: T -) : IrBodyGenerator { - override val context: GeneratorContext get() = parentGenerator.context - override val scope: Scope = Scope(parentGenerator.scope) -} - -fun IrBodyGenerator.toExpectedType(irExpression: IrExpression, expectedType: KotlinType?): IrExpression { - if (irExpression is IrBlock && !irExpression.hasResult) return irExpression - - if (expectedType == null) return irExpression - if (KotlinBuiltIns.isUnit(expectedType)) return irExpression // TODO expose coercion to Unit in IR? - - val valueType = irExpression.type ?: throw AssertionError("expectedType != null, valueType == null: $this") - - if (valueType.isNullabilityFlexible() && !expectedType.isMarkedNullable) { - return block(irExpression, IrOperator.IMPLICIT_NOTNULL) { - add(scope.introduceTemporary(irExpression)) - result(ifThenElse(expectedType, - equalsNull(scope.valueOf(irExpression)!!), - op0(context.irBuiltIns.throwNpe), - scope.valueOf(irExpression)!!)) - } - } - - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { - return IrTypeOperatorCallImpl(irExpression.startOffset, irExpression.endOffset, expectedType, - IrTypeOperator.IMPLICIT_CAST, expectedType, irExpression) - } - - return irExpression -} - -fun StatementGenerator.generateExpressionWithExpectedType(ktExpression: KtExpression, expectedType: KotlinType?) = - toExpectedType(generateExpression(ktExpression), expectedType) - -fun IrBodyGenerator.toExpectedTypeOrNull(irExpression: IrExpression?, expectedType: KotlinType?): IrExpression? = - irExpression?.let { toExpectedType(it, expectedType) } - diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt index baa47510498..4ba36874ab4 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.BindingContext -class ModuleGenerator(override val context: GeneratorContext) : IrGenerator { +class ModuleGenerator(override val context: GeneratorContext) : Generator { fun generateModule(ktFiles: List): IrModule { val irDeclarationGenerator = DeclarationGenerator(context) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/operators/OperatorConventions.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt similarity index 98% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/operators/OperatorConventions.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt index 2eb6bff18b7..13366e979d4 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/operators/OperatorConventions.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.psi2ir.generators.operators +package org.jetbrains.kotlin.psi2ir.generators import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.ir.expressions.IrOperator diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt index 74a52cbfe2f..1b2b43de60e 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt @@ -25,15 +25,19 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.defaultLoad -import org.jetbrains.kotlin.psi2ir.generators.operators.* -import org.jetbrains.kotlin.psi2ir.generators.values.* +import org.jetbrains.kotlin.psi2ir.intermediate.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.types.typeUtil.makeNullable +import java.lang.AssertionError +class OperatorExpressionGenerator( + val statementGenerator: StatementGenerator +) : BodyGenerator { + override val scope: Scope get() = statementGenerator.scope + override val context: GeneratorContext get() = statementGenerator.context -class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase(parentGenerator) { fun generatePrefixExpression(expression: KtPrefixExpression): IrExpression { val ktOperator = expression.operationReference.getReferencedNameElementType() val irOperator = getPrefixOperator(ktOperator) @@ -72,7 +76,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild } return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, resultType, irOperator, rhsType, - parentGenerator.generateExpression(expression.left)) + statementGenerator.generateExpression(expression.left)) } fun generateInstanceOfExpression(expression: KtIsExpression): IrStatement { @@ -81,7 +85,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild val againstType = getOrFail(BindingContext.TYPE, expression.typeReference) return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, context.builtIns.booleanType, irOperator, - againstType, parentGenerator.generateExpression(expression.leftHandSide)) + againstType, statementGenerator.generateExpression(expression.leftHandSide)) } fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression { @@ -110,25 +114,28 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild private fun generateElvis(expression: KtBinaryExpression): IrExpression { val specialCallForElvis = getResolvedCall(expression)!! val returnType = specialCallForElvis.resultingDescriptor.returnType!! - val irArgument0 = parentGenerator.generateExpressionWithExpectedType(expression.left!!, returnType.makeNullable()) - val irArgument1 = parentGenerator.generateExpressionWithExpectedType(expression.right!!, returnType) - return block(expression, IrOperator.ELVIS) { - add(scope.introduceTemporary(irArgument0)) - result(ifThenElse(returnType, - equalsNull(scope.valueOf(irArgument0)!!), - irArgument1, - scope.valueOf(irArgument0)!!)) - } + val irArgument0 = statementGenerator.generateExpression(expression.left!!) + val irArgument1 = statementGenerator.generateExpression(expression.right!!) + + val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, returnType, true, IrOperator.ELVIS) + val irArgument0Value = createRematerializableOrTemporary(scope, irArgument0, irBlock, "elvis_lhs") + irBlock.addStatement(IrIfThenElseImpl( + expression.startOffset, expression.endOffset, returnType, + equalsNull(expression.startOffset, expression.endOffset, irArgument0Value.load()), + irArgument1, + irArgument0Value.load() + )) + return irBlock } private fun generateBinaryBooleanOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { - val irArgument0 = parentGenerator.generateExpressionWithExpectedType(expression.left!!, context.builtIns.booleanType) - val irArgument1 = parentGenerator.generateExpressionWithExpectedType(expression.right!!, context.builtIns.booleanType) + val irArgument0 = statementGenerator.generateExpression(expression.left!!) + val irArgument1 = statementGenerator.generateExpression(expression.right!!) return when (irOperator) { IrOperator.OROR -> - IrIfThenElseImpl.oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1) + oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1) IrOperator.ANDAND -> - IrIfThenElseImpl.andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1) + andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1) else -> throw AssertionError("Unexpected binary boolean operator $irOperator") } @@ -137,7 +144,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild private fun generateInOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { val containsCall = getResolvedCall(expression)!! - val irContainsCall = CallGenerator(parentGenerator).generateCall(expression, containsCall, irOperator) + val irContainsCall = CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(containsCall), irOperator) return when (irOperator) { IrOperator.IN -> @@ -152,8 +159,8 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild } private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { - val irArgument0 = parentGenerator.generateExpression(expression.left!!) - val irArgument1 = parentGenerator.generateExpression(expression.right!!) + val irArgument0 = statementGenerator.generateExpression(expression.left!!) + val irArgument1 = statementGenerator.generateExpression(expression.right!!) val irIdentityEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset, irOperator, context.irBuiltIns.eqeqeq, @@ -172,8 +179,8 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild } private fun generateEqualityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { - val irArgument0 = parentGenerator.generateExpression(expression.left!!) - val irArgument1 = parentGenerator.generateExpression(expression.right!!) + val irArgument0 = statementGenerator.generateExpression(expression.left!!) + val irArgument1 = statementGenerator.generateExpression(expression.right!!) val irEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset, irOperator, context.irBuiltIns.eqeq, irArgument0, irArgument1) @@ -193,8 +200,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild private fun generateComparisonOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { val compareToCall = getResolvedCall(expression)!! - val irCallGenerator = CallGenerator(parentGenerator) - val irCompareToCall = irCallGenerator.generateCall(expression, compareToCall, irOperator) + val irCompareToCall = CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(compareToCall), irOperator) val compareToZeroDescriptor = when (irOperator) { IrOperator.LT -> context.irBuiltIns.lt0 @@ -210,99 +216,102 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild private fun generateBinaryOperatorAsCall(expression: KtBinaryExpression, irOperator: IrOperator?): IrExpression { val operatorCall = getResolvedCall(expression)!! - return CallGenerator(parentGenerator).generateCall(expression, operatorCall, irOperator) + return CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(operatorCall), irOperator) } private fun generatePrefixIncrementDecrementOperator(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression { + val opResolvedCall = getResolvedCall(expression)!! val ktBaseExpression = expression.baseExpression!! - val irLValue = generateLValue(ktBaseExpression, irOperator) - val operatorCall = getResolvedCall(expression)!! + val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, irOperator) - if (irLValue is IrLValueWithAugmentedStore) { - return irLValue.prefixAugmentedStore(operatorCall, irOperator) + return irAssignmentReceiver.assign { irLValue -> + val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator) + + // VAR tmp = [lhs].inc() + val opCall = statementGenerator.pregenerateCall(opResolvedCall) + opCall.setExplicitReceiverValue(irLValue) + val irOpCall = CallGenerator(this).generateCall(expression, opCall, irOperator) + val irTmp = statementGenerator.scope.createTemporaryVariable(irOpCall) + irBlock.addStatement(irTmp) + + // [lhs] = tmp + irBlock.addStatement(irLValue.store(irTmp.defaultLoad())) + + // ^ tmp + irBlock.addStatement(irTmp.defaultLoad()) + + irBlock } - - val opCallGenerator = CallGenerator(parentGenerator).apply { scope.putValue(ktBaseExpression, irLValue) } - val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator) - val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) - val irTmp = parentGenerator.scope.createTemporaryVariable(irOpCall) - irBlock.addStatement(irTmp) - irBlock.addStatement(irLValue.store(irTmp.defaultLoad())) - irBlock.addStatement(irTmp.defaultLoad()) - return irBlock } private fun generatePostfixIncrementDecrementOperator(expression: KtPostfixExpression, irOperator: IrOperator): IrExpression { + val opResolvedCall = getResolvedCall(expression)!! val ktBaseExpression = expression.baseExpression!! - val irLValue = generateLValue(ktBaseExpression, irOperator) - val operatorCall = getResolvedCall(expression)!! + val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, irOperator) - if (irLValue is IrLValueWithAugmentedStore) { - return irLValue.postfixAugmentedStore(operatorCall, irOperator) + return irAssignmentReceiver.assign { irLValue -> + val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator) + + // VAR tmp = [lhs] + val irTmp = scope.createTemporaryVariable(irLValue.load()) + irBlock.addStatement(irTmp) + + // [lhs] = tmp.inc() + val opCall = statementGenerator.pregenerateCall(opResolvedCall) + opCall.setExplicitReceiverValue(VariableLValue(irTmp)) + val irOpCall = CallGenerator(this).generateCall(expression, opCall, irOperator) + irBlock.addStatement(irLValue.store(irOpCall)) + + // ^ tmp + irBlock.addStatement(irTmp.defaultLoad()) + + irBlock } - - val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator) - val opCallGenerator = CallGenerator(parentGenerator) - - val irTmp = parentGenerator.scope.createTemporaryVariable(irLValue.load()) - irBlock.addStatement(irTmp) - - opCallGenerator.scope.putValue(ktBaseExpression, VariableLValue(this, irTmp, irOperator)) - val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) - irBlock.addStatement(irLValue.store(irOpCall)) - - irBlock.addStatement(irTmp.defaultLoad()) - - return irBlock } private fun generatePrefixOperatorAsCall(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression { val resolvedCall = getResolvedCall(expression)!! - return CallGenerator(parentGenerator).generateCall(expression, resolvedCall, irOperator) + return CallGenerator(statementGenerator).generateCall(expression, statementGenerator.pregenerateCall(resolvedCall), irOperator) } private fun generateAugmentedAssignment(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { - val ktLeft = expression.left!! - val irLValue = generateLValue(ktLeft, irOperator) - val operatorCall = getResolvedCall(expression)!! - + val opResolvedCall = getResolvedCall(expression)!! val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false + val ktLeft = expression.left!! + val ktRight = expression.right!! + val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, irOperator) - if (isSimpleAssignment && irLValue is IrLValueWithAugmentedStore) { - return irLValue.augmentedStore(operatorCall, irOperator, parentGenerator.generateExpression(expression.right!!)) - } + return irAssignmentReceiver.assign { irLValue -> + val opCall = statementGenerator.pregenerateCall(opResolvedCall) + opCall.setExplicitReceiverValue(irLValue) + opCall.irValueArgumentsByIndex[0] = statementGenerator.generateExpression(ktRight) + val irOpCall = CallGenerator(this).generateCall(expression, opCall, irOperator) - val opCallGenerator = CallGenerator(parentGenerator).apply { scope.putValue(ktLeft, irLValue) } - val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) - - return if (isSimpleAssignment) { - // Set( Op( Get(), RHS ) ) - irLValue.store(irOpCall) - } - else { - // Op( Get(), RHS ) - irOpCall + if (isSimpleAssignment) { + // Set( Op( Get(), RHS ) ) + irLValue.store(irOpCall) + } + else { + // Op( Get(), RHS ) + irOpCall + } } } private fun generateAssignment(expression: KtBinaryExpression): IrExpression { val ktLeft = expression.left!! - val ktRight = expression.right!! - val irLValue = generateLValue(ktLeft, IrOperator.EQ) - return irLValue.store(parentGenerator.generateExpression(ktRight)) + val irRhs = statementGenerator.generateExpression(expression.right!!) + val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, IrOperator.EQ) + + return if (irAssignmentReceiver is IntermediateReference) + irAssignmentReceiver.store(irRhs) + else + irAssignmentReceiver.assign { irLValue -> irLValue.store(irRhs) } } - private fun generateLValue(ktLeft: KtExpression, irOperator: IrOperator?): IrLValue { + private fun generateAssignmentReceiver(ktLeft: KtExpression, irOperator: IrOperator): AssignmentReceiver { if (ktLeft is KtArrayAccessExpression) { - val irArrayValue = parentGenerator.generateExpression(ktLeft.arrayExpression!!) - val indexExpressions = ktLeft.indexExpressions.map { it to parentGenerator.generateExpression(it) } - val indexedGetCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft) - val indexedSetCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft) - val type = indexedGetCall?.run { resultingDescriptor.returnType } - ?: indexedSetCall?.run { resultingDescriptor.valueParameters.last().type } - ?: throw AssertionError("Either 'get' or 'set' call should be present for an indexed LValue: ${ktLeft.text}") - return IndexedLValue(parentGenerator, ktLeft, irOperator, - irArrayValue, type, indexExpressions, indexedGetCall, indexedSetCall) + return generateArrayAccessAssignmentReceiver(ktLeft, irOperator) } val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS") @@ -313,20 +322,31 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild if (descriptor.isDelegated) TODO("Delegated local variable") else - VariableLValue(this, ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator) - is PropertyDescriptor -> - CallGenerator(parentGenerator).run { - PropertyLValue( - this, - ktLeft, irOperator, descriptor, - generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter), - generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter), - resolvedCall.call.isSafeCall() - ) - } + VariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator) + is PropertyDescriptor -> { + val propertyReceiver = statementGenerator.generateCallReceiver( + ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall() + ) + SimplePropertyLValue(scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, propertyReceiver) + } else -> TODO("Other cases of LHS") } } + private fun generateArrayAccessAssignmentReceiver(ktLeft: KtArrayAccessExpression, irOperator: IrOperator): ArrayAccessAssignmentReceiver { + val irArray = statementGenerator.generateExpression(ktLeft.arrayExpression!!) + val irIndexExpressions = ktLeft.indexExpressions.map { statementGenerator.generateExpression(it) } + + val indexedGetResolvedCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft) + val indexedGetCall = indexedGetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) } + + val indexedSetResolvedCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft) + val indexedSetCall = indexedSetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) } + + return ArrayAccessAssignmentReceiver(irArray, irIndexExpressions, indexedGetCall, indexedSetCall, + CallGenerator(statementGenerator), + ktLeft.startOffset, ktLeft.endOffset, irOperator) + } + } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Primitives.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Primitives.kt new file mode 100644 index 00000000000..4090e73f867 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Primitives.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2016 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.psi2ir.generators + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.ir.expressions.* + +fun primitiveOp1(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, irOperator: IrOperator, + argument: IrExpression): IrExpression = + IrUnaryOperatorImpl(startOffset, endOffset, irOperator, primitiveOpDescriptor, argument) + +fun primitiveOp2(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, irOperator: IrOperator, + argument1: IrExpression, argument2: IrExpression): IrExpression = + IrBinaryOperatorImpl(startOffset, endOffset, irOperator, primitiveOpDescriptor, argument1, argument2) + +fun Generator.constNull(startOffset: Int, endOffset: Int): IrExpression = + IrConstImpl.constNull(startOffset, endOffset, context.builtIns.nullableNothingType) + +fun Generator.equalsNull(startOffset: Int, endOffset: Int, argument: IrExpression): IrExpression = + primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrOperator.EQEQ, + argument, constNull(startOffset, endOffset)) + +// a || b == if (a) true else b +fun Generator.oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen = + IrIfThenElseImpl(startOffset, endOffset, context.builtIns.booleanType, + a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type!!), b, + operator) + +fun Generator.oror(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen = + oror(b.startOffset, b.endOffset, a, b, operator) + +fun Generator.whenComma(a: IrExpression, b: IrExpression): IrWhen = + oror(a, b, IrOperator.WHEN_COMMA) + +// a && b == if (a) b else false +fun Generator.andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen = + IrIfThenElseImpl(startOffset, endOffset, context.builtIns.booleanType, + a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type!!), + operator) + +fun Generator.andand(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen = + andand(b.startOffset, b.endOffset, a, b, operator) \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Scope.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Scope.kt index 4407a0151c5..b6fb5b10dc6 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Scope.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Scope.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginKind import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariableImpl @@ -25,42 +24,12 @@ import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi2ir.generators.values.IrValue -import org.jetbrains.kotlin.psi2ir.generators.values.VariableLValue -import org.jetbrains.kotlin.psi2ir.generators.values.createRematerializableValue -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.KotlinType -import java.util.* +import java.lang.AssertionError -class Scope private constructor(val scopeOwner: DeclarationDescriptor, val parent: Scope? = null) { - internal lateinit var generator: IrBodyGenerator - - constructor(parent: Scope) : this(parent.scopeOwner, parent) { - this.generator = parent.generator - } - - private var lastTemporaryIndex: Int = parent?.lastTemporaryIndex ?: 0 - private fun nextTemporaryIndex(): Int = parent?.nextTemporaryIndex() ?: lastTemporaryIndex++ - - private val values = HashMap() - - private inline fun introduceTemporary(irExpression: IrExpression, nameHint: String?, register: (IrValue) -> Unit): IrVariable? { - val rematerializable = createRematerializableValue(irExpression) - return if (rematerializable != null) { - register(rematerializable) - null - } - else { - createTemporary(irExpression, nameHint, register) - } - } - - private inline fun createTemporary(irExpression: IrExpression, nameHint: String?, register: (IrValue) -> Unit): IrVariable { - val irTemporary = createTemporaryVariable(irExpression, nameHint) - register(VariableLValue(generator, irTemporary)) - return irTemporary - } +class Scope(val scopeOwner: DeclarationDescriptor) { + private var lastTemporaryIndex: Int = 0 + private fun nextTemporaryIndex(): Int = lastTemporaryIndex++ private fun createDescriptorForTemporaryVariable(type: KotlinType, nameHint: String? = null): IrTemporaryVariableDescriptor = IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier(getNameForTemporary(nameHint)), type) @@ -79,52 +48,4 @@ class Scope private constructor(val scopeOwner: DeclarationDescriptor, val paren ), irExpression ) - - fun introduceTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String? = null): IrVariable? = - introduceTemporary(irExpression, nameHint) { putValue(ktExpression, it) } - - fun introduceTemporary(valueParameterDescriptor: ValueParameterDescriptor, irExpression: IrExpression): IrVariable? = - introduceTemporary(irExpression, valueParameterDescriptor.name.asString()) { putValue(valueParameterDescriptor, it) } - - fun introduceTemporary(irExpression: IrExpression): IrVariable? = - introduceTemporary(irExpression, null) { putValue(irExpression, it) } - - fun createTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String?): IrVariable = - createTemporary(irExpression, nameHint) { putValue(ktExpression, it) } - - fun putValue(ktExpression: KtExpression, irValue: IrValue) { - values[ktExpression] = irValue - } - - fun putValue(receiver: ReceiverValue, irValue: IrValue) { - values[receiver] = irValue - } - - fun putValue(parameter: ValueParameterDescriptor, irValue: IrValue) { - values[parameter] = irValue - } - - fun putValue(irExpression: IrExpression, irValue: IrValue) { - values[irExpression] = irValue - } - - fun valueOf(ktExpression: KtExpression): IrExpression? = - values[ktExpression]?.load() ?: parent?.valueOf(ktExpression) - - fun valueOf(receiver: ReceiverValue): IrExpression? = - values[receiver]?.load() ?: parent?.valueOf(receiver) - - fun valueOf(parameter: ValueParameterDescriptor): IrExpression? = - values[parameter]?.load() ?: parent?.valueOf(parameter) - - fun valueOf(irExpression: IrExpression): IrExpression? = - values[irExpression]?.load() ?: parent?.valueOf(irExpression) - - companion object { - fun rootScope(scopeOwner: DeclarationDescriptor, generator: IrBodyGenerator): Scope { - val scope = Scope(scopeOwner) - scope.generator = generator - return scope - } - } } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt index ab2398ae50c..54b9e313ecd 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt @@ -26,7 +26,8 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.deparenthesize -import org.jetbrains.kotlin.psi2ir.generators.values.VariableLValue +import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary +import org.jetbrains.kotlin.psi2ir.intermediate.setExplicitReceiverValue import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -41,14 +42,14 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils import org.jetbrains.kotlin.utils.SmartList -import org.jetbrains.kotlin.utils.addToStdlib.constant +import java.lang.AssertionError class StatementGenerator( override val context: GeneratorContext, val scopeOwner: DeclarationDescriptor, - val expressionBodyGenerator: ExpressionBodyGenerator, + @Suppress("unused") val expressionBodyGenerator: ExpressionBodyGenerator, override val scope: Scope -) : KtVisitor(), IrBodyGenerator { +) : KtVisitor(), BodyGenerator { fun generateExpression(ktExpression: KtExpression): IrExpression = ktExpression.genExpr() @@ -67,10 +68,7 @@ class StatementGenerator( val variableDescriptor = getOrFail(BindingContext.VARIABLE, property) val irLocalVariable = IrVariableImpl(property.startOffset, property.endOffset, IrDeclarationOriginKind.DEFINED, variableDescriptor) - irLocalVariable.initializer = property.initializer?.let { - toExpectedType(it.genExpr(), variableDescriptor.type) - } - + irLocalVariable.initializer = property.initializer?.genExpr() return irLocalVariable } @@ -79,16 +77,19 @@ class StatementGenerator( val irBlock = IrBlockImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, false, IrOperator.SYNTHETIC_BLOCK) val ktInitializer = multiDeclaration.initializer!! - val irTmpInitializer = scope.createTemporaryVariable(ktInitializer.genExpr()) - irBlock.addStatement(irTmpInitializer) + val irTmpInitializerValue = createRematerializableOrTemporary(scope, ktInitializer.genExpr(), irBlock, "container") val irCallGenerator = CallGenerator(this) - irCallGenerator.scope.putValue(ktInitializer, VariableLValue(this, irTmpInitializer)) for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) + + val componentSubstitutedCall = pregenerateCall(componentResolvedCall) + componentSubstitutedCall.setExplicitReceiverValue(irTmpInitializerValue) + val componentVariable = getOrFail(BindingContext.VARIABLE, ktEntry) - val irComponentCall = irCallGenerator.generateCall(ktEntry, componentResolvedCall, IrOperator.COMPONENT_N.withIndex(index + 1)) + val irComponentCall = irCallGenerator.generateCall(ktEntry.startOffset, ktEntry.endOffset, componentSubstitutedCall, + IrOperator.COMPONENT_N.withIndex(index + 1)) val irComponentVar = IrVariableImpl(ktEntry.startOffset, ktEntry.endOffset, IrDeclarationOriginKind.DEFINED, componentVariable, irComponentCall) irBlock.addStatement(irComponentVar) @@ -106,13 +107,8 @@ class StatementGenerator( override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrStatement { val returnTarget = getReturnExpressionTarget(expression) - val irReturnedExpression = expression.returnedExpression?.let { - toExpectedType(it.genExpr(), returnTarget.returnType) - } - return IrReturnImpl( - expression.startOffset, expression.endOffset, - returnTarget, irReturnedExpression - ) + val irReturnedExpression = expression.returnedExpression?.genExpr() + return IrReturnImpl(expression.startOffset, expression.endOffset, returnTarget, irReturnedExpression) } private fun getReturnExpressionTarget(expression: KtReturnExpression): CallableDescriptor = @@ -137,7 +133,7 @@ class StatementGenerator( override fun visitConstantExpression(expression: KtConstantExpression, data: Nothing?): IrExpression { val compileTimeConstant = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext) ?: error("KtConstantExpression was not evaluated: ${expression.text}") - val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmarcastsOrFail(expression)) + val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmartcastsOrFail(expression)) val constantType = constantValue.type return when (constantValue) { @@ -163,7 +159,7 @@ class StatementGenerator( return entry0.genExpr() } } - entries.size == 0 -> return IrConstImpl.string(expression.startOffset, expression.endOffset, getInferredTypeWithSmarcastsOrFail(expression), "") + entries.size == 0 -> return IrConstImpl.string(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcastsOrFail(expression), "") } val irStringTemplate = IrStringConcatenationImpl(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression)) @@ -191,19 +187,19 @@ class StatementGenerator( is FakeCallableDescriptorForObject -> generateExpressionForReferencedDescriptor(descriptor.getReferencedDescriptor(), expression, resolvedCall) is ClassDescriptor -> - if (DescriptorUtils.isObject(descriptor)) - IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) - else if (DescriptorUtils.isEnumEntry(descriptor)) - IrGetEnumValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) - else { - val companionObjectDescriptor = descriptor.companionObjectDescriptor - ?: error("Class value without companion object: $descriptor") - IrGetObjectValueImpl(expression.startOffset, expression.endOffset, - descriptor.classValueType, - companionObjectDescriptor) + when { + DescriptorUtils.isObject(descriptor) -> + IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) + DescriptorUtils.isEnumEntry(descriptor) -> + IrGetEnumValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) + else -> { + IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, + descriptor.companionObjectDescriptor ?: throw AssertionError("Class value without companion object: $descriptor")) + } } is PropertyDescriptor -> { - CallGenerator(this).generateCall(expression, resolvedCall) + // TODO safe calls + CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall)) } is VariableDescriptor -> IrGetVariableImpl(expression.startOffset, expression.endOffset, descriptor) @@ -221,7 +217,9 @@ class StatementGenerator( TODO("VariableAsFunctionResolvedCall = variable call + invoke call") } - return CallGenerator(this).generateCall(expression, resolvedCall) + // TODO safe calls + + return CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall)) } override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression, data: Nothing?): IrStatement = @@ -275,8 +273,8 @@ class StatementGenerator( var irElseBranch: IrExpression? = null whenBranches@while (true) { - val irCondition = generateExpressionWithExpectedType(ktLastIf.condition!!, context.builtIns.booleanType) - val irThenBranch = generateExpressionWithExpectedType(ktLastIf.then!!, resultType) + val irCondition = ktLastIf.condition!!.genExpr() + val irThenBranch = ktLastIf.then!!.genExpr() irBranches.add(Pair(irCondition, irThenBranch)) val ktElse = ktLastIf.`else`?.deparenthesize() @@ -284,7 +282,7 @@ class StatementGenerator( null -> break@whenBranches is KtIfExpression -> ktLastIf = ktElse is KtExpression -> { - irElseBranch = generateExpressionWithExpectedType(ktElse, resultType) + irElseBranch = ktElse.genExpr() break@whenBranches } else -> throw AssertionError("Unexpected else expression: ${ktElse.text}") @@ -312,3 +310,5 @@ class StatementGenerator( } + + diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/WhenExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/WhenExpressionGenerator.kt index 9205e475217..5c5dc7e361c 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/WhenExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/WhenExpressionGenerator.kt @@ -22,16 +22,17 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.defaultLoad -import org.jetbrains.kotlin.psi2ir.generators.operators.getInfixOperator +import org.jetbrains.kotlin.psi2ir.generators.getInfixOperator import org.jetbrains.kotlin.resolve.BindingContext import java.lang.AssertionError -class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase(parentGenerator) { - fun generate(expression: KtWhenExpression): IrExpression { - val conditionsGenerator = CallGenerator(parentGenerator) +class WhenExpressionGenerator(val statementGenerator: StatementGenerator) : BodyGenerator { + override val scope: Scope get() = statementGenerator.scope + override val context: GeneratorContext get() = statementGenerator.context + fun generate(expression: KtWhenExpression): IrExpression { val irSubject = expression.subjectExpression?.let { - conditionsGenerator.scope.createTemporary(it, parentGenerator.generateExpression(it), "subject") + scope.createTemporaryVariable(statementGenerator.generateExpression(it), "subject") } val resultType = getInferredTypeWithSmartcasts(expression) @@ -40,7 +41,7 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody for (ktEntry in expression.entries) { if (ktEntry.isElse) { - irWhen.elseBranch = parentGenerator.generateExpressionWithExpectedType(ktEntry.expression!!, resultType) + irWhen.elseBranch = statementGenerator.generateExpression(ktEntry.expression!!) break } @@ -48,14 +49,14 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody for (ktCondition in ktEntry.conditions) { val irCondition = if (irSubject != null) - generateWhenConditionWithSubject(ktCondition, conditionsGenerator, irSubject) + generateWhenConditionWithSubject(ktCondition, irSubject) else generateWhenConditionNoSubject(ktCondition) - irBranchCondition = irBranchCondition?.let { IrIfThenElseImpl.whenComma(it, irCondition) } ?: irCondition + irBranchCondition = irBranchCondition?.let { whenComma(it, irCondition) } ?: irCondition } - val irBranchResult = parentGenerator.generateExpressionWithExpectedType(ktEntry.expression!!, resultType) + val irBranchResult = statementGenerator.generateExpression(ktEntry.expression!!) irWhen.addBranch(irBranchCondition!!, irBranchResult) } @@ -85,19 +86,14 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody } private fun generateWhenConditionNoSubject(ktCondition: KtWhenCondition): IrExpression = - parentGenerator.generateExpressionWithExpectedType((ktCondition as KtWhenConditionWithExpression).expression!!, - context.builtIns.booleanType) + statementGenerator.generateExpression((ktCondition as KtWhenConditionWithExpression).expression!!) - private fun generateWhenConditionWithSubject( - ktCondition: KtWhenCondition, - conditionsGenerator: CallGenerator, - irSubject: IrVariable - ): IrExpression { + private fun generateWhenConditionWithSubject(ktCondition: KtWhenCondition, irSubject: IrVariable): IrExpression { return when (ktCondition) { is KtWhenConditionWithExpression -> generateEqualsCondition(irSubject, ktCondition) is KtWhenConditionInRange -> - generateInRangeCondition(conditionsGenerator, ktCondition) + generateInRangeCondition(irSubject, ktCondition) is KtWhenConditionIsPattern -> generateIsPatternCondition(irSubject, ktCondition) else -> @@ -113,10 +109,11 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody ) } - private fun generateInRangeCondition(conditionsGenerator: CallGenerator, ktCondition: KtWhenConditionInRange): IrExpression { - val inResolvedCall = getResolvedCall(ktCondition.operationReference)!! + private fun generateInRangeCondition(irSubject: IrVariable, ktCondition: KtWhenConditionInRange): IrExpression { + val inCall = statementGenerator.pregenerateCall(getResolvedCall(ktCondition.operationReference)!!) + inCall.irValueArgumentsByIndex[0] = irSubject.defaultLoad() val inOperator = getInfixOperator(ktCondition.operationReference.getReferencedNameElementType()) - val irInCall = conditionsGenerator.generateCall(ktCondition, inResolvedCall, inOperator) + val irInCall = CallGenerator(statementGenerator).generateCall(ktCondition, inCall, inOperator) return when (inOperator) { IrOperator.IN -> irInCall IrOperator.NOT_IN -> @@ -129,6 +126,6 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody IrBinaryOperatorImpl( ktCondition.startOffset, ktCondition.endOffset, IrOperator.EQEQ, context.irBuiltIns.eqeq, - irSubject.defaultLoad(), parentGenerator.generateExpression(ktCondition.expression!!) + irSubject.defaultLoad(), statementGenerator.generateExpression(ktCondition.expression!!) ) } \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt deleted file mode 100644 index 937ad966a24..00000000000 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2010-2016 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.psi2ir.generators.values - -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.psi.KtArrayAccessExpression -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.psi2ir.defaultLoad -import org.jetbrains.kotlin.psi2ir.generators.CallGenerator -import org.jetbrains.kotlin.psi2ir.generators.Scope -import org.jetbrains.kotlin.psi2ir.generators.StatementGenerator -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.types.KotlinType - -class IndexedLValue( - val statementGenerator: StatementGenerator, - val ktArrayAccessExpression: KtArrayAccessExpression, - val irOperator: IrOperator?, - val irArray: IrExpression, - override val type: KotlinType?, - val indexValues: List>, - val indexedGetCall: ResolvedCall<*>?, - val indexedSetCall: ResolvedCall<*>? -) : IrLValueWithAugmentedStore { - override fun load(): IrExpression { - if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}") - - return generateGetOrSetCallAsDesugaredBlock(indexedGetCall) - } - - override fun store(irExpression: IrExpression): IrExpression { - if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}") - - return generateGetOrSetCallAsDesugaredBlock(indexedSetCall) - } - - private fun generateGetOrSetCallAsDesugaredBlock(call: ResolvedCall<*>, irArgument: IrExpression? = null): IrExpression { - val callGenerator = CallGenerator(statementGenerator) - setupCallGeneratorContext(callGenerator.scope) - - if (irArgument != null) { - callGenerator.scope.putValue(call.resultingDescriptor.valueParameters.last(), SingleExpressionValue(irArgument)) - } - - return callGenerator.generateCall(ktArrayAccessExpression, call, irOperator) - } - - override fun prefixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression { - if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}") - if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}") - - val callGenerator = CallGenerator(statementGenerator) - - val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, true, irOperator) - - val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver - val irGetCall = callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator) - callGenerator.scope.putValue(operatorCallReceiver!!, SingleExpressionValue(irGetCall)) - - val irOpCall = callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator) - val irTmp = callGenerator.scope.createTemporaryVariable(irOpCall) - irBlock.addStatement(irTmp) - - callGenerator.scope.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), VariableLValue(callGenerator, irTmp)) - - irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator)) - - irBlock.addStatement(irTmp.defaultLoad()) - - return irBlock - } - - override fun postfixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression { - if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}") - if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}") - - val callGenerator = CallGenerator(statementGenerator) - - val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, true, irOperator) - - val irGetCall = callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator) - val irTmp = callGenerator.scope.createTemporaryVariable(irGetCall) - irBlock.addStatement(irTmp) - - val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver - callGenerator.scope.putValue(operatorCallReceiver!!, VariableLValue(callGenerator, irTmp)) - val irOpCall = callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator) - callGenerator.scope.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), SingleExpressionValue(irOpCall)) - val irSetCall = callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator) - irBlock.addStatement(irSetCall) - - irBlock.addStatement(irTmp.defaultLoad()) - - return irBlock - } - - override fun augmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator, irOperatorArgument: IrExpression): IrExpression { - if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}") - if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}") - - val callGenerator = CallGenerator(statementGenerator) - - val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, false, irOperator) - - callGenerator.scope.putValue(operatorCall.resultingDescriptor.valueParameters[0], SingleExpressionValue(irOperatorArgument)) - - val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver - callGenerator.scope.putValue(operatorCallReceiver!!, - SingleExpressionValue(callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator))) - - callGenerator.scope.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), - SingleExpressionValue(callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator))) - - irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator)) - - return irBlock - } - - private fun createDesugaredBlockWithTemporaries( - call: ResolvedCall<*>, - callGenerator: CallGenerator, - hasResult: Boolean, - operator: IrOperator? - ): IrBlockImpl { - val irBlock = createDesugaredBlock(call, hasResult, operator) - defineTemporaryVariables(irBlock, callGenerator.scope) - return irBlock - } - - private fun createDesugaredBlock(call: ResolvedCall<*>, hasResult: Boolean, operator: IrOperator?): IrBlockImpl { - return IrBlockImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset, - if (hasResult) type else call.resultingDescriptor.returnType, - hasResult, operator) - } - - private fun defineTemporaryVariables(irBlock: IrBlockImpl, scope: Scope) { - irBlock.addIfNotNull(scope.introduceTemporary(ktArrayAccessExpression.arrayExpression!!, irArray, "array")) - - var index = 0 - for ((ktIndexExpression, irIndexValue) in indexValues) { - irBlock.addIfNotNull(scope.introduceTemporary(ktIndexExpression, irIndexValue, "index${index++}")) - } - } - - private fun setupCallGeneratorContext(scope: Scope) { - scope.putValue(ktArrayAccessExpression.arrayExpression!!, SingleExpressionValue(irArray)) - - for ((ktIndexExpression, irIndexValue) in indexValues) { - scope.putValue(ktIndexExpression, SingleExpressionValue(irIndexValue)) - } - } -} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt deleted file mode 100644 index 7883ae0c521..00000000000 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2016 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.psi2ir.generators.values - -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.psi2ir.generators.IrBodyGenerator -import org.jetbrains.kotlin.psi2ir.generators.toExpectedType -import org.jetbrains.kotlin.types.KotlinType - -class PropertyLValue( - val irBodyGenerator: IrBodyGenerator, - val ktElement: KtElement, - val irOperator: IrOperator?, - val descriptor: PropertyDescriptor, - val dispatchReceiver: IrExpression?, - val extensionReceiver: IrExpression?, - val isSafe: Boolean -) : IrLValue { - override val type: KotlinType? - get() = descriptor.type - - override fun load(): IrExpression { - val getter = descriptor.getter!! - return IrGetterCallImpl( - ktElement.startOffset, ktElement.endOffset, - getter.returnType, getter, isSafe, - dispatchReceiver, extensionReceiver, IrOperator.GET_PROPERTY - ) - } - - override fun store(irExpression: IrExpression): IrExpression { - val setter = descriptor.setter!! - val irArgument = irBodyGenerator.toExpectedType(irExpression, descriptor.type) - val irCall = IrSetterCallImpl(ktElement.startOffset, ktElement.endOffset, - setter.returnType, setter, isSafe, - dispatchReceiver, extensionReceiver, irArgument, irOperator) - return irCall - } -} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/RematerializableValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/RematerializableValue.kt deleted file mode 100644 index f75ec70bbed..00000000000 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/RematerializableValue.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2016 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.psi2ir.generators.values - -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.types.KotlinType - -interface IrRematerializableValue : IrValue { - val irExpression: IrExpression - - override val type: KotlinType? - get() = irExpression.type -} - -fun createRematerializableValue(irExpression: IrExpression): IrRematerializableValue? = - when (irExpression) { - is IrConst<*> -> IrRematerializableLiteralValue(irExpression) - is IrGetVariable -> IrRematerializableVariableValue(irExpression) - is IrGetExtensionReceiver -> IrRematerializableExtensionReceiverValue(irExpression) - is IrThisReference -> IrRematerializableThisValue(irExpression) - else -> null - } - -class IrRematerializableLiteralValue(override val irExpression: IrConst<*>): IrRematerializableValue { - override fun load(): IrExpression = - IrConstImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, - irExpression.kind, irExpression.kind.valueOf(irExpression)) -} - -class IrRematerializableVariableValue(override val irExpression: IrGetVariable) : IrRematerializableValue { - override fun load(): IrExpression = - IrGetVariableImpl(irExpression.startOffset, irExpression.endOffset, irExpression.descriptor) -} - -class IrRematerializableExtensionReceiverValue(override val irExpression: IrGetExtensionReceiver) : IrRematerializableValue { - override fun load(): IrExpression = - IrGetExtensionReceiverImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, irExpression.descriptor) -} - -class IrRematerializableThisValue(override val irExpression: IrThisReference): IrRematerializableValue { - override fun load(): IrExpression = - IrThisReferenceImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, irExpression.classDescriptor) -} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/Value.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/Value.kt deleted file mode 100644 index bc2fac03497..00000000000 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/Value.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2016 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.psi2ir.generators.values - -import org.jetbrains.kotlin.ir.assertDetached -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrOperator -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.types.KotlinType - -interface IrValue { - fun load(): IrExpression - val type: KotlinType? -} - -class SingleExpressionValue(val irExpression: IrExpression) : IrValue { - init { - irExpression.assertDetached() - } - - private var instantiated = false - - override fun load() = - if (!instantiated) { - instantiated = true - irExpression - } - else - throw AssertionError("Single exprssion value is already instantiated") - - override val type: KotlinType? get() = irExpression.type -} - -interface IrLValue : IrValue { - fun store(irExpression: IrExpression): IrExpression -} - -interface IrLValueWithAugmentedStore : IrLValue { - fun postfixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression - fun prefixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression - fun augmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator, irOperatorArgument: IrExpression): IrExpression -} - diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/ArrayAccessAssignmentReceiver.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/ArrayAccessAssignmentReceiver.kt new file mode 100644 index 00000000000..e7c5937c218 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/ArrayAccessAssignmentReceiver.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi2ir.generators.CallGenerator +import org.jetbrains.kotlin.types.KotlinType +import java.lang.AssertionError + +class ArrayAccessAssignmentReceiver( + val irArray: IrExpression, + val irIndices: List, + val indexedGetCall: PregeneratedCall?, + val indexedSetCall: PregeneratedCall?, + val callGenerator: CallGenerator, + val startOffset: Int, + val endOffset: Int, + val operator: IrOperator +) : AssignmentReceiver { + private val type: KotlinType = indexedGetCall?.let { it.descriptor.returnType!! } ?: + indexedSetCall?.let { it.descriptor.valueParameters.last().type } ?: + throw AssertionError("Array access should have either indexed-get call or indexed-set call") + + override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression { + val hasResult = operator.isAssignmentOperatorWithResult() + val resultType = if (hasResult) type else callGenerator.context.builtIns.unitType + val irBlock = IrBlockImpl(startOffset, endOffset, resultType, hasResult, operator) + + val irArrayValue = createRematerializableOrTemporary(callGenerator.scope, irArray, irBlock, "array") + + val irIndexValues = irIndices.mapIndexed { i, irIndex -> + createRematerializableOrTemporary(callGenerator.scope, irIndex, irBlock, "index$i") + } + + indexedGetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues) + indexedSetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues) + val irLValue = LValueWithGetterAndSetterCalls(callGenerator, indexedGetCall, indexedSetCall, startOffset, endOffset, operator) + irBlock.inlineStatement(withLValue(irLValue)) + + return irBlock + } + + private fun PregeneratedCall.fillArrayAndIndexArguments(arrayValue: IntermediateValue, indexValues: List) { + setExplicitReceiverValue(arrayValue) + indexValues.forEachIndexed { i, irIndexValue -> + irValueArgumentsByIndex[i] = irIndexValue.load() + } + } +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/LValueWithGetterAndSetterCalls.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/LValueWithGetterAndSetterCalls.kt new file mode 100644 index 00000000000..6d82359676b --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/LValueWithGetterAndSetterCalls.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrOperator +import org.jetbrains.kotlin.psi2ir.generators.CallGenerator +import org.jetbrains.kotlin.psi2ir.intermediate.PregeneratedCall +import org.jetbrains.kotlin.psi2ir.intermediate.argumentsCount +import org.jetbrains.kotlin.types.KotlinType + +class LValueWithGetterAndSetterCalls( + val callGenerator: CallGenerator, + val getterCall: PregeneratedCall?, + val setterCall: PregeneratedCall?, + val startOffset: Int, + val endOffset: Int, + val operator: IrOperator? = null +) : IntermediateReference { + private val descriptor: CallableDescriptor = + getterCall?.descriptor ?: setterCall?.descriptor ?: + throw AssertionError("Call-based LValue should have either a getter or a setter call") + + private var getterInstantiated = false + private var setterInstantiated = false + + override val type: KotlinType? get() = descriptor.returnType + + override fun load(): IrExpression { + if (getterCall == null) throw AssertionError("No getter call for $descriptor") + if (getterInstantiated) throw AssertionError("Getter for $descriptor has already been instantiated") + getterInstantiated = true + return callGenerator.generateCall(startOffset, endOffset, getterCall, operator) + } + + override fun store(irExpression: IrExpression): IrExpression { + if (setterCall == null) throw AssertionError("No setter call for $descriptor") + if (setterInstantiated) throw AssertionError("Setter for $descriptor has already been instantiated") + setterInstantiated = true + setterCall.irValueArgumentsByIndex[setterCall.argumentsCount - 1] = irExpression + return callGenerator.generateCall(startOffset, endOffset, setterCall, operator) + } + +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceCallValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceCallValue.kt new file mode 100644 index 00000000000..682c587298e --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceCallValue.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrOperator +import org.jetbrains.kotlin.psi2ir.generators.CallGenerator +import org.jetbrains.kotlin.psi2ir.generators.StatementGenerator +import org.jetbrains.kotlin.psi2ir.intermediate.PregeneratedCall +import org.jetbrains.kotlin.types.KotlinType + +class OnceCallValue( + val statementGenerator: StatementGenerator, + val call: PregeneratedCall, + val startOffset: Int, + val endOffset: Int, + val operator: IrOperator? = null +): IntermediateValue { + private var instantiated = false + + override fun load(): IrExpression { + if (instantiated) throw AssertionError("Value for call ${call.descriptor} has already been instantiated") + instantiated = true + return CallGenerator(statementGenerator).generateCall(startOffset, endOffset, call, operator) + } + + override val type: KotlinType? get() = call.descriptor.returnType +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceExpressionValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceExpressionValue.kt new file mode 100644 index 00000000000..3bdb4ac6599 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/OnceExpressionValue.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.ir.assertDetached +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.psi2ir.generators.CallGenerator +import org.jetbrains.kotlin.types.KotlinType + +class OnceExpressionValue(val irExpression: IrExpression) : IntermediateValue { + init { + irExpression.assertDetached() + } + + private var instantiated = false + + override fun load(): IrExpression { + if (instantiated) throw AssertionError("Single expression value for ${irExpression.render()} is already instantiated") + instantiated = true + return irExpression + } + + override val type: KotlinType? get() = irExpression.type +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PregeneratedCall.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PregeneratedCall.kt new file mode 100644 index 00000000000..559750fe822 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PregeneratedCall.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.psi2ir.isValueArgumentReorderingRequired +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.KotlinType + +class PregeneratedCall(val original: ResolvedCall<*>) { + val descriptor: CallableDescriptor = original.resultingDescriptor + + var superQualifier: ClassDescriptor? = null + + lateinit var callReceiver: CallReceiver + + val irValueArgumentsByIndex = arrayOfNulls(descriptor.valueParameters.size) + + fun getValueArgument(valueParameterDescriptor: ValueParameterDescriptor) = + irValueArgumentsByIndex[valueParameterDescriptor.index] +} + +val PregeneratedCall.argumentsCount: Int get() = + irValueArgumentsByIndex.size + +fun PregeneratedCall.getValueArgumentsInParameterOrder(): List = + descriptor.valueParameters.map { irValueArgumentsByIndex[it.index] } + +fun PregeneratedCall.isValueArgumentReorderingRequired() = + original.isValueArgumentReorderingRequired() + +val PregeneratedCall.hasExtensionReceiver: Boolean get() = + descriptor.extensionReceiverParameter != null + +val PregeneratedCall.hasDispatchReceiver: Boolean get() = + descriptor.dispatchReceiverParameter != null + +val PregeneratedCall.extensionReceiverType: KotlinType? get() = + descriptor.extensionReceiverParameter?.type + +val PregeneratedCall.dispatchReceiverType: KotlinType? get() = + descriptor.dispatchReceiverParameter?.type + +val PregeneratedCall.explicitReceiverParameter: ReceiverParameterDescriptor? get() = + descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter + +val PregeneratedCall.explicitReceiverType: KotlinType? get() = + explicitReceiverParameter?.type + +fun PregeneratedCall.setExplicitReceiverValue(explicitReceiverValue: IntermediateValue) { + val previousCallReceiver = callReceiver + callReceiver = object : CallReceiver { + override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression { + return previousCallReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + val newDispatchReceiverValue = if (hasExtensionReceiver) dispatchReceiverValue else explicitReceiverValue + val newExtensionReceiverValue = if (hasExtensionReceiver) explicitReceiverValue else null + withDispatchAndExtensionReceivers(newDispatchReceiverValue, newExtensionReceiverValue) + } + } + } +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/RematerializableValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/RematerializableValue.kt new file mode 100644 index 00000000000..18f808f18b6 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/RematerializableValue.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi2ir.generators.Scope +import org.jetbrains.kotlin.types.KotlinType + +class RematerializableValue(val irExpression: IrExpressionWithCopy) : IntermediateValue { + override val type: KotlinType? + get() = irExpression.type + + override fun load(): IrExpression = irExpression.copy() +} + +fun createRematerializableValue(irExpression: IrExpression): IntermediateValue? = + (irExpression as? IrExpressionWithCopy)?.let { RematerializableValue(it) } + +inline fun createRematerializableOrTemporary( + scope: Scope, + irExpression: IrExpression, + nameHint: String? = null, + addVariable: (IrVariable) -> Unit +): IntermediateValue { + val rematerializable = createRematerializableValue(irExpression) + if (rematerializable != null) { + return rematerializable + } + + val temporaryVariable = scope.createTemporaryVariable(irExpression, nameHint) + addVariable(temporaryVariable) + return VariableLValue(temporaryVariable) +} + +fun createRematerializableOrTemporary(scope: Scope, irExpression: IrExpression, block: IrBlockImpl, nameHint: String? = null): IntermediateValue = + createRematerializableOrTemporary(scope, irExpression, nameHint) { + block.addStatement(it) + } \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt new file mode 100644 index 00000000000..0d246755bc1 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SafeCallReceiver.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.ir.expressions.IrBlockImpl +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrIfThenElseImpl +import org.jetbrains.kotlin.ir.expressions.IrOperator +import org.jetbrains.kotlin.psi2ir.generators.BodyGenerator +import org.jetbrains.kotlin.psi2ir.generators.constNull +import org.jetbrains.kotlin.psi2ir.generators.equalsNull +import org.jetbrains.kotlin.types.typeUtil.makeNullable + + +class SafeCallReceiver( + val generator: BodyGenerator, + val startOffset: Int, + val endOffset: Int, + val explicitReceiver: IrExpression, + val implicitDispatchReceiverValue: IntermediateValue? +) : CallReceiver { + override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression { + val irTmp = generator.scope.createTemporaryVariable(explicitReceiver, "safe_receiver") + val safeReceiverValue = VariableLValue(irTmp) + + val dispatchReceiverValue: IntermediateValue + val extensionReceiverValue: IntermediateValue? + if (implicitDispatchReceiverValue != null) { + dispatchReceiverValue = implicitDispatchReceiverValue + extensionReceiverValue = safeReceiverValue + } + else { + dispatchReceiverValue = safeReceiverValue + extensionReceiverValue = null + } + + val irResult = withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue) + val resultType = irResult.type?.makeNullable() + + val irBlock = IrBlockImpl(startOffset, endOffset, resultType, resultType != null, IrOperator.SAFE_CALL) + + irBlock.addStatement(irTmp) + + val irIfThenElse = IrIfThenElseImpl(startOffset, endOffset, resultType, + generator.equalsNull(startOffset, endOffset, safeReceiverValue.load()), + generator.constNull(startOffset, endOffset), + irResult, + IrOperator.SAFE_CALL) + irBlock.addStatement(irIfThenElse) + + return irBlock + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KtElementUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimpleCallReceiver.kt similarity index 54% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KtElementUtils.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimpleCallReceiver.kt index efd3fedddb7..3ae8f470755 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KtElementUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimpleCallReceiver.kt @@ -14,12 +14,15 @@ * limitations under the License. */ -package org.jetbrains.kotlin.psi2ir +package org.jetbrains.kotlin.psi2ir.intermediate -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtPsiUtil - -fun KtElement.deparenthesize(): KtElement = - if (this is KtExpression) KtPsiUtil.safeDeparenthesize(this) else this +import org.jetbrains.kotlin.ir.expressions.IrExpression +class SimpleCallReceiver( + val dispatchReceiverValue: IntermediateValue?, + val extensionReceiverValue: IntermediateValue? +) : CallReceiver { + override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression { + return withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue) + } +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt new file mode 100644 index 00000000000..f4f77cbfc55 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi2ir.generators.Scope +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.SmartList + +class SimplePropertyLValue( + val scope: Scope, + val startOffset: Int, + val endOffset: Int, + val irOperator: IrOperator?, + val descriptor: PropertyDescriptor, + val callReceiver: CallReceiver +) : IntermediateReference, AssignmentReceiver { + override val type: KotlinType? + get() = descriptor.type + + override fun load(): IrExpression { + val getter = descriptor.getter!! + return callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + IrGetterCallImpl(startOffset, endOffset, getter, + dispatchReceiverValue?.load(), + extensionReceiverValue?.load(), + irOperator) + } + } + + override fun store(irExpression: IrExpression): IrExpression { + val setter = descriptor.setter!! + return callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + IrSetterCallImpl(startOffset, endOffset, setter, + dispatchReceiverValue?.load(), + extensionReceiverValue?.load(), + irExpression, irOperator) + } + } + + override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression { + return callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + val variablesForReceivers = SmartList() + + val tmpDispatchReceiverValue = dispatchReceiverValue?.load()?.let { + createRematerializableOrTemporary(scope, it, "this") { + irVar -> variablesForReceivers.add(irVar) + } + } + + val tmpExtensionReceiverValue = extensionReceiverValue?.load()?.let { + createRematerializableOrTemporary(scope, it, "receiver") { + irVar -> variablesForReceivers.add(irVar) + } + } + + val irResultExpression = withLValue( + SimplePropertyLValue(scope, startOffset, endOffset, irOperator, descriptor, + SimpleCallReceiver(tmpDispatchReceiverValue, tmpExtensionReceiverValue)) + ) + + if (variablesForReceivers.isEmpty()) { + irResultExpression + } + else { + val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irResultExpression.type != null, irOperator) + irBlock.addAll(variablesForReceivers) + irBlock.addStatement(irResultExpression) + irBlock + } + } + + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt new file mode 100644 index 00000000000..14f7b3bd124 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/Values.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 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.psi2ir.intermediate + +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.types.KotlinType + +interface IntermediateValue { + fun load(): IrExpression + val type: KotlinType? +} + +interface IntermediateReference : IntermediateValue { + fun store(irExpression: IrExpression): IrExpression +} + +interface AssignmentReceiver { + fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression +} + +interface CallReceiver { + fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/VariableLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt similarity index 71% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/VariableLValue.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt index 7fe7ec9d05a..30b8efe3363 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/VariableLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/VariableLValue.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.psi2ir.generators.values +package org.jetbrains.kotlin.psi2ir.intermediate import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.ir.declarations.IrVariable @@ -22,22 +22,16 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetVariableImpl import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.ir.expressions.IrSetVariableImpl -import org.jetbrains.kotlin.psi2ir.generators.IrBodyGenerator -import org.jetbrains.kotlin.psi2ir.generators.toExpectedType import org.jetbrains.kotlin.types.KotlinType class VariableLValue( - val irBodyGenerator: IrBodyGenerator, val startOffset: Int, val endOffset: Int, val descriptor: VariableDescriptor, val irOperator: IrOperator? = null -) : IrLValue { - constructor( - irBodyGenerator: IrBodyGenerator, - irVariable: IrVariable, - irOperator: IrOperator? = null - ) : this(irBodyGenerator, irVariable.startOffset, irVariable.endOffset, irVariable.descriptor, irOperator) +) : IntermediateReference, AssignmentReceiver { + constructor(irVariable: IrVariable, irOperator: IrOperator? = null) : + this(irVariable.startOffset, irVariable.endOffset, irVariable.descriptor, irOperator) override val type: KotlinType? get() = descriptor.type @@ -45,6 +39,8 @@ class VariableLValue( IrGetVariableImpl(startOffset, endOffset, descriptor, irOperator) override fun store(irExpression: IrExpression): IrExpression = - IrSetVariableImpl(startOffset, endOffset, descriptor, - irBodyGenerator.toExpectedType(irExpression, descriptor.type), irOperator) + IrSetVariableImpl(startOffset, endOffset, descriptor, irExpression, irOperator) + + override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression = + withLValue(this) } \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt new file mode 100644 index 00000000000..3349d5c5ce4 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2016 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.psi2ir.transformations + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.detach +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.replaceWith +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.psi2ir.containsNull +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.isNullabilityFlexible +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable +import org.jetbrains.kotlin.types.upperIfFlexible +import java.lang.AssertionError + +fun insertImplicitCasts(builtIns: KotlinBuiltIns, element: IrElement) { + element.accept(InsertImplicitCasts(builtIns), null) +} + +class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementVisitor { + override fun visitElement(element: IrElement, data: Nothing?) { + element.acceptChildren(this, null) + } + + override fun visitCall(expression: IrCall, data: Nothing?) { + expression.acceptChildren(this, null) + + with(expression) { + dispatchReceiver?.replaceWithCast(descriptor.dispatchReceiverParameter?.type) + extensionReceiver?.replaceWithCast(descriptor.extensionReceiverParameter?.type) + for (index in descriptor.valueParameters.indices) { + val parameterType = descriptor.valueParameters[index].type + getArgument(index)?.replaceWithCast(parameterType) + } + } + } + + override fun visitReturn(expression: IrReturn, data: Nothing?) { + expression.acceptChildren(this, null) + + expression.value?.replaceWithCast(expression.returnTarget.returnType) + } + + override fun visitSetVariable(expression: IrSetVariable, data: Nothing?) { + expression.acceptChildren(this, null) + + expression.value.replaceWithCast(expression.descriptor.type) + } + + override fun visitVariable(declaration: IrVariable, data: Nothing?) { + declaration.acceptChildren(this, null) + + declaration.initializer?.replaceWithCast(declaration.descriptor.type) + } + + override fun visitWhen(expression: IrWhen, data: Nothing?) { + expression.acceptChildren(this, null) + + for (branchIndex in expression.branchIndices) { + expression.getNthCondition(branchIndex)!!.replaceWithCast(builtIns.booleanType) + expression.getNthResult(branchIndex)!!.replaceWithCast(expression.type) + } + expression.elseBranch?.replaceWithCast(expression.type) + } + + override fun visitLoop(loop: IrLoop, data: Nothing?) { + loop.acceptChildren(this, null) + + loop.condition.replaceWithCast(builtIns.booleanType) + } + + private fun IrExpression.replaceWithCast(expectedType: KotlinType?) { + replaceWith { it.wrapWithImplicitCast(expectedType) } + } + + private fun IrExpression.wrapWithImplicitCast(expectedType: KotlinType?): IrExpression { + if (this is IrBlock && !this.hasResult) return this + if (expectedType == null) return this + if (expectedType.isError) return this + if (KotlinBuiltIns.isUnit(expectedType)) return this // TODO expose coercion to Unit in IR? + + val valueType = this.type ?: throw AssertionError("expectedType != null, valueType == null: $this") + + if (valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull()) { + val nonNullValueType = valueType.upperIfFlexible().makeNotNullable(); + return IrTypeOperatorCallImpl( + this.startOffset, this.endOffset, nonNullValueType, + IrTypeOperator.IMPLICIT_NOTNULL, nonNullValueType, this.detach() + ).wrapWithImplicitCast(expectedType) + } + + if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType.makeNotNullable(), expectedType)) { + return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, expectedType, + IrTypeOperator.IMPLICIT_CAST, expectedType, this.detach()) + } + + return this + } +} + diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt index 093dc3ea6e9..1a4fc992515 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt @@ -55,6 +55,15 @@ fun IrElement.replaceWith(otherElement: IrElement) { parent.replaceChild(slot, otherElement.detach()) } +fun T.replaceWith(transformation: (T) -> IrElement) { + val originalParent = this.parent ?: throw AssertionError("Can't replace a non-root element $this") + val originalSlot = this.slot + val transformed = transformation(this) + if (transformed != this) { + originalParent.replaceChild(originalSlot, transformed) + } +} + fun IrElement.assertChild(child: IrElement) { assert(getChild(child.slot) == child) { "$this: Invalid child: $child" } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt index 3c584517d1d..4553754f24a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt @@ -68,7 +68,7 @@ class IrVariableImpl( } override fun accept(visitor: IrElementVisitor, data: D): R { - return visitor.visitLocalVariable(this, data) + return visitor.visitVariable(this, data) } override fun acceptChildren(visitor: IrElementVisitor, data: D) { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt index d941e2a2ca6..f972ecea6ea 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt @@ -48,6 +48,15 @@ class IrBlockImpl( statements.add(statement) } + fun addAll(statements: List) { + statements.forEach { it.assertDetached() } + val originalSize = statements.size + this.statements.addAll(statements) + statements.forEachIndexed { i, irStatement -> + irStatement.setTreeLocation(this, originalSize + i) + } + } + override fun getChild(slot: Int): IrElement? = statements.getOrNull(slot) @@ -66,4 +75,14 @@ class IrBlockImpl( override fun acceptChildren(visitor: IrElementVisitor, data: D) { statements.forEach { it.accept(visitor, data) } } +} + +fun IrBlockImpl.inlineStatement(statement: IrStatement) { + if (statement is IrBlock) { + statement.statements.forEach { it.detach() } + addAll(statement.statements) + } + else { + addStatement(statement) + } } \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCall.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCall.kt index 658281a7f88..1e196082406 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCall.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCall.kt @@ -37,10 +37,9 @@ class IrCallImpl( endOffset: Int, type: KotlinType?, override val descriptor: CallableDescriptor, - isSafe: Boolean, override val operator: IrOperator? = null, override val superQualifier: ClassDescriptor? = null -) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrCall { +) : IrMemberAccessExpressionBase(startOffset, endOffset, type), IrCall { private val argumentsByParameterIndex = arrayOfNulls(descriptor.valueParameters.size) @@ -87,12 +86,10 @@ class IrCallImpl( abstract class IrPropertyAccessorCallBase( startOffset: Int, endOffset: Int, - type: KotlinType?, override val descriptor: CallableDescriptor, - isSafe: Boolean, override val operator: IrOperator? = null, override val superQualifier: ClassDescriptor? = null -) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrCall { +) : IrMemberAccessExpressionBase(startOffset, endOffset, descriptor.returnType), IrCall { override fun accept(visitor: IrElementVisitor, data: D): R { return visitor.visitCall(this, data) } @@ -101,23 +98,19 @@ abstract class IrPropertyAccessorCallBase( class IrGetterCallImpl( startOffset: Int, endOffset: Int, - type: KotlinType?, descriptor: CallableDescriptor, - isSafe: Boolean, operator: IrOperator? = null, superQualifier: ClassDescriptor? = null -) : IrPropertyAccessorCallBase(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier), IrCall { +) : IrPropertyAccessorCallBase(startOffset, endOffset, descriptor, operator, superQualifier), IrCall { constructor( startOffset: Int, endOffset: Int, - type: KotlinType?, descriptor: CallableDescriptor, - isSafe: Boolean, dispatchReceiver: IrExpression?, extensionReceiver: IrExpression?, operator: IrOperator? = null, superQualifier: ClassDescriptor? = null - ) : this(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier) { + ) : this(startOffset, endOffset, descriptor, operator, superQualifier) { this.dispatchReceiver = dispatchReceiver this.extensionReceiver = extensionReceiver } @@ -136,24 +129,20 @@ class IrGetterCallImpl( class IrSetterCallImpl( startOffset: Int, endOffset: Int, - type: KotlinType?, descriptor: CallableDescriptor, - isSafe: Boolean, operator: IrOperator? = null, superQualifier: ClassDescriptor? = null -) : IrPropertyAccessorCallBase(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier), IrCall { +) : IrPropertyAccessorCallBase(startOffset, endOffset, descriptor, operator, superQualifier), IrCall { constructor( startOffset: Int, endOffset: Int, - type: KotlinType?, descriptor: CallableDescriptor, - isSafe: Boolean, dispatchReceiver: IrExpression?, extensionReceiver: IrExpression?, argument: IrExpression, operator: IrOperator? = null, superQualifier: ClassDescriptor? = null - ) : this(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier) { + ) : this(startOffset, endOffset, descriptor, operator, superQualifier) { this.dispatchReceiver = dispatchReceiver this.extensionReceiver = extensionReceiver putArgument(SETTER_ARGUMENT_INDEX, argument) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrConst.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrConst.kt index c7654630ca2..f1d171f5d7a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrConst.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrConst.kt @@ -19,9 +19,11 @@ package org.jetbrains.kotlin.ir.expressions import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType -interface IrConst : IrExpression { +interface IrConst : IrExpression, IrExpressionWithCopy { val kind: IrConstKind val value: T + + override fun copy(): IrConst } sealed class IrConstKind(val asString: kotlin.String) { @@ -52,6 +54,9 @@ class IrConstImpl ( override fun accept(visitor: IrElementVisitor, data: D): R = visitor.visitConst(this, data) + override fun copy(): IrConst = + IrConstImpl(startOffset, endOffset, type, kind, value) + companion object { fun string(startOffset: Int, endOffset: Int, type: KotlinType, value: String): IrConstImpl = IrConstImpl(startOffset, endOffset, type, IrConstKind.String, value) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrDummyExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrDummyExpression.kt index bc860b7d18c..8fb8515859f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrDummyExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrDummyExpression.kt @@ -24,11 +24,14 @@ class IrDummyExpression( endOffset: Int, type: KotlinType?, val description: String -) : IrTerminalExpressionBase(startOffset, endOffset, type) { +) : IrTerminalExpressionBase(startOffset, endOffset, type), IrExpressionWithCopy { override fun accept(visitor: IrElementVisitor, data: D): R = visitor.visitDummyExpression(this, data) override fun acceptChildren(visitor: IrElementVisitor, data: D) { // No children } + + override fun copy(): IrDummyExpression = + IrDummyExpression(startOffset, endOffset, type, description) } \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt index cde1745d3a9..7fb5446eb62 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt @@ -27,6 +27,10 @@ interface IrExpression : IrStatement { val type: KotlinType? } +interface IrExpressionWithCopy : IrExpression { + fun copy(): IrExpression +} + abstract class IrExpressionBase( startOffset: Int, endOffset: Int, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiver.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiver.kt index 5a7fca3f38c..ce015d7abc3 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiver.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiver.kt @@ -20,8 +20,10 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType -interface IrGetExtensionReceiver : IrDeclarationReference { +interface IrGetExtensionReceiver : IrDeclarationReference, IrExpressionWithCopy { override val descriptor: ReceiverParameterDescriptor + + override fun copy(): IrGetExtensionReceiver } class IrGetExtensionReceiverImpl( @@ -32,4 +34,7 @@ class IrGetExtensionReceiverImpl( ) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, type, descriptor), IrGetExtensionReceiver { override fun accept(visitor: IrElementVisitor, data: D): R = visitor.visitGetExtensionReceiver(this, data) + + override fun copy(): IrGetExtensionReceiver = + IrGetExtensionReceiverImpl(startOffset, endOffset, type, descriptor) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrLoop.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrLoop.kt index 9e76b41c1ef..f6da22904b8 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrLoop.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrLoop.kt @@ -21,22 +21,17 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor interface IrLoop : IrExpression { var body: IrExpression -} - -interface IrConditionalLoop : IrLoop { var condition: IrExpression } -interface IrWhileLoop : IrConditionalLoop +interface IrWhileLoop : IrLoop -interface IrDoWhileLoop : IrConditionalLoop +interface IrDoWhileLoop : IrLoop abstract class IrLoopBase( startOffset: Int, endOffset: Int ) : IrExpressionBase(startOffset, endOffset, null), IrLoop { - - private var bodyImpl: IrExpression? = null override var body: IrExpression get() = bodyImpl!! @@ -63,7 +58,7 @@ abstract class IrLoopBase( abstract class IrConditionalLoopBase( startOffset: Int, endOffset: Int -) : IrLoopBase(startOffset, endOffset), IrConditionalLoop { +) : IrLoopBase(startOffset, endOffset) { private var conditionImpl: IrExpression? = null override var condition: IrExpression get() = conditionImpl!! diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrMemberAccessExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrMemberAccessExpression.kt index 5217a06fe16..21f90436f56 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrMemberAccessExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrMemberAccessExpression.kt @@ -23,14 +23,12 @@ import org.jetbrains.kotlin.types.KotlinType interface IrMemberAccessExpression : IrDeclarationReference { var dispatchReceiver: IrExpression? var extensionReceiver: IrExpression? - val isSafe: Boolean } abstract class IrMemberAccessExpressionBase( startOffset: Int, endOffset: Int, - type: KotlinType?, - override val isSafe: Boolean + type: KotlinType? ) : IrExpressionBase(startOffset, endOffset, type), IrMemberAccessExpression { override var dispatchReceiver: IrExpression? = null set(newReceiver) { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt index 600e8af113f..c819e13b720 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt @@ -20,6 +20,8 @@ interface IrOperator { abstract class IrOperatorImpl(val debugName: String): IrOperator { override fun toString(): String = debugName } + + object SAFE_CALL : IrOperatorImpl("SAFE_CALL") object UMINUS : IrOperatorImpl("UMINUS") object UPLUS : IrOperatorImpl("UPLUS") @@ -88,3 +90,12 @@ interface IrOperator { } } + +fun IrOperator.isAssignmentOperatorWithResult() = + when (this) { + IrOperator.PREFIX_INCR, IrOperator.PREFIX_DECR, + IrOperator.POSTFIX_INCR, IrOperator.POSTFIX_DECR -> + true + else -> + false + } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperatorExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperatorExpression.kt index 11c63ac3152..535347ff606 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperatorExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperatorExpression.kt @@ -33,8 +33,6 @@ abstract class IrOperatorCallBase( get() = null set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver") - override val isSafe: Boolean get() = false - override var extensionReceiver: IrExpression? get() = null set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver") diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrThisReference.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrThisReference.kt index db28aefa99a..2ed91ca1463 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrThisReference.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrThisReference.kt @@ -20,8 +20,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType -interface IrThisReference : IrExpression { +interface IrThisReference : IrExpression, IrExpressionWithCopy { val classDescriptor: ClassDescriptor + override fun copy(): IrThisReference } class IrThisReferenceImpl( @@ -32,4 +33,8 @@ class IrThisReferenceImpl( ) : IrTerminalExpressionBase(startOffset, endOffset, type), IrThisReference { override fun accept(visitor: IrElementVisitor, data: D): R = visitor.visitThisReference(this, data) + + override fun copy(): IrThisReference = + IrThisReferenceImpl(startOffset, endOffset, type, classDescriptor) + } \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt index 848680604f3..40f2dbf1cde 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.types.KotlinType enum class IrTypeOperator { CAST, IMPLICIT_CAST, + IMPLICIT_NOTNULL, SAFE_CAST, INSTANCEOF, NOT_INSTANCEOF; diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVariableAccessExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVariableAccessExpression.kt index 53626413655..4051f643a53 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVariableAccessExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVariableAccessExpression.kt @@ -26,11 +26,12 @@ interface IrVariableAccessExpression : IrDeclarationReference { val operator: IrOperator? } -interface IrGetVariable : IrVariableAccessExpression +interface IrGetVariable : IrVariableAccessExpression, IrExpressionWithCopy { + override fun copy(): IrGetVariable +} interface IrSetVariable : IrVariableAccessExpression { - val value: IrExpression - + var value: IrExpression } class IrGetVariableImpl( @@ -41,6 +42,9 @@ class IrGetVariableImpl( ) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, descriptor.type, descriptor), IrGetVariable { override fun accept(visitor: IrElementVisitor, data: D): R = visitor.visitGetVariable(this, data) + + override fun copy(): IrGetVariable = + IrGetVariableImpl(startOffset, endOffset, descriptor, operator) } class IrSetVariableImpl( diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhen.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhen.kt index 839e3c139e1..38a58b37b08 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhen.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhen.kt @@ -31,6 +31,8 @@ interface IrWhen : IrExpression { var elseBranch: IrExpression? } +val IrWhen.branchIndices: IntRange get() = 0 ..branchesCount - 1 + class IrWhenImpl( startOffset: Int, endOffset: Int, @@ -176,28 +178,4 @@ class IrIfThenElseImpl( thenBranch.accept(visitor, data) elseBranch?.accept(visitor, data) } - - companion object { - // a || b == if (a) true else b - fun oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen = - IrIfThenElseImpl(startOffset, endOffset, b.type!!, - a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type!!), b, - operator) - - fun oror(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen = - oror(b.startOffset, b.endOffset, a, b, operator) - - fun whenComma(a: IrExpression, b: IrExpression): IrWhen = - oror(a, b, IrOperator.WHEN_COMMA) - - // a && b == if (a) b else false - fun andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen = - IrIfThenElseImpl(startOffset, endOffset, b.type!!, - a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type!!), - operator) - - fun andand(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen = - andand(b.startOffset, b.endOffset, a, b, operator) - - } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt index fa0606d5c0f..473873b6949 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt @@ -51,7 +51,7 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitPropertySetter(declaration: IrPropertySetter, data: Nothing?): String = "IrPropertySetter ${declaration.descriptor.render()} property=${declaration.property?.name()}" - override fun visitLocalVariable(declaration: IrVariable, data: Nothing?): String = + override fun visitVariable(declaration: IrVariable, data: Nothing?): String = "VAR ${declaration.descriptor.render()}" override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): String = @@ -76,7 +76,7 @@ class RenderIrElementVisitor : IrElementVisitor { "THIS ${expression.classDescriptor.render()} type=${expression.renderType()}" override fun visitCall(expression: IrCall, data: Nothing?): String = - "CALL ${if (expression.isSafe) "?." else "."}${expression.descriptor.name} " + + "CALL .${expression.descriptor.name} " + "type=${expression.renderType()} operator=${expression.operator}" override fun visitGetVariable(expression: IrGetVariable, data: Nothing?): String = diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt index 397e66fd19f..0c8f858b9f4 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt @@ -33,7 +33,7 @@ interface IrElementVisitor { fun visitProperty(declaration: IrProperty, data: D): R = visitDeclaration(declaration, data) fun visitSimpleProperty(declaration: IrSimpleProperty, data: D): R = visitProperty(declaration, data) fun visitDelegatedProperty(declaration: IrDelegatedProperty, data: D): R = visitProperty(declaration, data) - fun visitLocalVariable(declaration: IrVariable, data: D) = visitDeclaration(declaration, data) + fun visitVariable(declaration: IrVariable, data: D) = visitDeclaration(declaration, data) fun visitBody(body: IrBody, data: D): R = visitElement(body, data) fun visitExpressionBody(body: IrExpressionBody, data: D): R = visitBody(body, data) diff --git a/compiler/testData/ir/irText/arrayAssignment.txt b/compiler/testData/ir/irText/arrayAssignment.txt index 68bcac7c335..19a2b1b507d 100644 --- a/compiler/testData/ir/irText/arrayAssignment.txt +++ b/compiler/testData/ir/irText/arrayAssignment.txt @@ -4,11 +4,13 @@ IrFile /arrayAssignment.kt BLOCK type= hasResult=false operator=null VAR val x: kotlin.IntArray CALL .intArrayOf type=kotlin.IntArray operator=null - elements: DUMMY vararg type=kotlin.Int - CALL .set type=kotlin.Unit operator=EQ - $this: GET_VAR x type=kotlin.IntArray operator=null - index: CONST Int type=kotlin.Int value='1' - value: CONST Int type=kotlin.Int value='0' + elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray + DUMMY vararg type=kotlin.Int + BLOCK type=kotlin.Unit hasResult=false operator=EQ + CALL .set type=kotlin.Unit operator=EQ + $this: GET_VAR x type=kotlin.IntArray operator=null + index: CONST Int type=kotlin.Int value='1' + value: CONST Int type=kotlin.Int value='0' IrFunction public fun foo(): kotlin.Int IrExpressionBody BLOCK type= hasResult=false operator=null @@ -17,8 +19,14 @@ IrFile /arrayAssignment.kt IrFunction public fun test2(): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false operator=null - CALL .set type=kotlin.Unit operator=EQ - $this: CALL .intArrayOf type=kotlin.IntArray operator=null - elements: DUMMY vararg type=kotlin.Int - index: CALL .foo type=kotlin.Int operator=null - value: CONST Int type=kotlin.Int value='1' + BLOCK type=kotlin.Unit hasResult=false operator=EQ + VAR val tmp0_array: kotlin.IntArray + CALL .intArrayOf type=kotlin.IntArray operator=null + elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray + DUMMY vararg type=kotlin.Int + VAR val tmp1_index0: kotlin.Int + CALL .foo type=kotlin.Int operator=null + CALL .set type=kotlin.Unit operator=EQ + $this: GET_VAR tmp0_array type=kotlin.IntArray operator=null + index: GET_VAR tmp1_index0 type=kotlin.Int operator=null + value: CONST Int type=kotlin.Int value='1' diff --git a/compiler/testData/ir/irText/arrayAugmentedAssignment1.kt b/compiler/testData/ir/irText/arrayAugmentedAssignment1.kt index 2fabccb3a17..503bc5554d6 100644 --- a/compiler/testData/ir/irText/arrayAugmentedAssignment1.kt +++ b/compiler/testData/ir/irText/arrayAugmentedAssignment1.kt @@ -1,9 +1,17 @@ fun foo(): IntArray = intArrayOf(1, 2, 3) fun bar() = 42 +class C(val x: IntArray) + fun testVariable() { var x = foo() x[0] += 1 - foo()[0] *= 2 - foo()[bar()] -= 1 } + +fun testCall() { + foo()[bar()] *= 2 +} + +fun testMember(c: C) { + c.x[0]++ +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt b/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt index 15a9d30ceec..dc502a71353 100644 --- a/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt +++ b/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt @@ -4,12 +4,14 @@ IrFile /arrayAugmentedAssignment1.kt BLOCK type= hasResult=false operator=null RETURN type= CALL .intArrayOf type=kotlin.IntArray operator=null - elements: DUMMY vararg type=kotlin.Int + elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray + DUMMY vararg type=kotlin.Int IrFunction public fun bar(): kotlin.Int IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= CONST Int type=kotlin.Int value='42' + DUMMY C IrFunction public fun testVariable(): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false operator=null @@ -24,27 +26,36 @@ IrFile /arrayAugmentedAssignment1.kt $this: GET_VAR x type=kotlin.IntArray operator=null index: CONST Int type=kotlin.Int value='0' other: CONST Int type=kotlin.Int value='1' + IrFunction public fun testCall(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false operator=null BLOCK type=kotlin.Unit hasResult=false operator=MULTEQ VAR val tmp0_array: kotlin.IntArray CALL .foo type=kotlin.IntArray operator=null + VAR val tmp1_index0: kotlin.Int + CALL .bar type=kotlin.Int operator=null CALL .set type=kotlin.Unit operator=MULTEQ $this: GET_VAR tmp0_array type=kotlin.IntArray operator=null - index: CONST Int type=kotlin.Int value='0' + index: GET_VAR tmp1_index0 type=kotlin.Int operator=null value: CALL .times type=kotlin.Int operator=MULTEQ $this: CALL .get type=kotlin.Int operator=MULTEQ $this: GET_VAR tmp0_array type=kotlin.IntArray operator=null - index: CONST Int type=kotlin.Int value='0' + index: GET_VAR tmp1_index0 type=kotlin.Int operator=null other: CONST Int type=kotlin.Int value='2' - BLOCK type=kotlin.Unit hasResult=false operator=MINUSEQ - VAR val tmp1_array: kotlin.IntArray - CALL .foo type=kotlin.IntArray operator=null - VAR val tmp2_index0: kotlin.Int - CALL .bar type=kotlin.Int operator=null - CALL .set type=kotlin.Unit operator=MINUSEQ - $this: GET_VAR tmp1_array type=kotlin.IntArray operator=null - index: GET_VAR tmp2_index0 type=kotlin.Int operator=null - value: CALL .minus type=kotlin.Int operator=MINUSEQ - $this: CALL .get type=kotlin.Int operator=MINUSEQ - $this: GET_VAR tmp1_array type=kotlin.IntArray operator=null - index: GET_VAR tmp2_index0 type=kotlin.Int operator=null - other: CONST Int type=kotlin.Int value='1' + IrFunction public fun testMember(/*0*/ c: C): kotlin.Unit + IrExpressionBody + BLOCK type=kotlin.Int hasResult=true operator=null + BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR + VAR val tmp0_array: kotlin.IntArray + CALL . type=kotlin.IntArray operator=GET_PROPERTY + $this: GET_VAR c type=C operator=null + VAR val tmp1: kotlin.Int + CALL .get type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp0_array type=kotlin.IntArray operator=null + index: CONST Int type=kotlin.Int value='0' + CALL .set type=kotlin.Unit operator=POSTFIX_INCR + $this: GET_VAR tmp0_array type=kotlin.IntArray operator=null + index: CONST Int type=kotlin.Int value='0' + value: CALL .inc type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp1 type=kotlin.Int operator=null + GET_VAR tmp1 type=kotlin.Int operator=null diff --git a/compiler/testData/ir/irText/augmentedAssignment1.txt b/compiler/testData/ir/irText/augmentedAssignment1.txt index 2ac3040a001..ef65438542a 100644 --- a/compiler/testData/ir/irText/augmentedAssignment1.txt +++ b/compiler/testData/ir/irText/augmentedAssignment1.txt @@ -32,21 +32,21 @@ IrFile /augmentedAssignment1.kt BLOCK type=kotlin.Int hasResult=true operator=null CALL . type=kotlin.Unit operator=PLUSEQ : CALL .plus type=kotlin.Int operator=PLUSEQ - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=PLUSEQ other: CONST Int type=kotlin.Int value='1' CALL . type=kotlin.Unit operator=MINUSEQ : CALL .minus type=kotlin.Int operator=MINUSEQ - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=MINUSEQ other: CONST Int type=kotlin.Int value='2' CALL . type=kotlin.Unit operator=MULTEQ : CALL .times type=kotlin.Int operator=MULTEQ - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=MULTEQ other: CONST Int type=kotlin.Int value='3' CALL . type=kotlin.Unit operator=DIVEQ : CALL .div type=kotlin.Int operator=DIVEQ - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=DIVEQ other: CONST Int type=kotlin.Int value='4' CALL . type=kotlin.Unit operator=PERCEQ : CALL .mod type=kotlin.Int operator=PERCEQ - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=PERCEQ other: CONST Int type=kotlin.Int value='5' diff --git a/compiler/testData/ir/irText/augmentedAssignment2.txt b/compiler/testData/ir/irText/augmentedAssignment2.txt index e7803bd395c..4df57de3b08 100644 --- a/compiler/testData/ir/irText/augmentedAssignment2.txt +++ b/compiler/testData/ir/irText/augmentedAssignment2.txt @@ -42,17 +42,17 @@ IrFile /augmentedAssignment2.kt IrExpressionBody BLOCK type= hasResult=false operator=null CALL .plusAssign type=kotlin.Unit operator=PLUSEQ - $receiver: CALL . type=A operator=GET_PROPERTY + $receiver: CALL . type=A operator=PLUSEQ s: CONST String type=kotlin.String value='+=' CALL .minusAssign type=kotlin.Unit operator=MINUSEQ - $receiver: CALL . type=A operator=GET_PROPERTY + $receiver: CALL . type=A operator=MINUSEQ s: CONST String type=kotlin.String value='-=' CALL .timesAssign type=kotlin.Unit operator=MULTEQ - $receiver: CALL . type=A operator=GET_PROPERTY + $receiver: CALL . type=A operator=MULTEQ s: CONST String type=kotlin.String value='*=' CALL .divAssign type=kotlin.Unit operator=DIVEQ - $receiver: CALL . type=A operator=GET_PROPERTY + $receiver: CALL . type=A operator=DIVEQ s: CONST String type=kotlin.String value='/=' CALL .modAssign type=kotlin.Unit operator=PERCEQ - $receiver: CALL . type=A operator=GET_PROPERTY + $receiver: CALL . type=A operator=PERCEQ s: CONST String type=kotlin.String value='%=' diff --git a/compiler/testData/ir/irText/callWithReorderedArguments.txt b/compiler/testData/ir/irText/callWithReorderedArguments.txt index c7cad7315f2..27917a8eb31 100644 --- a/compiler/testData/ir/irText/callWithReorderedArguments.txt +++ b/compiler/testData/ir/irText/callWithReorderedArguments.txt @@ -28,7 +28,7 @@ IrFile /callWithReorderedArguments.kt CALL .foo type=kotlin.Unit operator=null a: CALL .noReorder1 type=kotlin.Int operator=null b: CALL .noReorder2 type=kotlin.Int operator=null - BLOCK type=kotlin.Unit hasResult=false operator=SYNTHETIC_BLOCK + BLOCK type=kotlin.Unit hasResult=true operator=SYNTHETIC_BLOCK VAR val tmp0_b: kotlin.Int CALL .reordered1 type=kotlin.Int operator=null VAR val tmp1_a: kotlin.Int @@ -36,7 +36,7 @@ IrFile /callWithReorderedArguments.kt CALL .foo type=kotlin.Unit operator=null a: GET_VAR tmp1_a type=kotlin.Int operator=null b: GET_VAR tmp0_b type=kotlin.Int operator=null - BLOCK type=kotlin.Unit hasResult=false operator=SYNTHETIC_BLOCK + BLOCK type=kotlin.Unit hasResult=true operator=SYNTHETIC_BLOCK VAR val tmp2_a: kotlin.Int CALL .reordered2 type=kotlin.Int operator=null CALL .foo type=kotlin.Unit operator=null diff --git a/compiler/testData/ir/irText/destructuring1.txt b/compiler/testData/ir/irText/destructuring1.txt index c6c51131db5..175eae47c69 100644 --- a/compiler/testData/ir/irText/destructuring1.txt +++ b/compiler/testData/ir/irText/destructuring1.txt @@ -2,13 +2,13 @@ IrFunction public fun B.test(): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false operator=null BLOCK type= hasResult=false operator=SYNTHETIC_BLOCK - VAR val tmp0: A + VAR val tmp0_container: A GET_OBJECT A type=A VAR val x: kotlin.Int CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1) $this: $RECEIVER of: test type=B - $receiver: GET_VAR tmp0 type=A operator=null + $receiver: GET_VAR tmp0_container type=A operator=null VAR val y: kotlin.Int CALL .component2 type=kotlin.Int operator=COMPONENT_N(index=2) $this: $RECEIVER of: test type=B - $receiver: GET_VAR tmp0 type=A operator=null + $receiver: GET_VAR tmp0_container type=A operator=null diff --git a/compiler/testData/ir/irText/dotQualified.txt b/compiler/testData/ir/irText/dotQualified.txt index 0b3fc2c1904..015ebc9fef6 100644 --- a/compiler/testData/ir/irText/dotQualified.txt +++ b/compiler/testData/ir/irText/dotQualified.txt @@ -9,6 +9,13 @@ IrFile /dotQualified.kt IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= - CALL ?. type=kotlin.Int? operator=GET_PROPERTY - $this: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String + BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: kotlin.String? GET_VAR s type=kotlin.String? operator=null + WHEN type=kotlin.Int? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null diff --git a/compiler/testData/ir/irText/elvis.txt b/compiler/testData/ir/irText/elvis.txt index e6a81952180..36929a74b02 100644 --- a/compiler/testData/ir/irText/elvis.txt +++ b/compiler/testData/ir/irText/elvis.txt @@ -11,22 +11,24 @@ IrFile /elvis.kt IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= - WHEN type=kotlin.Any operator=ELVIS - if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS - arg0: GET_VAR a type=kotlin.Any? operator=null - arg1: CONST Null type=kotlin.Nothing? value='null' - then: GET_VAR b type=kotlin.Any operator=null - else: GET_VAR a type=kotlin.Any? operator=null + BLOCK type=kotlin.Any hasResult=true operator=ELVIS + WHEN type=kotlin.Any operator=null + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR a type=kotlin.Any? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: GET_VAR b type=kotlin.Any operator=null + else: GET_VAR a type=kotlin.Any? operator=null IrFunction public fun test2(/*0*/ a: kotlin.String?, /*1*/ b: kotlin.Any): kotlin.Any IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= - WHEN type=kotlin.Any operator=ELVIS - if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS - arg0: GET_VAR a type=kotlin.String? operator=null - arg1: CONST Null type=kotlin.Nothing? value='null' - then: GET_VAR b type=kotlin.Any operator=null - else: GET_VAR a type=kotlin.String? operator=null + BLOCK type=kotlin.Any hasResult=true operator=ELVIS + WHEN type=kotlin.Any operator=null + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR a type=kotlin.String? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: GET_VAR b type=kotlin.Any operator=null + else: GET_VAR a type=kotlin.String? operator=null IrFunction public fun test3(/*0*/ a: kotlin.Any?, /*1*/ b: kotlin.Any?): kotlin.String IrExpressionBody BLOCK type= hasResult=false operator=null @@ -42,39 +44,37 @@ IrFile /elvis.kt CONST String type=kotlin.String value='' RETURN type= BLOCK type=kotlin.String hasResult=true operator=ELVIS - VAR val tmp0: kotlin.String? - TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String? - GET_VAR a type=kotlin.Any? operator=null - WHEN type=kotlin.String operator=ELVIS - if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS - arg0: GET_VAR tmp0 type=kotlin.String? operator=null + WHEN type=kotlin.String operator=null + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR a type=kotlin.Any? operator=null arg1: CONST Null type=kotlin.Nothing? value='null' then: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String GET_VAR b type=kotlin.Any? operator=null - else: GET_VAR tmp0 type=kotlin.String? operator=null + else: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String + GET_VAR a type=kotlin.Any? operator=null IrFunction public fun test4(/*0*/ x: kotlin.Any): kotlin.Any IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= BLOCK type=kotlin.Any hasResult=true operator=ELVIS - VAR val tmp0: kotlin.Any? + VAR val tmp0_elvis_lhs: kotlin.Any? CALL . type=kotlin.Any? operator=GET_PROPERTY - WHEN type=kotlin.Any operator=ELVIS - if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS - arg0: GET_VAR tmp0 type=kotlin.Any? operator=null + WHEN type=kotlin.Any operator=null + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null arg1: CONST Null type=kotlin.Nothing? value='null' then: GET_VAR x type=kotlin.Any operator=null - else: GET_VAR tmp0 type=kotlin.Any? operator=null + else: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null IrFunction public fun test5(/*0*/ x: kotlin.Any): kotlin.Any IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= BLOCK type=kotlin.Any hasResult=true operator=ELVIS - VAR val tmp0: kotlin.Any? + VAR val tmp0_elvis_lhs: kotlin.Any? CALL .foo type=kotlin.Any? operator=null - WHEN type=kotlin.Any operator=ELVIS - if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS - arg0: GET_VAR tmp0 type=kotlin.Any? operator=null + WHEN type=kotlin.Any operator=null + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null arg1: CONST Null type=kotlin.Nothing? value='null' then: GET_VAR x type=kotlin.Any operator=null - else: GET_VAR tmp0 type=kotlin.Any? operator=null + else: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null diff --git a/compiler/testData/ir/irText/implicitCastOnPlatformType.txt b/compiler/testData/ir/irText/implicitCastOnPlatformType.txt index 281b8aec90e..f67d1dcfadd 100644 --- a/compiler/testData/ir/irText/implicitCastOnPlatformType.txt +++ b/compiler/testData/ir/irText/implicitCastOnPlatformType.txt @@ -3,13 +3,6 @@ IrFile /implicitCastOnPlatformType.kt IrExpressionBody BLOCK type= hasResult=false operator=null RETURN type= - BLOCK type=kotlin.String hasResult=true operator=IMPLICIT_NOTNULL - VAR val tmp0: kotlin.String! - CALL .getProperty type=kotlin.String! operator=null - p0: CONST String type=kotlin.String value='test' - WHEN type=kotlin.String operator=IMPLICIT_NOTNULL - if: CALL .EQEQ type=kotlin.Boolean operator=IMPLICIT_NOTNULL - arg0: GET_VAR tmp0 type=kotlin.String! operator=null - arg1: CONST Null type=kotlin.Nothing? value='null' - then: CALL .THROW_NPE type=kotlin.Nothing operator=IMPLICIT_NOTNULL - else: GET_VAR tmp0 type=kotlin.String! operator=null + TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=kotlin.String + CALL .getProperty type=kotlin.String! operator=null + p0: CONST String type=kotlin.String value='test' diff --git a/compiler/testData/ir/irText/incrementDecrement.txt b/compiler/testData/ir/irText/incrementDecrement.txt index c5bc4df73b4..05d70b579b9 100644 --- a/compiler/testData/ir/irText/incrementDecrement.txt +++ b/compiler/testData/ir/irText/incrementDecrement.txt @@ -5,7 +5,8 @@ IrFile /incrementDecrement.kt IrProperty public val arr: kotlin.IntArray getter=null setter=null IrExpressionBody CALL .intArrayOf type=kotlin.IntArray operator=null - elements: DUMMY vararg type=kotlin.Int + elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray + DUMMY vararg type=kotlin.Int IrFunction public fun testVarPrefix(): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false operator=null @@ -38,7 +39,7 @@ IrFile /incrementDecrement.kt GET_VAR x type=kotlin.Int operator=POSTFIX_INCR SET_VAR x type= operator=POSTFIX_INCR CALL .inc type=kotlin.Int operator=POSTFIX_INCR - $this: GET_VAR tmp0 type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp0 type=kotlin.Int operator=null GET_VAR tmp0 type=kotlin.Int operator=null VAR val x2: kotlin.Int BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_DECR @@ -46,7 +47,7 @@ IrFile /incrementDecrement.kt GET_VAR x type=kotlin.Int operator=POSTFIX_DECR SET_VAR x type= operator=POSTFIX_DECR CALL .dec type=kotlin.Int operator=POSTFIX_DECR - $this: GET_VAR tmp1 type=kotlin.Int operator=POSTFIX_DECR + $this: GET_VAR tmp1 type=kotlin.Int operator=null GET_VAR tmp1 type=kotlin.Int operator=null IrFunction public fun testPropPrefix(): kotlin.Unit IrExpressionBody @@ -55,7 +56,7 @@ IrFile /incrementDecrement.kt BLOCK type=kotlin.Int hasResult=true operator=PREFIX_INCR VAR val tmp0: kotlin.Int CALL .inc type=kotlin.Int operator=PREFIX_INCR - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=PREFIX_INCR CALL . type=kotlin.Unit operator=PREFIX_INCR : GET_VAR tmp0 type=kotlin.Int operator=null GET_VAR tmp0 type=kotlin.Int operator=null @@ -63,7 +64,7 @@ IrFile /incrementDecrement.kt BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR VAR val tmp1: kotlin.Int CALL .dec type=kotlin.Int operator=PREFIX_DECR - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=PREFIX_DECR CALL . type=kotlin.Unit operator=PREFIX_DECR : GET_VAR tmp1 type=kotlin.Int operator=null GET_VAR tmp1 type=kotlin.Int operator=null @@ -73,16 +74,16 @@ IrFile /incrementDecrement.kt VAR val p1: kotlin.Int BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR VAR val tmp0: kotlin.Int - CALL . type=kotlin.Int operator=GET_PROPERTY + CALL . type=kotlin.Int operator=POSTFIX_INCR CALL . type=kotlin.Unit operator=POSTFIX_INCR : CALL .inc type=kotlin.Int operator=POSTFIX_INCR - $this: GET_VAR tmp0 type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp0 type=kotlin.Int operator=null GET_VAR tmp0 type=kotlin.Int operator=null VAR val p2: kotlin.Int BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR VAR val tmp1: kotlin.Int CALL .dec type=kotlin.Int operator=PREFIX_DECR - $this: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: CALL . type=kotlin.Int operator=PREFIX_DECR CALL . type=kotlin.Unit operator=PREFIX_DECR : GET_VAR tmp1 type=kotlin.Int operator=null GET_VAR tmp1 type=kotlin.Int operator=null diff --git a/compiler/testData/ir/irText/safeCallWithIncrementDecrement.kt b/compiler/testData/ir/irText/safeCallWithIncrementDecrement.kt new file mode 100644 index 00000000000..3703fa446ad --- /dev/null +++ b/compiler/testData/ir/irText/safeCallWithIncrementDecrement.kt @@ -0,0 +1,20 @@ +package test + +class C + +var C?.p: Int + get() = 42 + set(value) {} + +operator fun Int?.inc(): Int? = this?.inc() + +operator fun Int?.get(index: Int): Int = 42 +operator fun Int?.set(index: Int, value: Int) {} + +fun testProperty(nc: C?) { + nc?.p++ +} + +fun testArrayAccess(nc: C?) { + nc?.p[0]++ +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/safeCallWithIncrementDecrement.txt b/compiler/testData/ir/irText/safeCallWithIncrementDecrement.txt new file mode 100644 index 00000000000..8937ec983d0 --- /dev/null +++ b/compiler/testData/ir/irText/safeCallWithIncrementDecrement.txt @@ -0,0 +1,53 @@ +IrFile /safeCallWithIncrementDecrement.kt + DUMMY C + IrProperty public var test.C?.p: kotlin.Int getter= setter= + IrPropertyGetter public fun test.C?.(): kotlin.Int property=p + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + CONST Int type=kotlin.Int value='42' + IrPropertySetter public fun test.C?.(/*0*/ value: kotlin.Int): kotlin.Unit property=p + IrExpressionBody + BLOCK type= hasResult=false operator=null + IrFunction public operator fun kotlin.Int?.inc(): kotlin.Int? + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: kotlin.Int? + $RECEIVER of: inc type=kotlin.Int? + WHEN type=kotlin.Int? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=kotlin.Int? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL .inc type=kotlin.Int operator=null + $this: GET_VAR tmp0_safe_receiver type=kotlin.Int? operator=null + IrFunction public operator fun kotlin.Int?.get(/*0*/ index: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + CONST Int type=kotlin.Int value='42' + IrFunction public operator fun kotlin.Int?.set(/*0*/ index: kotlin.Int, /*1*/ value: kotlin.Int): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false operator=null + IrFunction public fun testProperty(/*0*/ nc: test.C?): kotlin.Unit + IrExpressionBody + BLOCK type=kotlin.Int? hasResult=true operator=null + BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: test.C? + GET_VAR nc type=test.C? operator=null + WHEN type=kotlin.Int? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=test.C? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR + VAR val tmp1: kotlin.Int + CALL . type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp0_safe_receiver type=test.C? operator=null + CALL . type=kotlin.Unit operator=POSTFIX_INCR + $this: GET_VAR tmp0_safe_receiver type=test.C? operator=null + value: CALL .inc type=kotlin.Int? operator=POSTFIX_INCR + $receiver: GET_VAR tmp1 type=kotlin.Int operator=null + GET_VAR tmp1 type=kotlin.Int operator=null diff --git a/compiler/testData/ir/irText/safeCalls.kt b/compiler/testData/ir/irText/safeCalls.kt new file mode 100644 index 00000000000..ddebcb0c231 --- /dev/null +++ b/compiler/testData/ir/irText/safeCalls.kt @@ -0,0 +1,15 @@ +class Ref(var value: Int) + +interface IHost { + fun String.extLength() = length +} + +fun test1(x: String?) = x?.length +fun test2(x: String?) = x?.hashCode() +fun test3(x: String?, y: Any?) = x?.equals(y) + +fun test4(x: Ref?) { + x?.value = 0 +} + +fun IHost.test5(s: String?) = s?.extLength() \ No newline at end of file diff --git a/compiler/testData/ir/irText/safeCalls.txt b/compiler/testData/ir/irText/safeCalls.txt new file mode 100644 index 00000000000..749ffc66d98 --- /dev/null +++ b/compiler/testData/ir/irText/safeCalls.txt @@ -0,0 +1,75 @@ +IrFile /safeCalls.kt + DUMMY Ref + DUMMY IHost + IrFunction public fun test1(/*0*/ x: kotlin.String?): kotlin.Int? + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: kotlin.String? + GET_VAR x type=kotlin.String? operator=null + WHEN type=kotlin.Int? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL . type=kotlin.Int operator=GET_PROPERTY + $this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + IrFunction public fun test2(/*0*/ x: kotlin.String?): kotlin.Int? + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: kotlin.String? + GET_VAR x type=kotlin.String? operator=null + WHEN type=kotlin.Int? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL .hashCode type=kotlin.Int operator=null + $this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + IrFunction public fun test3(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.Any?): kotlin.Boolean? + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + BLOCK type=kotlin.Boolean? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: kotlin.String? + GET_VAR x type=kotlin.String? operator=null + WHEN type=kotlin.Boolean? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL .equals type=kotlin.Boolean operator=null + $this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + other: GET_VAR y type=kotlin.Any? operator=null + IrFunction public fun test4(/*0*/ x: Ref?): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false operator=null + BLOCK type=kotlin.Unit? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: Ref? + GET_VAR x type=Ref? operator=null + WHEN type=kotlin.Unit? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=Ref? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL . type=kotlin.Unit operator=EQ + $this: GET_VAR tmp0_safe_receiver type=Ref? operator=null + : CONST Int type=kotlin.Int value='0' + IrFunction public fun IHost.test5(/*0*/ s: kotlin.String?): kotlin.Int? + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL + VAR val tmp0_safe_receiver: kotlin.String? + GET_VAR s type=kotlin.String? operator=null + WHEN type=kotlin.Int? operator=SAFE_CALL + if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ + arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null + arg1: CONST Null type=kotlin.Nothing? value='null' + then: CONST Null type=kotlin.Nothing? value='null' + else: CALL .extLength type=kotlin.Int operator=null + $this: $RECEIVER of: test5 type=IHost + $receiver: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null diff --git a/compiler/testData/ir/irText/smartCastsWithDestructuring.txt b/compiler/testData/ir/irText/smartCastsWithDestructuring.txt index 06393ca2a95..1c7e5260c19 100644 --- a/compiler/testData/ir/irText/smartCastsWithDestructuring.txt +++ b/compiler/testData/ir/irText/smartCastsWithDestructuring.txt @@ -19,12 +19,10 @@ IrFile /smartCastsWithDestructuring.kt GET_VAR x type=I1 operator=null then: RETURN type= BLOCK type= hasResult=false operator=SYNTHETIC_BLOCK - VAR val tmp0: I1 - GET_VAR x type=I1 operator=null VAR val c1: kotlin.Int CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1) - $receiver: GET_VAR tmp0 type=I1 operator=null + $receiver: GET_VAR x type=I1 operator=null VAR val c2: kotlin.String CALL .component2 type=kotlin.String operator=COMPONENT_N(index=2) $receiver: TYPE_OP operator=IMPLICIT_CAST typeOperand=I2 - GET_VAR tmp0 type=I1 operator=null + GET_VAR x type=I1 operator=null diff --git a/compiler/testData/ir/irText/when.txt b/compiler/testData/ir/irText/when.txt index 8a95603868e..4f8654c0a11 100644 --- a/compiler/testData/ir/irText/when.txt +++ b/compiler/testData/ir/irText/when.txt @@ -21,8 +21,7 @@ IrFile /when.kt then: CONST String type=kotlin.String value='String' if: CALL .contains type=kotlin.Boolean operator=IN $receiver: CALL .setOf type=kotlin.collections.Set operator=null - element: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Any - GET_VAR tmp0_subject type=kotlin.Any? operator=null + element: GET_VAR tmp0_subject type=kotlin.Any? operator=null then: CONST String type=kotlin.String value='nothingness?' else: CONST String type=kotlin.String value='something' IrFunction public fun test(/*0*/ x: kotlin.Any?): kotlin.String @@ -43,8 +42,7 @@ IrFile /when.kt then: CONST String type=kotlin.String value='String' if: CALL .contains type=kotlin.Boolean operator=IN $receiver: CALL .setOf type=kotlin.collections.Set operator=null - element: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Any - GET_VAR x type=kotlin.Any? operator=null + element: GET_VAR x type=kotlin.Any? operator=null then: CONST String type=kotlin.String value='nothingness?' else: CONST String type=kotlin.String value='something' IrFunction public fun testComma(/*0*/ x: kotlin.Int): kotlin.String diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index a887f25d57b..93c715a3e60 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -179,6 +179,18 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("safeCallWithIncrementDecrement.kt") + public void testSafeCallWithIncrementDecrement() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/safeCallWithIncrementDecrement.kt"); + doTest(fileName); + } + + @TestMetadata("safeCalls.kt") + public void testSafeCalls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/safeCalls.kt"); + doTest(fileName); + } + @TestMetadata("simpleOperators.kt") public void testSimpleOperators() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/simpleOperators.kt");