Don't use builder inference if possible

The builder inference is running only if there are still uninferred type variables

^KT-48193 Fixed
This commit is contained in:
Victor Petukhov
2021-08-19 08:52:49 +03:00
committed by TeamCityServer
parent 3df5667a4b
commit 55811c8851
38 changed files with 408 additions and 543 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments
@@ -274,11 +275,15 @@ internal object PostponedVariablesInitializerResolutionPart : ResolutionPart() {
for ((argument, parameter) in resolvedCall.argumentToCandidateParameter) {
if (!callComponents.statelessCallbacks.isBuilderInferenceCall(argument, parameter)) continue
val receiverType = parameter.type.getReceiverTypeFromFunctionType() ?: continue
val dontUseBuilderInferenceIfPossible =
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)
if (argument is LambdaKotlinCallArgument && !argument.hasBuilderInferenceAnnotation) {
argument.hasBuilderInferenceAnnotation = true
}
if (dontUseBuilderInferenceIfPossible) continue
for (freshVariable in resolvedCall.freshVariablesSubstitutor.freshVariables) {
if (resolvedCall.typeArgumentMappingByOriginal.getTypeArgument(freshVariable.originalTypeParameter) is SimpleTypeArgument)
continue
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
@@ -21,6 +23,7 @@ class KotlinConstraintSystemCompleter(
private val resultTypeResolver: ResultTypeResolver,
val variableFixationFinder: VariableFixationFinder,
private val postponedArgumentInputTypesResolver: PostponedArgumentInputTypesResolver,
private val languageVersionSettings: LanguageVersionSettings
) {
fun runCompletion(
c: ConstraintSystemCompletionContext,
@@ -137,14 +140,19 @@ class KotlinConstraintSystemCompleter(
if (fixNextReadyVariable(completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments))
continue
// Stage 7: report "not enough information" for uninferred type variables
// Stage 7: try to complete call with the builder inference if there are uninferred type variables
val areThereAppearedProperConstraintsForSomeVariable =
tryToCompleteWithBuilderInference(completionMode, topLevelType, postponedArguments, analyze)
if (areThereAppearedProperConstraintsForSomeVariable)
continue
// Stage 8: report "not enough information" for uninferred type variables
reportNotEnoughTypeInformation(
completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments, diagnosticsHolder
)
if (wasFixedSomeVariable)
continue
// Stage 7: force analysis of remaining not analyzed postponed arguments and rerun stages if there are
// Stage 9: force analysis of remaining not analyzed postponed arguments and rerun stages if there are
if (completionMode == ConstraintSystemCompletionMode.FULL) {
if (analyzeRemainingNotAnalyzedPostponedArgument(postponedArguments, analyze))
continue
@@ -154,6 +162,47 @@ class KotlinConstraintSystemCompleter(
}
}
private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference(
completionMode: ConstraintSystemCompletionMode,
topLevelType: UnwrappedType,
postponedArguments: List<PostponedResolvedAtom>,
analyze: (PostponedResolvedAtom) -> Unit
): Boolean {
if (completionMode == ConstraintSystemCompletionMode.PARTIAL) return false
val useBuilderInferenceOnlyIfNeeded = languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)
// If we use the builder inference anyway (if the annotation is presented), then we are already analysed builder inference lambdas
if (!useBuilderInferenceOnlyIfNeeded) return false
val lambdaArguments = postponedArguments.filterIsInstance<ResolvedLambdaAtom>().takeIf { it.isNotEmpty() } ?: return false
return lambdaArguments.any { argument ->
if (!argument.atom.hasBuilderInferenceAnnotation)
return@any false
val notFixedInputTypeVariables = argument.inputTypes
.map { it.extractTypeVariables() }.flatten().filter { it !in fixedTypeVariables }
if (notFixedInputTypeVariables.isEmpty()) return@any false
for (variable in notFixedInputTypeVariables) {
getBuilder().markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable)
}
analyze(argument)
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
this, notFixedInputTypeVariables, postponedArguments, completionMode, topLevelType
)
// continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
// (after analysing a lambda with the builder inference)
// otherwise we report "not enough type information" error
return variableForFixation?.hasProperConstraint == true
}
}
private fun transformToAtomWithNewFunctionalExpectedType(
c: ConstraintSystemCompletionContext,
argument: PostponedAtomWithRevisableExpectedType,
@@ -179,10 +228,15 @@ class KotlinConstraintSystemCompleter(
postponedArguments: List<PostponedResolvedAtom>,
analyze: (PostponedResolvedAtom) -> Unit
): Boolean {
val argumentWithFixedOrPostponedInputTypes = findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments)
val useBuilderInferenceOnlyIfNeeded = languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)
val argumentToAnalyze = if (useBuilderInferenceOnlyIfNeeded) {
findPostponedArgumentWithFixedInputTypes(postponedArguments)
} else {
findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments)
}
if (argumentWithFixedOrPostponedInputTypes != null) {
analyze(argumentWithFixedOrPostponedInputTypes)
if (argumentToAnalyze != null) {
analyze(argumentToAnalyze)
return true
}
@@ -252,8 +306,12 @@ class KotlinConstraintSystemCompleter(
private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List<PostponedResolvedAtom>) =
postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType }
private fun ConstraintSystemCompletionContext.findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments: List<PostponedResolvedAtom>) =
postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } }
private fun ConstraintSystemCompletionContext.findPostponedArgumentWithFixedOrPostponedInputTypes(
postponedArguments: List<PostponedResolvedAtom>
) = postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } }
private fun ConstraintSystemCompletionContext.findPostponedArgumentWithFixedInputTypes(postponedArguments: List<PostponedResolvedAtom>) =
postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedVariables(it) } }
private fun fixVariable(
c: ConstraintSystemCompletionContext,