diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 078556686d8..f337d9fe35d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -459,6 +459,7 @@ public interface Errors { DiagnosticFactory1 TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPE_INFERENCE_CANNOT_CAPTURE_TYPES = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 TYPE_INFERENCE_INCORPORATION_ERROR = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 5eed0c12740..b1ae845790f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt index f08c04f856b..2a1061b0c49 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt @@ -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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java index e3ad15b08ef..6d57f4c0e68 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java @@ -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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index 10f47212712..1a0700d8119 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index 86b56658ea0..b784dcb9953 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -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() - 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(valueArgument, valueParameterDescriptor, constraintSystem, context) + addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context) } } resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java index a3f1ceaf1f6..69f1fec7b05 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java @@ -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)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTaskHolder.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTaskHolder.kt index 13e4117743c..edafbdb4cd4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTaskHolder.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTaskHolder.kt @@ -58,7 +58,7 @@ public class ResolutionTaskHolder( val lazyCandidates = { candidatesList[candidateIndex]().filter { priorityProvider.getPriority(it) == priority }.toReadOnlyList() } - tasks.add(ResolutionTask(basicCallResolutionContext, tracing, lazyCandidates)) + tasks.add(ResolutionTask(basicCallResolutionContext, tracing, lazyCandidates)) } } diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt index ad3d058475b..6e418323021 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt @@ -1,10 +1,10 @@ package foo open class A { - val B.w: Int by MyProperty() + val B.w: Int by MyProperty() } -val B.r: Int by MyProperty() +val B.r: Int by MyProperty() val A.e: Int by MyProperty() diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/conflictingSubstitutionsFromUpperBound.kt b/compiler/testData/diagnostics/tests/inference/upperBounds/conflictingSubstitutionsFromUpperBound.kt index 091243b2b07..062a40d549d 100644 --- a/compiler/testData/diagnostics/tests/inference/upperBounds/conflictingSubstitutionsFromUpperBound.kt +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/conflictingSubstitutionsFromUpperBound.kt @@ -7,6 +7,6 @@ fun > convert(src: Collection, dest: C): C = throw Except fun test(l: List) { //todo should be inferred - val r = convert(l, HashSet()) - checkSubtype(r) + val r = convert(l, HashSet()) + checkSubtype>(r) } diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt index 1e5cd3be0e3..775f12ec20c 100644 --- a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt @@ -29,8 +29,7 @@ fun test3() { fun emptyStrangeMap1(t: T): Map = throw Exception("$t") fun test4() { - //todo we may infer 'Int' for 'R' here - emptyStrangeMap1(1) + emptyStrangeMap1(1) } //-------------- @@ -38,8 +37,7 @@ fun test4() { fun emptyStrangeMap2(t: T): Map where R: A = throw Exception("$t") fun test5(a: A) { - //todo we may infer 'A' for 'R' here - emptyStrangeMap2(a) + emptyStrangeMap2(a) } //-------------- diff --git a/compiler/testData/diagnostics/tests/platformTypes/inference.kt b/compiler/testData/diagnostics/tests/platformTypes/inference.kt index a5fc0a7ff4d..bc322da00d1 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/inference.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/inference.kt @@ -16,11 +16,10 @@ public class HS extends Base {} import foo.*; -import java.util.HashSet fun > convert(src: HS, dest: C): C = throw Exception("$src $dest") fun test(l: HS) { //todo should be inferred - val r = convert(l, HS()) - checkSubtype(r) + val r = convert(l, HS()) + checkSubtype>(r) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/regressions/itselfAsUpperBoundLocal.kt b/compiler/testData/diagnostics/tests/regressions/itselfAsUpperBoundLocal.kt index 353988510c3..35af733335a 100644 --- a/compiler/testData/diagnostics/tests/regressions/itselfAsUpperBoundLocal.kt +++ b/compiler/testData/diagnostics/tests/regressions/itselfAsUpperBoundLocal.kt @@ -1,4 +1,4 @@ fun bar() { fun T?> foo() {} - foo() + foo() } diff --git a/compiler/testData/diagnostics/tests/resolve/nestedCalls/kt7597.kt b/compiler/testData/diagnostics/tests/resolve/nestedCalls/kt7597.kt index 9d8aac8a083..9e8cb9218b1 100644 --- a/compiler/testData/diagnostics/tests/resolve/nestedCalls/kt7597.kt +++ b/compiler/testData/diagnostics/tests/resolve/nestedCalls/kt7597.kt @@ -1,10 +1,8 @@ -// !CHECK_TYPE - interface Inv fun Inv.reduce2(): S = null!! fun test(a: Inv): Int { - val b = 1 + a.reduce2() - return b + val b = 1 + a.reduce2() + return b } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/nestedCalls/twoTypeParameters.kt b/compiler/testData/diagnostics/tests/resolve/nestedCalls/twoTypeParameters.kt index ab81fa1efc1..298efdc4e3d 100644 --- a/compiler/testData/diagnostics/tests/resolve/nestedCalls/twoTypeParameters.kt +++ b/compiler/testData/diagnostics/tests/resolve/nestedCalls/twoTypeParameters.kt @@ -6,7 +6,12 @@ fun List>.bar(t: ResolutionTask) = t public class ResolutionTaskHolder { fun test(candidate: ResolutionCandidate, tasks: MutableList>) { - tasks.bar(ResolutionTask(candidate)) + tasks.bar(ResolutionTask(candidate)) + tasks.add(ResolutionTask(candidate)) + + //todo the problem is the type of ResolutionTask is inferred as ResolutionTask too early + tasks.bar(ResolutionTask(candidate)) + tasks.add(ResolutionTask(candidate)) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/resolve/kt4711.kt b/compiler/testData/diagnostics/testsWithStdLib/resolve/kt4711.kt index 0a14b59baa4..1d783be79c8 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/resolve/kt4711.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/resolve/kt4711.kt @@ -6,10 +6,10 @@ fun main(args:Array) { val startTimeNanos = System.nanoTime() // the problem sits on the next line: - val pi = 4.0.toDouble() * delta * (1..n).reduce( + val pi = 4.0.toDouble() * delta * (1..n).reduce( {t, i -> val x = (i - 0.5) * delta - t + 1.0 / (1.0 + x * x) + t + 1.0 / (1.0 + x * x) }) // !!! pi has error type here diff --git a/compiler/testData/resolvedCalls/arguments/realExamples/emptyList.txt b/compiler/testData/resolvedCalls/arguments/realExamples/emptyList.txt index 24203fd1e26..2ae11394112 100644 --- a/compiler/testData/resolvedCalls/arguments/realExamples/emptyList.txt +++ b/compiler/testData/resolvedCalls/arguments/realExamples/emptyList.txt @@ -12,7 +12,7 @@ fun bar() { Resolved call: Candidate descriptor: fun foo(t: T): Unit defined in root package -Resulting descriptor: fun foo(t: List): Unit defined in root package +Resulting descriptor: fun 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() diff --git a/compiler/testData/resolvedCalls/arguments/realExamples/emptyMutableList.txt b/compiler/testData/resolvedCalls/arguments/realExamples/emptyMutableList.txt index 6537d71ccba..55ee516d0d4 100644 --- a/compiler/testData/resolvedCalls/arguments/realExamples/emptyMutableList.txt +++ b/compiler/testData/resolvedCalls/arguments/realExamples/emptyMutableList.txt @@ -12,7 +12,7 @@ fun bar() { Resolved call: Candidate descriptor: fun foo(t: T): Unit defined in root package -Resulting descriptor: fun foo(t: MutableList): Unit defined in root package +Resulting descriptor: fun 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() diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt index 1cffd813aba..e676fad2428 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt @@ -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() 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 { diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt index 5818c150f54..e3b98365222 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt @@ -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 \ No newline at end of file +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) \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt index 169096171a0..ca4b957f452 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt @@ -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 = positions.toList() + val positions: Collection = + positions.flatMap { if (it is CompoundConstraintPosition) it.positions else listOf(it) }.toCollection(LinkedHashSet()) 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 } +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt index 8a41660f50d..3e18a4c3b7f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt @@ -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) + public fun registerTypeVariables( + typeVariables: Collection, + 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 diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt index 968451918b4..4e620384132 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt @@ -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() + fun ConstraintKind.toBound() = if (this == SUB_TYPE) UPPER_BOUND else EXACT_BOUND + + private val allTypeParameterBounds = LinkedHashMap() + private val externalTypeParameters = HashSet() + private val typeParameterBounds: Map + get() = if (externalTypeParameters.isEmpty()) allTypeParameterBounds + else allTypeParameterBounds.filter { !externalTypeParameters.contains(it.key) } + + private val usedInBounds = HashMap>() private val errors = ArrayList() public val constraintErrors: List @@ -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) { - for ((typeVariable, positionVariance) in typeVariables) { - typeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, positionVariance)) + override fun registerTypeVariables( + typeVariables: Collection, + 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 { + 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 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 = 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) { + registerTypeVariables(typeVariables.keySet(), { typeVariables[it]!! }) +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemStatus.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemStatus.kt index 367ec7c1479..1b403fb513a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemStatus.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemStatus.kt @@ -84,4 +84,9 @@ public trait ConstraintSystemStatus { * in invocation foo(array) where array has type Array<Array<out Int>>. */ public fun hasCannotCaptureTypesError(): Boolean + + /** + * Returns true if there's an error in constraint system incorporation. + */ + public fun hasTypeInferenceIncorporationError(): Boolean } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt index fc759a211a8..4ac4d79d743 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt @@ -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 public val value: JetType? + get() = if (values.size() == 1) values.first() else null public val values: Collection @@ -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 } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt index 2b921f82b4a..1a05aaeef8d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt @@ -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() + override val bounds = ArrayList() private var resultValues: Collection? = 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, kind: BoundKind): Set { @@ -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 get() { if (resultValues == null) { @@ -90,6 +100,8 @@ public class TypeBoundsImpl( private fun computeValues(): Collection { val values = LinkedHashSet() + 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) - 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, 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.substitute(substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor?): List { + 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() +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt new file mode 100644 index 00000000000..821a129c10b --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt @@ -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', and a substitution 'R <=> Type'. + // Here <=> means lower_bound, upper_bound or exact_bound constraint. + // Then a new bound 'T <=> My' can be generated. + + // A variance of R in 'My' (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, MutableList, Comparator, the variance of My may be any. + + // T <=> My, R <=> Type -> T <=> My + + // T < My, R = Int -> T < My + if (substitutionKind == EXACT_BOUND) return constrainingKind + + // T < MutableList, R < Number - nothing can be inferred (R might become 'Int' later) + // todo T < MutableList, R < Int => T < MutableList + if (substitutionVariance == INVARIANT) return null + + val kind = if (substitutionVariance == IN_VARIANCE) substitutionKind.reverse() else substitutionKind + + // T = List, R < Int -> T < List; T = Consumer, R < Int -> T > Consumer + if (constrainingKind == EXACT_BOUND) return kind + + // T < List, R < Int -> T < List; T < Consumer, R > Int -> T < Consumer + if (constrainingKind == kind) return kind + + // otherwise we can generate no new constraints + return null +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java index 32cd7348529..9c03dd4c489 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java @@ -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(); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 071e9567383..43f0a4984a8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -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 { val declarationDescriptor = getConstructor().getDeclarationDescriptor() @@ -63,7 +57,8 @@ fun DeclarationDescriptor.getCapturedTypeParameters(): Collection { // 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 { + val result = ArrayList() + + val stack = ArrayDeque() + 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 } \ No newline at end of file diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt index 48f8687fbed..d2be701309b 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt @@ -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() - 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