[FIR] Organize code for postponed arguments

This commit is contained in:
Dmitriy Novozhilov
2020-03-06 16:21:20 +03:00
parent 1c0fd7342f
commit 92449d6d9b
3 changed files with 270 additions and 235 deletions
@@ -0,0 +1,117 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.isExtensionFunctionType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.types.*
fun ConeKotlinType.isBuiltinFunctionalType(session: FirSession): Boolean {
return when (this) {
is ConeClassLikeType -> fullyExpandedType(session).lookupTag.classId.asString().startsWith("kotlin/Function")
else -> false
}
}
fun ConeKotlinType.isSuspendFunctionType(session: FirSession): Boolean {
return when (val type = this) {
is ConeClassLikeType -> {
val classId = type.fullyExpandedType(session).lookupTag.classId
classId.packageFqName.asString() == "kotlin" && classId.relativeClassName.asString().startsWith("SuspendFunction")
}
else -> false
}
}
fun ConeKotlinType.receiverType(expectedTypeRef: FirTypeRef, session: FirSession): ConeKotlinType? {
if (isBuiltinFunctionalType(session) && expectedTypeRef.isExtensionFunctionType(session)) {
return ((this as ConeClassLikeType).fullyExpandedType(session).typeArguments.first() as ConeKotlinTypeProjection).type
}
return null
}
fun ConeKotlinType.returnType(session: FirSession): ConeKotlinType? {
require(this is ConeClassLikeType)
val projection = fullyExpandedType(session).typeArguments.last()
return (projection as? ConeKotlinTypeProjection)?.type
}
fun ConeKotlinType.valueParameterTypesIncludingReceiver(session: FirSession): List<ConeKotlinType?> {
require(this is ConeClassLikeType)
return fullyExpandedType(session).typeArguments.dropLast(1).map {
(it as? ConeKotlinTypeProjection)?.type
}
}
val FirAnonymousFunction.returnType get() = returnTypeRef.coneTypeSafe<ConeKotlinType>()
val FirAnonymousFunction.receiverType get() = receiverTypeRef?.coneTypeSafe<ConeKotlinType>()
fun extractLambdaInfoFromFunctionalType(
expectedType: ConeKotlinType?,
expectedTypeRef: FirTypeRef,
argument: FirAnonymousFunction,
returnTypeVariable: ConeTypeVariableForLambdaReturnType?,
components: BodyResolveComponents,
candidate: Candidate?
): ResolvedLambdaAtom? {
val session = components.session
if (expectedType == null) return null
if (expectedType is ConeFlexibleType) {
return extractLambdaInfoFromFunctionalType(expectedType.lowerBound, expectedTypeRef, argument, returnTypeVariable, components, candidate)
}
if (!expectedType.isBuiltinFunctionalType(session)) return null
val receiverType = argument.receiverType ?: expectedType.receiverType(expectedTypeRef, session)
val returnType = argument.returnType ?: expectedType.returnType(session) ?: return null
val parameters = extractLambdaParameters(expectedType, argument, expectedTypeRef.isExtensionFunctionType(session), session)
return ResolvedLambdaAtom(
argument,
expectedType,
expectedType.isSuspendFunctionType(session),
receiverType,
parameters,
returnType,
typeVariableForLambdaReturnType = returnTypeVariable,
candidate
)
}
private fun extractLambdaParameters(
expectedType: ConeKotlinType,
argument: FirAnonymousFunction,
expectedTypeIsExtensionFunctionType: Boolean,
session: FirSession
): List<ConeKotlinType> {
val parameters = argument.valueParameters
val expectedParameters = expectedType.extractParametersForFunctionalType(expectedTypeIsExtensionFunctionType, session)
val nullableAnyType = argument.session.builtinTypes.nullableAnyType.type
if (parameters.isEmpty()) {
return expectedParameters.map { it?.type ?: nullableAnyType }
}
return parameters.mapIndexed { index, parameter ->
parameter.returnTypeRef.coneTypeSafe() ?: expectedParameters.getOrNull(index) ?: nullableAnyType
}
}
private fun ConeKotlinType.extractParametersForFunctionalType(
isExtensionFunctionType: Boolean,
session: FirSession
): List<ConeKotlinType?> {
return valueParameterTypesIncludingReceiver(session).let {
if (isExtensionFunctionType) {
it.drop(1)
} else {
it
}
}
}
@@ -8,21 +8,11 @@ package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.CandidateApplicability
import org.jetbrains.kotlin.fir.resolve.calls.isExtensionFunctionType
import org.jetbrains.kotlin.fir.resolve.createFunctionalType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
fun Candidate.preprocessLambdaArgument(
@@ -58,47 +48,6 @@ fun Candidate.preprocessCallableReference(
postponedAtoms += ResolvedCallableReferenceAtom(argument, expectedType, lhs, bodyResolveComponents.session)
}
fun ConeKotlinType.isBuiltinFunctionalType(session: FirSession): Boolean {
return when (this) {
is ConeClassLikeType -> fullyExpandedType(session).lookupTag.classId.asString().startsWith("kotlin/Function")
else -> false
}
}
fun ConeKotlinType.isSuspendFunctionType(session: FirSession): Boolean {
return when (val type = this) {
is ConeClassLikeType -> {
val classId = type.fullyExpandedType(session).lookupTag.classId
classId.packageFqName.asString() == "kotlin" && classId.relativeClassName.asString().startsWith("SuspendFunction")
}
else -> false
}
}
fun ConeKotlinType.receiverType(expectedTypeRef: FirTypeRef, session: FirSession): ConeKotlinType? {
if (isBuiltinFunctionalType(session) && expectedTypeRef.isExtensionFunctionType(session)) {
return ((this as ConeClassLikeType).fullyExpandedType(session).typeArguments.first() as ConeKotlinTypeProjection).type
}
return null
}
fun ConeKotlinType.returnType(session: FirSession): ConeKotlinType? {
require(this is ConeClassLikeType)
val projection = fullyExpandedType(session).typeArguments.last()
return (projection as? ConeKotlinTypeProjection)?.type
}
private fun ConeKotlinType.valueParameterTypesIncludingReceiver(session: FirSession): List<ConeKotlinType?> {
require(this is ConeClassLikeType)
return fullyExpandedType(session).typeArguments.dropLast(1).map {
(it as? ConeKotlinTypeProjection)?.type
}
}
private val FirAnonymousFunction.returnType get() = returnTypeRef.coneTypeSafe<ConeKotlinType>()
private val FirAnonymousFunction.receiverType get() = receiverTypeRef?.coneTypeSafe<ConeKotlinType>()
private fun extraLambdaInfo(
expectedType: ConeKotlinType?,
argument: FirAnonymousFunction,
@@ -139,187 +88,3 @@ private fun extraLambdaInfo(
candidate
)
}
internal fun extractLambdaInfoFromFunctionalType(
expectedType: ConeKotlinType?,
expectedTypeRef: FirTypeRef,
argument: FirAnonymousFunction,
returnTypeVariable: ConeTypeVariableForLambdaReturnType?,
components: BodyResolveComponents,
candidate: Candidate?
): ResolvedLambdaAtom? {
val session = components.session
if (expectedType == null) return null
if (expectedType is ConeFlexibleType) {
return extractLambdaInfoFromFunctionalType(expectedType.lowerBound, expectedTypeRef, argument, returnTypeVariable, components, candidate)
}
if (!expectedType.isBuiltinFunctionalType(session)) return null
val receiverType = argument.receiverType ?: expectedType.receiverType(expectedTypeRef, session)
val returnType = argument.returnType ?: expectedType.returnType(session) ?: return null
val parameters = extractLambdaParameters(expectedType, argument, expectedTypeRef.isExtensionFunctionType(session), session)
return ResolvedLambdaAtom(
argument,
expectedType,
expectedType.isSuspendFunctionType(session),
receiverType,
parameters,
returnType,
typeVariableForLambdaReturnType = returnTypeVariable,
candidate
)
}
private fun extractLambdaParameters(
expectedType: ConeKotlinType,
argument: FirAnonymousFunction,
expectedTypeIsExtensionFunctionType: Boolean,
session: FirSession
): List<ConeKotlinType> {
val parameters = argument.valueParameters
val expectedParameters = expectedType.extractParametersForFunctionalType(expectedTypeIsExtensionFunctionType, session)
val nullableAnyType = argument.session.builtinTypes.nullableAnyType.type
if (parameters.isEmpty()) {
return expectedParameters.map { it?.type ?: nullableAnyType }
}
return parameters.mapIndexed { index, parameter ->
parameter.returnTypeRef.coneTypeSafe() ?: expectedParameters.getOrNull(index) ?: nullableAnyType
}
}
private fun ConeKotlinType.extractParametersForFunctionalType(
isExtensionFunctionType: Boolean,
session: FirSession
): List<ConeKotlinType?> {
return valueParameterTypesIncludingReceiver(session).let {
if (isExtensionFunctionType) {
it.drop(1)
} else {
it
}
}
}
class ConeTypeVariableForLambdaReturnType(val argument: FirAnonymousFunction, name: String) : ConeTypeVariable(name)
sealed class PostponedResolvedAtom : PostponedResolvedAtomMarker {
abstract override val inputTypes: Collection<ConeKotlinType>
abstract override val outputType: ConeKotlinType?
override var analyzed: Boolean = false
abstract val expectedType: ConeKotlinType?
}
class ResolvedLambdaAtom(
val atom: FirAnonymousFunction,
override val expectedType: ConeKotlinType?,
val isSuspend: Boolean,
val receiver: ConeKotlinType?,
val parameters: List<ConeKotlinType>,
val returnType: ConeKotlinType,
val typeVariableForLambdaReturnType: ConeTypeVariableForLambdaReturnType?,
val candidateForOuterCall: Candidate?
) : PostponedResolvedAtom() {
init {
candidateForOuterCall?.let {
it.postponedAtoms += this
}
}
lateinit var returnStatements: Collection<FirStatement>
override val inputTypes: Collection<ConeKotlinType> get() = receiver?.let { parameters + it } ?: parameters
override val outputType: ConeKotlinType get() = returnType
}
class LambdaWithTypeVariableAsExpectedTypeAtom(
val atom: FirAnonymousFunction,
override val expectedType: ConeKotlinType,
val expectedTypeRef: FirTypeRef,
val candidateForOuterCall: Candidate
) : PostponedResolvedAtom() {
init {
candidateForOuterCall.postponedAtoms += this
}
override val inputTypes: Collection<ConeKotlinType> get() = listOf(expectedType)
override val outputType: ConeKotlinType? get() = null
}
class ResolvedCallableReferenceAtom(
val reference: FirCallableReferenceAccess,
override val expectedType: ConeKotlinType?,
val lhs: DoubleColonLHS?,
private val session: FirSession
) : PostponedResolvedAtom() {
var postponed: Boolean = false
var resultingCandidate: Pair<Candidate, CandidateApplicability>? = null
override val inputTypes: Collection<ConeKotlinType>
get() {
if (!postponed) return emptyList()
return extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, session)?.inputTypes
?: listOfNotNull(expectedType)
}
override val outputType: ConeKotlinType?
get() {
if (!postponed) return null
return extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, session)?.outputType
}
}
private data class InputOutputTypes(val inputTypes: List<ConeKotlinType>, val outputType: ConeKotlinType)
private fun extractInputOutputTypesFromCallableReferenceExpectedType(
expectedType: ConeKotlinType?,
session: FirSession
): InputOutputTypes? {
if (expectedType == null) return null
return when {
expectedType.isBuiltinFunctionalType(session) || expectedType.isSuspendFunctionType(session) ->
extractInputOutputTypesFromFunctionType(expectedType, session)
// ReflectionTypes.isBaseTypeForNumberedReferenceTypes(expectedType) ->
// InputOutputTypes(emptyList(), expectedType.arguments.single().type.unwrap())
//
// ReflectionTypes.isNumberedKFunction(expectedType) -> {
// val functionFromSupertype = expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
// extractInputOutputTypesFromFunctionType(functionFromSupertype)
// }
//
// ReflectionTypes.isNumberedKSuspendFunction(expectedType) -> {
// val kSuspendFunctionType = expectedType.immediateSupertypes().first { it.isSuspendFunctionType }.unwrap()
// extractInputOutputTypesFromFunctionType(kSuspendFunctionType)
// }
//
// ReflectionTypes.isNumberedKPropertyOrKMutablePropertyType(expectedType) -> {
// val functionFromSupertype = expectedType.supertypes().first { it.isFunctionType }.unwrap()
// extractInputOutputTypesFromFunctionType(functionFromSupertype)
// }
else -> null
}
}
private fun extractInputOutputTypesFromFunctionType(
functionType: ConeKotlinType,
session: FirSession
): InputOutputTypes {
val parameters = functionType.valueParameterTypesIncludingReceiver(session).map {
it ?: ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(StandardClassIds.Nothing), emptyArray(),
isNullable = false
)
}
val outputType = functionType.returnType(session) ?: ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(StandardClassIds.Any), emptyArray(),
isNullable = true
)
return InputOutputTypes(parameters, outputType)
}
@@ -0,0 +1,153 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.CandidateApplicability
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
// --------------------------- Variables ---------------------------
class ConeTypeVariableForLambdaReturnType(val argument: FirAnonymousFunction, name: String) : ConeTypeVariable(name)
// -------------------------- Atoms --------------------------
sealed class PostponedResolvedAtom : PostponedResolvedAtomMarker {
abstract override val inputTypes: Collection<ConeKotlinType>
abstract override val outputType: ConeKotlinType?
override var analyzed: Boolean = false
abstract val expectedType: ConeKotlinType?
}
// ------------- Lambdas -------------
class ResolvedLambdaAtom(
val atom: FirAnonymousFunction,
override val expectedType: ConeKotlinType?,
val isSuspend: Boolean,
val receiver: ConeKotlinType?,
val parameters: List<ConeKotlinType>,
val returnType: ConeKotlinType,
val typeVariableForLambdaReturnType: ConeTypeVariableForLambdaReturnType?,
val candidateOfOuterCall: Candidate?
) : PostponedResolvedAtom() {
init {
candidateOfOuterCall?.let {
it.postponedAtoms += this
}
}
lateinit var returnStatements: Collection<FirStatement>
override val inputTypes: Collection<ConeKotlinType> get() = receiver?.let { parameters + it } ?: parameters
override val outputType: ConeKotlinType get() = returnType
}
class LambdaWithTypeVariableAsExpectedTypeAtom(
val atom: FirAnonymousFunction,
override val expectedType: ConeKotlinType,
val expectedTypeRef: FirTypeRef,
val candidateOfOuterCall: Candidate
) : PostponedResolvedAtom() {
init {
candidateOfOuterCall.postponedAtoms += this
}
override val inputTypes: Collection<ConeKotlinType> get() = listOf(expectedType)
override val outputType: ConeKotlinType? get() = null
}
// ------------- References -------------
class ResolvedCallableReferenceAtom(
val reference: FirCallableReferenceAccess,
override val expectedType: ConeKotlinType?,
val lhs: DoubleColonLHS?,
private val session: FirSession
) : PostponedResolvedAtom() {
var postponed: Boolean = false
var resultingCandidate: Pair<Candidate, CandidateApplicability>? = null
override val inputTypes: Collection<ConeKotlinType>
get() {
if (!postponed) return emptyList()
return extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, session)?.inputTypes
?: listOfNotNull(expectedType)
}
override val outputType: ConeKotlinType?
get() {
if (!postponed) return null
return extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, session)?.outputType
}
}
// -------------------------- Utils --------------------------
private data class InputOutputTypes(val inputTypes: List<ConeKotlinType>, val outputType: ConeKotlinType)
private fun extractInputOutputTypesFromCallableReferenceExpectedType(
expectedType: ConeKotlinType?,
session: FirSession
): InputOutputTypes? {
if (expectedType == null) return null
return when {
expectedType.isBuiltinFunctionalType(session) || expectedType.isSuspendFunctionType(session) ->
extractInputOutputTypesFromFunctionType(expectedType, session)
// ReflectionTypes.isBaseTypeForNumberedReferenceTypes(expectedType) ->
// InputOutputTypes(emptyList(), expectedType.arguments.single().type.unwrap())
//
// ReflectionTypes.isNumberedKFunction(expectedType) -> {
// val functionFromSupertype = expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
// extractInputOutputTypesFromFunctionType(functionFromSupertype)
// }
//
// ReflectionTypes.isNumberedKSuspendFunction(expectedType) -> {
// val kSuspendFunctionType = expectedType.immediateSupertypes().first { it.isSuspendFunctionType }.unwrap()
// extractInputOutputTypesFromFunctionType(kSuspendFunctionType)
// }
//
// ReflectionTypes.isNumberedKPropertyOrKMutablePropertyType(expectedType) -> {
// val functionFromSupertype = expectedType.supertypes().first { it.isFunctionType }.unwrap()
// extractInputOutputTypesFromFunctionType(functionFromSupertype)
// }
else -> null
}
}
private fun extractInputOutputTypesFromFunctionType(
functionType: ConeKotlinType,
session: FirSession
): InputOutputTypes {
val parameters = functionType.valueParameterTypesIncludingReceiver(session).map {
it ?: ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(StandardClassIds.Nothing), emptyArray(),
isNullable = false
)
}
val outputType = functionType.returnType(session) ?: ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(StandardClassIds.Any), emptyArray(),
isNullable = true
)
return InputOutputTypes(parameters, outputType)
}