FIR: add a resolution mode for property delegates
Like function arguments, they are context-dependent, but unlike function arguments, callable references should be resolved eagerly as if they are explicit receivers.
This commit is contained in:
+10
-10
@@ -6,12 +6,12 @@
|
||||
package org.jetbrains.kotlin.fir.backend.generators
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.backend.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.dispatchReceiverClassOrNull
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.FirDelegateFieldReference
|
||||
import org.jetbrains.kotlin.fir.references.FirReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.psi.KtPropertyDelegate
|
||||
import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects
|
||||
import org.jetbrains.kotlin.types.AbstractTypeApproximator
|
||||
|
||||
@@ -59,11 +58,12 @@ class CallAndReferenceGenerator(
|
||||
val symbol =
|
||||
callableReferenceAccess.calleeReference.toSymbolForCall(session, classifierStorage, declarationStorage, conversionScope)
|
||||
val type = callableReferenceAccess.typeRef.toIrType()
|
||||
fun propertyOrigin(): IrStatementOrigin? =
|
||||
when (callableReferenceAccess.source?.psi?.parent) {
|
||||
is KtPropertyDelegate -> IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE
|
||||
else -> null
|
||||
}
|
||||
// val x by y ->
|
||||
// val `x$delegate` = y
|
||||
// val x get() = `x$delegate`.getValue(this, ::x)
|
||||
// The reference here (like the rest of the accessor) has DefaultAccessor source kind.
|
||||
val isForDelegate = callableReferenceAccess.source?.kind == FirFakeSourceElementKind.DefaultAccessor
|
||||
val origin = if (isForDelegate) IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE else null
|
||||
return callableReferenceAccess.convertWithOffsets { startOffset, endOffset ->
|
||||
when (symbol) {
|
||||
is IrPropertySymbol -> {
|
||||
@@ -82,7 +82,7 @@ class CallAndReferenceGenerator(
|
||||
field = backingFieldSymbol,
|
||||
getter = referencedPropertyGetter?.symbol,
|
||||
setter = referencedPropertySetterSymbol,
|
||||
propertyOrigin()
|
||||
origin = origin
|
||||
)
|
||||
}
|
||||
is IrLocalDelegatedPropertySymbol -> {
|
||||
@@ -91,7 +91,7 @@ class CallAndReferenceGenerator(
|
||||
delegate = symbol.owner.delegate.symbol,
|
||||
getter = symbol.owner.getter.symbol,
|
||||
setter = symbol.owner.setter?.symbol,
|
||||
IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE
|
||||
origin = origin
|
||||
)
|
||||
}
|
||||
is IrFieldSymbol -> {
|
||||
@@ -111,7 +111,7 @@ class CallAndReferenceGenerator(
|
||||
field = symbol,
|
||||
getter = if (referencedField.isStatic) null else propertySymbol.owner.getter?.symbol,
|
||||
setter = if (referencedField.isStatic) null else propertySymbol.owner.setter?.symbol,
|
||||
propertyOrigin()
|
||||
origin
|
||||
)
|
||||
}
|
||||
is IrConstructorSymbol -> {
|
||||
|
||||
Generated
+5
@@ -10028,6 +10028,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
+14
-13
@@ -292,6 +292,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
else -> null
|
||||
}
|
||||
val isMember = ownerSymbol != null
|
||||
val fakeSource = delegateBuilder.source?.fakeElement(FirFakeSourceElementKind.DefaultAccessor)
|
||||
|
||||
/*
|
||||
* If we have delegation with provide delegate then we generate call like
|
||||
@@ -310,7 +311,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
fun thisRef(isForDelegateProviderCall: Boolean = false): FirExpression =
|
||||
when {
|
||||
ownerSymbol != null -> buildThisReceiverExpression {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
calleeReference = buildImplicitThisReference {
|
||||
boundSymbol = ownerSymbol
|
||||
}
|
||||
@@ -320,7 +321,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
}
|
||||
}
|
||||
isExtension && !isForDelegateProviderCall -> buildThisReceiverExpression {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
calleeReference = buildImplicitThisReference {
|
||||
boundSymbol = this@generateAccessorsByDelegate.symbol
|
||||
}
|
||||
@@ -329,7 +330,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
}
|
||||
|
||||
fun delegateAccess() = buildQualifiedAccessExpression {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
calleeReference = buildDelegateFieldReference {
|
||||
resolvedSymbol = delegateFieldSymbol
|
||||
}
|
||||
@@ -340,9 +341,9 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
|
||||
val isVar = this@generateAccessorsByDelegate.isVar
|
||||
fun propertyRef() = buildCallableReferenceAccess {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
calleeReference = buildResolvedNamedReference {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
name = this@generateAccessorsByDelegate.name
|
||||
resolvedSymbol = this@generateAccessorsByDelegate.symbol
|
||||
}
|
||||
@@ -368,7 +369,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
delegateBuilder.delegateProvider = if (stubMode) buildExpressionStub() else buildFunctionCall {
|
||||
explicitReceiver = receiver
|
||||
calleeReference = buildSimpleNamedReference {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
name = PROVIDE_DELEGATE
|
||||
}
|
||||
argumentList = buildBinaryArgumentList(thisRef(isForDelegateProviderCall = true), propertyRef())
|
||||
@@ -379,7 +380,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
val annotations = getter?.annotations
|
||||
val returnTarget = FirFunctionTarget(null, isLambda = false)
|
||||
getter = buildPropertyAccessor {
|
||||
this.source = delegateBuilder.source
|
||||
this.source = fakeSource
|
||||
this.session = session
|
||||
origin = FirDeclarationOrigin.Source
|
||||
returnTypeRef = buildImplicitTypeRef()
|
||||
@@ -390,10 +391,10 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
body = FirSingleExpressionBlock(
|
||||
buildReturnExpression {
|
||||
result = buildFunctionCall {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
explicitReceiver = delegateAccess()
|
||||
calleeReference = buildSimpleNamedReference {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
name = GET_VALUE
|
||||
}
|
||||
argumentList = buildBinaryArgumentList(thisRef(), propertyRef())
|
||||
@@ -411,14 +412,14 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
if (isVar && (setter == null || setter is FirDefaultPropertyAccessor)) {
|
||||
val annotations = setter?.annotations
|
||||
setter = buildPropertyAccessor {
|
||||
this.source = delegateBuilder.source
|
||||
this.source = fakeSource
|
||||
this.session = session
|
||||
origin = FirDeclarationOrigin.Source
|
||||
returnTypeRef = session.builtinTypes.unitType
|
||||
isGetter = false
|
||||
status = FirDeclarationStatusImpl(Visibilities.Unknown, Modality.FINAL)
|
||||
val parameter = buildValueParameter {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
this.session = session
|
||||
origin = FirDeclarationOrigin.Source
|
||||
returnTypeRef = buildImplicitTypeRef()
|
||||
@@ -432,7 +433,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
symbol = FirPropertyAccessorSymbol()
|
||||
body = FirSingleExpressionBlock(
|
||||
buildFunctionCall {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
explicitReceiver = delegateAccess()
|
||||
calleeReference = buildSimpleNamedReference {
|
||||
name = SET_VALUE
|
||||
@@ -442,7 +443,7 @@ fun FirPropertyBuilder.generateAccessorsByDelegate(
|
||||
arguments += propertyRef()
|
||||
arguments += buildQualifiedAccessExpression {
|
||||
calleeReference = buildResolvedNamedReference {
|
||||
source = delegateBuilder.source
|
||||
source = fakeSource
|
||||
name = DELEGATED_SETTER_PARAM
|
||||
resolvedSymbol = parameter.symbol
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
|
||||
sealed class ResolutionMode {
|
||||
object ContextDependent : ResolutionMode()
|
||||
object ContextDependentDelegate : ResolutionMode()
|
||||
object ContextIndependent : ResolutionMode()
|
||||
// TODO: it's better not to use WithExpectedType(FirImplicitTypeRef)
|
||||
class WithExpectedType(val expectedTypeRef: FirTypeRef) : ResolutionMode()
|
||||
|
||||
+4
-4
@@ -192,7 +192,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
|
||||
}
|
||||
|
||||
private fun transformPropertyWithDelegate(property: FirProperty) {
|
||||
property.transformDelegate(transformer, ResolutionMode.ContextDependent)
|
||||
property.transformDelegate(transformer, ResolutionMode.ContextDependentDelegate)
|
||||
|
||||
val delegateExpression = property.delegate!!
|
||||
|
||||
@@ -230,7 +230,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
|
||||
): CompositeTransformResult<FirStatement> {
|
||||
dataFlowAnalyzer.enterDelegateExpression()
|
||||
try {
|
||||
val delegateProvider = wrappedDelegateExpression.delegateProvider.transformSingle(transformer, ResolutionMode.ContextDependent)
|
||||
val delegateProvider = wrappedDelegateExpression.delegateProvider.transformSingle(transformer, data)
|
||||
when (val calleeReference = (delegateProvider as FirResolvable).calleeReference) {
|
||||
is FirResolvedNamedReference -> return delegateProvider.compose()
|
||||
is FirNamedReferenceWithCandidate -> {
|
||||
@@ -243,7 +243,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
|
||||
|
||||
(delegateProvider as? FirFunctionCall)?.let { dataFlowAnalyzer.dropSubgraphFromCall(it) }
|
||||
return wrappedDelegateExpression.expression
|
||||
.transformSingle(transformer, ResolutionMode.ContextDependent)
|
||||
.transformSingle(transformer, data)
|
||||
.approximateIfIsIntegerConst()
|
||||
.compose()
|
||||
} finally {
|
||||
@@ -669,7 +669,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
|
||||
context.saveContextForAnonymousFunction(anonymousFunction)
|
||||
}
|
||||
return when (data) {
|
||||
ResolutionMode.ContextDependent -> {
|
||||
is ResolutionMode.ContextDependent, is ResolutionMode.ContextDependentDelegate -> {
|
||||
dataFlowAnalyzer.visitPostponedAnonymousFunction(anonymousFunction)
|
||||
anonymousFunction.addReturn().compose()
|
||||
}
|
||||
|
||||
+1
-2
@@ -33,7 +33,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.*
|
||||
import org.jetbrains.kotlin.fir.visitors.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
@@ -612,7 +611,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
||||
else
|
||||
callableReferenceAccess
|
||||
|
||||
if (data !is ResolutionMode.ContextDependent) {
|
||||
if (data !is ResolutionMode.ContextDependent /* ContextDependentDelegate is Ok here */) {
|
||||
val resolvedReference =
|
||||
components.syntheticCallGenerator.resolveCallableReferenceWithSyntheticOuterCall(
|
||||
callableReferenceAccess, data.expectedType, resolutionContext,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// WITH_REFLECT
|
||||
// WITH_RUNTIME
|
||||
class C(val x: String)
|
||||
|
||||
val x = "O"
|
||||
val y by ::x
|
||||
val z by C("K")::x
|
||||
|
||||
fun box(): String = y + z
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
Failures detected in FirBodyResolveTransformerAdapter, file: /provideDelegateOnFunctionalTypeWithThis.fir.kt
|
||||
Cause: java.lang.ClassCastException: org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImpl cannot be cast to org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
+1
-1
@@ -13,7 +13,7 @@ fun wrong(arg: Wrong) {}
|
||||
class Wrong
|
||||
|
||||
class Right {
|
||||
val prop: () -> Unit by <!DELEGATE_SPECIAL_FUNCTION_MISSING, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>::wrong<!>
|
||||
val prop: () -> Unit by <!UNRESOLVED_REFERENCE!>::wrong<!>
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+5
@@ -11428,6 +11428,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
+5
@@ -11433,6 +11433,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
+5
@@ -10028,6 +10028,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
Generated
+5
@@ -8543,6 +8543,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
Generated
+5
@@ -8543,6 +8543,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
+5
@@ -8543,6 +8543,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateForExtPropertyInClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateToAnother.kt")
|
||||
public void testDelegateToAnother() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateToAnother.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegateWithPrivateSet.kt")
|
||||
public void testDelegateWithPrivateSet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/delegatedProperty/delegateWithPrivateSet.kt");
|
||||
|
||||
Reference in New Issue
Block a user