[NI] Select variable with proper non-trivial constraint first
This commit is contained in:
committed by
Mikhail Zarechenskiy
parent
0bf81aeec0
commit
c0d6eff97c
+28
-12
@@ -16,8 +16,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
|
||||
typealias VariableResolutionNode = FixationOrderCalculator.NodeWithDirection
|
||||
|
||||
class InferenceStepResolver(
|
||||
private val resultTypeResolver: ResultTypeResolver
|
||||
@@ -26,18 +32,28 @@ class InferenceStepResolver(
|
||||
* Resolves one or more of the `variables`.
|
||||
* Returns `true` if type variable resolution should stop.
|
||||
*/
|
||||
fun resolveVariables(c: KotlinCallCompleter.Context, variables: List<FixationOrderCalculator.NodeWithDirection>): Boolean {
|
||||
for ((variableWithConstraints, direction) in variables) {
|
||||
if (c.hasContradiction) return true
|
||||
val variable = variableWithConstraints.typeVariable
|
||||
val resultType = resultTypeResolver.findResultType(c.asResultTypeResolverContext(), variableWithConstraints, direction)
|
||||
if (resultType == null) {
|
||||
c.addError(NotEnoughInformationForTypeParameter(variable))
|
||||
break
|
||||
}
|
||||
c.fixVariable(variable, resultType)
|
||||
return false
|
||||
fun resolveVariables(c: KotlinCallCompleter.Context, variables: List<VariableResolutionNode>): Boolean {
|
||||
if (variables.isEmpty()) return true
|
||||
if (c.hasContradiction) return true
|
||||
|
||||
val nodeToResolve = variables.firstOrNull { it.variableWithConstraints.hasProperConstraint(c) } ?:
|
||||
variables.first()
|
||||
val (variableWithConstraints, direction) = nodeToResolve
|
||||
val variable = variableWithConstraints.typeVariable
|
||||
|
||||
val resultType = resultTypeResolver.findResultType(c.asResultTypeResolverContext(), variableWithConstraints, direction)
|
||||
if (resultType == null) {
|
||||
c.addError(NotEnoughInformationForTypeParameter(variable))
|
||||
return true
|
||||
}
|
||||
return true
|
||||
c.fixVariable(variable, resultType)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun VariableWithConstraints.hasProperConstraint(c: KotlinCallCompleter.Context) =
|
||||
constraints.any { !it.isTrivial() && c.canBeProper(it.type) }
|
||||
|
||||
private fun Constraint.isTrivial() =
|
||||
kind == ConstraintKind.LOWER && KotlinBuiltIns.isNothing(type) ||
|
||||
kind == ConstraintKind.UPPER && KotlinBuiltIns.isNullableAny(type)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fun <X, Y> foo(): (X) -> Y = TODO()
|
||||
|
||||
interface Inv2<A, B>
|
||||
|
||||
fun <T, R> check(x: T, y: R, f: (T) -> R): Inv2<T, R> = TODO()
|
||||
|
||||
fun test() = check("", 1, foo())
|
||||
|
||||
fun box(): String {
|
||||
val x: Inv2<String, Int> = test()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
FILE /fixationOrder1.kt
|
||||
FUN public fun <X, Y> foo(): (X) -> Y
|
||||
TYPE_PARAMETER <X>
|
||||
TYPE_PARAMETER <Y>
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='foo(): (X) -> Y'
|
||||
CALL 'TODO(): Nothing' type=kotlin.Nothing origin=null
|
||||
CLASS INTERFACE Inv2
|
||||
$this: VALUE_PARAMETER this@Inv2: Inv2<A, B>
|
||||
TYPE_PARAMETER <A>
|
||||
TYPE_PARAMETER <B>
|
||||
FUN FAKE_OVERRIDE public open override fun equals(other: kotlin.Any?): kotlin.Boolean
|
||||
$this: VALUE_PARAMETER this@Any: Any
|
||||
VALUE_PARAMETER value-parameter other: kotlin.Any?
|
||||
FUN FAKE_OVERRIDE public open override fun hashCode(): kotlin.Int
|
||||
$this: VALUE_PARAMETER this@Any: Any
|
||||
FUN FAKE_OVERRIDE public open override fun toString(): kotlin.String
|
||||
$this: VALUE_PARAMETER this@Any: Any
|
||||
FUN public fun <T, R> check(x: T, y: R, f: (T) -> R): Inv2<T, R>
|
||||
TYPE_PARAMETER <T>
|
||||
TYPE_PARAMETER <R>
|
||||
VALUE_PARAMETER value-parameter x: T
|
||||
VALUE_PARAMETER value-parameter y: R
|
||||
VALUE_PARAMETER value-parameter f: (T) -> R
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='check(T, R, (T) -> R): Inv2<T, R>'
|
||||
CALL 'TODO(): Nothing' type=kotlin.Nothing origin=null
|
||||
FUN public fun test(): Inv2<kotlin.String, kotlin.Int>
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='test(): Inv2<String, Int>'
|
||||
CALL 'check(String, Int, (String) -> Int): Inv2<String, Int>' type=Inv2<kotlin.String, kotlin.Int> origin=null
|
||||
<T>: String
|
||||
<R>: Int
|
||||
x: CONST String type=kotlin.String value=''
|
||||
y: CONST Int type=kotlin.Int value='1'
|
||||
f: CALL 'foo(): (String) -> Int' type=(kotlin.String) -> kotlin.Int origin=null
|
||||
<X>: String
|
||||
<Y>: Int
|
||||
FUN public fun box(): kotlin.String
|
||||
BLOCK_BODY
|
||||
VAR val x: Inv2<kotlin.String, kotlin.Int>
|
||||
CALL 'test(): Inv2<String, Int>' type=Inv2<kotlin.String, kotlin.Int> origin=null
|
||||
RETURN type=kotlin.Nothing from='box(): String'
|
||||
CONST String type=kotlin.String value='OK'
|
||||
@@ -0,0 +1,3 @@
|
||||
fun <X, Y, Z> foo(f: (Y) -> Z, g: (X) -> Y, x: X): Z = f(g(x))
|
||||
|
||||
fun test() = foo({ it + 1 }, { it.length }, "")
|
||||
@@ -0,0 +1,40 @@
|
||||
FILE /fixationOrder2.kt
|
||||
FUN public fun <X, Y, Z> foo(f: (Y) -> Z, g: (X) -> Y, x: X): Z
|
||||
TYPE_PARAMETER <X>
|
||||
TYPE_PARAMETER <Y>
|
||||
TYPE_PARAMETER <Z>
|
||||
VALUE_PARAMETER value-parameter f: (Y) -> Z
|
||||
VALUE_PARAMETER value-parameter g: (X) -> Y
|
||||
VALUE_PARAMETER value-parameter x: X
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='foo((Y) -> Z, (X) -> Y, X): Z'
|
||||
CALL 'invoke(Y): Z' type=Z origin=INVOKE
|
||||
$this: GET_VAR 'value-parameter f: (Y) -> Z' type=(Y) -> Z origin=VARIABLE_AS_FUNCTION
|
||||
p1: CALL 'invoke(X): Y' type=Y origin=INVOKE
|
||||
$this: GET_VAR 'value-parameter g: (X) -> Y' type=(X) -> Y origin=VARIABLE_AS_FUNCTION
|
||||
p1: GET_VAR 'value-parameter x: X' type=X origin=null
|
||||
FUN public fun test(): kotlin.Int
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='test(): Int'
|
||||
CALL 'foo((Int) -> Int, (String) -> Int, String): Int' type=kotlin.Int origin=null
|
||||
<X>: String
|
||||
<Y>: Int
|
||||
<Z>: Int
|
||||
f: BLOCK type=(kotlin.Int) -> kotlin.Int origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA local final fun <anonymous>(it: kotlin.Int): kotlin.Int
|
||||
VALUE_PARAMETER value-parameter it: kotlin.Int
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='<anonymous>(Int): Int'
|
||||
CALL 'plus(Int): Int' type=kotlin.Int origin=PLUS
|
||||
$this: GET_VAR 'value-parameter it: Int' type=kotlin.Int origin=null
|
||||
other: CONST Int type=kotlin.Int value='1'
|
||||
FUNCTION_REFERENCE '<anonymous>(Int): Int' type=(kotlin.Int) -> kotlin.Int origin=LAMBDA
|
||||
g: BLOCK type=(kotlin.String) -> kotlin.Int origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA local final fun <anonymous>(it: kotlin.String): kotlin.Int
|
||||
VALUE_PARAMETER value-parameter it: kotlin.String
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='<anonymous>(String): Int'
|
||||
CALL '<get-length>(): Int' type=kotlin.Int origin=GET_PROPERTY
|
||||
$this: GET_VAR 'value-parameter it: String' type=kotlin.String origin=null
|
||||
FUNCTION_REFERENCE '<anonymous>(String): Int' type=(kotlin.String) -> kotlin.Int origin=LAMBDA
|
||||
x: CONST String type=kotlin.String value=''
|
||||
@@ -1090,6 +1090,27 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/regressions/typeAliasCtorForGenericClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/ir/irText/regressions/newInference")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class NewInference extends AbstractIrTextTestCase {
|
||||
public void testAllFilesPresentInNewInference() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText/regressions/newInference"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("fixationOrder1.kt")
|
||||
public void testFixationOrder1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/regressions/newInference/fixationOrder1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fixationOrder2.kt")
|
||||
public void testFixationOrder2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/regressions/newInference/fixationOrder2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/ir/irText/singletons")
|
||||
|
||||
Reference in New Issue
Block a user