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:
@@ -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);
|
||||
|
||||
|
||||
+1
@@ -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)
|
||||
|
||||
+5
-9
@@ -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())
|
||||
|
||||
+3
@@ -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));
|
||||
|
||||
+1
-1
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -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)
|
||||
}
|
||||
|
||||
+2
-4
@@ -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
-1
@@ -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()
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
+11
-11
@@ -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> {
|
||||
|
||||
+9
-3
@@ -16,8 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
|
||||
open class ConstraintError(val constraintPosition: ConstraintPosition)
|
||||
|
||||
@@ -25,7 +26,12 @@ class TypeConstructorMismatch(constraintPosition: ConstraintPosition): Constrain
|
||||
|
||||
class ErrorInConstrainingType(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition)
|
||||
|
||||
class TypeInferenceError(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition)
|
||||
|
||||
class CannotCapture(constraintPosition: ConstraintPosition, val typeVariable: TypeParameterDescriptor): ConstraintError(constraintPosition)
|
||||
|
||||
fun ConstraintError.substituteTypeVariable(substitution: (TypeParameterDescriptor) -> TypeParameterDescriptor) =
|
||||
if (this is CannotCapture) CannotCapture(constraintPosition, substitution(typeVariable)) else this
|
||||
fun ConstraintError.substituteTypeVariable(substitution: (TypeParameterDescriptor) -> TypeParameterDescriptor?) =
|
||||
if (this is CannotCapture) CannotCapture(constraintPosition, substitution(typeVariable) ?: typeVariable) else this
|
||||
|
||||
fun newTypeInferenceOrConstructorMismatchError(constraintPosition: ConstraintPosition) =
|
||||
if (constraintPosition is CompoundConstraintPosition) TypeInferenceError(constraintPosition) else TypeConstructorMismatch(constraintPosition)
|
||||
+8
-2
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.constraintPosition
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
|
||||
import java.util.*
|
||||
|
||||
public enum class ConstraintPositionKind {
|
||||
RECEIVER_POSITION,
|
||||
@@ -56,9 +57,14 @@ private data class ConstraintPositionWithIndex(override val kind: ConstraintPosi
|
||||
class CompoundConstraintPosition(
|
||||
vararg positions: ConstraintPosition
|
||||
) : ConstraintPositionImpl(ConstraintPositionKind.COMPOUND_CONSTRAINT_POSITION) {
|
||||
val positions: Collection<ConstraintPosition> = positions.toList()
|
||||
val positions: Collection<ConstraintPosition> =
|
||||
positions.flatMap { if (it is CompoundConstraintPosition) it.positions else listOf(it) }.toCollection(LinkedHashSet<ConstraintPosition>())
|
||||
|
||||
override fun isStrong() = positions.any { it.isStrong() }
|
||||
|
||||
override fun toString() = "$kind(${positions.joinToString()}"
|
||||
override fun toString() = "$kind(${positions.joinToString()})"
|
||||
}
|
||||
|
||||
fun ConstraintPosition.equalsOrContains(position: ConstraintPosition): Boolean {
|
||||
return if (this !is CompoundConstraintPosition) this == position else positions.any { it == position }
|
||||
}
|
||||
+7
-2
@@ -26,11 +26,16 @@ public trait ConstraintSystem {
|
||||
|
||||
/**
|
||||
* Registers variables in a constraint system.
|
||||
* The type variables for the corresponding function are local, the type variables of inner arguments calls are non-local.
|
||||
*/
|
||||
public fun registerTypeVariables(typeVariables: Map<TypeParameterDescriptor, Variance>)
|
||||
public fun registerTypeVariables(
|
||||
typeVariables: Collection<TypeParameterDescriptor>,
|
||||
typeVariableVariance: (TypeParameterDescriptor) -> Variance,
|
||||
external: Boolean = false
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns a set of all registered type variables.
|
||||
* Returns a set of all non-external registered type variables.
|
||||
*/
|
||||
public fun getTypeVariables(): Set<TypeParameterDescriptor>
|
||||
|
||||
|
||||
+153
-111
@@ -16,35 +16,31 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.TypeProjection
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.ErrorUtils.FunctionPlaceholderTypeConstructor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.TYPE_BOUND_POSITION
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.equalsOrContains
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.ErrorUtils.FunctionPlaceholderTypeConstructor
|
||||
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import java.util.LinkedHashMap
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.*
|
||||
import java.util.HashMap
|
||||
import org.jetbrains.kotlin.types.typeUtil.getNestedTypeArguments
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
|
||||
import org.jetbrains.kotlin.types.getCustomTypeVariable
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
|
||||
import org.jetbrains.kotlin.types.TypeSubstitution
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import java.util.HashMap
|
||||
import java.util.HashSet
|
||||
import java.util.LinkedHashMap
|
||||
|
||||
public class ConstraintSystemImpl : ConstraintSystem {
|
||||
|
||||
@@ -53,7 +49,15 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
EQUAL
|
||||
}
|
||||
|
||||
private val typeParameterBounds = LinkedHashMap<TypeParameterDescriptor, TypeBoundsImpl>()
|
||||
fun ConstraintKind.toBound() = if (this == SUB_TYPE) UPPER_BOUND else EXACT_BOUND
|
||||
|
||||
private val allTypeParameterBounds = LinkedHashMap<TypeParameterDescriptor, TypeBoundsImpl>()
|
||||
private val externalTypeParameters = HashSet<TypeParameterDescriptor>()
|
||||
private val typeParameterBounds: Map<TypeParameterDescriptor, TypeBoundsImpl>
|
||||
get() = if (externalTypeParameters.isEmpty()) allTypeParameterBounds
|
||||
else allTypeParameterBounds.filter { !externalTypeParameters.contains(it.key) }
|
||||
|
||||
private val usedInBounds = HashMap<TypeParameterDescriptor, MutableList<TypeBounds.Bound>>()
|
||||
|
||||
private val errors = ArrayList<ConstraintError>()
|
||||
public val constraintErrors: List<ConstraintError>
|
||||
@@ -64,7 +68,8 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
|
||||
override fun isSuccessful() = !hasContradiction() && !hasUnknownParameters()
|
||||
|
||||
override fun hasContradiction() = hasTypeConstructorMismatch() || hasConflictingConstraints() || hasCannotCaptureTypesError()
|
||||
override fun hasContradiction() = hasTypeConstructorMismatch() || hasTypeInferenceIncorporationError()
|
||||
|| hasConflictingConstraints() || hasCannotCaptureTypesError()
|
||||
|
||||
override fun hasViolatedUpperBound() = !isSuccessful() && getSystemWithoutWeakConstraints().getStatus().isSuccessful()
|
||||
|
||||
@@ -77,12 +82,14 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
override fun hasOnlyErrorsFromPosition(constraintPosition: ConstraintPosition): Boolean {
|
||||
if (isSuccessful()) return false
|
||||
if (filterConstraintsOut(constraintPosition).getStatus().isSuccessful()) return true
|
||||
return errors.isNotEmpty() && errors.all { it.constraintPosition == constraintPosition }
|
||||
return errors.isNotEmpty() && errors.all { it.constraintPosition.equalsOrContains(constraintPosition) }
|
||||
}
|
||||
|
||||
override fun hasErrorInConstrainingTypes() = errors.any { it is ErrorInConstrainingType }
|
||||
|
||||
override fun hasCannotCaptureTypesError() = errors.any { it is CannotCapture }
|
||||
|
||||
override fun hasTypeInferenceIncorporationError() = errors.any { it is TypeInferenceError }
|
||||
}
|
||||
|
||||
private fun getParameterToInferredValueMap(
|
||||
@@ -105,7 +112,7 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
}
|
||||
|
||||
private fun replaceUninferredBy(getDefaultValue: (TypeParameterDescriptor) -> TypeProjection): TypeSubstitutor {
|
||||
return TypeUtils.makeSubstitutorForTypeParametersMap(getParameterToInferredValueMap(typeParameterBounds, getDefaultValue))
|
||||
return TypeUtils.makeSubstitutorForTypeParametersMap(getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue))
|
||||
}
|
||||
|
||||
private fun replaceUninferredBy(defaultValue: JetType): TypeSubstitutor {
|
||||
@@ -118,63 +125,78 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
|
||||
override fun getStatus(): ConstraintSystemStatus = constraintSystemStatus
|
||||
|
||||
override fun registerTypeVariables(typeVariables: Map<TypeParameterDescriptor, Variance>) {
|
||||
for ((typeVariable, positionVariance) in typeVariables) {
|
||||
typeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, positionVariance))
|
||||
override fun registerTypeVariables(
|
||||
typeVariables: Collection<TypeParameterDescriptor>,
|
||||
typeVariableVariance: (TypeParameterDescriptor) -> Variance,
|
||||
external: Boolean
|
||||
) {
|
||||
if (external) externalTypeParameters.addAll(typeVariables)
|
||||
|
||||
for (typeVariable in typeVariables) {
|
||||
allTypeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, typeVariableVariance(typeVariable)))
|
||||
}
|
||||
val constantSubstitutor = TypeUtils.makeConstantSubstitutor(typeParameterBounds.keySet(), DONT_CARE)
|
||||
for ((typeVariable, typeBounds) in typeParameterBounds) {
|
||||
for ((typeVariable, typeBounds) in allTypeParameterBounds) {
|
||||
for (declaredUpperBound in typeVariable.getUpperBounds()) {
|
||||
if (KotlinBuiltIns.getInstance().getNullableAnyType() == declaredUpperBound) continue //todo remove this line (?)
|
||||
val substitutedBound = constantSubstitutor?.substitute(declaredUpperBound, Variance.INVARIANT)
|
||||
val position = TYPE_BOUND_POSITION.position(typeVariable.getIndex())
|
||||
if (substitutedBound != null && !isErrorOrSpecialType(substitutedBound, position)) {
|
||||
typeBounds.addBound(UPPER_BOUND, substitutedBound, position)
|
||||
}
|
||||
val variableType = JetTypeImpl(Annotations.EMPTY, typeVariable.getTypeConstructor(), false, listOf(), JetScope.Empty)
|
||||
addBound(variableType, Bound(declaredUpperBound, UPPER_BOUND, position, declaredUpperBound.isProper()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis({ it }, { it.copy() }, { true })
|
||||
|
||||
public fun substituteTypeVariables(typeVariablesMap: (TypeParameterDescriptor) -> TypeParameterDescriptor): ConstraintSystem {
|
||||
// type bounds are proper types and don't contain other variables
|
||||
return createNewConstraintSystemFromThis(typeVariablesMap, { it }, { true })
|
||||
fun JetType.isProper() = !TypeUtils.containsSpecialType(this) {
|
||||
type -> type.getConstructor().getDeclarationDescriptor() in getAllTypeVariables()
|
||||
}
|
||||
|
||||
public fun filterConstraintsOut(vararg excludePositions: ConstraintPosition): ConstraintSystem {
|
||||
val positions = excludePositions.toSet()
|
||||
return filterConstraints { !positions.contains(it) }
|
||||
fun JetType.getNestedTypeVariables(): List<TypeParameterDescriptor> {
|
||||
return getNestedTypeArguments().map { typeProjection ->
|
||||
typeProjection.getType().getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
|
||||
}.filterNotNull().filter { it in getAllTypeVariables() }
|
||||
}
|
||||
|
||||
public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis({ it }, { true })
|
||||
|
||||
public fun substituteTypeVariables(typeVariablesMap: (TypeParameterDescriptor) -> TypeParameterDescriptor?): ConstraintSystem {
|
||||
// type bounds are proper types and don't contain other variables
|
||||
return createNewConstraintSystemFromThis(typeVariablesMap, { true })
|
||||
}
|
||||
|
||||
public fun filterConstraintsOut(excludePosition: ConstraintPosition): ConstraintSystem {
|
||||
return filterConstraints { !it.equalsOrContains(excludePosition) }
|
||||
}
|
||||
|
||||
public fun filterConstraints(condition: (ConstraintPosition) -> Boolean): ConstraintSystem {
|
||||
return createNewConstraintSystemFromThis({ it }, { it.filter(condition) }, condition)
|
||||
return createNewConstraintSystemFromThis({ it }, condition)
|
||||
}
|
||||
|
||||
public fun getSystemWithoutWeakConstraints(): ConstraintSystem {
|
||||
return filterConstraints {
|
||||
constraintPosition ->
|
||||
return filterConstraints(fun (constraintPosition): Boolean {
|
||||
if (constraintPosition !is CompoundConstraintPosition) return constraintPosition.isStrong()
|
||||
|
||||
// 'isStrong' for compound means 'has some strong constraints'
|
||||
// but for testing absence of weak constraints we need 'has only strong constraints' here
|
||||
if (constraintPosition is CompoundConstraintPosition) {
|
||||
constraintPosition.positions.all { it.isStrong() }
|
||||
}
|
||||
else {
|
||||
constraintPosition.isStrong()
|
||||
}
|
||||
}
|
||||
return constraintPosition.positions.all { it.isStrong() }
|
||||
})
|
||||
}
|
||||
|
||||
private fun createNewConstraintSystemFromThis(
|
||||
substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor,
|
||||
replaceTypeBounds: (TypeBoundsImpl) -> TypeBoundsImpl,
|
||||
substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor?,
|
||||
filterConstraintPosition: (ConstraintPosition) -> Boolean
|
||||
): ConstraintSystem {
|
||||
val newSystem = ConstraintSystemImpl()
|
||||
for ((typeParameter, typeBounds) in typeParameterBounds) {
|
||||
val newTypeParameter = substituteTypeVariable(typeParameter)
|
||||
newSystem.typeParameterBounds.put(newTypeParameter, replaceTypeBounds(typeBounds))
|
||||
for ((typeParameter, typeBounds) in allTypeParameterBounds) {
|
||||
val newTypeParameter = substituteTypeVariable(typeParameter) ?: typeParameter
|
||||
newSystem.allTypeParameterBounds.put(newTypeParameter, typeBounds.copy(substituteTypeVariable).filter(filterConstraintPosition))
|
||||
}
|
||||
for ((typeVariable, bounds) in usedInBounds) {
|
||||
if (bounds.isNotEmpty()) {
|
||||
val newTypeVariable = substituteTypeVariable(typeVariable) ?: typeVariable
|
||||
newSystem.usedInBounds.put(newTypeVariable, ArrayList(bounds.substitute(substituteTypeVariable)))
|
||||
}
|
||||
}
|
||||
newSystem.externalTypeParameters.addAll(externalTypeParameters.map { substituteTypeVariable(it) ?: it })
|
||||
newSystem.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) }.map { it.substituteTypeVariable(substituteTypeVariable) })
|
||||
return newSystem
|
||||
}
|
||||
@@ -189,7 +211,7 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition)
|
||||
}
|
||||
|
||||
private fun addConstraint(constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, constraintPosition: ConstraintPosition) {
|
||||
fun addConstraint(constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, constraintPosition: ConstraintPosition) {
|
||||
val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks {
|
||||
private var depth = 0
|
||||
|
||||
@@ -213,7 +235,9 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
}
|
||||
|
||||
override fun capture(typeVariable: JetType, typeProjection: TypeProjection): Boolean {
|
||||
if (isMyTypeVariable(typeProjection.getType())) return false
|
||||
val myTypeVariable = getMyTypeVariable(typeVariable)
|
||||
|
||||
if (myTypeVariable != null && constraintPosition.isCaptureAllowed()) {
|
||||
if (depth > 0) {
|
||||
errors.add(CannotCapture(constraintPosition, myTypeVariable))
|
||||
@@ -225,7 +249,7 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
}
|
||||
|
||||
override fun noCorrespondingSupertype(subtype: JetType, supertype: JetType): Boolean {
|
||||
errors.add(TypeConstructorMismatch(constraintPosition))
|
||||
errors.add(newTypeInferenceOrConstructorMismatchError(constraintPosition))
|
||||
return true
|
||||
}
|
||||
})
|
||||
@@ -266,42 +290,54 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
// we don't add it without knowing whether it's a function type or an extension function type
|
||||
return
|
||||
}
|
||||
createCorrespondingFunctionTypeForFunctionPlaceholder(subType, superType)
|
||||
createTypeForFunctionPlaceholder(subType, superType)
|
||||
}
|
||||
else {
|
||||
subType
|
||||
}
|
||||
|
||||
fun simplifyConstraint(subType: JetType, superType: JetType) {
|
||||
// can be equal for the recursive invocations: fun <T> foo(i: Int) : T { ... return foo(i); } => T <: T
|
||||
// the right processing of constraints connecting type variables is not supported yet
|
||||
if (isMyTypeVariable(subType) && isMyTypeVariable(superType)) return
|
||||
|
||||
if (isMyTypeVariable(subType)) {
|
||||
val boundKind = if (constraintKind == SUB_TYPE) UPPER_BOUND else EXACT_BOUND
|
||||
generateTypeParameterConstraint(subType, superType, boundKind, constraintPosition)
|
||||
generateTypeParameterBound(subType, superType, constraintKind.toBound(), constraintPosition)
|
||||
return
|
||||
}
|
||||
if (isMyTypeVariable(superType)) {
|
||||
val boundKind = if (constraintKind == SUB_TYPE) LOWER_BOUND else EXACT_BOUND
|
||||
generateTypeParameterConstraint(superType, subType, boundKind, constraintPosition)
|
||||
generateTypeParameterBound(superType, subType, constraintKind.toBound().reverse(), constraintPosition)
|
||||
return
|
||||
}
|
||||
// if superType is nullable and subType is not nullable, unsafe call or type mismatch error will be generated later,
|
||||
// but constraint system should be solved anyway
|
||||
val subTypeNotNullable = TypeUtils.makeNotNullable(subType)
|
||||
val superTypeNotNullable = TypeUtils.makeNotNullable(superType)
|
||||
if (constraintKind == EQUAL) {
|
||||
val result = if (constraintKind == EQUAL) {
|
||||
typeCheckingProcedure.equalTypes(subTypeNotNullable, superTypeNotNullable)
|
||||
}
|
||||
else {
|
||||
typeCheckingProcedure.isSubtypeOf(subTypeNotNullable, superTypeNotNullable)
|
||||
typeCheckingProcedure.isSubtypeOf(subTypeNotNullable, superType)
|
||||
}
|
||||
if (!result) errors.add(newTypeInferenceOrConstructorMismatchError(constraintPosition))
|
||||
}
|
||||
simplifyConstraint(newSubType, superType)
|
||||
|
||||
}
|
||||
|
||||
private fun generateTypeParameterConstraint(
|
||||
fun addBound(variable: JetType, bound: Bound) {
|
||||
val typeBounds = getTypeBounds(variable)
|
||||
if (typeBounds.bounds.contains(bound)) return
|
||||
|
||||
typeBounds.addBound(bound)
|
||||
|
||||
if (!bound.isProper) {
|
||||
for (dependentTypeVariable in bound.constrainingType.getNestedTypeVariables()) {
|
||||
val dependentBounds = usedInBounds.getOrPut(dependentTypeVariable) { arrayListOf() }
|
||||
dependentBounds.add(bound)
|
||||
}
|
||||
}
|
||||
|
||||
incorporateBound(variable, bound)
|
||||
}
|
||||
|
||||
private fun generateTypeParameterBound(
|
||||
parameterType: JetType,
|
||||
constrainingType: JetType,
|
||||
boundKind: TypeBounds.BoundKind,
|
||||
@@ -324,10 +360,8 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
}
|
||||
}
|
||||
|
||||
val typeBounds = getTypeBounds(parameterType)
|
||||
|
||||
if (!parameterType.isMarkedNullable() || !TypeUtils.isNullableType(newConstrainingType)) {
|
||||
typeBounds.addBound(boundKind, newConstrainingType, constraintPosition)
|
||||
addBound(parameterType, Bound(newConstrainingType, boundKind, constraintPosition, newConstrainingType.isProper()))
|
||||
return
|
||||
}
|
||||
// For parameter type T:
|
||||
@@ -335,13 +369,14 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
// constraint T? = Int! should transform to T >: Int and T <: Int!
|
||||
|
||||
// constraints T? >: Int?; T? >: Int! should transform to T >: Int
|
||||
val notNullParameterType = TypeUtils.makeNotNullable(parameterType)
|
||||
val notNullConstrainingType = TypeUtils.makeNotNullable(newConstrainingType)
|
||||
if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) {
|
||||
typeBounds.addBound(LOWER_BOUND, notNullConstrainingType, constraintPosition)
|
||||
addBound(notNullParameterType, Bound(notNullConstrainingType, LOWER_BOUND, constraintPosition, notNullConstrainingType.isProper()))
|
||||
}
|
||||
// constraints T? <: Int?; T? <: Int! should transform to T <: Int?; T <: Int! correspondingly
|
||||
if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
|
||||
typeBounds.addBound(UPPER_BOUND, newConstrainingType, constraintPosition)
|
||||
addBound(notNullParameterType, Bound(newConstrainingType, UPPER_BOUND, constraintPosition, newConstrainingType.isProper()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +390,6 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
&& constrainingTypeProjection.getProjectionKind() == Variance.IN_VARIANCE) {
|
||||
errors.add(CannotCapture(constraintPosition, typeVariable))
|
||||
}
|
||||
val typeBounds = getTypeBounds(typeVariable)
|
||||
val typeProjection = if (parameterType.isMarkedNullable()) {
|
||||
TypeProjectionImpl(constrainingTypeProjection.getProjectionKind(), TypeUtils.makeNotNullable(constrainingTypeProjection.getType()))
|
||||
}
|
||||
@@ -363,51 +397,32 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
constrainingTypeProjection
|
||||
}
|
||||
val capturedType = createCapturedType(typeProjection)
|
||||
typeBounds.addBound(EXACT_BOUND, capturedType, constraintPosition)
|
||||
}
|
||||
|
||||
public fun processDeclaredBoundConstraints() {
|
||||
for ((typeParameterDescriptor, typeBounds) in typeParameterBounds) {
|
||||
fun compoundPosition(bound: Bound) = CompoundConstraintPosition(
|
||||
TYPE_BOUND_POSITION.position(typeParameterDescriptor.getIndex()), bound.position)
|
||||
|
||||
// todo order matters here
|
||||
// it's important to create a separate variable here,
|
||||
// because the following code may add new elements to typeBounds.bounds collection
|
||||
val bounds = ArrayList(typeBounds.bounds)
|
||||
for (declaredUpperBound in typeParameterDescriptor.getUpperBounds()) {
|
||||
bounds.filter { it.kind != UPPER_BOUND }.forEach {
|
||||
lowerOrExactBound ->
|
||||
addSubtypeConstraint(lowerOrExactBound.constrainingType, declaredUpperBound, compoundPosition(lowerOrExactBound))
|
||||
}
|
||||
if (!isMyTypeVariable(declaredUpperBound)) continue
|
||||
getTypeBounds(declaredUpperBound).bounds.filter { it.kind != LOWER_BOUND }.forEach {
|
||||
upperOrExactBound ->
|
||||
typeBounds.addBound(UPPER_BOUND, upperOrExactBound.constrainingType, compoundPosition(upperOrExactBound))
|
||||
}
|
||||
}
|
||||
}
|
||||
addBound(TypeUtils.makeNotNullable(parameterType), Bound(capturedType, EXACT_BOUND, constraintPosition))
|
||||
}
|
||||
|
||||
override fun getTypeVariables() = typeParameterBounds.keySet()
|
||||
|
||||
fun getAllTypeVariables() = allTypeParameterBounds.keySet()
|
||||
|
||||
fun getBoundsUsedIn(typeVariable: TypeParameterDescriptor): List<Bound> = usedInBounds[typeVariable] ?: emptyList()
|
||||
|
||||
override fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl {
|
||||
if (!isMyTypeVariable(typeVariable)) {
|
||||
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable")
|
||||
}
|
||||
return typeParameterBounds[typeVariable]!!
|
||||
return allTypeParameterBounds[typeVariable]!!
|
||||
}
|
||||
|
||||
private fun getTypeBounds(parameterType: JetType): TypeBoundsImpl {
|
||||
fun getTypeBounds(parameterType: JetType): TypeBoundsImpl {
|
||||
assert (isMyTypeVariable(parameterType)) { "Type is not a type variable for constraint system: $parameterType" }
|
||||
return getTypeBounds(getMyTypeVariable(parameterType)!!)
|
||||
}
|
||||
|
||||
private fun isMyTypeVariable(typeVariable: TypeParameterDescriptor) = typeParameterBounds.contains(typeVariable)
|
||||
fun isMyTypeVariable(typeVariable: TypeParameterDescriptor) = allTypeParameterBounds.contains(typeVariable)
|
||||
|
||||
private fun isMyTypeVariable(type: JetType): Boolean = getMyTypeVariable(type) != null
|
||||
fun isMyTypeVariable(type: JetType): Boolean = getMyTypeVariable(type) != null
|
||||
|
||||
private fun getMyTypeVariable(type: JetType): TypeParameterDescriptor? {
|
||||
fun getMyTypeVariable(type: JetType): TypeParameterDescriptor? {
|
||||
val typeParameterDescriptor = type.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
return if (typeParameterDescriptor != null && isMyTypeVariable(typeParameterDescriptor)) typeParameterDescriptor else null
|
||||
}
|
||||
@@ -415,13 +430,36 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
override fun getResultingSubstitutor() = replaceUninferredBySpecialErrorType().setApproximateCapturedTypes()
|
||||
|
||||
override fun getCurrentSubstitutor() = replaceUninferredBy(TypeUtils.DONT_CARE).setApproximateCapturedTypes()
|
||||
|
||||
fun fixVariable(typeVariable: TypeParameterDescriptor) {
|
||||
val typeBounds = getTypeBounds(typeVariable)
|
||||
if (typeBounds.isFixed) return
|
||||
typeBounds.setFixed()
|
||||
|
||||
val nestedTypeVariables = typeBounds.bounds.flatMap { it.constrainingType.getNestedTypeVariables() }
|
||||
nestedTypeVariables.forEach { fixVariable(it) }
|
||||
|
||||
val value = typeBounds.value ?: return
|
||||
|
||||
val type = JetTypeImpl(Annotations.EMPTY, typeVariable.getTypeConstructor(), false, emptyList(), JetScope.Empty)
|
||||
addBound(type, TypeBounds.Bound(value, TypeBounds.BoundKind.EXACT_BOUND, ConstraintPositionKind.FROM_COMPLETER.position()))
|
||||
}
|
||||
|
||||
fun fixVariables() {
|
||||
// todo variables should be fixed in the right order
|
||||
val (external, functionTypeParameters) = getAllTypeVariables().partition { externalTypeParameters.contains(it) }
|
||||
external.forEach { fixVariable(it) }
|
||||
functionTypeParameters.forEach { fixVariable(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun createCorrespondingFunctionTypeForFunctionPlaceholder(
|
||||
fun createTypeForFunctionPlaceholder(
|
||||
functionPlaceholder: JetType,
|
||||
expectedType: JetType
|
||||
): JetType {
|
||||
assert(ErrorUtils.isFunctionPlaceholder(functionPlaceholder)) { "Function placeholder type expected: $functionPlaceholder" }
|
||||
if (!ErrorUtils.isFunctionPlaceholder(functionPlaceholder)) return functionPlaceholder
|
||||
|
||||
val functionPlaceholderTypeConstructor = functionPlaceholder.getConstructor() as FunctionPlaceholderTypeConstructor
|
||||
|
||||
val isExtension = KotlinBuiltIns.isExtensionFunctionType(expectedType)
|
||||
@@ -450,3 +488,7 @@ private class SubstitutionWithCapturedTypeApproximation(val substitution: TypeSu
|
||||
override fun isEmpty() = substitution.isEmpty()
|
||||
override fun approximateCapturedTypes() = true
|
||||
}
|
||||
|
||||
public fun ConstraintSystemImpl.registerTypeVariables(typeVariables: Map<TypeParameterDescriptor, Variance>) {
|
||||
registerTypeVariables(typeVariables.keySet(), { typeVariables[it]!! })
|
||||
}
|
||||
+5
@@ -84,4 +84,9 @@ public trait ConstraintSystemStatus {
|
||||
* in invocation <tt>foo(array)</tt> where array has type <tt>Array<Array<out Int>></tt>.
|
||||
*/
|
||||
public fun hasCannotCaptureTypesError(): Boolean
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if there's an error in constraint system incorporation.
|
||||
*/
|
||||
public fun hasTypeInferenceIncorporationError(): Boolean
|
||||
}
|
||||
|
||||
@@ -16,10 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public trait TypeBounds {
|
||||
public val varianceOfPosition: Variance
|
||||
@@ -29,6 +34,7 @@ public trait TypeBounds {
|
||||
public val bounds: Collection<Bound>
|
||||
|
||||
public val value: JetType?
|
||||
get() = if (values.size() == 1) values.first() else null
|
||||
|
||||
public val values: Collection<JetType>
|
||||
|
||||
@@ -38,5 +44,41 @@ public trait TypeBounds {
|
||||
EXACT_BOUND
|
||||
}
|
||||
|
||||
public class Bound(public val constrainingType: JetType, public val kind: BoundKind, public val position: ConstraintPosition)
|
||||
public class Bound(
|
||||
public val constrainingType: JetType,
|
||||
public val kind: BoundKind,
|
||||
public val position: ConstraintPosition,
|
||||
public val isProper: Boolean = true
|
||||
) {
|
||||
public var typeVariable: TypeParameterDescriptor by Delegates.notNull()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
|
||||
val bound = other as Bound
|
||||
|
||||
if (constrainingType != bound.constrainingType) return false
|
||||
if (kind != bound.kind) return false
|
||||
|
||||
if (position.isStrong() != bound.position.isStrong()) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = constrainingType.hashCode()
|
||||
result = 31 * result + kind.hashCode()
|
||||
result = 31 * result + if (position.isStrong()) 1 else 0
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString() = "Bound($constrainingType, $kind, $position, isProper = $isProper)"
|
||||
}
|
||||
}
|
||||
|
||||
fun BoundKind.reverse() = when (this) {
|
||||
LOWER_BOUND -> UPPER_BOUND
|
||||
UPPER_BOUND -> LOWER_BOUND
|
||||
EXACT_BOUND -> EXACT_BOUND
|
||||
}
|
||||
|
||||
+64
-25
@@ -17,32 +17,40 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.CommonSupertypes
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import java.util.LinkedHashSet
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.types.singleBestRepresentative
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
public class TypeBoundsImpl(
|
||||
override val typeVariable: TypeParameterDescriptor,
|
||||
override val varianceOfPosition: Variance
|
||||
) : TypeBounds {
|
||||
override val bounds = LinkedHashSet<Bound>()
|
||||
override val bounds = ArrayList<Bound>()
|
||||
|
||||
private var resultValues: Collection<JetType>? = null
|
||||
|
||||
public fun addBound(kind: BoundKind, constrainingType: JetType, position: ConstraintPosition) {
|
||||
var isFixed: Boolean = false
|
||||
private set
|
||||
|
||||
public fun setFixed() {
|
||||
isFixed = true
|
||||
}
|
||||
|
||||
public fun addBound(bound: Bound) {
|
||||
resultValues = null
|
||||
bounds.add(Bound(constrainingType, kind, position))
|
||||
bound.typeVariable = typeVariable
|
||||
bounds.add(bound)
|
||||
}
|
||||
|
||||
private fun filterBounds(bounds: Collection<Bound>, kind: BoundKind): Set<JetType> {
|
||||
@@ -64,9 +72,14 @@ public class TypeBoundsImpl(
|
||||
return result
|
||||
}
|
||||
|
||||
fun copy(): TypeBoundsImpl {
|
||||
val typeBounds = TypeBoundsImpl(typeVariable, varianceOfPosition)
|
||||
typeBounds.bounds.addAll(bounds)
|
||||
fun copy(substituteTypeVariable: ((TypeParameterDescriptor) -> TypeParameterDescriptor?)? = null): TypeBoundsImpl {
|
||||
val typeBounds = TypeBoundsImpl(substituteTypeVariable?.invoke(typeVariable) ?: typeVariable, varianceOfPosition)
|
||||
if (substituteTypeVariable == null) {
|
||||
typeBounds.bounds.addAll(bounds)
|
||||
}
|
||||
else {
|
||||
typeBounds.bounds.addAll(bounds.substitute(substituteTypeVariable))
|
||||
}
|
||||
typeBounds.resultValues = resultValues
|
||||
return typeBounds
|
||||
}
|
||||
@@ -77,9 +90,6 @@ public class TypeBoundsImpl(
|
||||
return result
|
||||
}
|
||||
|
||||
override val value: JetType?
|
||||
get() = if (values.size() == 1) values.first() else null
|
||||
|
||||
override val values: Collection<JetType>
|
||||
get() {
|
||||
if (resultValues == null) {
|
||||
@@ -90,6 +100,8 @@ public class TypeBoundsImpl(
|
||||
|
||||
private fun computeValues(): Collection<JetType> {
|
||||
val values = LinkedHashSet<JetType>()
|
||||
val bounds = bounds.filter { it.isProper }
|
||||
|
||||
if (bounds.isEmpty()) {
|
||||
return listOf()
|
||||
}
|
||||
@@ -101,7 +113,7 @@ public class TypeBoundsImpl(
|
||||
val exactBounds = filterBounds(bounds, EXACT_BOUND, values)
|
||||
val bestFit = exactBounds.singleBestRepresentative()
|
||||
if (bestFit != null) {
|
||||
if (tryPossibleAnswer(bestFit)) {
|
||||
if (tryPossibleAnswer(bounds, bestFit)) {
|
||||
return listOf(bestFit)
|
||||
}
|
||||
}
|
||||
@@ -111,7 +123,7 @@ public class TypeBoundsImpl(
|
||||
filterBounds(bounds, LOWER_BOUND, values).partition { it.getConstructor() is IntegerValueTypeConstructor }
|
||||
|
||||
val superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds)
|
||||
if (tryPossibleAnswer(superTypeOfLowerBounds)) {
|
||||
if (tryPossibleAnswer(bounds, superTypeOfLowerBounds)) {
|
||||
return setOf(superTypeOfLowerBounds!!)
|
||||
}
|
||||
values.addIfNotNull(superTypeOfLowerBounds)
|
||||
@@ -121,14 +133,14 @@ public class TypeBoundsImpl(
|
||||
//foo(1, c: Consumer<Any>) - infer Int, not Any here
|
||||
|
||||
val superTypeOfNumberLowerBounds = TypeUtils.commonSupertypeForNumberTypes(numberLowerBounds)
|
||||
if (tryPossibleAnswer(superTypeOfNumberLowerBounds)) {
|
||||
if (tryPossibleAnswer(bounds, superTypeOfNumberLowerBounds)) {
|
||||
return setOf(superTypeOfNumberLowerBounds!!)
|
||||
}
|
||||
values.addIfNotNull(superTypeOfNumberLowerBounds)
|
||||
|
||||
if (superTypeOfLowerBounds != null && superTypeOfNumberLowerBounds != null) {
|
||||
val superTypeOfAllLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(listOf(superTypeOfLowerBounds, superTypeOfNumberLowerBounds))
|
||||
if (tryPossibleAnswer(superTypeOfAllLowerBounds)) {
|
||||
if (tryPossibleAnswer(bounds, superTypeOfAllLowerBounds)) {
|
||||
return setOf(superTypeOfAllLowerBounds!!)
|
||||
}
|
||||
}
|
||||
@@ -136,7 +148,7 @@ public class TypeBoundsImpl(
|
||||
val upperBounds = filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND, values)
|
||||
val intersectionOfUpperBounds = TypeUtils.intersect(JetTypeChecker.DEFAULT, upperBounds)
|
||||
if (!upperBounds.isEmpty() && intersectionOfUpperBounds != null) {
|
||||
if (tryPossibleAnswer(intersectionOfUpperBounds)) {
|
||||
if (tryPossibleAnswer(bounds, intersectionOfUpperBounds)) {
|
||||
return setOf(intersectionOfUpperBounds)
|
||||
}
|
||||
}
|
||||
@@ -146,7 +158,7 @@ public class TypeBoundsImpl(
|
||||
return values
|
||||
}
|
||||
|
||||
private fun tryPossibleAnswer(possibleAnswer: JetType?): Boolean {
|
||||
private fun tryPossibleAnswer(bounds: Collection<Bound>, possibleAnswer: JetType?): Boolean {
|
||||
if (possibleAnswer == null) return false
|
||||
// a captured type might be an answer
|
||||
if (!possibleAnswer.getConstructor().isDenotable() && !possibleAnswer.isCaptured()) return false
|
||||
@@ -169,3 +181,30 @@ public class TypeBoundsImpl(
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
fun Collection<Bound>.substitute(substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor?): List<Bound> {
|
||||
val typeSubstitutor = TypeSubstitutor.create(object : TypeSubstitution() {
|
||||
override fun get(key: TypeConstructor): TypeProjection? {
|
||||
val descriptor = key.getDeclarationDescriptor()
|
||||
if (descriptor !is TypeParameterDescriptor) return null
|
||||
val typeParameterDescriptor = substituteTypeVariable(descriptor) ?: return null
|
||||
|
||||
val type = JetTypeImpl(Annotations.EMPTY, typeParameterDescriptor.getTypeConstructor(), false, listOf(), JetScope.Empty)
|
||||
return TypeProjectionImpl(type)
|
||||
}
|
||||
})
|
||||
|
||||
return map {
|
||||
//todo captured types
|
||||
val substitutedType = if (it.constrainingType.getConstructor().isDenotable()) {
|
||||
typeSubstitutor.substitute(it.constrainingType, Variance.INVARIANT)
|
||||
} else {
|
||||
it.constrainingType
|
||||
}
|
||||
substitutedType?.let { type ->
|
||||
val newBound = Bound(type, it.kind, it.position, it.isProper)
|
||||
newBound.typeVariable = substituteTypeVariable(it.typeVariable) ?: it.typeVariable
|
||||
newBound
|
||||
}
|
||||
}.filterNotNull()
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.Variance.INVARIANT
|
||||
import org.jetbrains.kotlin.types.Variance.IN_VARIANCE
|
||||
import org.jetbrains.kotlin.types.typeUtil.getNestedTypeArguments
|
||||
import java.util.ArrayList
|
||||
|
||||
fun ConstraintSystemImpl.incorporateBound(variable: JetType, newBound: Bound) {
|
||||
val typeVariable = getMyTypeVariable(variable)!!
|
||||
val typeBounds = getTypeBounds(typeVariable)
|
||||
|
||||
for (oldBoundIndex in typeBounds.bounds.indices) {
|
||||
addConstraintFromBounds(typeBounds.bounds[oldBoundIndex], newBound)
|
||||
}
|
||||
val boundsUsedIn = getBoundsUsedIn(typeVariable)
|
||||
for (index in boundsUsedIn.indices) {
|
||||
val boundUsedIn = boundsUsedIn[index]
|
||||
val type = JetTypeImpl(Annotations.EMPTY, boundUsedIn.typeVariable.getTypeConstructor(), false, listOf(), JetScope.Empty)
|
||||
generateNewBound(type, boundUsedIn, newBound)
|
||||
}
|
||||
|
||||
val constrainingType = newBound.constrainingType
|
||||
if (isMyTypeVariable(constrainingType)) {
|
||||
val bound = Bound(variable, newBound.kind.reverse(), newBound.position, isProper = false)
|
||||
addBound(constrainingType, bound)
|
||||
return
|
||||
}
|
||||
constrainingType.getNestedTypeVariables().forEach {
|
||||
val boundsForNestedVariable = getTypeBounds(it).bounds
|
||||
for (index in boundsForNestedVariable.indices) {
|
||||
generateNewBound(variable, newBound, boundsForNestedVariable[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConstraintSystemImpl.addConstraintFromBounds(old: Bound, new: Bound) {
|
||||
if (old == new) return
|
||||
|
||||
val oldType = old.constrainingType
|
||||
val newType = new.constrainingType
|
||||
val position = CompoundConstraintPosition(old.position, new.position)
|
||||
|
||||
when (old.kind to new.kind) {
|
||||
LOWER_BOUND to UPPER_BOUND, LOWER_BOUND to EXACT_BOUND, EXACT_BOUND to UPPER_BOUND ->
|
||||
addConstraint(SUB_TYPE, oldType, newType, position)
|
||||
|
||||
UPPER_BOUND to LOWER_BOUND, UPPER_BOUND to EXACT_BOUND, EXACT_BOUND to LOWER_BOUND ->
|
||||
addConstraint(SUB_TYPE, newType, oldType, position)
|
||||
|
||||
EXACT_BOUND to EXACT_BOUND ->
|
||||
addConstraint(EQUAL, oldType, newType, position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConstraintSystemImpl.generateNewBound(
|
||||
variable: JetType,
|
||||
bound: Bound,
|
||||
substitution: Bound
|
||||
) {
|
||||
// Let's have a variable T, a bound 'T <=> My<R>', and a substitution 'R <=> Type'.
|
||||
// Here <=> means lower_bound, upper_bound or exact_bound constraint.
|
||||
// Then a new bound 'T <=> My<Type>' can be generated.
|
||||
|
||||
// A variance of R in 'My<R>' (with respect to both use-site and declaration-site variance).
|
||||
val substitutionVariance: Variance = bound.constrainingType.getNestedTypeArguments().firstOrNull {
|
||||
getMyTypeVariable(it.getType()) === substitution.typeVariable
|
||||
}?.getProjectionKind() ?: return
|
||||
|
||||
// We don't substitute anything into recursive constraints
|
||||
if (substitution.typeVariable == bound.typeVariable) return
|
||||
|
||||
//todo variance checker
|
||||
val newKind = computeKindOfNewBound(bound.kind, substitutionVariance, substitution.kind) ?: return
|
||||
|
||||
val newTypeProjection = TypeProjectionImpl(substitutionVariance, substitution.constrainingType)
|
||||
val substitutor = TypeSubstitutor.create(mapOf(substitution.typeVariable.getTypeConstructor() to newTypeProjection))
|
||||
val newConstrainingType = substitutor.substitute(bound.constrainingType, INVARIANT)!!
|
||||
|
||||
// We don't generate new recursive constraints
|
||||
val nestedTypeVariables = newConstrainingType.getNestedTypeVariables()
|
||||
if (nestedTypeVariables.contains(bound.typeVariable) || nestedTypeVariables.contains(substitution.typeVariable)) return
|
||||
|
||||
val position = CompoundConstraintPosition(bound.position, substitution.position)
|
||||
addBound(variable, Bound(newConstrainingType, newKind, position, newConstrainingType.isProper()))
|
||||
}
|
||||
|
||||
private fun computeKindOfNewBound(constrainingKind: BoundKind, substitutionVariance: Variance, substitutionKind: BoundKind): BoundKind? {
|
||||
// In examples below: List<out T>, MutableList<T>, Comparator<in T>, the variance of My<T> may be any.
|
||||
|
||||
// T <=> My<R>, R <=> Type -> T <=> My<Type>
|
||||
|
||||
// T < My<R>, R = Int -> T < My<Int>
|
||||
if (substitutionKind == EXACT_BOUND) return constrainingKind
|
||||
|
||||
// T < MutableList<R>, R < Number - nothing can be inferred (R might become 'Int' later)
|
||||
// todo T < MutableList<R>, R < Int => T < MutableList<out Int>
|
||||
if (substitutionVariance == INVARIANT) return null
|
||||
|
||||
val kind = if (substitutionVariance == IN_VARIANCE) substitutionKind.reverse() else substitutionKind
|
||||
|
||||
// T = List<R>, R < Int -> T < List<Int>; T = Consumer<R>, R < Int -> T > Consumer<Int>
|
||||
if (constrainingKind == EXACT_BOUND) return kind
|
||||
|
||||
// T < List<R>, R < Int -> T < List<Int>; T < Consumer<R>, R > Int -> T < Consumer<Int>
|
||||
if (constrainingKind == kind) return kind
|
||||
|
||||
// otherwise we can generate no new constraints
|
||||
return null
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.registerTypeVariables;
|
||||
import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
|
||||
|
||||
public class TypeUtils {
|
||||
@@ -301,7 +302,7 @@ public class TypeUtils {
|
||||
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
|
||||
processAllTypeParameters(expected, Variance.INVARIANT, processor);
|
||||
ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
|
||||
constraintSystem.registerTypeVariables(parameters);
|
||||
registerTypeVariables(constraintSystem, parameters);
|
||||
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
|
||||
|
||||
return constraintSystem.getStatus().isSuccessful();
|
||||
|
||||
@@ -16,23 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.types.typeUtil
|
||||
|
||||
import java.util.LinkedHashSet
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.Flexibility
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.types.isDynamic
|
||||
import org.jetbrains.kotlin.types.TypeProjection
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.DelegatingType
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||
import java.util.*
|
||||
|
||||
private fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
||||
@@ -63,7 +57,8 @@ fun DeclarationDescriptor.getCapturedTypeParameters(): Collection<TypeParameterD
|
||||
|
||||
public fun JetType.getContainedAndCapturedTypeParameterConstructors(): Collection<TypeConstructor> {
|
||||
// todo type arguments (instead of type parameters) of the type of outer class must be considered; KT-6325
|
||||
val typeParameters = getContainedTypeParameters() + getConstructor().getDeclarationDescriptor()!!.getCapturedTypeParameters()
|
||||
val capturedTypeParameters = getConstructor().getDeclarationDescriptor()?.getCapturedTypeParameters() ?: emptyList()
|
||||
val typeParameters = getContainedTypeParameters() + capturedTypeParameters
|
||||
return typeParameters.map { it.getTypeConstructor() }.toReadOnlyList()
|
||||
}
|
||||
|
||||
@@ -90,4 +85,32 @@ public fun JetTypeChecker.equalTypesOrNulls(type1: JetType?, type2: JetType?): B
|
||||
if (type1 identityEquals type2) return true
|
||||
if (type1 == null || type2 == null) return false
|
||||
return equalTypes(type1, type2)
|
||||
}
|
||||
|
||||
fun JetType.getNestedTypeArguments(): List<TypeProjection> {
|
||||
val result = ArrayList<TypeProjection>()
|
||||
|
||||
val stack = ArrayDeque<TypeProjection>()
|
||||
stack.push(TypeProjectionImpl(this))
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
val typeProjection = stack.pop()
|
||||
if (typeProjection.isStarProjection()) continue
|
||||
|
||||
result.add(typeProjection)
|
||||
|
||||
val type = typeProjection.getType()
|
||||
|
||||
type.getConstructor().getParameters().zip(type.getArguments()).forEach {
|
||||
val (parameter, argument) = it
|
||||
val newTypeProjection = if (argument.getProjectionKind() == Variance.INVARIANT && parameter.getVariance() != Variance.INVARIANT) {
|
||||
TypeProjectionImpl(parameter.getVariance(), argument.getType())
|
||||
}
|
||||
else {
|
||||
argument
|
||||
}
|
||||
stack.add(newTypeProjection)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -16,18 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.util
|
||||
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl
|
||||
import java.util.LinkedHashMap
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintsUtil
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import java.util.HashSet
|
||||
|
||||
fun CallableDescriptor.fuzzyReturnType(): FuzzyType? {
|
||||
val returnType = getReturnType() ?: return null
|
||||
@@ -103,18 +100,14 @@ class FuzzyType(
|
||||
}
|
||||
|
||||
val constraintSystem = ConstraintSystemImpl()
|
||||
val typeVariables = LinkedHashMap<TypeParameterDescriptor, Variance>()
|
||||
for (typeParameter in freeParameters) {
|
||||
typeVariables[typeParameter] = Variance.INVARIANT
|
||||
}
|
||||
constraintSystem.registerTypeVariables(typeVariables)
|
||||
constraintSystem.registerTypeVariables(freeParameters, { Variance.INVARIANT })
|
||||
|
||||
when (matchKind) {
|
||||
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType, ConstraintPositionKind.SPECIAL.position())
|
||||
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType, type, ConstraintPositionKind.SPECIAL.position())
|
||||
}
|
||||
|
||||
constraintSystem.processDeclaredBoundConstraints()
|
||||
constraintSystem.fixVariables()
|
||||
|
||||
if (!constraintSystem.getStatus().hasContradiction()) {
|
||||
// currently ConstraintSystem return successful status in case there are problems with nullability
|
||||
|
||||
Reference in New Issue
Block a user