diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt index f8f52f2ed17..fe0935085e4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintPosition.kt @@ -38,6 +38,8 @@ enum class ConstraintPositionKind { } } +fun valueParameterPosition(index: Int) = ConstraintPositionKind.VALUE_PARAMETER_POSITION.position(index) + interface ConstraintPosition { val kind: ConstraintPositionKind diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt index 6a89fc76bac..a774684c57c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt @@ -33,9 +33,6 @@ class FlatSignature( val hasVarargs: Boolean, val numDefaults: Int ) { - override fun toString() = "<${typeParameters.joinToString {it.name.asString()} }> ${valueParameterTypes.joinToString()}; " + - "${if (hasVarargs) "varargs;" else ""} $numDefaults" - val isGeneric = typeParameters.isNotEmpty() companion object { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureSpecificity.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureSpecificity.kt new file mode 100644 index 00000000000..9d0f8d0d0ba --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureSpecificity.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2016 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.results + +import org.jetbrains.kotlin.resolve.calls.inference.CallHandle +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.valueParameterPosition +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker + +interface SpecificityComparisonCallbacks { + fun isNotLessSpecificByOrigin(signature1: FlatSignature, signature2: FlatSignature): Boolean? + fun isTypeNotLessSpecific(type1: KotlinType, type2: KotlinType): Boolean? +} + +fun isSignatureNotLessSpecific( + signature1: FlatSignature, + signature2: FlatSignature, + callHandle: CallHandle, + callbacks: SpecificityComparisonCallbacks +): Boolean { + callbacks.isNotLessSpecificByOrigin(signature1, signature2)?.let { return it } + + val typeParameters = signature2.typeParameters + val constraintSystemBuilder: ConstraintSystem.Builder = ConstraintSystemBuilderImpl() + var numConstraints = 0 + val typeSubstitutor = constraintSystemBuilder.registerTypeVariables(callHandle, typeParameters) + + for ((type1, type2) in signature1.valueParameterTypes.zip(signature2.valueParameterTypes)) { + if (type1 == null || type2 == null) continue + + if (isDefinitelyLessSpecificByTypeSpecificity(type1, type2)) { + return false + } + + if (typeParameters.isEmpty() || !TypeUtils.dependsOnTypeParameters(type2, typeParameters)) { + if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(type1, type2)) { + callbacks.isTypeNotLessSpecific(type1, type2)?.let { if (!it) return false } + } + } + else { + val constraintPosition = valueParameterPosition(numConstraints++) + val substitutedType2 = typeSubstitutor.safeSubstitute(type2, Variance.INVARIANT) + constraintSystemBuilder.addSubtypeConstraint(type1, substitutedType2, constraintPosition) + } + } + + if (numConstraints > 0) { + constraintSystemBuilder.fixVariables() + val constraintSystem = constraintSystemBuilder.build() + if (constraintSystem.status.hasContradiction()) { + return false + } + } + + return true +} + +private fun isDefinitelyLessSpecificByTypeSpecificity(specific: KotlinType, general: KotlinType): Boolean { + val sThanG = specific.getSpecificityRelationTo(general) + val gThanS = general.getSpecificityRelationTo(specific) + return sThanG == Specificity.Relation.LESS_SPECIFIC && + gThanS != Specificity.Relation.LESS_SPECIFIC +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt index a0fcdaa0ffb..9912638d5c6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt @@ -21,17 +21,16 @@ import gnu.trove.TObjectHashingStrategy import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION import org.jetbrains.kotlin.resolve.calls.inference.toHandle import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionMutableResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall -import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.Specificity +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.getSpecificityRelationTo class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { @@ -147,59 +146,41 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { call1: FlatSignature>, call2: FlatSignature>, discriminateGenerics: Boolean - ): Boolean { - if (discriminateGenerics) { - val isGeneric1 = call1.isGeneric - val isGeneric2 = call2.isGeneric + ): Boolean = + isSignatureNotLessSpecific(call1, call2, call1.callHandle(), + if (discriminateGenerics) + SpecificityComparisonDiscriminatingGenerics + else + DefaultSpecificityComparison) + + private abstract inner class SpecificityComparisonWithNumerics : SpecificityComparisonCallbacks> { + override fun isTypeNotLessSpecific(type1: KotlinType, type2: KotlinType): Boolean? = + numericTypeMoreSpecific(type1, type2) + } + + private val DefaultSpecificityComparison = object : SpecificityComparisonWithNumerics() { + override fun isNotLessSpecificByOrigin( + signature1: FlatSignature>, + signature2: FlatSignature> + ): Boolean? = + null + } + + private val SpecificityComparisonDiscriminatingGenerics = object : SpecificityComparisonWithNumerics() { + override fun isNotLessSpecificByOrigin( + signature1: FlatSignature>, + signature2: FlatSignature> + ): Boolean? { + val isGeneric1 = signature1.isGeneric + val isGeneric2 = signature2.isGeneric // generic loses to non-generic if (isGeneric1 && !isGeneric2) return false if (!isGeneric1 && isGeneric2) return true // two generics are non-comparable if (isGeneric1 && isGeneric2) return false + // otherwise continue comparing signatures + return null } - - val typeParameters = call2.typeParameters - val constraintSystemBuilder: ConstraintSystem.Builder = ConstraintSystemBuilderImpl() - var hasConstraints = false - val typeSubstitutor = constraintSystemBuilder.registerTypeVariables(call1.callHandle(), typeParameters) - - fun compareTypesAndUpdateConstraints(type1: KotlinType?, type2: KotlinType?, constraintPosition: ConstraintPosition): Boolean { - if (type1 == null || type2 == null) return true - - if (typeParameters.isEmpty() || !TypeUtils.dependsOnTypeParameters(type2, typeParameters)) { - if (!typeNotLessSpecific(type1, type2)) { - return false - } - } - else if (isDefinitelyLessSpecificByTypeSpecificity(type1, type2)) { - return false - } - else { - hasConstraints = true - val substitutedType2 = typeSubstitutor.safeSubstitute(type2, Variance.INVARIANT) - constraintSystemBuilder.addSubtypeConstraint(type1, substitutedType2, constraintPosition) - } - return true - } - - var argumentIndex = 0 - for ((type1, type2) in call1.valueParameterTypes.zip(call2.valueParameterTypes)) { - // We use this constraint system for subtyping relation check only, - // so exact value parameter position doesn't matter. - if (!compareTypesAndUpdateConstraints(type1, type2, VALUE_PARAMETER_POSITION.position(++argumentIndex))) { - return false - } - } - - if (hasConstraints) { - constraintSystemBuilder.fixVariables() - val constraintSystem = constraintSystemBuilder.build() - if (constraintSystem.status.hasContradiction()) { - return false - } - } - - return true } private fun isOfNotLessSpecificShape(