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
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
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.IrExpressionWithCopy
|
||||
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.psi2ir.intermediate.*
|
||||
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.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
@@ -100,12 +103,22 @@ fun StatementGenerator.generateBackingFieldReceiver(
|
||||
|
||||
fun StatementGenerator.generateCallReceiver(
|
||||
ktDefaultElement: KtElement,
|
||||
calleeDescriptor: CallableDescriptor,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
extensionReceiver: ReceiverValue?,
|
||||
isSafe: Boolean,
|
||||
isAssignmentReceiver: Boolean = false
|
||||
) : 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)
|
||||
|
||||
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? {
|
||||
if (varargArgument.arguments.isEmpty()) {
|
||||
return null
|
||||
@@ -192,14 +217,25 @@ private fun StatementGenerator.pregenerateValueArguments(call: CallBuilder, reso
|
||||
}
|
||||
|
||||
fun StatementGenerator.pregenerateCallWithReceivers(resolvedCall: ResolvedCall<*>): CallBuilder {
|
||||
val call = CallBuilder(resolvedCall)
|
||||
val call = CallBuilder(resolvedCall, unwrapCallableDescriptor(resolvedCall.resultingDescriptor))
|
||||
|
||||
call.callReceiver = generateCallReceiver(resolvedCall.call.callElement,
|
||||
resolvedCall.resultingDescriptor,
|
||||
resolvedCall.dispatchReceiver,
|
||||
resolvedCall.extensionReceiver,
|
||||
resolvedCall.call.isSafeCall())
|
||||
isSafe = resolvedCall.call.isSafeCall())
|
||||
|
||||
call.superQualifier = getSuperQualifier(resolvedCall)
|
||||
|
||||
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(
|
||||
ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
||||
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
||||
isSafe = resolvedCall.call.isSafeCall(),
|
||||
isAssignmentReceiver = true)
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.psi2ir.generators
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
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.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
@@ -106,13 +105,8 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
|
||||
IrGetValueImpl(startOffset, endOffset, descriptor, origin)
|
||||
|
||||
fun generateDelegatingConstructorCall(startOffset: Int, endOffset: Int, call: CallBuilder) : IrExpression {
|
||||
val descriptor = call.descriptor.let { callDescriptor ->
|
||||
when (callDescriptor) {
|
||||
is ClassConstructorDescriptor -> callDescriptor
|
||||
is TypeAliasConstructorDescriptor -> callDescriptor.underlyingConstructorDescriptor
|
||||
else -> throw AssertionError("Unexpected constructor descriptor: $callDescriptor")
|
||||
}
|
||||
}
|
||||
val descriptor = call.descriptor as? ClassConstructorDescriptor
|
||||
?: throw AssertionError("Class constructor expected: ${call.descriptor}")
|
||||
|
||||
return call.callReceiver.call { dispatchReceiver, extensionReceiver ->
|
||||
val irCall = IrDelegatingConstructorCallImpl(startOffset, endOffset, descriptor, getTypeArguments(call.original))
|
||||
@@ -215,7 +209,7 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
|
||||
irArgumentValues[valueParameter] = irArgumentValue
|
||||
}
|
||||
|
||||
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
|
||||
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, _ ->
|
||||
val valueParameter = valueParameters[index]
|
||||
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.types.KotlinType
|
||||
|
||||
class CallBuilder(val original: ResolvedCall<*>) {
|
||||
val descriptor: CallableDescriptor = original.resultingDescriptor
|
||||
|
||||
class CallBuilder(
|
||||
val original: ResolvedCall<*>,
|
||||
val descriptor: CallableDescriptor
|
||||
) {
|
||||
var superQualifier: ClassDescriptor? = null
|
||||
|
||||
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.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 {
|
||||
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);
|
||||
}
|
||||
|
||||
@TestMetadata("membersImportedFromObject.kt")
|
||||
public void testMembersImportedFromObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/membersImportedFromObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("objectAsCallable.kt")
|
||||
public void testObjectAsCallable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/objectAsCallable.kt");
|
||||
|
||||
Reference in New Issue
Block a user