diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentLHS.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentLHS.kt new file mode 100644 index 00000000000..9abccf57f48 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentLHS.kt @@ -0,0 +1,61 @@ +/* + * 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.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrOperator +import org.jetbrains.kotlin.ir.expressions.IrSetPropertyExpressionImpl +import org.jetbrains.kotlin.ir.expressions.IrSetVariableExpressionImpl +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.types.KotlinType + +interface AssignmentLHS { + val type: KotlinType? + + fun generateAssignment(ktOperation: KtBinaryExpression, irOperator: IrOperator, irRight: IrExpression): IrExpression +} + +class VariableLHS( + val descriptor: VariableDescriptor +) : AssignmentLHS { + override val type: KotlinType? = descriptor.type + + override fun generateAssignment(ktOperation: KtBinaryExpression, irOperator: IrOperator, irRight: IrExpression) = + IrSetVariableExpressionImpl(ktOperation.startOffset, ktOperation.endOffset, descriptor, irRight, irOperator) +} + +class PropertyLHS( + val descriptor: PropertyDescriptor, + val dispatchReceiver: IrExpression?, + val extensionReceiver: IrExpression?, + val isSafe: Boolean +) : AssignmentLHS { + override val type: KotlinType? = descriptor.type + + override fun generateAssignment(ktOperation: KtBinaryExpression, irOperator: IrOperator, irRight: IrExpression): IrExpression = + IrSetPropertyExpressionImpl(ktOperation.startOffset, ktOperation.endOffset, isSafe, descriptor, irOperator).apply { + dispatchReceiver = this@PropertyLHS.dispatchReceiver + extensionReceiver = this@PropertyLHS.extensionReceiver + value = irRight + } +} + + diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt index 6c477e7b6ae..663e7639c12 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt @@ -37,12 +37,15 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera temporaries[ktExpression] = temporaryVariableDescriptor } - private fun generateExpressionOrTemporary(ktExpression: KtExpression): IrExpression { + private fun generateExpressionOrTemporary(ktExpression: KtExpression, expectedType: KotlinType?): IrExpression { val temporary = temporaries[ktExpression] return if (temporary != null) - IrGetVariableExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, getType(ktExpression), temporary) + irStatementGenerator.smartCastTo( + IrGetVariableExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, temporary.type, temporary), + expectedType + ) else - irStatementGenerator.generateExpression(ktExpression) + irStatementGenerator.generateExpression(ktExpression, expectedType) } fun generateCall( @@ -50,27 +53,24 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera resolvedCall: ResolvedCall, operator: IrOperator? = null, superQualifier: ClassDescriptor? = null - ) = generateCall(ktExpression, getTypeOrFail(ktExpression), resolvedCall, operator, superQualifier) - - fun generateCall( - ktExpression: KtExpression, - resultType: KotlinType?, - resolvedCall: ResolvedCall, - operator: IrOperator? = null, - superQualifier: ClassDescriptor? = null ): IrExpression { val descriptor = resolvedCall.resultingDescriptor + val returnType = getReturnType(resolvedCall) + return when (descriptor) { is PropertyDescriptor -> IrGetPropertyExpressionImpl( - ktExpression.startOffset, ktExpression.endOffset, resultType, + ktExpression.startOffset, ktExpression.endOffset, + returnType, resolvedCall.call.isSafeCall(), descriptor ).apply { - dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver) - extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver) + dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver, + descriptor.dispatchReceiverParameter?.type) + extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver, + descriptor.extensionReceiverParameter?.type) } is FunctionDescriptor -> - generateFunctionCall(descriptor, ktExpression, resultType, operator, resolvedCall, superQualifier) + generateFunctionCall(descriptor, ktExpression, returnType, operator, resolvedCall, superQualifier) else -> TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}") } @@ -102,8 +102,10 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera ktExpression.startOffset, ktExpression.endOffset, resultType, descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier ) - irCall.dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver) - irCall.extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver) + irCall.dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver, + descriptor.dispatchReceiverParameter?.type) + irCall.extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver, + descriptor.extensionReceiverParameter?.type) return if (resolvedCall.requiresArgumentReordering()) { generateCallWithArgumentReordering(irCall, ktExpression, resolvedCall, resultType) @@ -113,7 +115,11 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera val valueArguments = resolvedCall.valueArgumentsByIndex for (index in valueArguments!!.indices) { val valueArgument = valueArguments[index] - val irArgument = generateValueArgument(valueArgument) ?: continue + val valueParameter = descriptor.valueParameters[index] + val irArgument = generateValueArgument( + valueArgument, + valueParameter.varargElementType ?: valueParameter.type + ) ?: continue irCall.putArgument(index, irArgument) } } @@ -136,7 +142,7 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera val temporaryVariablesForValueArguments = HashMap>() for (valueArgument in valueArgumentsInEvaluationOrder) { - val irArgument = generateValueArgument(valueArgument) ?: continue + val irArgument = generateValueArgument(valueArgument, null) ?: continue val irTemporary = irStatementGenerator.declarationFactory.createTemporaryVariable(irArgument) irBlock.addStatement(irTemporary) @@ -145,9 +151,10 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { val (temporaryDescriptor, irArgument) = temporaryVariablesForValueArguments[valueArgument]!! - val irGetTemporary = IrGetVariableExpressionImpl(irArgument.startOffset, irArgument.endOffset, - irArgument.type, temporaryDescriptor) - irCall.putArgument(index, irGetTemporary) + val valueParameter = resolvedCall.resultingDescriptor.valueParameters[index] + val irGetTemporary = IrGetVariableExpressionImpl(irArgument.startOffset, irArgument.endOffset, irArgument.type, + temporaryDescriptor) + irCall.putArgument(index, irStatementGenerator.smartCastTo(irGetTemporary, valueParameter.type)) } irBlock.addStatement(irCall) @@ -156,7 +163,7 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera } // TODO smart casts on implicit receivers - fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? = + fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?, expectedType: KotlinType?): IrExpression? = when (receiver) { is ImplicitClassReceiver -> IrThisExpressionImpl(ktExpression.startOffset, ktExpression.startOffset, receiver.type, receiver.classDescriptor) @@ -165,7 +172,7 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera IrThisExpressionImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor) } ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver") is ExpressionReceiver -> - generateExpressionOrTemporary(receiver.expression) + generateExpressionOrTemporary(receiver.expression, expectedType) is ClassValueReceiver -> IrGetObjectValueExpressionImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type, receiver.classQualifier.descriptor) @@ -178,16 +185,15 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera TODO("Receiver: ${receiver.javaClass.simpleName}") } - fun generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? { - when (valueArgument) { - is DefaultValueArgument -> - return null - is ExpressionValueArgument -> - return generateExpressionOrTemporary(valueArgument.valueArgument!!.getArgumentExpression()!!) - is VarargValueArgument -> - TODO("vararg") - else -> - TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") - } - } + fun generateValueArgument(valueArgument: ResolvedValueArgument, expectedType: KotlinType?) = + when (valueArgument) { + is DefaultValueArgument -> + null + is ExpressionValueArgument -> + generateExpressionOrTemporary(valueArgument.valueArgument!!.getArgumentExpression()!!, expectedType) + is VarargValueArgument -> + TODO("vararg") + else -> + TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") + } } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt index faa7f180678..dbb93f78e08 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.psi2ir.generators -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor -import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDummyDeclaration import org.jetbrains.kotlin.ir.expressions.* @@ -51,13 +48,17 @@ abstract class IrDeclarationGeneratorBase( } private fun generateClassOrObjectDeclaration(ktDeclaration: KtClassOrObject) { - IrDummyDeclaration(ktDeclaration.startOffset, ktDeclaration.endOffset, getOrFail(BindingContext.CLASS, ktDeclaration)) - .register() + IrDummyDeclaration( + ktDeclaration.startOffset, ktDeclaration.endOffset, + getOrFail(BindingContext.CLASS, ktDeclaration) + ).register() } private fun generateTypeAliasDeclaration(ktDeclaration: KtTypeAlias) { - IrDummyDeclaration(ktDeclaration.startOffset, ktDeclaration.endOffset, getOrFail(BindingContext.TYPE_ALIAS, ktDeclaration)) - .register() + IrDummyDeclaration( + ktDeclaration.startOffset, ktDeclaration.endOffset, + getOrFail(BindingContext.TYPE_ALIAS, ktDeclaration) + ).register() } fun generateFunctionDeclaration(ktNamedFunction: KtNamedFunction) { @@ -92,16 +93,17 @@ abstract class IrDeclarationGeneratorBase( } private fun generateExpressionWithinContext(ktExpression: KtExpression, scopeOwner: DeclarationDescriptor): IrExpression = - IrStatementGenerator(context, IrLocalDeclarationsFactory(scopeOwner)).generateExpression(ktExpression) + IrStatementGenerator(context, scopeOwner, IrLocalDeclarationsFactory(scopeOwner)) + .generateExpression(ktExpression, getExpectedTypeForLastInferredCall(ktExpression)) - private fun generateFunctionBody(scopeOwner: DeclarationDescriptor, ktBody: KtExpression): IrBody { + private fun generateFunctionBody(scopeOwner: CallableDescriptor, ktBody: KtExpression): IrBody { val irRhs = generateExpressionWithinContext(ktBody, scopeOwner) val irExpressionBody = if (ktBody is KtBlockExpression) irRhs else IrBlockExpressionImpl(ktBody.startOffset, ktBody.endOffset, null, hasResult = false, isDesugared = true).apply { - addStatement(IrReturnExpressionImpl(ktBody.startOffset, ktBody.endOffset, null, irRhs)) + addStatement(IrReturnExpressionImpl(ktBody.startOffset, ktBody.endOffset, scopeOwner, irRhs)) } return IrExpressionBodyImpl(ktBody.startOffset, ktBody.endOffset, irExpressionBody) } 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/IrGenerator.kt index 99c2f25ea9c..6ca73283894 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/IrGenerator.kt @@ -17,23 +17,25 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.ir.expressions.IrDummyExpression +import org.jetbrains.kotlin.psi.KtBlockExpression 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.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice interface IrGenerator { val context: IrGeneratorContext } -fun IrGenerator.getType(key: KtExpression): KotlinType? = - context.bindingContext.getType(key) - -fun IrGenerator.getTypeOrFail(key: KtExpression): KotlinType = - getType(key) ?: TODO("No type for expression: ${key.text}") - fun IrGenerator.get(slice: ReadOnlySlice, key: K): V? = context.bindingContext[slice, key] @@ -43,11 +45,54 @@ fun IrGenerator.getOrFail(slice: ReadOnlySlice, key: K): V = inline fun IrGenerator.getOrFail(slice: ReadOnlySlice, key: K, message: (K) -> String): V = context.bindingContext[slice, key] ?: throw RuntimeException(message(key)) -fun IrGenerator.isUsedAsExpression(ktExpression: KtExpression) = - get(BindingContext.USED_AS_EXPRESSION, ktExpression) ?: false - inline fun IrGenerator.getOrElse(slice: ReadOnlySlice, key: K, otherwise: (K) -> V): V = context.bindingContext[slice, key] ?: otherwise(key) +fun IrGenerator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? = + context.bindingContext.getType(key) + +fun IrGenerator.getExpectedTypeForLastInferredCall(key: KtExpression): KotlinType? = + get(BindingContext.EXPECTED_EXPRESSION_TYPE, key) + +fun IrGenerator.getInferredTypeWithSmarcastsOrFail(key: KtExpression): KotlinType = + getInferredTypeWithSmartcasts(key) ?: TODO("No type for expression: ${key.text}") + +fun IrGenerator.isUsedAsExpression(ktExpression: KtExpression) = + get(BindingContext.USED_AS_EXPRESSION, ktExpression) ?: false + fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall? = - key.getResolvedCall(context.bindingContext) \ No newline at end of file + key.getResolvedCall(context.bindingContext) + +fun IrGenerator.getReturnType(key: KtExpression): KotlinType? { + val resolvedCall = getResolvedCall(key) + if (resolvedCall != null) { + return getReturnType(resolvedCall) + } + + if (key is KtBlockExpression) { + if (!isUsedAsExpression(key)) return null + return getReturnType(key.statements.last()) + } + + throw AssertionError("Unexpected expression: $key") +} + +fun getReturnType(resolvedCall: ResolvedCall<*>): KotlinType { + val descriptor = resolvedCall.resultingDescriptor + 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 + } + else -> + throw AssertionError("Unexpected desciptor in resolved call: $descriptor") + } +} + +fun IrGenerator.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/IrOperatorExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt new file mode 100644 index 00000000000..95490bb905a --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt @@ -0,0 +1,73 @@ +/* + * 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.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrOperator +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall + +class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator { + override val context: IrGeneratorContext get() = irStatementGenerator.context + + fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression { + val ktOperator = expression.operationReference.getReferencedNameElementType() + + return when (ktOperator) { + KtTokens.EQ -> generateAssignment(expression) + else -> createDummyExpression(expression, ktOperator.toString()) + } + } + + private fun generateAssignment(expression: KtBinaryExpression): IrExpression { + val ktLeft = expression.left!! + val ktRight = expression.right!! + val lhs = generateAssignmentLHS(ktLeft) + return lhs.generateAssignment(expression, IrOperator.EQ, irStatementGenerator.generateExpression(ktRight, lhs.type)) + } + + private fun generateAssignmentLHS(ktLeft: KtExpression): AssignmentLHS { + val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS") + val descriptor = resolvedCall.candidateDescriptor + + return when (descriptor) { + is LocalVariableDescriptor -> + if (descriptor.isDelegated) + TODO("Delegated local variable") + else + VariableLHS(descriptor) + is PropertyDescriptor -> + IrCallGenerator(irStatementGenerator).run { + PropertyLHS( + descriptor, + generateReceiver(ktLeft, resolvedCall.dispatchReceiver, + descriptor.dispatchReceiverParameter?.type), + generateReceiver(ktLeft, resolvedCall.extensionReceiver, + descriptor.extensionReceiverParameter?.type), + resolvedCall.call.isSafeCall() + ) + } + else -> + TODO("Other cases of LHS") + } + } + +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt index f64d305403f..118c999a2c4 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.psi2ir.generators -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.assertCast import org.jetbrains.kotlin.ir.expressions.* @@ -28,43 +25,52 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.deparenthesize import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.constants.IntValue import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator +import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils class IrStatementGenerator( override val context: IrGeneratorContext, + val scopeOwner: DeclarationDescriptor, val declarationFactory: IrLocalDeclarationsFactory ) : KtVisitor(), IrGenerator { - fun generateStatement(ktExpression: KtExpression) = ktExpression.generate() - fun generateExpression(ktExpression: KtExpression) = ktExpression.generateExpression() + fun generateExpression(ktExpression: KtExpression, expectedType: KotlinType?): IrExpression = + ktExpression.genExpr(expectedType) - private fun KtElement.generate(): IrStatement = - deparenthesize() - .accept(this@IrStatementGenerator, null) - .applySmartCastIfNeeded(this) - - private fun KtElement.generateExpression(): IrExpression = - generate().assertCast() - - private fun IrStatement.applySmartCastIfNeeded(ktElement: KtElement): IrStatement { - if (this is IrExpression && ktElement is KtExpression) { - val smartCastType = get(BindingContext.SMARTCAST, ktElement) - if (smartCastType != null) { - return IrTypeOperatorExpressionImpl( - ktElement.startOffset, ktElement.endOffset, smartCastType, - IrTypeOperator.SMART_AS, this@applySmartCastIfNeeded, smartCastType - ) - } + private fun KtElement.genStmt(expectedType: KotlinType?): IrStatement { + val irStatement = deparenthesize().accept(this@IrStatementGenerator, null) + if (irStatement is IrExpression) { + return smartCastTo(irStatement, expectedType) } - return this + return irStatement } + private fun KtElement.genExpr(expectedType: KotlinType?): IrExpression = + genStmt(expectedType).assertCast() + + fun smartCastTo(irExpression: IrExpression, expectedType: KotlinType?): IrExpression { + val actualType = irExpression.type + if (expectedType != null && actualType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(actualType, expectedType)) { + return IrTypeOperatorExpressionImpl( + irExpression.startOffset, irExpression.endOffset, expectedType, + IrTypeOperator.SMART_AS, expectedType, + irExpression + ) + } + return irExpression + } + + override fun visitExpression(expression: KtExpression, data: Nothing?): IrStatement = - IrDummyExpression(expression.startOffset, expression.endOffset, getType(expression), expression.javaClass.simpleName) + createDummyExpression(expression, expression.javaClass.simpleName) override fun visitProperty(property: KtProperty, data: Nothing?): IrStatement { if (property.delegateExpression != null) TODO("Local delegated property") @@ -72,7 +78,7 @@ class IrStatementGenerator( val variableDescriptor = getOrFail(BindingContext.VARIABLE, property) val irLocalVariable = declarationFactory.createLocalVariable(property, variableDescriptor) - irLocalVariable.initializer = property.initializer?.generateExpression() + irLocalVariable.initializer = property.initializer?.genExpr(variableDescriptor.type) return irLocalVariable } @@ -80,10 +86,10 @@ class IrStatementGenerator( override fun visitDestructuringDeclaration(multiDeclaration: KtDestructuringDeclaration, data: Nothing?): IrStatement { // TODO use some special form that introduces multiple declarations into surrounding scope? - val irBlock = IrBlockExpressionImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, getType(multiDeclaration), + val irBlock = IrBlockExpressionImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, hasResult = false, isDesugared = true) val ktInitializer = multiDeclaration.initializer!! - val irInitializer = declarationFactory.createTemporaryVariable(ktInitializer.generateExpression()) + val irInitializer = declarationFactory.createTemporaryVariable(ktInitializer.genExpr(null)) irBlock.addStatement(irInitializer) val irCallGenerator = IrCallGenerator(this).apply { putTemporary(ktInitializer, irInitializer.descriptor) } @@ -91,8 +97,7 @@ class IrStatementGenerator( for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) val componentVariable = getOrFail(BindingContext.VARIABLE, ktEntry) - val irComponentCall = irCallGenerator.generateCall(ktEntry, componentVariable.type, componentResolvedCall, - IrOperator.COMPONENT_N.withIndex(index + 1)) + val irComponentCall = irCallGenerator.generateCall(ktEntry, componentResolvedCall, IrOperator.COMPONENT_N.withIndex(index + 1)) val irComponentVar = declarationFactory.createLocalVariable(ktEntry, componentVariable, irComponentCall) irBlock.addStatement(irComponentVar) } @@ -101,20 +106,44 @@ class IrStatementGenerator( } override fun visitBlockExpression(expression: KtBlockExpression, data: Nothing?): IrStatement { - val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), + val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, getReturnType(expression), hasResult = isUsedAsExpression(expression), isDesugared = false) - expression.statements.forEach { irBlock.addStatement(it.generate()) } + expression.statements.forEach { irBlock.addStatement(it.genStmt(null)) } return irBlock } - override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrStatement = - IrReturnExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), - expression.returnedExpression?.generateExpression()) + override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrStatement { + val returnTarget = getReturnExpressionTarget(expression) + return IrReturnExpressionImpl( + expression.startOffset, expression.endOffset, + returnTarget, + expression.returnedExpression?.let { it.genExpr(returnTarget.returnType) } + ) + } + + private fun getReturnExpressionTarget(expression: KtReturnExpression): CallableDescriptor = + if (!ExpressionTypingUtils.isFunctionLiteral(scopeOwner) && !ExpressionTypingUtils.isFunctionExpression(scopeOwner)) { + scopeOwner as? CallableDescriptor ?: throw AssertionError("Return not in a callable: $scopeOwner") + } + else { + val label = expression.getTargetLabel() + if (label != null) { + val labelTarget = getOrFail(BindingContext.LABEL_TARGET, label) + val labelTargetDescriptor = getOrFail(BindingContext.DECLARATION_TO_DESCRIPTOR, labelTarget) + labelTargetDescriptor as CallableDescriptor + } + else if (ExpressionTypingUtils.isFunctionLiteral(scopeOwner)) { + BindingContextUtils.getContainingFunctionSkipFunctionLiterals(scopeOwner, true).first + } + else { + scopeOwner as? CallableDescriptor ?: throw AssertionError("Return not in a callable: $scopeOwner") + } + } 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(getTypeOrFail(expression)) + val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmarcastsOrFail(expression)) val constantType = constantValue.type return when (constantValue) { @@ -128,12 +157,17 @@ class IrStatementGenerator( } override fun visitStringTemplateExpression(expression: KtStringTemplateExpression, data: Nothing?): IrStatement { - if (expression.entries.size == 1 && expression.entries[0] is KtLiteralStringTemplateEntry) { - return expression.entries[0].generate() + if (expression.entries.size == 1) { + val entry0 = expression.entries[0] + if (entry0 is KtLiteralStringTemplateEntry) { + return entry0.genExpr(null) + } } - val irStringTemplate = IrStringConcatenationExpressionImpl(expression.startOffset, expression.endOffset, getType(expression)) - expression.entries.forEach { irStringTemplate.addArgument(it.generateExpression()) } + val irStringTemplate = IrStringConcatenationExpressionImpl(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression)) + expression.entries.forEach { it.expression!!.let { + irStringTemplate.addArgument(TODO()) + } } return irStringTemplate } @@ -152,21 +186,26 @@ class IrStatementGenerator( return when (descriptor) { is ClassDescriptor -> if (DescriptorUtils.isObject(descriptor)) - IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), descriptor) + IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) else if (DescriptorUtils.isEnumEntry(descriptor)) - IrGetEnumValueExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), descriptor) - else - IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), - descriptor.companionObjectDescriptor ?: error("Class value without companion object: $descriptor")) + IrGetEnumValueExpressionImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) + else { + val companionObjectDescriptor = descriptor.companionObjectDescriptor + ?: error("Class value without companion object: $descriptor") + IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, + descriptor.classValueType, + companionObjectDescriptor) + } is PropertyDescriptor -> { IrCallGenerator(this).generateCall(expression, resolvedCall) } is VariableDescriptor -> - IrGetVariableExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), descriptor) + IrGetVariableExpressionImpl(expression.startOffset, expression.endOffset, descriptor.type, descriptor) else -> - IrDummyExpression(expression.startOffset, expression.endOffset, getType(expression), - expression.getReferencedName() + - ": ${descriptor?.name} ${descriptor?.javaClass?.simpleName}") + IrDummyExpression( + expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression), + expression.getReferencedName() + ": ${descriptor?.name} ${descriptor?.javaClass?.simpleName}" + ) } } @@ -190,14 +229,24 @@ class IrStatementGenerator( val referenceTarget = getOrFail(BindingContext.REFERENCE_TARGET, expression.instanceReference) { "No reference target for this" } return when (referenceTarget) { is ClassDescriptor -> - IrThisExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), referenceTarget) - is CallableDescriptor -> - IrGetExtensionReceiverExpressionImpl( - expression.startOffset, expression.endOffset, getType(expression), - referenceTarget.extensionReceiverParameter!! + IrThisExpressionImpl( + expression.startOffset, expression.endOffset, + referenceTarget.defaultType, // TODO substituted type for 'this'? + referenceTarget ) + is CallableDescriptor -> { + val extensionReceiver = referenceTarget.extensionReceiverParameter ?: TODO("No extension receiver: $referenceTarget") + IrGetExtensionReceiverExpressionImpl( + expression.startOffset, expression.endOffset, + extensionReceiver.type, + extensionReceiver + ) + } else -> error("Expected this or receiver: $referenceTarget") } } + + override fun visitBinaryExpression(expression: KtBinaryExpression, data: Nothing?): IrStatement = + IrOperatorExpressionGenerator(this).generateBinaryExpression(expression) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetVariableExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiverExpression.kt similarity index 70% rename from compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetVariableExpression.kt rename to compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiverExpression.kt index ec8f9b10318..20c258b0264 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetVariableExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetExtensionReceiverExpression.kt @@ -17,28 +17,13 @@ package org.jetbrains.kotlin.ir.expressions import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType -interface IrGetVariableExpression : IrDeclarationReference { - override val descriptor: VariableDescriptor -} - interface IrGetExtensionReceiverExpression : IrDeclarationReference { override val descriptor: CallableDescriptor } -class IrGetVariableExpressionImpl( - startOffset: Int, - endOffset: Int, - type: KotlinType?, - descriptor: VariableDescriptor -) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, type, descriptor), IrGetVariableExpression { - override fun accept(visitor: IrElementVisitor, data: D): R = - visitor.visitGetVariable(this, data) -} - class IrGetExtensionReceiverExpressionImpl( startOffset: Int, endOffset: Int, @@ -47,4 +32,4 @@ class IrGetExtensionReceiverExpressionImpl( ) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, type, descriptor), IrGetExtensionReceiverExpression { override fun accept(visitor: IrElementVisitor, data: D): R = visitor.visitGetExtensionReceiver(this, data) -} \ No newline at end of file +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrPropertyAccessExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrPropertyAccessExpression.kt index b8f7a19375a..113a55f6741 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrPropertyAccessExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrPropertyAccessExpression.kt @@ -29,6 +29,7 @@ interface IrGetPropertyExpression : IrPropertyAccessExpression interface IrSetPropertyExpression : IrPropertyAccessExpression { var value: IrExpression + val operator: IrOperator } class IrGetPropertyExpressionImpl( @@ -45,18 +46,18 @@ class IrGetPropertyExpressionImpl( class IrSetPropertyExpressionImpl( startOffset: Int, endOffset: Int, - type: KotlinType?, isSafe: Boolean, - override val descriptor: PropertyDescriptor -) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrSetPropertyExpression { + override val descriptor: PropertyDescriptor, + override val operator: IrOperator = IrOperator.EQ +) : IrMemberAccessExpressionBase(startOffset, endOffset, null, isSafe), IrSetPropertyExpression { constructor( startOffset: Int, endOffset: Int, - type: KotlinType?, isSafe: Boolean, descriptor: PropertyDescriptor, - value: IrExpression - ) : this(startOffset, endOffset, type, isSafe, descriptor) { + value: IrExpression, + operator: IrOperator = IrOperator.EQ + ) : this(startOffset, endOffset, isSafe, descriptor, operator) { this.value = value } @@ -70,15 +71,6 @@ class IrSetPropertyExpressionImpl( value.setTreeLocation(this, ARGUMENT0_SLOT) } - override fun accept(visitor: IrElementVisitor, data: D): R { - return visitor.visitSetProperty(this, data) - } - - override fun acceptChildren(visitor: IrElementVisitor, data: D) { - super.acceptChildren(visitor, data) - value.accept(visitor, data) - } - override fun getChild(slot: Int): IrElement? = when (slot) { ARGUMENT0_SLOT -> value @@ -91,4 +83,13 @@ class IrSetPropertyExpressionImpl( else -> super.replaceChild(slot, newChild) } } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitSetProperty(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + super.acceptChildren(visitor, data) + value.accept(visitor, data) + } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrReturnExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrReturnExpression.kt index 1b5bb16e66d..e9a06c12bd4 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrReturnExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrReturnExpression.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.ir.expressions +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType @@ -23,19 +24,20 @@ import org.jetbrains.kotlin.types.KotlinType interface IrReturnExpression : IrExpression { var value: IrExpression? + val returnTarget: CallableDescriptor } class IrReturnExpressionImpl( startOffset: Int, endOffset: Int, - type: KotlinType? -) : IrExpressionBase(startOffset, endOffset, type), IrReturnExpression { + override val returnTarget: CallableDescriptor +) : IrExpressionBase(startOffset, endOffset, null), IrReturnExpression { constructor( startOffset: Int, endOffset: Int, - type: KotlinType?, + returnTarget: CallableDescriptor, value: IrExpression? - ) : this(startOffset, endOffset, type) { + ) : this(startOffset, endOffset, returnTarget) { this.value = value } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorExpression.kt index d4769816298..f60a3d664ef 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorExpression.kt @@ -46,8 +46,8 @@ class IrTypeOperatorExpressionImpl( endOffset: Int, type: KotlinType?, operator: IrTypeOperator, - argument: IrExpression, - typeOperand: KotlinType + typeOperand: KotlinType, + argument: IrExpression ) : this(startOffset, endOffset, type, operator, typeOperand) { this.argument = argument } 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 new file mode 100644 index 00000000000..0f4ab0184f3 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVariableAccessExpression.kt @@ -0,0 +1,92 @@ +/* + * 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.ir.expressions + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.types.KotlinType + +interface IrVariableAccessExpression : IrDeclarationReference { + override val descriptor: VariableDescriptor +} + +interface IrGetVariableExpression : IrVariableAccessExpression + +interface IrSetVariableExpression : IrVariableAccessExpression { + val value: IrExpression + val operator: IrOperator? +} + +class IrGetVariableExpressionImpl( + startOffset: Int, + endOffset: Int, + type: KotlinType?, + descriptor: VariableDescriptor +) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, type, descriptor), IrGetVariableExpression { + override fun accept(visitor: IrElementVisitor, data: D): R = + visitor.visitGetVariable(this, data) +} + +class IrSetVariableExpressionImpl( + startOffset: Int, + endOffset: Int, + override val descriptor: VariableDescriptor, + override val operator: IrOperator = IrOperator.EQ +) : IrExpressionBase(startOffset, endOffset, null), IrSetVariableExpression { + constructor( + startOffset: Int, + endOffset: Int, + descriptor: VariableDescriptor, + value: IrExpression, + operator: IrOperator = IrOperator.EQ + ) : this(startOffset, endOffset, descriptor, operator) { + this.value = value + } + + private var valueImpl: IrExpression? = null + override var value: IrExpression + get() = valueImpl!! + set(value) { + value.assertDetached() + valueImpl?.detach() + valueImpl = value + value.setTreeLocation(this, ARGUMENT0_SLOT) + } + + override fun getChild(slot: Int): IrElement? = + when (slot) { + ARGUMENT0_SLOT -> value + else -> null + } + + override fun replaceChild(slot: Int, newChild: IrElement) { + when (slot) { + ARGUMENT0_SLOT -> value = newChild.assertCast() + else -> throwNoSuchSlot(slot) + } + } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitSetVariable(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + value.accept(visitor, data) + } +} 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 c9242578ab3..a386bb84c53 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 @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.renderer.ClassifierNamePolicy import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRendererModifier import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy +import org.jetbrains.kotlin.types.KotlinType class RenderIrElementVisitor : IrElementVisitor { override fun visitElement(element: IrElement, data: Nothing?): String = @@ -83,6 +84,9 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitGetVariable(expression: IrGetVariableExpression, data: Nothing?): String = "GET_VAR ${expression.descriptor.name} type=${expression.renderType()}" + override fun visitSetVariable(expression: IrSetVariableExpression, data: Nothing?): String = + "SET_VAR ${expression.descriptor.name} type=${expression.renderType()}" + override fun visitGetObjectValue(expression: IrGetObjectValueExpression, data: Nothing?): String = "GET_OBJECT ${expression.descriptor.name} type=${expression.renderType()}" @@ -93,6 +97,10 @@ class RenderIrElementVisitor : IrElementVisitor { "SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" + "type=${expression.renderType()}" + override fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: Nothing?): String { + return "TYPE_OP operator=${expression.operator} typeOperand=${expression.typeOperand.render()}" + } + override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String = "DUMMY ${declaration.descriptor.name}" @@ -116,6 +124,9 @@ class RenderIrElementVisitor : IrElementVisitor { DESCRIPTOR_RENDERER.render(this) internal fun IrExpression.renderType(): String = - type?.let { DESCRIPTOR_RENDERER.renderType(it) } ?: "" + type.render() + + internal fun KotlinType?.render(): String = + this?.let { DESCRIPTOR_RENDERER.renderType(it) } ?: "" } } \ No newline at end of file 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 11df316f741..82ae67b1a35 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 @@ -50,6 +50,7 @@ interface IrElementVisitor { fun visitGetObjectValue(expression: IrGetObjectValueExpression, data: D) = visitDeclarationReference(expression, data) fun visitGetEnumValue(expression: IrGetEnumValueExpression, data: D) = visitDeclarationReference(expression, data) fun visitGetVariable(expression: IrGetVariableExpression, data: D) = visitDeclarationReference(expression, data) + fun visitSetVariable(expression: IrSetVariableExpression, data: D) = visitDeclarationReference(expression, data) fun visitGetExtensionReceiver(expression: IrGetExtensionReceiverExpression, data: D) = visitDeclarationReference(expression, data) fun visitMemberAccess(expression: IrMemberAccessExpression, data: D) = visitDeclarationReference(expression, data) fun visitCallExpression(expression: IrCallExpression, data: D) = visitMemberAccess(expression, data) @@ -65,4 +66,5 @@ interface IrElementVisitor { // NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data) fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data) + } diff --git a/compiler/testData/ir/irText/assignments.kt b/compiler/testData/ir/irText/assignments.kt new file mode 100644 index 00000000000..fe1103385e4 --- /dev/null +++ b/compiler/testData/ir/irText/assignments.kt @@ -0,0 +1,12 @@ +// <<< assignments.txt +class Ref(var x: Int) + +fun test1() { + var x = 0 + x = 1 + x = x + 1 +} + +fun test2(r: Ref) { + r.x = 0 +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/assignments.txt b/compiler/testData/ir/irText/assignments.txt new file mode 100644 index 00000000000..c0a9a17b8cd --- /dev/null +++ b/compiler/testData/ir/irText/assignments.txt @@ -0,0 +1,17 @@ +IrFile /assignments.kt + DUMMY Ref + IrFunction public fun test1(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + VAR var x: kotlin.Int + LITERAL Int type=kotlin.Int value='0' + SET_VAR x type= + LITERAL Int type=kotlin.Int value='1' + SET_VAR x type= + DUMMY PLUS type=kotlin.Int + IrFunction public fun test2(/*0*/ r: Ref): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + SET_PROPERTY .xtype= + $this: GET_VAR r type=Ref + $value: LITERAL Int type=kotlin.Int value='0' diff --git a/compiler/testData/ir/irText/callWithReorderedArguments.txt b/compiler/testData/ir/irText/callWithReorderedArguments.txt index 24d3cbbad5e..81af61ed412 100644 --- a/compiler/testData/ir/irText/callWithReorderedArguments.txt +++ b/compiler/testData/ir/irText/callWithReorderedArguments.txt @@ -1,7 +1,7 @@ IrFile /callWithReorderedArguments.kt IrFunction public fun foo(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Unit IrExpressionBody - BLOCK type=kotlin.Unit hasResult=false isDesugared=false + BLOCK type= hasResult=false isDesugared=false IrFunction public fun noReorder1(): kotlin.Int IrExpressionBody BLOCK type= hasResult=false isDesugared=true @@ -24,7 +24,7 @@ IrFile /callWithReorderedArguments.kt LITERAL Int type=kotlin.Int value='2' IrFunction public fun test(): kotlin.Unit IrExpressionBody - BLOCK type=kotlin.Unit hasResult=false isDesugared=false + BLOCK type= hasResult=false isDesugared=false CALL .foo type=kotlin.Unit operator= a: CALL .noReorder1 type=kotlin.Int operator= b: CALL .noReorder2 type=kotlin.Int operator= diff --git a/compiler/testData/ir/irText/destructuring1.txt b/compiler/testData/ir/irText/destructuring1.txt index de386acb978..ea817be22f9 100644 --- a/compiler/testData/ir/irText/destructuring1.txt +++ b/compiler/testData/ir/irText/destructuring1.txt @@ -1,6 +1,6 @@ IrFunction public fun B.test(): kotlin.Unit IrExpressionBody - BLOCK type=kotlin.Unit hasResult=false isDesugared=false + BLOCK type= hasResult=false isDesugared=false VAR val tmp0: A GET_VAR A type=A VAR val x: kotlin.Int diff --git a/compiler/testData/ir/irText/dotQualified.txt b/compiler/testData/ir/irText/dotQualified.txt index 2fc95f69332..fa1334b706a 100644 --- a/compiler/testData/ir/irText/dotQualified.txt +++ b/compiler/testData/ir/irText/dotQualified.txt @@ -10,4 +10,5 @@ IrFile /dotQualified.kt BLOCK type= hasResult=false isDesugared=true RETURN type= GET_PROPERTY ?.length type=kotlin.Int? - $this: GET_VAR s type=kotlin.String? + $this: TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR s type=kotlin.String? diff --git a/compiler/testData/ir/irText/references.txt b/compiler/testData/ir/irText/references.txt index aac1f4d4351..7d20a63fd72 100644 --- a/compiler/testData/ir/irText/references.txt +++ b/compiler/testData/ir/irText/references.txt @@ -23,10 +23,10 @@ IrFile /references.kt GET_VAR x type=kotlin.String IrFunction public fun test3(): kotlin.String IrExpressionBody - BLOCK type=kotlin.Nothing hasResult=false isDesugared=false + BLOCK type= hasResult=false isDesugared=false VAR val x: kotlin.String = "OK" LITERAL String type=kotlin.String value='OK' - RETURN type=kotlin.Nothing + RETURN type= GET_VAR x type=kotlin.String IrFunction public fun test4(): kotlin.String IrExpressionBody diff --git a/compiler/testData/ir/irText/smartCasts.kt b/compiler/testData/ir/irText/smartCasts.kt new file mode 100644 index 00000000000..cc8f80ea2f4 --- /dev/null +++ b/compiler/testData/ir/irText/smartCasts.kt @@ -0,0 +1,24 @@ +// <<< smartCasts.txt +fun expectsString(s: String) {} +fun expectsInt(i: Int) {} + +fun overloaded(s: String) = s +fun overloaded(x: Any) = x + +fun test1(x: Any) { + if (x !is String) return + println(x.length) + expectsString(x) + expectsInt(x.length) + expectsString(overloaded(x)) +} + +fun test2(x: Any): String { + if (x !is String) return "" + return overloaded(x) +} + +fun test3(x: Any): String { + if (x !is String) return "" + return x +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/smartCasts.txt b/compiler/testData/ir/irText/smartCasts.txt new file mode 100644 index 00000000000..37435d35de5 --- /dev/null +++ b/compiler/testData/ir/irText/smartCasts.txt @@ -0,0 +1,51 @@ +IrFile /smartCasts.kt + IrFunction public fun expectsString(/*0*/ s: kotlin.String): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrFunction public fun expectsInt(/*0*/ i: kotlin.Int): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrFunction public fun overloaded(/*0*/ s: kotlin.String): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + GET_VAR s type=kotlin.String + IrFunction public fun overloaded(/*0*/ x: kotlin.Any): kotlin.Any + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + GET_VAR x type=kotlin.Any + IrFunction public fun test1(/*0*/ x: kotlin.Any): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + DUMMY KtIfExpression type=kotlin.Unit + CALL .println type=kotlin.Unit operator= + message: GET_PROPERTY .length type=kotlin.Int + $this: TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR x type=kotlin.Any + CALL .expectsString type=kotlin.Unit operator= + s: TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR x type=kotlin.Any + CALL .expectsInt type=kotlin.Unit operator= + i: GET_PROPERTY .length type=kotlin.Int + $this: TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR x type=kotlin.Any + CALL .expectsString type=kotlin.Unit operator= + s: CALL .overloaded type=kotlin.String operator= + s: TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR x type=kotlin.Any + IrFunction public fun test2(/*0*/ x: kotlin.Any): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + DUMMY KtIfExpression type=kotlin.Unit + RETURN type= + CALL .overloaded type=kotlin.String operator= + s: TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR x type=kotlin.Any + IrFunction public fun test3(/*0*/ x: kotlin.Any): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + DUMMY KtIfExpression type=kotlin.Unit + RETURN type= + TYPE_OP operator=SMART_AS typeOperand=kotlin.String + GET_VAR x type=kotlin.Any diff --git a/compiler/testData/ir/irText/smartCastsWithDestructuring.kt b/compiler/testData/ir/irText/smartCastsWithDestructuring.kt new file mode 100644 index 00000000000..cf1d06fb9d7 --- /dev/null +++ b/compiler/testData/ir/irText/smartCastsWithDestructuring.kt @@ -0,0 +1,11 @@ +// <<< smartCastsWithDestructuring.txt +interface I1 +interface I2 + +operator fun I1.component1() = 1 +operator fun I2.component2() = "" + +fun test(x: I1) { + if (x !is I2) return + val (c1, c2) = x +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/smartCastsWithDestructuring.txt b/compiler/testData/ir/irText/smartCastsWithDestructuring.txt new file mode 100644 index 00000000000..804337b2b47 --- /dev/null +++ b/compiler/testData/ir/irText/smartCastsWithDestructuring.txt @@ -0,0 +1,26 @@ +IrFile /smartCastsWithDestructuring.kt + DUMMY I1 + DUMMY I2 + IrFunction public operator fun I1.component1(): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + LITERAL Int type=kotlin.Int value='1' + IrFunction public operator fun I2.component2(): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + ? IrStringConcatenationExpressionImpl type=kotlin.String + IrFunction public fun test(/*0*/ x: I1): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + DUMMY KtIfExpression type=kotlin.Unit + VAR val tmp0: I1 + GET_VAR x type=I1 + VAR val c1: kotlin.Int + CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1) + $receiver: GET_VAR tmp0 type=I1 + VAR val c2: kotlin.String + CALL .component2 type=kotlin.String operator=COMPONENT_N(index=2) + $receiver: TYPE_OP operator=SMART_AS typeOperand=I2 + GET_VAR tmp0 type=I1 diff --git a/compiler/testData/ir/irText/smoke.txt b/compiler/testData/ir/irText/smoke.txt index 012859c4d59..7e2789638a4 100644 --- a/compiler/testData/ir/irText/smoke.txt +++ b/compiler/testData/ir/irText/smoke.txt @@ -1,8 +1,8 @@ IrFile /smoke.kt IrFunction public fun testFun(): kotlin.String IrExpressionBody - BLOCK type=kotlin.Nothing hasResult=false isDesugared=false - RETURN type=kotlin.Nothing + BLOCK type= hasResult=false isDesugared=false + RETURN type= LITERAL String type=kotlin.String value='OK' IrProperty public val testSimpleVal: kotlin.Int = 1 getter=null setter=null IrExpressionBody @@ -24,4 +24,4 @@ IrFile /smoke.kt LITERAL Int type=kotlin.Int value='42' IrPropertySetter public fun (/*0*/ v: kotlin.Int): kotlin.Unit property=testVarWithAccessors IrExpressionBody - BLOCK type=kotlin.Unit hasResult=false isDesugared=false + BLOCK type= hasResult=false isDesugared=false diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 56700680953..abca1380949 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -35,6 +35,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("assignments.kt") + public void testAssignments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/assignments.kt"); + doTest(fileName); + } + @TestMetadata("boxOk.kt") public void testBoxOk() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/boxOk.kt"); @@ -77,6 +83,18 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("smartCasts.kt") + public void testSmartCasts() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smartCasts.kt"); + doTest(fileName); + } + + @TestMetadata("smartCastsWithDestructuring.kt") + public void testSmartCastsWithDestructuring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smartCastsWithDestructuring.kt"); + doTest(fileName); + } + @TestMetadata("smoke.kt") public void testSmoke() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smoke.kt");