[FIR/NI] Refactor type variable gathering from lambda types

Motivation:
- drop getArguments from type context as a duplicate of getArgumentList
- reduce the number of collection allocations in getAllDeeplyRelatedTypeVariables

Additional minor improvements, test data fixes
This commit is contained in:
Pavel Kirpichenkov
2020-10-05 19:31:52 +03:00
parent ef44077cb7
commit 39a87435ee
14 changed files with 77 additions and 56 deletions
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstrain
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemUtilContext
import org.jetbrains.kotlin.resolve.calls.inference.components.PostponedArgumentInputTypesResolver
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
@@ -56,7 +57,8 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
return ConeFixVariableConstraintPosition(variable) as FixVariableConstraintPosition<T>
}
override fun extractParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<ConeKotlinType?>? {
@OptIn(ExperimentalStdlibApi::class)
override fun extractLambdaParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<ConeKotlinType?>? {
require(declaration is PostponedResolvedAtom)
return when (declaration) {
is LambdaWithTypeVariableAsExpectedTypeAtom -> {
@@ -66,7 +68,7 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
atom.collectDeclaredValueParameterTypes()
else null
} else { // function expression - all types are explicit, shouldn't return null
mutableListOf<ConeKotlinType?>().apply {
buildList {
atom.receiverTypeRef?.coneType?.let { add(it) }
addAll(atom.collectDeclaredValueParameterTypes())
}
@@ -77,11 +79,7 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
}
private fun FirAnonymousFunction.collectDeclaredValueParameterTypes(): List<ConeKotlinType?> =
valueParameters.map {
if (it.returnTypeRef !is FirImplicitTypeRef)
it.returnTypeRef.coneType
else null
}
valueParameters.map { it.returnTypeRef.coneTypeSafe() }
override fun PostponedAtomWithRevisableExpectedType.isAnonymousFunction(): Boolean {
require(this is PostponedResolvedAtom)
@@ -102,7 +100,7 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
index: Int
): TypeVariableMarker {
return ConeTypeVariableForPostponedAtom(
"${PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE}$index"
PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE + index
)
}
@@ -111,7 +109,7 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
index: Int
): TypeVariableMarker {
return ConeTypeVariableForPostponedAtom(
"${PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE}$index"
PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE + index
)
}
@@ -158,10 +158,8 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) {
argument.revisedExpectedType?.takeIf { it.isFunctionOrKFunctionWithAnySuspendability() }?.cast() ?: return false
when (argument) {
is ResolvedCallableReferenceAtom -> {
is ResolvedCallableReferenceAtom ->
argument.reviseExpectedType(revisedExpectedType)
}
is LambdaWithTypeVariableAsExpectedTypeAtom ->
argument.transformToResolvedLambda(c.getBuilder(), resolutionContext, revisedExpectedType, null /*TODO()*/)
else -> throw IllegalStateException("Unsupported postponed argument type of $argument")
@@ -188,7 +186,7 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) {
}
// Avoiding smart cast from filterIsInstanceOrNull looks dirty
private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List<PostponedResolvedAtom>) =
private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List<PostponedResolvedAtom>): PostponedResolvedAtom? =
postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType }
private fun ConstraintSystemCompletionContext.fixVariablesOrReportNotEnoughInformation(
@@ -263,9 +261,14 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents) {
// TODO: non-top-level variables?
fun PostponedAtomWithRevisableExpectedType.collectNotFixedVariables() {
revisedExpectedType?.getArguments()?.map { it.getType().typeConstructor() }
?.filterIsInstance<ConeTypeVariableTypeConstructor>().orEmpty()
.mapNotNullTo(result) { it.takeIf { it in notFixedTypeVariables } }
revisedExpectedType?.lowerBoundIfFlexible()?.asArgumentList()?.let { typeArgumentList ->
for (typeArgument in typeArgumentList) {
val constructor = typeArgument.getType().typeConstructor()
if (constructor in notFixedTypeVariables) {
result.add(constructor)
}
}
}
}
fun FirStatement.collectAllTypeVariables() {
@@ -214,8 +214,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean {
require(this is ConeKotlinType)
return this.isTypeOrSubtypeOf {
it.lowerBoundIfFlexible().safeAs<ConeKotlinType>()?.isBuiltinFunctionalType(session)
?: error("Unexpected lower bound for type: ${it.render()}")
(it.lowerBoundIfFlexible() as ConeKotlinType).isBuiltinFunctionalType(session)
}
}
@@ -402,7 +401,8 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
this,
{
// FIXME supertypes of type constructor contain unsubstituted arguments
it.typeConstructor().supertypes().cast<Collection<ConeKotlinType>>()
@Suppress("UNCHECKED_CAST")
it.typeConstructor().supertypes() as Collection<ConeKotlinType>
},
DFS.VisitedWithSet(),
object : DFS.AbstractNodeHandler<ConeKotlinType, Boolean>() {
@@ -423,8 +423,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun KotlinTypeMarker.isSuspendFunctionTypeOrSubtype(): Boolean {
require(this is ConeKotlinType)
return isTypeOrSubtypeOf {
it.lowerBoundIfFlexible().safeAs<ConeKotlinType>()?.isSuspendFunctionType(session)
?: error("Unexpected lower bound for type: ${it.render()}")
(it.lowerBoundIfFlexible() as ConeKotlinType).isSuspendFunctionType(session)
}
}
@@ -433,7 +432,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return this.lowerBoundIfFlexible().safeAs<ConeKotlinType>()?.isExtensionFunctionType(session) == true
}
@ExperimentalStdlibApi
@OptIn(ExperimentalStdlibApi::class)
override fun KotlinTypeMarker.extractArgumentsForFunctionalTypeOrSubtype(): List<KotlinTypeMarker> {
val builtInFunctionalType = getFunctionalTypeFromSupertypes().cast<ConeKotlinType>()
return buildList {
@@ -452,7 +451,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return fullyExpandedType(session).let {
val simpleType = it.lowerBoundIfFlexible()
if (simpleType.safeAs<ConeKotlinType>()?.isBuiltinFunctionalType(session) == true)
if ((simpleType as ConeKotlinType).isBuiltinFunctionalType(session))
this
else {
var functionalSupertype: KotlinTypeMarker? = null
@@ -160,12 +160,6 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
?: session.builtinTypes.anyType.type//StandardClassIds.Any(session.firSymbolProvider).constructType(emptyArray(), false) // TODO wtf
}
override fun KotlinTypeMarker.getArguments(): List<TypeArgumentMarker> {
require(this is ConeKotlinType)
return this.typeArguments.toList()
}
override fun KotlinTypeMarker.asTypeArgument(): TypeArgumentMarker {
require(this is ConeKotlinType)
@@ -94,12 +94,6 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
else -> error("Type $this has no arguments")
}
override fun KotlinTypeMarker.getArguments(): List<TypeArgumentMarker> =
when (this) {
is IrSimpleType -> arguments
else -> error("Type $this has no arguments")
}
override fun KotlinTypeMarker.asTypeArgument() = this as IrTypeArgument
override fun CapturedTypeMarker.lowerType(): KotlinTypeMarker? = error("Captured Type is not valid for IrTypes")
@@ -27,7 +27,7 @@ interface ConstraintSystemUtilContext {
// PostponedArgumentInputTypesResolver
fun <T> createArgumentConstraintPosition(argument: T): ArgumentConstraintPosition<T>
fun <T> createFixVariableConstraintPosition(variable: TypeVariableMarker, atom: T): FixVariableConstraintPosition<T>
fun extractParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<KotlinTypeMarker?>?
fun extractLambdaParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<KotlinTypeMarker?>?
fun PostponedAtomWithRevisableExpectedType.isAnonymousFunction(): Boolean
fun PostponedAtomWithRevisableExpectedType.isFunctionExpressionWithReceiver(): Boolean
fun createTypeVariableForLambdaReturnType(): TypeVariableMarker
@@ -332,7 +332,7 @@ class PostponedArgumentInputTypesResolver(
for (argument in postponedArguments) {
if (argument !is LambdaWithTypeVariableAsExpectedTypeMarker) continue
if (argument.parameterTypesFromDeclaration != null) continue
argument.updateParameterTypesFromDeclaration(extractParameterTypesFromDeclaration(argument))
argument.updateParameterTypesFromDeclaration(extractLambdaParameterTypesFromDeclaration(argument))
}
return postponedArguments.any { argument ->
@@ -359,21 +359,33 @@ class PostponedArgumentInputTypesResolver(
private fun Context.getAllDeeplyRelatedTypeVariables(
type: KotlinTypeMarker,
variableDependencyProvider: TypeVariableDependencyInformationProvider
): List<TypeVariableTypeConstructorMarker> {
variableDependencyProvider: TypeVariableDependencyInformationProvider,
): Collection<TypeVariableTypeConstructorMarker> {
val collectedVariables = mutableSetOf<TypeVariableTypeConstructorMarker>()
getAllDeeplyRelatedTypeVariables(type, variableDependencyProvider, collectedVariables)
return collectedVariables
}
private fun Context.getAllDeeplyRelatedTypeVariables(
type: KotlinTypeMarker,
variableDependencyProvider: TypeVariableDependencyInformationProvider,
typeVariableCollector: MutableSet<TypeVariableTypeConstructorMarker>
) {
val typeConstructor = type.typeConstructor()
return when {
when {
typeConstructor is TypeVariableTypeConstructorMarker -> {
val relatedVariables = variableDependencyProvider.getDeeplyDependentVariables(typeConstructor).orEmpty()
listOf(typeConstructor) + relatedVariables.filterIsInstance<TypeVariableTypeConstructorMarker>()
typeVariableCollector.add(typeConstructor)
typeVariableCollector.addAll(relatedVariables.filterIsInstance<TypeVariableTypeConstructorMarker>())
}
type.argumentsCount() > 0 -> {
type.getArguments().flatMap {
if (it.isStarProjection()) emptyList() else getAllDeeplyRelatedTypeVariables(it.getType(), variableDependencyProvider)
for (typeArgument in type.lowerBoundIfFlexible().asArgumentList()) {
if (!typeArgument.isStarProjection()) {
getAllDeeplyRelatedTypeVariables(typeArgument.getType(), variableDependencyProvider, typeVariableCollector)
}
}
}
else -> emptyList()
}
}
@@ -63,7 +63,7 @@ class ClassicConstraintSystemUtilContext(
return FixVariableConstraintPositionImpl(variable, atom) as FixVariableConstraintPosition<T>
}
override fun extractParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<KotlinTypeMarker?>? {
override fun extractLambdaParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<KotlinTypeMarker?>? {
require(declaration is ResolvedAtom)
return when (val atom = declaration.atom) {
is FunctionExpression -> {
@@ -20,10 +20,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KotlinConstraintSystemCompleter(
private val resultTypeResolver: ResultTypeResolver,
val variableFixationFinder: VariableFixationFinder,
private val ctx: ConstraintSystemUtilContext,
private val postponedArgumentInputTypesResolver: PostponedArgumentInputTypesResolver,
) {
private val postponedArgumentInputTypesResolver = PostponedArgumentInputTypesResolver(resultTypeResolver, variableFixationFinder, ctx)
fun runCompletion(
c: ConstraintSystemCompletionContext,
completionMode: ConstraintSystemCompletionMode,
@@ -1,3 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
class Host(var value: String) {
@@ -0,0 +1,17 @@
// !LANGUAGE: -ExperimentalBuilderInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
interface Base
interface Controller<T> : Base {
suspend fun yield(t: T) {}
}
fun <S> generate(g: suspend Controller<S>.() -> Unit): S = TODO()
suspend fun Base.baseExtension() {}
val test1 = generate {
<!INAPPLICABLE_CANDIDATE!>yield<!>("foo")
baseExtension()
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: -ExperimentalBuilderInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
@@ -13,6 +12,6 @@ fun <S> generate(g: suspend Controller<S>.() -> Unit): S = TODO()
suspend fun Base.baseExtension() {}
val test1 = generate {
<!INAPPLICABLE_CANDIDATE!>yield<!>("foo")
yield("foo")
baseExtension()
}
@@ -246,7 +246,6 @@ interface TypeSystemContext : TypeSystemOptimizationContext {
fun KotlinTypeMarker.argumentsCount(): Int
fun KotlinTypeMarker.getArgument(index: Int): TypeArgumentMarker
fun KotlinTypeMarker.getArguments(): List<TypeArgumentMarker>
fun SimpleTypeMarker.getArgumentOrNull(index: Int): TypeArgumentMarker? {
if (index in 0 until argumentsCount()) return getArgument(index)
@@ -341,6 +340,18 @@ interface TypeSystemContext : TypeSystemOptimizationContext {
}
}
operator fun TypeArgumentListMarker.iterator() = object : Iterator<TypeArgumentMarker> {
private var argumentIndex: Int = 0
override fun hasNext(): Boolean = argumentIndex < size()
override fun next(): TypeArgumentMarker {
val argument = get(argumentIndex)
argumentIndex += 1
return argument
}
}
fun TypeConstructorMarker.isAnyConstructor(): Boolean
fun TypeConstructorMarker.isNothingConstructor(): Boolean
@@ -159,11 +159,6 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
return this.arguments[index]
}
override fun KotlinTypeMarker.getArguments(): List<TypeArgumentMarker> {
require(this is KotlinType, this::errorMessage)
return this.arguments
}
override fun TypeArgumentMarker.isStarProjection(): Boolean {
require(this is TypeProjection, this::errorMessage)
return this.isStarProjection