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:
Dmitry Petrov
2019-01-17 13:45:28 +03:00
parent 9c8188002d
commit 202e992ae3
6 changed files with 148 additions and 19 deletions
@@ -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(
@@ -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");