Constraint incorporation

In a constraint system a new bound is incorporated:
all new constrains that can be derived from it
(and from existing ones) are added
This commit is contained in:
Svetlana Isakova
2015-06-27 13:49:11 +03:00
parent 82acce4767
commit 9a5abf368f
30 changed files with 531 additions and 225 deletions
@@ -459,6 +459,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_CANNOT_CAPTURE_TYPES = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> TYPE_INFERENCE_INCORPORATION_ERROR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetExpression, JetType, JetType> TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
@@ -593,6 +593,7 @@ public class DefaultErrorMessages {
MAP.put(TYPE_INFERENCE_CANNOT_CAPTURE_TYPES, "Type inference failed: {0}", TYPE_INFERENCE_CANNOT_CAPTURE_TYPES_RENDERER);
MAP.put(TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER, "Type inference failed: {0}", TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER);
MAP.put(TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH, "Type inference failed: {0}", TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER);
MAP.put(TYPE_INFERENCE_INCORPORATION_ERROR, "Type inference failed. Please try to specify type arguments explicitly.");
MAP.put(TYPE_INFERENCE_UPPER_BOUND_VIOLATED, "{0}", TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER);
MAP.put(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH, "Type inference failed. Expected type mismatch: found: {1} required: {0}", RENDER_TYPE, RENDER_TYPE);
@@ -228,7 +228,7 @@ public object Renderers {
return result
.text(newText().normal("Not enough information to infer parameter ")
.strong(firstUnknownParameter!!.getName())
.strong(firstUnknownParameter.getName())
.normal(" in "))
.table(newTable()
.descriptor(inferenceErrorData.descriptor)
@@ -246,7 +246,7 @@ public object Renderers {
val systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints()
val typeParameterDescriptor = inferenceErrorData.descriptor.getTypeParameters().firstOrNull {
!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, true)
!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, true)
}
if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result)
@@ -55,7 +55,7 @@ import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumen
import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS;
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.DEPENDENT;
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.createCorrespondingFunctionTypeForFunctionPlaceholder;
import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.createTypeForFunctionPlaceholder;
import static org.jetbrains.kotlin.types.TypeUtils.DONT_CARE;
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
@@ -85,7 +85,7 @@ public class ArgumentTypeResolver {
@NotNull JetType expectedType
) {
if (ErrorUtils.isFunctionPlaceholder(actualType)) {
JetType functionType = createCorrespondingFunctionTypeForFunctionPlaceholder(actualType, expectedType);
JetType functionType = createTypeForFunctionPlaceholder(actualType, expectedType);
return JetTypeChecker.DEFAULT.isSubtypeOf(functionType, expectedType);
}
return JetTypeChecker.DEFAULT.isSubtypeOf(actualType, expectedType);
@@ -44,6 +44,9 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.DataFlowUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import java.util.ArrayList
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.ErrorUtils
public class CallCompleter(
val argumentTypeResolver: ArgumentTypeResolver,
@@ -151,8 +154,6 @@ public class CallCompleter(
}
}
(getConstraintSystem() as ConstraintSystemImpl).processDeclaredBoundConstraints()
if (returnType != null && expectedType === TypeUtils.UNIT_EXPECTED_TYPE) {
updateSystemIfSuccessful {
system ->
@@ -161,6 +162,9 @@ public class CallCompleter(
}
}
val constraintSystem = getConstraintSystem() as ConstraintSystemImpl
constraintSystem.fixVariables()
setResultingSubstitutor(getConstraintSystem()!!.getResultingSubstitutor())
}
@@ -280,7 +284,8 @@ public class CallCompleter(
argumentExpression: JetExpression,
trace: BindingTrace
): JetType? {
if (recordedType == updatedType || updatedType == null) return updatedType
//workaround for KT-8218
if ((!ErrorUtils.containsErrorType(recordedType) && recordedType == updatedType) || updatedType == null) return updatedType
fun deparenthesizeOrGetSelector(expression: JetExpression?): JetExpression? {
val deparenthesized = JetPsiUtil.deparenthesizeOnce(expression, /* deparenthesizeBinaryExpressionWithTypeRHS = */ false)
@@ -70,11 +70,9 @@ class GenericCandidateResolver(
// Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion)
val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate)
val typeVariables = Maps.newLinkedHashMap<TypeParameterDescriptor, Variance>()
for (typeParameterDescriptor in candidateWithFreshVariables.getTypeParameters()) {
typeVariables.put(typeParameterDescriptor, Variance.INVARIANT) // TODO: variance of the occurrences
}
constraintSystem.registerTypeVariables(typeVariables)
val backConversion = candidateWithFreshVariables.getTypeParameters().zip(candidate.getTypeParameters()).toMap()
constraintSystem.registerTypeVariables(candidateWithFreshVariables.getTypeParameters(), { Variance.INVARIANT })
val substituteDontCare = makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE)
@@ -111,9 +109,7 @@ class GenericCandidateResolver(
}
// Restore type variables before alpha-conversion
val constraintSystemWithRightTypeParameters = constraintSystem.substituteTypeVariables {
candidate.getTypeParameters().get(it.getIndex())
}
val constraintSystemWithRightTypeParameters = constraintSystem.substituteTypeVariables { backConversion.get(it) }
candidateCall.setConstraintSystem(constraintSystemWithRightTypeParameters)
@@ -174,7 +170,7 @@ class GenericCandidateResolver(
val valueParameterDescriptor = entry.getKey()
for (valueArgument in resolvedValueArgument.getArguments()) {
addConstraintForFunctionLiteral<D>(valueArgument, valueParameterDescriptor, constraintSystem, context)
addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context)
}
}
resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor())
@@ -233,6 +233,9 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
else if (status.hasConflictingConstraints()) {
trace.report(TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS.on(reference, data));
}
else if (status.hasTypeInferenceIncorporationError()) {
trace.report(TYPE_INFERENCE_INCORPORATION_ERROR.on(reference));
}
else {
assert status.hasUnknownParameters();
trace.report(TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER.on(reference, data));
@@ -58,7 +58,7 @@ public class ResolutionTaskHolder<D : CallableDescriptor, F : D>(
val lazyCandidates = {
candidatesList[candidateIndex]().filter { priorityProvider.getPriority(it) == priority }.toReadOnlyList()
}
tasks.add(ResolutionTask(basicCallResolutionContext, tracing, lazyCandidates))
tasks.add(ResolutionTask<D, F>(basicCallResolutionContext, tracing, lazyCandidates))
}
}
@@ -1,10 +1,10 @@
package foo
open class A {
val B.w: Int by <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>MyProperty<!>()
val B.w: Int by <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>MyProperty<!>()
}
val B.r: Int by <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>MyProperty<!>()
val B.r: Int by <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>MyProperty<!>()
val A.e: Int by MyProperty()
@@ -7,6 +7,6 @@ fun <T, C: Collection<T>> convert(src: Collection<T>, dest: C): C = throw Except
fun test(l: List<Int>) {
//todo should be inferred
val r = <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>convert<!>(l, <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>HashSet<!>())
checkSubtype<Int>(<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>r<!>)
val r = convert(l, HashSet())
checkSubtype<Collection<Int>>(r)
}
@@ -29,8 +29,7 @@ fun test3() {
fun <T, R: T> emptyStrangeMap1(t: T): Map<T, R> = throw Exception("$t")
fun test4() {
//todo we may infer 'Int' for 'R' here
<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyStrangeMap1<!>(1)
emptyStrangeMap1(1)
}
//--------------
@@ -38,8 +37,7 @@ fun test4() {
fun <T: A, R: T> emptyStrangeMap2(t: T): Map<T, R> where R: A = throw Exception("$t")
fun test5(a: A) {
//todo we may infer 'A' for 'R' here
<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyStrangeMap2<!>(a)
emptyStrangeMap2(a)
}
//--------------
@@ -16,11 +16,10 @@ public class HS<T> extends Base<T> {}
import foo.*;
import java.util.HashSet
fun <T, C: Base<T>> convert(src: HS<T>, dest: C): C = throw Exception("$src $dest")
fun test(l: HS<Int>) {
//todo should be inferred
val r = <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>convert<!>(l, <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>HS<!>())
checkSubtype<Int>(<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>r<!>)
val r = convert(l, HS())
checkSubtype<Base<Int>>(r)
}
@@ -1,4 +1,4 @@
fun bar() {
fun <T: <!CYCLIC_GENERIC_UPPER_BOUND!>T?<!>> foo() {}
foo()
<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>()
}
@@ -1,10 +1,8 @@
// !CHECK_TYPE
interface Inv<I>
fun <S, T: S> Inv<T>.reduce2(): S = null!!
fun test(a: Inv<Int>): Int {
val b = 1 <!OVERLOAD_RESOLUTION_AMBIGUITY!>+<!> a.reduce2()
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>b<!>
val b = 1 + a.reduce2()
return b
}
@@ -6,7 +6,12 @@ fun <D, E : D> List<ResolutionTask<D, E>>.bar(t: ResolutionTask<D, E>) = t
public class ResolutionTaskHolder<F, G : F> {
fun test(candidate: ResolutionCandidate<F>, tasks: MutableList<ResolutionTask<F, G>>) {
tasks.bar(ResolutionTask(candidate))
tasks.bar(ResolutionTask<F, G>(candidate))
tasks.add(ResolutionTask<F, G>(candidate))
//todo the problem is the type of ResolutionTask is inferred as ResolutionTask<F, F> too early
tasks.<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>bar<!>(ResolutionTask(candidate))
tasks.<!NONE_APPLICABLE!>add<!>(ResolutionTask(candidate))
}
}
@@ -6,10 +6,10 @@ fun main(args:Array<String>) {
val startTimeNanos = System.nanoTime()
// the problem sits on the next line:
val pi = 4.0.toDouble() * delta <!OVERLOAD_RESOLUTION_AMBIGUITY!>*<!> (1..n).reduce(
val pi = 4.0.toDouble() * delta <!OVERLOAD_RESOLUTION_AMBIGUITY!>*<!> (1..n).<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>reduce<!>(
{t, i ->
val x = (i - 0.5) * delta
<!TYPE_MISMATCH!>t + 1.0 / (1.0 + x * x)<!>
t + 1.0 / (1.0 + x * x)
})
// !!! pi has error type here
@@ -12,7 +12,7 @@ fun bar() {
Resolved call:
Candidate descriptor: fun <T> foo(t: T): Unit defined in root package
Resulting descriptor: fun <T> foo(t: List<???>): Unit defined in root package
Resulting descriptor: fun <T> foo(t: ???): Unit defined in root package
Explicit receiver kind = NO_EXPLICIT_RECEIVER
Dispatch receiver = NO_RECEIVER
@@ -20,4 +20,4 @@ Extension receiver = NO_RECEIVER
Value arguments mapping:
MATCH_MODULO_UNINFERRED_TYPES t : List<???> = someList()
MATCH_MODULO_UNINFERRED_TYPES t : ??? = someList()
@@ -12,7 +12,7 @@ fun bar() {
Resolved call:
Candidate descriptor: fun <T> foo(t: T): Unit defined in root package
Resulting descriptor: fun <T> foo(t: MutableList<???>): Unit defined in root package
Resulting descriptor: fun <T> foo(t: ???): Unit defined in root package
Explicit receiver kind = NO_EXPLICIT_RECEIVER
Dispatch receiver = NO_RECEIVER
@@ -20,4 +20,4 @@ Extension receiver = NO_RECEIVER
Value arguments mapping:
MATCH_MODULO_UNINFERRED_TYPES t : MutableList<???> = someList()
MATCH_MODULO_UNINFERRED_TYPES t : ??? = someList()
@@ -36,9 +36,10 @@ import java.util.LinkedHashMap
import java.util.regex.Pattern
abstract public class AbstractConstraintSystemTest() : JetLiteFixture() {
private val typePattern = """([\w|<|>|\(|\)]+)"""
val constraintPattern = Pattern.compile("""(SUBTYPE|SUPERTYPE)\s+$typePattern\s+$typePattern\s*(weak)?""")
private val typePattern = """([\w|<|\,|>|?|\(|\)]+)"""
val constraintPattern = Pattern.compile("""(SUBTYPE|SUPERTYPE|EQUAL)\s+$typePattern\s+$typePattern\s*(weak)?""")
val variablesPattern = Pattern.compile("VARIABLES\\s+(.*)")
val fixVariablesPattern = "FIX_VARIABLES"
private var _typeResolver: TypeResolver? = null
private val typeResolver: TypeResolver
@@ -83,12 +84,10 @@ abstract public class AbstractConstraintSystemTest() : JetLiteFixture() {
val constraintSystem = ConstraintSystemImpl()
val typeParameterDescriptors = LinkedHashMap<TypeParameterDescriptor, Variance>()
val variables = parseVariables(constraintsFileText)
for (variable in variables) {
typeParameterDescriptors.put(testDeclarations.getParameterDescriptor(variable), Variance.INVARIANT)
}
constraintSystem.registerTypeVariables(typeParameterDescriptors)
val fixVariables = constraintsFileText.contains(fixVariablesPattern)
val typeParameterDescriptors = variables.map { testDeclarations.getParameterDescriptor(it) }
constraintSystem.registerTypeVariables(typeParameterDescriptors, { Variance.INVARIANT })
val constraints = parseConstraints(constraintsFileText)
for (constraint in constraints) {
@@ -98,17 +97,18 @@ abstract public class AbstractConstraintSystemTest() : JetLiteFixture() {
when (constraint.kind) {
MyConstraintKind.SUBTYPE -> constraintSystem.addSubtypeConstraint(firstType, secondType, position)
MyConstraintKind.SUPERTYPE -> constraintSystem.addSupertypeConstraint(firstType, secondType, position)
MyConstraintKind.EQUAL -> constraintSystem.addConstraint(ConstraintSystemImpl.ConstraintKind.EQUAL, firstType, secondType, position)
}
}
constraintSystem.processDeclaredBoundConstraints()
if (fixVariables) constraintSystem.fixVariables()
val resultingStatus = Renderers.RENDER_CONSTRAINT_SYSTEM_SHORT.render(constraintSystem)
val resultingSubstitutor = constraintSystem.getResultingSubstitutor()
val result = StringBuilder() append "result:\n"
for ((typeParameter, variance) in typeParameterDescriptors) {
for (typeParameter in typeParameterDescriptors) {
val parameterType = testDeclarations.getType(typeParameter.getName().asString())
val resultType = resultingSubstitutor.substitute(parameterType, variance)
val resultType = resultingSubstitutor.substitute(parameterType, Variance.INVARIANT)
result append "${typeParameter.getName()}=${resultType?.let{ DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }}\n"
}
@@ -118,7 +118,7 @@ abstract public class AbstractConstraintSystemTest() : JetLiteFixture() {
class MyConstraint(val kind: MyConstraintKind, val firstType: String, val secondType: String, val isWeak: Boolean)
enum class MyConstraintKind {
SUBTYPE, SUPERTYPE
SUBTYPE, SUPERTYPE, EQUAL
}
private fun parseVariables(text: String): List<String> {