From 022162322dcc36905ab040a65f84a22f7426aa11 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 17 Feb 2016 13:44:21 +0300 Subject: [PATCH] Introduce "flat signature" - a generalized abstraction for a (call) signatures that can be compared for specificity. --- .../CandidateCallWithArgumentMapping.kt | 92 ---------------- .../resolve/calls/results/FlatSignature.kt | 75 +++++++++++++ .../results/OverloadingConflictResolver.kt | 101 ++++++++---------- 3 files changed, 117 insertions(+), 151 deletions(-) delete mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/CandidateCallWithArgumentMapping.kt create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/CandidateCallWithArgumentMapping.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/CandidateCallWithArgumentMapping.kt deleted file mode 100644 index e1a88596449..00000000000 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/CandidateCallWithArgumentMapping.kt +++ /dev/null @@ -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 private constructor( - val resolvedCall: MutableResolvedCall, - private val argumentsToParameters: Map, - 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 - get() = candidateDescriptor.original.typeParameters - - val argumentsCount: Int - get() = argumentsToParameters.size - - val argumentKeys: Collection - 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 create( - call: MutableResolvedCall, - resolvedArgumentToKeys: (ResolvedValueArgument) -> Collection - ): CandidateCallWithArgumentMapping { - val argumentsToParameters = hashMapOf() - 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) - } - } -} - 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 new file mode 100644 index 00000000000..6a89fc76bac --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt @@ -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( + val origin: T, + val typeParameters: Collection, + val valueParameterTypes: List, + 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 > createFromResolvedCall(call: RC): FlatSignature { + val originalDescriptor = call.candidateDescriptor.original + val originalValueParameters = originalDescriptor.valueParameters + val originalTypeParameters = originalDescriptor.typeParameters + + var numDefaults = 0 + val valueArgumentToParameterType = HashMap() + 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 + } +} 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 1a45ceee78c..a0fcdaa0ffb 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 @@ -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 Collection.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 isNotLessSpecificCallWithArgumentMapping( - call1: CandidateCallWithArgumentMapping, - call2: CandidateCallWithArgumentMapping, + private fun isNotLessSpecificCallWithArgumentMapping( + call1: FlatSignature>, + call2: FlatSignature>, 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 compareCallsByUsedArguments( - call1: CandidateCallWithArgumentMapping, - call2: CandidateCallWithArgumentMapping, + private fun compareCallsByUsedArguments( + call1: FlatSignature>, + call2: FlatSignature>, 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 isOfNotLessSpecificShape( - call1: CandidateCallWithArgumentMapping, - call2: CandidateCallWithArgumentMapping + private fun isOfNotLessSpecificShape( + call1: FlatSignature>, + call2: FlatSignature> ): 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 isOfNotLessSpecificVisibilityForDebugger( - call1: CandidateCallWithArgumentMapping, - call2: CandidateCallWithArgumentMapping, + private fun isOfNotLessSpecificVisibilityForDebugger( + call1: FlatSignature>, + call2: FlatSignature>, 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 } - } + +internal fun FlatSignature>.candidateDescriptor() = + origin.candidateDescriptor.original + +internal fun FlatSignature>.callHandle() = + origin.call.toHandle() + +internal fun FlatSignature>.descriptorVisibility() = + candidateDescriptor().visibility