Safe calls.

This commit is contained in:
Dmitry Petrov
2016-08-22 18:13:21 +03:00
committed by Dmitry Petrov
parent 985f3b20c7
commit 594a553a04
69 changed files with 1567 additions and 1093 deletions
@@ -19,9 +19,13 @@ package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.containsNull
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isNullabilityFlexible import org.jetbrains.kotlin.types.isNullabilityFlexible
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.upperIfFlexible
fun IrVariable.defaultLoad(): IrExpression = fun IrVariable.defaultLoad(): IrExpression =
IrGetVariableImpl(startOffset, endOffset, descriptor) IrGetVariableImpl(startOffset, endOffset, descriptor)
@@ -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
}
@@ -17,13 +17,13 @@
package org.jetbrains.kotlin.psi2ir package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrModule import org.jetbrains.kotlin.ir.declarations.IrModule
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator
import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation
import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks
import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
class Psi2IrTranslator(val configuration: Configuration = Configuration()) { class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
@@ -39,8 +39,9 @@ class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
return irModule return irModule
} }
private fun postprocess(irElement: IrElement) { private fun postprocess(irModule: IrModule) {
if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irElement) insertImplicitCasts(irModule.irBuiltins.builtIns, irModule)
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irElement) if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irModule)
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irModule)
} }
} }
@@ -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
}
@@ -16,109 +16,104 @@
package org.jetbrains.kotlin.psi2ir.generators 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.ir.expressions.*
import org.jetbrains.kotlin.psi.KtElement 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.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.psi2ir.intermediate.PregeneratedCall
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.psi2ir.intermediate.getValueArgumentsInParameterOrder
import org.jetbrains.kotlin.resolve.scopes.receivers.* 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 org.jetbrains.kotlin.types.KotlinType
import java.util.* import java.util.*
class CallGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase<StatementGenerator>(parentGenerator) { class CallGenerator(
override val context: GeneratorContext,
override val scope: Scope
) : BodyGenerator {
constructor(parent: BodyGenerator) : this(parent.context, parent.scope)
fun generateCall( fun generateCall(
ktElement: KtElement, startOffset: Int,
resolvedCall: ResolvedCall<out CallableDescriptor>, endOffset: Int,
operator: IrOperator? = null, call: PregeneratedCall,
superQualifier: ClassDescriptor? = null operator: IrOperator? = null
): IrExpression { ): IrExpression {
val descriptor = resolvedCall.resultingDescriptor val descriptor = call.descriptor
return when (descriptor) { return when (descriptor) {
is PropertyDescriptor -> is PropertyDescriptor ->
generatePropertyGetterCall(descriptor, ktElement, resolvedCall) generatePropertyGetterCall(descriptor, startOffset, endOffset, call)
is FunctionDescriptor -> is FunctionDescriptor ->
generateFunctionCall(descriptor, ktElement, operator, resolvedCall, superQualifier) generateFunctionCall(descriptor, startOffset, endOffset, operator, call)
else -> else ->
TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}") TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}")
} }
} }
private fun CallGenerator.generatePropertyGetterCall( private fun generatePropertyGetterCall(
descriptor: PropertyDescriptor, descriptor: PropertyDescriptor,
ktElement: KtElement, startOffset: Int,
resolvedCall: ResolvedCall<*> endOffset: Int,
): IrGetterCallImpl { call: PregeneratedCall
val returnType = getReturnType(resolvedCall) ): IrExpression {
val dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter) return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
val extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter) IrGetterCallImpl(startOffset, endOffset, descriptor.getter!!,
return IrGetterCallImpl(ktElement.startOffset, ktElement.endOffset, dispatchReceiverValue?.load(),
returnType, descriptor.getter!!, resolvedCall.call.isSafeCall(), extensionReceiverValue?.load(),
dispatchReceiver, extensionReceiver, IrOperator.GET_PROPERTY) 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
} }
return false
} }
private fun generateFunctionCall( private fun generateFunctionCall(
descriptor: FunctionDescriptor, descriptor: FunctionDescriptor,
ktElement: KtElement, startOffset: Int,
endOffset: Int,
operator: IrOperator?, operator: IrOperator?,
resolvedCall: ResolvedCall<out CallableDescriptor>, call: PregeneratedCall
superQualifier: ClassDescriptor?
): IrExpression { ): IrExpression {
val returnType = descriptor.returnType val returnType = descriptor.returnType
val irCall = IrCallImpl( return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
ktElement.startOffset, ktElement.endOffset, returnType, val irCall = IrCallImpl(startOffset, endOffset, returnType, descriptor, operator, call.superQualifier)
descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier irCall.dispatchReceiver = dispatchReceiverValue?.load()
) irCall.extensionReceiver = extensionReceiverValue?.load()
irCall.dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
irCall.extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
return if (resolvedCall.requiresArgumentReordering()) { val irCallWithReordering =
generateCallWithArgumentReordering(irCall, ktElement, resolvedCall, returnType) if (call.isValueArgumentReorderingRequired()) {
} generateCallWithArgumentReordering(irCall, startOffset, endOffset, call, returnType)
else { }
val valueArguments = resolvedCall.valueArgumentsByIndex else {
for (index in valueArguments!!.indices) { val valueArguments = call.getValueArgumentsInParameterOrder()
val valueArgument = valueArguments[index] for ((index, valueArgument) in valueArguments.withIndex()) {
val valueParameter = descriptor.valueParameters[index] irCall.putArgument(index, valueArgument)
val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue }
irCall.putArgument(index, irArgument) irCall
} }
irCall
irCallWithReordering
} }
} }
private fun generateCallWithArgumentReordering( private fun generateCallWithArgumentReordering(
irCall: IrCall, irCall: IrCall,
ktElement: KtElement, startOffset: Int,
resolvedCall: ResolvedCall<out CallableDescriptor>, endOffset: Int,
call: PregeneratedCall,
resultType: KotlinType? resultType: KotlinType?
): IrExpression { ): IrExpression {
// TODO use IrLetExpression? val resolvedCall = call.original
val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values
val valueParameters = resolvedCall.resultingDescriptor.valueParameters val valueParameters = resolvedCall.resultingDescriptor.valueParameters
val hasResult = isUsedAsExpression(ktElement) val irBlock = IrBlockImpl(startOffset, endOffset, resultType, true, IrOperator.SYNTHETIC_BLOCK)
val irBlock = IrBlockImpl(ktElement.startOffset, ktElement.endOffset, resultType, hasResult,
IrOperator.SYNTHETIC_BLOCK)
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>() val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
@@ -126,86 +121,28 @@ class CallGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorB
valueArgumentsToValueParameters[valueArgument] = valueParameter valueArgumentsToValueParameters[valueArgument] = valueParameter
} }
val reorderingScope = Scope(this.scope) val irArgumentValues = HashMap<ValueParameterDescriptor, IntermediateValue>()
for (valueArgument in valueArgumentsInEvaluationOrder) { for (valueArgument in valueArgumentsInEvaluationOrder) {
val valueParameter = valueArgumentsToValueParameters[valueArgument]!! val valueParameter = valueArgumentsToValueParameters[valueArgument]!!
val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue val irArgument = call.getValueArgument(valueParameter) ?: continue
val irTmpArg = reorderingScope.introduceTemporary(valueParameter, irArgument) val irArgumentValue = createRematerializableOrTemporary(scope, irArgument, irBlock, valueParameter.name.asString())
irBlock.addIfNotNull(irTmpArg) irArgumentValues[valueParameter] = irArgumentValue
} }
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
val valueParameter = valueParameters[index] val valueParameter = valueParameters[index]
val irGetTemporary = reorderingScope.valueOf(valueParameter)!! irCall.putArgument(index, irArgumentValues[valueParameter]?.load())
irCall.putArgument(index, toExpectedType(irGetTemporary, valueParameter.type))
} }
irBlock.addStatement(irCall) irBlock.addStatement(irCall)
return irBlock 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)
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
class DeclarationGenerator(override val context: GeneratorContext) : IrGenerator { class DeclarationGenerator(override val context: GeneratorContext) : Generator {
fun generateAnnotationEntries(annotationEntries: List<KtAnnotationEntry>) { fun generateAnnotationEntries(annotationEntries: List<KtAnnotationEntry>) {
// TODO create IrAnnotation's for each KtAnnotationEntry // TODO create IrAnnotation's for each KtAnnotationEntry
} }
@@ -25,13 +25,13 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val context: GeneratorContext): IrBodyGenerator { class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val context: GeneratorContext): BodyGenerator {
override val scope = Scope.rootScope(scopeOwner, this) override val scope = Scope(scopeOwner)
fun generateFunctionBody(ktBody: KtExpression): IrExpression { fun generateFunctionBody(ktBody: KtExpression): IrExpression {
resetInternalContext() resetInternalContext()
val irBodyExpression = createStatementGenerator().generateExpressionWithExpectedType(ktBody, scopeOwner.returnType) val irBodyExpression = createStatementGenerator().generateExpression(ktBody)
val irBodyExpressionAsBlock = val irBodyExpressionAsBlock =
if (ktBody is KtBlockExpression) if (ktBody is KtBlockExpression)
@@ -52,7 +52,7 @@ class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val c
} }
fun generatePropertyInitializerBody(ktInitializer: KtExpression): IrExpression = fun generatePropertyInitializerBody(ktInitializer: KtExpression): IrExpression =
createStatementGenerator().generateExpressionWithExpectedType(ktInitializer, scopeOwner.returnType!!) createStatementGenerator().generateExpression(ktInitializer)
private fun createStatementGenerator() = private fun createStatementGenerator() =
StatementGenerator(context, scopeOwner, this, scope) StatementGenerator(context, scopeOwner, this, scope)
@@ -35,35 +35,38 @@ import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import java.lang.AssertionError import java.lang.AssertionError
import java.lang.RuntimeException import java.lang.RuntimeException
interface IrGenerator {
interface Generator {
val context: GeneratorContext val context: GeneratorContext
} }
fun <K, V : Any> IrGenerator.get(slice: ReadOnlySlice<K, V>, key: K): V? = interface BodyGenerator : Generator {
val scope: Scope
}
fun <K, V : Any> Generator.get(slice: ReadOnlySlice<K, V>, key: K): V? =
context.bindingContext[slice, key] context.bindingContext[slice, key]
fun <K, V : Any> IrGenerator.getOrFail(slice: ReadOnlySlice<K, V>, key: K): V = fun <K, V : Any> Generator.getOrFail(slice: ReadOnlySlice<K, V>, key: K): V =
context.bindingContext[slice, key] ?: throw RuntimeException("No $slice for $key") context.bindingContext[slice, key] ?: throw RuntimeException("No $slice for $key")
inline fun <K, V : Any> IrGenerator.getOrFail(slice: ReadOnlySlice<K, V>, key: K, message: (K) -> String): V = inline fun <K, V : Any> Generator.getOrFail(slice: ReadOnlySlice<K, V>, key: K, message: (K) -> String): V =
context.bindingContext[slice, key] ?: throw RuntimeException(message(key)) context.bindingContext[slice, key] ?: throw RuntimeException(message(key))
fun IrGenerator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? = fun Generator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? =
context.bindingContext.getType(key) context.bindingContext.getType(key)
fun IrGenerator.getExpectedTypeForLastInferredCall(key: KtExpression): KotlinType? = fun Generator.getInferredTypeWithSmartcastsOrFail(key: KtExpression): KotlinType =
get(BindingContext.EXPECTED_EXPRESSION_TYPE, key) getInferredTypeWithSmartcasts(key) ?: throw AssertionError("No type for expression: ${key.text}")
fun IrGenerator.getInferredTypeWithSmarcastsOrFail(key: KtExpression): KotlinType = fun Generator.isUsedAsExpression(ktElement: KtElement) =
getInferredTypeWithSmartcasts(key) ?: TODO("No type for expression: ${key.text}")
fun IrGenerator.isUsedAsExpression(ktElement: KtElement) =
get(BindingContext.USED_AS_EXPRESSION, ktElement) ?: false get(BindingContext.USED_AS_EXPRESSION, ktElement) ?: false
fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? = fun Generator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? =
key.getResolvedCall(context.bindingContext) key.getResolvedCall(context.bindingContext)
fun IrGenerator.getReturnType(key: KtExpression): KotlinType? { fun Generator.getReturnType(key: KtExpression): KotlinType? {
val resolvedCall = getResolvedCall(key) val resolvedCall = getResolvedCall(key)
if (resolvedCall != null) { if (resolvedCall != null) {
return getReturnType(resolvedCall) return getReturnType(resolvedCall)
@@ -78,21 +81,21 @@ fun IrGenerator.getReturnType(key: KtExpression): KotlinType? {
} }
fun getReturnType(resolvedCall: ResolvedCall<*>): 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) { return when (descriptor) {
is ClassDescriptor -> is ClassDescriptor ->
descriptor.classValueType ?: throw AssertionError("Class descriptor without companion object: $descriptor") descriptor.classValueType ?: throw AssertionError("Class descriptor without companion object: $descriptor")
is CallableDescriptor -> { is CallableDescriptor -> {
val returnType = descriptor.returnType ?: throw AssertionError("Callable descriptor without return type: $descriptor") descriptor.returnType ?: throw AssertionError("Callable descriptor without return type: $descriptor")
if (resolvedCall.call.isSafeCall())
returnType.makeNullable()
else
returnType
} }
else -> 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) IrDummyExpression(ktExpression.startOffset, ktExpression.endOffset, getInferredTypeWithSmartcasts(ktExpression), description)
@@ -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<IrStatement>()
private var resultType: KotlinType? = null
private var hasResult = false
val scope: Scope get() = generator.scope
fun <T : IrStatement?> add(irStatement: T): T {
if (irStatement != null) {
statements.add(irStatement)
}
return irStatement
}
fun <T : IrExpression> 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 <T> IrBlockBuilder.const(constKind: IrConstKind<T>, 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)
@@ -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<out T : IrBodyGenerator>(
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) }
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
class ModuleGenerator(override val context: GeneratorContext) : IrGenerator { class ModuleGenerator(override val context: GeneratorContext) : Generator {
fun generateModule(ktFiles: List<KtFile>): IrModule { fun generateModule(ktFiles: List<KtFile>): IrModule {
val irDeclarationGenerator = DeclarationGenerator(context) val irDeclarationGenerator = DeclarationGenerator(context)
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.psi2ir.generators.operators package org.jetbrains.kotlin.psi2ir.generators
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.ir.expressions.IrOperator
@@ -25,15 +25,19 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.defaultLoad import org.jetbrains.kotlin.psi2ir.defaultLoad
import org.jetbrains.kotlin.psi2ir.generators.operators.* import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.psi2ir.generators.values.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.types.typeUtil.makeNullable 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<StatementGenerator>(parentGenerator) {
fun generatePrefixExpression(expression: KtPrefixExpression): IrExpression { fun generatePrefixExpression(expression: KtPrefixExpression): IrExpression {
val ktOperator = expression.operationReference.getReferencedNameElementType() val ktOperator = expression.operationReference.getReferencedNameElementType()
val irOperator = getPrefixOperator(ktOperator) val irOperator = getPrefixOperator(ktOperator)
@@ -72,7 +76,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
} }
return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, resultType, irOperator, rhsType, return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, resultType, irOperator, rhsType,
parentGenerator.generateExpression(expression.left)) statementGenerator.generateExpression(expression.left))
} }
fun generateInstanceOfExpression(expression: KtIsExpression): IrStatement { fun generateInstanceOfExpression(expression: KtIsExpression): IrStatement {
@@ -81,7 +85,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
val againstType = getOrFail(BindingContext.TYPE, expression.typeReference) val againstType = getOrFail(BindingContext.TYPE, expression.typeReference)
return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, context.builtIns.booleanType, irOperator, 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 { fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression {
@@ -110,25 +114,28 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateElvis(expression: KtBinaryExpression): IrExpression { private fun generateElvis(expression: KtBinaryExpression): IrExpression {
val specialCallForElvis = getResolvedCall(expression)!! val specialCallForElvis = getResolvedCall(expression)!!
val returnType = specialCallForElvis.resultingDescriptor.returnType!! val returnType = specialCallForElvis.resultingDescriptor.returnType!!
val irArgument0 = parentGenerator.generateExpressionWithExpectedType(expression.left!!, returnType.makeNullable()) val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = parentGenerator.generateExpressionWithExpectedType(expression.right!!, returnType) val irArgument1 = statementGenerator.generateExpression(expression.right!!)
return block(expression, IrOperator.ELVIS) {
add(scope.introduceTemporary(irArgument0)) val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, returnType, true, IrOperator.ELVIS)
result(ifThenElse(returnType, val irArgument0Value = createRematerializableOrTemporary(scope, irArgument0, irBlock, "elvis_lhs")
equalsNull(scope.valueOf(irArgument0)!!), irBlock.addStatement(IrIfThenElseImpl(
irArgument1, expression.startOffset, expression.endOffset, returnType,
scope.valueOf(irArgument0)!!)) equalsNull(expression.startOffset, expression.endOffset, irArgument0Value.load()),
} irArgument1,
irArgument0Value.load()
))
return irBlock
} }
private fun generateBinaryBooleanOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { private fun generateBinaryBooleanOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val irArgument0 = parentGenerator.generateExpressionWithExpectedType(expression.left!!, context.builtIns.booleanType) val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = parentGenerator.generateExpressionWithExpectedType(expression.right!!, context.builtIns.booleanType) val irArgument1 = statementGenerator.generateExpression(expression.right!!)
return when (irOperator) { return when (irOperator) {
IrOperator.OROR -> IrOperator.OROR ->
IrIfThenElseImpl.oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1) oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
IrOperator.ANDAND -> IrOperator.ANDAND ->
IrIfThenElseImpl.andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1) andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
else -> else ->
throw AssertionError("Unexpected binary boolean operator $irOperator") throw AssertionError("Unexpected binary boolean operator $irOperator")
} }
@@ -137,7 +144,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateInOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { private fun generateInOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val containsCall = getResolvedCall(expression)!! 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) { return when (irOperator) {
IrOperator.IN -> IrOperator.IN ->
@@ -152,8 +159,8 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
} }
private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val irArgument0 = parentGenerator.generateExpression(expression.left!!) val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = parentGenerator.generateExpression(expression.right!!) val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irIdentityEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset, irOperator, context.irBuiltIns.eqeqeq, 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 { private fun generateEqualityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val irArgument0 = parentGenerator.generateExpression(expression.left!!) val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = parentGenerator.generateExpression(expression.right!!) val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset, val irEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset,
irOperator, context.irBuiltIns.eqeq, irArgument0, irArgument1) irOperator, context.irBuiltIns.eqeq, irArgument0, irArgument1)
@@ -193,8 +200,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateComparisonOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { private fun generateComparisonOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val compareToCall = getResolvedCall(expression)!! val compareToCall = getResolvedCall(expression)!!
val irCallGenerator = CallGenerator(parentGenerator) val irCompareToCall = CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(compareToCall), irOperator)
val irCompareToCall = irCallGenerator.generateCall(expression, compareToCall, irOperator)
val compareToZeroDescriptor = when (irOperator) { val compareToZeroDescriptor = when (irOperator) {
IrOperator.LT -> context.irBuiltIns.lt0 IrOperator.LT -> context.irBuiltIns.lt0
@@ -210,99 +216,102 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateBinaryOperatorAsCall(expression: KtBinaryExpression, irOperator: IrOperator?): IrExpression { private fun generateBinaryOperatorAsCall(expression: KtBinaryExpression, irOperator: IrOperator?): IrExpression {
val operatorCall = getResolvedCall(expression)!! 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 { private fun generatePrefixIncrementDecrementOperator(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression {
val opResolvedCall = getResolvedCall(expression)!!
val ktBaseExpression = expression.baseExpression!! val ktBaseExpression = expression.baseExpression!!
val irLValue = generateLValue(ktBaseExpression, irOperator) val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, irOperator)
val operatorCall = getResolvedCall(expression)!!
if (irLValue is IrLValueWithAugmentedStore) { return irAssignmentReceiver.assign { irLValue ->
return irLValue.prefixAugmentedStore(operatorCall, irOperator) 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 { private fun generatePostfixIncrementDecrementOperator(expression: KtPostfixExpression, irOperator: IrOperator): IrExpression {
val opResolvedCall = getResolvedCall(expression)!!
val ktBaseExpression = expression.baseExpression!! val ktBaseExpression = expression.baseExpression!!
val irLValue = generateLValue(ktBaseExpression, irOperator) val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, irOperator)
val operatorCall = getResolvedCall(expression)!!
if (irLValue is IrLValueWithAugmentedStore) { return irAssignmentReceiver.assign { irLValue ->
return irLValue.postfixAugmentedStore(operatorCall, irOperator) 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 { private fun generatePrefixOperatorAsCall(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression {
val resolvedCall = getResolvedCall(expression)!! 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 { private fun generateAugmentedAssignment(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val ktLeft = expression.left!! val opResolvedCall = getResolvedCall(expression)!!
val irLValue = generateLValue(ktLeft, irOperator)
val operatorCall = getResolvedCall(expression)!!
val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false 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 irAssignmentReceiver.assign { irLValue ->
return irLValue.augmentedStore(operatorCall, irOperator, parentGenerator.generateExpression(expression.right!!)) 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) } if (isSimpleAssignment) {
val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) // Set( Op( Get(), RHS ) )
irLValue.store(irOpCall)
return if (isSimpleAssignment) { }
// Set( Op( Get(), RHS ) ) else {
irLValue.store(irOpCall) // Op( Get(), RHS )
} irOpCall
else { }
// Op( Get(), RHS )
irOpCall
} }
} }
private fun generateAssignment(expression: KtBinaryExpression): IrExpression { private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
val ktLeft = expression.left!! val ktLeft = expression.left!!
val ktRight = expression.right!! val irRhs = statementGenerator.generateExpression(expression.right!!)
val irLValue = generateLValue(ktLeft, IrOperator.EQ) val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, IrOperator.EQ)
return irLValue.store(parentGenerator.generateExpression(ktRight))
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) { if (ktLeft is KtArrayAccessExpression) {
val irArrayValue = parentGenerator.generateExpression(ktLeft.arrayExpression!!) return generateArrayAccessAssignmentReceiver(ktLeft, irOperator)
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)
} }
val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS") val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS")
@@ -313,20 +322,31 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
if (descriptor.isDelegated) if (descriptor.isDelegated)
TODO("Delegated local variable") TODO("Delegated local variable")
else else
VariableLValue(this, ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator) VariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator)
is PropertyDescriptor -> is PropertyDescriptor -> {
CallGenerator(parentGenerator).run { val propertyReceiver = statementGenerator.generateCallReceiver(
PropertyLValue( ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall()
this, )
ktLeft, irOperator, descriptor, SimplePropertyLValue(scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, propertyReceiver)
generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter), }
generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter),
resolvedCall.call.isSafeCall()
)
}
else -> else ->
TODO("Other cases of LHS") 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)
}
} }
@@ -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)
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.psi2ir.generators package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.IrDeclarationOriginKind
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.IrVariableImpl 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.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.name.Name 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 org.jetbrains.kotlin.types.KotlinType
import java.util.* import java.lang.AssertionError
class Scope private constructor(val scopeOwner: DeclarationDescriptor, val parent: Scope? = null) { class Scope(val scopeOwner: DeclarationDescriptor) {
internal lateinit var generator: IrBodyGenerator private var lastTemporaryIndex: Int = 0
private fun nextTemporaryIndex(): Int = lastTemporaryIndex++
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<Any, IrValue>()
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
}
private fun createDescriptorForTemporaryVariable(type: KotlinType, nameHint: String? = null): IrTemporaryVariableDescriptor = private fun createDescriptorForTemporaryVariable(type: KotlinType, nameHint: String? = null): IrTemporaryVariableDescriptor =
IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier(getNameForTemporary(nameHint)), type) IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier(getNameForTemporary(nameHint)), type)
@@ -79,52 +48,4 @@ class Scope private constructor(val scopeOwner: DeclarationDescriptor, val paren
), ),
irExpression 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
}
}
} }
@@ -26,7 +26,8 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.deparenthesize 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.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils 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.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.constant import java.lang.AssertionError
class StatementGenerator( class StatementGenerator(
override val context: GeneratorContext, override val context: GeneratorContext,
val scopeOwner: DeclarationDescriptor, val scopeOwner: DeclarationDescriptor,
val expressionBodyGenerator: ExpressionBodyGenerator, @Suppress("unused") val expressionBodyGenerator: ExpressionBodyGenerator,
override val scope: Scope override val scope: Scope
) : KtVisitor<IrStatement, Nothing?>(), IrBodyGenerator { ) : KtVisitor<IrStatement, Nothing?>(), BodyGenerator {
fun generateExpression(ktExpression: KtExpression): IrExpression = fun generateExpression(ktExpression: KtExpression): IrExpression =
ktExpression.genExpr() ktExpression.genExpr()
@@ -67,10 +68,7 @@ class StatementGenerator(
val variableDescriptor = getOrFail(BindingContext.VARIABLE, property) val variableDescriptor = getOrFail(BindingContext.VARIABLE, property)
val irLocalVariable = IrVariableImpl(property.startOffset, property.endOffset, IrDeclarationOriginKind.DEFINED, variableDescriptor) val irLocalVariable = IrVariableImpl(property.startOffset, property.endOffset, IrDeclarationOriginKind.DEFINED, variableDescriptor)
irLocalVariable.initializer = property.initializer?.let { irLocalVariable.initializer = property.initializer?.genExpr()
toExpectedType(it.genExpr(), variableDescriptor.type)
}
return irLocalVariable return irLocalVariable
} }
@@ -79,16 +77,19 @@ class StatementGenerator(
val irBlock = IrBlockImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, false, IrOperator.SYNTHETIC_BLOCK) val irBlock = IrBlockImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, false, IrOperator.SYNTHETIC_BLOCK)
val ktInitializer = multiDeclaration.initializer!! val ktInitializer = multiDeclaration.initializer!!
val irTmpInitializer = scope.createTemporaryVariable(ktInitializer.genExpr()) val irTmpInitializerValue = createRematerializableOrTemporary(scope, ktInitializer.genExpr(), irBlock, "container")
irBlock.addStatement(irTmpInitializer)
val irCallGenerator = CallGenerator(this) val irCallGenerator = CallGenerator(this)
irCallGenerator.scope.putValue(ktInitializer, VariableLValue(this, irTmpInitializer))
for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { for ((index, ktEntry) in multiDeclaration.entries.withIndex()) {
val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry)
val componentSubstitutedCall = pregenerateCall(componentResolvedCall)
componentSubstitutedCall.setExplicitReceiverValue(irTmpInitializerValue)
val componentVariable = getOrFail(BindingContext.VARIABLE, ktEntry) 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, val irComponentVar = IrVariableImpl(ktEntry.startOffset, ktEntry.endOffset, IrDeclarationOriginKind.DEFINED,
componentVariable, irComponentCall) componentVariable, irComponentCall)
irBlock.addStatement(irComponentVar) irBlock.addStatement(irComponentVar)
@@ -106,13 +107,8 @@ class StatementGenerator(
override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrStatement { override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrStatement {
val returnTarget = getReturnExpressionTarget(expression) val returnTarget = getReturnExpressionTarget(expression)
val irReturnedExpression = expression.returnedExpression?.let { val irReturnedExpression = expression.returnedExpression?.genExpr()
toExpectedType(it.genExpr(), returnTarget.returnType) return IrReturnImpl(expression.startOffset, expression.endOffset, returnTarget, irReturnedExpression)
}
return IrReturnImpl(
expression.startOffset, expression.endOffset,
returnTarget, irReturnedExpression
)
} }
private fun getReturnExpressionTarget(expression: KtReturnExpression): CallableDescriptor = private fun getReturnExpressionTarget(expression: KtReturnExpression): CallableDescriptor =
@@ -137,7 +133,7 @@ class StatementGenerator(
override fun visitConstantExpression(expression: KtConstantExpression, data: Nothing?): IrExpression { override fun visitConstantExpression(expression: KtConstantExpression, data: Nothing?): IrExpression {
val compileTimeConstant = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext) val compileTimeConstant = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext)
?: error("KtConstantExpression was not evaluated: ${expression.text}") ?: error("KtConstantExpression was not evaluated: ${expression.text}")
val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmarcastsOrFail(expression)) val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmartcastsOrFail(expression))
val constantType = constantValue.type val constantType = constantValue.type
return when (constantValue) { return when (constantValue) {
@@ -163,7 +159,7 @@ class StatementGenerator(
return entry0.genExpr() 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)) val irStringTemplate = IrStringConcatenationImpl(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression))
@@ -191,19 +187,19 @@ class StatementGenerator(
is FakeCallableDescriptorForObject -> is FakeCallableDescriptorForObject ->
generateExpressionForReferencedDescriptor(descriptor.getReferencedDescriptor(), expression, resolvedCall) generateExpressionForReferencedDescriptor(descriptor.getReferencedDescriptor(), expression, resolvedCall)
is ClassDescriptor -> is ClassDescriptor ->
if (DescriptorUtils.isObject(descriptor)) when {
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) DescriptorUtils.isObject(descriptor) ->
else if (DescriptorUtils.isEnumEntry(descriptor)) IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
IrGetEnumValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor) DescriptorUtils.isEnumEntry(descriptor) ->
else { IrGetEnumValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
val companionObjectDescriptor = descriptor.companionObjectDescriptor else -> {
?: error("Class value without companion object: $descriptor") IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType,
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.companionObjectDescriptor ?: throw AssertionError("Class value without companion object: $descriptor"))
descriptor.classValueType, }
companionObjectDescriptor)
} }
is PropertyDescriptor -> { is PropertyDescriptor -> {
CallGenerator(this).generateCall(expression, resolvedCall) // TODO safe calls
CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall))
} }
is VariableDescriptor -> is VariableDescriptor ->
IrGetVariableImpl(expression.startOffset, expression.endOffset, descriptor) IrGetVariableImpl(expression.startOffset, expression.endOffset, descriptor)
@@ -221,7 +217,9 @@ class StatementGenerator(
TODO("VariableAsFunctionResolvedCall = variable call + invoke call") 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 = override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression, data: Nothing?): IrStatement =
@@ -275,8 +273,8 @@ class StatementGenerator(
var irElseBranch: IrExpression? = null var irElseBranch: IrExpression? = null
whenBranches@while (true) { whenBranches@while (true) {
val irCondition = generateExpressionWithExpectedType(ktLastIf.condition!!, context.builtIns.booleanType) val irCondition = ktLastIf.condition!!.genExpr()
val irThenBranch = generateExpressionWithExpectedType(ktLastIf.then!!, resultType) val irThenBranch = ktLastIf.then!!.genExpr()
irBranches.add(Pair(irCondition, irThenBranch)) irBranches.add(Pair(irCondition, irThenBranch))
val ktElse = ktLastIf.`else`?.deparenthesize() val ktElse = ktLastIf.`else`?.deparenthesize()
@@ -284,7 +282,7 @@ class StatementGenerator(
null -> break@whenBranches null -> break@whenBranches
is KtIfExpression -> ktLastIf = ktElse is KtIfExpression -> ktLastIf = ktElse
is KtExpression -> { is KtExpression -> {
irElseBranch = generateExpressionWithExpectedType(ktElse, resultType) irElseBranch = ktElse.genExpr()
break@whenBranches break@whenBranches
} }
else -> throw AssertionError("Unexpected else expression: ${ktElse.text}") else -> throw AssertionError("Unexpected else expression: ${ktElse.text}")
@@ -312,3 +310,5 @@ class StatementGenerator(
} }
@@ -22,16 +22,17 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.defaultLoad 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 org.jetbrains.kotlin.resolve.BindingContext
import java.lang.AssertionError import java.lang.AssertionError
class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase<StatementGenerator>(parentGenerator) { class WhenExpressionGenerator(val statementGenerator: StatementGenerator) : BodyGenerator {
fun generate(expression: KtWhenExpression): IrExpression { override val scope: Scope get() = statementGenerator.scope
val conditionsGenerator = CallGenerator(parentGenerator) override val context: GeneratorContext get() = statementGenerator.context
fun generate(expression: KtWhenExpression): IrExpression {
val irSubject = expression.subjectExpression?.let { val irSubject = expression.subjectExpression?.let {
conditionsGenerator.scope.createTemporary(it, parentGenerator.generateExpression(it), "subject") scope.createTemporaryVariable(statementGenerator.generateExpression(it), "subject")
} }
val resultType = getInferredTypeWithSmartcasts(expression) val resultType = getInferredTypeWithSmartcasts(expression)
@@ -40,7 +41,7 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
for (ktEntry in expression.entries) { for (ktEntry in expression.entries) {
if (ktEntry.isElse) { if (ktEntry.isElse) {
irWhen.elseBranch = parentGenerator.generateExpressionWithExpectedType(ktEntry.expression!!, resultType) irWhen.elseBranch = statementGenerator.generateExpression(ktEntry.expression!!)
break break
} }
@@ -48,14 +49,14 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
for (ktCondition in ktEntry.conditions) { for (ktCondition in ktEntry.conditions) {
val irCondition = val irCondition =
if (irSubject != null) if (irSubject != null)
generateWhenConditionWithSubject(ktCondition, conditionsGenerator, irSubject) generateWhenConditionWithSubject(ktCondition, irSubject)
else else
generateWhenConditionNoSubject(ktCondition) 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) irWhen.addBranch(irBranchCondition!!, irBranchResult)
} }
@@ -85,19 +86,14 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
} }
private fun generateWhenConditionNoSubject(ktCondition: KtWhenCondition): IrExpression = private fun generateWhenConditionNoSubject(ktCondition: KtWhenCondition): IrExpression =
parentGenerator.generateExpressionWithExpectedType((ktCondition as KtWhenConditionWithExpression).expression!!, statementGenerator.generateExpression((ktCondition as KtWhenConditionWithExpression).expression!!)
context.builtIns.booleanType)
private fun generateWhenConditionWithSubject( private fun generateWhenConditionWithSubject(ktCondition: KtWhenCondition, irSubject: IrVariable): IrExpression {
ktCondition: KtWhenCondition,
conditionsGenerator: CallGenerator,
irSubject: IrVariable
): IrExpression {
return when (ktCondition) { return when (ktCondition) {
is KtWhenConditionWithExpression -> is KtWhenConditionWithExpression ->
generateEqualsCondition(irSubject, ktCondition) generateEqualsCondition(irSubject, ktCondition)
is KtWhenConditionInRange -> is KtWhenConditionInRange ->
generateInRangeCondition(conditionsGenerator, ktCondition) generateInRangeCondition(irSubject, ktCondition)
is KtWhenConditionIsPattern -> is KtWhenConditionIsPattern ->
generateIsPatternCondition(irSubject, ktCondition) generateIsPatternCondition(irSubject, ktCondition)
else -> else ->
@@ -113,10 +109,11 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
) )
} }
private fun generateInRangeCondition(conditionsGenerator: CallGenerator, ktCondition: KtWhenConditionInRange): IrExpression { private fun generateInRangeCondition(irSubject: IrVariable, ktCondition: KtWhenConditionInRange): IrExpression {
val inResolvedCall = getResolvedCall(ktCondition.operationReference)!! val inCall = statementGenerator.pregenerateCall(getResolvedCall(ktCondition.operationReference)!!)
inCall.irValueArgumentsByIndex[0] = irSubject.defaultLoad()
val inOperator = getInfixOperator(ktCondition.operationReference.getReferencedNameElementType()) val inOperator = getInfixOperator(ktCondition.operationReference.getReferencedNameElementType())
val irInCall = conditionsGenerator.generateCall(ktCondition, inResolvedCall, inOperator) val irInCall = CallGenerator(statementGenerator).generateCall(ktCondition, inCall, inOperator)
return when (inOperator) { return when (inOperator) {
IrOperator.IN -> irInCall IrOperator.IN -> irInCall
IrOperator.NOT_IN -> IrOperator.NOT_IN ->
@@ -129,6 +126,6 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
IrBinaryOperatorImpl( IrBinaryOperatorImpl(
ktCondition.startOffset, ktCondition.endOffset, ktCondition.startOffset, ktCondition.endOffset,
IrOperator.EQEQ, context.irBuiltIns.eqeq, IrOperator.EQEQ, context.irBuiltIns.eqeq,
irSubject.defaultLoad(), parentGenerator.generateExpression(ktCondition.expression!!) irSubject.defaultLoad(), statementGenerator.generateExpression(ktCondition.expression!!)
) )
} }
@@ -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<Pair<KtExpression, IrExpression>>,
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))
}
}
}
@@ -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
}
}
@@ -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)
}
@@ -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
}
@@ -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<IrExpression>,
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<IntermediateValue>) {
setExplicitReceiverValue(arrayValue)
indexValues.forEachIndexed { i, irIndexValue ->
irValueArgumentsByIndex[i] = irIndexValue.load()
}
}
}
@@ -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)
}
}
@@ -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
}
@@ -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
}
@@ -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<IrExpression>(descriptor.valueParameters.size)
fun getValueArgument(valueParameterDescriptor: ValueParameterDescriptor) =
irValueArgumentsByIndex[valueParameterDescriptor.index]
}
val PregeneratedCall.argumentsCount: Int get() =
irValueArgumentsByIndex.size
fun PregeneratedCall.getValueArgumentsInParameterOrder(): List<IrExpression?> =
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)
}
}
}
}
@@ -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)
}
@@ -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
}
}
@@ -14,12 +14,15 @@
* limitations under the License. * 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.ir.expressions.IrExpression
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
class SimpleCallReceiver(
val dispatchReceiverValue: IntermediateValue?,
val extensionReceiverValue: IntermediateValue?
) : CallReceiver {
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
return withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
}
}
@@ -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<IrVariable>()
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
}
}
}
}
@@ -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
}
@@ -14,7 +14,7 @@
* limitations under the License. * 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.descriptors.VariableDescriptor
import org.jetbrains.kotlin.ir.declarations.IrVariable 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.IrGetVariableImpl
import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.ir.expressions.IrSetVariableImpl 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 import org.jetbrains.kotlin.types.KotlinType
class VariableLValue( class VariableLValue(
val irBodyGenerator: IrBodyGenerator,
val startOffset: Int, val startOffset: Int,
val endOffset: Int, val endOffset: Int,
val descriptor: VariableDescriptor, val descriptor: VariableDescriptor,
val irOperator: IrOperator? = null val irOperator: IrOperator? = null
) : IrLValue { ) : IntermediateReference, AssignmentReceiver {
constructor( constructor(irVariable: IrVariable, irOperator: IrOperator? = null) :
irBodyGenerator: IrBodyGenerator, this(irVariable.startOffset, irVariable.endOffset, irVariable.descriptor, irOperator)
irVariable: IrVariable,
irOperator: IrOperator? = null
) : this(irBodyGenerator, irVariable.startOffset, irVariable.endOffset, irVariable.descriptor, irOperator)
override val type: KotlinType? get() = descriptor.type override val type: KotlinType? get() = descriptor.type
@@ -45,6 +39,8 @@ class VariableLValue(
IrGetVariableImpl(startOffset, endOffset, descriptor, irOperator) IrGetVariableImpl(startOffset, endOffset, descriptor, irOperator)
override fun store(irExpression: IrExpression): IrExpression = override fun store(irExpression: IrExpression): IrExpression =
IrSetVariableImpl(startOffset, endOffset, descriptor, IrSetVariableImpl(startOffset, endOffset, descriptor, irExpression, irOperator)
irBodyGenerator.toExpectedType(irExpression, descriptor.type), irOperator)
override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression =
withLValue(this)
} }
@@ -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<Unit, Nothing?> {
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
}
}
@@ -55,6 +55,15 @@ fun IrElement.replaceWith(otherElement: IrElement) {
parent.replaceChild(slot, otherElement.detach()) parent.replaceChild(slot, otherElement.detach())
} }
fun <T : IrElement> 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) { fun IrElement.assertChild(child: IrElement) {
assert(getChild(child.slot) == child) { "$this: Invalid child: $child" } assert(getChild(child.slot) == child) { "$this: Invalid child: $child" }
} }
@@ -68,7 +68,7 @@ class IrVariableImpl(
} }
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitLocalVariable(this, data) return visitor.visitVariable(this, data)
} }
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) { override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
@@ -48,6 +48,15 @@ class IrBlockImpl(
statements.add(statement) statements.add(statement)
} }
fun addAll(statements: List<IrStatement>) {
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? = override fun getChild(slot: Int): IrElement? =
statements.getOrNull(slot) statements.getOrNull(slot)
@@ -66,4 +75,14 @@ class IrBlockImpl(
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) { override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
statements.forEach { it.accept(visitor, data) } 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)
}
} }
@@ -37,10 +37,9 @@ class IrCallImpl(
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?,
override val descriptor: CallableDescriptor, override val descriptor: CallableDescriptor,
isSafe: Boolean,
override val operator: IrOperator? = null, override val operator: IrOperator? = null,
override val superQualifier: ClassDescriptor? = null override val superQualifier: ClassDescriptor? = null
) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrCall { ) : IrMemberAccessExpressionBase(startOffset, endOffset, type), IrCall {
private val argumentsByParameterIndex = private val argumentsByParameterIndex =
arrayOfNulls<IrExpression>(descriptor.valueParameters.size) arrayOfNulls<IrExpression>(descriptor.valueParameters.size)
@@ -87,12 +86,10 @@ class IrCallImpl(
abstract class IrPropertyAccessorCallBase( abstract class IrPropertyAccessorCallBase(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?,
override val descriptor: CallableDescriptor, override val descriptor: CallableDescriptor,
isSafe: Boolean,
override val operator: IrOperator? = null, override val operator: IrOperator? = null,
override val superQualifier: ClassDescriptor? = null override val superQualifier: ClassDescriptor? = null
) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrCall { ) : IrMemberAccessExpressionBase(startOffset, endOffset, descriptor.returnType), IrCall {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitCall(this, data) return visitor.visitCall(this, data)
} }
@@ -101,23 +98,19 @@ abstract class IrPropertyAccessorCallBase(
class IrGetterCallImpl( class IrGetterCallImpl(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor, descriptor: CallableDescriptor,
isSafe: Boolean,
operator: IrOperator? = null, operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null superQualifier: ClassDescriptor? = null
) : IrPropertyAccessorCallBase(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier), IrCall { ) : IrPropertyAccessorCallBase(startOffset, endOffset, descriptor, operator, superQualifier), IrCall {
constructor( constructor(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor, descriptor: CallableDescriptor,
isSafe: Boolean,
dispatchReceiver: IrExpression?, dispatchReceiver: IrExpression?,
extensionReceiver: IrExpression?, extensionReceiver: IrExpression?,
operator: IrOperator? = null, operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null superQualifier: ClassDescriptor? = null
) : this(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier) { ) : this(startOffset, endOffset, descriptor, operator, superQualifier) {
this.dispatchReceiver = dispatchReceiver this.dispatchReceiver = dispatchReceiver
this.extensionReceiver = extensionReceiver this.extensionReceiver = extensionReceiver
} }
@@ -136,24 +129,20 @@ class IrGetterCallImpl(
class IrSetterCallImpl( class IrSetterCallImpl(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor, descriptor: CallableDescriptor,
isSafe: Boolean,
operator: IrOperator? = null, operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null superQualifier: ClassDescriptor? = null
) : IrPropertyAccessorCallBase(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier), IrCall { ) : IrPropertyAccessorCallBase(startOffset, endOffset, descriptor, operator, superQualifier), IrCall {
constructor( constructor(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor, descriptor: CallableDescriptor,
isSafe: Boolean,
dispatchReceiver: IrExpression?, dispatchReceiver: IrExpression?,
extensionReceiver: IrExpression?, extensionReceiver: IrExpression?,
argument: IrExpression, argument: IrExpression,
operator: IrOperator? = null, operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null superQualifier: ClassDescriptor? = null
) : this(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier) { ) : this(startOffset, endOffset, descriptor, operator, superQualifier) {
this.dispatchReceiver = dispatchReceiver this.dispatchReceiver = dispatchReceiver
this.extensionReceiver = extensionReceiver this.extensionReceiver = extensionReceiver
putArgument(SETTER_ARGUMENT_INDEX, argument) putArgument(SETTER_ARGUMENT_INDEX, argument)
@@ -19,9 +19,11 @@ package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
interface IrConst<out T> : IrExpression { interface IrConst<out T> : IrExpression, IrExpressionWithCopy {
val kind: IrConstKind<T> val kind: IrConstKind<T>
val value: T val value: T
override fun copy(): IrConst<T>
} }
sealed class IrConstKind<out T>(val asString: kotlin.String) { sealed class IrConstKind<out T>(val asString: kotlin.String) {
@@ -52,6 +54,9 @@ class IrConstImpl<out T> (
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitConst(this, data) visitor.visitConst(this, data)
override fun copy(): IrConst<T> =
IrConstImpl(startOffset, endOffset, type, kind, value)
companion object { companion object {
fun string(startOffset: Int, endOffset: Int, type: KotlinType, value: String): IrConstImpl<String> = fun string(startOffset: Int, endOffset: Int, type: KotlinType, value: String): IrConstImpl<String> =
IrConstImpl(startOffset, endOffset, type, IrConstKind.String, value) IrConstImpl(startOffset, endOffset, type, IrConstKind.String, value)
@@ -24,11 +24,14 @@ class IrDummyExpression(
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?,
val description: String val description: String
) : IrTerminalExpressionBase(startOffset, endOffset, type) { ) : IrTerminalExpressionBase(startOffset, endOffset, type), IrExpressionWithCopy {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitDummyExpression(this, data) visitor.visitDummyExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) { override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children // No children
} }
override fun copy(): IrDummyExpression =
IrDummyExpression(startOffset, endOffset, type, description)
} }
@@ -27,6 +27,10 @@ interface IrExpression : IrStatement {
val type: KotlinType? val type: KotlinType?
} }
interface IrExpressionWithCopy : IrExpression {
fun copy(): IrExpression
}
abstract class IrExpressionBase( abstract class IrExpressionBase(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
@@ -20,8 +20,10 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
interface IrGetExtensionReceiver : IrDeclarationReference { interface IrGetExtensionReceiver : IrDeclarationReference, IrExpressionWithCopy {
override val descriptor: ReceiverParameterDescriptor override val descriptor: ReceiverParameterDescriptor
override fun copy(): IrGetExtensionReceiver
} }
class IrGetExtensionReceiverImpl( class IrGetExtensionReceiverImpl(
@@ -32,4 +34,7 @@ class IrGetExtensionReceiverImpl(
) : IrTerminalDeclarationReferenceBase<ReceiverParameterDescriptor>(startOffset, endOffset, type, descriptor), IrGetExtensionReceiver { ) : IrTerminalDeclarationReferenceBase<ReceiverParameterDescriptor>(startOffset, endOffset, type, descriptor), IrGetExtensionReceiver {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitGetExtensionReceiver(this, data) visitor.visitGetExtensionReceiver(this, data)
override fun copy(): IrGetExtensionReceiver =
IrGetExtensionReceiverImpl(startOffset, endOffset, type, descriptor)
} }
@@ -21,22 +21,17 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
interface IrLoop : IrExpression { interface IrLoop : IrExpression {
var body: IrExpression var body: IrExpression
}
interface IrConditionalLoop : IrLoop {
var condition: IrExpression var condition: IrExpression
} }
interface IrWhileLoop : IrConditionalLoop interface IrWhileLoop : IrLoop
interface IrDoWhileLoop : IrConditionalLoop interface IrDoWhileLoop : IrLoop
abstract class IrLoopBase( abstract class IrLoopBase(
startOffset: Int, startOffset: Int,
endOffset: Int endOffset: Int
) : IrExpressionBase(startOffset, endOffset, null), IrLoop { ) : IrExpressionBase(startOffset, endOffset, null), IrLoop {
private var bodyImpl: IrExpression? = null private var bodyImpl: IrExpression? = null
override var body: IrExpression override var body: IrExpression
get() = bodyImpl!! get() = bodyImpl!!
@@ -63,7 +58,7 @@ abstract class IrLoopBase(
abstract class IrConditionalLoopBase( abstract class IrConditionalLoopBase(
startOffset: Int, startOffset: Int,
endOffset: Int endOffset: Int
) : IrLoopBase(startOffset, endOffset), IrConditionalLoop { ) : IrLoopBase(startOffset, endOffset) {
private var conditionImpl: IrExpression? = null private var conditionImpl: IrExpression? = null
override var condition: IrExpression override var condition: IrExpression
get() = conditionImpl!! get() = conditionImpl!!
@@ -23,14 +23,12 @@ import org.jetbrains.kotlin.types.KotlinType
interface IrMemberAccessExpression : IrDeclarationReference { interface IrMemberAccessExpression : IrDeclarationReference {
var dispatchReceiver: IrExpression? var dispatchReceiver: IrExpression?
var extensionReceiver: IrExpression? var extensionReceiver: IrExpression?
val isSafe: Boolean
} }
abstract class IrMemberAccessExpressionBase( abstract class IrMemberAccessExpressionBase(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?
override val isSafe: Boolean
) : IrExpressionBase(startOffset, endOffset, type), IrMemberAccessExpression { ) : IrExpressionBase(startOffset, endOffset, type), IrMemberAccessExpression {
override var dispatchReceiver: IrExpression? = null override var dispatchReceiver: IrExpression? = null
set(newReceiver) { set(newReceiver) {
@@ -20,6 +20,8 @@ interface IrOperator {
abstract class IrOperatorImpl(val debugName: String): IrOperator { abstract class IrOperatorImpl(val debugName: String): IrOperator {
override fun toString(): String = debugName override fun toString(): String = debugName
} }
object SAFE_CALL : IrOperatorImpl("SAFE_CALL")
object UMINUS : IrOperatorImpl("UMINUS") object UMINUS : IrOperatorImpl("UMINUS")
object UPLUS : IrOperatorImpl("UPLUS") 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
}
@@ -33,8 +33,6 @@ abstract class IrOperatorCallBase(
get() = null get() = null
set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver") set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver")
override val isSafe: Boolean get() = false
override var extensionReceiver: IrExpression? override var extensionReceiver: IrExpression?
get() = null get() = null
set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver") set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver")
@@ -20,8 +20,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
interface IrThisReference : IrExpression { interface IrThisReference : IrExpression, IrExpressionWithCopy {
val classDescriptor: ClassDescriptor val classDescriptor: ClassDescriptor
override fun copy(): IrThisReference
} }
class IrThisReferenceImpl( class IrThisReferenceImpl(
@@ -32,4 +33,8 @@ class IrThisReferenceImpl(
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrThisReference { ) : IrTerminalExpressionBase(startOffset, endOffset, type), IrThisReference {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitThisReference(this, data) visitor.visitThisReference(this, data)
override fun copy(): IrThisReference =
IrThisReferenceImpl(startOffset, endOffset, type, classDescriptor)
} }
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.types.KotlinType
enum class IrTypeOperator { enum class IrTypeOperator {
CAST, CAST,
IMPLICIT_CAST, IMPLICIT_CAST,
IMPLICIT_NOTNULL,
SAFE_CAST, SAFE_CAST,
INSTANCEOF, INSTANCEOF,
NOT_INSTANCEOF; NOT_INSTANCEOF;
@@ -26,11 +26,12 @@ interface IrVariableAccessExpression : IrDeclarationReference {
val operator: IrOperator? val operator: IrOperator?
} }
interface IrGetVariable : IrVariableAccessExpression interface IrGetVariable : IrVariableAccessExpression, IrExpressionWithCopy {
override fun copy(): IrGetVariable
}
interface IrSetVariable : IrVariableAccessExpression { interface IrSetVariable : IrVariableAccessExpression {
val value: IrExpression var value: IrExpression
} }
class IrGetVariableImpl( class IrGetVariableImpl(
@@ -41,6 +42,9 @@ class IrGetVariableImpl(
) : IrTerminalDeclarationReferenceBase<VariableDescriptor>(startOffset, endOffset, descriptor.type, descriptor), IrGetVariable { ) : IrTerminalDeclarationReferenceBase<VariableDescriptor>(startOffset, endOffset, descriptor.type, descriptor), IrGetVariable {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitGetVariable(this, data) visitor.visitGetVariable(this, data)
override fun copy(): IrGetVariable =
IrGetVariableImpl(startOffset, endOffset, descriptor, operator)
} }
class IrSetVariableImpl( class IrSetVariableImpl(
@@ -31,6 +31,8 @@ interface IrWhen : IrExpression {
var elseBranch: IrExpression? var elseBranch: IrExpression?
} }
val IrWhen.branchIndices: IntRange get() = 0 ..branchesCount - 1
class IrWhenImpl( class IrWhenImpl(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
@@ -176,28 +178,4 @@ class IrIfThenElseImpl(
thenBranch.accept(visitor, data) thenBranch.accept(visitor, data)
elseBranch?.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)
}
} }
@@ -51,7 +51,7 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitPropertySetter(declaration: IrPropertySetter, data: Nothing?): String = override fun visitPropertySetter(declaration: IrPropertySetter, data: Nothing?): String =
"IrPropertySetter ${declaration.descriptor.render()} property=${declaration.property?.name()}" "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()}" "VAR ${declaration.descriptor.render()}"
override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): String = override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): String =
@@ -76,7 +76,7 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"THIS ${expression.classDescriptor.render()} type=${expression.renderType()}" "THIS ${expression.classDescriptor.render()} type=${expression.renderType()}"
override fun visitCall(expression: IrCall, data: Nothing?): String = 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}" "type=${expression.renderType()} operator=${expression.operator}"
override fun visitGetVariable(expression: IrGetVariable, data: Nothing?): String = override fun visitGetVariable(expression: IrGetVariable, data: Nothing?): String =
@@ -33,7 +33,7 @@ interface IrElementVisitor<out R, in D> {
fun visitProperty(declaration: IrProperty, data: D): R = visitDeclaration(declaration, data) fun visitProperty(declaration: IrProperty, data: D): R = visitDeclaration(declaration, data)
fun visitSimpleProperty(declaration: IrSimpleProperty, data: D): R = visitProperty(declaration, data) fun visitSimpleProperty(declaration: IrSimpleProperty, data: D): R = visitProperty(declaration, data)
fun visitDelegatedProperty(declaration: IrDelegatedProperty, 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 visitBody(body: IrBody, data: D): R = visitElement(body, data)
fun visitExpressionBody(body: IrExpressionBody, data: D): R = visitBody(body, data) fun visitExpressionBody(body: IrExpressionBody, data: D): R = visitBody(body, data)
+18 -10
View File
@@ -4,11 +4,13 @@ IrFile /arrayAssignment.kt
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
VAR val x: kotlin.IntArray VAR val x: kotlin.IntArray
CALL .intArrayOf type=kotlin.IntArray operator=null CALL .intArrayOf type=kotlin.IntArray operator=null
elements: DUMMY vararg type=kotlin.Int elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
CALL .set type=kotlin.Unit operator=EQ DUMMY vararg type=kotlin.Int
$this: GET_VAR x type=kotlin.IntArray operator=null BLOCK type=kotlin.Unit hasResult=false operator=EQ
index: CONST Int type=kotlin.Int value='1' CALL .set type=kotlin.Unit operator=EQ
value: CONST Int type=kotlin.Int value='0' $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 IrFunction public fun foo(): kotlin.Int
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
@@ -17,8 +19,14 @@ IrFile /arrayAssignment.kt
IrFunction public fun test2(): kotlin.Unit IrFunction public fun test2(): kotlin.Unit
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
CALL .set type=kotlin.Unit operator=EQ BLOCK type=kotlin.Unit hasResult=false operator=EQ
$this: CALL .intArrayOf type=kotlin.IntArray operator=null VAR val tmp0_array: kotlin.IntArray
elements: DUMMY vararg type=kotlin.Int CALL .intArrayOf type=kotlin.IntArray operator=null
index: CALL .foo type=kotlin.Int operator=null elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
value: CONST Int type=kotlin.Int value='1' 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'
+10 -2
View File
@@ -1,9 +1,17 @@
fun foo(): IntArray = intArrayOf(1, 2, 3) fun foo(): IntArray = intArrayOf(1, 2, 3)
fun bar() = 42 fun bar() = 42
class C(val x: IntArray)
fun testVariable() { fun testVariable() {
var x = foo() var x = foo()
x[0] += 1 x[0] += 1
foo()[0] *= 2
foo()[bar()] -= 1
} }
fun testCall() {
foo()[bar()] *= 2
}
fun testMember(c: C) {
c.x[0]++
}
+27 -16
View File
@@ -4,12 +4,14 @@ IrFile /arrayAugmentedAssignment1.kt
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
CALL .intArrayOf type=kotlin.IntArray operator=null 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 IrFunction public fun bar(): kotlin.Int
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
CONST Int type=kotlin.Int value='42' CONST Int type=kotlin.Int value='42'
DUMMY C
IrFunction public fun testVariable(): kotlin.Unit IrFunction public fun testVariable(): kotlin.Unit
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
@@ -24,27 +26,36 @@ IrFile /arrayAugmentedAssignment1.kt
$this: GET_VAR x type=kotlin.IntArray operator=null $this: GET_VAR x type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='0' index: CONST Int type=kotlin.Int value='0'
other: CONST Int type=kotlin.Int value='1' other: CONST Int type=kotlin.Int value='1'
IrFunction public fun testCall(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
BLOCK type=kotlin.Unit hasResult=false operator=MULTEQ BLOCK type=kotlin.Unit hasResult=false operator=MULTEQ
VAR val tmp0_array: kotlin.IntArray VAR val tmp0_array: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator=null 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 CALL .set type=kotlin.Unit operator=MULTEQ
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null $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 value: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .get type=kotlin.Int operator=MULTEQ $this: CALL .get type=kotlin.Int operator=MULTEQ
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null $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' other: CONST Int type=kotlin.Int value='2'
BLOCK type=kotlin.Unit hasResult=false operator=MINUSEQ IrFunction public fun testMember(/*0*/ c: C): kotlin.Unit
VAR val tmp1_array: kotlin.IntArray IrExpressionBody
CALL .foo type=kotlin.IntArray operator=null BLOCK type=kotlin.Int hasResult=true operator=null
VAR val tmp2_index0: kotlin.Int BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR
CALL .bar type=kotlin.Int operator=null VAR val tmp0_array: kotlin.IntArray
CALL .set type=kotlin.Unit operator=MINUSEQ CALL .<get-x> type=kotlin.IntArray operator=GET_PROPERTY
$this: GET_VAR tmp1_array type=kotlin.IntArray operator=null $this: GET_VAR c type=C operator=null
index: GET_VAR tmp2_index0 type=kotlin.Int operator=null VAR val tmp1: kotlin.Int
value: CALL .minus type=kotlin.Int operator=MINUSEQ CALL .get type=kotlin.Int operator=POSTFIX_INCR
$this: CALL .get type=kotlin.Int operator=MINUSEQ $this: GET_VAR tmp0_array type=kotlin.IntArray operator=null
$this: GET_VAR tmp1_array type=kotlin.IntArray operator=null index: CONST Int type=kotlin.Int value='0'
index: GET_VAR tmp2_index0 type=kotlin.Int operator=null CALL .set type=kotlin.Unit operator=POSTFIX_INCR
other: CONST Int type=kotlin.Int value='1' $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
+5 -5
View File
@@ -32,21 +32,21 @@ IrFile /augmentedAssignment1.kt
BLOCK type=kotlin.Int hasResult=true operator=null BLOCK type=kotlin.Int hasResult=true operator=null
CALL .<set-p> type=kotlin.Unit operator=PLUSEQ CALL .<set-p> type=kotlin.Unit operator=PLUSEQ
<set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ <set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=PLUSEQ
other: CONST Int type=kotlin.Int value='1' other: CONST Int type=kotlin.Int value='1'
CALL .<set-p> type=kotlin.Unit operator=MINUSEQ CALL .<set-p> type=kotlin.Unit operator=MINUSEQ
<set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ <set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=MINUSEQ
other: CONST Int type=kotlin.Int value='2' other: CONST Int type=kotlin.Int value='2'
CALL .<set-p> type=kotlin.Unit operator=MULTEQ CALL .<set-p> type=kotlin.Unit operator=MULTEQ
<set-?>: CALL .times type=kotlin.Int operator=MULTEQ <set-?>: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=MULTEQ
other: CONST Int type=kotlin.Int value='3' other: CONST Int type=kotlin.Int value='3'
CALL .<set-p> type=kotlin.Unit operator=DIVEQ CALL .<set-p> type=kotlin.Unit operator=DIVEQ
<set-?>: CALL .div type=kotlin.Int operator=DIVEQ <set-?>: CALL .div type=kotlin.Int operator=DIVEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=DIVEQ
other: CONST Int type=kotlin.Int value='4' other: CONST Int type=kotlin.Int value='4'
CALL .<set-p> type=kotlin.Unit operator=PERCEQ CALL .<set-p> type=kotlin.Unit operator=PERCEQ
<set-?>: CALL .mod type=kotlin.Int operator=PERCEQ <set-?>: CALL .mod type=kotlin.Int operator=PERCEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=PERCEQ
other: CONST Int type=kotlin.Int value='5' other: CONST Int type=kotlin.Int value='5'
+5 -5
View File
@@ -42,17 +42,17 @@ IrFile /augmentedAssignment2.kt
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY $receiver: CALL .<get-p> type=A operator=PLUSEQ
s: CONST String type=kotlin.String value='+=' s: CONST String type=kotlin.String value='+='
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY $receiver: CALL .<get-p> type=A operator=MINUSEQ
s: CONST String type=kotlin.String value='-=' s: CONST String type=kotlin.String value='-='
CALL .timesAssign type=kotlin.Unit operator=MULTEQ CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY $receiver: CALL .<get-p> type=A operator=MULTEQ
s: CONST String type=kotlin.String value='*=' s: CONST String type=kotlin.String value='*='
CALL .divAssign type=kotlin.Unit operator=DIVEQ CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY $receiver: CALL .<get-p> type=A operator=DIVEQ
s: CONST String type=kotlin.String value='/=' s: CONST String type=kotlin.String value='/='
CALL .modAssign type=kotlin.Unit operator=PERCEQ CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY $receiver: CALL .<get-p> type=A operator=PERCEQ
s: CONST String type=kotlin.String value='%=' s: CONST String type=kotlin.String value='%='
+2 -2
View File
@@ -28,7 +28,7 @@ IrFile /callWithReorderedArguments.kt
CALL .foo type=kotlin.Unit operator=null CALL .foo type=kotlin.Unit operator=null
a: CALL .noReorder1 type=kotlin.Int operator=null a: CALL .noReorder1 type=kotlin.Int operator=null
b: CALL .noReorder2 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 VAR val tmp0_b: kotlin.Int
CALL .reordered1 type=kotlin.Int operator=null CALL .reordered1 type=kotlin.Int operator=null
VAR val tmp1_a: kotlin.Int VAR val tmp1_a: kotlin.Int
@@ -36,7 +36,7 @@ IrFile /callWithReorderedArguments.kt
CALL .foo type=kotlin.Unit operator=null CALL .foo type=kotlin.Unit operator=null
a: GET_VAR tmp1_a type=kotlin.Int operator=null a: GET_VAR tmp1_a type=kotlin.Int operator=null
b: GET_VAR tmp0_b 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 VAR val tmp2_a: kotlin.Int
CALL .reordered2 type=kotlin.Int operator=null CALL .reordered2 type=kotlin.Int operator=null
CALL .foo type=kotlin.Unit operator=null CALL .foo type=kotlin.Unit operator=null
+3 -3
View File
@@ -2,13 +2,13 @@ IrFunction public fun B.test(): kotlin.Unit
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK
VAR val tmp0: A VAR val tmp0_container: A
GET_OBJECT A type=A GET_OBJECT A type=A
VAR val x: kotlin.Int VAR val x: kotlin.Int
CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1) CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1)
$this: $RECEIVER of: test type=B $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 VAR val y: kotlin.Int
CALL .component2 type=kotlin.Int operator=COMPONENT_N(index=2) CALL .component2 type=kotlin.Int operator=COMPONENT_N(index=2)
$this: $RECEIVER of: test type=B $this: $RECEIVER of: test type=B
$receiver: GET_VAR tmp0 type=A operator=null $receiver: GET_VAR tmp0_container type=A operator=null
+9 -2
View File
@@ -9,6 +9,13 @@ IrFile /dotQualified.kt
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
CALL ?.<get-length> type=kotlin.Int? operator=GET_PROPERTY BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
$this: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String VAR val tmp0_safe_receiver: kotlin.String?
GET_VAR s type=kotlin.String? operator=null 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 .<get-length> type=kotlin.Int operator=GET_PROPERTY
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
+29 -29
View File
@@ -11,22 +11,24 @@ IrFile /elvis.kt
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
WHEN type=kotlin.Any operator=ELVIS BLOCK type=kotlin.Any hasResult=true operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS WHEN type=kotlin.Any operator=null
arg0: GET_VAR a type=kotlin.Any? operator=null if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg1: CONST Null type=kotlin.Nothing? value='null' arg0: GET_VAR a type=kotlin.Any? operator=null
then: GET_VAR b type=kotlin.Any operator=null arg1: CONST Null type=kotlin.Nothing? value='null'
else: GET_VAR a type=kotlin.Any? operator=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 IrFunction public fun test2(/*0*/ a: kotlin.String?, /*1*/ b: kotlin.Any): kotlin.Any
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
WHEN type=kotlin.Any operator=ELVIS BLOCK type=kotlin.Any hasResult=true operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS WHEN type=kotlin.Any operator=null
arg0: GET_VAR a type=kotlin.String? operator=null if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg1: CONST Null type=kotlin.Nothing? value='null' arg0: GET_VAR a type=kotlin.String? operator=null
then: GET_VAR b type=kotlin.Any operator=null arg1: CONST Null type=kotlin.Nothing? value='null'
else: GET_VAR a type=kotlin.String? operator=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 IrFunction public fun test3(/*0*/ a: kotlin.Any?, /*1*/ b: kotlin.Any?): kotlin.String
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
@@ -42,39 +44,37 @@ IrFile /elvis.kt
CONST String type=kotlin.String value='' CONST String type=kotlin.String value=''
RETURN type=<no-type> RETURN type=<no-type>
BLOCK type=kotlin.String hasResult=true operator=ELVIS BLOCK type=kotlin.String hasResult=true operator=ELVIS
VAR val tmp0: kotlin.String? WHEN type=kotlin.String operator=null
TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String? if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
GET_VAR a type=kotlin.Any? operator=null arg0: 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
arg1: CONST Null type=kotlin.Nothing? value='null' arg1: CONST Null type=kotlin.Nothing? value='null'
then: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String then: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String
GET_VAR b type=kotlin.Any? operator=null 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 IrFunction public fun test4(/*0*/ x: kotlin.Any): kotlin.Any
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
BLOCK type=kotlin.Any hasResult=true operator=ELVIS BLOCK type=kotlin.Any hasResult=true operator=ELVIS
VAR val tmp0: kotlin.Any? VAR val tmp0_elvis_lhs: kotlin.Any?
CALL .<get-p> type=kotlin.Any? operator=GET_PROPERTY CALL .<get-p> type=kotlin.Any? operator=GET_PROPERTY
WHEN type=kotlin.Any operator=ELVIS WHEN type=kotlin.Any operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0 type=kotlin.Any? operator=null arg0: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null' arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR x type=kotlin.Any operator=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 IrFunction public fun test5(/*0*/ x: kotlin.Any): kotlin.Any
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
BLOCK type=kotlin.Any hasResult=true operator=ELVIS 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 CALL .foo type=kotlin.Any? operator=null
WHEN type=kotlin.Any operator=ELVIS WHEN type=kotlin.Any operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0 type=kotlin.Any? operator=null arg0: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null' arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR x type=kotlin.Any operator=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
+3 -10
View File
@@ -3,13 +3,6 @@ IrFile /implicitCastOnPlatformType.kt
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type> RETURN type=<no-type>
BLOCK type=kotlin.String hasResult=true operator=IMPLICIT_NOTNULL TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=kotlin.String
VAR val tmp0: kotlin.String! CALL .getProperty type=kotlin.String! operator=null
CALL .getProperty type=kotlin.String! operator=null p0: CONST String type=kotlin.String value='test'
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
+9 -8
View File
@@ -5,7 +5,8 @@ IrFile /incrementDecrement.kt
IrProperty public val arr: kotlin.IntArray getter=null setter=null IrProperty public val arr: kotlin.IntArray getter=null setter=null
IrExpressionBody IrExpressionBody
CALL .intArrayOf type=kotlin.IntArray operator=null 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 IrFunction public fun testVarPrefix(): kotlin.Unit
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null BLOCK type=<no-type> hasResult=false operator=null
@@ -38,7 +39,7 @@ IrFile /incrementDecrement.kt
GET_VAR x type=kotlin.Int operator=POSTFIX_INCR GET_VAR x type=kotlin.Int operator=POSTFIX_INCR
SET_VAR x type=<no-type> operator=POSTFIX_INCR SET_VAR x type=<no-type> operator=POSTFIX_INCR
CALL .inc type=kotlin.Int 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 GET_VAR tmp0 type=kotlin.Int operator=null
VAR val x2: kotlin.Int VAR val x2: kotlin.Int
BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_DECR 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 GET_VAR x type=kotlin.Int operator=POSTFIX_DECR
SET_VAR x type=<no-type> operator=POSTFIX_DECR SET_VAR x type=<no-type> operator=POSTFIX_DECR
CALL .dec type=kotlin.Int 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 GET_VAR tmp1 type=kotlin.Int operator=null
IrFunction public fun testPropPrefix(): kotlin.Unit IrFunction public fun testPropPrefix(): kotlin.Unit
IrExpressionBody IrExpressionBody
@@ -55,7 +56,7 @@ IrFile /incrementDecrement.kt
BLOCK type=kotlin.Int hasResult=true operator=PREFIX_INCR BLOCK type=kotlin.Int hasResult=true operator=PREFIX_INCR
VAR val tmp0: kotlin.Int VAR val tmp0: kotlin.Int
CALL .inc type=kotlin.Int operator=PREFIX_INCR CALL .inc type=kotlin.Int operator=PREFIX_INCR
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=PREFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_INCR CALL .<set-p> type=kotlin.Unit operator=PREFIX_INCR
<set-?>: GET_VAR tmp0 type=kotlin.Int operator=null <set-?>: GET_VAR tmp0 type=kotlin.Int operator=null
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 BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR
VAR val tmp1: kotlin.Int VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null <set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
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 VAR val p1: kotlin.Int
BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR
VAR val tmp0: kotlin.Int VAR val tmp0: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
<set-?>: CALL .inc type=kotlin.Int operator=POSTFIX_INCR <set-?>: 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 GET_VAR tmp0 type=kotlin.Int operator=null
VAR val p2: kotlin.Int VAR val p2: kotlin.Int
BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR
VAR val tmp1: kotlin.Int VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY $this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null <set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null GET_VAR tmp1 type=kotlin.Int operator=null
@@ -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]++
}
@@ -0,0 +1,53 @@
IrFile /safeCallWithIncrementDecrement.kt
DUMMY C
IrProperty public var test.C?.p: kotlin.Int getter=<get-p> setter=<set-p>
IrPropertyGetter public fun test.C?.<get-p>(): kotlin.Int property=p
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
CONST Int type=kotlin.Int value='42'
IrPropertySetter public fun test.C?.<set-p>(/*0*/ value: kotlin.Int): kotlin.Unit property=p
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
IrFunction public operator fun kotlin.Int?.inc(): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-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=<no-type> hasResult=false operator=null
RETURN type=<no-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=<no-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 .<get-p> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null
CALL .<set-p> 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
+15
View File
@@ -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()
+75
View File
@@ -0,0 +1,75 @@
IrFile /safeCalls.kt
DUMMY Ref
DUMMY IHost
IrFunction public fun test1(/*0*/ x: kotlin.String?): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-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 .<get-length> 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=<no-type> hasResult=false operator=null
RETURN type=<no-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=<no-type> hasResult=false operator=null
RETURN type=<no-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=<no-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 .<set-value> type=kotlin.Unit operator=EQ
$this: GET_VAR tmp0_safe_receiver type=Ref? operator=null
<set-?>: CONST Int type=kotlin.Int value='0'
IrFunction public fun IHost.test5(/*0*/ s: kotlin.String?): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-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
@@ -19,12 +19,10 @@ IrFile /smartCastsWithDestructuring.kt
GET_VAR x type=I1 operator=null GET_VAR x type=I1 operator=null
then: RETURN type=<no-type> then: RETURN type=<no-type>
BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK
VAR val tmp0: I1
GET_VAR x type=I1 operator=null
VAR val c1: kotlin.Int VAR val c1: kotlin.Int
CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1) 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 VAR val c2: kotlin.String
CALL .component2 type=kotlin.String operator=COMPONENT_N(index=2) CALL .component2 type=kotlin.String operator=COMPONENT_N(index=2)
$receiver: TYPE_OP operator=IMPLICIT_CAST typeOperand=I2 $receiver: TYPE_OP operator=IMPLICIT_CAST typeOperand=I2
GET_VAR tmp0 type=I1 operator=null GET_VAR x type=I1 operator=null
+2 -4
View File
@@ -21,8 +21,7 @@ IrFile /when.kt
then: CONST String type=kotlin.String value='String' then: CONST String type=kotlin.String value='String'
if: CALL .contains type=kotlin.Boolean operator=IN if: CALL .contains type=kotlin.Boolean operator=IN
$receiver: CALL .setOf type=kotlin.collections.Set<kotlin.Nothing> operator=null $receiver: CALL .setOf type=kotlin.collections.Set<kotlin.Nothing> operator=null
element: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Any element: GET_VAR tmp0_subject type=kotlin.Any? operator=null
GET_VAR tmp0_subject type=kotlin.Any? operator=null
then: CONST String type=kotlin.String value='nothingness?' then: CONST String type=kotlin.String value='nothingness?'
else: CONST String type=kotlin.String value='something' else: CONST String type=kotlin.String value='something'
IrFunction public fun test(/*0*/ x: kotlin.Any?): kotlin.String 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' then: CONST String type=kotlin.String value='String'
if: CALL .contains type=kotlin.Boolean operator=IN if: CALL .contains type=kotlin.Boolean operator=IN
$receiver: CALL .setOf type=kotlin.collections.Set<kotlin.Nothing> operator=null $receiver: CALL .setOf type=kotlin.collections.Set<kotlin.Nothing> operator=null
element: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Any element: GET_VAR x type=kotlin.Any? operator=null
GET_VAR x type=kotlin.Any? operator=null
then: CONST String type=kotlin.String value='nothingness?' then: CONST String type=kotlin.String value='nothingness?'
else: CONST String type=kotlin.String value='something' else: CONST String type=kotlin.String value='something'
IrFunction public fun testComma(/*0*/ x: kotlin.Int): kotlin.String IrFunction public fun testComma(/*0*/ x: kotlin.Int): kotlin.String
@@ -179,6 +179,18 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName); 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") @TestMetadata("simpleOperators.kt")
public void testSimpleOperators() throws Exception { public void testSimpleOperators() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/simpleOperators.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/simpleOperators.kt");