Generalize signature comparison: make it work for regular call overloading conflict resolution.

This commit is contained in:
Dmitry Petrov
2016-02-17 15:40:48 +03:00
parent 022162322d
commit c5f3cbc645
4 changed files with 114 additions and 55 deletions
@@ -38,6 +38,8 @@ enum class ConstraintPositionKind {
}
}
fun valueParameterPosition(index: Int) = ConstraintPositionKind.VALUE_PARAMETER_POSITION.position(index)
interface ConstraintPosition {
val kind: ConstraintPositionKind
@@ -33,9 +33,6 @@ class FlatSignature<out T>(
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 {
@@ -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<T> {
fun isNotLessSpecificByOrigin(signature1: FlatSignature<T>, signature2: FlatSignature<T>): Boolean?
fun isTypeNotLessSpecific(type1: KotlinType, type2: KotlinType): Boolean?
}
fun <T> isSignatureNotLessSpecific(
signature1: FlatSignature<T>,
signature2: FlatSignature<T>,
callHandle: CallHandle,
callbacks: SpecificityComparisonCallbacks<T>
): 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
}
@@ -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<MutableResolvedCall<D>>,
call2: FlatSignature<MutableResolvedCall<D>>,
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<MutableResolvedCall<*>> {
override fun isTypeNotLessSpecific(type1: KotlinType, type2: KotlinType): Boolean? =
numericTypeMoreSpecific(type1, type2)
}
private val DefaultSpecificityComparison = object : SpecificityComparisonWithNumerics() {
override fun isNotLessSpecificByOrigin(
signature1: FlatSignature<MutableResolvedCall<*>>,
signature2: FlatSignature<MutableResolvedCall<*>>
): Boolean? =
null
}
private val SpecificityComparisonDiscriminatingGenerics = object : SpecificityComparisonWithNumerics() {
override fun isNotLessSpecificByOrigin(
signature1: FlatSignature<MutableResolvedCall<*>>,
signature2: FlatSignature<MutableResolvedCall<*>>
): 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 <D: CallableDescriptor> isOfNotLessSpecificShape(