From fe8cd195aadddfd3911d4f2d12a60231b5a8e27a Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 23 Oct 2019 14:10:30 +0300 Subject: [PATCH] FIR: Support callable references outside of the calls ^KT-32725 In Progress --- .../kotlin/fir/symbols/SyntheticCallableId.kt | 6 ++- .../jetbrains/kotlin/fir/FirCallResolver.kt | 2 +- .../kotlin/fir/resolve/SessionHolder.kt | 2 + .../kotlin/fir/resolve/calls/CallKind.kt | 14 +++++++ .../kotlin/fir/resolve/calls/ResolverParts.kt | 6 ++- .../transformers/FirSyntheticCallGenerator.kt | 37 +++++++++++++++---- .../FirAbstractBodyResolveTransformer.kt | 1 + ...ControlFlowStatementsResolveTransformer.kt | 2 +- .../FirExpressionsResolveTransformer.kt | 9 +++++ .../callableReferences/beyoundCalls.kt | 14 +++++++ .../callableReferences/beyoundCalls.txt | 20 ++++++++++ .../moreSpecificAmbiguousExtensions.txt | 14 +++---- .../fir/FirDiagnosticsTestGenerated.java | 5 +++ 13 files changed, 113 insertions(+), 19 deletions(-) create mode 100644 compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.kt create mode 100644 compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.txt diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/SyntheticCallableId.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/SyntheticCallableId.kt index b3ea9eb1d35..2339af4cecd 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/SyntheticCallableId.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/SyntheticCallableId.kt @@ -17,4 +17,8 @@ object SyntheticCallableId { FqName("_synthetic"), Name.identifier("TRY_CALL") ) -} \ No newline at end of file + val ID = CallableId( + FqName("_synthetic"), + Name.identifier("ID_CALL") + ) +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index c6e03484f25..70e07ad4e4f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -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 = diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt index fb1c338b03a..488b0e076de 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt @@ -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 AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner get() = phasedFir(session, FirResolvePhase.DECLARATIONS) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt index 5a57e8abaf7..c20e9db3f2c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt @@ -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 = listOf( + MapArguments, + CreateFreshTypeVariableSubstitutorStage, + CheckArguments, + EagerResolveOfCallableReferences + ) + } + object Function : CallKind() { override val resolutionSequence: List = listOf( CheckVisibility, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index d147490ba66..0e9a400fbee 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -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() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt index 615a5a57daf..e5e8371dca8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSyntheticCallGenerator.kt @@ -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, - 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) = CallInfo( - callKind = CallKind.SyntheticSelect, + private fun generateCallInfo(arguments: List, 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) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt index 502b8339066..ef32bfeaec4 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt @@ -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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt index 262e89dce36..0c9df428c1d 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt @@ -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) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 3e65c36e836..1df382050a2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -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() } diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.kt b/compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.kt new file mode 100644 index 00000000000..79db3de80e7 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.kt @@ -0,0 +1,14 @@ +fun bar(x: String): Int = 1 +fun bar(x: String): Double = 1 + +fun baz(x: String): Int = 1 +fun foobaz(x: T): R = TODO() + +fun foo() { + val x: (String) -> Int = ::bar + val y = ::bar + val z = ::baz + val w: (String) -> Int = ::foobaz + + ::baz +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.txt new file mode 100644 index 00000000000..208d6f8e00c --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/beyoundCalls.txt @@ -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 foobaz(x: R|T|): R|R| { + ^foobaz R|kotlin/TODO|() + } + public final fun foo(): R|kotlin/Unit| { + lval x: R|kotlin/Function1| = ::R|/bar| + lval y: = ::# + lval z: R|kotlin/reflect/KFunction1| = ::R|/baz| + lval w: R|kotlin/Function1| = ::R|/foobaz| + ::R|/baz| + } diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/fromBasicDiagnosticTests/moreSpecificAmbiguousExtensions.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/fromBasicDiagnosticTests/moreSpecificAmbiguousExtensions.txt index 988fa56e898..f169da5f2b2 100644 --- a/compiler/fir/resolve/testData/diagnostics/callableReferences/fromBasicDiagnosticTests/moreSpecificAmbiguousExtensions.txt +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/fromBasicDiagnosticTests/moreSpecificAmbiguousExtensions.txt @@ -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: = Q|IA|::extFun# - lval extFun2: = Q|IB|::extFun# + lval extFun1: R|kotlin/reflect/KFunction2| = Q|IA|::R|/extFun| + lval extFun2: R|kotlin/reflect/KFunction2| = Q|IB|::R|/extFun| } public final fun testWithExpectedType(): R|kotlin/Unit| { - lval extFun_AB_A: R|kotlin/Function2| = Q|IA|::extFun# - lval extFun_AA_B: R|kotlin/Function2| = Q|IB|::extFun# - lval extFun_BB_A: R|kotlin/Function2| = Q|IA|::extFun# - lval extFun_BA_B: R|kotlin/Function2| = Q|IB|::extFun# - lval extFun_BB_B: R|kotlin/Function2| = Q|IB|::extFun# + lval extFun_AB_A: R|kotlin/Function2| = Q|IA|::R|/extFun| + lval extFun_AA_B: R|kotlin/Function2| = Q|IB|::R|/extFun| + lval extFun_BB_A: R|kotlin/Function2| = Q|IA|::R|/extFun| + lval extFun_BA_B: R|kotlin/Function2| = Q|IB|::R|/extFun| + lval extFun_BB_B: R|kotlin/Function2| = Q|IB|::R|/extFun| } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 037d3e8c7d5..bdfc615e658 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -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");