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> {
@@ -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)
@@ -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 }
}
@@ -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>
@@ -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]!! })
}
@@ -84,4 +84,9 @@ public trait ConstraintSystemStatus {
* in invocation <tt>foo(array)</tt> where array has type <tt>Array&lt;Array&lt;out Int&gt;&gt;</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
}
@@ -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()
}
@@ -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