Be able to infer a type variable based on several builder inference lambdas
^KT-48329 Fixed
This commit is contained in:
committed by
TeamCityServer
parent
bf1e68a53f
commit
a1519b7b4e
+2
@@ -339,6 +339,8 @@ class BuilderInferenceSession(
|
|||||||
val callSubstitutor = storage.buildResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false)
|
val callSubstitutor = storage.buildResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false)
|
||||||
|
|
||||||
for (initialConstraint in storage.initialConstraints) {
|
for (initialConstraint in storage.initialConstraints) {
|
||||||
|
if (initialConstraint.position is BuilderInferencePosition) continue
|
||||||
|
|
||||||
val substitutedConstraint = initialConstraint.substitute(callSubstitutor)
|
val substitutedConstraint = initialConstraint.substitute(callSubstitutor)
|
||||||
val (lower, upper) = substituteNotFixedVariables(
|
val (lower, upper) = substituteNotFixedVariables(
|
||||||
substitutedConstraint.a as KotlinType,
|
substitutedConstraint.a as KotlinType,
|
||||||
|
|||||||
+3
-1
@@ -205,7 +205,9 @@ class PostponedArgumentsAnalyzer(
|
|||||||
val variable = variableWithConstraints.typeVariable
|
val variable = variableWithConstraints.typeVariable
|
||||||
|
|
||||||
c.getBuilder().unmarkPostponedVariable(variable)
|
c.getBuilder().unmarkPostponedVariable(variable)
|
||||||
c.getBuilder().addEqualityConstraint(variable.defaultType(c), resultType, BuilderInferencePosition)
|
|
||||||
|
// We add <inferred type> <: TypeVariable(T) to be able to contribute type info from several builder inference lambdas
|
||||||
|
c.getBuilder().addSubtypeConstraint(resultType, variable.defaultType(c), BuilderInferencePosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-12
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.model.*
|
|||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
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.model.safeSubstitute
|
||||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
@@ -178,31 +179,34 @@ class KotlinConstraintSystemCompleter(
|
|||||||
val lambdaArguments = postponedArguments.filterIsInstance<ResolvedLambdaAtom>().takeIf { it.isNotEmpty() } ?: return false
|
val lambdaArguments = postponedArguments.filterIsInstance<ResolvedLambdaAtom>().takeIf { it.isNotEmpty() } ?: return false
|
||||||
val useBuilderInferenceWithoutAnnotation =
|
val useBuilderInferenceWithoutAnnotation =
|
||||||
languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceWithoutAnnotation)
|
languageVersionSettings.supportsFeature(LanguageFeature.UseBuilderInferenceWithoutAnnotation)
|
||||||
|
val allNotFixedInputTypeVariables = mutableSetOf<TypeVariableTypeConstructorMarker>()
|
||||||
|
|
||||||
return lambdaArguments.any { argument ->
|
for (argument in lambdaArguments) {
|
||||||
if (!argument.atom.hasBuilderInferenceAnnotation && !useBuilderInferenceWithoutAnnotation)
|
if (!argument.atom.hasBuilderInferenceAnnotation && !useBuilderInferenceWithoutAnnotation)
|
||||||
return@any false
|
continue
|
||||||
|
|
||||||
val notFixedInputTypeVariables = argument.inputTypes
|
val notFixedInputTypeVariables = argument.inputTypes
|
||||||
.map { it.extractTypeVariables() }.flatten().filter { it !in fixedTypeVariables }
|
.map { it.extractTypeVariables() }.flatten().filter { it !in fixedTypeVariables }
|
||||||
|
|
||||||
if (notFixedInputTypeVariables.isEmpty()) return@any false
|
if (notFixedInputTypeVariables.isEmpty()) continue
|
||||||
|
|
||||||
|
allNotFixedInputTypeVariables.addAll(notFixedInputTypeVariables)
|
||||||
|
|
||||||
for (variable in notFixedInputTypeVariables) {
|
for (variable in notFixedInputTypeVariables) {
|
||||||
getBuilder().markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable)
|
getBuilder().markPostponedVariable(notFixedTypeVariables.getValue(variable).typeVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyze(argument)
|
analyze(argument)
|
||||||
|
|
||||||
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
|
|
||||||
this, notFixedInputTypeVariables, postponedArguments, completionMode, topLevelType
|
|
||||||
)
|
|
||||||
|
|
||||||
// continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
|
|
||||||
// (after analysing a lambda with the builder inference)
|
|
||||||
// otherwise we report "not enough type information" error
|
|
||||||
return variableForFixation?.hasProperConstraint == true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
|
||||||
|
this, allNotFixedInputTypeVariables.toList(), postponedArguments, completionMode, topLevelType
|
||||||
|
)
|
||||||
|
|
||||||
|
// continue completion (rerun stages) only if ready for fixation variables with proper constraints have appeared
|
||||||
|
// (after analysing a lambda with the builder inference)
|
||||||
|
// otherwise we don't continue and report "not enough type information" error
|
||||||
|
return variableForFixation?.hasProperConstraint == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformToAtomWithNewFunctionalExpectedType(
|
private fun transformToAtomWithNewFunctionalExpectedType(
|
||||||
|
|||||||
Vendored
+63
@@ -0,0 +1,63 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
|
import kotlin.experimental.ExperimentalTypeInference
|
||||||
|
|
||||||
|
class In<in K> {
|
||||||
|
fun contribute(x: K) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Out<out K> {
|
||||||
|
fun get(): K = null as K
|
||||||
|
}
|
||||||
|
|
||||||
|
class Inv<K> {
|
||||||
|
fun get(): K = null as K
|
||||||
|
}
|
||||||
|
|
||||||
|
interface A
|
||||||
|
class B: A
|
||||||
|
class C: A
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTypeInference::class)
|
||||||
|
fun <K> build1(@BuilderInference builderAction1: In<K>.() -> Unit, @BuilderInference builderAction2: In<K>.() -> Unit): K = 1 as K
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTypeInference::class)
|
||||||
|
fun <K> build2(@BuilderInference builderAction1: In<K>.() -> Unit, @BuilderInference builderAction2: In<K>.() -> Unit): K = B() as K
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTypeInference::class)
|
||||||
|
fun <K> build3(@BuilderInference builderAction1: Out<K>.() -> Unit, @BuilderInference builderAction2: Out<K>.() -> Unit): K = 1 as K
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTypeInference::class)
|
||||||
|
fun <K> build4(@BuilderInference builderAction1: Out<K>.() -> Unit, @BuilderInference builderAction2: Out<K>.() -> Unit): K = B() as K
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTypeInference::class)
|
||||||
|
fun <K> build5(@BuilderInference builderAction1: Inv<K>.() -> Unit, @BuilderInference builderAction2: Inv<K>.() -> Unit): K = 1 as K
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTypeInference::class)
|
||||||
|
fun <K> build6(@BuilderInference builderAction1: Inv<K>.() -> Unit, @BuilderInference builderAction2: Inv<K>.() -> Unit): K = B() as K
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
fun box(): String {
|
||||||
|
val x1 = build1({ contribute(1f) }, { contribute(1.0) })
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("{Comparable<*> & Number}")!>x1<!>
|
||||||
|
|
||||||
|
val y1 = build2({ contribute(B()) }, { contribute(C()) })
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("A")!>y1<!>
|
||||||
|
|
||||||
|
val x2 = build3({ val x: Float = get() }, { val x: Double = get() })
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("{Comparable<*> & Number}")!>x2<!>
|
||||||
|
|
||||||
|
val y2 = build4({ val x: B = get() }, { val x: C = get() })
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("A")!>y2<!>
|
||||||
|
|
||||||
|
val x3 = build3({ val x: Float = get() }, { val x: Double = get() })
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("{Comparable<*> & Number}")!>x2<!>
|
||||||
|
|
||||||
|
val y3 = build4({ val x: B = get() }, { val x: C = get() })
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("A")!>y2<!>
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+6
@@ -17923,6 +17923,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt");
|
runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("cstBasedOnTwoBuilderInferenceLambda.kt")
|
||||||
|
public void testCstBasedOnTwoBuilderInferenceLambda() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("intersect.kt")
|
@TestMetadata("intersect.kt")
|
||||||
public void testIntersect() throws Exception {
|
public void testIntersect() throws Exception {
|
||||||
|
|||||||
+6
@@ -18061,6 +18061,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt");
|
runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("cstBasedOnTwoBuilderInferenceLambda.kt")
|
||||||
|
public void testCstBasedOnTwoBuilderInferenceLambda() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("inferFromExpectedType.kt")
|
@TestMetadata("inferFromExpectedType.kt")
|
||||||
public void testInferFromExpectedType() throws Exception {
|
public void testInferFromExpectedType() throws Exception {
|
||||||
|
|||||||
+5
@@ -14850,6 +14850,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt");
|
runTest("compiler/testData/codegen/box/inference/builderInference/constraintsBetweenTwoStubVariables.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("cstBasedOnTwoBuilderInferenceLambda.kt")
|
||||||
|
public void testCstBasedOnTwoBuilderInferenceLambda() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inference/builderInference/cstBasedOnTwoBuilderInferenceLambda.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("intersect.kt")
|
@TestMetadata("intersect.kt")
|
||||||
public void testIntersect() throws Exception {
|
public void testIntersect() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inference/builderInference/intersect.kt");
|
runTest("compiler/testData/codegen/box/inference/builderInference/intersect.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user