[NI] Remove type variables for lambda arguments -- use existing instead.

This commit is contained in:
Stanislav Erokhin
2017-05-19 16:31:52 +03:00
committed by Mikhail Zarechenskiy
parent ff6a28b64c
commit 3a25405088
7 changed files with 73 additions and 168 deletions
@@ -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)
@@ -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<UnwrappedType> {
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<R>, 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<LambdaTypeVariable>()
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<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 } ?:
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)
@@ -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
}
@@ -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<Variable, ResolveDirection>()
private val lambdaResultDependencyEdges = HashMap<Variable, MutableList<Variable>>()
private val lambdaEdges = HashMap<Variable, MutableSet<Variable>>()
// first in the list -- first fix
fun getCompletionOrder(topReturnType: UnwrappedType): List<NodeWithDirection> {
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<Variable>()
lambdaArgument.returnType.findTypeVariables(typeVariablesInReturnType)
val lambdaArgument = lambdaResultVariable.lambdaArgument
if (typeVariablesInReturnType.isEmpty()) continue // optimization
if (resolvedLambdaArguments[lambdaArgument]?.analyzed ?: false) continue
val typeVariablesInParameters = SmartList<Variable>()
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<Variable>) = contains {
c.notFixedTypeVariables[it.constructor]?.let { variable -> to.add(variable) }
false
}
private fun topologicalOrderWith0Priority(): List<Variable> {
val handler = object : DFS.CollectingNodeHandler<Variable, Variable, LinkedHashSet<Variable>>(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<Variable>().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<Variable> =
lambdaResultDependencyEdges.getOrElse(variable) { emptyList() }
private fun getLambdaDependencies(variable: Variable): List<Variable> {
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<Variable>.() -> Unit): List<Variable> =
SmartList<Variable>().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<Variable> = lambdaEdges[variable]?.toList() ?: emptyList()
private fun UnwrappedType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) =
when (this) {
@@ -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]"
}
}
@@ -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<NewTypeVariable>
abstract val inputType: Collection<UnwrappedType> // parameters and implicit receiver
abstract val inputTypes: Collection<UnwrappedType> // 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<LambdaTypeVariable>,
val isSuspend: Boolean,
val receiver: UnwrappedType?,
val parameters: List<UnwrappedType>,
@@ -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<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
override val inputTypes: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
override val outputType: UnwrappedType get() = returnType
lateinit var resultArguments: List<KotlinCallArgument>
@@ -57,9 +53,9 @@ class ResolvedLambdaArgument(
class ResolvedCallableReferenceArgument(
override val outerCall: KotlinCall,
override val argument: CallableReferenceKotlinCallArgument,
override val myTypeVariables: List<NewTypeVariable>,
val myTypeVariables: List<NewTypeVariable>,
val callableResolutionCandidate: CallableReferenceCandidate
) : ArgumentWithPostponeResolution() {
override val inputType: Collection<UnwrappedType> get() = emptyList()
override val inputTypes: Collection<UnwrappedType> get() = emptyList()
override val outputType: UnwrappedType? = null
}
@@ -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.