diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 8d8e79f06ba..970c5c67d5f 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -14457,6 +14457,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/inference/builderInference/labaledCall.kt"); } + @Test + @TestMetadata("multiLambdaRestriction.kt") + public void testMultiLambdaRestriction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt"); + } + + @Test + @TestMetadata("multiLambdaRestrictionDisabled.kt") + public void testMultiLambdaRestrictionDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt"); + } + @Test @TestMetadata("propertySubstitution.kt") public void testPropertySubstitution() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 99857507fec..0004dacaa56 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -14457,6 +14457,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/inference/builderInference/labaledCall.kt"); } + @Test + @TestMetadata("multiLambdaRestriction.kt") + public void testMultiLambdaRestriction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt"); + } + + @Test + @TestMetadata("multiLambdaRestrictionDisabled.kt") + public void testMultiLambdaRestrictionDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt"); + } + @Test @TestMetadata("propertySubstitution.kt") public void testPropertySubstitution() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 29345c9a08a..0a20c57bccd 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -14457,6 +14457,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/inference/builderInference/labaledCall.kt"); } + @Test + @TestMetadata("multiLambdaRestriction.kt") + public void testMultiLambdaRestriction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt"); + } + + @Test + @TestMetadata("multiLambdaRestrictionDisabled.kt") + public void testMultiLambdaRestrictionDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt"); + } + @Test @TestMetadata("propertySubstitution.kt") public void testPropertySubstitution() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 2ef6afdf088..7664f6014ca 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -861,6 +861,8 @@ public interface Errors { DiagnosticFactory3 OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES = DiagnosticFactory3.create(ERROR); DiagnosticFactory3 STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY = DiagnosticFactory3.create(ERROR); DiagnosticFactory4 STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY = DiagnosticFactory4.create(ERROR); + DiagnosticFactory2 BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION = DiagnosticFactory2.create(ERROR); + DiagnosticFactory1>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1>> UNRESOLVED_REFERENCE_WRONG_RECEIVER = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index dd3ca330f81..1c9decc2988 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -944,6 +944,7 @@ public class DefaultErrorMessages { STRING, STRING, STRING); MAP.put(STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY, "The type of an argument hasn''t been inferred yet. To disambiguate this call, explicitly cast it to `{0}` if you want the builder''s type parameter(s) `{1}` to be inferred to `{2}`.", RENDER_TYPE, STRING, STRING); MAP.put(STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY, "The type of a receiver hasn''t been inferred yet. To disambiguate this call, explicitly cast it to `{0}` if you want the builder''s type parameter(s) `{1}` to be inferred to `{2}`.", RENDER_TYPE, STRING, STRING, null); + MAP.put(BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION, "Unstable inference behaviour with multiple lambdas. Please either specify type argument for generic parameter `{0}` of `{1}` explicitly or add @BuilderInference to corresponding parameter declaration", TO_STRING, TO_STRING); MAP.put(NONE_APPLICABLE, "None of the following functions can be called with the arguments supplied: {0}", AMBIGUOUS_CALLS); MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS); MAP.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: {0}", AMBIGUOUS_CALLS); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index b10820174e8..cd7edadfabf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -10,9 +10,11 @@ import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.* import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce +import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isNull import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis @@ -319,6 +321,19 @@ class DiagnosticReporterByTrackingStrategy( ) ) } + + MultiLambdaBuilderInferenceRestriction::class.java -> { + diagnostic as MultiLambdaBuilderInferenceRestriction + val typeParameter = diagnostic.typeParameter as? TypeParameterDescriptor + + trace.reportDiagnosticOnce( + BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION.on( + callArgument.psiCallArgument.valueArgument.asElement(), + typeParameter?.name ?: SpecialNames.NO_NAME_PROVIDED, + typeParameter?.containingDeclaration?.name ?: SpecialNames.NO_NAME_PROVIDED, + ) + ) + } } } 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 b91717121a7..65e3fe4b0b6 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 @@ -204,6 +204,14 @@ class PostponedArgumentsAnalyzer( return } + // WARN: Following type constraint system unification algorithm is incorrect, + // as in fact direction of constraint should depend on projection direction + // To perform constraint unification properly, original constraints should be + // unified instead of simple result type based constraint + // Other possible solution is to add equality constraint, but it will be too strict + // and will limit usability + // Nevertheless, proper design should be done before fixing this + // Causes KT-53740 for ((constructor, resultType) in postponedVariables) { val variableWithConstraints = constraintSystemBuilder.currentStorage().notFixedTypeVariables[constructor] ?: continue val variable = variableWithConstraints.typeVariable 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 7e7bd091a01..c69df4f5db8 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 @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.types.error.ErrorTypeKind import org.jetbrains.kotlin.types.error.ErrorUtils 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 @@ -158,7 +159,7 @@ class KotlinConstraintSystemCompleter( // 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 + completionMode, topLevelAtoms, topLevelType, postponedArguments, collectVariablesFromContext, diagnosticsHolder, analyze ) if (areThereAppearedProperConstraintsForSomeVariable) @@ -208,6 +209,7 @@ class KotlinConstraintSystemCompleter( topLevelType: UnwrappedType, postponedArguments: List, collectVariablesFromContext: Boolean, + diagnosticsHolder: KotlinDiagnosticsHolder, analyze: (PostponedResolvedAtom) -> Unit ): Boolean { if (completionMode == ConstraintSystemCompletionMode.PARTIAL) return false @@ -218,24 +220,55 @@ class KotlinConstraintSystemCompleter( if (!useBuilderInferenceOnlyIfNeeded) return false val lambdaArguments = postponedArguments.filterIsInstance().takeIf { it.isNotEmpty() } ?: return false + + fun ResolvedLambdaAtom.notFixedInputTypeVariables(): List = + inputTypes.flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables } + val useBuilderInferenceWithoutAnnotation = languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceWithoutAnnotation) + val checkForDangerousBuilderInference = + !languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction) + + // Let's call builder lambda (BL) a lambda that has non-zero not fixed input type variables in + // type arguments of it's input types + // ex: MutableList.() -> Unit + // During type inference of call-site such lambda will be considered BL, if + // T not fixed yet + // Given we have two or more builder lambdas among postponed arguments, it could result in incorrect type inference due to + // incorrect constraint propagation into common system + // See KT-53740 + // Constraint propagation into common system happens at + // org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer.applyResultsOfAnalyzedLambdaToCandidateSystem + val dangerousBuilderInferenceWithoutAnnotation = + lambdaArguments.size >= 2 && lambdaArguments.count { it.notFixedInputTypeVariables().isNotEmpty() } >= 2 + val builder = getBuilder() for (argument in lambdaArguments) { - if (!argument.atom.hasBuilderInferenceAnnotation && !useBuilderInferenceWithoutAnnotation) - continue + val reallyHasBuilderInferenceAnnotation = argument.atom.hasBuilderInferenceAnnotation + + // no annotation and builder inference without annotation is disabled + if (!reallyHasBuilderInferenceAnnotation && !useBuilderInferenceWithoutAnnotation) continue // Imitate having builder inference annotation. TODO: Remove after getting rid of @BuilderInference - if (!argument.atom.hasBuilderInferenceAnnotation && useBuilderInferenceWithoutAnnotation) { + if (!reallyHasBuilderInferenceAnnotation) { argument.atom.hasBuilderInferenceAnnotation = true } - val notFixedInputTypeVariables = argument.inputTypes - .flatMap { it.extractTypeVariables() }.filter { it !in fixedTypeVariables } + val notFixedInputTypeVariables = argument.notFixedInputTypeVariables() + // lambda is subject to builder inference past this point if (notFixedInputTypeVariables.isEmpty()) continue + // we have dangerous inference situation + // if lambda annotated with BuilderInference it's probably safe, due to type shape + // otherwise report multi-lambda builder inference restriction diagnostic + if (checkForDangerousBuilderInference && dangerousBuilderInferenceWithoutAnnotation && !reallyHasBuilderInferenceAnnotation) { + for (variable in notFixedInputTypeVariables) { + diagnosticsHolder.addDiagnostic(MultiLambdaBuilderInferenceRestriction(argument.atom, variable.typeParameter)) + } + } + for (variable in notFixedInputTypeVariables) { builder.markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt index bc4ab8f78d9..284620868db 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.* import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.model.TypeParameterMarker interface TransformableToWarning { fun transformToWarning(): T? @@ -65,6 +66,13 @@ class NonVarargSpread(val argument: KotlinCallArgument) : KotlinCallDiagnostic(I override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentSpread(argument, this) } +class MultiLambdaBuilderInferenceRestriction( + val argument: KotlinCallArgument, + val typeParameter: TypeParameterMarker? +) : KotlinCallDiagnostic(RESOLVED) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) +} + class MixingNamedAndPositionArguments(override val argument: KotlinCallArgument) : InapplicableArgumentDiagnostic() class NamedArgumentNotAllowed(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) : KotlinCallDiagnostic(INAPPLICABLE) { diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt index 3edd24a0e44..4b83e8cb632 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.fir.kt @@ -1,6 +1,7 @@ // WITH_STDLIB // !DIAGNOSTICS: -OPT_IN_USAGE_ERROR // For FIR, see: KT-50704 +import kotlin.experimental.ExperimentalTypeInference @JvmName("foo1") fun foo(x: Inv) {} @@ -201,7 +202,8 @@ interface Foo2 { fun entries(): MutableSet> } -fun twoBuilderLambdas(block: Foo.() -> Unit, block2: Foo2.() -> Unit) {} +@OptIn(ExperimentalTypeInference::class) +fun twoBuilderLambdas(@BuilderInference block: Foo.() -> Unit, @BuilderInference block2: Foo2.() -> Unit) {} fun test() { twoBuilderLambdas( diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.kt b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.kt index 606c68cf020..96f206e3b55 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.kt @@ -1,6 +1,7 @@ // WITH_STDLIB // !DIAGNOSTICS: -OPT_IN_USAGE_ERROR // For FIR, see: KT-50704 +import kotlin.experimental.ExperimentalTypeInference @JvmName("foo1") fun foo(x: Inv) {} @@ -201,7 +202,8 @@ interface Foo2 { fun entries(): MutableSet> } -fun twoBuilderLambdas(block: Foo.() -> Unit, block2: Foo2.() -> Unit) {} +@OptIn(ExperimentalTypeInference::class) +fun twoBuilderLambdas(@BuilderInference block: Foo.() -> Unit, @BuilderInference block2: Foo2.() -> Unit) {} fun test() { twoBuilderLambdas( diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.txt b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.txt index 1ea391c302c..54bb83f7e20 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.txt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt49828.txt @@ -19,7 +19,7 @@ public fun foo11(/*0*/ x: kotlin.collections.MutableSet>): kotlin.Unit public fun main(): kotlin.Unit public fun test(): kotlin.Unit -public fun twoBuilderLambdas(/*0*/ block: Foo.() -> kotlin.Unit, /*1*/ block2: Foo2.() -> kotlin.Unit): kotlin.Unit +@kotlin.OptIn(markerClass = {kotlin.experimental.ExperimentalTypeInference::class}) public fun twoBuilderLambdas(/*0*/ @kotlin.BuilderInference block: Foo.() -> kotlin.Unit, /*1*/ @kotlin.BuilderInference block2: Foo2.() -> kotlin.Unit): kotlin.Unit public fun kotlin.Int.bar(): kotlin.Unit @kotlin.jvm.JvmName(name = "bar1") public fun kotlin.String.bar(): kotlin.Unit public fun kotlin.Int.foo0003(/*0*/ y: kotlin.Number, /*1*/ z: kotlin.String): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.fir.kt new file mode 100644 index 00000000000..5ec0bf50632 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.fir.kt @@ -0,0 +1,55 @@ +// WITH_STDLIB +fun test() { + foo( + flow { emit(0) } + ) { it.collect {} } + + // 0. Initial + // W <: Any / declared upper bound + // FlowCollector.() -> Unit <: FlowCollector.() -> Unit / from Argument { emit(0) } + // F <: Any / declared upper bound + // Flow <: F / from Argument flow { emit(0) } + // Scope.(F) -> Unit -> Scope.(F) -> Unit / from Argument { it.collect() } + + // 1. after analyze for { emit(0 } + // Unit <: Unit / from Lambda argument, probably { emit(0) } + // Int <: W / from For builder inference call + // Flow <: F / from For builder inference call + + // 2. after analyze for { it.collect {} } + // Unit <: Unit / from Lambda argument, probably { it.collect {} } + // Flow<*> <: F / from For builder inference call + // ERROR_TYPE <: W / from For builder inference call +} + +fun foo( + bar: F, + block: Scope.(F) -> Unit +) {} + +@OptIn(kotlin.experimental.ExperimentalTypeInference::class) +fun flow(@BuilderInference block: FlowCollector.()->Unit): Flow { + val collector = FlowCollectorImpl() + collector.block() + return object : Flow { + override fun collect(collector: FlowCollector) { + } + } +} + +class Scope + +interface Flow { + fun collect(collector: FlowCollector) +} + +fun interface FlowCollector { + + fun emit(value: I) +} + +class FlowCollectorImpl : FlowCollector { + override fun emit(value: C) {} +} + +fun Flow<*>.collect() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.kt b/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.kt index 8d64d72ff72..e2c112f8304 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt53422.kt @@ -1,10 +1,26 @@ -// FIR_IDENTICAL // WITH_STDLIB // SKIP_TXT fun test() { foo( flow { emit(0) } - ) { it.collect {} } + ) { it.collect {} } + + // 0. Initial + // W <: Any / declared upper bound + // FlowCollector.() -> Unit <: FlowCollector.() -> Unit / from Argument { emit(0) } + // F <: Any / declared upper bound + // Flow <: F / from Argument flow { emit(0) } + // Scope.(F) -> Unit -> Scope.(F) -> Unit / from Argument { it.collect() } + + // 1. after analyze for { emit(0 } + // Unit <: Unit / from Lambda argument, probably { emit(0) } + // Int <: W / from For builder inference call + // Flow <: F / from For builder inference call + + // 2. after analyze for { it.collect {} } + // Unit <: Unit / from Lambda argument, probably { it.collect {} } + // Flow<*> <: F / from For builder inference call + // ERROR_TYPE <: W / from For builder inference call } fun foo( @@ -13,7 +29,7 @@ fun foo( ) {} @OptIn(kotlin.experimental.ExperimentalTypeInference::class) -fun flow(BuilderInference block: FlowCollector.()->Unit): Flow { +fun flow(@BuilderInference block: FlowCollector.()->Unit): Flow { val collector = FlowCollectorImpl() collector.block() return object : Flow { diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/kt53639.kt b/compiler/testData/diagnostics/tests/inference/builderInference/kt53639.kt index 9136d0093eb..34ba219e2e5 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/kt53639.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/kt53639.kt @@ -12,7 +12,7 @@ data class Output(val source: InputWrapper>) fun main2(input: InputWrapper): Output { val output = input.doMapping( foo = { buildList { add("this is List") } }, - bar = { it.isNotEmpty() }, + bar = { it.isNotEmpty() }, ) return Output(source = output) diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.fir.kt new file mode 100644 index 00000000000..64de8362a14 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.fir.kt @@ -0,0 +1,70 @@ +// WITH_STDLIB +// SKIP_TXT + +fun List.myExt() {} +fun List.myGenericExt() {} + +fun a(first: R, second: (List) -> Unit) {} + +fun test1() { + a( + buildList { add("") }, + second = { + it.myGenericExt() + } + ) +} + + +fun b(first: () -> List, second: (List) -> Unit) {} + +fun test2() { + b( + first = { + buildList { add("") } + }, + second = { + it.myExt() // Note: must be extension to add constraints + } + ) +} + +fun select(a: Q, b: Q): Q = a + +// Note: no builder inference annotation +fun myBuildList(builder: MutableList.() -> Unit): List { + val list = mutableListOf() + list.builder() + return list +} + +fun test3() { + select( + buildList { add("") }, + buildList { add(1) } + ) + + select ( + myBuildList { add("") }, + myBuildList { add(1) }, + ) + + select ( + run { myBuildList { add("") } }, + myBuildList { add(1) }, + ) +} + +fun buildPartList(left: MutableList.() -> Unit, right: MutableList.() -> Unit): List { + val list = mutableListOf() + list.left() + list.right() + return list +} + +fun test4() { + buildPartList( + left = { add(1) }, + right = { add("") } + ) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt b/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt new file mode 100644 index 00000000000..611b9170fb6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt @@ -0,0 +1,70 @@ +// WITH_STDLIB +// SKIP_TXT + +fun List.myExt() {} +fun List.myGenericExt() {} + +fun a(first: R, second: (List) -> Unit) {} + +fun test1() { + a( + buildList { add("") }, + second = { + it.myGenericExt() + } + ) +} + + +fun b(first: () -> List, second: (List) -> Unit) {} + +fun test2() { + b( + first = { + buildList { add("") } + }, + second = { + it.myExt() // Note: must be extension to add constraints + } + ) +} + +fun select(a: Q, b: Q): Q = a + +// Note: no builder inference annotation +fun myBuildList(builder: MutableList.() -> Unit): List { + val list = mutableListOf() + list.builder() + return list +} + +fun test3() { + select( + buildList { add("") }, + buildList { add(1) } + ) + + select ( + myBuildList { add("") }, + myBuildList { add(1) }, + ) + + select ( + run { myBuildList { add("") } }, + myBuildList { add(1) }, + ) +} + +fun buildPartList(left: MutableList.() -> Unit, right: MutableList.() -> Unit): List { + val list = mutableListOf() + list.left() + list.right() + return list +} + +fun test4() { + buildPartList( + left = { add(1) }, + right = { add("") } + ) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt b/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt new file mode 100644 index 00000000000..3f85c79ab35 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt @@ -0,0 +1,72 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// LANGUAGE: +NoBuilderInferenceWithoutAnnotationRestriction +// SKIP_TXT + +fun List.myExt() {} +fun List.myGenericExt() {} + +fun a(first: R, second: (List) -> Unit) {} + +fun test1() { + a( + buildList { add("") }, + second = { + it.myGenericExt() + } + ) +} + + +fun b(first: () -> List, second: (List) -> Unit) {} + +fun test2() { + b( + first = { + buildList { add("") } + }, + second = { + it.myExt() // Note: must be extension to add constraints + } + ) +} + +fun select(a: Q, b: Q): Q = a + +// Note: no builder inference annotation +fun myBuildList(builder: MutableList.() -> Unit): List { + val list = mutableListOf() + list.builder() + return list +} + +fun test3() { + select( + buildList { add("") }, + buildList { add(1) } + ) + + select ( + myBuildList { add("") }, + myBuildList { add(1) }, + ) + + select ( + run { myBuildList { add("") } }, + myBuildList { add(1) }, + ) +} + +fun buildPartList(left: MutableList.() -> Unit, right: MutableList.() -> Unit): List { + val list = mutableListOf() + list.left() + list.right() + return list +} + +fun test4() { + buildPartList( + left = { add(1) }, + right = { add("") } + ) +} \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 8e10da119f8..77441d80500 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -14463,6 +14463,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inference/builderInference/labaledCall.kt"); } + @Test + @TestMetadata("multiLambdaRestriction.kt") + public void testMultiLambdaRestriction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt"); + } + + @Test + @TestMetadata("multiLambdaRestrictionDisabled.kt") + public void testMultiLambdaRestrictionDisabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt"); + } + @Test @TestMetadata("propertySubstitution.kt") public void testPropertySubstitution() throws Exception {