[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
This commit is contained in:
Stanislav Erokhin
2017-06-23 05:09:50 +03:00
committed by Mikhail Zarechenskiy
parent 1bc68e073d
commit 3450340d7f
8 changed files with 406 additions and 372 deletions
@@ -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()
}
@@ -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<KotlinCallDiagnostic> {
val diagnostics = SmartList<KotlinCallDiagnostic>()
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<PostponeCallableReferenceArgument>? = 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<R>, 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<UnwrappedType>
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<T>
* fun <Y> bar(l: Inv<Y>): Y = ...
*
* fun <X : Inv<out Int>> 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<out Int>`(we chose supertype which contains supertype with expectedTypeConstructor) and capture from this type.
* It is correct, because it is like this code:
* fun <X : Inv<out Int>> foo(x: X) {
* val inv: Inv<out Int> = 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()
}
@@ -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()) {
@@ -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<PostponeCallableReferenceArgument>
): 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<R>, 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<UnwrappedType>
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
}
@@ -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<KotlinCallDiagnostic> {
@@ -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<KotlinCallDiagnostic> {
val diagnostics = SmartList<KotlinCallDiagnostic>()
for (parameterDescriptor in descriptorWithFreshTypes.valueParameters) {
// error was reported in ArgumentsToParametersMapper
val resolvedCallArgument = argumentMappingByOriginal[parameterDescriptor.original] ?: continue
for (argument in resolvedCallArgument.arguments) {
fun <D : CallableDescriptor> 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)
}
@@ -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<T>
* fun <Y> bar(l: Inv<Y>): Y = ...
*
* fun <X : Inv<out Int>> 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<out Int>`(we chose supertype which contains supertype with expectedTypeConstructor) and capture from this type.
* It is correct, because it is like this code:
* fun <X : Inv<out Int>> foo(x: X) {
* val inv: Inv<out Int> = 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
}
@@ -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
@@ -47,7 +47,7 @@ class ResolvedLambdaArgument(
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
override val outputType: UnwrappedType get() = returnType
lateinit var resultArguments: List<KotlinCallArgument>
lateinit var resultArguments: List<SimpleKotlinCallArgument>
lateinit var finalReturnType: UnwrappedType
}