From 105358dcf6dbdae920de00d996b4e6e8f89ea305 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 26 Aug 2022 16:24:11 +0200 Subject: [PATCH] K1: add diagnostic BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION Let's call builder lambdas (BL) a lambda that has non-fixed input type projection at the moment of lambda arguments analysis, such lambdas is a subject to be analyzed with builder inference Due to bug in constraint system joining algorithm, currently system of two or more such lambdas may lead to unsound type inference Diagnostic added here should be reported in case when there are two BL that shares a common constraint system, while not annotated with @BuilderInference, as a protection against aforementioned bug It's reported by ConstraintSystemCompleter when such situation has occurred during builder inference phase, it is the same place that decides wherever lambdas is subject to builder inference or not KT-53740 --- ...CompilerTestFE10TestdataTestGenerated.java | 12 ++++ ...irOldFrontendDiagnosticsTestGenerated.java | 12 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 12 ++++ .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 1 + .../DiagnosticReporterByTrackingStrategy.kt | 15 ++++ .../components/PostponedArgumentsAnalyzer.kt | 8 +++ .../KotlinConstraintSystemCompleter.kt | 45 ++++++++++-- .../calls/model/KotlinCallDiagnostics.kt | 8 +++ .../inference/builderInference/kt49828.fir.kt | 4 +- .../inference/builderInference/kt49828.kt | 4 +- .../inference/builderInference/kt49828.txt | 2 +- .../inference/builderInference/kt53422.fir.kt | 55 ++++++++++++++ .../inference/builderInference/kt53422.kt | 22 +++++- .../inference/builderInference/kt53639.kt | 2 +- .../multiLambdaRestriction.fir.kt | 70 ++++++++++++++++++ .../multiLambdaRestriction.kt | 70 ++++++++++++++++++ .../multiLambdaRestrictionDisabled.kt | 72 +++++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 12 ++++ 19 files changed, 415 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/builderInference/kt53422.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestriction.kt create mode 100644 compiler/testData/diagnostics/tests/inference/builderInference/multiLambdaRestrictionDisabled.kt 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 {