KT-16671 Calls to members imported from objects should be desugared in PSI2IR
This commit is contained in:
+40
-4
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.psi2ir.generators
|
package org.jetbrains.kotlin.psi2ir.generators
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy
|
import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
||||||
@@ -31,6 +33,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.psi2ir.intermediate.*
|
import org.jetbrains.kotlin.psi2ir.intermediate.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getSuperCallExpression
|
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getSuperCallExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
@@ -100,12 +103,22 @@ fun StatementGenerator.generateBackingFieldReceiver(
|
|||||||
|
|
||||||
fun StatementGenerator.generateCallReceiver(
|
fun StatementGenerator.generateCallReceiver(
|
||||||
ktDefaultElement: KtElement,
|
ktDefaultElement: KtElement,
|
||||||
|
calleeDescriptor: CallableDescriptor,
|
||||||
dispatchReceiver: ReceiverValue?,
|
dispatchReceiver: ReceiverValue?,
|
||||||
extensionReceiver: ReceiverValue?,
|
extensionReceiver: ReceiverValue?,
|
||||||
isSafe: Boolean,
|
isSafe: Boolean,
|
||||||
isAssignmentReceiver: Boolean = false
|
isAssignmentReceiver: Boolean = false
|
||||||
) : CallReceiver {
|
) : CallReceiver {
|
||||||
val dispatchReceiverValue = generateReceiverOrNull(ktDefaultElement, dispatchReceiver)
|
val dispatchReceiverValue: IntermediateValue? =
|
||||||
|
if (calleeDescriptor is ImportedFromObjectCallableDescriptor<*>) {
|
||||||
|
assert(dispatchReceiver == null) {
|
||||||
|
"Call for member imported from object $calleeDescriptor has non-null dispatch receiver $dispatchReceiver"
|
||||||
|
}
|
||||||
|
generateReceiverForCalleeImportedFromObject(ktDefaultElement.startOffset, ktDefaultElement.endOffset, calleeDescriptor)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
generateReceiverOrNull(ktDefaultElement, dispatchReceiver)
|
||||||
|
|
||||||
val extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver)
|
val extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver)
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
@@ -119,6 +132,18 @@ fun StatementGenerator.generateCallReceiver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateReceiverForCalleeImportedFromObject(
|
||||||
|
startOffset: Int,
|
||||||
|
endOffset: Int,
|
||||||
|
calleeDescriptor: ImportedFromObjectCallableDescriptor<*>
|
||||||
|
): ExpressionValue {
|
||||||
|
val objectDescriptor = calleeDescriptor.containingObject
|
||||||
|
val objectType = objectDescriptor.defaultType
|
||||||
|
return generateExpressionValue(objectType) {
|
||||||
|
IrGetObjectValueImpl(startOffset, endOffset, objectType, objectDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun StatementGenerator.generateVarargExpression(varargArgument: VarargValueArgument, valueParameter: ValueParameterDescriptor) : IrExpression? {
|
fun StatementGenerator.generateVarargExpression(varargArgument: VarargValueArgument, valueParameter: ValueParameterDescriptor) : IrExpression? {
|
||||||
if (varargArgument.arguments.isEmpty()) {
|
if (varargArgument.arguments.isEmpty()) {
|
||||||
return null
|
return null
|
||||||
@@ -192,14 +217,25 @@ private fun StatementGenerator.pregenerateValueArguments(call: CallBuilder, reso
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun StatementGenerator.pregenerateCallWithReceivers(resolvedCall: ResolvedCall<*>): CallBuilder {
|
fun StatementGenerator.pregenerateCallWithReceivers(resolvedCall: ResolvedCall<*>): CallBuilder {
|
||||||
val call = CallBuilder(resolvedCall)
|
val call = CallBuilder(resolvedCall, unwrapCallableDescriptor(resolvedCall.resultingDescriptor))
|
||||||
|
|
||||||
call.callReceiver = generateCallReceiver(resolvedCall.call.callElement,
|
call.callReceiver = generateCallReceiver(resolvedCall.call.callElement,
|
||||||
|
resolvedCall.resultingDescriptor,
|
||||||
resolvedCall.dispatchReceiver,
|
resolvedCall.dispatchReceiver,
|
||||||
resolvedCall.extensionReceiver,
|
resolvedCall.extensionReceiver,
|
||||||
resolvedCall.call.isSafeCall())
|
isSafe = resolvedCall.call.isSafeCall())
|
||||||
|
|
||||||
call.superQualifier = getSuperQualifier(resolvedCall)
|
call.superQualifier = getSuperQualifier(resolvedCall)
|
||||||
|
|
||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unwrapCallableDescriptor(resultingDescriptor: CallableDescriptor): CallableDescriptor =
|
||||||
|
when (resultingDescriptor) {
|
||||||
|
is ImportedFromObjectCallableDescriptor<*> ->
|
||||||
|
resultingDescriptor.callableFromObject
|
||||||
|
is TypeAliasConstructorDescriptor ->
|
||||||
|
resultingDescriptor.underlyingConstructorDescriptor
|
||||||
|
else ->
|
||||||
|
resultingDescriptor
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -140,7 +140,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
val propertyReceiver = statementGenerator.generateCallReceiver(
|
val propertyReceiver = statementGenerator.generateCallReceiver(
|
||||||
ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
||||||
isSafe = resolvedCall.call.isSafeCall(),
|
isSafe = resolvedCall.call.isSafeCall(),
|
||||||
isAssignmentReceiver = true)
|
isAssignmentReceiver = true)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.psi2ir.generators
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||||
@@ -106,13 +105,8 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
|
|||||||
IrGetValueImpl(startOffset, endOffset, descriptor, origin)
|
IrGetValueImpl(startOffset, endOffset, descriptor, origin)
|
||||||
|
|
||||||
fun generateDelegatingConstructorCall(startOffset: Int, endOffset: Int, call: CallBuilder) : IrExpression {
|
fun generateDelegatingConstructorCall(startOffset: Int, endOffset: Int, call: CallBuilder) : IrExpression {
|
||||||
val descriptor = call.descriptor.let { callDescriptor ->
|
val descriptor = call.descriptor as? ClassConstructorDescriptor
|
||||||
when (callDescriptor) {
|
?: throw AssertionError("Class constructor expected: ${call.descriptor}")
|
||||||
is ClassConstructorDescriptor -> callDescriptor
|
|
||||||
is TypeAliasConstructorDescriptor -> callDescriptor.underlyingConstructorDescriptor
|
|
||||||
else -> throw AssertionError("Unexpected constructor descriptor: $callDescriptor")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return call.callReceiver.call { dispatchReceiver, extensionReceiver ->
|
return call.callReceiver.call { dispatchReceiver, extensionReceiver ->
|
||||||
val irCall = IrDelegatingConstructorCallImpl(startOffset, endOffset, descriptor, getTypeArguments(call.original))
|
val irCall = IrDelegatingConstructorCallImpl(startOffset, endOffset, descriptor, getTypeArguments(call.original))
|
||||||
@@ -215,7 +209,7 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
|
|||||||
irArgumentValues[valueParameter] = irArgumentValue
|
irArgumentValues[valueParameter] = irArgumentValue
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
|
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, _ ->
|
||||||
val valueParameter = valueParameters[index]
|
val valueParameter = valueParameters[index]
|
||||||
irCall.putValueArgument(index, irArgumentValues[valueParameter]?.load())
|
irCall.putValueArgument(index, irArgumentValues[valueParameter]?.load())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ import org.jetbrains.kotlin.psi2ir.isValueArgumentReorderingRequired
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
class CallBuilder(val original: ResolvedCall<*>) {
|
class CallBuilder(
|
||||||
val descriptor: CallableDescriptor = original.resultingDescriptor
|
val original: ResolvedCall<*>,
|
||||||
|
val descriptor: CallableDescriptor
|
||||||
|
) {
|
||||||
var superQualifier: ClassDescriptor? = null
|
var superQualifier: ClassDescriptor? = null
|
||||||
|
|
||||||
lateinit var callReceiver: CallReceiver
|
lateinit var callReceiver: CallReceiver
|
||||||
|
|||||||
+7
@@ -21,6 +21,13 @@ import org.jetbrains.kotlin.ir.util.render
|
|||||||
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
|
abstract class ExpressionValue(override val type: KotlinType) : IntermediateValue
|
||||||
|
|
||||||
|
inline fun generateExpressionValue(type: KotlinType, crossinline generate: () -> IrExpression) =
|
||||||
|
object : ExpressionValue(type) {
|
||||||
|
override fun load(): IrExpression = generate()
|
||||||
|
}
|
||||||
|
|
||||||
class OnceExpressionValue(val irExpression: IrExpression) : LValue, AssignmentReceiver {
|
class OnceExpressionValue(val irExpression: IrExpression) : LValue, AssignmentReceiver {
|
||||||
private var instantiated = false
|
private var instantiated = false
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import A.foo
|
||||||
|
import A.bar
|
||||||
|
import A.fooExt
|
||||||
|
import A.barExt
|
||||||
|
|
||||||
|
object A {
|
||||||
|
fun foo() = 1
|
||||||
|
fun Int.fooExt() = 2
|
||||||
|
val bar = 42
|
||||||
|
val Int.barExt get() = 43
|
||||||
|
}
|
||||||
|
|
||||||
|
val test1 = foo()
|
||||||
|
val test2 = bar
|
||||||
|
val test3 = 1.fooExt()
|
||||||
|
val test4 = 1.barExt
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
FILE /membersImportedFromObject.kt
|
||||||
|
CLASS OBJECT A
|
||||||
|
CONSTRUCTOR private constructor A()
|
||||||
|
BLOCK_BODY
|
||||||
|
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
|
||||||
|
INSTANCE_INITIALIZER_CALL classDescriptor='A'
|
||||||
|
FUN public final fun foo(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='foo(): Int'
|
||||||
|
CONST Int type=kotlin.Int value='1'
|
||||||
|
FUN public final fun kotlin.Int.fooExt(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='fooExt() on Int: Int'
|
||||||
|
CONST Int type=kotlin.Int value='2'
|
||||||
|
PROPERTY public final val bar: kotlin.Int = 42
|
||||||
|
FIELD PROPERTY_BACKING_FIELD public final val bar: kotlin.Int = 42
|
||||||
|
EXPRESSION_BODY
|
||||||
|
CONST Int type=kotlin.Int value='42'
|
||||||
|
FUN DEFAULT_PROPERTY_ACCESSOR public final fun <get-bar>(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<get-bar>(): Int'
|
||||||
|
GET_FIELD 'bar: Int' type=kotlin.Int origin=null
|
||||||
|
receiver: GET_VAR '<receiver: A>' type=A origin=null
|
||||||
|
PROPERTY public final val kotlin.Int.barExt: kotlin.Int
|
||||||
|
FUN public final fun kotlin.Int.<get-barExt>(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<get-barExt>() on Int: Int'
|
||||||
|
CONST Int type=kotlin.Int value='43'
|
||||||
|
PROPERTY public val test1: kotlin.Int
|
||||||
|
FIELD PROPERTY_BACKING_FIELD public val test1: kotlin.Int
|
||||||
|
EXPRESSION_BODY
|
||||||
|
CALL 'foo(): Int' type=kotlin.Int origin=null
|
||||||
|
$this: GET_OBJECT 'A' type=A
|
||||||
|
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test1>(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<get-test1>(): Int'
|
||||||
|
GET_FIELD 'test1: Int' type=kotlin.Int origin=null
|
||||||
|
PROPERTY public val test2: kotlin.Int = 42
|
||||||
|
FIELD PROPERTY_BACKING_FIELD public val test2: kotlin.Int = 42
|
||||||
|
EXPRESSION_BODY
|
||||||
|
CALL '<get-bar>(): Int' type=kotlin.Int origin=GET_PROPERTY
|
||||||
|
$this: GET_OBJECT 'A' type=A
|
||||||
|
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test2>(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<get-test2>(): Int'
|
||||||
|
GET_FIELD 'test2: Int' type=kotlin.Int origin=null
|
||||||
|
PROPERTY public val test3: kotlin.Int
|
||||||
|
FIELD PROPERTY_BACKING_FIELD public val test3: kotlin.Int
|
||||||
|
EXPRESSION_BODY
|
||||||
|
CALL 'fooExt() on Int: Int' type=kotlin.Int origin=null
|
||||||
|
$this: GET_OBJECT 'A' type=A
|
||||||
|
$receiver: CONST Int type=kotlin.Int value='1'
|
||||||
|
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test3>(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<get-test3>(): Int'
|
||||||
|
GET_FIELD 'test3: Int' type=kotlin.Int origin=null
|
||||||
|
PROPERTY public val test4: kotlin.Int
|
||||||
|
FIELD PROPERTY_BACKING_FIELD public val test4: kotlin.Int
|
||||||
|
EXPRESSION_BODY
|
||||||
|
CALL '<get-barExt>() on Int: Int' type=kotlin.Int origin=GET_PROPERTY
|
||||||
|
$this: GET_OBJECT 'A' type=A
|
||||||
|
$receiver: CONST Int type=kotlin.Int value='1'
|
||||||
|
FUN DEFAULT_PROPERTY_ACCESSOR public fun <get-test4>(): kotlin.Int
|
||||||
|
BLOCK_BODY
|
||||||
|
RETURN type=kotlin.Nothing from='<get-test4>(): Int'
|
||||||
|
GET_FIELD 'test4: Int' type=kotlin.Int origin=null
|
||||||
@@ -596,6 +596,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("membersImportedFromObject.kt")
|
||||||
|
public void testMembersImportedFromObject() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/membersImportedFromObject.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("objectAsCallable.kt")
|
@TestMetadata("objectAsCallable.kt")
|
||||||
public void testObjectAsCallable() throws Exception {
|
public void testObjectAsCallable() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/objectAsCallable.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/objectAsCallable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user