[FIR] Use correct source for implicit this receiver

#KT-57489 Fixed
#KT-56139 Fixed
#KT-56755 Fixed
This commit is contained in:
Ivan Kylchik
2023-05-10 12:51:47 +02:00
committed by Space Team
parent ab03cb2357
commit 8f88f08573
23 changed files with 286 additions and 36 deletions
@@ -48,7 +48,6 @@ import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.createConeDiagnosticForCandidateWithError
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithCandidates
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeHiddenCandidateError
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -790,9 +789,8 @@ internal class KtFirCallResolver(
}
private fun FirExpression.toKtReceiverValue(): KtReceiverValue? {
val psi = psi
return when (this) {
is FirSmartCastExpression -> {
return when {
this is FirSmartCastExpression -> {
val result = originalExpression.toKtReceiverValue()
if (result != null && isStable) {
KtSmartCastedReceiverValue(result, smartcastType.coneType.asKtType())
@@ -800,21 +798,17 @@ internal class KtFirCallResolver(
result
}
}
is FirThisReceiverExpression -> {
if (psi == null) {
val implicitPartiallyAppliedSymbol = when (val partiallyAppliedSymbol = calleeReference.boundSymbol) {
is FirClassSymbol<*> -> partiallyAppliedSymbol.toKtSymbol()
is FirCallableSymbol<*> -> firSymbolBuilder.callableBuilder.buildExtensionReceiverSymbol(partiallyAppliedSymbol)
?: return null
else -> return null
}
KtImplicitReceiverValue(implicitPartiallyAppliedSymbol, typeRef.coneType.asKtType())
} else {
if (psi !is KtExpression) return null
psi.toExplicitReceiverValue(typeRef.coneType.asKtType())
this is FirThisReceiverExpression && this.isImplicit -> {
val implicitPartiallyAppliedSymbol = when (val partiallyAppliedSymbol = calleeReference.boundSymbol) {
is FirClassSymbol<*> -> partiallyAppliedSymbol.toKtSymbol()
is FirCallableSymbol<*> -> firSymbolBuilder.callableBuilder.buildExtensionReceiverSymbol(partiallyAppliedSymbol)
?: return null
else -> return null
}
KtImplicitReceiverValue(implicitPartiallyAppliedSymbol, typeRef.coneType.asKtType())
}
else -> {
val psi = psi
if (psi !is KtExpression) return null
psi.toExplicitReceiverValue(typeRef.coneType.asKtType())
}
@@ -92,6 +92,12 @@ internal fun <T : IrElement> FirQualifiedAccessExpression.convertWithOffsets(
return convertWithOffsets(this.calleeReference, f)
}
internal fun <T : IrElement> FirThisReceiverExpression.convertWithOffsets(
f: (startOffset: Int, endOffset: Int) -> T
): T {
return source.convertWithOffsets(f)
}
internal fun <T : IrElement> FirStatement.convertWithOffsets(
calleeReference: FirReference,
f: (startOffset: Int, endOffset: Int) -> T
@@ -247,6 +247,18 @@ public class FirLightTreeSteppingTestGenerated extends AbstractFirLightTreeStepp
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test
@TestMetadata("implicitThis.kt")
public void testImplicitThis() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThis.kt");
}
@Test
@TestMetadata("implicitThisOnInvoke.kt")
public void testImplicitThisOnInvoke() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThisOnInvoke.kt");
}
@Test
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
@@ -247,6 +247,18 @@ public class FirPsiSteppingTestGenerated extends AbstractFirPsiSteppingTest {
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test
@TestMetadata("implicitThis.kt")
public void testImplicitThis() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThis.kt");
}
@Test
@TestMetadata("implicitThisOnInvoke.kt")
public void testImplicitThisOnInvoke() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThisOnInvoke.kt");
}
@Test
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
@@ -99,6 +99,11 @@ class FirCallResolver(
// We need desugaring
val resultFunctionCall = if (candidate != null && candidate.callInfo != result.info) {
// This branch support case for the call of the type `a.invoke()`
// 1. Handle candidate for `a`
(resolvedReceiver?.calleeReference as? FirNamedReferenceWithCandidate)?.candidate?.updateSourcesOfReceivers()
// 2. Handle candidate for `invoke`
candidate.updateSourcesOfReceivers()
functionCall.copyAsImplicitInvokeCall {
explicitReceiver = candidate.callInfo.explicitReceiver
dispatchReceiver = candidate.dispatchReceiverExpression()
@@ -107,6 +112,7 @@ class FirCallResolver(
contextReceiverArguments.addAll(candidate.contextReceiverArguments())
}
} else {
candidate?.updateSourcesOfReceivers()
functionCall
}
val typeRef = components.typeFromCallee(resultFunctionCall)
@@ -344,6 +350,7 @@ class FirCallResolver(
qualifiedAccess.replaceCalleeReference(nameReference)
if (reducedCandidates.size == 1) {
val candidate = reducedCandidates.single()
candidate.updateSourcesOfReceivers()
qualifiedAccess.apply {
replaceDispatchReceiver(candidate.dispatchReceiverExpression())
replaceExtensionReceiver(candidate.chosenExtensionReceiverExpression())
@@ -419,6 +426,7 @@ class FirCallResolver(
}
val chosenCandidate = reducedCandidates.single()
chosenCandidate.updateSourcesOfReceivers()
constraintSystemBuilder.runTransaction {
chosenCandidate.outerConstraintBuilderEffect!!(this)
@@ -596,6 +604,7 @@ class FirCallResolver(
return call.apply {
call.replaceCalleeReference(nameReference)
val singleCandidate = reducedCandidates.singleOrNull()
singleCandidate?.updateSourcesOfReceivers()
if (singleCandidate != null) {
val symbol = singleCandidate.symbol
if (symbol is FirConstructorSymbol && symbol.fir.isInner) {
@@ -5,8 +5,12 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpressionCopy
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
@@ -16,6 +20,7 @@ import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
@@ -103,16 +108,46 @@ class Candidate(
var passedStages: Int = 0
// FirExpressionStub can be located here in case of callable reference resolution
fun dispatchReceiverExpression(): FirExpression =
dispatchReceiver?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
private var sourcesWereUpdated = false
// FirExpressionStub can be located here in case of callable reference resolution
fun chosenExtensionReceiverExpression(): FirExpression =
chosenExtensionReceiver?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
fun dispatchReceiverExpression(): FirExpression {
return dispatchReceiver?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
}
fun contextReceiverArguments(): List<FirExpression> =
contextReceiverArguments ?: emptyList()
// FirExpressionStub can be located here in case of callable reference resolution
fun chosenExtensionReceiverExpression(): FirExpression {
return chosenExtensionReceiver?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
}
fun contextReceiverArguments(): List<FirExpression> {
return contextReceiverArguments ?: emptyList()
}
// In case of implicit receivers we want to update corresponding sources to generate correct offset. This method must be called only
// once when candidate was selected and confirmed to be correct one.
fun updateSourcesOfReceivers() {
require(!sourcesWereUpdated)
sourcesWereUpdated = true
dispatchReceiver = dispatchReceiver?.tryToSetSourceForImplicitReceiver()
chosenExtensionReceiver = chosenExtensionReceiver?.tryToSetSourceForImplicitReceiver()
contextReceiverArguments = contextReceiverArguments?.map { it.tryToSetSourceForImplicitReceiver() }
}
private fun FirExpression.tryToSetSourceForImplicitReceiver(): FirExpression {
return when {
this is FirSmartCastExpression -> {
this.apply { replaceOriginalExpression(this.originalExpression.tryToSetSourceForImplicitReceiver()) }
}
this is FirThisReceiverExpression && isImplicit -> {
buildThisReceiverExpressionCopy(this) {
source = callInfo.callSite.source?.fakeElement(KtFakeSourceElementKind.ImplicitReceiver)
}
}
else -> this
}
}
var hasVisibleBackingField = false
@@ -144,13 +144,13 @@ class VariableStorageImpl(private val session: FirSession) : VariableStorage() {
}
private fun FirBasedSymbol<*>.getStability(originalFir: FirElement): PropertyStability? {
if (originalFir is FirThisReceiverExpression) return PropertyStability.STABLE_VALUE
when (this) {
is FirAnonymousObjectSymbol -> return null
is FirFunctionSymbol<*>,
is FirClassSymbol<*>,
is FirBackingFieldSymbol -> return PropertyStability.STABLE_VALUE
}
if (originalFir is FirThisReceiverExpression) return PropertyStability.STABLE_VALUE
if (this !is FirVariableSymbol<*>) return null
if (this is FirFieldSymbol && !this.isFinal) return PropertyStability.MUTABLE_PROPERTY
@@ -38,6 +38,8 @@ abstract class FirSmartCastExpression : FirExpression() {
abstract override fun replaceTypeRef(newTypeRef: FirTypeRef)
abstract fun replaceOriginalExpression(newOriginalExpression: FirExpression)
abstract override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirSmartCastExpression
abstract fun <D> transformOriginalExpression(transformer: FirTransformer<D>, data: D): FirSmartCastExpression
@@ -84,3 +84,19 @@ inline fun buildThisReceiverExpression(init: FirThisReceiverExpressionBuilder.()
}
return FirThisReceiverExpressionBuilder().apply(init).build()
}
@OptIn(ExperimentalContracts::class)
inline fun buildThisReceiverExpressionCopy(original: FirThisReceiverExpression, init: FirThisReceiverExpressionBuilder.() -> Unit): FirThisReceiverExpression {
contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = FirThisReceiverExpressionBuilder()
copyBuilder.typeRef = original.typeRef
copyBuilder.annotations.addAll(original.annotations)
copyBuilder.contextReceiverArguments.addAll(original.contextReceiverArguments)
copyBuilder.typeArguments.addAll(original.typeArguments)
copyBuilder.source = original.source
copyBuilder.calleeReference = original.calleeReference
copyBuilder.isImplicit = original.isImplicit
return copyBuilder.apply(init).build()
}
@@ -69,4 +69,8 @@ internal class FirSmartCastExpressionImpl(
override fun replaceTypeRef(newTypeRef: FirTypeRef) {
typeRef = newTypeRef
}
override fun replaceOriginalExpression(newOriginalExpression: FirExpression) {
originalExpression = newOriginalExpression
}
}
@@ -257,6 +257,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
builder(thisReceiverExpression) {
parents += qualifiedAccessExpressionBuilder
default("isImplicit", "false")
withCopy()
}
builder(thisReference, "FirExplicitThisReference") {
@@ -562,7 +562,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
smartCastExpression.configure {
+typeRefField
+field("originalExpression", expression).withTransform()
+field("originalExpression", expression, withReplace = true).withTransform()
+field("typesFromSmartCast", "Collection<ConeKotlinType>", null, customType = coneKotlinTypeType)
+field("smartcastType", typeRef)
+field("smartcastTypeWithoutNullableNothing", typeRef, nullable = true)
@@ -20,5 +20,3 @@ suspend fun SequenceScope<Int>.awaitSeq(): Int = 42
// JVM_TEMPLATES
// 1 LINENUMBER 9 L18
// 1 LOCALVARIABLE a I L[0-9]+ L18
// IGNORE_BACKEND_K2: JVM_IR
@@ -1,5 +1,5 @@
// IGNORE_BACKEND_K2_LIGHT_TREE: JVM_IR
// Reason: KT-56755
// FILE: test.kt
fun blockFun(blockArg: String.() -> Unit) =
"OK".blockArg()
@@ -1,5 +1,5 @@
// IGNORE_BACKEND_K2_LIGHT_TREE: JVM_IR
// Reason: KT-56755
// FILE: test.kt
fun blockFun(blockArg: String.() -> Unit) =
"OK".blockArg()
@@ -1,5 +1,5 @@
// IGNORE_BACKEND_K2_LIGHT_TREE: JVM_IR
// Reason: KT-56755
// FILE: test.kt
fun blockFun(blockArg: String.() -> Unit) =
"OK".blockArg()
@@ -1,5 +1,5 @@
// IGNORE_BACKEND_K2_LIGHT_TREE: JVM_IR
// Reason: KT-56755
// FILE: test.kt
fun blockFun(blockArg: String.() -> Unit) =
"OK".blockArg()
+68
View File
@@ -0,0 +1,68 @@
// FILE: test.kt
fun box() {
A().test()
}
class A {
fun test() {
//Breakpoint!
foo()
prop
prop = 2
}
companion object {
private fun foo() {
val a = 1
}
private var prop: Int = 2
get() {
return 1
}
set(i: Int) {
field = i
}
}
}
// EXPECTATIONS JVM JVM_IR
// test.kt:4 box
// test.kt:20 <clinit>
// test.kt:15 <init>
// test.kt:20 <clinit>
// test.kt:20 <clinit>
// test.kt:4 box
// test.kt:7 <init>
// test.kt:4 box
// test.kt:10 test
// test.kt:17 foo
// test.kt:18 foo
// test.kt:11 test
// test.kt:22 getProp
// test.kt:11 test
// test.kt:12 test
// test.kt:25 setProp
// test.kt:26 setProp
// test.kt:13 test
// test.kt:5 box
// EXPECTATIONS JS_IR
// test.kt:4 box
// test.kt:7 <init>
// test.kt:20 <init>
// test.kt:15 <init>
// test.kt:7 <init>
// test.kt:4 box
// test.kt:10 test
// test.kt:10 test
// test.kt:17 foo
// test.kt:18 foo
// test.kt:11 test
// test.kt:11 test
// test.kt:22 <get-prop>
// test.kt:12 test
// test.kt:13 test
// test.kt:5 box
@@ -0,0 +1,45 @@
// FILE: test.kt
fun box() {
test(B(A()))
}
class A
class B(val a: A) {
operator fun A.invoke() {}
}
fun test(b: B) {
with(b) {
a()
}
}
// EXPECTATIONS JVM JVM_IR
// test.kt:4 box
// test.kt:7 <init>
// test.kt:4 box
// test.kt:9 <init>
// test.kt:4 box
// test.kt:14 test
// test.kt:15 test
// test.kt:9 getA
// test.kt:15 test
// test.kt:10 invoke
// test.kt:16 test
// test.kt:14 test
// test.kt:17 test
// test.kt:5 box
// EXPECTATIONS JS_IR
// test.kt:4 box
// test.kt:7 <init>
// test.kt:4 box
// test.kt:9 <init>
// test.kt:9 <init>
// test.kt:4 box
// test.kt:10 invoke
// test.kt:17 test
// test.kt:5 box
@@ -247,6 +247,18 @@ public class IrSteppingWithBytecodeInlinerTestGenerated extends AbstractIrSteppi
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test
@TestMetadata("implicitThis.kt")
public void testImplicitThis() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThis.kt");
}
@Test
@TestMetadata("implicitThisOnInvoke.kt")
public void testImplicitThisOnInvoke() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThisOnInvoke.kt");
}
@Test
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
@@ -247,6 +247,18 @@ public class IrSteppingWithIrInlinerTestGenerated extends AbstractIrSteppingWith
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test
@TestMetadata("implicitThis.kt")
public void testImplicitThis() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThis.kt");
}
@Test
@TestMetadata("implicitThisOnInvoke.kt")
public void testImplicitThisOnInvoke() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThisOnInvoke.kt");
}
@Test
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
@@ -247,6 +247,18 @@ public class SteppingTestGenerated extends AbstractSteppingTest {
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test
@TestMetadata("implicitThis.kt")
public void testImplicitThis() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThis.kt");
}
@Test
@TestMetadata("implicitThisOnInvoke.kt")
public void testImplicitThisOnInvoke() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThisOnInvoke.kt");
}
@Test
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
@@ -247,6 +247,18 @@ public class IrJsSteppingTestGenerated extends AbstractIrJsSteppingTest {
runTest("compiler/testData/debug/stepping/iincStepping.kt");
}
@Test
@TestMetadata("implicitThis.kt")
public void testImplicitThis() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThis.kt");
}
@Test
@TestMetadata("implicitThisOnInvoke.kt")
public void testImplicitThisOnInvoke() throws Exception {
runTest("compiler/testData/debug/stepping/implicitThisOnInvoke.kt");
}
@Test
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {