psi2ir: handle 'this' as reference to a super companion object
In super class constructor arguments, 'this' can be resolved as a reference to a companion object of a superclass. This breaks an assumption in psi2ir that 'this' can only refer to some receiver from the current scope. If 'this' refers to an 'object' (including 'companion obejct'), and we are not inside the corresponding class scope, then 'this' represents a reference to a singleton instance "by name" (represented as IrGetObjectValue).
This commit is contained in:
+3
-7
@@ -134,14 +134,10 @@ private fun StatementGenerator.shouldGenerateReceiverAsSingletonReference(receiv
|
||||
}
|
||||
|
||||
private fun StatementGenerator.generateThisOrSuperReceiver(receiver: ReceiverValue, classDescriptor: ClassDescriptor): IrExpression {
|
||||
val expressionReceiver =
|
||||
receiver as? ExpressionReceiver ?: throw AssertionError("'this' or 'super' receiver should be an expression receiver")
|
||||
val expressionReceiver = receiver as? ExpressionReceiver
|
||||
?: throw AssertionError("'this' or 'super' receiver should be an expression receiver")
|
||||
val ktReceiver = expressionReceiver.expression
|
||||
return IrGetValueImpl(
|
||||
ktReceiver.startOffsetSkippingComments, ktReceiver.endOffset,
|
||||
expressionReceiver.type.toIrType(),
|
||||
context.symbolTable.referenceValueParameter(classDescriptor.thisAsReceiverParameter)
|
||||
)
|
||||
return generateThisReceiver(ktReceiver.startOffsetSkippingComments, ktReceiver.endOffset, expressionReceiver.type, classDescriptor)
|
||||
}
|
||||
|
||||
fun StatementGenerator.generateBackingFieldReceiver(
|
||||
|
||||
+35
-10
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi2ir.intermediate.createTemporaryVariableInBlock
|
||||
import org.jetbrains.kotlin.psi2ir.intermediate.setExplicitReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
@@ -364,28 +365,52 @@ class StatementGenerator(
|
||||
override fun visitSafeQualifiedExpression(expression: KtSafeQualifiedExpression, data: Nothing?): IrStatement =
|
||||
expression.selectorExpression!!.accept(this, data)
|
||||
|
||||
private fun isInsideClass(classDescriptor: ClassDescriptor): Boolean {
|
||||
var scopeDescriptor: DeclarationDescriptor? = scopeOwner
|
||||
while (scopeDescriptor != null) {
|
||||
if (scopeDescriptor == classDescriptor) return true
|
||||
scopeDescriptor = scopeDescriptor.containingDeclaration
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun generateThisReceiver(startOffset: Int, endOffset: Int, kotlinType: KotlinType, classDescriptor: ClassDescriptor): IrExpression {
|
||||
val thisAsReceiverParameter = classDescriptor.thisAsReceiverParameter
|
||||
val thisType = kotlinType.toIrType()
|
||||
|
||||
return if (DescriptorUtils.isObject(classDescriptor) && !isInsideClass(classDescriptor)) {
|
||||
IrGetObjectValueImpl(
|
||||
startOffset, endOffset,
|
||||
thisType,
|
||||
context.symbolTable.referenceClass(classDescriptor)
|
||||
)
|
||||
} else {
|
||||
IrGetValueImpl(
|
||||
startOffset, endOffset,
|
||||
thisType,
|
||||
context.symbolTable.referenceValueParameter(thisAsReceiverParameter)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThisExpression(expression: KtThisExpression, data: Nothing?): IrExpression {
|
||||
val referenceTarget = getOrFail(BindingContext.REFERENCE_TARGET, expression.instanceReference) { "No reference target for this" }
|
||||
val startOffset = expression.startOffsetSkippingComments
|
||||
val endOffset = expression.endOffset
|
||||
return when (referenceTarget) {
|
||||
is ClassDescriptor -> {
|
||||
val thisAsReceiverParameter = referenceTarget.thisAsReceiverParameter
|
||||
val thisType = thisAsReceiverParameter.type.toIrType()
|
||||
IrGetValueImpl(
|
||||
expression.startOffsetSkippingComments, expression.endOffset,
|
||||
thisType,
|
||||
context.symbolTable.referenceValueParameter(thisAsReceiverParameter)
|
||||
)
|
||||
}
|
||||
is ClassDescriptor ->
|
||||
generateThisReceiver(startOffset, endOffset, referenceTarget.thisAsReceiverParameter.type, referenceTarget)
|
||||
|
||||
is CallableDescriptor -> {
|
||||
val extensionReceiver = referenceTarget.extensionReceiverParameter ?: TODO("No extension receiver: $referenceTarget")
|
||||
val extensionReceiverType = extensionReceiver.type.toIrType()
|
||||
IrGetValueImpl(
|
||||
expression.startOffsetSkippingComments, expression.endOffset,
|
||||
startOffset, endOffset,
|
||||
extensionReceiverType,
|
||||
context.symbolTable.referenceValueParameter(extensionReceiver)
|
||||
)
|
||||
}
|
||||
|
||||
else ->
|
||||
error("Expected this or receiver: $referenceTarget")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR, JS_IR
|
||||
|
||||
fun WithCompanion.test(): String {
|
||||
object : WithCompanion(this) {}
|
||||
return "OK"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
fun WithCompanion.test() {
|
||||
val test1 = object : WithCompanion(this) {}
|
||||
val test2 = object : WithCompanion(this.foo()) {}
|
||||
}
|
||||
|
||||
open class WithCompanion(a: WithCompanion.Companion) {
|
||||
companion object {
|
||||
fun foo(): WithCompanion.Companion = this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
FILE fqName:<root> fileName:/thisReferenceBeforeClassDeclared.kt
|
||||
FUN name:test visibility:public modality:FINAL <> ($receiver:WithCompanion) returnType:kotlin.Unit flags:
|
||||
$receiver: VALUE_PARAMETER name:<this> type:WithCompanion flags:
|
||||
BLOCK_BODY
|
||||
VAR name:test1 type:test.<no name provided> flags:val
|
||||
BLOCK type=test.<no name provided> origin=OBJECT_LITERAL
|
||||
CLASS CLASS name:<no name provided> modality:FINAL visibility:local flags: superTypes:[WithCompanion]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:test.<no name provided> flags:
|
||||
CONSTRUCTOR visibility:public <> () returnType:test.<no name provided> flags:primary
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'constructor WithCompanion(WithCompanion.Companion)'
|
||||
a: GET_OBJECT 'companion object of WithCompanion' type=WithCompanion.Companion
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='<no name provided>'
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
overridden:
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
overridden:
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
overridden:
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
CALL 'constructor <no name provided>()' type=test.<no name provided> origin=OBJECT_LITERAL
|
||||
VAR name:test2 type:test.<no name provided> flags:val
|
||||
BLOCK type=test.<no name provided> origin=OBJECT_LITERAL
|
||||
CLASS CLASS name:<no name provided> modality:FINAL visibility:local flags: superTypes:[WithCompanion]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:test.<no name provided> flags:
|
||||
CONSTRUCTOR visibility:public <> () returnType:test.<no name provided> flags:primary
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'constructor WithCompanion(WithCompanion.Companion)'
|
||||
a: CALL 'foo(): WithCompanion.Companion' type=WithCompanion.Companion origin=null
|
||||
$this: GET_OBJECT 'companion object of WithCompanion' type=WithCompanion.Companion
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='<no name provided>'
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
overridden:
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
overridden:
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
overridden:
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
CALL 'constructor <no name provided>()' type=test.<no name provided> origin=OBJECT_LITERAL
|
||||
CLASS CLASS name:WithCompanion modality:OPEN visibility:public flags: superTypes:[kotlin.Any]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:WithCompanion flags:
|
||||
CONSTRUCTOR visibility:public <> (a:WithCompanion.Companion) returnType:WithCompanion flags:primary
|
||||
VALUE_PARAMETER name:a index:0 type:WithCompanion.Companion flags:
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='WithCompanion'
|
||||
CLASS OBJECT name:Companion modality:FINAL visibility:public flags:companion superTypes:[kotlin.Any]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:WithCompanion.Companion flags:
|
||||
CONSTRUCTOR visibility:private <> () returnType:WithCompanion.Companion flags:primary
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='companion object of WithCompanion'
|
||||
FUN name:foo visibility:public modality:FINAL <> ($this:WithCompanion.Companion) returnType:WithCompanion.Companion flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:WithCompanion.Companion flags:
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='foo(): WithCompanion.Companion'
|
||||
GET_VAR 'this@Companion: Companion' type=WithCompanion.Companion origin=null
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
overridden:
|
||||
FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
overridden:
|
||||
FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
overridden:
|
||||
FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
overridden:
|
||||
FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
overridden:
|
||||
FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
overridden:
|
||||
FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
|
||||
@@ -1132,6 +1132,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
||||
runTest("compiler/testData/ir/irText/expressions/thisOfGenericOuterClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thisReferenceBeforeClassDeclared.kt")
|
||||
public void testThisReferenceBeforeClassDeclared() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/thisReferenceBeforeClassDeclared.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("throw.kt")
|
||||
public void testThrow() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/expressions/throw.kt");
|
||||
|
||||
Reference in New Issue
Block a user