From 8a2b80fe3fd3c7b07b326fe2a0f2e5c6b9cd3af7 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 17 Feb 2016 18:10:53 +0300 Subject: [PATCH] Generalize signature comparison: checking for conflicting overloads. Also, vararg parameter no longer conflicts with Array (CONFLICTING_JVM_DECLARATIONS is reported, can be avoided using @JvmName). --- .../jetbrains/kotlin/resolve/OverloadUtil.kt | 68 ++++++------------- .../resolve/calls/results/FlatSignature.kt | 29 ++++---- .../calls/results/FlatSignatureSpecificity.kt | 51 +++++++------- .../results/OverloadingConflictResolver.kt | 67 +++++++----------- .../tests/duplicateJvmSignature/vararg.kt | 9 ++- .../tests/duplicateJvmSignature/vararg.txt | 2 + .../jetbrains/kotlin/types/ErrorUtils.java | 9 +++ 7 files changed, 102 insertions(+), 133 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadUtil.kt index ab4c9295471..7c5e2c53934 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadUtil.kt @@ -21,15 +21,14 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.INCOMPATIBLE -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.ConstraintPositionKind.VALUE_PARAMETER_POSITION +import org.jetbrains.kotlin.resolve.calls.results.FlatSignature +import org.jetbrains.kotlin.resolve.calls.results.SpecificityComparisonCallbacks +import org.jetbrains.kotlin.resolve.calls.results.isSignatureNotLessSpecific +import org.jetbrains.kotlin.resolve.calls.results.varargParameterPosition import org.jetbrains.kotlin.resolve.descriptorUtil.hasLowPriorityInOverloadResolution import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.singletonOrEmptyList object OverloadUtil { @@ -46,52 +45,29 @@ object OverloadUtil { return checkOverloadability(a, b) } + private object OverloadabilitySpecificityCallbacks : SpecificityComparisonCallbacks { + override fun isNonSubtypeNotLessSpecific(specific: KotlinType, general: KotlinType): Boolean = + false + } + private fun checkOverloadability(a: CallableDescriptor, b: CallableDescriptor): Boolean { if (a.hasLowPriorityInOverloadResolution() != b.hasLowPriorityInOverloadResolution()) return true + + // NB this makes generic and non-generic declarations with equivalent signatures non-conflicting + // E.g., 'fun foo()' and 'fun foo()'. + // They can be disambiguated by providing explicit type parameters. if (a.typeParameters.isEmpty() != b.typeParameters.isEmpty()) return true - OverridingUtil.checkReceiverAndParameterCount(a, b)?.let { return it.result == INCOMPATIBLE } + if (ErrorUtils.containsErrorType(a) || ErrorUtils.containsErrorType(b)) return true + if (a.varargParameterPosition() != b.varargParameterPosition()) return true - val aValueParameters = OverridingUtil.compiledValueParameters(a) - val bValueParameters = OverridingUtil.compiledValueParameters(b) + val aSignature = FlatSignature.createFromCallableDescriptor(a) + val bSignature = FlatSignature.createFromCallableDescriptor(b) - val aTypeParameters = a.typeParameters - val bTypeParameters = b.typeParameters + val aIsNotLessSpecificThanB = isSignatureNotLessSpecific(aSignature, bSignature, OverloadabilitySpecificityCallbacks) + val bIsNotLessSpecificThanA = isSignatureNotLessSpecific(bSignature, aSignature, OverloadabilitySpecificityCallbacks) - val avsbConstraintsBuilder: ConstraintSystem.Builder = ConstraintSystemBuilderImpl() - val avsbTypeSubstitutor = avsbConstraintsBuilder.registerTypeVariables(CallHandle.NONE, aTypeParameters) - val bvsaConstraintsBuilder: ConstraintSystem.Builder = ConstraintSystemBuilderImpl() - val bvsaTypeSubstitutor = bvsaConstraintsBuilder.registerTypeVariables(CallHandle.NONE, bTypeParameters) - - var constraintIndex = 0 - - for ((aType, bType) in aValueParameters.zip(bValueParameters)) { - if (aType.isError || bType.isError) return true - - if (oneMoreSpecificThanAnother(bType, aType)) return true - - if (!TypeUtils.dependsOnTypeParameters(aType, aTypeParameters) - && !TypeUtils.dependsOnTypeParameters(bType, bTypeParameters) - && !KotlinTypeChecker.DEFAULT.equalTypes(aType, bType)) { - return true - } - - constraintIndex++ - val aTypeSubstituted = avsbTypeSubstitutor.safeSubstitute(aType, Variance.INVARIANT) - val bTypeSubstituted = bvsaTypeSubstitutor.safeSubstitute(bType, Variance.INVARIANT) - avsbConstraintsBuilder.addSubtypeConstraint(bType, aTypeSubstituted, - VALUE_PARAMETER_POSITION.position(constraintIndex)) - bvsaConstraintsBuilder.addSubtypeConstraint(aType, bTypeSubstituted, - VALUE_PARAMETER_POSITION.position(constraintIndex)) - } - - if (constraintIndex == 0) return false - - avsbConstraintsBuilder.fixVariables() - bvsaConstraintsBuilder.fixVariables() - - return avsbConstraintsBuilder.build().status.hasContradiction() - || bvsaConstraintsBuilder.build().status.hasContradiction() + return !(aIsNotLessSpecificThanB && bIsNotLessSpecificThanA) } private enum class DeclarationCategory { 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 f707d85b396..0aadc2647f0 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 @@ -30,49 +30,50 @@ class FlatSignature( val origin: T, val typeParameters: Collection, val valueParameterTypes: List, + val hasExtensionReceiver: Boolean, val hasVarargs: Boolean, val numDefaults: Int ) { val isGeneric = typeParameters.isNotEmpty() companion object { - fun > createFromResolvedCall(call: RC): FlatSignature { - val originalDescriptor = call.candidateDescriptor.original + fun > createFromResolvedCall(resolvedCall: RC): FlatSignature { + val originalDescriptor = resolvedCall.candidateDescriptor.original val originalValueParameters = originalDescriptor.valueParameters - val originalTypeParameters = originalDescriptor.typeParameters var numDefaults = 0 val valueArgumentToParameterType = HashMap() - for ((valueParameter, resolvedValueArgument) in call.valueArguments.entries) { + for ((valueParameter, resolvedValueArgument) in resolvedCall.valueArguments.entries) { if (resolvedValueArgument is DefaultValueArgument) { numDefaults++ } else { val originalValueParameter = originalValueParameters[valueParameter.index] - val parameterType = originalValueParameter.flatType + val parameterType = originalValueParameter.argumentValueType for (valueArgument in resolvedValueArgument.arguments) { - if (valueArgument.getArgumentExpression() == null) continue valueArgumentToParameterType[valueArgument] = parameterType } } } - val valueArgumentParameterTypes = call.call.valueArguments.map { valueArgumentToParameterType[it] } - val valueParameterTypes = originalDescriptor.extensionReceiverTypeOrEmpty() + valueArgumentParameterTypes - - val hasVarargs = originalDescriptor.valueParameters.any { it.varargElementType != null } - - return FlatSignature(call, originalTypeParameters, valueParameterTypes, hasVarargs, numDefaults) + return FlatSignature(resolvedCall, + originalDescriptor.typeParameters, + valueParameterTypes = originalDescriptor.extensionReceiverTypeOrEmpty() + + resolvedCall.call.valueArguments.map { valueArgumentToParameterType[it] }, + hasExtensionReceiver = originalDescriptor.extensionReceiverParameter != null, + hasVarargs = originalDescriptor.valueParameters.any { it.varargElementType != null }, + numDefaults = numDefaults) } fun createFromCallableDescriptor(descriptor: D): FlatSignature = FlatSignature(descriptor, descriptor.typeParameters, - valueParameterTypes = descriptor.extensionReceiverTypeOrEmpty() + descriptor.valueParameters.map { it.flatType }, + valueParameterTypes = descriptor.extensionReceiverTypeOrEmpty() + descriptor.valueParameters.map { it.argumentValueType }, + hasExtensionReceiver = descriptor.extensionReceiverParameter != null, hasVarargs = descriptor.valueParameters.any { it.varargElementType != null }, numDefaults = 0) - val ValueParameterDescriptor.flatType: KotlinType + val ValueParameterDescriptor.argumentValueType: KotlinType get() = varargElementType ?: type fun CallableDescriptor.extensionReceiverTypeOrEmpty() = 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 index 4e95314037a..06cba25d2c4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureSpecificity.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignatureSpecificity.kt @@ -23,52 +23,47 @@ import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.valuePara import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -interface SpecificityComparisonCallbacks { - fun isNotLessSpecificSignature(signature1: FlatSignature, signature2: FlatSignature): Boolean? - fun isTypeNotLessSpecific(type1: KotlinType, type2: KotlinType): Boolean? +interface SpecificityComparisonCallbacks { + fun isNonSubtypeNotLessSpecific(specific: KotlinType, general: KotlinType): Boolean } fun isSignatureNotLessSpecific( - signature1: FlatSignature, - signature2: FlatSignature, - callHandle: CallHandle, - callbacks: SpecificityComparisonCallbacks + specific: FlatSignature, + general: FlatSignature, + callbacks: SpecificityComparisonCallbacks, + callHandle: CallHandle = CallHandle.NONE ): Boolean { - callbacks.isNotLessSpecificSignature(signature1, signature2)?.let { return it } + if (specific.hasExtensionReceiver != general.hasExtensionReceiver) return false + if (specific.valueParameterTypes.size != general.valueParameterTypes.size) return false - val typeParameters = signature2.typeParameters + val typeParameters = general.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 + var numConstraints = 0 + for ((specificType, generalType) in specific.valueParameterTypes.zip(general.valueParameterTypes)) { + if (specificType == null || generalType == null) continue - if (isDefinitelyLessSpecificByTypeSpecificity(type1, type2)) { + if (isDefinitelyLessSpecificByTypeSpecificity(specificType, generalType)) { return false } - if (typeParameters.isEmpty() || !TypeUtils.dependsOnTypeParameters(type2, typeParameters)) { - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(type1, type2)) { - callbacks.isTypeNotLessSpecific(type1, type2)?.let { if (!it) return false } + if (typeParameters.isEmpty() || !TypeUtils.dependsOnTypeParameters(generalType, typeParameters)) { + if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(specificType, generalType)) { + if (!callbacks.isNonSubtypeNotLessSpecific(specificType, generalType)) { + return false + } } } else { - val constraintPosition = valueParameterPosition(numConstraints++) - val substitutedType2 = typeSubstitutor.safeSubstitute(type2, Variance.INVARIANT) - constraintSystemBuilder.addSubtypeConstraint(type1, substitutedType2, constraintPosition) + val substitutedGeneralType = typeSubstitutor.safeSubstitute(generalType, Variance.INVARIANT) + constraintSystemBuilder.addSubtypeConstraint(specificType, substitutedGeneralType, valueParameterPosition(numConstraints++)) } } - if (numConstraints > 0) { - constraintSystemBuilder.fixVariables() - val constraintSystem = constraintSystemBuilder.build() - if (constraintSystem.status.hasContradiction()) { - return false - } - } - - return true + constraintSystemBuilder.fixVariables() + val constraintSystem = constraintSystemBuilder.build() + return !constraintSystem.status.hasContradiction() } private fun isDefinitelyLessSpecificByTypeSpecificity(specific: KotlinType, general: KotlinType): Boolean { 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 ce8a8fa890c..aacf49a2a85 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.descriptors.ScriptDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode -import org.jetbrains.kotlin.resolve.calls.inference.CallHandle import org.jetbrains.kotlin.resolve.calls.inference.toHandle import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall @@ -147,18 +146,22 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { call1: FlatSignature>, call2: FlatSignature>, discriminateGenerics: Boolean - ): Boolean = - isSignatureNotLessSpecific(call1, call2, call1.callHandle(), - if (discriminateGenerics) - SpecificityComparisonDiscriminatingGenerics - else - DefaultCallSpecificityComparison) + ): Boolean { + if (discriminateGenerics) { + val isGeneric1 = call1.isGeneric + val isGeneric2 = call2.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 + } - private abstract inner class SpecificityComparisonWithNumerics : SpecificityComparisonCallbacks { - override fun isNotLessSpecificSignature(signature1: FlatSignature, signature2: FlatSignature): Boolean? - = null + return isSignatureNotLessSpecific(call1, call2, SpecificityComparisonWithNumerics, call1.callHandle()) + } - override fun isTypeNotLessSpecific(type1: KotlinType, type2: KotlinType): Boolean? { + private val SpecificityComparisonWithNumerics = object : SpecificityComparisonCallbacks { + override fun isNonSubtypeNotLessSpecific(specific: KotlinType, general: KotlinType): Boolean { val _double = builtIns.doubleType val _float = builtIns.floatType val _long = builtIns.longType @@ -167,41 +170,21 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { val _short = builtIns.shortType when { - TypeUtils.equalTypes(type1, _double) && TypeUtils.equalTypes(type2, _float) -> return true - TypeUtils.equalTypes(type1, _int) -> { + TypeUtils.equalTypes(specific, _double) && TypeUtils.equalTypes(general, _float) -> return true + TypeUtils.equalTypes(specific, _int) -> { when { - TypeUtils.equalTypes(type2, _long) -> return true - TypeUtils.equalTypes(type2, _byte) -> return true - TypeUtils.equalTypes(type2, _short) -> return true + TypeUtils.equalTypes(general, _long) -> return true + TypeUtils.equalTypes(general, _byte) -> return true + TypeUtils.equalTypes(general, _short) -> return true } } - TypeUtils.equalTypes(type1, _short) && TypeUtils.equalTypes(type2, _byte) -> return true + TypeUtils.equalTypes(specific, _short) && TypeUtils.equalTypes(general, _byte) -> return true } return false } } - private val DefaultCallSpecificityComparison = object : SpecificityComparisonWithNumerics>() {} - private val DefaultDescriptorSpecificityComparison = object : SpecificityComparisonWithNumerics() {} - - private val SpecificityComparisonDiscriminatingGenerics = object : SpecificityComparisonWithNumerics>() { - override fun isNotLessSpecificSignature( - 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 - } - } - private fun isOfNotLessSpecificShape( call1: FlatSignature>, call2: FlatSignature> @@ -250,23 +233,23 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { } /** - * Returns `true` if `d1` is definitely not less specific than `d2`, - * `false` if `d1` is definitely less specific than `d2`, + * Returns `true` if `f` is definitely not less specific than `g`, + * `false` if `f` is definitely less specific than `g`, * `null` if undecided. */ - private fun compareFunctionParameterTypes(f: CallableDescriptor, g: CallableDescriptor): Boolean { + private fun isNotLessSpecificCallableReferenceDescriptor(f: CallableDescriptor, g: CallableDescriptor): Boolean { if (f.valueParameters.size != g.valueParameters.size) return false if (f.varargParameterPosition() != g.varargParameterPosition()) return false val fSignature = FlatSignature.createFromCallableDescriptor(f) val gSignature = FlatSignature.createFromCallableDescriptor(g) - return isSignatureNotLessSpecific(fSignature, gSignature, CallHandle.NONE, DefaultDescriptorSpecificityComparison) + return isSignatureNotLessSpecific(fSignature, gSignature, SpecificityComparisonWithNumerics) } private fun isNotLessSpecificCallableReference(f: CallableDescriptor, g: CallableDescriptor): Boolean = // TODO should we "discriminate generic descriptors" for callable references? tryCompareDescriptorsFromScripts(f, g) ?: - compareFunctionParameterTypes(f, g) + isNotLessSpecificCallableReferenceDescriptor(f, g) companion object { // Different smartcasts may lead to the same candidate descriptor wrapped into different ResolvedCallImpl objects diff --git a/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.kt b/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.kt index 96d510905ca..a0f9b68c76a 100644 --- a/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.kt +++ b/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.kt @@ -1,5 +1,8 @@ -fun foo(vararg x: Int) {} -fun foo(x: IntArray) {} +fun foo(vararg x: Int) {} +fun foo(x: IntArray) {} fun foo(vararg x: Int?) {} -fun foo(x: Array) {} \ No newline at end of file +fun foo(x: Array) {} + +fun foo(vararg nn: Number) {} +fun foo(nn: Array) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.txt b/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.txt index c9d2a02a323..ba4b302ad5f 100644 --- a/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.txt +++ b/compiler/testData/diagnostics/tests/duplicateJvmSignature/vararg.txt @@ -2,5 +2,7 @@ package public fun foo(/*0*/ x: kotlin.Array): kotlin.Unit public fun foo(/*0*/ vararg x: kotlin.Int? /*kotlin.Array*/): kotlin.Unit +public fun foo(/*0*/ nn: kotlin.Array): kotlin.Unit +public fun foo(/*0*/ vararg nn: kotlin.Number /*kotlin.Array*/): kotlin.Unit public fun foo(/*0*/ vararg x: kotlin.Int /*kotlin.IntArray*/): kotlin.Unit public fun foo(/*0*/ x: kotlin.IntArray): kotlin.Unit diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java index 1475e98f7b5..d447fb84282 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java @@ -136,6 +136,15 @@ public class ErrorUtils { }; } + public static boolean containsErrorType(@NotNull CallableDescriptor callableDescriptor) { + if (callableDescriptor instanceof FunctionDescriptor) { + return containsErrorType((FunctionDescriptor) callableDescriptor); + } + else { + return containsErrorType(callableDescriptor.getReturnType()); + } + } + public static boolean containsErrorType(@NotNull FunctionDescriptor function) { if (containsErrorType(function.getReturnType())) { return true;