Introduce "flat signature" - a generalized abstraction for a (call) signatures that can be compared for specificity.

This commit is contained in:
Dmitry Petrov
2016-02-17 13:44:21 +03:00
parent 77b7128689
commit 022162322d
3 changed files with 117 additions and 151 deletions
@@ -1,92 +0,0 @@
/*
* 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.results
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.types.KotlinType
class CandidateCallWithArgumentMapping<D : CallableDescriptor, K> private constructor(
val resolvedCall: MutableResolvedCall<D>,
private val argumentsToParameters: Map<K, ValueParameterDescriptor>,
val parametersWithDefaultValuesCount: Int
) {
override fun toString(): String =
"${resolvedCall.call}: $parametersWithDefaultValuesCount defaults in ${resolvedCall.candidateDescriptor}"
val candidateDescriptor: D
get() = resolvedCall.candidateDescriptor
val hasVarargs: Boolean
get() = candidateDescriptor.valueParameters.any { it.varargElementType != null }
val typeParameters: List<TypeParameterDescriptor>
get() = candidateDescriptor.original.typeParameters
val argumentsCount: Int
get() = argumentsToParameters.size
val argumentKeys: Collection<K>
get() = argumentsToParameters.keys
val isGeneric: Boolean = typeParameters.isNotEmpty()
val extensionReceiverType: KotlinType?
get() = candidateDescriptor.extensionReceiverParameter?.type
/**
* Returns the type of a value that can be used in place of the corresponding parameter.
*/
fun getValueParameterType(argumentKey: K): KotlinType? =
argumentsToParameters[argumentKey]?.let {
valueParameterDescriptor ->
valueParameterDescriptor.varargElementType ?: valueParameterDescriptor.type
}
companion object {
fun <D : CallableDescriptor, K> create(
call: MutableResolvedCall<D>,
resolvedArgumentToKeys: (ResolvedValueArgument) -> Collection<K>
): CandidateCallWithArgumentMapping<D, K> {
val argumentsToParameters = hashMapOf<K, ValueParameterDescriptor>()
var parametersWithDefaultValuesCount = 0
val unsubstitutedValueParameters = call.candidateDescriptor.original.valueParameters
for ((valueParameterDescriptor, resolvedValueArgument) in call.valueArguments.entries) {
if (resolvedValueArgument is DefaultValueArgument) {
parametersWithDefaultValuesCount++
}
else {
val keys = resolvedArgumentToKeys(resolvedValueArgument)
for (argumentKey in keys) {
// TODO fix 'original' for value parameters of Java generic descriptors.
// Should be able to use just 'valueParameterDescriptor' below.
// Doesn't work for Java generic descriptors. See also KT-10939.
argumentsToParameters[argumentKey] = unsubstitutedValueParameters[valueParameterDescriptor.index]
}
}
}
return CandidateCallWithArgumentMapping(call, argumentsToParameters, parametersWithDefaultValuesCount)
}
}
}
@@ -0,0 +1,75 @@
/*
* 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.results
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.singletonOrEmptyList
import java.util.*
class FlatSignature<out T>(
val origin: T,
val typeParameters: Collection<TypeParameterDescriptor>,
val valueParameterTypes: List<KotlinType?>,
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 {
fun <D : CallableDescriptor, RC : ResolvedCall<D>> createFromResolvedCall(call: RC): FlatSignature<RC> {
val originalDescriptor = call.candidateDescriptor.original
val originalValueParameters = originalDescriptor.valueParameters
val originalTypeParameters = originalDescriptor.typeParameters
var numDefaults = 0
val valueArgumentToParameterType = HashMap<ValueArgument, KotlinType>()
for ((valueParameter, resolvedValueArgument) in call.valueArguments.entries) {
if (resolvedValueArgument is DefaultValueArgument) {
numDefaults++
}
else {
val originalValueParameter = originalValueParameters[valueParameter.index]
val parameterType = originalValueParameter.flatType
for (valueArgument in resolvedValueArgument.arguments) {
if (valueArgument.getArgumentExpression() == null) continue
valueArgumentToParameterType[valueArgument] = parameterType
}
}
}
val extensionReceiverParameterType = originalDescriptor.extensionReceiverParameter?.type
val valueArgumentParameterTypes = call.call.valueArguments.map { valueArgumentToParameterType[it] }
val valueParameterTypes = extensionReceiverParameterType.singletonOrEmptyList() + valueArgumentParameterTypes
val hasVarargs = originalDescriptor.valueParameters.any { it.varargElementType != null }
return FlatSignature(call, originalTypeParameters, valueParameterTypes, hasVarargs, numDefaults)
}
val ValueParameterDescriptor.flatType: KotlinType
get() = varargElementType ?: type
}
}
@@ -24,7 +24,6 @@ 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.RECEIVER_POSITION
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
@@ -33,7 +32,6 @@ import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionMutableResolve
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.check
class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
@@ -84,23 +82,21 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
val conflictingCandidates = filteredCandidates.map {
candidateCall ->
CandidateCallWithArgumentMapping.create(candidateCall) { it.arguments.filter { it.getArgumentExpression() != null } }
FlatSignature.createFromResolvedCall(candidateCall)
}
val bestCandidatesByParameterTypes = conflictingCandidates.mapNotNull {
val bestCandidatesByParameterTypes = conflictingCandidates.filter {
candidate ->
candidate.check {
isMostSpecific(candidate, conflictingCandidates) {
call1, call2 ->
isNotLessSpecificCallWithArgumentMapping(call1, call2, discriminateGenerics)
}
isMostSpecific(candidate, conflictingCandidates) {
call1, call2 ->
isNotLessSpecificCallWithArgumentMapping(call1, call2, discriminateGenerics)
}
}
return bestCandidatesByParameterTypes.exactMaxWith {
call1, call2 ->
isOfNotLessSpecificShape(call1, call2) && isOfNotLessSpecificVisibilityForDebugger(call1, call2, isDebuggerContext)
}?.resolvedCall
}?.origin
}
private inline fun <C : Any> Collection<C>.exactMaxWith(isNotWorse: (C, C) -> Boolean): C? {
@@ -134,12 +130,12 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
/**
* `call1` is not less specific than `call2`
*/
private fun <D : CallableDescriptor, K> isNotLessSpecificCallWithArgumentMapping(
call1: CandidateCallWithArgumentMapping<D, K>,
call2: CandidateCallWithArgumentMapping<D, K>,
private fun <D : CallableDescriptor> isNotLessSpecificCallWithArgumentMapping(
call1: FlatSignature<MutableResolvedCall<D>>,
call2: FlatSignature<MutableResolvedCall<D>>,
discriminateGenerics: Boolean
): Boolean {
return tryCompareDescriptorsFromScripts(call1.candidateDescriptor, call2.candidateDescriptor) ?:
return tryCompareDescriptorsFromScripts(call1.candidateDescriptor(), call2.candidateDescriptor()) ?:
compareCallsByUsedArguments(call1, call2, discriminateGenerics)
}
@@ -147,9 +143,9 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
* Returns `true` if `d1` is definitely not less specific than `d2`,
* `false` otherwise.
*/
private fun <D : CallableDescriptor, K> compareCallsByUsedArguments(
call1: CandidateCallWithArgumentMapping<D, K>,
call2: CandidateCallWithArgumentMapping<D, K>,
private fun <D : CallableDescriptor> compareCallsByUsedArguments(
call1: FlatSignature<MutableResolvedCall<D>>,
call2: FlatSignature<MutableResolvedCall<D>>,
discriminateGenerics: Boolean
): Boolean {
if (discriminateGenerics) {
@@ -165,7 +161,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
val typeParameters = call2.typeParameters
val constraintSystemBuilder: ConstraintSystem.Builder = ConstraintSystemBuilderImpl()
var hasConstraints = false
val typeSubstitutor = constraintSystemBuilder.registerTypeVariables(call1.resolvedCall.call.toHandle(), typeParameters)
val typeSubstitutor = constraintSystemBuilder.registerTypeVariables(call1.callHandle(), typeParameters)
fun compareTypesAndUpdateConstraints(type1: KotlinType?, type2: KotlinType?, constraintPosition: ConstraintPosition): Boolean {
if (type1 == null || type2 == null) return true
@@ -186,23 +182,11 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
return true
}
val extensionReceiverType1 = call1.extensionReceiverType
val extensionReceiverType2 = call2.extensionReceiverType
if (!compareTypesAndUpdateConstraints(extensionReceiverType1, extensionReceiverType2, RECEIVER_POSITION.position())) {
return false
}
assert(call1.argumentsCount == call2.argumentsCount) {
"$call1 and $call2 have different number of explicit arguments"
}
for (argumentKey in call1.argumentKeys) {
val type1 = call1.getValueParameterType(argumentKey)
val type2 = call2.getValueParameterType(argumentKey)
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(0))) {
if (!compareTypesAndUpdateConstraints(type1, type2, VALUE_PARAMETER_POSITION.position(++argumentIndex))) {
return false
}
}
@@ -218,32 +202,29 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
return true
}
private fun <D: CallableDescriptor, K> isOfNotLessSpecificShape(
call1: CandidateCallWithArgumentMapping<D, K>,
call2: CandidateCallWithArgumentMapping<D, K>
private fun <D: CallableDescriptor> isOfNotLessSpecificShape(
call1: FlatSignature<MutableResolvedCall<D>>,
call2: FlatSignature<MutableResolvedCall<D>>
): Boolean {
val hasVarargs1 = call1.hasVarargs
val hasVarargs2 = call2.hasVarargs
if (hasVarargs1 && !hasVarargs2) return false
if (!hasVarargs1 && hasVarargs2) return true
if (call1.parametersWithDefaultValuesCount > call2.parametersWithDefaultValuesCount) {
if (call1.numDefaults > call2.numDefaults) {
return false
}
return true
}
private fun <D: CallableDescriptor, K> isOfNotLessSpecificVisibilityForDebugger(
call1: CandidateCallWithArgumentMapping<D, K>,
call2: CandidateCallWithArgumentMapping<D, K>,
private fun <D: CallableDescriptor> isOfNotLessSpecificVisibilityForDebugger(
call1: FlatSignature<MutableResolvedCall<D>>,
call2: FlatSignature<MutableResolvedCall<D>>,
isDebuggerContext: Boolean
): Boolean {
if (isDebuggerContext) {
val isMoreVisible1 = Visibilities.compare(
call1.resolvedCall.resultingDescriptor.visibility,
call2.resolvedCall.resultingDescriptor.visibility
)
val isMoreVisible1 = Visibilities.compare(call1.descriptorVisibility(), call2.descriptorVisibility())
if (isMoreVisible1 != null && isMoreVisible1 < 0) return false
}
@@ -268,24 +249,20 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
return null
}
/**
* Returns `false` if `d1` is definitely less specific than `d2`,
* `null` if undecided.
*/
private fun tryCompareExtensionReceiverType(type1: KotlinType?, type2: KotlinType?): Boolean? {
if (type1 != null && type2 != null) {
if (!typeNotLessSpecific(type1, type2))
return false
}
return null
}
/**
* Returns `true` if `d1` is definitely not less specific than `d2`,
* `false` if `d1` is definitely less specific than `d2`,
* `null` if undecided.
*/
private fun compareFunctionParameterTypes(f: CallableDescriptor, g: CallableDescriptor): Boolean {
val fReceiverType = f.extensionReceiverType
val gReceiverType = g.extensionReceiverType
if (fReceiverType != null && gReceiverType != null) {
if (!typeNotLessSpecific(fReceiverType, gReceiverType)) {
return false
}
}
val fParams = f.valueParameters
val gParams = g.valueParameters
@@ -319,8 +296,6 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
private fun isNotLessSpecificCallableReference(f: CallableDescriptor, g: CallableDescriptor): Boolean =
// TODO should we "discriminate generic descriptors" for callable references?
tryCompareDescriptorsFromScripts(f, g) ?:
// TODO compare functional types for 'f' and 'g'
tryCompareExtensionReceiverType(f.extensionReceiverType, g.extensionReceiverType) ?:
compareFunctionParameterTypes(f, g)
private fun getVarargElementTypeOrType(parameterDescriptor: ValueParameterDescriptor): KotlinType =
@@ -398,5 +373,13 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
ResolvedCallHashingStrategy as TObjectHashingStrategy<C>
}
}
internal fun <D : CallableDescriptor> FlatSignature<ResolvedCall<D>>.candidateDescriptor() =
origin.candidateDescriptor.original
internal fun <D : CallableDescriptor> FlatSignature<ResolvedCall<D>>.callHandle() =
origin.call.toHandle()
internal fun <D : CallableDescriptor> FlatSignature<ResolvedCall<D>>.descriptorVisibility() =
candidateDescriptor().visibility