FIR: detect extension function types by reference annotation

Later it should be replaced by cone type annotation analysis
This commit is contained in:
Simon Ogorodnik
2019-04-17 15:40:05 +03:00
committed by Mikhail Glukhikh
parent 3d73c28d1e
commit fda777ecae
4 changed files with 32 additions and 6 deletions
@@ -27,6 +27,7 @@ fun resolveArgumentExpression(
csBuilder: ConstraintSystemBuilder, csBuilder: ConstraintSystemBuilder,
argument: FirExpression, argument: FirExpression,
expectedType: ConeKotlinType, expectedType: ConeKotlinType,
expectedTypeRef: FirTypeRef,
sink: CheckerSink, sink: CheckerSink,
isReceiver: Boolean, isReceiver: Boolean,
acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit, acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit,
@@ -42,7 +43,7 @@ fun resolveArgumentExpression(
typeProvider typeProvider
) )
// TODO:! // TODO:!
is FirAnonymousFunction -> preprocessLambdaArgument(csBuilder, argument, expectedType, acceptLambdaAtoms) is FirAnonymousFunction -> preprocessLambdaArgument(csBuilder, argument, expectedType, expectedTypeRef, acceptLambdaAtoms)
// TODO:! // TODO:!
is FirCallableReferenceAccess -> Unit is FirCallableReferenceAccess -> Unit
// TODO:! // TODO:!
@@ -51,6 +52,7 @@ fun resolveArgumentExpression(
csBuilder, csBuilder,
argument.expression, argument.expression,
expectedType, expectedType,
expectedTypeRef,
sink, sink,
isReceiver, isReceiver,
acceptLambdaAtoms, acceptLambdaAtoms,
@@ -60,6 +62,7 @@ fun resolveArgumentExpression(
csBuilder, csBuilder,
argument.expression, argument.expression,
expectedType, expectedType,
expectedTypeRef,
sink, sink,
isReceiver, isReceiver,
acceptLambdaAtoms, acceptLambdaAtoms,
@@ -120,6 +123,7 @@ internal fun Candidate.resolveArgument(
this.system.getBuilder(), this.system.getBuilder(),
argument, argument,
expectedType, expectedType,
parameter.returnTypeRef,
sink, sink,
isReceiver, isReceiver,
{ this.postponedAtoms += it }, { this.postponedAtoms += it },
@@ -15,6 +15,7 @@ fun preprocessLambdaArgument(
csBuilder: ConstraintSystemBuilder, csBuilder: ConstraintSystemBuilder,
argument: FirAnonymousFunction, argument: FirAnonymousFunction,
expectedType: ConeKotlinType?, expectedType: ConeKotlinType?,
expectedTypeRef: FirTypeRef,
acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit, acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit,
forceResolution: Boolean = false forceResolution: Boolean = false
) { ) {
@@ -22,7 +23,9 @@ fun preprocessLambdaArgument(
//return LambdaWithTypeVariableAsExpectedTypeAtom(argument, expectedType) //return LambdaWithTypeVariableAsExpectedTypeAtom(argument, expectedType)
} }
val resolvedArgument = extractLambdaInfoFromFunctionalType(expectedType, argument) ?: extraLambdaInfo(expectedType, argument, csBuilder) val resolvedArgument =
extractLambdaInfoFromFunctionalType(expectedType, expectedTypeRef, argument)
?: extraLambdaInfo(expectedType, expectedTypeRef, argument, csBuilder)
if (expectedType != null) { if (expectedType != null) {
// val lambdaType = createFunctionType( // val lambdaType = createFunctionType(
@@ -58,7 +61,19 @@ private val ConeKotlinType.isSuspendFunctionType: Boolean
} }
} }
private val ConeKotlinType.receiverType: ConeKotlinType? get() = null private fun ConeKotlinType.receiverType(expectedTypeRef: FirTypeRef): ConeKotlinType? {
if (isFunctionalTypeWithReceiver(expectedTypeRef)) {
return (this.typeArguments.first() as ConeTypedProjection).type
}
return null
}
private fun isFunctionalTypeWithReceiver(typeRef: FirTypeRef) =
typeRef.annotations.any {
val coneTypeSafe = it.annotationTypeRef.coneTypeSafe<ConeClassType>() ?: return@any false
coneTypeSafe.lookupTag.classId.asString() == "kotlin/ExtensionFunctionType"
}
private val ConeKotlinType.returnType: ConeKotlinType private val ConeKotlinType.returnType: ConeKotlinType
get() { get() {
require(this is ConeClassType) require(this is ConeClassType)
@@ -81,6 +96,7 @@ private val FirAnonymousFunction.receiverType get() = receiverTypeRef?.coneTypeS
private fun extraLambdaInfo( private fun extraLambdaInfo(
expectedType: ConeKotlinType?, expectedType: ConeKotlinType?,
expectedTypeRef: FirTypeRef,
argument: FirAnonymousFunction, argument: FirAnonymousFunction,
csBuilder: ConstraintSystemBuilder csBuilder: ConstraintSystemBuilder
): ResolvedLambdaAtom { ): ResolvedLambdaAtom {
@@ -93,7 +109,7 @@ private fun extraLambdaInfo(
val typeVariable = TypeVariableForLambdaReturnType(argument, "_L") val typeVariable = TypeVariableForLambdaReturnType(argument, "_L")
val receiverType = null//argumentAsFunctionExpression?.receiverType val receiverType = argumentAsFunctionExpression?.receiverType
val returnType = val returnType =
argumentAsFunctionExpression?.returnType argumentAsFunctionExpression?.returnType
?: expectedType?.typeArguments?.singleOrNull()?.safeAs<ConeTypedProjection>()?.type?.takeIf { isFunctionSupertype } ?: expectedType?.typeArguments?.singleOrNull()?.safeAs<ConeTypedProjection>()?.type?.takeIf { isFunctionSupertype }
@@ -107,12 +123,16 @@ private fun extraLambdaInfo(
return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed }) return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed })
} }
private fun extractLambdaInfoFromFunctionalType(expectedType: ConeKotlinType?, argument: FirAnonymousFunction): ResolvedLambdaAtom? { private fun extractLambdaInfoFromFunctionalType(
expectedType: ConeKotlinType?,
expectedTypeRef: FirTypeRef,
argument: FirAnonymousFunction
): ResolvedLambdaAtom? {
if (expectedType == null || !expectedType.isBuiltinFunctionalType) return null if (expectedType == null || !expectedType.isBuiltinFunctionalType) return null
val parameters = extractLambdaParameters(expectedType, argument) val parameters = extractLambdaParameters(expectedType, argument)
val argumentAsFunctionExpression = argument//.safeAs<FunctionExpression>() val argumentAsFunctionExpression = argument//.safeAs<FunctionExpression>()
val receiverType = argumentAsFunctionExpression?.receiverType ?: expectedType.receiverType val receiverType = argumentAsFunctionExpression?.receiverType ?: expectedType.receiverType(expectedTypeRef)
val returnType = argumentAsFunctionExpression?.returnType ?: expectedType.returnType val returnType = argumentAsFunctionExpression?.returnType ?: expectedType.returnType
return ResolvedLambdaAtom( return ResolvedLambdaAtom(
@@ -106,6 +106,7 @@ class PostponedArgumentsAnalyzer(
c.getBuilder(), c.getBuilder(),
it, it,
lambda.returnType.let(::substitute), lambda.returnType.let(::substitute),
lambda.atom.returnTypeRef, // TODO: proper ref
checkerSink, checkerSink,
false, false,
{ atom = it }, { atom = it },
@@ -95,6 +95,7 @@ internal sealed class CheckReceivers : ResolutionStage() {
resolveArgumentExpression( resolveArgumentExpression(
candidate.csBuilder, explicitReceiverExpression, candidate.csBuilder, explicitReceiverExpression,
candidate.substitutor.substituteOrSelf(receiverParameterValue.type), candidate.substitutor.substituteOrSelf(receiverParameterValue.type),
explicitReceiverExpression.typeRef,
sink, isReceiver = true, typeProvider = callInfo.typeProvider, acceptLambdaAtoms = { candidate.postponedAtoms += it } sink, isReceiver = true, typeProvider = callInfo.typeProvider, acceptLambdaAtoms = { candidate.postponedAtoms += it }
) )
} }