From 3450340d7f2d37a7dba96507711e45ab63e34c5a Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Fri, 23 Jun 2017 05:09:50 +0300 Subject: [PATCH] [NI] Refactoring. Introduced PostponableCallArgument Also here argument resolution was divided to two parts: for SimpleCallArguments and for PostponableCallArguments. Call Resolution for SimpleCallArguments also used for CheckReceivers and lambda result arguments checks --- .../calls/components/ArgumentsUtils.kt | 51 +++ .../CheckArgumentsResolutionPart.kt | 333 ------------------ .../calls/components/KotlinCallCompleter.kt | 9 +- .../components/PostponeArgumentsChecks.kt | 159 +++++++++ .../calls/components/ResolutionParts.kt | 54 ++- .../calls/components/SimpleArgumentsChecks.kt | 162 +++++++++ .../calls/model/KotlinCallArguments.kt | 8 +- .../calls/model/ResolvedCallElements.kt | 2 +- 8 files changed, 406 insertions(+), 372 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt delete mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt new file mode 100644 index 00000000000..c2d5137ad59 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsUtils.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2017 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.components + +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.intersectWrappedTypes + + +internal fun unexpectedArgument(argument: KotlinCallArgument): Nothing = + error("Unexpected argument type: $argument, ${argument.javaClass.canonicalName}.") + +// if expression is not stable and has smart casts, then we create this type +internal val ReceiverValueWithSmartCastInfo.unstableType: UnwrappedType? + get() { + if (isStable || possibleTypes.isEmpty()) return null + return intersectWrappedTypes(possibleTypes + receiverValue.type) + } + +// with all smart casts if stable +internal val ReceiverValueWithSmartCastInfo.stableType: UnwrappedType + get() { + if (!isStable || possibleTypes.isEmpty()) return receiverValue.type.unwrap() + return intersectWrappedTypes(possibleTypes + receiverValue.type) + } + + + +internal fun KotlinCallArgument.getExpectedType(parameter: ValueParameterDescriptor) = + if (this.isSpread) { + parameter.type.unwrap() + } + else { + parameter.varargElementType?.unwrap() ?: parameter.type.unwrap() + } \ No newline at end of file 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 deleted file mode 100644 index ca0d43e69a5..00000000000 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2010-2017 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.components - -import org.jetbrains.kotlin.builtins.* -import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible -import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.model.ReceiverConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType -import org.jetbrains.kotlin.resolve.calls.model.* -import org.jetbrains.kotlin.resolve.calls.tower.isSuccess -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo -import org.jetbrains.kotlin.types.UnwrappedType -import org.jetbrains.kotlin.types.checker.captureFromExpression -import org.jetbrains.kotlin.types.checker.hasSupertypeWithGivenTypeConstructor -import org.jetbrains.kotlin.types.checker.intersectWrappedTypes -import org.jetbrains.kotlin.types.lowerIfFlexible -import org.jetbrains.kotlin.types.typeUtil.builtIns -import org.jetbrains.kotlin.types.typeUtil.supertypes -import org.jetbrains.kotlin.types.upperIfFlexible -import org.jetbrains.kotlin.utils.SmartList -import org.jetbrains.kotlin.utils.addIfNotNull - -internal object CheckArguments : ResolutionPart { - override fun SimpleKotlinResolutionCandidate.process(): List { - val diagnostics = SmartList() - for (parameterDescriptor in descriptorWithFreshTypes.valueParameters) { - // error was reported in ArgumentsToParametersMapper - val resolvedCallArgument = argumentMappingByOriginal[parameterDescriptor.original] ?: continue - for (argument in resolvedCallArgument.arguments) { - - val diagnostic = checkArgument(callContext, kotlinCall, csBuilder, argument, argument.getExpectedType(parameterDescriptor), - postponeCallableReferenceArguments) - diagnostics.addIfNotNull(diagnostic) - - if (diagnostic != null && !diagnostic.candidateApplicability.isSuccess) break - } - } - return diagnostics - } - - fun checkArgument( - callContext: KotlinCallContext, - kotlinCall: KotlinCall, - csBuilder: ConstraintSystemBuilder, - argument: KotlinCallArgument, - expectedType: UnwrappedType, - postponeCallableReferenceArguments: MutableList? = null - ): KotlinCallDiagnostic? { - return when (argument) { - is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, argument, expectedType, isReceiver = false) - is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, argument, expectedType, isReceiver = false) - is LambdaKotlinCallArgument -> processLambdaArgument(kotlinCall, csBuilder, argument, expectedType) - is CallableReferenceKotlinCallArgument -> { - if (postponeCallableReferenceArguments == null) { - processCallableReferenceArgument(callContext, kotlinCall, csBuilder, argument, expectedType) - } - else { - // callable reference resolution will be run after choosing single descriptor - postponeCallableReferenceArguments.add(PostponeCallableReferenceArgument(argument, expectedType)) - checkCallableExpectedType(csBuilder, argument, expectedType) - } - } - is CollectionLiteralKotlinCallArgument -> processCollectionLiteralArgument(kotlinCall, csBuilder, argument, expectedType) - else -> error("Incorrect argument type: $argument, ${argument.javaClass.canonicalName}.") - } - } - - // if expected type isn't function type, then may be it is Function, Any or just `T` - private fun processLambdaArgument( - kotlinCall: KotlinCall, - csBuilder: ConstraintSystemBuilder, - argument: LambdaKotlinCallArgument, - expectedType: UnwrappedType - ): KotlinCallDiagnostic? { - val builtIns = expectedType.builtIns - val isSuspend = expectedType.isSuspendFunctionType - - val receiverType: UnwrappedType? // null means that there is no receiver - val parameters: List - val returnType: UnwrappedType - - if (expectedType.isBuiltinFunctionalType) { - receiverType = if (argument is FunctionExpression) argument.receiverType else expectedType.getReceiverTypeFromFunctionType()?.unwrap() - - val expectedParameters = expectedType.getValueParameterTypesFromFunctionType() - if (argument.parametersTypes != null) { - parameters = argument.parametersTypes!!.mapIndexed { - index, type -> - type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: builtIns.anyType - } - } - else { - // lambda without explicit parameters: { } - parameters = expectedParameters.map { it.type.unwrap() } - } - returnType = (argument as? FunctionExpression)?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap() - } - else { - val isFunctionSupertype = KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType) - receiverType = (argument as? FunctionExpression)?.receiverType - parameters = argument.parametersTypes?.map { it ?: builtIns.nothingType } ?: emptyList() - returnType = (argument as? FunctionExpression)?.returnType ?: - expectedType.arguments.singleOrNull()?.type?.unwrap()?.takeIf { isFunctionSupertype } ?: - createFreshTypeVariableForLambdaReturnType(csBuilder, argument, builtIns) - - // what about case where expected type is type variable? In old TY such cases was not supported. => do nothing for now. todo design - } - - val resolvedArgument = ResolvedLambdaArgument(kotlinCall, argument, isSuspend, receiverType, parameters, returnType) - - csBuilder.addSubtypeConstraint(resolvedArgument.type, expectedType, ArgumentConstraintPosition(argument)) - csBuilder.addLambdaArgument(resolvedArgument) - - return null - } - - private fun createFreshTypeVariableForLambdaReturnType( - csBuilder: ConstraintSystemBuilder, - argument: LambdaKotlinCallArgument, - builtIns: KotlinBuiltIns - ): UnwrappedType { - val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L") - csBuilder.registerVariable(typeVariable) - return typeVariable.defaultType - } - - private 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, - csBuilder: ConstraintSystemBuilder, - argument: CallableReferenceKotlinCallArgument, - expectedType: UnwrappedType - ): KotlinCallDiagnostic? { - val subLHSCall = ((argument.lhsResult as? LHSResult.Expression)?.lshCallArgument as? SubKotlinCallArgument) - if (subLHSCall != null) { - csBuilder.addInnerCall(subLHSCall.resolvedCall) - } - val candidates = callContext.callableReferenceResolver.runRLSResolution(callContext, argument, expectedType) { checkCallableReference -> - csBuilder.runTransaction { checkCallableReference(this); false } - } - val chosenCandidate = when (candidates.size) { - 0 -> return NoneCallableReferenceCandidates(argument) - 1 -> candidates.single() - else -> return CallableReferenceCandidatesAmbiguity(argument, candidates) - } - val (toFreshSubstitutor, diagnostic) = with(chosenCandidate) { - csBuilder.checkCallableReference(argument, dispatchReceiver, extensionReceiver, candidate, - reflectionCandidateType, expectedType, callContext.scopeTower.lexicalScope.ownerDescriptor) - } - - val resolvedCallableReference = ResolvedCallableReferenceArgument( - kotlinCall, argument, toFreshSubstitutor.freshVariables, - chosenCandidate, toFreshSubstitutor.safeSubstitute(chosenCandidate.reflectionCandidateType)) - - csBuilder.addCallableReferenceArgument(resolvedCallableReference) - - return diagnostic - } - - fun processCollectionLiteralArgument( - kotlinCall: KotlinCall, - csBuilder: ConstraintSystemBuilder, - collectionLiteralArgument: CollectionLiteralKotlinCallArgument, - expectedType: UnwrappedType - ): KotlinCallDiagnostic? { - csBuilder.addCollectionLiteralArgument(ResolvedCollectionLiteralArgument(kotlinCall, collectionLiteralArgument, expectedType)) - return null - } -} - -internal fun checkExpressionArgument( - csBuilder: ConstraintSystemBuilder, - expressionArgument: ExpressionKotlinCallArgument, - expectedType: UnwrappedType, - isReceiver: Boolean -): KotlinCallDiagnostic? { - // todo run this approximation only once for call - val argumentType = captureFromTypeParameterUpperBoundIfNeeded(expressionArgument.receiver.stableType, expectedType) - - fun unstableSmartCastOrSubtypeError( - unstableType: UnwrappedType?, expectedType: UnwrappedType, position: ConstraintPosition - ): KotlinCallDiagnostic? { - if (unstableType != null) { - if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { - return UnstableSmartCast(expressionArgument, unstableType) - } - } - csBuilder.addSubtypeConstraint(argumentType, expectedType, position) - return null - } - - val expectedNullableType = expectedType.makeNullableAsSpecified(true) - val position = if (isReceiver) ReceiverConstraintPosition(expressionArgument) else ArgumentConstraintPosition(expressionArgument) - if (expressionArgument.isSafeCall) { - if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { - return unstableSmartCastOrSubtypeError(expressionArgument.receiver.unstableType, expectedNullableType, position)?.let { return it } - } - return null - } - - if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { - if (!isReceiver) { - return unstableSmartCastOrSubtypeError(expressionArgument.receiver.unstableType, expectedType, position)?.let { return it } - } - - val unstableType = expressionArgument.receiver.unstableType - if (unstableType != null && csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { - return UnstableSmartCast(expressionArgument, unstableType) - } - else if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { - return UnsafeCallError(expressionArgument) - } - else { - csBuilder.addSubtypeConstraint(argumentType, expectedType, position) - return null - } - } - - return null -} - -/** - * interface Inv - * fun bar(l: Inv): Y = ... - * - * fun > foo(x: X) { - * val xr = bar(x) - * } - * Here we try to capture from upper bound from type parameter. - * We replace type of `x` to `Inv`(we chose supertype which contains supertype with expectedTypeConstructor) and capture from this type. - * It is correct, because it is like this code: - * fun > foo(x: X) { - * val inv: Inv = x - * val xr = bar(inv) - * } - * - */ -private fun captureFromTypeParameterUpperBoundIfNeeded(argumentType: UnwrappedType, expectedType: UnwrappedType): UnwrappedType { - val expectedTypeConstructor = expectedType.upperIfFlexible().constructor - - if (argumentType.lowerIfFlexible().constructor.declarationDescriptor is TypeParameterDescriptor) { - val chosenSupertype = argumentType.lowerIfFlexible().supertypes().singleOrNull { - it.constructor.declarationDescriptor is ClassifierDescriptorWithTypeParameters && - it.unwrap().hasSupertypeWithGivenTypeConstructor(expectedTypeConstructor) - } - if (chosenSupertype != null) { - return captureFromExpression(chosenSupertype.unwrap()) ?: argumentType - } - } - - return argumentType -} - -// if expression is not stable and has smart casts, then we create this type -private val ReceiverValueWithSmartCastInfo.unstableType: UnwrappedType? - get() { - if (isStable || possibleTypes.isEmpty()) return null - return intersectWrappedTypes(possibleTypes + receiverValue.type) - } - -// with all smart casts if stable -internal val ReceiverValueWithSmartCastInfo.stableType: UnwrappedType - get() { - if (!isStable || possibleTypes.isEmpty()) return receiverValue.type.unwrap() - return intersectWrappedTypes(possibleTypes + receiverValue.type) - } - - -internal fun checkSubCallArgument( - csBuilder: ConstraintSystemBuilder, - subCallArgument: SubKotlinCallArgument, - expectedType: UnwrappedType, - isReceiver: Boolean -): KotlinCallDiagnostic? { - val resolvedCall = subCallArgument.resolvedCall - val expectedNullableType = expectedType.makeNullableAsSpecified(true) - val position = ArgumentConstraintPosition(subCallArgument) - - csBuilder.addInnerCall(resolvedCall) - - // subArgument cannot has stable smartcast - val currentReturnType = subCallArgument.receiver.receiverValue.type.unwrap() - if (subCallArgument.isSafeCall) { - csBuilder.addSubtypeConstraint(currentReturnType, expectedNullableType, position) - return null - } - - if (isReceiver && !csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedType, position) && - csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedNullableType, position) - ) { - return UnsafeCallError(subCallArgument) - } - - csBuilder.addSubtypeConstraint(currentReturnType, expectedType, position) - return null -} - -internal fun KotlinCallArgument.getExpectedType(parameter: ValueParameterDescriptor) = - if (this.isSpread) { - parameter.type.unwrap() - } - else { - parameter.varargElementType?.unwrap() ?: parameter.type.unwrap() - } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 4a6c0149f62..4505948042b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -104,7 +104,7 @@ class KotlinCallCompleter( private fun resolveCallableReferenceArguments(candidate: SimpleKotlinResolutionCandidate) { for (callableReferenceArgument in candidate.postponeCallableReferenceArguments) { - CheckArguments.processCallableReferenceArgument(candidate.callContext, candidate.kotlinCall, candidate.csBuilder, + processCallableReferenceArgument(candidate.callContext, candidate.kotlinCall, candidate.csBuilder, callableReferenceArgument.argument, callableReferenceArgument.expectedType) } candidate.postponeCallableReferenceArguments.clear() @@ -242,7 +242,7 @@ class KotlinCallCompleter( val lambda = c.lambdaArguments.find { canWeAnalyzeIt(c, it) } if (lambda != null) { - analyzeLambda(c, resolutionCallbacks, callContext, kotlinCall, lambda) + analyzeLambda(c, resolutionCallbacks, lambda) return false } @@ -262,7 +262,7 @@ class KotlinCallCompleter( return true } - private fun analyzeLambda(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, topLevelCallContext: KotlinCallContext, topLevelCall: KotlinCall, lambda: ResolvedLambdaArgument) { + private fun analyzeLambda(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, lambda: ResolvedLambdaArgument) { val currentSubstitutor = c.buildCurrentSubstitutor() fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type) @@ -273,8 +273,7 @@ class KotlinCallCompleter( lambda.resultArguments = resolutionCallbacks.analyzeAndGetLambdaResultArguments(lambda.outerCall, lambda.argument, lambda.isSuspend, receiver, parameters, expectedType) for (innerCall in lambda.resultArguments) { - // todo strange code -- why top-level kotlinCall? may be it isn't right outer call - CheckArguments.checkArgument(topLevelCallContext, topLevelCall, c.getBuilder(), innerCall, lambda.returnType.let(::substitute)) + checkSimpleArgument(c.getBuilder(), innerCall, lambda.returnType.let(::substitute)) } if (lambda.resultArguments.isEmpty()) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt new file mode 100644 index 00000000000..639d74fb8ac --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt @@ -0,0 +1,159 @@ +/* + * Copyright 2010-2017 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.components + +import org.jetbrains.kotlin.builtins.* +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.typeUtil.builtIns + +fun createPostponedArgumentAndPerformInitialChecks( + kotlinCall: KotlinCall, + csBuilder: ConstraintSystemBuilder, + argument: PostponableCallArgument, + parameterDescriptor: ValueParameterDescriptor, + postponeCallableReferenceArguments: MutableList +): KotlinCallDiagnostic? { + val expectedType = argument.getExpectedType(parameterDescriptor) + return when (argument) { + is LambdaKotlinCallArgument -> processLambdaArgument(kotlinCall, csBuilder, argument, expectedType) + is CallableReferenceKotlinCallArgument -> { + // callable reference resolution will be run after choosing single descriptor + postponeCallableReferenceArguments.add(PostponeCallableReferenceArgument(argument, expectedType)) + checkCallableExpectedType(csBuilder, argument, expectedType) + } + is CollectionLiteralKotlinCallArgument -> processCollectionLiteralArgument(kotlinCall, csBuilder, argument, expectedType) + else -> unexpectedArgument(argument) + } +} + +// if expected type isn't function type, then may be it is Function, Any or just `T` +private fun processLambdaArgument( + kotlinCall: KotlinCall, + csBuilder: ConstraintSystemBuilder, + argument: LambdaKotlinCallArgument, + expectedType: UnwrappedType +): KotlinCallDiagnostic? { + val builtIns = expectedType.builtIns + val isSuspend = expectedType.isSuspendFunctionType + + val receiverType: UnwrappedType? // null means that there is no receiver + val parameters: List + val returnType: UnwrappedType + + if (expectedType.isBuiltinFunctionalType) { + receiverType = if (argument is FunctionExpression) argument.receiverType else expectedType.getReceiverTypeFromFunctionType()?.unwrap() + + val expectedParameters = expectedType.getValueParameterTypesFromFunctionType() + if (argument.parametersTypes != null) { + parameters = argument.parametersTypes!!.mapIndexed { + index, type -> + type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: builtIns.anyType + } + } + else { + // lambda without explicit parameters: { } + parameters = expectedParameters.map { it.type.unwrap() } + } + returnType = (argument as? FunctionExpression)?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap() + } + else { + val isFunctionSupertype = KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType) + receiverType = (argument as? FunctionExpression)?.receiverType + parameters = argument.parametersTypes?.map { it ?: builtIns.nothingType } ?: emptyList() + returnType = (argument as? FunctionExpression)?.returnType ?: + expectedType.arguments.singleOrNull()?.type?.unwrap()?.takeIf { isFunctionSupertype } ?: + createFreshTypeVariableForLambdaReturnType(csBuilder, argument, builtIns) + + // what about case where expected type is type variable? In old TY such cases was not supported. => do nothing for now. todo design + } + + val resolvedArgument = ResolvedLambdaArgument(kotlinCall, argument, isSuspend, receiverType, parameters, returnType) + + csBuilder.addSubtypeConstraint(resolvedArgument.type, expectedType, ArgumentConstraintPosition(argument)) + csBuilder.addLambdaArgument(resolvedArgument) + + return null +} + + + +private fun createFreshTypeVariableForLambdaReturnType( + csBuilder: ConstraintSystemBuilder, + argument: LambdaKotlinCallArgument, + builtIns: KotlinBuiltIns +): UnwrappedType { + val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L") + csBuilder.registerVariable(typeVariable) + return typeVariable.defaultType +} + +private 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, + csBuilder: ConstraintSystemBuilder, + argument: CallableReferenceKotlinCallArgument, + expectedType: UnwrappedType +): KotlinCallDiagnostic? { + val subLHSCall = ((argument.lhsResult as? LHSResult.Expression)?.lshCallArgument as? SubKotlinCallArgument) + if (subLHSCall != null) { + csBuilder.addInnerCall(subLHSCall.resolvedCall) + } + val candidates = callContext.callableReferenceResolver.runRLSResolution(callContext, argument, expectedType) { checkCallableReference -> + csBuilder.runTransaction { checkCallableReference(this); false } + } + val chosenCandidate = when (candidates.size) { + 0 -> return NoneCallableReferenceCandidates(argument) + 1 -> candidates.single() + else -> return CallableReferenceCandidatesAmbiguity(argument, candidates) + } + val (toFreshSubstitutor, diagnostic) = with(chosenCandidate) { + csBuilder.checkCallableReference(argument, dispatchReceiver, extensionReceiver, candidate, + reflectionCandidateType, expectedType, callContext.scopeTower.lexicalScope.ownerDescriptor) + } + + val resolvedCallableReference = ResolvedCallableReferenceArgument( + kotlinCall, argument, toFreshSubstitutor.freshVariables, + chosenCandidate, toFreshSubstitutor.safeSubstitute(chosenCandidate.reflectionCandidateType)) + + csBuilder.addCallableReferenceArgument(resolvedCallableReference) + + return diagnostic +} + +fun processCollectionLiteralArgument( + kotlinCall: KotlinCall, + csBuilder: ConstraintSystemBuilder, + collectionLiteralArgument: CollectionLiteralKotlinCallArgument, + expectedType: UnwrappedType +): KotlinCallDiagnostic? { + csBuilder.addCollectionLiteralArgument(ResolvedCollectionLiteralArgument(kotlinCall, collectionLiteralArgument, expectedType)) + return null +} 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 aecc52ba2f4..8e9da0ea9a4 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 @@ -32,10 +32,11 @@ import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.INAPPLICABLE import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.RUNTIME_ERROR import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError +import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.types.TypeConstructor -import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.UnwrappedType -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addIfNotNull internal object CheckInstantiationOfAbstractClass : ResolutionPart { override fun SimpleKotlinResolutionCandidate.process(): List { @@ -220,27 +221,36 @@ internal object CheckReceivers : ResolutionPart { val expectedType = receiverParameter.type.unwrap() - return when (receiverArgument) { - is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, receiverArgument, expectedType, isReceiver = true) - is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, receiverArgument, expectedType, isReceiver = true) - else -> incorrectReceiver(receiverArgument) - } + return checkSimpleArgument(csBuilder, receiverArgument, expectedType, isReceiver = true) } - private fun incorrectReceiver(callReceiver: SimpleKotlinCallArgument): Nothing = - error("Incorrect receiver type: $callReceiver. Class name: ${callReceiver.javaClass.canonicalName}") - override fun SimpleKotlinResolutionCandidate.process() = listOfNotNull(checkReceiver(dispatchReceiverArgument, descriptorWithFreshTypes.dispatchReceiverParameter), checkReceiver(extensionReceiver, descriptorWithFreshTypes.extensionReceiverParameter)) } +internal object CheckArguments : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + val diagnostics = SmartList() + for (parameterDescriptor in descriptorWithFreshTypes.valueParameters) { + // error was reported in ArgumentsToParametersMapper + val resolvedCallArgument = argumentMappingByOriginal[parameterDescriptor.original] ?: continue + for (argument in resolvedCallArgument.arguments) { -fun D.safeSubstitute(substitutor: TypeSubstitutor): D = - @Suppress("UNCHECKED_CAST") (substitute(substitutor) as D) - -fun UnwrappedType.substitute(substitutor: TypeSubstitutor): UnwrappedType = substitutor.substitute(this, Variance.INVARIANT)!!.unwrap() + val diagnostic = when (argument) { + is SimpleKotlinCallArgument -> checkSimpleArgument(csBuilder, argument, argument.getExpectedType(parameterDescriptor)) + is PostponableCallArgument -> + createPostponedArgumentAndPerformInitialChecks(kotlinCall, csBuilder, argument, parameterDescriptor, postponeCallableReferenceArguments) + else -> unexpectedArgument(argument) + } + diagnostics.addIfNotNull(diagnostic) + if (diagnostic != null && !diagnostic.candidateApplicability.isSuccess) break + } + } + return diagnostics + } +} object InstantiationOfAbstractClass : KotlinCallDiagnostic(RUNTIME_ERROR) { override fun report(reporter: DiagnosticReporter) = reporter.onCall(this) @@ -255,22 +265,6 @@ class UnsafeCallError(val receiver: SimpleKotlinCallArgument) : KotlinCallDiagno override fun report(reporter: DiagnosticReporter) = reporter.onCallReceiver(receiver, this) } -class ExpectedLambdaParametersCountMismatch( - val lambdaArgument: LambdaKotlinCallArgument, - val expected: Int, - val actual: Int -) : KotlinCallDiagnostic(INAPPLICABLE) { - override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(lambdaArgument, this) -} - -class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(INAPPLICABLE) { - override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this) -} - -class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(INAPPLICABLE) { - override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this) -} - class NoneCallableReferenceCandidates(val argument: CallableReferenceKotlinCallArgument) : KotlinCallDiagnostic(INAPPLICABLE) { override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt new file mode 100644 index 00000000000..f8197b1587d --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt @@ -0,0 +1,162 @@ +/* + * Copyright 2010-2017 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.components + +import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible +import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.ReceiverConstraintPosition +import org.jetbrains.kotlin.resolve.calls.model.ExpressionKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.SubKotlinCallArgument +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.captureFromExpression +import org.jetbrains.kotlin.types.checker.hasSupertypeWithGivenTypeConstructor +import org.jetbrains.kotlin.types.lowerIfFlexible +import org.jetbrains.kotlin.types.typeUtil.supertypes +import org.jetbrains.kotlin.types.upperIfFlexible + + +fun checkSimpleArgument( + csBuilder: ConstraintSystemBuilder, + argument: SimpleKotlinCallArgument, + expectedType: UnwrappedType, + isReceiver: Boolean = false +): KotlinCallDiagnostic? { + return when (argument) { + is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, argument, expectedType, isReceiver) + is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, argument, expectedType, isReceiver) + else -> unexpectedArgument(argument) + } +} + +private fun checkExpressionArgument( + csBuilder: ConstraintSystemBuilder, + expressionArgument: ExpressionKotlinCallArgument, + expectedType: UnwrappedType, + isReceiver: Boolean +): KotlinCallDiagnostic? { + // todo run this approximation only once for call + val argumentType = captureFromTypeParameterUpperBoundIfNeeded(expressionArgument.receiver.stableType, expectedType) + + fun unstableSmartCastOrSubtypeError( + unstableType: UnwrappedType?, actualExpectedType: UnwrappedType, position: ConstraintPosition + ): KotlinCallDiagnostic? { + if (unstableType != null) { + if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, actualExpectedType, position)) { + return UnstableSmartCast(expressionArgument, unstableType) + } + } + csBuilder.addSubtypeConstraint(argumentType, actualExpectedType, position) + return null + } + + val expectedNullableType = expectedType.makeNullableAsSpecified(true) + val position = if (isReceiver) ReceiverConstraintPosition(expressionArgument) else ArgumentConstraintPosition(expressionArgument) + if (expressionArgument.isSafeCall) { + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { + return unstableSmartCastOrSubtypeError(expressionArgument.receiver.unstableType, expectedNullableType, position)?.let { return it } + } + return null + } + + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { + if (!isReceiver) { + return unstableSmartCastOrSubtypeError(expressionArgument.receiver.unstableType, expectedType, position)?.let { return it } + } + + val unstableType = expressionArgument.receiver.unstableType + if (unstableType != null && csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { + return UnstableSmartCast(expressionArgument, unstableType) + } + else if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { + return UnsafeCallError(expressionArgument) + } + else { + csBuilder.addSubtypeConstraint(argumentType, expectedType, position) + return null + } + } + + return null +} + +/** + * interface Inv + * fun bar(l: Inv): Y = ... + * + * fun > foo(x: X) { + * val xr = bar(x) + * } + * Here we try to capture from upper bound from type parameter. + * We replace type of `x` to `Inv`(we chose supertype which contains supertype with expectedTypeConstructor) and capture from this type. + * It is correct, because it is like this code: + * fun > foo(x: X) { + * val inv: Inv = x + * val xr = bar(inv) + * } + * + */ +private fun captureFromTypeParameterUpperBoundIfNeeded(argumentType: UnwrappedType, expectedType: UnwrappedType): UnwrappedType { + val expectedTypeConstructor = expectedType.upperIfFlexible().constructor + + if (argumentType.lowerIfFlexible().constructor.declarationDescriptor is TypeParameterDescriptor) { + val chosenSupertype = argumentType.lowerIfFlexible().supertypes().singleOrNull { + it.constructor.declarationDescriptor is ClassifierDescriptorWithTypeParameters && + it.unwrap().hasSupertypeWithGivenTypeConstructor(expectedTypeConstructor) + } + if (chosenSupertype != null) { + return captureFromExpression(chosenSupertype.unwrap()) ?: argumentType + } + } + + return argumentType +} + +private fun checkSubCallArgument( + csBuilder: ConstraintSystemBuilder, + subCallArgument: SubKotlinCallArgument, + expectedType: UnwrappedType, + isReceiver: Boolean +): KotlinCallDiagnostic? { + val resolvedCall = subCallArgument.resolvedCall + val expectedNullableType = expectedType.makeNullableAsSpecified(true) + val position = ArgumentConstraintPosition(subCallArgument) + + csBuilder.addInnerCall(resolvedCall) + + // subArgument cannot has stable smartcast + val currentReturnType = subCallArgument.receiver.receiverValue.type.unwrap() + if (subCallArgument.isSafeCall) { + csBuilder.addSubtypeConstraint(currentReturnType, expectedNullableType, position) + return null + } + + if (isReceiver && !csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedType, position) && + csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedNullableType, position) + ) { + return UnsafeCallError(subCallArgument) + } + + csBuilder.addSubtypeConstraint(currentReturnType, expectedType, position) + return null +} + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt index b345ff030bf..80a3ad0a9dd 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt @@ -39,6 +39,8 @@ interface KotlinCallArgument { val argumentName: Name? } +interface PostponableCallArgument : KotlinCallArgument + interface SimpleKotlinCallArgument : KotlinCallArgument, ReceiverKotlinCallArgument { override val receiver: ReceiverValueWithSmartCastInfo @@ -51,7 +53,7 @@ interface SubKotlinCallArgument : SimpleKotlinCallArgument { val resolvedCall: ResolvedKotlinCall.OnlyResolvedKotlinCall } -interface LambdaKotlinCallArgument : KotlinCallArgument { +interface LambdaKotlinCallArgument : PostponableCallArgument { override val isSpread: Boolean get() = false @@ -110,7 +112,7 @@ sealed class LHSResult { object Empty: LHSResult() } -interface CallableReferenceKotlinCallArgument : KotlinCallArgument { +interface CallableReferenceKotlinCallArgument : PostponableCallArgument { override val isSpread: Boolean get() = false @@ -119,7 +121,7 @@ interface CallableReferenceKotlinCallArgument : KotlinCallArgument { val rhsName: Name } -interface CollectionLiteralKotlinCallArgument : KotlinCallArgument +interface CollectionLiteralKotlinCallArgument : PostponableCallArgument interface TypeArgument diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt index def4858150e..6819c9ad7b9 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt @@ -47,7 +47,7 @@ class ResolvedLambdaArgument( override val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters override val outputType: UnwrappedType get() = returnType - lateinit var resultArguments: List + lateinit var resultArguments: List lateinit var finalReturnType: UnwrappedType }