FIR: Support callable references outside of the calls

^KT-32725 In Progress
This commit is contained in:
Denis Zharkov
2019-10-23 14:10:30 +03:00
parent 152236d84a
commit fe8cd195aa
13 changed files with 113 additions and 19 deletions
@@ -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>()
@@ -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)
}
}
@@ -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
@@ -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)
@@ -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
}
@@ -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|
}
@@ -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|
}
@@ -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");