[NI] Select variable with proper non-trivial constraint first

This commit is contained in:
Dmitry Petrov
2017-06-26 12:50:02 +03:00
committed by Mikhail Zarechenskiy
parent 0bf81aeec0
commit c0d6eff97c
6 changed files with 149 additions and 12 deletions
@@ -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")