From 92449d6d9b37461f4076adeb07cb2c8e3f7b4989 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 6 Mar 2020 16:21:20 +0300 Subject: [PATCH] [FIR] Organize code for postponed arguments --- .../fir/resolve/inference/InferenceUtils.kt | 117 +++++++++ .../resolve/inference/PostponedArguments.kt | 235 ------------------ .../fir/resolve/inference/PostponedAtoms.kt | 153 ++++++++++++ 3 files changed, 270 insertions(+), 235 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedAtoms.kt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt new file mode 100644 index 00000000000..2eb5cae5267 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt @@ -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 { + require(this is ConeClassLikeType) + return fullyExpandedType(session).typeArguments.dropLast(1).map { + (it as? ConeKotlinTypeProjection)?.type + } +} + +val FirAnonymousFunction.returnType get() = returnTypeRef.coneTypeSafe() +val FirAnonymousFunction.receiverType get() = receiverTypeRef?.coneTypeSafe() + +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 { + 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 { + return valueParameterTypesIncludingReceiver(session).let { + if (isExtensionFunctionType) { + it.drop(1) + } else { + it + } + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt index f96d1cda940..2ca06d701fb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt @@ -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 { - require(this is ConeClassLikeType) - return fullyExpandedType(session).typeArguments.dropLast(1).map { - (it as? ConeKotlinTypeProjection)?.type - } -} - -private val FirAnonymousFunction.returnType get() = returnTypeRef.coneTypeSafe() -private val FirAnonymousFunction.receiverType get() = receiverTypeRef?.coneTypeSafe() - - 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 { - 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 { - 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 - 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, - val returnType: ConeKotlinType, - val typeVariableForLambdaReturnType: ConeTypeVariableForLambdaReturnType?, - val candidateForOuterCall: Candidate? -) : PostponedResolvedAtom() { - init { - candidateForOuterCall?.let { - it.postponedAtoms += this - } - } - - lateinit var returnStatements: Collection - - override val inputTypes: Collection 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 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? = null - - override val inputTypes: Collection - 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, 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) -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedAtoms.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedAtoms.kt new file mode 100644 index 00000000000..2493cb63a0e --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedAtoms.kt @@ -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 + 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, + val returnType: ConeKotlinType, + val typeVariableForLambdaReturnType: ConeTypeVariableForLambdaReturnType?, + val candidateOfOuterCall: Candidate? +) : PostponedResolvedAtom() { + init { + candidateOfOuterCall?.let { + it.postponedAtoms += this + } + } + + lateinit var returnStatements: Collection + + override val inputTypes: Collection 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 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? = null + + override val inputTypes: Collection + 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, 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) +} \ No newline at end of file