diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt index 09075e793f5..b2243f1ed0f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.psi.JetSuperExpression import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.types.* @@ -64,22 +65,17 @@ public fun replaceReturnTypeByUnknown(type: JetType): JetType { return JetTypeImpl(type.getAnnotations(), type.getConstructor(), type.isMarkedNullable(), newArguments, type.getMemberScope()) } -private fun hasReturnTypeDependentOnUninferredParams(candidateDescriptor: CallableDescriptor, constraintSystem: ConstraintSystem): Boolean { - val returnType = candidateDescriptor.getReturnType() ?: return false +private fun CallableDescriptor.hasReturnTypeDependentOnUninferredParams(constraintSystem: ConstraintSystem): Boolean { + val returnType = getReturnType() ?: return false - for (typeVariable in constraintSystem.getTypeVariables()) { - val inferredValueForTypeVariable = constraintSystem.getTypeBounds(typeVariable).value - if (inferredValueForTypeVariable == null) { - if (TypeUtils.dependsOnTypeParameters(returnType, setOf(typeVariable))) { - return true - } - } + val nestedTypeVariables = with (constraintSystem as ConstraintSystemImpl) { + returnType.getNestedTypeVariables(original = true) } - return false + return nestedTypeVariables.any { constraintSystem.getTypeBounds(it).value == null } } -public fun hasInferredReturnType(candidateDescriptor: CallableDescriptor, constraintSystem: ConstraintSystem): Boolean { - if (hasReturnTypeDependentOnUninferredParams(candidateDescriptor, constraintSystem)) return false +public fun CallableDescriptor.hasInferredReturnType(constraintSystem: ConstraintSystem): Boolean { + if (hasReturnTypeDependentOnUninferredParams(constraintSystem)) return false // Expected type mismatch was reported before as 'TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH' if (constraintSystem.getStatus().hasOnlyErrorsFromPosition(EXPECTED_TYPE_POSITION.position())) return 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 624c889111c..57a4c612e67 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl 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.RECEIVER_POSITION import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus @@ -62,6 +63,7 @@ class GenericCandidateResolver( val candidate = candidateCall.getCandidateDescriptor() val constraintSystem = ConstraintSystemImpl() + candidateCall.setConstraintSystem(constraintSystem) // If the call is recursive, e.g. // fun foo(t : T) : T = foo(t) @@ -71,16 +73,15 @@ class GenericCandidateResolver( // Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion) val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate) - val backConversion = candidateWithFreshVariables.getTypeParameters().zip(candidate.getTypeParameters()).toMap() + val conversionToOriginal = candidateWithFreshVariables.getTypeParameters().zip(candidate.getTypeParameters()).toMap() + constraintSystem.registerTypeVariables(candidateWithFreshVariables.getTypeParameters(), { Variance.INVARIANT }, { conversionToOriginal[it]!! }) - constraintSystem.registerTypeVariables(candidateWithFreshVariables.getTypeParameters(), { Variance.INVARIANT }) - - val substituteDontCare = makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE) + val substituteDontCare = makeConstantSubstitutor(candidate.getTypeParameters(), DONT_CARE) // Value parameters for (entry in candidateCall.getValueArguments().entrySet()) { val resolvedValueArgument = entry.getValue() - val valueParameterDescriptor = candidateWithFreshVariables.getValueParameters().get(entry.getKey().getIndex()) + val valueParameterDescriptor = candidate.getValueParameters().get(entry.getKey().getIndex()) for (valueArgument in resolvedValueArgument.getArguments()) { @@ -97,7 +98,7 @@ class GenericCandidateResolver( // Receiver // Error is already reported if something is missing val receiverArgument = candidateCall.getExtensionReceiver() - val receiverParameter = candidateWithFreshVariables.getExtensionReceiverParameter() + val receiverParameter = candidate.getExtensionReceiverParameter() if (receiverArgument.exists() && receiverParameter != null) { var receiverType: JetType? = if (context.candidateCall.isSafeCall()) TypeUtils.makeNotNullable(receiverArgument.getType()) @@ -109,11 +110,6 @@ class GenericCandidateResolver( constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), RECEIVER_POSITION.position()) } - // Restore type variables before alpha-conversion - val constraintSystemWithRightTypeParameters = constraintSystem.substituteTypeVariables { backConversion.get(it) } - candidateCall.setConstraintSystem(constraintSystemWithRightTypeParameters) - - // Solution val hasContradiction = constraintSystem.getStatus().hasContradiction() if (!hasContradiction) { @@ -168,7 +164,7 @@ class GenericCandidateResolver( val returnType = candidateDescriptor.getReturnType() ?: return false val nestedTypeVariables = with (argumentConstraintSystem) { - returnType.getNestedTypeVariables() + returnType.getNestedTypeVariables(original = true) } // we add an additional type variable only if no information is inferred for it. // otherwise we add currently inferred return type as before @@ -178,7 +174,7 @@ class GenericCandidateResolver( val conversion = candidateDescriptor.getTypeParameters().zip(candidateWithFreshVariables.getTypeParameters()).toMap() val freshVariables = nestedTypeVariables.map { conversion[it] }.filterNotNull() - constraintSystem.registerTypeVariables(freshVariables, { Variance.INVARIANT }, external = true) + constraintSystem.registerTypeVariables(freshVariables, { Variance.INVARIANT }, { it }, external = true) constraintSystem.addSubtypeConstraint(candidateWithFreshVariables.getReturnType(), effectiveExpectedType, constraintPosition) return true diff --git a/compiler/testData/diagnostics/tests/inference/constraints/kt6320.kt b/compiler/testData/diagnostics/tests/inference/constraints/kt6320.kt index aa90703e00a..fd2b06233bc 100644 --- a/compiler/testData/diagnostics/tests/inference/constraints/kt6320.kt +++ b/compiler/testData/diagnostics/tests/inference/constraints/kt6320.kt @@ -15,6 +15,6 @@ class C class D(foo: C) { fun test(a: C) { - val d: D = D(a) + val d: D = D(a) } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt b/compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt index ebb32733fdf..723967042de 100644 --- a/compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt +++ b/compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt @@ -17,8 +17,8 @@ fun test(out: Out, i: In, inv: A) { r checkType { _() } // T? <: Int => error - doIn(i) + doIn(i) // T? >: Int => error - doA(inv) + doA(inv) } diff --git a/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt b/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt index db6c4843960..6b8be3fbbdb 100644 --- a/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt +++ b/compiler/testData/diagnostics/tests/inference/immutableArrayList.kt @@ -12,6 +12,6 @@ import p.J.* class Foo: Sub { fun foo(): Super { - return Foo() + return Foo() } } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt index 8ed593b72fb..6cf755ac65b 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.TypeResolver import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.TYPE_BOUND_POSITION +import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariables import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.JetLiteFixture 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 3e18a4c3b7f..1a0071ed007 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 @@ -17,10 +17,10 @@ package org.jetbrains.kotlin.resolve.calls.inference import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition +import org.jetbrains.kotlin.types.Variance public trait ConstraintSystem { @@ -30,7 +30,8 @@ public trait ConstraintSystem { */ public fun registerTypeVariables( typeVariables: Collection, - typeVariableVariance: (TypeParameterDescriptor) -> Variance, + variance: (TypeParameterDescriptor) -> Variance, + mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor, external: Boolean = false ) 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 d424a67d23b..b4c0e6e804d 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 @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.resolve.calls.inference import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_B 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.EXPECTED_TYPE_POSITION 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 @@ -70,6 +72,12 @@ public class ConstraintSystemImpl : ConstraintSystem { private val initialConstraints = ArrayList() + private val originalToVariablesSubstitutor: TypeSubstitutor by lazy { + createTypeSubstitutor { originalToVariables[it] } + } + private val originalToVariables = LinkedHashMap() + private val variablesToOriginal = LinkedHashMap() + private val constraintSystemStatus = object : ConstraintSystemStatus { // for debug ConstraintsUtil.getDebugMessageForStatus might be used @@ -101,7 +109,8 @@ public class ConstraintSystemImpl : ConstraintSystem { private fun getParameterToInferredValueMap( typeParameterBounds: Map, - getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection + getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection, + substituteOriginal: Boolean ): Map { val substitutionContext = HashMap() for ((typeParameter, typeBounds) in typeParameterBounds) { @@ -113,34 +122,34 @@ public class ConstraintSystemImpl : ConstraintSystem { else { typeProjection = getDefaultTypeProjection(typeParameter) } - substitutionContext.put(typeParameter, typeProjection) + substitutionContext.put(if (substituteOriginal) variablesToOriginal[typeParameter]!! else typeParameter, typeProjection) } return substitutionContext } - private fun replaceUninferredBy(getDefaultValue: (TypeParameterDescriptor) -> TypeProjection): TypeSubstitutor { - return TypeUtils.makeSubstitutorForTypeParametersMap(getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue)) - } - - private fun replaceUninferredBy(defaultValue: JetType): TypeSubstitutor { - return replaceUninferredBy { TypeProjectionImpl(defaultValue) } - } - - private fun replaceUninferredBySpecialErrorType(): TypeSubstitutor { - return replaceUninferredBy { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) } + private fun replaceUninferredBy( + getDefaultValue: (TypeParameterDescriptor) -> TypeProjection, + substituteOriginal: Boolean + ): TypeSubstitutor { + val parameterToInferredValueMap = getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue, substituteOriginal) + return TypeUtils.makeSubstitutorForTypeParametersMap(parameterToInferredValueMap) } override fun getStatus(): ConstraintSystemStatus = constraintSystemStatus override fun registerTypeVariables( typeVariables: Collection, - typeVariableVariance: (TypeParameterDescriptor) -> Variance, + variance: (TypeParameterDescriptor) -> Variance, + mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor, external: Boolean ) { if (external) externalTypeParameters.addAll(typeVariables) for (typeVariable in typeVariables) { - allTypeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, typeVariableVariance(typeVariable))) + allTypeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, variance(typeVariable))) + val original = mapToOriginal(typeVariable) + originalToVariables[original] = typeVariable + variablesToOriginal[typeVariable] = original } for ((typeVariable, typeBounds) in allTypeParameterBounds) { for (declaredUpperBound in typeVariable.getUpperBounds()) { @@ -160,11 +169,10 @@ public class ConstraintSystemImpl : ConstraintSystem { type -> type.getConstructor().getDeclarationDescriptor() in getAllTypeVariables() } - fun JetType.getNestedTypeVariables(): List { + fun JetType.getNestedTypeVariables(original: Boolean = false): List { return getNestedTypeArguments().map { typeProjection -> typeProjection.getType().getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor - - }.filterNotNull().filter { it in getAllTypeVariables() } + }.filterNotNull().filter { if (original) it in originalToVariables.keySet() else it in getAllTypeVariables() } } public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis({ it }, { true }) @@ -216,17 +224,21 @@ public class ConstraintSystemImpl : ConstraintSystem { val newSuperType = typeSubstitutor.substitute(it.superType, Variance.INVARIANT) if (newSubType != null && newSuperType != null) Constraint(it.kind, newSubType, newSuperType, it.position) else null }) + newSystem.originalToVariables.putAll(originalToVariables) + newSystem.variablesToOriginal.putAll(variablesToOriginal) return newSystem } override fun addSupertypeConstraint(constrainingType: JetType?, subjectType: JetType, constraintPosition: ConstraintPosition) { if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return - addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition) + val newSubjectType = originalToVariablesSubstitutor.substitute(subjectType, Variance.INVARIANT) + addConstraint(SUB_TYPE, newSubjectType, constrainingType, constraintPosition) } override fun addSubtypeConstraint(constrainingType: JetType?, subjectType: JetType, constraintPosition: ConstraintPosition) { - addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition) + val newSubjectType = originalToVariablesSubstitutor.substitute(subjectType, Variance.INVARIANT) + addConstraint(SUB_TYPE, constrainingType, newSubjectType, constraintPosition) } fun addConstraint(constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, constraintPosition: ConstraintPosition) { @@ -429,24 +441,23 @@ public class ConstraintSystemImpl : ConstraintSystem { addBound(typeVariable, capturedType, EXACT_BOUND, constraintPosition) } - override fun getTypeVariables() = typeParameterBounds.keySet() + override fun getTypeVariables() = originalToVariables.keySet() fun getAllTypeVariables() = allTypeParameterBounds.keySet() fun getBoundsUsedIn(typeVariable: TypeParameterDescriptor): List = usedInBounds[typeVariable] ?: emptyList() override fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl { + val variableForOriginal = originalToVariables[typeVariable] + if (variableForOriginal != null && variableForOriginal != typeVariable) { + return getTypeBounds(variableForOriginal) + } if (!isMyTypeVariable(typeVariable)) { throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable") } return allTypeParameterBounds[typeVariable]!! } - fun getTypeBounds(parameterType: JetType): TypeBoundsImpl { - assert (isMyTypeVariable(parameterType)) { "Type is not a type variable for constraint system: $parameterType" } - return getTypeBounds(getMyTypeVariable(parameterType)!!) - } - fun isMyTypeVariable(typeVariable: TypeParameterDescriptor) = allTypeParameterBounds.contains(typeVariable) fun isMyTypeVariable(type: JetType): Boolean = getMyTypeVariable(type) != null @@ -456,9 +467,14 @@ public class ConstraintSystemImpl : ConstraintSystem { return if (typeParameterDescriptor != null && isMyTypeVariable(typeParameterDescriptor)) typeParameterDescriptor else null } - override fun getResultingSubstitutor() = replaceUninferredBySpecialErrorType().setApproximateCapturedTypes() + override fun getResultingSubstitutor() = + getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) } - override fun getCurrentSubstitutor() = replaceUninferredBy(TypeUtils.DONT_CARE).setApproximateCapturedTypes() + override fun getCurrentSubstitutor() = + getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(TypeUtils.DONT_CARE) } + + private fun getSubstitutor(substituteOriginal: Boolean, getDefaultValue: (TypeParameterDescriptor) -> TypeProjection) = + replaceUninferredBy(getDefaultValue, substituteOriginal).setApproximateCapturedTypes() private fun storeInitialConstraint(constraintKind: ConstraintKind, subType: JetType, superType: JetType, position: ConstraintPosition) { if (position is CompoundConstraintPosition) return @@ -467,7 +483,8 @@ public class ConstraintSystemImpl : ConstraintSystem { private fun satisfyInitialConstraints(): Boolean { fun JetType.substituteAndMakeNotNullable(): JetType? { - val result = getResultingSubstitutor().substitute(this, Variance.INVARIANT) ?: return null + val substitutor = getSubstitutor(substituteOriginal = false) { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) } + val result = substitutor.substitute(this, Variance.INVARIANT) ?: return null return TypeUtils.makeNotNullable(result) } return initialConstraints.all { @@ -538,5 +555,12 @@ private class SubstitutionWithCapturedTypeApproximation(val substitution: TypeSu } public fun ConstraintSystemImpl.registerTypeVariables(typeVariables: Map) { - registerTypeVariables(typeVariables.keySet(), { typeVariables[it]!! }) + registerTypeVariables(typeVariables.keySet(), { typeVariables[it]!! }, { it }) +} + +public fun ConstraintSystemImpl.registerTypeVariables( + typeVariables: Collection, + variance: (TypeParameterDescriptor) -> Variance +) { + registerTypeVariables(typeVariables, variance, { it }) } \ 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 d2be701309b..c62bf2b3c20 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 @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind +import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariables import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.Variance