diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt index d4c44bc1282..794b06dc63f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt @@ -77,7 +77,7 @@ class KotlinResolutionCallbacksImpl( val approximatesExpectedType = typeApproximator.approximateToSubType(expectedType, TypeApproximatorConfiguration.LocalDeclaration) ?: expectedType val actualContext = outerCallContext.replaceBindingTrace(trace). - replaceContextDependency(ContextDependency.DEPENDENT).replaceExpectedType(approximatesExpectedType) + replaceContextDependency(if (expectedReturnType == null) ContextDependency.DEPENDENT else ContextDependency.INDEPENDENT).replaceExpectedType(approximatesExpectedType) val functionTypeInfo = expressionTypingServices.getTypeInfo(expression, actualContext) 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 4f4956ad678..0254f6bfb40 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 @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.resolve.calls.components.CreateDescriptorWithFreshTy 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.LambdaTypeVariable import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.types.UnwrappedType @@ -82,80 +81,49 @@ internal object CheckArguments : ResolutionPart { } } - inline fun computeParameterTypes( - argument: LambdaKotlinCallArgument, - expectedType: UnwrappedType, - createFreshType: () -> UnwrappedType - ): List { - argument.parametersTypes?.map { it ?: createFreshType() } ?.let { return it } - - if (expectedType.isBuiltinFunctionalType) { - return expectedType.getValueParameterTypesFromFunctionType().map { createFreshType() } - } - - // if expected type is non-functional type and there is no declared parameters - return emptyList() - } - - inline fun computeReceiver( - argument: LambdaKotlinCallArgument, - expectedType: UnwrappedType, - createFreshType: () -> UnwrappedType - ) : UnwrappedType? { - if (argument is FunctionExpression) return argument.receiverType - - if (expectedType.isBuiltinExtensionFunctionalType) return createFreshType() - - return null - } - - inline fun computeReturnType( - argument: LambdaKotlinCallArgument, - createFreshType: () -> UnwrappedType - ) : UnwrappedType { - if (argument is FunctionExpression) return argument.receiverType ?: createFreshType() - - return createFreshType() - } - + // if expected type isn't function type, then may be it is Function, Any or just `T` fun processLambdaArgument( kotlinCall: KotlinCall, csBuilder: ConstraintSystemBuilder, argument: LambdaKotlinCallArgument, expectedType: UnwrappedType ): KotlinCallDiagnostic? { - // initial checks - if (expectedType.isBuiltinFunctionalType) { - val expectedParameterCount = expectedType.getValueParameterTypesFromFunctionType().size - - argument.parametersTypes?.size?.let { - if (expectedParameterCount != it) return ExpectedLambdaParametersCountMismatch(argument, expectedParameterCount, it) - } - - if (argument is FunctionExpression) { - if (argument.receiverType != null && !expectedType.isBuiltinExtensionFunctionalType) return UnexpectedReceiver(argument) - if (argument.receiverType == null && expectedType.isBuiltinExtensionFunctionalType) return MissingReceiver(argument) - } - } - val builtIns = expectedType.builtIns - val freshVariables = SmartList() - val receiver = computeReceiver(argument, expectedType) { - LambdaTypeVariable(argument, LambdaTypeVariable.Kind.RECEIVER, builtIns).apply { freshVariables.add(this) }.defaultType - } - - val parameters = computeParameterTypes(argument, expectedType) { - LambdaTypeVariable(argument, LambdaTypeVariable.Kind.PARAMETER, builtIns).apply { freshVariables.add(this) }.defaultType - } - - val returnType = computeReturnType(argument) { - LambdaTypeVariable(argument, LambdaTypeVariable.Kind.RETURN_TYPE, builtIns).apply { freshVariables.add(this) }.defaultType - } - val isSuspend = expectedType.isSuspendFunctionType - val resolvedArgument = ResolvedLambdaArgument(kotlinCall, argument, freshVariables, isSuspend, receiver, parameters, returnType) - freshVariables.forEach(csBuilder::registerVariable) + 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 } ?: + builtIns.nullableAnyType + + // 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) 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 9f805c7ddff..68de9906799 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 @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalc import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing @@ -220,6 +219,7 @@ class KotlinCallCompleter( } // true if it is the end (happy or not) + // every step we fix type variable or analyzeLambda private fun SimpleKotlinResolutionCandidate.oneStepToEndOrLambda(c: Context, resolutionCallbacks: KotlinResolutionCallbacks): Boolean { if (c.hasContradiction) return true @@ -240,14 +240,7 @@ class KotlinCallCompleter( break } c.fixVariable(variable, resultType) - - if (variable is LambdaTypeVariable) { - val resolvedLambda = c.lambdaArguments.find { it.argument == variable.lambdaArgument } ?: return true - if (canWeAnalyzeIt(c, resolvedLambda)) { - analyzeLambda(c, resolutionCallbacks, callContext, kotlinCall, resolvedLambda) - return false - } - } + return false } return true } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt index b136c1f72f6..bfb75336232 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt @@ -16,16 +16,16 @@ package org.jetbrains.kotlin.resolve.calls.inference.components -import org.jetbrains.kotlin.resolve.calls.inference.model.* +import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker import org.jetbrains.kotlin.types.checker.isIntersectionType +import org.jetbrains.kotlin.types.typeUtil.contains import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.SmartList -import org.jetbrains.kotlin.utils.addIfNotNull -import java.util.* -import kotlin.collections.HashMap private typealias Variable = VariableWithConstraints @@ -57,6 +57,8 @@ class FixationOrderCalculator { * result type U depends-on all parameters types V of the corresponding lambda * * LAMBDA-RESULT + * Since there is no separate type variables for lambda such edges removed for now + * * V is a lambda result type variable, * V <: T constraint exists for V, * U is a constituent type of T in position matching approximation direction for U @@ -73,54 +75,45 @@ class FixationOrderCalculator { private class DependencyGraph(val c: Context) { private val directions = HashMap() - private val lambdaResultDependencyEdges = HashMap>() + private val lambdaEdges = HashMap>() // first in the list -- first fix fun getCompletionOrder(topReturnType: UnwrappedType): List { setupDirections(topReturnType) - computeLambdaResultDependencyEdges() + buildLambdaEdges() return topologicalOrderWith0Priority().map { NodeWithDirection(it, directions[it] ?: ResolveDirection.UNKNOWN) } } - private fun computeLambdaResultDependencyEdges() { - val resolvedLambdaArguments = c.lambdaArguments.associateBy({ it.argument }, { it }) + private fun buildLambdaEdges() { + for (lambdaArgument in c.lambdaArguments) { + if (lambdaArgument.analyzed) continue - for (variable in c.notFixedTypeVariables.values) { - val lambdaResultVariable = variable.typeVariable.takeLambdaResultVariable() ?: continue + val typeVariablesInReturnType = SmartList() + lambdaArgument.returnType.findTypeVariables(typeVariablesInReturnType) - val lambdaArgument = lambdaResultVariable.lambdaArgument + if (typeVariablesInReturnType.isEmpty()) continue // optimization - if (resolvedLambdaArguments[lambdaArgument]?.analyzed ?: false) continue + val typeVariablesInParameters = SmartList() + lambdaArgument.inputTypes.forEach { it.findTypeVariables(typeVariablesInParameters) } - for (constraint in variable.constraints) { - val initialDirection = when (constraint.kind) { - ConstraintKind.LOWER -> ResolveDirection.TO_SUBTYPE - ConstraintKind.UPPER -> ResolveDirection.TO_SUPERTYPE - ConstraintKind.EQUALITY -> ResolveDirection.UNKNOWN // ??? - } - - constraint.type.visitType(initialDirection) { constituentVariable, direction -> - val constituentVariableDirection = directions.getOrElse(constituentVariable) { ResolveDirection.UNKNOWN } - - val constituentTypeVariable = constituentVariable.typeVariable - if (constituentTypeVariable is LambdaTypeVariable) { - if (constituentTypeVariable.lambdaArgument == lambdaArgument) return@visitType - } - - if (direction == ResolveDirection.UNKNOWN || direction == constituentVariableDirection) { - lambdaResultDependencyEdges.getOrPut(constituentVariable) { SmartList() }.add(variable) - } - } + for (returnTypeVariable in typeVariablesInReturnType) { + lambdaEdges.getOrPut(returnTypeVariable) { LinkedHashSet() }.addAll(typeVariablesInParameters) } } } + private fun UnwrappedType.findTypeVariables(to: MutableCollection) = contains { + c.notFixedTypeVariables[it.constructor]?.let { variable -> to.add(variable) } + false + } + private fun topologicalOrderWith0Priority(): List { val handler = object : DFS.CollectingNodeHandler>(LinkedHashSet()) { override fun afterChildren(current: Variable) { // LAMBDA dependency edges should always be satisfied + // Note that cyclic by lambda edges are possible result.addAll(getLambdaDependencies(current)) result.add(current) @@ -139,11 +132,9 @@ class FixationOrderCalculator { enterToNode(variableWithConstraints, direction) } for (resolvedLambdaArgument in c.lambdaArguments) { - inner@ for (typeVariable in resolvedLambdaArgument.myTypeVariables) { - if (typeVariable.kind == LambdaTypeVariable.Kind.RETURN_TYPE) continue@inner - - c.notFixedTypeVariables[typeVariable.freshTypeConstructor]?.let { - enterToNode(it, ResolveDirection.TO_SUBTYPE) + inner@ for (inputType in resolvedLambdaArgument.inputTypes) { + inputType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction -> + enterToNode(variableWithConstraints, direction) } } } @@ -172,7 +163,6 @@ class FixationOrderCalculator { val constraintEdges = LinkedHashSet().also { set -> getConstraintDependencies(variable, direction).mapTo(set) { it.variableWithConstraints } - set.addAll(getLambdaResultDependencies(variable)) }.toList().sortByTypeVariable() val lambdaEdges = getLambdaDependencies(variable).sortByTypeVariable() return constraintEdges + lambdaEdges @@ -211,28 +201,8 @@ class FixationOrderCalculator { !(direction == ResolveDirection.TO_SUBTYPE && constraint.kind == ConstraintKind.UPPER) && !(direction == ResolveDirection.TO_SUPERTYPE && constraint.kind == ConstraintKind.LOWER) - private fun getLambdaResultDependencies(variable: Variable): List = - lambdaResultDependencyEdges.getOrElse(variable) { emptyList() } - private fun getLambdaDependencies(variable: Variable): List { - val typeVariable = variable.typeVariable.takeLambdaResultVariable() ?: return emptyList() - - val resolvedLambdaArgument = c.lambdaArguments.find { it.argument == typeVariable.lambdaArgument } ?: - error("Missing resolved lambda argument for ${typeVariable.lambdaArgument}") - - return buildVariablesList { - for (lambdaTypeVariable in resolvedLambdaArgument.myTypeVariables) { - if (lambdaTypeVariable.kind == LambdaTypeVariable.Kind.RETURN_TYPE) continue - addIfNotNull(c.notFixedTypeVariables[lambdaTypeVariable.freshTypeConstructor]) - } - } - } - - private inline fun buildVariablesList(builder: MutableList.() -> Unit): List = - SmartList().apply(builder).toList() - - private fun NewTypeVariable.takeLambdaResultVariable(): LambdaTypeVariable? = - if (this is LambdaTypeVariable && this.kind == LambdaTypeVariable.Kind.RETURN_TYPE) this else null + private fun getLambdaDependencies(variable: Variable): List = lambdaEdges[variable]?.toList() ?: emptyList() private fun UnwrappedType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) = when (this) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt index 88b0ac80809..3bec778dc42 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.model.KotlinCall -import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor @@ -53,24 +52,3 @@ class TypeVariableFromCallableDescriptor( val originalTypeParameter: TypeParameterDescriptor, val call: KotlinCall? = null ) : NewTypeVariable(originalTypeParameter.builtIns, originalTypeParameter.name.identifier) - -class LambdaTypeVariable( - val lambdaArgument: LambdaKotlinCallArgument, - val kind: Kind, - builtIns: KotlinBuiltIns -) : NewTypeVariable(builtIns, createDebugName(lambdaArgument, kind)) { - enum class Kind { - RECEIVER, - PARAMETER, - RETURN_TYPE - } -} - -private fun createDebugName(lambdaArgument: LambdaKotlinCallArgument, kind: LambdaTypeVariable.Kind): String { - val text = lambdaArgument.toString().let { it.substring(0..(Math.min(20, it.lastIndex))) } - return when (kind) { - LambdaTypeVariable.Kind.RECEIVER -> "Receiver[$text]" - LambdaTypeVariable.Kind.PARAMETER -> "Parameter[$text]" - LambdaTypeVariable.Kind.RETURN_TYPE -> "Result[$text]" - } -} \ No newline at end of file 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 a048dbfe801..99933d2f02e 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 @@ -17,10 +17,8 @@ package org.jetbrains.kotlin.resolve.calls.model import org.jetbrains.kotlin.builtins.createFunctionType -import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceCandidate -import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.UnwrappedType @@ -30,8 +28,7 @@ import org.jetbrains.kotlin.types.typeUtil.builtIns sealed class ArgumentWithPostponeResolution { abstract val outerCall: KotlinCall abstract val argument: KotlinCallArgument - abstract val myTypeVariables: Collection - abstract val inputType: Collection // parameters and implicit receiver + abstract val inputTypes: Collection // parameters and implicit receiver abstract val outputType: UnwrappedType? var analyzed: Boolean = false @@ -40,7 +37,6 @@ sealed class ArgumentWithPostponeResolution { class ResolvedLambdaArgument( override val outerCall: KotlinCall, override val argument: LambdaKotlinCallArgument, - override val myTypeVariables: Collection, val isSuspend: Boolean, val receiver: UnwrappedType?, val parameters: List, @@ -48,7 +44,7 @@ class ResolvedLambdaArgument( ) : ArgumentWithPostponeResolution() { val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType, isSuspend) // todo support annotations - override val inputType: Collection get() = receiver?.let { parameters + it } ?: parameters + override val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters override val outputType: UnwrappedType get() = returnType lateinit var resultArguments: List @@ -57,9 +53,9 @@ class ResolvedLambdaArgument( class ResolvedCallableReferenceArgument( override val outerCall: KotlinCall, override val argument: CallableReferenceKotlinCallArgument, - override val myTypeVariables: List, + val myTypeVariables: List, val callableResolutionCandidate: CallableReferenceCandidate ) : ArgumentWithPostponeResolution() { - override val inputType: Collection get() = emptyList() + override val inputTypes: Collection get() = emptyList() override val outputType: UnwrappedType? = null } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt index 4824b4178fd..6635d420c1b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -123,9 +123,9 @@ class TypeApproximator { if (conf.flexible) { /** - * Let inputType = L_1..U_1; resultType = L_2..U_2 - * We should create resultType such as inputType <: resultType. - * It means that if A <: inputType, then A <: U_1. And, because inputType <: resultType, + * Let inputTypes = L_1..U_1; resultType = L_2..U_2 + * We should create resultType such as inputTypes <: resultType. + * It means that if A <: inputTypes, then A <: U_1. And, because inputTypes <: resultType, * A <: resultType => A <: U_2. I.e. for every type A such A <: U_1, A <: U_2 => U_1 <: U_2. * * Similar for L_1 <: L_2: Let B : resultType <: B. L_2 <: B and L_1 <: B. @@ -138,7 +138,7 @@ class TypeApproximator { /** * If C <: L..U then C <: L. - * inputType.lower <: lowerResult => inputType.lower <: lowerResult?.lowerIfFlexible() + * inputTypes.lower <: lowerResult => inputTypes.lower <: lowerResult?.lowerIfFlexible() * i.e. this type is correct. We use this type, because this type more flexible. * * If U_1 <: U_2.lower .. U_2.upper, then we know only that U_1 <: U_2.upper.