diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt index 85163ca6777..cb7ea5d2af5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt @@ -339,6 +339,8 @@ class BuilderInferenceSession( val callSubstitutor = storage.buildResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) for (initialConstraint in storage.initialConstraints) { + if (initialConstraint.position is BuilderInferencePosition) continue + val substitutedConstraint = initialConstraint.substitute(callSubstitutor) val (lower, upper) = substituteNotFixedVariables( substitutedConstraint.a as KotlinType, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt index 5cbbf2e05f7..47867c27904 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt @@ -205,7 +205,9 @@ class PostponedArgumentsAnalyzer( val variable = variableWithConstraints.typeVariable c.getBuilder().unmarkPostponedVariable(variable) - c.getBuilder().addEqualityConstraint(variable.defaultType(c), resultType, BuilderInferencePosition) + + // We add <: TypeVariable(T) to be able to contribute type info from several builder inference lambdas + c.getBuilder().addSubtypeConstraint(resultType, variable.defaultType(c), BuilderInferencePosition) } } } 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 5f4ce7883a0..b4ffa9a2c96 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 @@ -13,6 +13,7 @@ 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 @@ -178,31 +179,34 @@ class KotlinConstraintSystemCompleter( val lambdaArguments = postponedArguments.filterIsInstance().takeIf { it.isNotEmpty() } ?: return false val useBuilderInferenceWithoutAnnotation = languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceWithoutAnnotation) + val allNotFixedInputTypeVariables = mutableSetOf() - return lambdaArguments.any { argument -> + for (argument in lambdaArguments) { if (!argument.atom.hasBuilderInferenceAnnotation && !useBuilderInferenceWithoutAnnotation) - return@any false + continue val notFixedInputTypeVariables = argument.inputTypes .map { it.extractTypeVariables() }.flatten().filter { it !in fixedTypeVariables } - if (notFixedInputTypeVariables.isEmpty()) return@any false + if (notFixedInputTypeVariables.isEmpty()) continue + + allNotFixedInputTypeVariables.addAll(notFixedInputTypeVariables) for (variable in notFixedInputTypeVariables) { getBuilder().markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable) } analyze(argument) - - val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - this, notFixedInputTypeVariables, postponedArguments, completionMode, topLevelType - ) - - // continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared - // (after analysing a lambda with the builder inference) - // otherwise we report "not enough type information" error - return variableForFixation?.hasProperConstraint == true } + + val variableForFixation = variableFixationFinder.findFirstVariableForFixation( + this, allNotFixedInputTypeVariables.toList(), 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( diff --git a/compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt b/compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt new file mode 100644 index 00000000000..6f120990447 --- /dev/null +++ b/compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt @@ -0,0 +1,63 @@ +// WITH_RUNTIME +// DONT_TARGET_EXACT_BACKEND: WASM +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM + +import kotlin.experimental.ExperimentalTypeInference + +class In { + fun contribute(x: K) {} +} + +class Out { + fun get(): K = null as K +} + +class Inv { + fun get(): K = null as K +} + +interface A +class B: A +class C: A + +@OptIn(ExperimentalTypeInference::class) +fun build1(@BuilderInference builderAction1: In.() -> Unit, @BuilderInference builderAction2: In.() -> Unit): K = 1 as K + +@OptIn(ExperimentalTypeInference::class) +fun build2(@BuilderInference builderAction1: In.() -> Unit, @BuilderInference builderAction2: In.() -> Unit): K = B() as K + +@OptIn(ExperimentalTypeInference::class) +fun build3(@BuilderInference builderAction1: Out.() -> Unit, @BuilderInference builderAction2: Out.() -> Unit): K = 1 as K + +@OptIn(ExperimentalTypeInference::class) +fun build4(@BuilderInference builderAction1: Out.() -> Unit, @BuilderInference builderAction2: Out.() -> Unit): K = B() as K + +@OptIn(ExperimentalTypeInference::class) +fun build5(@BuilderInference builderAction1: Inv.() -> Unit, @BuilderInference builderAction2: Inv.() -> Unit): K = 1 as K + +@OptIn(ExperimentalTypeInference::class) +fun build6(@BuilderInference builderAction1: Inv.() -> Unit, @BuilderInference builderAction2: Inv.() -> Unit): K = B() as K + +@OptIn(ExperimentalStdlibApi::class) +fun box(): String { + val x1 = build1({ contribute(1f) }, { contribute(1.0) }) + & Number}")!>x1 + + val y1 = build2({ contribute(B()) }, { contribute(C()) }) + y1 + + val x2 = build3({ val x: Float = get() }, { val x: Double = get() }) + & Number}")!>x2 + + val y2 = build4({ val x: B = get() }, { val x: C = get() }) + y2 + + val x3 = build3({ val x: Float = get() }, { val x: Double = get() }) + & Number}")!>x2 + + val y3 = build4({ val x: B = get() }, { val x: C = get() }) + y2 + + return "OK" +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 2127fd2df00..8cc2218ee10 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -17917,6 +17917,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt"); } + @Test + @TestMetadata("cstBasedOnTwoBuilderInferenceLambda.kt") + public void testCstBasedOnTwoBuilderInferenceLambda() throws Exception { + runTest("compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt"); + } + @Test @TestMetadata("intersect.kt") public void testIntersect() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 3058414f7c1..97631f70bdd 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -18055,6 +18055,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt"); } + @Test + @TestMetadata("cstBasedOnTwoBuilderInferenceLambda.kt") + public void testCstBasedOnTwoBuilderInferenceLambda() throws Exception { + runTest("compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt"); + } + @Test @TestMetadata("inferFromExpectedType.kt") public void testInferFromExpectedType() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 5caceb86060..f2a56017596 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -14845,6 +14845,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt"); } + @TestMetadata("cstBasedOnTwoBuilderInferenceLambda.kt") + public void testCstBasedOnTwoBuilderInferenceLambda() throws Exception { + runTest("compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt"); + } + @TestMetadata("intersect.kt") public void testIntersect() throws Exception { runTest("compiler/testData/codegen/box/inference/builderInference/intersect.kt");