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