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 bae460cd39b..7f73d61b1bb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -56,17 +56,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes val candidate = candidateCall.candidateDescriptor val builder = ConstraintSystemBuilderImpl() - - // If the call is recursive, e.g. - // fun foo(t : T) : T = foo(t) - // we can't use same descriptor objects for T's as actual type values and same T's as unknowns, - // because constraints become trivial (T :< T), and inference fails - // - // Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion) - val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate) - - val conversionToOriginal = candidateWithFreshVariables.typeParameters.zip(candidate.typeParameters).toMap() - builder.registerTypeVariables(candidateWithFreshVariables.typeParameters, { conversionToOriginal[it]!! }) + builder.registerTypeVariables(candidate.typeParameters) val substituteDontCare = makeConstantSubstitutor(candidate.typeParameters, DONT_CARE) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt index c27a4b0aafb..c11719773fc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt @@ -65,14 +65,10 @@ interface ConstraintSystem { interface Builder { /** - * 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. + * Registers variables in a constraint system. Returns a substitutor which maps type parameter descriptors given as parameters + * to the corresponding type variables of the system. Use that substitutor to provide constraints to the system */ - fun registerTypeVariables( - typeVariables: Collection, - mapToDescriptor: (TypeParameterDescriptor) -> TypeParameterDescriptor = { it }, - external: Boolean = false - ) + fun registerTypeVariables(typeParameters: Collection, external: Boolean = false): TypeSubstitutor /** * Adds a constraint that the constraining type is a subtype of the subject type. diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt index 76bb4b5d29d..c886720c9c2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt @@ -67,19 +67,25 @@ class ConstraintSystemBuilderImpl : ConstraintSystem.Builder { }) } - override fun registerTypeVariables( - typeVariables: Collection, - mapToDescriptor: (TypeParameterDescriptor) -> TypeParameterDescriptor, - external: Boolean - ) { - if (external) externalTypeParameters.addAll(typeVariables) + override fun registerTypeVariables(typeParameters: Collection, external: Boolean): TypeSubstitutor { + if (typeParameters.isEmpty()) return TypeSubstitutor.EMPTY - for (typeVariable in typeVariables) { + val typeVariables = if (external) { + externalTypeParameters.addAll(typeParameters) + typeParameters.toList() + } + else ArrayList(typeParameters.size).apply { + DescriptorSubstitutor.substituteTypeParameters( + typeParameters.toList(), TypeSubstitution.EMPTY, typeParameters.first().containingDeclaration, this + ) + } + + for ((descriptor, typeVariable) in typeParameters.zip(typeVariables)) { allTypeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable)) - val descriptor = mapToDescriptor(typeVariable) descriptorToVariable[descriptor] = typeVariable variableToDescriptor[typeVariable] = descriptor } + for ((typeVariable, typeBounds) in allTypeParameterBounds) { for (declaredUpperBound in typeVariable.upperBounds) { if (declaredUpperBound.isDefaultBound()) continue //todo remove this line (?) @@ -87,6 +93,10 @@ class ConstraintSystemBuilderImpl : ConstraintSystem.Builder { addBound(typeVariable, declaredUpperBound, UPPER_BOUND, context) } } + + return TypeSubstitutor.create(TypeConstructorSubstitution.createByParametersMap( + typeParameters.zip(TypeUtils.getDefaultTypeProjections(typeVariables)).toMap() + )) } internal val TypeParameterDescriptor.correspondingType: KotlinType diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java b/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java index 1c34030fcdc..3f019bd223f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java @@ -22,7 +22,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem; @@ -186,12 +185,7 @@ public class TypeIntersector { processAllTypeParameters(withParameters, Variance.INVARIANT, processor); processAllTypeParameters(expected, Variance.INVARIANT, processor); ConstraintSystem.Builder constraintSystem = new ConstraintSystemBuilderImpl(); - constraintSystem.registerTypeVariables(parameters.keySet(), new Function1() { - @Override - public TypeParameterDescriptor invoke(TypeParameterDescriptor descriptor) { - return descriptor; - } - }, false); + constraintSystem.registerTypeVariables(parameters.keySet(), false); constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position()); return constraintSystem.build().getStatus().isSuccessful(); diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt index eac7b2a44ed..248db3025de 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt @@ -81,16 +81,20 @@ abstract public class AbstractConstraintSystemTest() : KotlinLiteFixture() { val variables = parseVariables(constraintsFileText) val fixVariables = constraintsFileText.contains("FIX_VARIABLES") val typeParameterDescriptors = variables.map { testDeclarations.getParameterDescriptor(it) } - builder.registerTypeVariables(typeParameterDescriptors) + val substitutor = builder.registerTypeVariables(typeParameterDescriptors) val constraints = parseConstraints(constraintsFileText) - fun KotlinType.assertNotError(): KotlinType { - assert(!ErrorUtils.containsErrorType(this)) { "Type $this is resolved to or contains error type" } - return this + + fun getType(typeString: String): KotlinType { + val type = testDeclarations.getType(typeString).apply { + assert(!ErrorUtils.containsErrorType(this)) { "Type $this is resolved to or contains error type" } + } + return substitutor.substitute(type, Variance.INVARIANT) ?: error("Failed to substitute $type") } + for (constraint in constraints) { - val firstType = testDeclarations.getType(constraint.firstType).assertNotError() - val secondType = testDeclarations.getType(constraint.secondType).assertNotError() + val firstType = getType(constraint.firstType) + val secondType = getType(constraint.secondType) val context = ConstraintContext(SPECIAL.position(), initial = true) when (constraint.kind) { MyConstraintKind.SUBTYPE -> builder.addSubtypeConstraint(firstType, secondType, context.position) @@ -118,8 +122,7 @@ abstract public class AbstractConstraintSystemTest() : KotlinLiteFixture() { } class MyConstraint(val kind: MyConstraintKind, val firstType: String, val secondType: String) - enum class MyConstraintKind - private constructor(val token: String) { + enum class MyConstraintKind(val token: String) { SUBTYPE("<:"), SUPERTYPE(">:"), EQUAL(":=") } @@ -132,8 +135,8 @@ abstract public class AbstractConstraintSystemTest() : KotlinLiteFixture() { } private fun parseConstraints(lines: List): List { - val kindsMap = MyConstraintKind.values().map { it.token to it }.toMap() - val kinds = kindsMap.keySet() + val kindsMap = MyConstraintKind.values.map { it.token to it }.toMap() + val kinds = kindsMap.keys val linesWithConstraints = lines.filter { line -> kinds.any { kind -> line.contains(kind) } } return linesWithConstraints.map { line -> 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 dd49561452d..c27afc934cf 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 @@ -115,12 +115,19 @@ class FuzzyType( } val builder = ConstraintSystemBuilderImpl() - builder.registerTypeVariables(freeParameters) - builder.registerTypeVariables(otherType.freeParameters) + val typeVariableSubstitutor = builder.registerTypeVariables(freeParameters + otherType.freeParameters) when (matchKind) { - MatchKind.IS_SUBTYPE -> builder.addSubtypeConstraint(type, otherType.type, ConstraintPositionKind.RECEIVER_POSITION.position()) - MatchKind.IS_SUPERTYPE -> builder.addSubtypeConstraint(otherType.type, type, ConstraintPositionKind.RECEIVER_POSITION.position()) + MatchKind.IS_SUBTYPE -> builder.addSubtypeConstraint( + typeVariableSubstitutor.substitute(type, Variance.INVARIANT), + otherType.type, + ConstraintPositionKind.RECEIVER_POSITION.position() + ) + MatchKind.IS_SUPERTYPE -> builder.addSubtypeConstraint( + typeVariableSubstitutor.substitute(otherType.type, Variance.INVARIANT), + type, + ConstraintPositionKind.RECEIVER_POSITION.position() + ) } builder.fixVariables()