From e9941f8c12d4bf90acb52d942e27cc5134427bb7 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Mon, 16 Dec 2019 19:02:03 +0300 Subject: [PATCH] [NI] Implement algorithm for completion mode selection Current selection of completion mode for call is not always correct in case of full mode, and sometimes too conservative in case of partial mode. Updated algorithm checks constraints wrt position of type variables in return type and in other related constraints. Full completion happens if proper constraint requirements are satisfied for variables. --- ...irOldFrontendDiagnosticsTestGenerated.java | 33 ++++ .../components/CompletionModeCalculator.kt | 176 ++++++++++++++++++ .../calls/components/KotlinCallCompleter.kt | 65 +------ .../tests/inference/completion/basic.fir.kt | 70 +++++++ .../tests/inference/completion/basic.kt | 70 +++++++ .../tests/inference/completion/basic.txt | 58 ++++++ .../tests/inference/completion/kt33166.fir.kt | 45 +++++ .../tests/inference/completion/kt33166.kt | 45 +++++ .../tests/inference/completion/kt33166.txt | 37 ++++ .../inference/completion/partialForIlt.fir.kt | 16 ++ .../inference/completion/partialForIlt.kt | 16 ++ .../inference/completion/partialForIlt.txt | 8 + .../inference/completion/withExact.fir.kt | 26 +++ .../tests/inference/completion/withExact.kt | 26 +++ .../tests/inference/completion/withExact.txt | 42 +++++ .../checkers/DiagnosticsTestGenerated.java | 33 ++++ .../DiagnosticsUsingJavacTestGenerated.java | 33 ++++ 17 files changed, 735 insertions(+), 64 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/basic.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/basic.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/basic.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/kt33166.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/kt33166.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/kt33166.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/partialForIlt.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/partialForIlt.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/withExact.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/withExact.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/withExact.txt diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index 563c3c35c99..c99eb06e260 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -10362,6 +10362,39 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte } } + @TestMetadata("compiler/testData/diagnostics/tests/inference/completion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Completion extends AbstractFirOldFrontendDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); + } + + @TestMetadata("kt33166.kt") + public void testKt33166() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); + } + + @TestMetadata("partialForIlt.kt") + public void testPartialForIlt() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); + } + + @TestMetadata("withExact.kt") + public void testWithExact() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/inference/constraints") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt new file mode 100644 index 00000000000..ba3831f2486 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode +import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.model.* +import java.util.* + +typealias CsCompleterContext = KotlinConstraintSystemCompleter.Context + +class CompletionModeCalculator { + companion object { + fun computeCompletionMode( + candidate: KotlinResolutionCandidate, + expectedType: UnwrappedType?, + returnType: UnwrappedType? + ): ConstraintSystemCompletionMode = with(candidate) { + val csCompleterContext = getSystem().asConstraintSystemCompleterContext() + + // Presence of expected type means that we are trying to complete outermost call => completion mode should be full + if (expectedType != null) return ConstraintSystemCompletionMode.FULL + + // This is questionable as null return type can be only for error call + if (returnType == null) return ConstraintSystemCompletionMode.PARTIAL + + // Full if return type for call has no type variables + if (csBuilder.isProperType(returnType)) return ConstraintSystemCompletionMode.FULL + + // For nested call with variables in return type check possibility of full completion + return CalculatorForNestedCall(returnType, csCompleterContext).computeCompletionMode() + } + } + + private class CalculatorForNestedCall( + private val returnType: UnwrappedType?, + private val csCompleterContext: CsCompleterContext + ) { + private enum class FixationDirection { + TO_SUBTYPE, TO_SUPERTYPE, EQUALITY + } + + private val fixationDirectionsForVariables = mutableMapOf() + private val variablesWithQueuedConstraints = mutableSetOf() + private val typesToProcess: Queue = ArrayDeque() + + fun computeCompletionMode(): ConstraintSystemCompletionMode = with(csCompleterContext) { + // Add fixation directions for variables based on effective variance in type + typesToProcess.add(returnType) + computeDirections() + + // If all variables have required proper constraint, run full completion + if (directionRequirementsForVariablesHold()) + return ConstraintSystemCompletionMode.FULL + + return ConstraintSystemCompletionMode.PARTIAL + } + + private fun CsCompleterContext.computeDirections() { + while (typesToProcess.isNotEmpty()) { + val type = typesToProcess.poll() ?: break + + fixationRequirementForTopLevel(type)?.let { directionForVariable -> + updateDirection(directionForVariable) + enqueueTypesFromConstraints(directionForVariable.variable) + } + + // find all variables in type and make requirements for them + type.contains { fromReturnType -> + for (directionForVariable in directionsForVariablesInTypeArguments(fromReturnType)) { + updateDirection(directionForVariable) + enqueueTypesFromConstraints(directionForVariable.variable) + } + false + } + } + } + + private fun enqueueTypesFromConstraints(variableWithConstraints: VariableWithConstraints) { + val variable = variableWithConstraints.typeVariable + if (variable !in variablesWithQueuedConstraints) { + for (constraint in variableWithConstraints.constraints) { + typesToProcess.add(constraint.type) + } + + variablesWithQueuedConstraints.add(variable) + } + } + + private fun CsCompleterContext.directionRequirementsForVariablesHold(): Boolean { + for ((variable, fixationDirection) in fixationDirectionsForVariables) { + if (!hasProperConstraint(variable, fixationDirection)) + return false + } + return true + } + + private fun updateDirection(directionForVariable: FixationDirectionForVariable) { + val (variable, newDirection) = directionForVariable + fixationDirectionsForVariables[variable]?.let { oldDirection -> + // To sub and to super are merged into equality, old equality stays + if (oldDirection != FixationDirection.EQUALITY && oldDirection != newDirection) + fixationDirectionsForVariables[variable] = FixationDirection.EQUALITY + } + fixationDirectionsForVariables[variable] = newDirection + } + + private data class FixationDirectionForVariable(val variable: VariableWithConstraints, val direction: FixationDirection) + + private fun CsCompleterContext.fixationRequirementForTopLevel(type: KotlinTypeMarker): FixationDirectionForVariable? { + return notFixedTypeVariables[type.typeConstructor()]?.let { + FixationDirectionForVariable(it, FixationDirection.TO_SUBTYPE) + } + } + + private fun CsCompleterContext.directionsForVariablesInTypeArguments(type: KotlinTypeMarker): List { + assert(type.argumentsCount() == type.typeConstructor().parametersCount()) { + "Arguments and parameters count don't match for type $type. " + + "Arguments: ${type.argumentsCount()}, parameters: ${type.typeConstructor().parametersCount()}" + } + + val directionsForVariables = mutableListOf() + + for (position in 0 until type.argumentsCount()) { + val argument = type.getArgument(position) + if (!argument.getType().mayBeTypeVariable()) + continue + + val variableWithConstraints = notFixedTypeVariables[argument.getType().typeConstructor()] ?: continue + + val parameter = type.typeConstructor().getParameter(position) + val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance()) + ?: TypeVariance.OUT // Discuss + + val direction = when (effectiveVariance) { + TypeVariance.IN -> FixationDirection.TO_SUPERTYPE + TypeVariance.OUT -> FixationDirection.TO_SUBTYPE + TypeVariance.INV -> FixationDirection.EQUALITY + } + + val requirement = FixationDirectionForVariable(variableWithConstraints, direction) + directionsForVariables.add(requirement) + } + + return directionsForVariables + } + + private fun CsCompleterContext.hasProperConstraint( + variableWithConstraints: VariableWithConstraints, + direction: FixationDirection + ): Boolean { + val constraints = variableWithConstraints.constraints + + // todo check correctness for @Exact + return constraints.isNotEmpty() && constraints.any { constraint -> + constraint.hasRequiredKind(direction) + && !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor() + && isProperType(constraint.type) + } + } + + private fun Constraint.hasRequiredKind(direction: FixationDirection) = when (direction) { + FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual() + FixationDirection.TO_SUPERTYPE -> kind.isUpper() || kind.isEqual() + FixationDirection.EQUALITY -> kind.isEqual() + } + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 31f774d6712..5fb2bbf093e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -53,7 +53,7 @@ class KotlinCallCompleter( return if (resolutionCallbacks.inferenceSession.shouldRunCompletion(candidate)) candidate.runCompletion( - candidate.computeCompletionMode(expectedType, returnType), + CompletionModeCalculator.computeCompletionMode(candidate, expectedType, returnType), diagnosticHolder, resolutionCallbacks ) @@ -208,69 +208,6 @@ class KotlinCallCompleter( csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom)) } - private fun KotlinResolutionCandidate.computeCompletionMode( - expectedType: UnwrappedType?, - currentReturnType: UnwrappedType? - ): ConstraintSystemCompletionMode { - // Presence of expected type means that we trying to complete outermost call => completion mode should be full - if (expectedType != null) return ConstraintSystemCompletionMode.FULL - - // This is questionable as null return type can be only for error call - if (currentReturnType == null) return ConstraintSystemCompletionMode.PARTIAL - - return when { - // Consider call foo(bar(x)), if return type of bar is a proper one, then we can complete resolve for bar => full completion mode - // Otherwise, we shouldn't complete bar until we process call foo - csBuilder.isProperType(currentReturnType) -> ConstraintSystemCompletionMode.FULL - - // Nested call is connected with the outer one through the UPPER constraint (returnType <: expectedOuterType) - // This means that there will be no new LOWER constraints => - // it's possible to complete call now if there are proper LOWER constraints - csBuilder.isTypeVariable(currentReturnType) -> - if (hasProperNonTrivialLowerConstraints(currentReturnType)) - ConstraintSystemCompletionMode.FULL - else - ConstraintSystemCompletionMode.PARTIAL - - // Return type has proper equal constraints => there is no need in the outer call - containsTypeVariablesWithProperEqualConstraints(currentReturnType) -> ConstraintSystemCompletionMode.FULL - - else -> ConstraintSystemCompletionMode.PARTIAL - } - } - - private fun KotlinResolutionCandidate.containsTypeVariablesWithProperEqualConstraints(type: UnwrappedType): Boolean { - for ((variableConstructor, variableWithConstraints) in csBuilder.currentStorage().notFixedTypeVariables) { - if (!type.contains { it.constructor == variableConstructor }) continue - - val constraints = variableWithConstraints.constraints - val onlyProperEqualConstraints = - constraints.isNotEmpty() && constraints.any { it.kind.isEqual() && csBuilder.isProperType(it.type) } - - if (!onlyProperEqualConstraints) return false - } - - return true - } - - private fun KotlinResolutionCandidate.hasProperNonTrivialLowerConstraints(typeVariable: UnwrappedType): Boolean { - assert(csBuilder.isTypeVariable(typeVariable)) { "$typeVariable is not a type variable" } - - val context = getSystem() as TypeSystemInferenceExtensionContext - val constructor = typeVariable.constructor - val variableWithConstraints = csBuilder.currentStorage().notFixedTypeVariables[constructor] ?: return false - val constraints = variableWithConstraints.constraints - return constraints.isNotEmpty() && constraints.anyOrAll(requireAll = typeVariable.hasExactAnnotation()) { - !it.type.typeConstructor(context).isIntegerLiteralTypeConstructor(context) && - (it.kind.isLower() || it.kind.isEqual()) && - csBuilder.isProperType(it.type) && - !trivialConstraintTypeInferenceOracle.isNotInterestingConstraint(it) - } - } - - private inline fun Iterable.anyOrAll(requireAll: Boolean, p: (T) -> Boolean): Boolean = - if (requireAll) all(p) else any(p) - fun KotlinResolutionCandidate.asCallResolutionResult( type: ConstraintSystemCompletionMode, diagnosticsHolder: KotlinDiagnosticsHolder.SimpleHolder diff --git a/compiler/testData/diagnostics/tests/inference/completion/basic.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/basic.fir.kt new file mode 100644 index 00000000000..84291207f65 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/basic.fir.kt @@ -0,0 +1,70 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +interface Bound +class Cls : Bound + +class Inv(val prop: T) +class In(param: I) +class InB(param: I) +class Out(val prop: O) + +fun id(arg: K): K = arg +fun makeInv(arg: W): Inv = TODO() +fun wrapOut(arg: O): Inv = TODO() +fun wrapIn(arg: I): Inv = TODO() + +fun test1(cls: Cls) { + id( + Inv(cls) + ) +} + +fun test2(cls: Cls) { + id>( + Inv(cls) + ) +} + +fun test3(cls: Cls) { + id>( + Out(cls) + ) +} + +fun test4(cls: Cls) { + id( + Out(cls) + ) +} + +fun test5(cls: Cls) { + id( + In(cls) + ) +} + +fun test6(cls: Cls) { + id>( + In(cls) + ) +} + +fun test7(cls: Cls) { + id( + wrapOut(cls) + ) +} + +fun test8(cls: Cls) { + // TODO + id( + wrapIn(cls) + ) +} + +fun test9(cls: Cls) { + id( + InB(cls) + ) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/basic.kt b/compiler/testData/diagnostics/tests/inference/completion/basic.kt new file mode 100644 index 00000000000..a0ee120c7c9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/basic.kt @@ -0,0 +1,70 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +interface Bound +class Cls : Bound + +class Inv(val prop: T) +class In(param: I) +class InB(param: I) +class Out(val prop: O) + +fun id(arg: K): K = arg +fun makeInv(arg: W): Inv = TODO() +fun wrapOut(arg: O): Inv = TODO() +fun wrapIn(arg: I): Inv = TODO() + +fun test1(cls: Cls) { + id( + ")!>Inv(cls) + ) +} + +fun test2(cls: Cls) { + id>( + ")!>Inv(cls) + ) +} + +fun test3(cls: Cls) { + id>( + ")!>Out(cls) + ) +} + +fun test4(cls: Cls) { + id( + ")!>Out(cls) + ) +} + +fun test5(cls: Cls) { + id( + ")!>In(cls) + ) +} + +fun test6(cls: Cls) { + id>( + ")!>In(cls) + ) +} + +fun test7(cls: Cls) { + id( + ")!>wrapOut(cls) + ) +} + +fun test8(cls: Cls) { + // TODO + id( + ")!>wrapIn(cls) + ) +} + +fun test9(cls: Cls) { + id( + ")!>InB(cls) + ) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/basic.txt b/compiler/testData/diagnostics/tests/inference/completion/basic.txt new file mode 100644 index 00000000000..a6ff4be5dc9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/basic.txt @@ -0,0 +1,58 @@ +package + +public fun id(/*0*/ arg: K): K +public fun makeInv(/*0*/ arg: W): Inv +public fun test1(/*0*/ cls: Cls): kotlin.Unit +public fun test2(/*0*/ cls: Cls): kotlin.Unit +public fun test3(/*0*/ cls: Cls): kotlin.Unit +public fun test4(/*0*/ cls: Cls): kotlin.Unit +public fun test5(/*0*/ cls: Cls): kotlin.Unit +public fun test6(/*0*/ cls: Cls): kotlin.Unit +public fun test7(/*0*/ cls: Cls): kotlin.Unit +public fun test8(/*0*/ cls: Cls): kotlin.Unit +public fun test9(/*0*/ cls: Cls): kotlin.Unit +public fun wrapIn(/*0*/ arg: I): Inv +public fun wrapOut(/*0*/ arg: O): Inv + +public interface Bound { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Cls : Bound { + public constructor Cls() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class In { + public constructor In(/*0*/ param: I) + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class InB { + public constructor InB(/*0*/ param: I) + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Inv { + public constructor Inv(/*0*/ prop: T) + public final val prop: T + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Out { + public constructor Out(/*0*/ prop: O) + public final val prop: O + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/kt33166.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/kt33166.fir.kt new file mode 100644 index 00000000000..257cfd0b79c --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/kt33166.fir.kt @@ -0,0 +1,45 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +// FILE: GenericRunnable.java + +public interface GenericRunnable { + T invoke(); +} + +// FILE: OurFuture.java + +public class OurFuture { + static OurFuture createOurFuture(T result) { + return null; + } + + public OurFuture compose(GenericRunnable> mapper) { + return null; + } +} + +// FILE: test.kt + +open class Either { + class Left(val a: L) : Either() +} + +fun f1(future: OurFuture, e: Either.Left) { + future.compose> { + val x = when { + true -> OurFuture.createOurFuture(e) + else -> throw Exception() + } + x + } +} + +fun f2(future: OurFuture, e: Either.Left) { + future.compose> { + when { + true -> OurFuture.createOurFuture(e) + else -> throw Exception() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/kt33166.kt b/compiler/testData/diagnostics/tests/inference/completion/kt33166.kt new file mode 100644 index 00000000000..f2e008933fe --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/kt33166.kt @@ -0,0 +1,45 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +// FILE: GenericRunnable.java + +public interface GenericRunnable { + T invoke(); +} + +// FILE: OurFuture.java + +public class OurFuture { + static OurFuture createOurFuture(T result) { + return null; + } + + public OurFuture compose(GenericRunnable> mapper) { + return null; + } +} + +// FILE: test.kt + +open class Either { + class Left(val a: L) : Either() +} + +fun f1(future: OurFuture, e: Either.Left) { + future.compose> { + val x = when { + true -> OurFuture.createOurFuture(e) + else -> throw Exception() + } + x + } +} + +fun f2(future: OurFuture, e: Either.Left) { + future.compose> { + when { + true -> OurFuture.createOurFuture(e) + else -> throw Exception() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/kt33166.txt b/compiler/testData/diagnostics/tests/inference/completion/kt33166.txt new file mode 100644 index 00000000000..f0a742ae4bd --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/kt33166.txt @@ -0,0 +1,37 @@ +package + +public fun f1(/*0*/ future: OurFuture, /*1*/ e: Either.Left): kotlin.Unit +public fun f2(/*0*/ future: OurFuture, /*1*/ e: Either.Left): kotlin.Unit + +public open class Either { + public constructor Either() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class Left : Either { + public constructor Left(/*0*/ a: L) + public final val a: L + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public interface GenericRunnable { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract operator fun invoke(): T! + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class OurFuture { + public constructor OurFuture() + public open fun compose(/*0*/ mapper: GenericRunnable!>!): OurFuture! + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public/*package*/ open fun createOurFuture(/*0*/ result: T!): OurFuture! +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.fir.kt new file mode 100644 index 00000000000..0308772d09d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.fir.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun takeByte(ilt: Byte) {} +fun takeShort(ilt: Short) {} +fun takeInt(ilt: Int) {} +fun takeLong(ilt: Long) {} + +fun id(arg: T): T = arg + +fun test() { + takeByte(id(42)) + takeShort(id(42)) + takeInt(id(42)) + takeLong(id(42)) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt b/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt new file mode 100644 index 00000000000..d6d238bf7e8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun takeByte(ilt: Byte) {} +fun takeShort(ilt: Short) {} +fun takeInt(ilt: Int) {} +fun takeLong(ilt: Long) {} + +fun id(arg: T): T = arg + +fun test() { + takeByte(id(42)) + takeShort(id(42)) + takeInt(id(42)) + takeLong(id(42)) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.txt b/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.txt new file mode 100644 index 00000000000..aae3c8f5b0c --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/partialForIlt.txt @@ -0,0 +1,8 @@ +package + +public fun id(/*0*/ arg: T): T +public fun takeByte(/*0*/ ilt: kotlin.Byte): kotlin.Unit +public fun takeInt(/*0*/ ilt: kotlin.Int): kotlin.Unit +public fun takeLong(/*0*/ ilt: kotlin.Long): kotlin.Unit +public fun takeShort(/*0*/ ilt: kotlin.Short): kotlin.Unit +public fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/inference/completion/withExact.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/withExact.fir.kt new file mode 100644 index 00000000000..8a9c36b2f74 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/withExact.fir.kt @@ -0,0 +1,26 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + +import kotlin.internal.Exact + +class Inv(val arg: I) +class InvExact(val arg: @kotlin.internal.Exact E) + +interface Base +class Derived : Base +class Other : Base + +fun id(arg: K): K = arg + +fun test1(arg: Derived) { + id>(Inv(arg)) + id>(InvExact(arg)) +} + +fun Inv<@Exact R>.select(first: R, second: R): R = TODO() + +fun test2(derived: Derived, other: Other) { + Inv(derived).select(derived, other) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/withExact.kt b/compiler/testData/diagnostics/tests/inference/completion/withExact.kt new file mode 100644 index 00000000000..34e66140ba0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/withExact.kt @@ -0,0 +1,26 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + +import kotlin.internal.Exact + +class Inv(val arg: I) +class InvExact(val arg: @kotlin.internal.Exact E) + +interface Base +class Derived : Base +class Other : Base + +fun id(arg: K): K = arg + +fun test1(arg: Derived) { + id>(Inv(arg)) + id>(InvExact(arg)) +} + +fun Inv<@Exact R>.select(first: R, second: R): R = TODO() + +fun test2(derived: Derived, other: Other) { + Inv(derived).select(derived, other) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/withExact.txt b/compiler/testData/diagnostics/tests/inference/completion/withExact.txt new file mode 100644 index 00000000000..4c0efdc7abc --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/withExact.txt @@ -0,0 +1,42 @@ +package + +public fun id(/*0*/ arg: K): K +public fun test1(/*0*/ arg: Derived): kotlin.Unit +public fun test2(/*0*/ derived: Derived, /*1*/ other: Other): kotlin.Unit +public fun Inv.select(/*0*/ first: R, /*1*/ second: R): R + +public interface Base { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Derived : Base { + public constructor Derived() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Inv { + public constructor Inv(/*0*/ arg: I) + public final val arg: I + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class InvExact { + public constructor InvExact(/*0*/ arg: E) + public final val arg: E + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Other : Base { + public constructor Other() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8b64711d43d..005d0d545cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10369,6 +10369,39 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/inference/completion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Completion extends AbstractDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); + } + + @TestMetadata("kt33166.kt") + public void testKt33166() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); + } + + @TestMetadata("partialForIlt.kt") + public void testPartialForIlt() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); + } + + @TestMetadata("withExact.kt") + public void testWithExact() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/inference/constraints") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index e831d6de918..ecabe26b1f5 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -10364,6 +10364,39 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing } } + @TestMetadata("compiler/testData/diagnostics/tests/inference/completion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Completion extends AbstractDiagnosticsUsingJavacTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); + } + + @TestMetadata("kt33166.kt") + public void testKt33166() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); + } + + @TestMetadata("partialForIlt.kt") + public void testPartialForIlt() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); + } + + @TestMetadata("withExact.kt") + public void testWithExact() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/inference/constraints") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)