From ee16a7961297d507df6de17abec6153b07cd823f Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Wed, 10 May 2017 12:30:18 +0300 Subject: [PATCH] [NI] Add pre-resolution callable reference argument check. If expected type for callable reference argument isn't callable type then make such candidate unsuccessful. Sometimes expected type is just `T`, where `T` is type variable. To support such case we take all supertypes and check them instead. --- .../CheckArgumentsResolutionPart.kt | 12 +++++++++- .../calls/components/ResolutionParts.kt | 22 +++++++++++++------ .../inference/ConstraintSystemBuilder.kt | 3 +++ .../model/NewConstraintSystemImpl.kt | 10 +++++++++ .../kotlin/builtins/ReflectionTypes.kt | 21 ++++++++++++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt index 12b277b3348..abba36e84fb 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.resolve.calls.components +import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isFunctionType @@ -77,7 +78,7 @@ internal object CheckArguments : ResolutionPart { else { // callable reference resolution will be run after choosing single descriptor postponeCallableReferenceArguments.add(PostponeCallableReferenceArgument(argument, expectedType)) - null + checkCallableExpectedType(csBuilder, argument, expectedType) } } else -> error("Incorrect argument type: $argument, ${argument.javaClass.canonicalName}.") @@ -163,6 +164,15 @@ internal object CheckArguments : ResolutionPart { return null } + fun checkCallableExpectedType( + csBuilder: ConstraintSystemBuilder, + argument: CallableReferenceKotlinCallArgument, + expectedType: UnwrappedType + ): KotlinCallDiagnostic? { + val notCallableTypeConstructor = csBuilder.getProperSuperTypeConstructors(expectedType).firstOrNull { !ReflectionTypes.isPossibleExpectedCallableType(it) } + return notCallableTypeConstructor?.let { NotCallableExpectedType(argument, expectedType, notCallableTypeConstructor) } + } + fun processCallableReferenceArgument( callContext: KotlinCallContext, kotlinCall: KotlinCall, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index a03f59c76b5..9ac47cc51dc 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -28,9 +28,9 @@ import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability -import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE -import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.RUNTIME_ERROR +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.* import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError +import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.Variance @@ -250,25 +250,33 @@ class ExpectedLambdaParametersCountMismatch( val lambdaArgument: LambdaKotlinCallArgument, val expected: Int, val actual: Int -) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { +) : KotlinCallDiagnostic(INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(lambdaArgument, this) } -class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { +class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this) } -class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { +class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this) } -class NoneCallableReferenceCandidates(val argument: CallableReferenceKotlinCallArgument) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { +class NoneCallableReferenceCandidates(val argument: CallableReferenceKotlinCallArgument) : KotlinCallDiagnostic(INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) } class CallableReferenceCandidatesAmbiguity( val argument: CallableReferenceKotlinCallArgument, val candidates: Collection -) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { +) : KotlinCallDiagnostic(INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) } + +class NotCallableExpectedType( + val argument: CallableReferenceKotlinCallArgument, + val expectedType: UnwrappedType, + val notCallableTypeConstructor: TypeConstructor +) : KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt index ed58e66717b..f49af82457b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallableReferenceArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument +import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.UnwrappedType interface ConstraintSystemOperation { @@ -32,6 +33,8 @@ interface ConstraintSystemOperation { fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) fun isProperType(type: UnwrappedType): Boolean + + fun getProperSuperTypeConstructors(type: UnwrappedType): List } interface ConstraintSystemBuilder : ConstraintSystemOperation { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index ff7385380ce..3719bfa271b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -90,6 +90,16 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re override fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) = constraintInjector.addInitialEqualityConstraint(apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }, a, b, position) + override fun getProperSuperTypeConstructors(type: UnwrappedType): List { + checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) + val variableWithConstraints = notFixedTypeVariables[type.constructor] ?: return listOf(type.constructor) + + return variableWithConstraints.constraints.mapNotNull { + if (it.kind == ConstraintKind.LOWER) return@mapNotNull null + it.type.constructor.takeUnless { allTypeVariables.containsKey(it) } + } + } + // ConstraintSystemBuilder private fun transactionRegisterVariable(variable: NewTypeVariable) { if (state != State.TRANSACTION) return diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt index c4f9741efff..c32021678fe 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt @@ -149,5 +149,26 @@ class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: Not return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kPropertyClass, listOf(StarProjectionImpl(kPropertyClass.typeConstructor.parameters.single()))) } + + fun isPossibleExpectedCallableType(typeConstructor: TypeConstructor): Boolean { + val descriptor = typeConstructor.declarationDescriptor as? ClassDescriptor ?: return false + if (KotlinBuiltIns.isAny(descriptor)) return true + + val shortName = descriptor.name.asString() + + val packageName = DescriptorUtils.getFqName(descriptor).parent().toSafe() + if (packageName == KOTLIN_REFLECT_FQ_NAME) { + return shortName.startsWith("KFunction") // KFunctionN, KFunction + || shortName.startsWith("KProperty") // KPropertyN, KProperty + || shortName.startsWith("KMutableProperty") // KMutablePropertyN, KMutableProperty + || shortName == "KCallable" || shortName == "KAnnotatedElement" + + } + if (packageName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) { + return shortName.startsWith("Function") // FunctionN, Function + } + + return false + } } }