From e8be9d4861de2a8a425045140f1e58619e9f9aea Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 23 Dec 2021 12:16:01 +0300 Subject: [PATCH] FIR: support don't use builder inference if possible In this commit we upgrade FIR builder inference logic from the compiler version to 1.7. FIR-based compiler now works with "don't use builder inference" flag always ON and supports switching the flag "use builder inference only if needed". To do it, ContraintSystemCompleter (FIR) and KotlinConstraintSystemCompleter (FE 1.0) are made similar with extracting some common parts into ConstraintSystemCompletionContext. Test status: one BB test fails after this commit (KT-49285). Also we have a crush in DFA logic in FIR bootstrap test and somehow questionable behavior in FIR diagnostic test. However, two BB tests were fixed, the 3rd case from KT-49925 were also fixed. #KT-49925 Fixed --- .../resolveWithStdlib/complexPostponedCfg.dot | 1 + .../builderInferenceAndCoercionToUnit.fir.txt | 2 +- .../inference/problems/buildListLazy.fir.txt | 4 +- .../inference/problems/buildListLazy.kt | 2 +- .../fir/resolve/calls/ResolutionStages.kt | 3 + .../inference/ConstraintSystemCompleter.kt | 427 +++++++++--------- .../ConstraintSystemCompletionContext.kt | 102 ++++- .../model/NewConstraintSystemImpl.kt | 4 +- .../KotlinConstraintSystemCompleter.kt | 297 +++++------- .../box/inference/builderInference/kt42139.kt | 2 - .../builderInference/withoutAnnotation.kt | 2 - ...ambiguityWithBoundExtensionReceiver.fir.kt | 2 +- ...ingResolveIfDontUseBuilderInference.fir.kt | 6 +- .../inference/builderInference/kt49828.fir.kt | 2 +- ...thAnotherLambdaWithBuilderInference.fir.kt | 20 - ...llWithAnotherLambdaWithBuilderInference.kt | 1 + .../withoutAnnotationDisabledFeature.fir.kt | 4 +- .../diagnostics/tests/inference/kt6175.fir.kt | 8 +- .../inferCoroutineTypeInOldVersion.fir.kt | 29 ++ .../inferCoroutineTypeInOldVersion.kt | 1 - ...esolveUsualCallWithBuilderInference.fir.kt | 38 ++ .../resolveUsualCallWithBuilderInference.kt | 1 - ...ithBuilderInferenceWithRestrictions.fir.kt | 38 ++ ...allWithBuilderInferenceWithRestrictions.kt | 1 - .../inference/applyInsideCoroutine.fir.kt | 14 +- ...sionsWithNonValuableConstraints_1_2.fir.kt | 17 - ...xtensionsWithNonValuableConstraints_1_2.kt | 1 + .../inference/recursiveGenerators2.fir.kt | 23 + .../inference/recursiveGenerators2.kt | 1 - .../inference/suspendCallsWithErrors.fir.kt | 6 +- .../suspendCallsWrongUpperBound.fir.kt | 12 + .../inference/suspendCallsWrongUpperBound.kt | 1 - .../inference/withUninferredParameter.fir.kt | 20 + .../inference/withUninferredParameter.kt | 1 - .../castsInsideCoroutineInference.fir.ir.txt | 18 +- .../castsInsideCoroutineInference.fir.kt.txt | 2 +- 36 files changed, 638 insertions(+), 475 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.fir.kt diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot index f4efbcab670..487f64699e1 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot @@ -126,6 +126,7 @@ digraph complexPostponedCfg_kt { 35 -> {36} [color=green]; 36 -> {37}; 37 -> {38}; + 38 -> {10} [color=red]; 38 -> {22} [color=green]; 39 -> {40}; 40 -> {41}; diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/builderInferenceAndCoercionToUnit.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/builderInferenceAndCoercionToUnit.fir.txt index 3e92ce56a8d..409bee0e8f3 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/builderInferenceAndCoercionToUnit.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/builderInferenceAndCoercionToUnit.fir.txt @@ -9,7 +9,7 @@ FILE: builderInferenceAndCoercionToUnit.kt } public final fun test(strings: R|kotlin/collections/List|): R|kotlin/Unit| { - lval dropDown: R|DropDownComponent| = R|/DropDownComponent.DropDownComponent|(initialValues = R|kotlin/collections/buildList|( = buildList@fun R|kotlin/collections/MutableList|.(): R|kotlin/Unit| { + lval dropDown: R|DropDownComponent| = R|/DropDownComponent.DropDownComponent|(initialValues = R|kotlin/collections/buildList|( = buildList@fun R|kotlin/collections/MutableList|.(): R|kotlin/Unit| { this@R|special/anonymous|.R|SubstitutionOverride|(R|/strings|) } )) diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.fir.txt index b24613a93c9..140d26fad41 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.fir.txt @@ -28,8 +28,8 @@ FILE: buildListLazy.kt this@R|special/anonymous|.R|kotlin/collections/plusAssign|(R|/NameAndSafeValue.NameAndSafeValue|(R|/name|, R|/value|)) } ) - this@R|special/anonymous|.#( = sortBy@fun (it: R|NameAndSafeValue|): { - ^ R|/it|.# + this@R|special/anonymous|.R|kotlin/collections/sortBy|( = sortBy@fun (it: R|NameAndSafeValue|): R|kotlin/String?| { + ^ R|/it|.R|/NameAndSafeValue.name| } ) } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.kt index 9913c674a08..6ec179a9d50 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/problems/buildListLazy.kt @@ -7,6 +7,6 @@ private val environment: List by lazy { getEnv().forEach { (name, value) -> this += NameAndSafeValue(name, value) } - sortBy { it.name } + sortBy { it.name } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index f9e8071dadf..53a8fa2f3b7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -497,6 +497,9 @@ internal object PostponedVariablesInitializerResolutionStage : ResolutionStage() if (!parameter.hasBuilderInferenceAnnotation()) continue val type = parameter.returnTypeRef.coneType val receiverType = type.receiverType(callInfo.session) ?: continue + val dontUseBuilderInferenceIfPossible = + context.session.languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded) + if (dontUseBuilderInferenceIfPossible) continue for (freshVariable in candidate.freshVariables) { if (candidate.csBuilder.isPostponedTypeVariable(freshVariable)) continue diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt index a626bf537b2..bd063c2426e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.fir.resolve.inference +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate @@ -34,10 +36,11 @@ import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlin.utils.addToStdlib.safeAs -class ConstraintSystemCompleter(private val components: BodyResolveComponents, private val context: BodyResolveContext) { +class ConstraintSystemCompleter(components: BodyResolveComponents, private val context: BodyResolveContext) { private val inferenceComponents = components.session.inferenceComponents val variableFixationFinder = inferenceComponents.variableFixationFinder private val postponedArgumentsInputTypesResolver = inferenceComponents.postponedArgumentInputTypesResolver + private val languageVersionSettings = components.session.languageVersionSettings fun complete( c: ConstraintSystemCompletionContext, @@ -47,35 +50,56 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p context: ResolutionContext, collectVariablesFromContext: Boolean = false, analyze: (PostponedResolvedAtom) -> Unit - ) = with(c) { - val topLevelTypeVariables = candidateReturnType.extractTypeVariables() + ) = c.runCompletion(completionMode, topLevelAtoms, candidateReturnType, context, collectVariablesFromContext, analyze) - c.processForkConstraints() + private fun ConstraintSystemCompletionContext.runCompletion( + completionMode: ConstraintSystemCompletionMode, + topLevelAtoms: List, + topLevelType: ConeKotlinType, + context: ResolutionContext, + collectVariablesFromContext: Boolean = false, + analyze: (PostponedResolvedAtom) -> Unit + ) { + val topLevelTypeVariables = topLevelType.extractTypeVariables() + + // NB: it's called in ConstraintSystemForks resolution stage by FE 1.0 + processForkConstraints() completion@ while (true) { - val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) // TODO: This is very slow + // TODO: This is very slow + val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) - if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze(postponedArguments)) return + if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze( + languageVersionSettings, + postponedArguments + ) + ) return - // Stage 1 - if (analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze)) + // Stage 1: analyze postponed arguments with fixed parameter types + if (analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze)) continue - val someVariableIsReadyForFixation = isAnyVariableReadyForFixation( - completionMode, topLevelAtoms, candidateReturnType, collectVariablesFromContext, postponedArguments - ) + val isThereAnyReadyForFixationVariable = variableFixationFinder.findFirstVariableForFixation( + this, + getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + postponedArguments, + completionMode, + topLevelType + ) != null - if (postponedArguments.isEmpty() && !someVariableIsReadyForFixation) + // If there aren't any postponed arguments and ready for fixation variables, then completion isn't needed: nothing to do + if (postponedArguments.isEmpty() && !isThereAnyReadyForFixationVariable) break val postponedArgumentsWithRevisableType = postponedArguments .filterIsInstance() + // NB: FE 1.0 does not perform this check .filter { it.revisedExpectedType == null } val dependencyProvider = - TypeVariableDependencyInformationProvider(notFixedTypeVariables, postponedArguments, candidateReturnType, this) + TypeVariableDependencyInformationProvider(notFixedTypeVariables, postponedArguments, topLevelType, this) - // Stage 2 - val newExpectedTypeWasBuilt = postponedArgumentsInputTypesResolver.collectParameterTypesAndBuildNewExpectedTypes( + // Stage 2: collect parameter types for postponed arguments + val wasBuiltNewExpectedTypeForSomeArgument = postponedArgumentsInputTypesResolver.collectParameterTypesAndBuildNewExpectedTypes( this, postponedArgumentsWithRevisableType, completionMode, @@ -83,19 +107,21 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p topLevelTypeVariables ) - if (newExpectedTypeWasBuilt) + if (wasBuiltNewExpectedTypeForSomeArgument) continue if (completionMode == ConstraintSystemCompletionMode.FULL) { - // Stage 3 + // Stage 3: fix variables for parameter types of all postponed arguments for (argument in postponedArguments) { val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded( this, argument, postponedArguments, - candidateReturnType, + topLevelType, dependencyProvider, - ) { // atom provided here is used only inside constraint positions, omitting right now + ) { + // NB: FE 1.0 calls findResolvedAtomBy here + // atom provided here is used only inside constraint positions, omitting right now null } @@ -103,10 +129,11 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p continue@completion } - // Stage 4 + // Stage 4: create atoms with revised expected types if needed for (argument in postponedArgumentsWithRevisableType) { - val argumentWasTransformed = - transformToAtomWithNewFunctionalExpectedType(this, context, argument) + val argumentWasTransformed = transformToAtomWithNewFunctionalExpectedType( + this, context, argument + ) if (argumentWasTransformed) continue@completion @@ -114,17 +141,27 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p } // Stage 5: analyze the next ready postponed argument - if (analyzeNextReadyPostponedArgument(postponedArguments, completionMode, analyze)) + if (analyzeNextReadyPostponedArgument(languageVersionSettings, postponedArguments, completionMode, analyze)) continue - // Stage 6: fix type variables – fix if possible or report not enough information (if completion mode is full) - val variableWasFixed = fixVariablesOrReportNotEnoughInformation( - completionMode, topLevelAtoms, candidateReturnType, collectVariablesFromContext, postponedArguments + // Stage 6: fix next ready type variable with proper constraints + if (fixNextReadyVariable(completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments)) + continue + + // Stage 7: try to complete call with the builder inference if there are uninferred type variables + val areThereAppearedProperConstraintsForSomeVariable = tryToCompleteWithBuilderInference( + completionMode, topLevelAtoms, topLevelType, postponedArguments, collectVariablesFromContext, analyze ) - if (variableWasFixed) + + if (areThereAppearedProperConstraintsForSomeVariable) continue - // Stage 7: force analysis of remaining not analyzed postponed arguments and rerun stages if there are + // Stage 8: report "not enough information" for uninferred type variables + reportNotEnoughTypeInformation( + completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments + ) + + // 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 @@ -134,37 +171,44 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p } } - private fun ConstraintSystemCompletionContext.analyzeArgumentWithFixedParameterTypes( - postponedArguments: List, - analyze: (PostponedResolvedAtom) -> Unit - ): Boolean { - val argumentWithFixedOrPostponedInputTypes = findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments) - - if (argumentWithFixedOrPostponedInputTypes != null) { - analyze(argumentWithFixedOrPostponedInputTypes) - return true - } - - return false - } - - private fun ConstraintSystemCompletionContext.findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments: List) = - postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } } - - private fun ConstraintSystemCompletionContext.isAnyVariableReadyForFixation( + private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference( completionMode: ConstraintSystemCompletionMode, topLevelAtoms: List, topLevelType: ConeKotlinType, - collectVariablesFromContext: Boolean, postponedArguments: List, + collectVariablesFromContext: Boolean, + analyze: (PostponedResolvedAtom) -> Unit ): Boolean { - return variableFixationFinder.findFirstVariableForFixation( - this, - getOrderedAllTypeVariables(this, topLevelAtoms, collectVariablesFromContext), - postponedArguments, - completionMode, - topLevelType - ) != null + if (completionMode == ConstraintSystemCompletionMode.PARTIAL) return false + + // If we use the builder inference anyway (if the annotation is presented), then we are already analysed builder inference lambdas + if (!languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded)) return false + + val lambdaArguments = postponedArguments.filterIsInstance().takeIf { it.isNotEmpty() } ?: return false + + // We assume useBuilderInferenceWithoutAnnotation = true for FIR + + for (argument in lambdaArguments) { + val notFixedInputTypeVariables = argument.inputTypes + .map { it.extractTypeVariables() }.flatten().filter { it !in fixedTypeVariables } + + if (notFixedInputTypeVariables.isEmpty()) continue + + for (variable in notFixedInputTypeVariables) { + getBuilder().markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable) + } + + analyze(argument) + } + + val variableForFixation = variableFixationFinder.findFirstVariableForFixation( + this, getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), 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 don't continue and report "not enough type information" error + return variableForFixation?.hasProperConstraint == true } private fun transformToAtomWithNewFunctionalExpectedType( @@ -186,81 +230,73 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p return true } - private fun ConstraintSystemCompletionContext.analyzeNextReadyPostponedArgument( - postponedArguments: List, - completionMode: ConstraintSystemCompletionMode, - analyze: (PostponedResolvedAtom) -> Unit, - ): Boolean { - if (completionMode == ConstraintSystemCompletionMode.FULL) { - val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments) - - if (argumentWithTypeVariableAsExpectedType != null) { - analyze(argumentWithTypeVariableAsExpectedType) - return true - } - } - - return analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze) - } - - // Avoiding smart cast from filterIsInstanceOrNull looks dirty - private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List): PostponedResolvedAtom? = - postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType } - - private fun ConstraintSystemCompletionContext.fixVariablesOrReportNotEnoughInformation( + private fun ConstraintSystemCompletionContext.fixNextReadyVariable( completionMode: ConstraintSystemCompletionMode, topLevelAtoms: List, topLevelType: ConeKotlinType, collectVariablesFromContext: Boolean, postponedArguments: List, ): Boolean { - while (true) { - val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this, - getOrderedAllTypeVariables(this, topLevelAtoms, collectVariablesFromContext), - postponedArguments, - completionMode, - topLevelType - ) ?: break + val variableForFixation = variableFixationFinder.findFirstVariableForFixation( + this, + getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + postponedArguments, + completionMode, + topLevelType + ) ?: return false - if (!variableForFixation.hasProperConstraint && completionMode == ConstraintSystemCompletionMode.PARTIAL) - break - - val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) - - when { - variableForFixation.hasProperConstraint -> { - fixVariable(this, topLevelType, variableWithConstraints, postponedArguments) - return true - } - context.inferenceSession.isSyntheticTypeVariable(variableWithConstraints.typeVariable) -> { - context.inferenceSession.fixSyntheticTypeVariableWithNotEnoughInformation( - variableWithConstraints.typeVariable as ConeTypeVariable, - this - ) - return true - } - else -> { - processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms) - } + val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) + if (!variableForFixation.hasProperConstraint) { + if (context.inferenceSession.isSyntheticTypeVariable(variableWithConstraints.typeVariable)) { + context.inferenceSession.fixSyntheticTypeVariableWithNotEnoughInformation( + variableWithConstraints.typeVariable as ConeTypeVariable, + this + ) + return true } + + return false } - return false + fixVariable(this, topLevelType, variableWithConstraints, postponedArguments) + + return true } - private fun processVariableWhenNotEnoughInformation( - c: ConstraintSystemCompletionContext, + private fun ConstraintSystemCompletionContext.reportNotEnoughTypeInformation( + completionMode: ConstraintSystemCompletionMode, + topLevelAtoms: List, + topLevelType: ConeKotlinType, + collectVariablesFromContext: Boolean, + postponedArguments: List, + ) { + while (true) { + val variableForFixation = variableFixationFinder.findFirstVariableForFixation( + this, getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + postponedArguments, completionMode, topLevelType, + ) ?: break + + assert(!variableForFixation.hasProperConstraint) { + "At this stage there should be no remaining variables with proper constraints" + } + + if (completionMode == ConstraintSystemCompletionMode.PARTIAL) break + + val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) + processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms) + } + } + + private fun ConstraintSystemCompletionContext.processVariableWhenNotEnoughInformation( variableWithConstraints: VariableWithConstraints, topLevelAtoms: List, ) { val typeVariable = variableWithConstraints.typeVariable - val resolvedAtom = - findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() + val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() if (resolvedAtom != null) { - c.addError( - NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom, c.couldBeResolvedWithUnrestrictedBuilderInference()) + addError( + NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom, couldBeResolvedWithUnrestrictedBuilderInference()) ) } @@ -274,84 +310,19 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p else -> createCannotInferErrorType("Cannot infer type variable $typeVariable") } - c.fixVariable(typeVariable, resultErrorType, ConeFixVariableConstraintPosition(typeVariable)) + fixVariable(typeVariable, resultErrorType, ConeFixVariableConstraintPosition(typeVariable)) } - private fun createCannotInferErrorType(message: String, isUninferredParameter: Boolean = false) = - ConeClassErrorType( - ConeSimpleDiagnostic( - message, - DiagnosticKind.CannotInferParameterType, - ), - isUninferredParameter, - ) - - private fun findResolvedAtomBy( - typeVariable: TypeVariableMarker, + private fun ConstraintSystemCompletionContext.getOrderedAllTypeVariables( + collectVariablesFromContext: Boolean, topLevelAtoms: List - ): FirStatement? { - - fun FirStatement.findFirstAtomContainingVariable(): FirStatement? { - - var result: FirStatement? = null - - fun suggestElement(element: FirElement) { - if (result == null && element is FirStatement) { - result = element - } - } - - this@findFirstAtomContainingVariable.processAllContainingCallCandidates(processBlocks = true) { candidate -> - if (typeVariable in candidate.freshVariables) { - suggestElement(candidate.callInfo.callSite) - } - - for (postponedAtom in candidate.postponedAtoms) { - if (postponedAtom is ResolvedLambdaAtom) { - if (postponedAtom.typeVariableForLambdaReturnType == typeVariable) { - suggestElement(postponedAtom.atom) - } - } - } - } - - return result - } - - return topLevelAtoms.firstNotNullOfOrNull(FirStatement::findFirstAtomContainingVariable) - } - - private fun analyzeRemainingNotAnalyzedPostponedArgument( - postponedArguments: List, - analyze: (PostponedResolvedAtom) -> Unit - ): Boolean { - val remainingNotAnalyzedPostponedArgument = postponedArguments.firstOrNull { !it.analyzed } - - if (remainingNotAnalyzedPostponedArgument != null) { - analyze(remainingNotAnalyzedPostponedArgument) - return true - } - - return false - } - - private fun ConstraintSystemCompletionContext.hasLambdaToAnalyze( - postponedArguments: List - ): Boolean { - return analyzeArgumentWithFixedParameterTypes(postponedArguments) {} - } - - private fun getOrderedAllTypeVariables( - c: ConstraintSystemCompletionContext, - topLevelAtoms: List, - collectVariablesFromContext: Boolean - ): List = with(c) { + ): List { if (collectVariablesFromContext) { - return c.notFixedTypeVariables.keys.toList() + return notFixedTypeVariables.keys.toList() } - val result = LinkedHashSet(c.notFixedTypeVariables.size) + val result = LinkedHashSet(notFixedTypeVariables.size) fun ConeTypeVariable?.toTypeConstructor(): TypeConstructorMarker? = - this?.typeConstructor?.takeIf { it in c.notFixedTypeVariables.keys } + this?.typeConstructor?.takeIf { it in notFixedTypeVariables.keys } // TODO: non-top-level variables? fun PostponedAtomWithRevisableExpectedType.collectNotFixedVariables() { @@ -372,33 +343,35 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p } for (postponedAtom in candidate.postponedAtoms) { - when { - postponedAtom is ResolvedLambdaAtom -> { + when (postponedAtom) { + is ResolvedLambdaAtom -> { result.addIfNotNull(postponedAtom.typeVariableForLambdaReturnType.toTypeConstructor()) } - postponedAtom is LambdaWithTypeVariableAsExpectedTypeAtom -> { + is LambdaWithTypeVariableAsExpectedTypeAtom -> { postponedAtom.collectNotFixedVariables() } - postponedAtom is ResolvedCallableReferenceAtom -> { + is ResolvedCallableReferenceAtom -> { if (postponedAtom.mightNeedAdditionalResolution) { postponedAtom.collectNotFixedVariables() } } + // ResolvedCallAtom? + // ResolvedCallableReferenceArgumentAtom? } } } } - for (topLevel in topLevelAtoms) { - topLevel.collectAllTypeVariables() + for (topLevelAtom in topLevelAtoms) { + topLevelAtom.collectAllTypeVariables() } if (context.inferenceSession.hasSyntheticTypeVariables()) { - result.addAll(c.notFixedTypeVariables.filter { context.inferenceSession.isSyntheticTypeVariable(it.value.typeVariable) }.keys.asIterable()) + result.addAll(notFixedTypeVariables.filter { context.inferenceSession.isSyntheticTypeVariable(it.value.typeVariable) }.keys.asIterable()) } - require(result.size == c.notFixedTypeVariables.size) { - val notFoundTypeVariables = c.notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } + require(result.size == notFixedTypeVariables.size) { + val notFoundTypeVariables = notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } "Not all type variables found: $notFoundTypeVariables" } @@ -417,21 +390,69 @@ class ConstraintSystemCompleter(private val components: BodyResolveComponents, p c.fixVariable(variable, resultType, ConeFixVariableConstraintPosition(variable)) // TODO: obtain atom for diagnostics } - private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { - val notAnalyzedArguments = arrayListOf() - for (primitive in topLevelAtoms) { - primitive.processAllContainingCallCandidates( - // TODO: remove this argument and relevant parameter - // Currently, it's used because otherwise problem happens with a lambda in a try-block (see tryWithLambdaInside test) - processBlocks = true - ) { candidate -> - candidate.postponedAtoms.forEach { - notAnalyzedArguments.addIfNotNull(it.safeAs()?.takeUnless { it.analyzed }) + companion object { + private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { + val notAnalyzedArguments = arrayListOf() + for (primitive in topLevelAtoms) { + primitive.processAllContainingCallCandidates( + // TODO: remove this argument and relevant parameter + // Currently, it's used because otherwise problem happens with a lambda in a try-block (see tryWithLambdaInside test) + processBlocks = true + ) { candidate -> + candidate.postponedAtoms.forEach { atom -> + notAnalyzedArguments.addIfNotNull(atom.safeAs()?.takeUnless { it.analyzed }) + } } } + + return notAnalyzedArguments } - return notAnalyzedArguments + private fun findResolvedAtomBy( + typeVariable: TypeVariableMarker, + topLevelAtoms: List + ): FirStatement? { + + fun FirStatement.findFirstAtomContainingVariable(): FirStatement? { + + var result: FirStatement? = null + + fun suggestElement(element: FirElement) { + if (result == null && element is FirStatement) { + result = element + } + } + + this@findFirstAtomContainingVariable.processAllContainingCallCandidates(processBlocks = true) { candidate -> + if (typeVariable in candidate.freshVariables) { + suggestElement(candidate.callInfo.callSite) + } + + for (postponedAtom in candidate.postponedAtoms) { + if (postponedAtom is ResolvedLambdaAtom) { + if (postponedAtom.typeVariableForLambdaReturnType == typeVariable) { + suggestElement(postponedAtom.atom) + } + } + } + } + + return result + } + + return topLevelAtoms.firstNotNullOfOrNull(FirStatement::findFirstAtomContainingVariable) + } + + private fun createCannotInferErrorType(message: String, isUninferredParameter: Boolean = false) = + ConeClassErrorType( + ConeSimpleDiagnostic( + message, + DiagnosticKind.CannotInferParameterType, + ), + isUninferredParameter, + ) + + } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt index 677daaaa311..b81b3886d5f 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt @@ -5,33 +5,109 @@ 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.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError import org.jetbrains.kotlin.resolve.calls.inference.model.FixVariableConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker -interface ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context { - val allTypeVariables: Map - override val notFixedTypeVariables: Map - override val fixedTypeVariables: Map - override val postponedTypeVariables: List +abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context { + abstract val allTypeVariables: Map + abstract override val notFixedTypeVariables: Map + abstract override val fixedTypeVariables: Map + abstract override val postponedTypeVariables: List - fun getBuilder(): ConstraintSystemBuilder + abstract fun getBuilder(): ConstraintSystemBuilder // type can be proper if it not contains not fixed type variables - fun canBeProper(type: KotlinTypeMarker): Boolean + abstract fun canBeProper(type: KotlinTypeMarker): Boolean - fun containsOnlyFixedOrPostponedVariables(type: KotlinTypeMarker): Boolean - fun containsOnlyFixedVariables(type: KotlinTypeMarker): Boolean + abstract fun containsOnlyFixedOrPostponedVariables(type: KotlinTypeMarker): Boolean + abstract fun containsOnlyFixedVariables(type: KotlinTypeMarker): Boolean // mutable operations - fun addError(error: ConstraintSystemError) + abstract fun addError(error: ConstraintSystemError) - fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>) + abstract fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>) - fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean - fun processForkConstraints() + abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean + abstract fun processForkConstraints() + + fun analyzeArgumentWithFixedParameterTypes( + languageVersionSettings: LanguageVersionSettings, + postponedArguments: List, + analyze: (A) -> Unit + ): Boolean { + val useBuilderInferenceOnlyIfNeeded = + languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded) + val argumentToAnalyze = if (useBuilderInferenceOnlyIfNeeded) { + findPostponedArgumentWithFixedInputTypes(postponedArguments) + } else { + findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments) + } + + if (argumentToAnalyze != null) { + analyze(argumentToAnalyze) + return true + } + + return false + } + + fun analyzeNextReadyPostponedArgument( + languageVersionSettings: LanguageVersionSettings, + postponedArguments: List, + completionMode: ConstraintSystemCompletionMode, + analyze: (A) -> Unit + ): Boolean { + if (completionMode == ConstraintSystemCompletionMode.FULL) { + val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments) + + if (argumentWithTypeVariableAsExpectedType != null) { + analyze(argumentWithTypeVariableAsExpectedType) + return true + } + } + + return analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze) + } + + fun analyzeRemainingNotAnalyzedPostponedArgument( + postponedArguments: List, + analyze: (A) -> Unit + ): Boolean { + val remainingNotAnalyzedPostponedArgument = postponedArguments.firstOrNull { !it.analyzed } + + if (remainingNotAnalyzedPostponedArgument != null) { + analyze(remainingNotAnalyzedPostponedArgument) + return true + } + + return false + } + + fun hasLambdaToAnalyze( + languageVersionSettings: LanguageVersionSettings, + postponedArguments: List + ): Boolean { + return analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments) {} + } + + // Avoiding smart cast from filterIsInstanceOrNull looks dirty + private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List): A? = + postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType } + + private fun findPostponedArgumentWithFixedOrPostponedInputTypes( + postponedArguments: List + ) = postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } } + + private fun findPostponedArgumentWithFixedInputTypes( + postponedArguments: List + ) = postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedVariables(it) } } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index 70192b54975..94b4c10bf27 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -20,12 +20,12 @@ import kotlin.math.max class NewConstraintSystemImpl( private val constraintInjector: ConstraintInjector, val typeSystemContext: TypeSystemInferenceExtensionContext -) : TypeSystemInferenceExtensionContext by typeSystemContext, +) : ConstraintSystemCompletionContext(), + TypeSystemInferenceExtensionContext by typeSystemContext, NewConstraintSystem, ConstraintSystemBuilder, ConstraintInjector.Context, ResultTypeResolver.Context, - ConstraintSystemCompletionContext, PostponedArgumentsAnalyzerContext { private val utilContext = constraintInjector.constraintIncorporator.utilContext diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index 55920b3c4a2..ae0ad199d4c 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -14,7 +14,6 @@ import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker -import org.jetbrains.kotlin.types.model.TypeVariableTypeConstructorMarker import org.jetbrains.kotlin.types.model.safeSubstitute import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.utils.addIfNotNull @@ -24,7 +23,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs class KotlinConstraintSystemCompleter( private val resultTypeResolver: ResultTypeResolver, val variableFixationFinder: VariableFixationFinder, - private val postponedArgumentInputTypesResolver: PostponedArgumentInputTypesResolver, + private val postponedArgumentsInputTypesResolver: PostponedArgumentInputTypesResolver, private val languageVersionSettings: LanguageVersionSettings ) { fun runCompletion( @@ -74,28 +73,38 @@ class KotlinConstraintSystemCompleter( val topLevelTypeVariables = topLevelType.extractTypeVariables() completion@ while (true) { - // TODO + // TODO: This is very slow val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) - if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze(postponedArguments)) return + + if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze( + languageVersionSettings, + postponedArguments + ) + ) return // Stage 1: analyze postponed arguments with fixed parameter types - if (analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze)) + if (analyzeArgumentWithFixedParameterTypes(languageVersionSettings, postponedArguments, analyze)) continue - val isThereAnyReadyForFixationVariable = isThereAnyReadyForFixationVariable( - completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments - ) + val isThereAnyReadyForFixationVariable = variableFixationFinder.findFirstVariableForFixation( + this, + getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + postponedArguments, + completionMode, + topLevelType + ) != null // If there aren't any postponed arguments and ready for fixation variables, then completion isn't needed: nothing to do if (postponedArguments.isEmpty() && !isThereAnyReadyForFixationVariable) break - val postponedArgumentsWithRevisableType = postponedArguments.filterIsInstance() + val postponedArgumentsWithRevisableType = postponedArguments + .filterIsInstance() val dependencyProvider = TypeVariableDependencyInformationProvider(notFixedTypeVariables, postponedArguments, topLevelType, this) // Stage 2: collect parameter types for postponed arguments - val wasBuiltNewExpectedTypeForSomeArgument = postponedArgumentInputTypesResolver.collectParameterTypesAndBuildNewExpectedTypes( + val wasBuiltNewExpectedTypeForSomeArgument = postponedArgumentsInputTypesResolver.collectParameterTypesAndBuildNewExpectedTypes( this, postponedArgumentsWithRevisableType, completionMode, @@ -109,7 +118,7 @@ class KotlinConstraintSystemCompleter( if (completionMode == ConstraintSystemCompletionMode.FULL) { // Stage 3: fix variables for parameter types of all postponed arguments for (argument in postponedArguments) { - val wasFixedSomeVariable = postponedArgumentInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded( + val variableWasFixed = postponedArgumentsInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded( this, argument, postponedArguments, @@ -119,23 +128,23 @@ class KotlinConstraintSystemCompleter( findResolvedAtomBy(it, topLevelAtoms) ?: topLevelAtoms.firstOrNull() } - if (wasFixedSomeVariable) + if (variableWasFixed) continue@completion } // Stage 4: create atoms with revised expected types if needed for (argument in postponedArgumentsWithRevisableType) { - val wasTransformedSomeArgument = transformToAtomWithNewFunctionalExpectedType( + val argumentWasTransformed = transformToAtomWithNewFunctionalExpectedType( this, argument, diagnosticsHolder ) - if (wasTransformedSomeArgument) + if (argumentWasTransformed) continue@completion } } // Stage 5: analyze the next ready postponed argument - if (analyzeNextReadyPostponedArgument(postponedArguments, completionMode, analyze)) + if (analyzeNextReadyPostponedArgument(languageVersionSettings, postponedArguments, completionMode, analyze)) continue // Stage 6: fix next ready type variable with proper constraints @@ -165,6 +174,29 @@ class KotlinConstraintSystemCompleter( } } + fun prepareLambdaAtomForFactoryPattern( + atom: ResolvedLambdaAtom, + candidate: SimpleResolutionCandidate, + diagnosticsHolder: KotlinDiagnosticsHolder, + ): ResolvedLambdaAtom { + val returnVariable = TypeVariableForLambdaReturnType(candidate.callComponents.builtIns, "_R") + val csBuilder = candidate.getSystem().getBuilder() + csBuilder.registerVariable(returnVariable) + val functionalType: KotlinType = csBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.getSystem().asConstraintSystemCompleterContext(), atom.expectedType!!) as KotlinType + val expectedType = KotlinTypeFactory.simpleType( + functionalType.attributes, + functionalType.constructor, + functionalType.arguments.dropLast(1) + returnVariable.defaultType.asTypeProjection(), + functionalType.isMarkedNullable + ) + csBuilder.addSubtypeConstraint( + expectedType, + functionalType, + ArgumentConstraintPositionImpl(atom.atom) + ) + return atom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable) + } + private fun ConstraintSystemCompletionContext.tryToCompleteWithBuilderInference( completionMode: ConstraintSystemCompletionMode, topLevelAtoms: List, @@ -231,115 +263,6 @@ class KotlinConstraintSystemCompleter( return true } - private fun ConstraintSystemCompletionContext.analyzeArgumentWithFixedParameterTypes( - postponedArguments: List, - analyze: (PostponedResolvedAtom) -> Unit - ): Boolean { - val useBuilderInferenceOnlyIfNeeded = languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceOnlyIfNeeded) - val argumentToAnalyze = if (useBuilderInferenceOnlyIfNeeded) { - findPostponedArgumentWithFixedInputTypes(postponedArguments) - } else { - findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments) - } - - if (argumentToAnalyze != null) { - analyze(argumentToAnalyze) - return true - } - - return false - } - - private fun ConstraintSystemCompletionContext.analyzeNextReadyPostponedArgument( - postponedArguments: List, - completionMode: ConstraintSystemCompletionMode, - analyze: (PostponedResolvedAtom) -> Unit - ): Boolean { - if (completionMode == ConstraintSystemCompletionMode.FULL) { - val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments) - - if (argumentWithTypeVariableAsExpectedType != null) { - analyze(argumentWithTypeVariableAsExpectedType) - return true - } - } - - return analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze) - } - - private fun analyzeRemainingNotAnalyzedPostponedArgument( - postponedArguments: List, - analyze: (PostponedResolvedAtom) -> Unit - ): Boolean { - val remainingNotAnalyzedPostponedArgument = postponedArguments.firstOrNull { !it.analyzed } - - if (remainingNotAnalyzedPostponedArgument != null) { - analyze(remainingNotAnalyzedPostponedArgument) - return true - } - - return false - } - - fun prepareLambdaAtomForFactoryPattern( - atom: ResolvedLambdaAtom, - candidate: SimpleResolutionCandidate, - diagnosticsHolder: KotlinDiagnosticsHolder, - ): ResolvedLambdaAtom { - val returnVariable = TypeVariableForLambdaReturnType(candidate.callComponents.builtIns, "_R") - val csBuilder = candidate.getSystem().getBuilder() - csBuilder.registerVariable(returnVariable) - val functionalType: KotlinType = csBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.getSystem().asConstraintSystemCompleterContext(), atom.expectedType!!) as KotlinType - val expectedType = KotlinTypeFactory.simpleType( - functionalType.attributes, - functionalType.constructor, - functionalType.arguments.dropLast(1) + returnVariable.defaultType.asTypeProjection(), - functionalType.isMarkedNullable - ) - csBuilder.addSubtypeConstraint( - expectedType, - functionalType, - ArgumentConstraintPositionImpl(atom.atom) - ) - return atom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable) - } - - private fun ConstraintSystemCompletionContext.hasLambdaToAnalyze( - postponedArguments: List - ): Boolean { - return analyzeArgumentWithFixedParameterTypes(postponedArguments) {} - } - - private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List) = - postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType } - - private fun ConstraintSystemCompletionContext.findPostponedArgumentWithFixedOrPostponedInputTypes( - postponedArguments: List - ) = postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } } - - private fun ConstraintSystemCompletionContext.findPostponedArgumentWithFixedInputTypes(postponedArguments: List) = - postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedVariables(it) } } - - private fun fixVariable( - c: ConstraintSystemCompletionContext, - variableWithConstraints: VariableWithConstraints, - topLevelAtoms: List - ) { - fixVariable(c, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, topLevelAtoms) - } - - private fun fixVariable( - c: ConstraintSystemCompletionContext, - variableWithConstraints: VariableWithConstraints, - direction: TypeVariableDirectionCalculator.ResolveDirection, - topLevelAtoms: List - ) { - val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction) - val variable = variableWithConstraints.typeVariable - val resolvedAtom = findResolvedAtomBy(variable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() - c.fixVariable(variable, resultType, FixVariableConstraintPositionImpl(variable, resolvedAtom)) - } - private fun ConstraintSystemCompletionContext.fixNextReadyVariable( completionMode: ConstraintSystemCompletionMode, topLevelAtoms: List, @@ -348,8 +271,11 @@ class KotlinConstraintSystemCompleter( postponedArguments: List, ): Boolean { val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this, getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), - postponedArguments, completionMode, topLevelType, + this, + getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + postponedArguments, + completionMode, + topLevelType ) ?: return false if (!variableForFixation.hasProperConstraint) return false @@ -380,32 +306,41 @@ class KotlinConstraintSystemCompleter( if (completionMode == ConstraintSystemCompletionMode.PARTIAL) break val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) - val typeVariable = variableWithConstraints.typeVariable - val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() - - if (resolvedAtom != null) { - addError( - NotEnoughInformationForTypeParameterImpl(typeVariable, resolvedAtom, couldBeResolvedWithUnrestrictedBuilderInference()) - ) - } - - val resultErrorType = when { - typeVariable is TypeVariableFromCallableDescriptor -> { - ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter) - } - typeVariable is TypeVariableForLambdaParameterType && typeVariable.atom is LambdaKotlinCallArgument -> { - diagnosticsHolder.addDiagnostic( - NotEnoughInformationForLambdaParameter(typeVariable.atom, typeVariable.index) - ) - ErrorUtils.createErrorType("Cannot infer lambda parameter type") - } - else -> ErrorUtils.createErrorType("Cannot infer type variable $typeVariable") - } - - fixVariable(typeVariable, resultErrorType, FixVariableConstraintPositionImpl(typeVariable, resolvedAtom)) + processVariableWhenNotEnoughInformation(variableWithConstraints, topLevelAtoms, diagnosticsHolder) } } + private fun ConstraintSystemCompletionContext.processVariableWhenNotEnoughInformation( + variableWithConstraints: VariableWithConstraints, + topLevelAtoms: List, + diagnosticsHolder: KotlinDiagnosticsHolder + ) { + val typeVariable = variableWithConstraints.typeVariable + val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() + + if (resolvedAtom != null) { + addError( + NotEnoughInformationForTypeParameterImpl(typeVariable, resolvedAtom, couldBeResolvedWithUnrestrictedBuilderInference()) + ) + } + + val resultErrorType = when { + typeVariable is TypeVariableFromCallableDescriptor -> { + ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter) + } + typeVariable is TypeVariableForLambdaParameterType && typeVariable.atom is LambdaKotlinCallArgument -> { + diagnosticsHolder.addDiagnostic( + NotEnoughInformationForLambdaParameter(typeVariable.atom, typeVariable.index) + ) + ErrorUtils.createErrorType("Cannot infer lambda parameter type") + } + else -> ErrorUtils.createErrorType("Cannot infer type variable $typeVariable") + } + + fixVariable(typeVariable, resultErrorType, FixVariableConstraintPositionImpl(typeVariable, resolvedAtom)) + + } + private fun ConstraintSystemCompletionContext.getOrderedAllTypeVariables( collectVariablesFromContext: Boolean, topLevelAtoms: List @@ -416,19 +351,27 @@ class KotlinConstraintSystemCompleter( fun getVariablesFromRevisedExpectedType(revisedExpectedType: KotlinType?) = revisedExpectedType?.arguments?.map { it.type.constructor }?.filterIsInstance() - fun ResolvedAtom.process(to: LinkedHashSet) { + // Note that it's important to use Set here, because several atoms can share the same type variable + val result = linkedSetOf() + + fun ResolvedAtom.collectAllTypeVariables() { val typeVariables = when (this) { - is LambdaWithTypeVariableAsExpectedTypeAtom -> getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty() - is ResolvedCallAtom -> freshVariablesSubstitutor.freshVariables.map { it.freshTypeConstructor } - is PostponedCallableReferenceAtom -> + is ResolvedLambdaAtom -> { + listOfNotNull(typeVariableForLambdaReturnType?.freshTypeConstructor) + } + is LambdaWithTypeVariableAsExpectedTypeAtom -> { + getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty() + } + is PostponedCallableReferenceAtom -> { getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty() + candidate?.freshVariablesSubstitutor?.freshVariables?.map { it.freshTypeConstructor }.orEmpty() + } + is ResolvedCallAtom -> freshVariablesSubstitutor.freshVariables.map { it.freshTypeConstructor } is ResolvedCallableReferenceArgumentAtom -> candidate?.freshVariablesSubstitutor?.freshVariables?.map { it.freshTypeConstructor }.orEmpty() - is ResolvedLambdaAtom -> listOfNotNull(typeVariableForLambdaReturnType?.freshTypeConstructor) else -> emptyList() } - typeVariables.mapNotNullTo(to) { + typeVariables.mapNotNullTo(result) { it.takeIf { notFixedTypeVariables.containsKey(it) } } @@ -436,21 +379,19 @@ class KotlinConstraintSystemCompleter( * Hack for completing error candidates in delegate resolve */ if (this is StubResolvedAtom && typeVariable in notFixedTypeVariables) { - to += typeVariable + result += typeVariable } if (analyzed) { - subResolvedAtoms?.forEach { it.process(to) } + subResolvedAtoms?.forEach { it.collectAllTypeVariables() } } } - // Note that it's important to use Set here, because several atoms can share the same type variable - val result = linkedSetOf() - for (primitive in topLevelAtoms) { - primitive.process(result) + for (topLevelAtom in topLevelAtoms) { + topLevelAtom.collectAllTypeVariables() } - assert(result.size == notFixedTypeVariables.size) { + require(result.size == notFixedTypeVariables.size) { val notFoundTypeVariables = notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } "Not all type variables found: $notFoundTypeVariables" } @@ -458,19 +399,25 @@ class KotlinConstraintSystemCompleter( return result.toList() } - private fun ConstraintSystemCompletionContext.isThereAnyReadyForFixationVariable( - completionMode: ConstraintSystemCompletionMode, - topLevelAtoms: List, - topLevelType: UnwrappedType, - collectVariablesFromContext: Boolean, - postponedArguments: List - ) = variableFixationFinder.findFirstVariableForFixation( - this, - getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), - postponedArguments, - completionMode, - topLevelType, - ) != null + private fun fixVariable( + c: ConstraintSystemCompletionContext, + variableWithConstraints: VariableWithConstraints, + topLevelAtoms: List + ) { + fixVariable(c, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, topLevelAtoms) + } + + private fun fixVariable( + c: ConstraintSystemCompletionContext, + variableWithConstraints: VariableWithConstraints, + direction: TypeVariableDirectionCalculator.ResolveDirection, + topLevelAtoms: List + ) { + val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction) + val variable = variableWithConstraints.typeVariable + val resolvedAtom = findResolvedAtomBy(variable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() + c.fixVariable(variable, resultType, FixVariableConstraintPositionImpl(variable, resolvedAtom)) + } companion object { fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { diff --git a/compiler/testData/codegen/box/inference/builderInference/kt42139.kt b/compiler/testData/codegen/box/inference/builderInference/kt42139.kt index 334f2d8f603..e71de78bc3f 100644 --- a/compiler/testData/codegen/box/inference/builderInference/kt42139.kt +++ b/compiler/testData/codegen/box/inference/builderInference/kt42139.kt @@ -1,7 +1,5 @@ // !LANGUAGE: +UnrestrictedBuilderInference // WITH_STDLIB -// IGNORE_BACKEND_FIR: JVM_IR -// FIR status: NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER on select call (K) fun select(vararg x: R) = x[0] fun myEmptyList(): List = emptyList() diff --git a/compiler/testData/codegen/box/inference/builderInference/withoutAnnotation.kt b/compiler/testData/codegen/box/inference/builderInference/withoutAnnotation.kt index 3e7d839bd75..53e49c46d4e 100644 --- a/compiler/testData/codegen/box/inference/builderInference/withoutAnnotation.kt +++ b/compiler/testData/codegen/box/inference/builderInference/withoutAnnotation.kt @@ -1,6 +1,4 @@ // WITH_STDLIB -// IGNORE_BACKEND_FIR: JVM_IR -// FIR status: NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER on buildMap call (K) // !LANGUAGE: +UseBuilderInferenceWithoutAnnotation fun buildMap(builderAction: MutableMap.() -> Unit): Map = mapOf() diff --git a/compiler/testData/diagnostics/tests/callableReference/resolve/ambiguityWithBoundExtensionReceiver.fir.kt b/compiler/testData/diagnostics/tests/callableReference/resolve/ambiguityWithBoundExtensionReceiver.fir.kt index 9fcc383dd51..91a83c25be8 100644 --- a/compiler/testData/diagnostics/tests/callableReference/resolve/ambiguityWithBoundExtensionReceiver.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/resolve/ambiguityWithBoundExtensionReceiver.fir.kt @@ -10,6 +10,6 @@ fun A.foo() = "" class A { fun main() { - bar(::foo) checkType { _() } + bar(::foo) checkType { _() } } } diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/changingResolveIfDontUseBuilderInference.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/changingResolveIfDontUseBuilderInference.fir.kt index b8ba0ef3b85..0b0a0ac938d 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/changingResolveIfDontUseBuilderInference.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/changingResolveIfDontUseBuilderInference.fir.kt @@ -20,10 +20,10 @@ fun id(x: K): K = x fun main() { val x: Map = buildMap { put("", "") - swap(foo()) + swap(foo()) } // `Map` if we use builder inference, `Map` if we don't val y: MutableMap = build7 { - id(run { this }) + id(run { this }) } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt index 9d26d72be01..faad5dd9fee 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt @@ -204,7 +204,7 @@ interface Foo2 { fun twoBuilderLambdas(@BuilderInference block: Foo.() -> Unit, @BuilderInference block2: Foo2.() -> Unit) {} fun test() { - twoBuilderLambdas( + twoBuilderLambdas( { add("") with (get()) { diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.fir.kt deleted file mode 100644 index 186bbcd3dcd..00000000000 --- a/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.fir.kt +++ /dev/null @@ -1,20 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -DEPRECATION -OPT_IN_IS_NOT_ENABLED -UNUSED_VARIABLE -// WITH_STDLIB - -import kotlin.experimental.ExperimentalTypeInference - -@OptIn(ExperimentalTypeInference::class) -fun combined( - check: () -> Unit, - @BuilderInference block: TestInterface.() -> Unit -): R = TODO() - -interface TestInterface { - fun emit(r: R) -} - -fun test() { - val ret = combined({ }) { - emit(1) - } -} diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.kt b/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.kt index f8dab6c71b6..2359743985f 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/simpleLambdaInCallWithAnotherLambdaWithBuilderInference.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -DEPRECATION -OPT_IN_IS_NOT_ENABLED -UNUSED_VARIABLE // WITH_STDLIB diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/withoutAnnotationDisabledFeature.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/withoutAnnotationDisabledFeature.fir.kt index 35cdd7ee1ba..f8caffc1532 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/withoutAnnotationDisabledFeature.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/withoutAnnotationDisabledFeature.fir.kt @@ -5,8 +5,8 @@ fun buildMap(builderAction: MutableMap.() -> Unit): Map = mapOf() fun box(): String { - val x = buildMap { + val x = buildMap { put("", "") } return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inference/kt6175.fir.kt b/compiler/testData/diagnostics/tests/inference/kt6175.fir.kt index be734e6e9e1..d0fc6987f7a 100644 --- a/compiler/testData/diagnostics/tests/inference/kt6175.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/kt6175.fir.kt @@ -26,10 +26,10 @@ fun test2() { fun baz(body: (List) -> T): T = fail() fun test3() { - baz { + baz { true } - baz { x -> + baz { x -> true } } @@ -37,10 +37,10 @@ fun test3() { fun brr(body: (List) -> T): T = fail() fun test4() { - brr { + brr { true } - brr { x -> + brr { x -> true } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt new file mode 100644 index 00000000000..f6482deec02 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt @@ -0,0 +1,29 @@ +// !LANGUAGE: +UnrestrictedBuilderInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Builder { + suspend fun add(t: T) {} +} + +fun build(g: suspend Builder.() -> Unit): List = TODO() +fun wrongBuild(g: Builder.() -> Unit): List = TODO() + +fun Builder.extensionAdd(s: S) {} + +suspend fun Builder.safeExtensionAdd(s: S) {} + +val member = build { + add(42) +} + +val memberWithoutAnn = wrongBuild { + add(42) +} + +val extension = build { + extensionAdd("foo") +} + +val safeExtension = build { + safeExtensionAdd("foo") +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt index 83b6a12e409..871f58e247e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !LANGUAGE: +UnrestrictedBuilderInference // !DIAGNOSTICS: -UNUSED_PARAMETER diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.fir.kt new file mode 100644 index 00000000000..3fd2361ddb7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.fir.kt @@ -0,0 +1,38 @@ +// ALLOW_KOTLIN_PACKAGE +// !LANGUAGE: +UnrestrictedBuilderInference +// !DIAGNOSTICS: -UNUSED_PARAMETER +// FILE: annotation.kt + +package kotlin + +annotation class BuilderInference + +// FILE: test.kt + +class Builder { + fun add(t: T) {} +} + +fun build(@BuilderInference g: Builder.() -> Unit): List = TODO() +fun wrongBuild(g: Builder.() -> Unit): List = TODO() + +fun Builder.extensionAdd(s: S) {} + +@BuilderInference +fun Builder.safeExtensionAdd(s: S) {} + +val member = build { + add(42) +} + +val memberWithoutAnn = wrongBuild { + add(42) +} + +val extension = build { + extensionAdd("foo") +} + +val safeExtension = build { + safeExtensionAdd("foo") +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.kt index ca076f9bb1f..10b32ee4a01 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInference.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // ALLOW_KOTLIN_PACKAGE // !LANGUAGE: +UnrestrictedBuilderInference // !DIAGNOSTICS: -UNUSED_PARAMETER diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.fir.kt new file mode 100644 index 00000000000..818332bb37d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.fir.kt @@ -0,0 +1,38 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// ALLOW_KOTLIN_PACKAGE +// !WITH_NEW_INFERENCE +// FILE: annotation.kt + +package kotlin + +annotation class BuilderInference + +// FILE: test.kt + +class Builder { + fun add(t: T) {} +} + +fun build(@BuilderInference g: Builder.() -> Unit): List = TODO() +fun wrongBuild(g: Builder.() -> Unit): List = TODO() + +fun Builder.extensionAdd(s: S) {} + +@BuilderInference +fun Builder.safeExtensionAdd(s: S) {} + +val member = build { + add(42) +} + +val memberWithoutAnn = wrongBuild { + add(42) +} + +val extension = build { + extensionAdd("foo") +} + +val safeExtension = build { + safeExtensionAdd("foo") +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.kt index e054470c1f0..f56fe7ef2d5 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/resolveUsualCallWithBuilderInferenceWithRestrictions.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER // ALLOW_KOTLIN_PACKAGE // !WITH_NEW_INFERENCE diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/applyInsideCoroutine.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/applyInsideCoroutine.fir.kt index 4b09001f5bb..464b854d07e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/applyInsideCoroutine.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/applyInsideCoroutine.fir.kt @@ -7,25 +7,25 @@ class Controller { fun generate(g: suspend Controller.() -> Unit): S = TODO() -val test1 = generate { - apply { +val test1 = generate { + apply { yield(4) } } -val test2 = generate { +val test2 = generate { yield(B) - apply { + apply { yield(C) } } -val test3 = generate { - this.let { +val test3 = generate { + this.let { yield(B) } - apply { + apply { yield(C) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.fir.kt deleted file mode 100644 index 314b0fe2855..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -// !LANGUAGE: -ExperimentalBuilderInference -// !DIAGNOSTICS: -UNUSED_PARAMETER - -interface Base - -interface Controller : Base { - suspend fun yield(t: T) {} -} - -fun generate(g: suspend Controller.() -> Unit): S = TODO() - -suspend fun Base.baseExtension() {} - -val test1 = generate { - yield("foo") - baseExtension() -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt index 460262f44a4..36234b25411 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: -ExperimentalBuilderInference // !DIAGNOSTICS: -UNUSED_PARAMETER diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.fir.kt new file mode 100644 index 00000000000..874106fa952 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.fir.kt @@ -0,0 +1,23 @@ +// !OPT_IN: kotlin.RequiresOptIn +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE +// NI_EXPECTED_FILE + +@file:OptIn(ExperimentalTypeInference::class) + +import kotlin.experimental.ExperimentalTypeInference + +class GenericController { + suspend fun yield(t: T) {} +} + +fun generate(@BuilderInference g: suspend GenericController.() -> Unit): List = TODO() + +@BuilderInference +suspend fun GenericController>.yieldGenerate(g: suspend GenericController.() -> Unit): Unit = TODO() + +val test1 = generate { + // TODO: KT-15185 + yieldGenerate { + yield(4) + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.kt index b8f03a3f044..a2b60c8daf3 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !OPT_IN: kotlin.RequiresOptIn // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE // NI_EXPECTED_FILE diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWithErrors.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWithErrors.fir.kt index d923108ab8e..d451eb553e1 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWithErrors.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWithErrors.fir.kt @@ -18,6 +18,6 @@ val test1 = generate { yield(A) } -val test2: Int = generate { - yield(A()) -} +val test2: Int = generate { + yield(A()) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.fir.kt new file mode 100644 index 00000000000..fc861e2b93c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.fir.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// NI_EXPECTED_FILE + +class Controller { + suspend fun yield(t: T) {} +} + +fun generate(g: suspend Controller.() -> Unit): S = TODO() + +val test = generate { + yield("foo") +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.kt index 177c78cfdc1..916b60bc8b0 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/suspendCallsWrongUpperBound.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER // NI_EXPECTED_FILE diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.fir.kt new file mode 100644 index 00000000000..72d9fb268a1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.fir.kt @@ -0,0 +1,20 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER -UNUSED_VARIABLE +// NI_EXPECTED_FILE + +class GenericController { + suspend fun yield(t: T) {} +} + +fun generate(g: suspend GenericController.(S) -> Unit): S = TODO() + +val test1 = generate { + yield(4) +} + +val test2 = generate { + yield(4) +} + +val test3 = generate { bar: Int -> + yield(4) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.kt index 58b437e1b95..4e2499f3c0f 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withUninferredParameter.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER -UNUSED_VARIABLE // NI_EXPECTED_FILE diff --git a/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.ir.txt b/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.ir.txt index 601ca02ad03..8869744f779 100644 --- a/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.ir.txt +++ b/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.ir.txt @@ -94,8 +94,8 @@ FILE fqName: fileName:/castsInsideCoroutineInference.kt BLOCK_BODY VAR name:channel type:.ChannelCoroutine [val] TYPE_OP type=.ChannelCoroutine origin=CAST typeOperand=.ChannelCoroutine - CALL 'public abstract fun (): .SendChannel.ProducerScope> declared in .ProducerScope' type=.SendChannel origin=GET_PROPERTY - $this: GET_VAR '$this$produce: .ProducerScope declared in .asFairChannel.' type=.ProducerScope origin=null + CALL 'public abstract fun (): .SendChannel.ProducerScope> declared in .ProducerScope' type=.SendChannel origin=GET_PROPERTY + $this: GET_VAR '$this$produce: .ProducerScope declared in .asFairChannel.' type=.ProducerScope origin=null CALL 'public final fun collect (action: kotlin.coroutines.SuspendFunction1<@[ParameterName(name = 'value')] T of .collect, kotlin.Unit>): kotlin.Unit [inline,suspend] declared in ' type=kotlin.Unit origin=null : kotlin.Any? $receiver: GET_VAR 'flow: .Flow<*> declared in .asFairChannel' type=.Flow<*> origin=null @@ -123,12 +123,12 @@ FILE fqName: fileName:/castsInsideCoroutineInference.kt VALUE_PARAMETER name:flow index:0 type:.Flow<*> BLOCK_BODY RETURN type=kotlin.Nothing from='private final fun asChannel (flow: .Flow<*>): .ReceiveChannel declared in ' - CALL 'public final fun produce (block: @[ExtensionFunctionType] kotlin.coroutines.SuspendFunction1<.ProducerScope.produce>, kotlin.Unit>): .ReceiveChannel.produce> declared in ' type=.ReceiveChannel<@[ParameterName(name = 'value')] kotlin.Any> origin=null - : @[ParameterName(name = 'value')] kotlin.Any + CALL 'public final fun produce (block: @[ExtensionFunctionType] kotlin.coroutines.SuspendFunction1<.ProducerScope.produce>, kotlin.Unit>): .ReceiveChannel.produce> declared in ' type=.ReceiveChannel origin=null + : kotlin.Any $receiver: GET_VAR ': .CoroutineScope declared in .asChannel' type=.CoroutineScope origin=null - block: FUN_EXPR type=@[ExtensionFunctionType] kotlin.coroutines.SuspendFunction1<.ProducerScope<@[ParameterName(name = 'value')] kotlin.Any>, kotlin.Unit> origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:.ProducerScope<@[ParameterName(name = 'value')] kotlin.Any>) returnType:kotlin.Unit [suspend] - $receiver: VALUE_PARAMETER name:$this$produce type:.ProducerScope<@[ParameterName(name = 'value')] kotlin.Any> + block: FUN_EXPR type=@[ExtensionFunctionType] kotlin.coroutines.SuspendFunction1<.ProducerScope, kotlin.Unit> origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:.ProducerScope) returnType:kotlin.Unit [suspend] + $receiver: VALUE_PARAMETER name:$this$produce type:.ProducerScope BLOCK_BODY CALL 'public final fun collect (action: kotlin.coroutines.SuspendFunction1<@[ParameterName(name = 'value')] T of .collect, kotlin.Unit>): kotlin.Unit [inline,suspend] declared in ' type=kotlin.Unit origin=null : kotlin.Any? @@ -139,8 +139,8 @@ FILE fqName: fileName:/castsInsideCoroutineInference.kt BLOCK_BODY RETURN type=kotlin.Nothing from='local final fun (value: @[ParameterName(name = 'value')] kotlin.Any?): kotlin.Unit [suspend] declared in .asChannel.' CALL 'public abstract fun send (e: E of .SendChannel): kotlin.Unit [suspend] declared in .SendChannel' type=kotlin.Unit origin=null - $this: CALL 'public abstract fun (): .SendChannel.ProducerScope> declared in .ProducerScope' type=.SendChannel<@[ParameterName(name = 'value')] kotlin.Any> origin=GET_PROPERTY - $this: GET_VAR '$this$produce: .ProducerScope<@[ParameterName(name = 'value')] kotlin.Any> declared in .asChannel.' type=.ProducerScope<@[ParameterName(name = 'value')] kotlin.Any> origin=null + $this: CALL 'public abstract fun (): .SendChannel.ProducerScope> declared in .ProducerScope' type=.SendChannel origin=GET_PROPERTY + $this: GET_VAR '$this$produce: .ProducerScope declared in .asChannel.' type=.ProducerScope origin=null e: BLOCK type=@[ParameterName(name = 'value')] kotlin.Any origin=ELVIS VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:@[ParameterName(name = 'value')] kotlin.Any? [val] GET_VAR 'value: @[ParameterName(name = 'value')] kotlin.Any? declared in .asChannel..' type=@[ParameterName(name = 'value')] kotlin.Any? origin=null diff --git a/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.kt.txt b/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.kt.txt index 96ea9b1f064..ff9560c7bff 100644 --- a/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.kt.txt +++ b/compiler/testData/ir/irText/types/castsInsideCoroutineInference.fir.kt.txt @@ -52,7 +52,7 @@ private fun CoroutineScope.asFairChannel(flow: Flow<*>): ReceiveChannel { } private fun CoroutineScope.asChannel(flow: Flow<*>): ReceiveChannel { - return .produce<@ParameterName(name = "value") Any>(block = local suspend fun ProducerScope<@ParameterName(name = "value") Any>.() { + return .produce(block = local suspend fun ProducerScope.() { flow.collect(action = local suspend fun (value: @ParameterName(name = "value") Any?) { return $this$produce.().send(e = { // BLOCK val : @ParameterName(name = "value") Any? = value