PSI2IR: Fix argument adaptation for unbound references

NB front-end "lies".
This commit is contained in:
Dmitry Petrov
2020-01-24 21:33:29 +03:00
parent 0152f19d5f
commit c939fb7b05
7 changed files with 230 additions and 14 deletions
@@ -1379,6 +1379,11 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest {
runTest("compiler/testData/ir/irText/expressions/callableReferences/typeArguments.kt");
}
@TestMetadata("unboundMemberReferenceWithAdaptedArguments.kt")
public void testUnboundMemberReferenceWithAdaptedArguments() throws Exception {
runTest("compiler/testData/ir/irText/expressions/callableReferences/unboundMemberReferenceWithAdaptedArguments.kt");
}
@TestMetadata("withAdaptedArguments.kt")
public void testWithAdaptedArguments() throws Exception {
runTest("compiler/testData/ir/irText/expressions/callableReferences/withAdaptedArguments.kt");
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategyImpl
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.checkers.MissingDependencySupertypeChecker
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
@@ -370,6 +371,16 @@ class ResolvedAtomCompleter(
if (callableReferenceAdaptation == null) return
val callElement = resolvedCall.call.callElement
val isUnboundReference = resolvedCall.dispatchReceiver is TransientReceiver
fun makeFakeValueArgument(callArgument: KotlinCallArgument): ValueArgument {
val fakeCallArgument = callArgument as? FakeKotlinCallArgumentForCallableReference
?: throw AssertionError("FakeKotlinCallArgumentForCallableReference expected: $callArgument")
return FakePositionalValueArgumentForCallableReferenceImpl(
callElement,
if (isUnboundReference) fakeCallArgument.index + 1 else fakeCallArgument.index
)
}
for ((valueParameter, resolvedCallArgument) in callableReferenceAdaptation.mappedArguments) {
resolvedCall.recordValueArgument(
@@ -378,7 +389,7 @@ class ResolvedAtomCompleter(
ResolvedCallArgument.DefaultArgument ->
DefaultValueArgument.DEFAULT
is ResolvedCallArgument.SimpleArgument -> {
val valueArgument = makeFakeValueArgument(resolvedCallArgument.callArgument, callElement)
val valueArgument = makeFakeValueArgument(resolvedCallArgument.callArgument)
if (valueParameter.isVararg)
VarargValueArgument(
listOf(
@@ -391,7 +402,7 @@ class ResolvedAtomCompleter(
is ResolvedCallArgument.VarargArgument ->
VarargValueArgument(
resolvedCallArgument.arguments.map {
makeFakeValueArgument(it, callElement)
makeFakeValueArgument(it)
}
)
}
@@ -399,15 +410,6 @@ class ResolvedAtomCompleter(
}
}
private fun makeFakeValueArgument(
callArgument: KotlinCallArgument,
callElement: KtElement
): ValueArgument {
val fakeCallArgument = callArgument as? FakeKotlinCallArgumentForCallableReference
?: throw AssertionError("FakeKotlinCallArgumentForCallableReference expected: $callArgument")
return FakePositionalValueArgumentForCallableReferenceImpl(callElement, fakeCallArgument.index)
}
private fun completeCollectionLiteralCalls(collectionLiteralArgument: ResolvedCollectionLiteralAtom) {
val psiCallArgument = collectionLiteralArgument.atom.psiCallArgument as CollectionLiteralKotlinCallArgumentImpl
val context = psiCallArgument.outerCallContext
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
import org.jetbrains.kotlin.psi2ir.intermediate.CallBuilder
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -274,6 +275,13 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
throw AssertionError("Callable reference with adapted arguments expected: ${resolvedCall.call.callElement.text}")
}
if (resolvedCall.dispatchReceiver is TransientReceiver) {
// Unbound callable reference 'A::foo', receiver is passed as a first parameter
val irAdaptedReceiverParameter = irAdapterFun.valueParameters[0]
irAdapteeCall.dispatchReceiver =
IrGetValueImpl(startOffset, endOffset, irAdaptedReceiverParameter.type, irAdaptedReceiverParameter.symbol)
}
for ((valueParameter, valueArgument) in adaptedArguments) {
val substitutedValueParameter = resolvedCall.resultingDescriptor.valueParameters[valueParameter.index]
irAdapteeCall.putValueArgument(
@@ -289,8 +297,8 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
resolvedValueArgument: ResolvedValueArgument,
irAdapterFun: IrSimpleFunction,
valueParameter: ValueParameterDescriptor
): IrExpression? =
when (resolvedValueArgument) {
): IrExpression? {
return when (resolvedValueArgument) {
is DefaultValueArgument ->
null
is VarargValueArgument ->
@@ -309,8 +317,9 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
else ->
throw AssertionError("Unexpected ResolvedValueArgument: $resolvedValueArgument")
}
}
private fun adaptValueArgument(
fun adaptValueArgument(
startOffset: Int,
endOffset: Int,
valueArgument: ValueArgument,
@@ -0,0 +1,70 @@
FILE fqName:<root> fileName:/unboundMemberReferenceWithAdaptedArguments.kt
FUN name:use1 visibility:public modality:FINAL <> (fn:kotlin.Function2<<root>.A, kotlin.Int, kotlin.Unit>) returnType:kotlin.Unit
VALUE_PARAMETER name:fn index:0 type:kotlin.Function2<<root>.A, kotlin.Int, kotlin.Unit>
BLOCK_BODY
FUN name:use2 visibility:public modality:FINAL <> (fn:kotlin.Function1<kotlin.Int, kotlin.Unit>) returnType:kotlin.Unit
VALUE_PARAMETER name:fn index:0 type:kotlin.Function1<kotlin.Int, kotlin.Unit>
BLOCK_BODY
CLASS CLASS name:A modality:OPEN visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.A
CONSTRUCTOR visibility:public <> () returnType:<root>.A [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:OPEN visibility:public superTypes:[kotlin.Any]'
FUN name:foo visibility:public modality:OPEN <> ($this:<root>.A, xs:kotlin.IntArray) returnType:kotlin.Int
$this: VALUE_PARAMETER name:<this> type:<root>.A
VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (xs: kotlin.IntArray): kotlin.Int declared in <root>.A'
CONST Int type=kotlin.Int value=1
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS OBJECT name:Obj modality:FINAL visibility:public superTypes:[<root>.A]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.Obj
CONSTRUCTOR visibility:private <> () returnType:<root>.Obj [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.A'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS OBJECT name:Obj modality:FINAL visibility:public superTypes:[<root>.A]'
FUN name:foo visibility:public modality:FINAL <> ($this:<root>.Obj, xs:kotlin.IntArray) returnType:kotlin.Int
$this: VALUE_PARAMETER name:<this> type:<root>.Obj
VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun foo (xs: kotlin.IntArray): kotlin.Int declared in <root>.Obj'
CONST Int type=kotlin.Int value=1
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN name:testUnbound visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/use1]>#' type=IrErrorType
FUNCTION_REFERENCE 'public open fun foo (xs: kotlin.IntArray): kotlin.Int declared in <root>.A' type=kotlin.reflect.KFunction2<<root>.A, kotlin.IntArray, kotlin.Int> origin=null reflectionTarget=<same>
FUN name:testBound visibility:public modality:FINAL <> (a:<root>.A) returnType:kotlin.Unit
VALUE_PARAMETER name:a index:0 type:<root>.A
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/use2]>#' type=IrErrorType
FUNCTION_REFERENCE 'public open fun foo (xs: kotlin.IntArray): kotlin.Int declared in <root>.A' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null reflectionTarget=<same>
FUN name:testObject visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): [/use2]>#' type=IrErrorType
FUNCTION_REFERENCE 'public final fun foo (xs: kotlin.IntArray): kotlin.Int declared in <root>.Obj' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null reflectionTarget=<same>
@@ -0,0 +1,25 @@
// !LANGUAGE: +NewInference, +FunctionReferenceWithDefaultValueAsOtherType
fun use1(fn: (A, Int) -> Unit) {}
fun use2(fn: (Int) -> Unit) {}
open class A {
open fun foo(vararg xs: Int) = 1
}
object Obj : A() {
override fun foo(vararg xs: Int) = 1
}
fun testUnbound() {
use1(A::foo)
}
fun testBound(a: A) {
use2(a::foo)
}
fun testObject() {
use2(Obj::foo)
}
@@ -0,0 +1,100 @@
FILE fqName:<root> fileName:/unboundMemberReferenceWithAdaptedArguments.kt
FUN name:use1 visibility:public modality:FINAL <> (fn:kotlin.Function2<<root>.A, kotlin.Int, kotlin.Unit>) returnType:kotlin.Unit
VALUE_PARAMETER name:fn index:0 type:kotlin.Function2<<root>.A, kotlin.Int, kotlin.Unit>
BLOCK_BODY
FUN name:use2 visibility:public modality:FINAL <> (fn:kotlin.Function1<kotlin.Int, kotlin.Unit>) returnType:kotlin.Unit
VALUE_PARAMETER name:fn index:0 type:kotlin.Function1<kotlin.Int, kotlin.Unit>
BLOCK_BODY
CLASS CLASS name:A modality:OPEN visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.A
CONSTRUCTOR visibility:public <> () returnType:<root>.A [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:OPEN visibility:public superTypes:[kotlin.Any]'
FUN name:foo visibility:public modality:OPEN <> ($this:<root>.A, xs:kotlin.IntArray) returnType:kotlin.Int
$this: VALUE_PARAMETER name:<this> type:<root>.A
VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray varargElementType:kotlin.Int [vararg]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (vararg xs: kotlin.Int): kotlin.Int declared in <root>.A'
CONST Int type=kotlin.Int value=1
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS OBJECT name:Obj modality:FINAL visibility:public superTypes:[<root>.A]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.Obj
CONSTRUCTOR visibility:private <> () returnType:<root>.Obj [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.A'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS OBJECT name:Obj modality:FINAL visibility:public superTypes:[<root>.A]'
FUN name:foo visibility:public modality:OPEN <> ($this:<root>.Obj, xs:kotlin.IntArray) returnType:kotlin.Int
overridden:
public open fun foo (vararg xs: kotlin.Int): kotlin.Int declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:<root>.Obj
VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray varargElementType:kotlin.Int [vararg]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (vararg xs: kotlin.Int): kotlin.Int declared in <root>.Obj'
CONST Int type=kotlin.Int value=1
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [fake_override,operator] declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int [fake_override] declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String [fake_override] declared in <root>.A
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN name:testUnbound visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun use1 (fn: kotlin.Function2<<root>.A, kotlin.Int, kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: BLOCK type=kotlin.reflect.KFunction2<<root>.A, kotlin.Int, kotlin.Unit> origin=null
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo visibility:local modality:FINAL <> (p0:<root>.A, p1:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:<root>.A
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p1 index:1 type:kotlin.Int
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
CALL 'public open fun foo (vararg xs: kotlin.Int): kotlin.Int declared in <root>.A' type=kotlin.Int origin=null
$this: GET_VAR 'p0: <root>.A declared in <root>.testUnbound.foo' type=<root>.A origin=null
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
GET_VAR 'p1: kotlin.Int declared in <root>.testUnbound.foo' type=kotlin.Int origin=null
FUNCTION_REFERENCE 'local final fun foo (p0: <root>.A, p1: kotlin.Int): kotlin.Unit declared in <root>.testUnbound' type=kotlin.reflect.KFunction2<<root>.A, kotlin.Int, kotlin.Unit> origin=null reflectionTarget=null
FUN name:testBound visibility:public modality:FINAL <> (a:<root>.A) returnType:kotlin.Unit
VALUE_PARAMETER name:a index:0 type:<root>.A
BLOCK_BODY
CALL 'public final fun use2 (fn: kotlin.Function1<kotlin.Int, kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: BLOCK type=kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit> origin=null
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
CALL 'public open fun foo (vararg xs: kotlin.Int): kotlin.Int declared in <root>.A' type=kotlin.Int origin=null
$this: GET_VAR 'a: <root>.A declared in <root>.testBound' type=<root>.A origin=null
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
GET_VAR 'p0: kotlin.Int declared in <root>.testBound.foo' type=kotlin.Int origin=null
FUNCTION_REFERENCE 'local final fun foo (p0: kotlin.Int): kotlin.Unit declared in <root>.testBound' type=kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit> origin=null reflectionTarget=null
FUN name:testObject visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun use2 (fn: kotlin.Function1<kotlin.Int, kotlin.Unit>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: BLOCK type=kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit> origin=null
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:foo visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
CALL 'public open fun foo (vararg xs: kotlin.Int): kotlin.Int declared in <root>.Obj' type=kotlin.Int origin=null
$this: GET_OBJECT 'CLASS OBJECT name:Obj modality:FINAL visibility:public superTypes:[<root>.A]' type=<root>.Obj
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
GET_VAR 'p0: kotlin.Int declared in <root>.testObject.foo' type=kotlin.Int origin=null
FUNCTION_REFERENCE 'local final fun foo (p0: kotlin.Int): kotlin.Unit declared in <root>.testObject' type=kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit> origin=null reflectionTarget=null
@@ -1378,6 +1378,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
runTest("compiler/testData/ir/irText/expressions/callableReferences/typeArguments.kt");
}
@TestMetadata("unboundMemberReferenceWithAdaptedArguments.kt")
public void testUnboundMemberReferenceWithAdaptedArguments() throws Exception {
runTest("compiler/testData/ir/irText/expressions/callableReferences/unboundMemberReferenceWithAdaptedArguments.kt");
}
@TestMetadata("withAdaptedArguments.kt")
public void testWithAdaptedArguments() throws Exception {
runTest("compiler/testData/ir/irText/expressions/callableReferences/withAdaptedArguments.kt");