FIR: Support callable references outside of the calls
^KT-32725 In Progress
This commit is contained in:
@@ -17,4 +17,8 @@ object SyntheticCallableId {
|
||||
FqName("_synthetic"),
|
||||
Name.identifier("TRY_CALL")
|
||||
)
|
||||
}
|
||||
val ID = CallableId(
|
||||
FqName("_synthetic"),
|
||||
Name.identifier("ID_CALL")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ class FirCallResolver(
|
||||
val callableReferenceAccess = resolvedCallableReferenceAtom.atom
|
||||
val lhs = resolvedCallableReferenceAtom.lhs
|
||||
val coneSubstitutor = constraintSystemBuilder.buildCurrentSubstitutor() as ConeSubstitutor
|
||||
val expectedType = resolvedCallableReferenceAtom.expectedType?.let(coneSubstitutor::substituteOrSelf) ?: return false
|
||||
val expectedType = resolvedCallableReferenceAtom.expectedType?.let(coneSubstitutor::substituteOrSelf)
|
||||
|
||||
val result = CandidateCollector(this, resolutionStageRunner)
|
||||
val consumer =
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.InferenceComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionStageRunner
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirSyntheticCallGenerator
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.phasedFir
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
@@ -35,6 +36,7 @@ interface BodyResolveComponents : SessionHolder {
|
||||
val samResolver: FirSamResolver
|
||||
val callResolver: FirCallResolver
|
||||
val doubleColonExpressionResolver: FirDoubleColonExpressionResolver
|
||||
val syntheticCallGenerator: FirSyntheticCallGenerator
|
||||
|
||||
val <D> AbstractFirBasedSymbol<D>.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner<D>
|
||||
get() = phasedFir(session, FirResolvePhase.DECLARATIONS)
|
||||
|
||||
@@ -20,6 +20,20 @@ sealed class CallKind {
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Used for resolution of synthetic calls for resolving callable references
|
||||
* beyond any calls like `val x = ::foo` (they're transformed to `val x = id(::foo)`)
|
||||
* It's needed to avoid having different callable references resolution parts within calls and beyond them
|
||||
*/
|
||||
object SyntheticIdForCallableReferencesResolution : CallKind() {
|
||||
override val resolutionSequence: List<ResolutionStage> = listOf(
|
||||
MapArguments,
|
||||
CreateFreshTypeVariableSubstitutorStage,
|
||||
CheckArguments,
|
||||
EagerResolveOfCallableReferences
|
||||
)
|
||||
}
|
||||
|
||||
object Function : CallKind() {
|
||||
override val resolutionSequence: List<ResolutionStage> = listOf(
|
||||
CheckVisibility,
|
||||
|
||||
@@ -175,7 +175,7 @@ internal object EagerResolveOfCallableReferences : CheckerStage() {
|
||||
internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
||||
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
|
||||
val outerCsBuilder = callInfo.outerCSBuilder ?: return
|
||||
val expectedType = callInfo.expectedType ?: return
|
||||
val expectedType = callInfo.expectedType
|
||||
val candidateSymbol = candidate.symbol as? FirCallableSymbol<*> ?: return
|
||||
|
||||
val resultingReceiverType = when (callInfo.lhs) {
|
||||
@@ -201,7 +201,9 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
||||
|
||||
val position = SimpleConstraintSystemConstraintPosition //TODO
|
||||
|
||||
addSubtypeConstraint(resultingType, expectedType, position)
|
||||
if (expectedType != null) {
|
||||
addSubtypeConstraint(resultingType, expectedType, position)
|
||||
}
|
||||
|
||||
val declarationReceiverType: ConeKotlinType? =
|
||||
(fir as? FirCallableMemberDeclaration<*>)?.receiverTypeRef?.coneTypeSafe<ConeKotlinType>()
|
||||
|
||||
+30
-7
@@ -14,12 +14,15 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirTypeParameterImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl
|
||||
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirTryExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirFunctionCallImpl
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirStubReference
|
||||
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
@@ -35,9 +38,13 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
class FirSyntheticCallGenerator(private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents) : BodyResolveComponents by components {
|
||||
class FirSyntheticCallGenerator(
|
||||
private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
|
||||
private val callCompleter: FirCallCompleter
|
||||
) : BodyResolveComponents by components {
|
||||
private val whenSelectFunction: FirSimpleFunctionImpl = generateSyntheticSelectFunction(SyntheticCallableId.WHEN)
|
||||
private val trySelectFunction: FirSimpleFunctionImpl = generateSyntheticSelectFunction(SyntheticCallableId.TRY)
|
||||
private val idFunction: FirSimpleFunctionImpl = generateSyntheticSelectFunction(SyntheticCallableId.ID)
|
||||
|
||||
fun generateCalleeForWhenExpression(whenExpression: FirWhenExpression): FirWhenExpression? {
|
||||
val stubReference = whenExpression.calleeReference
|
||||
@@ -77,12 +84,28 @@ class FirSyntheticCallGenerator(private val components: FirAbstractBodyResolveTr
|
||||
return tryExpression.copy(calleeReference = reference)
|
||||
}
|
||||
|
||||
fun resolveCallableReferenceWithSyntheticOuterCall(
|
||||
callableReferenceAccess: FirCallableReferenceAccess,
|
||||
expectedTypeRef: FirTypeRef?
|
||||
): FirCallableReferenceAccess? {
|
||||
val arguments = listOf(callableReferenceAccess)
|
||||
|
||||
val reference =
|
||||
generateCalleeReferenceWithCandidate(
|
||||
idFunction, arguments, SyntheticCallableId.ID.callableName, CallKind.SyntheticIdForCallableReferencesResolution
|
||||
) ?: return null
|
||||
val fakeCallElement = FirFunctionCallImpl(null).copy(calleeReference = reference, arguments = arguments)
|
||||
|
||||
return callCompleter.completeCall(fakeCallElement, expectedTypeRef).arguments[0] as FirCallableReferenceAccess?
|
||||
}
|
||||
|
||||
private fun generateCalleeReferenceWithCandidate(
|
||||
function: FirSimpleFunctionImpl,
|
||||
arguments: List<FirExpression>,
|
||||
name: Name
|
||||
name: Name,
|
||||
callKind: CallKind = CallKind.SyntheticSelect
|
||||
): FirNamedReferenceWithCandidate? {
|
||||
val callInfo = generateCallInfo(arguments)
|
||||
val callInfo = generateCallInfo(arguments, callKind)
|
||||
val candidate = generateCandidate(callInfo, function)
|
||||
val applicability = resolutionStageRunner.processCandidate(candidate)
|
||||
if (applicability <= CandidateApplicability.INAPPLICABLE) {
|
||||
@@ -100,8 +123,8 @@ class FirSyntheticCallGenerator(private val components: FirAbstractBodyResolveTr
|
||||
explicitReceiverKind = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER
|
||||
)
|
||||
|
||||
private fun generateCallInfo(arguments: List<FirExpression>) = CallInfo(
|
||||
callKind = CallKind.SyntheticSelect,
|
||||
private fun generateCallInfo(arguments: List<FirExpression>, callKind: CallKind) = CallInfo(
|
||||
callKind = callKind,
|
||||
explicitReceiver = null,
|
||||
arguments = arguments,
|
||||
isSafeCall = false,
|
||||
@@ -111,7 +134,7 @@ class FirSyntheticCallGenerator(private val components: FirAbstractBodyResolveTr
|
||||
container = container
|
||||
) { it.resultType }
|
||||
|
||||
private fun generateSyntheticSelectFunction(callableId: CallableId): FirSimpleFunctionImpl {
|
||||
private fun generateSyntheticSelectFunction(callableId: CallableId, isVararg: Boolean = true): FirSimpleFunctionImpl {
|
||||
val functionSymbol = FirSyntheticFunctionSymbol(callableId)
|
||||
val typeParameterSymbol = FirTypeParameterSymbol()
|
||||
val typeParameter = FirTypeParameterImpl(null, session, Name.identifier("K"), typeParameterSymbol, Variance.INVARIANT, false)
|
||||
@@ -123,7 +146,7 @@ class FirSyntheticCallGenerator(private val components: FirAbstractBodyResolveTr
|
||||
|
||||
return generateMemberFunction(session, functionSymbol, callableId.callableName, typeArgument.typeRef).apply {
|
||||
typeParameters += typeParameter
|
||||
valueParameters += argumentType.toValueParameter(session, "branches", isVararg = true)
|
||||
valueParameters += argumentType.toValueParameter(session, "branches", isVararg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -98,6 +98,7 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
|
||||
override val samResolver: FirSamResolver = FirSamResolverImpl(session, scopeSession)
|
||||
val callCompleter: FirCallCompleter = FirCallCompleter(transformer, this)
|
||||
val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzer(this)
|
||||
override val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this, callCompleter)
|
||||
|
||||
internal var _container: FirDeclaration? = null
|
||||
override var container: FirDeclaration
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTransformer) :
|
||||
FirPartialBodyResolveTransformer(transformer) {
|
||||
|
||||
private val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(components)
|
||||
private val syntheticCallGenerator: FirSyntheticCallGenerator get() = components.syntheticCallGenerator
|
||||
private val whenExhaustivenessTransformer = FirWhenExhaustivenessTransformer(components)
|
||||
|
||||
|
||||
|
||||
+9
@@ -266,6 +266,15 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
|
||||
else
|
||||
callableReferenceAccess
|
||||
|
||||
if (data != null) {
|
||||
val resolvedReference =
|
||||
components.syntheticCallGenerator.resolveCallableReferenceWithSyntheticOuterCall(
|
||||
callableReferenceAccess, data as? FirTypeRef
|
||||
) ?: callableReferenceAccess
|
||||
|
||||
return resolvedReference.compose()
|
||||
}
|
||||
|
||||
return callableReferenceAccessWithTransformedLHS.compose()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
fun bar(x: String): Int = 1
|
||||
fun bar(x: String): Double = 1
|
||||
|
||||
fun baz(x: String): Int = 1
|
||||
fun <T, R> foobaz(x: T): R = TODO()
|
||||
|
||||
fun foo() {
|
||||
val x: (String) -> Int = ::bar
|
||||
val y = ::bar
|
||||
val z = ::baz
|
||||
val w: (String) -> Int = ::foobaz
|
||||
|
||||
::baz
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
FILE: beyoundCalls.kt
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
^bar Int(1)
|
||||
}
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/Double| {
|
||||
^bar Int(1)
|
||||
}
|
||||
public final fun baz(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
^baz Int(1)
|
||||
}
|
||||
public final fun <T, R> foobaz(x: R|T|): R|R| {
|
||||
^foobaz R|kotlin/TODO|()
|
||||
}
|
||||
public final fun foo(): R|kotlin/Unit| {
|
||||
lval x: R|kotlin/Function1<kotlin/String, kotlin/Int>| = ::R|/bar|
|
||||
lval y: <ERROR TYPE REF: No result type for initializer> = ::<Unresolved reference: bar>#
|
||||
lval z: R|kotlin/reflect/KFunction1<kotlin/String, kotlin/Int>| = ::R|/baz|
|
||||
lval w: R|kotlin/Function1<kotlin/String, kotlin/Int>| = ::R|/foobaz<kotlin/String, kotlin/Int>|
|
||||
::R|/baz|
|
||||
}
|
||||
+7
-7
@@ -8,13 +8,13 @@ FILE: moreSpecificAmbiguousExtensions.kt
|
||||
public final fun R|IB|.extFun(x: R|IA|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
lval extFun1: <ERROR TYPE REF: No result type for initializer> = Q|IA|::extFun#
|
||||
lval extFun2: <ERROR TYPE REF: No result type for initializer> = Q|IB|::extFun#
|
||||
lval extFun1: R|kotlin/reflect/KFunction2<IA, IB, kotlin/Unit>| = Q|IA|::R|/extFun|
|
||||
lval extFun2: R|kotlin/reflect/KFunction2<IB, IB, kotlin/Unit>| = Q|IB|::R|/extFun|
|
||||
}
|
||||
public final fun testWithExpectedType(): R|kotlin/Unit| {
|
||||
lval extFun_AB_A: R|kotlin/Function2<IA, IB, kotlin/Unit>| = Q|IA|::extFun#
|
||||
lval extFun_AA_B: R|kotlin/Function2<IA, IA, kotlin/Unit>| = Q|IB|::extFun#
|
||||
lval extFun_BB_A: R|kotlin/Function2<IB, IB, kotlin/Unit>| = Q|IA|::extFun#
|
||||
lval extFun_BA_B: R|kotlin/Function2<IB, IA, kotlin/Unit>| = Q|IB|::extFun#
|
||||
lval extFun_BB_B: R|kotlin/Function2<IB, IB, kotlin/Unit>| = Q|IB|::extFun#
|
||||
lval extFun_AB_A: R|kotlin/Function2<IA, IB, kotlin/Unit>| = Q|IA|::R|/extFun|
|
||||
lval extFun_AA_B: R|kotlin/Function2<IA, IA, kotlin/Unit>| = Q|IB|::R|/extFun|
|
||||
lval extFun_BB_A: R|kotlin/Function2<IB, IB, kotlin/Unit>| = Q|IA|::R|/extFun|
|
||||
lval extFun_BA_B: R|kotlin/Function2<IB, IA, kotlin/Unit>| = Q|IB|::R|/extFun|
|
||||
lval extFun_BB_B: R|kotlin/Function2<IB, IB, kotlin/Unit>| = Q|IB|::R|/extFun|
|
||||
}
|
||||
|
||||
+5
@@ -40,6 +40,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/diagnostics/callableReferences"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("beyoundCalls.kt")
|
||||
public void testBeyoundCalls() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("companions.kt")
|
||||
public void testCompanions() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/companions.kt");
|
||||
|
||||
Reference in New Issue
Block a user