[NI] New inference -- initial commit.

This commit is contained in:
Stanislav Erokhin
2016-08-01 08:19:11 +03:00
parent 036090be91
commit b012681a53
64 changed files with 6089 additions and 63 deletions
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2016 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
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.components.NewOverloadingConflictResolver
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.types.UnwrappedType
import java.lang.UnsupportedOperationException
class KotlinCallResolver(
private val towerResolver: TowerResolver,
private val kotlinCallCompleter: KotlinCallCompleter,
private val overloadingConflictResolver: NewOverloadingConflictResolver
) {
fun resolveCall(
callContext: KotlinCallContext,
kotlinCall: KotlinCall,
expectedType: UnwrappedType?,
factoryProviderForInvoke: CandidateFactoryProviderForInvoke<KotlinResolutionCandidate>
): Collection<ResolvedKotlinCall> {
val scopeTower = callContext.scopeTower
kotlinCall.checkCallInvariants()
val candidateFactory = SimpleCandidateFactory(callContext, kotlinCall)
val processor = when(kotlinCall.callKind) {
KotlinCallKind.VARIABLE -> {
createVariableAndObjectProcessor(scopeTower, kotlinCall.name, candidateFactory, kotlinCall.explicitReceiver?.receiver)
}
KotlinCallKind.FUNCTION -> {
createFunctionProcessor(scopeTower, kotlinCall.name, candidateFactory, factoryProviderForInvoke, kotlinCall.explicitReceiver?.receiver)
}
KotlinCallKind.UNSUPPORTED -> throw UnsupportedOperationException()
}
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kotlinCall.callKind != KotlinCallKind.UNSUPPORTED)
return choseMostSpecific(callContext, expectedType, candidates)
}
fun resolveGivenCandidates(
callContext: KotlinCallContext,
kotlinCall: KotlinCall,
expectedType: UnwrappedType?,
givenCandidates: Collection<GivenCandidate>
): Collection<ResolvedKotlinCall> {
kotlinCall.checkCallInvariants()
val resolutionCandidates = givenCandidates.map {
SimpleKotlinResolutionCandidate(callContext,
kotlinCall,
if (it.dispatchReceiver == null) ExplicitReceiverKind.NO_EXPLICIT_RECEIVER else ExplicitReceiverKind.DISPATCH_RECEIVER,
it.dispatchReceiver?.let { ReceiverExpressionKotlinCallArgument(it) },
null,
it.descriptor,
listOf()
)
}
val candidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolutionCandidates),
TowerResolver.SuccessfulResultCollector { it.status },
useOrder = true)
return choseMostSpecific(callContext, expectedType, candidates)
}
private fun choseMostSpecific(
callContext: KotlinCallContext,
expectedType: UnwrappedType?,
candidates: Collection<KotlinResolutionCandidate>
): Collection<ResolvedKotlinCall> {
val maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates(
candidates,
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
discriminateGenerics = true, // todo
isDebuggerContext = callContext.scopeTower.isDebuggerContext)
val singleResult = maximallySpecificCandidates.singleOrNull()?.let {
kotlinCallCompleter.completeCallIfNecessary(it, expectedType, callContext.lambdaAnalyzer)
}
if (singleResult != null) {
return listOf(singleResult)
}
return maximallySpecificCandidates.map {
kotlinCallCompleter.transformWhenAmbiguity(it)
}
}
}
@@ -0,0 +1,21 @@
/*
* 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
val USE_NEW_INFERENCE = false
val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC = false
@@ -0,0 +1,328 @@
/*
* Copyright 2010-2016 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.CallableDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.*
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import java.util.*
class ArgumentsToParametersMapper {
data class ArgumentMapping(
// This map should be ordered by arguments as written, e.g.:
// fun foo(a: Int, b: Int) {}
// foo(b = bar(), a = qux())
// parameterToCallArgumentMap.values() should be [ 'bar()', 'foo()' ]
val parameterToCallArgumentMap: Map<ValueParameterDescriptor, ResolvedCallArgument>,
val diagnostics: List<KotlinCallDiagnostic>
)
val EmptyArgumentMapping = ArgumentMapping(emptyMap(), emptyList())
fun mapArguments(call: KotlinCall, descriptor: CallableDescriptor): ArgumentMapping =
mapArguments(call.argumentsInParenthesis, call.externalArgument, descriptor)
fun mapArguments(
argumentsInParenthesis: List<KotlinCallArgument>,
externalArgument: KotlinCallArgument?,
descriptor: CallableDescriptor
): ArgumentMapping {
// optimization for case of variable
if (argumentsInParenthesis.isEmpty() && externalArgument == null && descriptor.valueParameters.isEmpty()) {
return EmptyArgumentMapping
}
else {
val processor = CallArgumentProcessor(descriptor)
processor.processArgumentsInParenthesis(argumentsInParenthesis)
if (externalArgument != null) {
processor.processExternalArgument(externalArgument)
}
processor.processDefaultsAndRunChecks()
return ArgumentMapping(processor.result, processor.getDiagnostics())
}
}
private class CallArgumentProcessor(val descriptor: CallableDescriptor) {
val result: MutableMap<ValueParameterDescriptor, ResolvedCallArgument> = LinkedHashMap()
private var state = State.POSITION_ARGUMENTS
private val parameters: List<ValueParameterDescriptor> get() = descriptor.valueParameters
private var diagnostics: MutableList<KotlinCallDiagnostic>? = null
private var nameToParameter: Map<Name, ValueParameterDescriptor>? = null
private var varargArguments: MutableList<KotlinCallArgument>? = null
private var currentParameterIndex = 0
private fun addDiagnostic(diagnostic: KotlinCallDiagnostic) {
if (diagnostics == null) {
diagnostics = ArrayList()
}
diagnostics!!.add(diagnostic)
}
fun getDiagnostics() = diagnostics ?: emptyList<KotlinCallDiagnostic>()
private fun getParameterByName(name: Name): ValueParameterDescriptor? {
if (nameToParameter == null) {
nameToParameter = parameters.associateBy { it.name }
}
return nameToParameter!![name]
}
private fun addVarargArgument(argument: KotlinCallArgument) {
if (varargArguments == null) {
varargArguments = ArrayList()
}
varargArguments!!.add(argument)
}
private enum class State {
POSITION_ARGUMENTS,
VARARG_POSITION,
NAMED_ARGUMENT
}
private fun completeVarargPositionArguments() {
assert(state == State.VARARG_POSITION) { "Incorrect state: $state" }
val parameter = parameters[currentParameterIndex]
result.put(parameter.original, ResolvedCallArgument.VarargArgument(varargArguments!!))
}
// return true, if it was mapped to vararg parameter
private fun processPositionArgument(argument: KotlinCallArgument): Boolean {
if (state == State.NAMED_ARGUMENT) {
addDiagnostic(MixingNamedAndPositionArguments(argument))
return false
}
val parameter = parameters.getOrNull(currentParameterIndex)
if (parameter == null) {
addDiagnostic(TooManyArguments(argument, descriptor))
return false
}
if (!parameter.isVararg) {
currentParameterIndex++
result.put(parameter.original, ResolvedCallArgument.SimpleArgument(argument))
return false
}
// all position arguments will be mapped to current vararg parameter
else {
addVarargArgument(argument)
return true
}
}
private fun processNamedArgument(argument: KotlinCallArgument, name: Name) {
if (!descriptor.hasStableParameterNames()) {
addDiagnostic(NamedArgumentNotAllowed(argument, descriptor))
}
val parameter = findParameterByName(argument, name) ?: return
addDiagnostic(NamedArgumentReference(argument, parameter))
result[parameter.original]?.let {
addDiagnostic(ArgumentPassedTwice(argument, parameter, it))
return
}
result[parameter.original] = ResolvedCallArgument.SimpleArgument(argument)
}
private fun findParameterByName(argument: KotlinCallArgument, name: Name): ValueParameterDescriptor? {
val parameter = getParameterByName(name)
if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
if (parameter == null) {
for (valueParameter in descriptor.valueParameters) {
val matchedParameter = valueParameter.overriddenDescriptors.firstOrNull {
it.containingDeclaration.hasStableParameterNames() && it.name == name
}
if (matchedParameter != null) {
addDiagnostic(NamedArgumentReference(argument, valueParameter))
addDiagnostic(NameForAmbiguousParameter(argument, valueParameter, matchedParameter))
return valueParameter
}
}
}
else {
parameter.getOverriddenParameterWithOtherName()?.let {
addDiagnostic(NameForAmbiguousParameter(argument, parameter, it))
}
}
}
if (parameter == null) addDiagnostic(NameNotFound(argument, descriptor))
return parameter
}
fun processArgumentsInParenthesis(arguments: List<KotlinCallArgument>) {
for (argument in arguments) {
val argumentName = argument.argumentName
// process position argument
if (argumentName == null) {
if (processPositionArgument(argument)) {
state = State.VARARG_POSITION
}
}
// process named argument
else {
if (state == State.VARARG_POSITION) {
completeVarargPositionArguments()
}
state = State.POSITION_ARGUMENTS
processNamedArgument(argument, argumentName)
}
}
if (state == State.VARARG_POSITION) {
completeVarargPositionArguments()
}
}
fun processExternalArgument(externalArgument: KotlinCallArgument) {
val lastParameter = parameters.lastOrNull()
if (lastParameter == null) {
addDiagnostic(TooManyArguments(externalArgument, descriptor))
return
}
if (lastParameter.isVararg) {
addDiagnostic(VarargArgumentOutsideParentheses(externalArgument, lastParameter))
return
}
val previousOccurrence = result[lastParameter.original]
if (previousOccurrence != null) {
addDiagnostic(TooManyArguments(externalArgument, descriptor))
return
}
result[lastParameter.original] = ResolvedCallArgument.SimpleArgument(externalArgument)
}
fun processDefaultsAndRunChecks() {
for ((parameter, resolvedArgument) in result) {
if (!parameter.isVararg) {
if (resolvedArgument !is ResolvedCallArgument.SimpleArgument) {
error("Incorrect resolved argument for parameter $parameter :$resolvedArgument")
}
else {
if (resolvedArgument.callArgument.isSpread) {
addDiagnostic(NonVarargSpread(resolvedArgument.callArgument, parameter))
}
}
}
}
for (parameter in parameters) {
if (!result.containsKey(parameter.original)) {
if (parameter.hasDefaultValue()) {
result[parameter.original] = ResolvedCallArgument.DefaultArgument
}
else if (parameter.isVararg) {
result[parameter.original] = ResolvedCallArgument.VarargArgument(emptyList())
}
else {
addDiagnostic(NoValueForParameter(parameter, descriptor))
}
}
}
}
}
}
class TooManyArguments(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}
class NonVarargSpread (val argument: KotlinCallArgument, val parameterDescriptor: ValueParameterDescriptor) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentSpread(argument, this)
}
class MixingNamedAndPositionArguments(val argument: KotlinCallArgument) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}
class NamedArgumentNotAllowed(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this)
}
class NameNotFound(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this)
}
class NoValueForParameter(val parameterDescriptor: ValueParameterDescriptor,
val descriptor: CallableDescriptor) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCall(this)
}
class ArgumentPassedTwice(val argument: KotlinCallArgument,
val parameterDescriptor: ValueParameterDescriptor,
val firstOccurrence: ResolvedCallArgument) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this)
}
class VarargArgumentOutsideParentheses(
val argument: KotlinCallArgument,
val parameterDescriptor: ValueParameterDescriptor) :
KotlinCallDiagnostic(INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}
class NameForAmbiguousParameter(
val argument: KotlinCallArgument,
val parameterDescriptor: ValueParameterDescriptor,
val overriddenParameterWithOtherName: ValueParameterDescriptor
) : KotlinCallDiagnostic(CONVENTION_ERROR) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this)
}
class NamedArgumentReference(
val argument: KotlinCallArgument,
val parameterDescriptor: ValueParameterDescriptor
) : KotlinCallDiagnostic(RESOLVED) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this)
}
val ValueParameterDescriptor.isVararg: Boolean get() = varargElementType != null
fun ValueParameterDescriptor.getOverriddenParameterWithOtherName() = overriddenDescriptors.firstOrNull {
it.containingDeclaration.hasStableParameterNames() && it.name != name
}
@@ -0,0 +1,119 @@
/*
* 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.ReflectionTypes
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.upperIfFlexible
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
class CallableReferenceResolver(
val reflectionTypes: ReflectionTypes,
val argumentsToParametersMapper: ArgumentsToParametersMapper
) {
fun resolvePropertyReference(
descriptor: PropertyDescriptor,
propertyReference: ChosenCallableReferenceDescriptor,
outerCall: KotlinCall,
scopeOwnerDescriptor: DeclarationDescriptor
): ResolvedPropertyReference {
val mutable = descriptor.isVar && run {
val setter = descriptor.setter
setter == null || Visibilities.isVisible(propertyReference.candidate.dispatchReceiver?.receiverValue, setter, scopeOwnerDescriptor)
}
val reflectionType = reflectionTypes.getKPropertyType(Annotations.EMPTY, listOfNotNull(propertyReference.dispatchNotBoundReceiver,
propertyReference.extensionNotBoundReceiver), descriptor.type.unwrap(), mutable)
return ResolvedPropertyReference(outerCall, propertyReference, reflectionType)
}
private fun createFakeArgumentsAndMapArguments(
functionReference: ChosenCallableReferenceDescriptor,
argumentCount: Int?
): Pair<List<UnwrappedType>, ArgumentsToParametersMapper.ArgumentMapping?> {
if (argumentCount == null) {
return functionReference.candidate.descriptor.valueParameters.map { it.varargElementType?.unwrap() ?: it.type.unwrap() } to null
}
val fakeArguments = (0..(argumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(functionReference, it) }
val argumentsToParametersMapping = argumentsToParametersMapper.mapArguments(fakeArguments, null, functionReference.candidate.descriptor)
val parameters = Array<UnwrappedType?>(argumentCount) { null }
for ((parameter, resolvedArgument) in argumentsToParametersMapping.parameterToCallArgumentMap) {
for (argument in resolvedArgument.arguments) {
val index = (argument as FakeKotlinCallArgumentForCallableReference).index
parameters[index] = parameter.type.unwrap()
}
}
return parameters.map { it ?: ErrorUtils.createErrorType("Wrong parameters mapping") } to argumentsToParametersMapping
}
fun resolveFunctionReference(
functionReference: ChosenCallableReferenceDescriptor,
outerCall: KotlinCall,
expectedType: UnwrappedType
): ResolvedFunctionReference {
val functionType =
if (expectedType.isFunctionType) {
expectedType
}
else if (ReflectionTypes.isNumberedKFunction(expectedType)) {
expectedType.immediateSupertypes().first { it.isFunctionType }
}
else {
null
}
val parameterTypes = ArrayList<UnwrappedType>(functionType?.arguments?.size ?: 2)
parameterTypes.addIfNotNull(functionReference.dispatchNotBoundReceiver)
parameterTypes.addIfNotNull(functionReference.extensionNotBoundReceiver)
// (A, B, C) -> Int if A -- receiver, then B & C -- parameters
// here parameterTypes contains only receivers, all parameters will be added later
val argumentCount = functionType?.arguments?.let { it.size - parameterTypes.size - 1 }?.takeIf { it >= 0 }
val (parameters, mapping) = createFakeArgumentsAndMapArguments(functionReference, argumentCount)
parameterTypes.addAll(parameters)
val unitExpectedType = functionType?.let(KotlinType::getReturnTypeFromFunctionType)?.takeIf { it.upperIfFlexible().isUnit() }
// coercion to unit
val returnType = unitExpectedType ?: functionReference.candidate.descriptor.returnType
?: ErrorUtils.createErrorType("Error return type")
val kFunctionType = reflectionTypes.getKFunctionType(Annotations.EMPTY, null, parameterTypes, null, returnType, expectedType.builtIns)
return ResolvedFunctionReference(outerCall, functionReference, kFunctionType, mapping)
}
}
@@ -0,0 +1,299 @@
/*
* 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.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
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.LambdaTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import java.lang.UnsupportedOperationException
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))
diagnostics.addIfNotNull(diagnostic)
if (diagnostic != null && !diagnostic.candidateApplicability.isSuccess) break
}
}
return diagnostics
}
fun checkArgument(
callContext: KotlinCallContext,
kotlinCall: KotlinCall,
csBuilder: ConstraintSystemBuilder,
argument: KotlinCallArgument,
expectedType: UnwrappedType
): 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 -> processCallableReferenceArgument(callContext, kotlinCall, csBuilder, argument, expectedType)
else -> error("Incorrect argument type: $argument, ${argument.javaClass.canonicalName}.")
}
}
inline fun computeParameterTypes(
argument: LambdaKotlinCallArgument,
expectedType: UnwrappedType,
createFreshType: () -> UnwrappedType
): List<UnwrappedType> {
argument.parametersTypes?.map { it ?: createFreshType() } ?.let { return it }
if (expectedType.isFunctionType) {
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.isExtensionFunctionType) return createFreshType()
return null
}
inline fun computeReturnType(
argument: LambdaKotlinCallArgument,
createFreshType: () -> UnwrappedType
) : UnwrappedType {
if (argument is FunctionExpression) return argument.receiverType ?: createFreshType()
return createFreshType()
}
fun processLambdaArgument(
kotlinCall: KotlinCall,
csBuilder: ConstraintSystemBuilder,
argument: LambdaKotlinCallArgument,
expectedType: UnwrappedType
): KotlinCallDiagnostic? {
// initial checks
if (expectedType.isFunctionType) {
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.isExtensionFunctionType) return UnexpectedReceiver(argument)
if (argument.receiverType == null && expectedType.isExtensionFunctionType) 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 resolvedArgument = ResolvedLambdaArgument(kotlinCall, argument, freshVariables, receiver, parameters, returnType)
freshVariables.forEach(csBuilder::registerVariable)
csBuilder.addSubtypeConstraint(resolvedArgument.type, expectedType, ArgumentConstraintPosition(argument))
csBuilder.addLambdaArgument(resolvedArgument)
return null
}
fun processCallableReferenceArgument(
callContext: KotlinCallContext,
kotlinCall: KotlinCall,
csBuilder: ConstraintSystemBuilder,
argument: CallableReferenceKotlinCallArgument,
expectedType: UnwrappedType
): KotlinCallDiagnostic? {
val position = ArgumentConstraintPosition(argument)
if (argument !is ChosenCallableReferenceDescriptor) {
val lhsType = argument.lhsType
if (lhsType != null) {
// todo: case with two receivers
val expectedReceiverType = expectedType.supertypes().firstOrNull { it.isFunctionType }?.arguments?.first()?.type?.unwrap()
if (expectedReceiverType != null) {
// (lhsType) -> .. <: (expectedReceiverType) -> ... => expectedReceiverType <: lhsType
csBuilder.addSubtypeConstraint(expectedReceiverType, lhsType, position)
}
}
return null
}
val descriptor = argument.candidate.descriptor
when (descriptor) {
is FunctionDescriptor -> {
// todo store resolved
val resolvedFunctionReference = callContext.callableReferenceResolver.resolveFunctionReference(
argument, kotlinCall, expectedType)
csBuilder.addSubtypeConstraint(resolvedFunctionReference.reflectionType, expectedType, position)
return resolvedFunctionReference.argumentsMapping?.diagnostics?.let {
ErrorCallableMapping(resolvedFunctionReference)
}
}
is PropertyDescriptor -> {
// todo store resolved
val resolvedPropertyReference = callContext.callableReferenceResolver.resolvePropertyReference(descriptor,
argument, kotlinCall, callContext.scopeTower.lexicalScope.ownerDescriptor)
csBuilder.addSubtypeConstraint(resolvedPropertyReference.reflectionType, expectedType, position)
}
else -> throw UnsupportedOperationException("Callable reference resolved to an unsupported descriptor: $descriptor")
}
return null
}
}
internal fun checkExpressionArgument(
csBuilder: ConstraintSystemBuilder,
expressionArgument: ExpressionKotlinCallArgument,
expectedType: UnwrappedType,
isReceiver: Boolean
): KotlinCallDiagnostic? {
// todo run this approximation only once for call
val argumentType = expressionArgument.stableType
fun unstableSmartCastOrSubtypeError(
unstableType: UnwrappedType?, expectedType: UnwrappedType, position: ArgumentConstraintPosition
): 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 = ArgumentConstraintPosition(expressionArgument)
if (expressionArgument.isSafeCall) {
if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) {
return unstableSmartCastOrSubtypeError(expressionArgument.unstableType, expectedNullableType, position)?.let { return it }
}
return null
}
if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) {
if (!isReceiver) {
return unstableSmartCastOrSubtypeError(expressionArgument.unstableType, expectedType, position)?.let { return it }
}
val unstableType = expressionArgument.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
}
// if expression is not stable and has smart casts, then we create this type
private val ExpressionKotlinCallArgument.unstableType: UnwrappedType?
get() {
if (receiver.isStable || receiver.possibleTypes.isEmpty()) return null
return intersectWrappedTypes(receiver.possibleTypes + receiver.receiverValue.type)
}
// with all smart casts if stable
internal val ExpressionKotlinCallArgument.stableType: UnwrappedType
get() {
if (!receiver.isStable || receiver.possibleTypes.isEmpty()) return receiver.receiverValue.type.unwrap()
return intersectWrappedTypes(receiver.possibleTypes + receiver.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()
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2016 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.CallableDescriptor
import org.jetbrains.kotlin.resolve.calls.model.KotlinCall
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument
import org.jetbrains.kotlin.types.UnwrappedType
interface IsDescriptorFromSourcePredicate: (CallableDescriptor) -> Boolean
interface CommonSupertypeCalculator: (Collection<UnwrappedType>) -> UnwrappedType
interface LambdaAnalyzer {
fun analyzeAndGetRelatedCalls(
topLevelCall: KotlinCall,
lambdaArgument: LambdaKotlinCallArgument,
receiverType: UnwrappedType?,
parameters: List<UnwrappedType>,
expectedReturnType: UnwrappedType? // null means, that return type is not proper i.e. it depends on some type variables
): List<KotlinCallArgument>
}
@@ -0,0 +1,265 @@
/*
* 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.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector
import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator
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
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateStatus
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
class KotlinCallCompleter(
val resultTypeResolver: ResultTypeResolver,
val fixationOrderCalculator: FixationOrderCalculator
) {
interface Context {
val innerCalls: List<ResolvedKotlinCall.OnlyResolvedKotlinCall>
val hasContradiction: Boolean
fun buildCurrentSubstitutor(): TypeSubstitutor
fun buildResultingSubstitutor(): TypeSubstitutor
val lambdaArguments: List<ResolvedLambdaArgument>
// type can be proper if it not contains not fixed type variables
fun canBeProper(type: UnwrappedType): Boolean
fun asFixationOrderCalculatorContext(): FixationOrderCalculator.Context
fun asResultTypeResolverContext(): ResultTypeResolver.Context
// mutable operations
fun asConstraintInjectorContext(): ConstraintInjector.Context
fun addError(error: KotlinCallDiagnostic)
fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType)
fun getBuilder(): ConstraintSystemBuilder
}
fun transformWhenAmbiguity(candidate: KotlinResolutionCandidate): ResolvedKotlinCall =
toCompletedBaseResolvedCall(candidate.lastCall.constraintSystem.asCallCompleterContext(), candidate)
fun completeCallIfNecessary(
candidate: KotlinResolutionCandidate,
expectedType: UnwrappedType?,
lambdaAnalyzer: LambdaAnalyzer
): ResolvedKotlinCall {
val topLevelCall =
if (candidate is VariableAsFunctionKotlinResolutionCandidate) {
candidate.invokeCandidate
}
else {
candidate as SimpleKotlinResolutionCandidate
}
if (topLevelCall.prepareForCompletion(expectedType)) {
val c = candidate.lastCall.constraintSystem.asCallCompleterContext()
topLevelCall.competeCall(c, lambdaAnalyzer)
return toCompletedBaseResolvedCall(c, candidate)
}
return ResolvedKotlinCall.OnlyResolvedKotlinCall(candidate)
}
private fun toCompletedBaseResolvedCall(
c: Context,
candidate: KotlinResolutionCandidate
): ResolvedKotlinCall.CompletedResolvedKotlinCall {
val currentSubstitutor = c.buildResultingSubstitutor()
val completedCall = candidate.toCompletedCall(currentSubstitutor)
val competedCalls = c.innerCalls.map {
it.candidate.toCompletedCall(currentSubstitutor)
}
return ResolvedKotlinCall.CompletedResolvedKotlinCall(completedCall, competedCalls)
}
private fun KotlinResolutionCandidate.toCompletedCall(substitutor: TypeSubstitutor): CompletedKotlinCall {
if (this is VariableAsFunctionKotlinResolutionCandidate) {
val variable = resolvedVariable.toCompletedCall(substitutor)
val invoke = invokeCandidate.toCompletedCall(substitutor)
return CompletedKotlinCall.VariableAsFunction(kotlinCall, variable, invoke)
}
return (this as SimpleKotlinResolutionCandidate).toCompletedCall(substitutor)
}
private fun SimpleKotlinResolutionCandidate.toCompletedCall(substitutor: TypeSubstitutor): CompletedKotlinCall.Simple {
val resultingDescriptor = if (descriptorWithFreshTypes.typeParameters.isNotEmpty()) descriptorWithFreshTypes.substitute(substitutor)!! else descriptorWithFreshTypes
val typeArguments = descriptorWithFreshTypes.typeParameters.map { substitutor.safeSubstitute(it.defaultType, Variance.INVARIANT).unwrap() }
val status = computeStatus(this, resultingDescriptor)
return CompletedKotlinCall.Simple(kotlinCall, candidateDescriptor, resultingDescriptor, status, explicitReceiverKind,
dispatchReceiverArgument?.receiver, extensionReceiver?.receiver, typeArguments, argumentMappingByOriginal)
}
private fun computeStatus(candidate: SimpleKotlinResolutionCandidate, resultingDescriptor: CallableDescriptor): ResolutionCandidateStatus {
val smartCasts = reportSmartCasts(candidate, resultingDescriptor).takeIf { it.isNotEmpty() } ?: return candidate.status
return ResolutionCandidateStatus(candidate.status.diagnostics + smartCasts)
}
private fun createSmartCastDiagnostic(argument: KotlinCallArgument, expectedResultType: UnwrappedType): SmartCastDiagnostic? {
if (argument !is ExpressionKotlinCallArgument) return null
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argument.receiver.receiverValue.type, expectedResultType)) {
return SmartCastDiagnostic(argument, expectedResultType.unwrap())
}
return null
}
private fun reportSmartCastOnReceiver(
candidate: KotlinResolutionCandidate,
receiver: SimpleKotlinCallArgument?,
parameter: ReceiverParameterDescriptor?
): SmartCastDiagnostic? {
if (receiver == null || parameter == null) return null
val expectedType = parameter.type.unwrap().let { if (receiver.isSafeCall) it.makeNullableAsSpecified(true) else it }
val smartCastDiagnostic = createSmartCastDiagnostic(receiver, expectedType) ?: return null
// todo may be we have smart cast to Int?
return smartCastDiagnostic.takeIf {
candidate.status.diagnostics.filterIsInstance<UnsafeCallError>().none {
it.receiver == receiver
}
&&
candidate.status.diagnostics.filterIsInstance<UnstableSmartCast>().none {
it.expressionArgument == receiver
}
}
}
private fun reportSmartCasts(candidate: SimpleKotlinResolutionCandidate, resultingDescriptor: CallableDescriptor): List<KotlinCallDiagnostic> = SmartList<KotlinCallDiagnostic>().apply {
addIfNotNull(reportSmartCastOnReceiver(candidate, candidate.extensionReceiver, resultingDescriptor.extensionReceiverParameter))
addIfNotNull(reportSmartCastOnReceiver(candidate, candidate.dispatchReceiverArgument, resultingDescriptor.dispatchReceiverParameter))
for (parameter in resultingDescriptor.valueParameters) {
for (argument in candidate.argumentMappingByOriginal[parameter.original]?.arguments ?: continue) {
val smartCastDiagnostic = createSmartCastDiagnostic(argument, argument.getExpectedType(parameter)) ?: continue
val thereIsUnstableSmartCastError = candidate.status.diagnostics.filterIsInstance<UnstableSmartCast>().any {
it.expressionArgument == argument
}
if (!thereIsUnstableSmartCastError) {
add(smartCastDiagnostic)
}
}
}
}
// true if we should complete this call
private fun SimpleKotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): Boolean {
val returnType = descriptorWithFreshTypes.returnType?.unwrap() ?: return false
if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) {
csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(kotlinCall))
}
return expectedType != null || csBuilder.isProperType(returnType)
}
private fun SimpleKotlinResolutionCandidate.competeCall(c: Context, lambdaAnalyzer: LambdaAnalyzer) {
while (!oneStepToEndOrLambda(c, lambdaAnalyzer)) {
// do nothing -- be happy
}
}
// true if it is the end (happy or not)
private fun SimpleKotlinResolutionCandidate.oneStepToEndOrLambda(c: Context, lambdaAnalyzer: LambdaAnalyzer): Boolean {
if (c.hasContradiction) return true
val lambda = c.lambdaArguments.find { canWeAnalyzeIt(c, it) }
if (lambda != null) {
analyzeLambda(c, lambdaAnalyzer, callContext, kotlinCall, lambda)
return false
}
val completionOrder = fixationOrderCalculator.computeCompletionOrder(c.asFixationOrderCalculatorContext(), descriptorWithFreshTypes.returnTypeOrNothing)
for ((variableWithConstraints, direction) in completionOrder) {
if (c.hasContradiction) return true
val variable = variableWithConstraints.typeVariable
val resultType = resultTypeResolver.findResultType(c.asResultTypeResolverContext(), variableWithConstraints, direction)
if (resultType == null) {
c.addError(NotEnoughInformationForTypeParameter(variable))
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, lambdaAnalyzer, callContext, kotlinCall, resolvedLambda)
return false
}
}
}
return true
}
private fun analyzeLambda(c: Context, lambdaAnalyzer: LambdaAnalyzer, topLevelCallContext: KotlinCallContext, topLevelCall: KotlinCall, lambda: ResolvedLambdaArgument) {
val currentSubstitutor = c.buildCurrentSubstitutor()
fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type, Variance.INVARIANT).unwrap()
val receiver = lambda.receiver?.let(::substitute)
val parameters = lambda.parameters.map(::substitute)
val expectedType = lambda.returnType.takeIf { c.canBeProper(it) }?.let(::substitute)
val callsFromLambda = lambdaAnalyzer.analyzeAndGetRelatedCalls(topLevelCall, lambda.argument, receiver, parameters, expectedType)
lambda.analyzed = true
for (innerCall in callsFromLambda) {
// todo strange code -- why top-level kotlinCall? may be it isn't right outer call
CheckArguments.checkArgument(topLevelCallContext, topLevelCall, c.getBuilder(), innerCall, lambda.returnType)
}
// when (innerCall) {
// is ResolvedKotlinCall.CompletedResolvedKotlinCall -> {
// val returnType = innerCall.completedCall.lastCall.resultingDescriptor.returnTypeOrNothing
// constraintInjector.addInitialSubtypeConstraint(injectorContext, returnType, lambda.returnType, position)
// }
// is ResolvedKotlinCall.OnlyResolvedKotlinCall -> {
// // todo register call
// val returnType = innerCall.candidate.lastCall.descriptorWithFreshTypes.returnTypeOrNothing
// c.addInnerCall(innerCall)
// constraintInjector.addInitialSubtypeConstraint(injectorContext, returnType, lambda.returnType, position)
// }
// }
}
private fun canWeAnalyzeIt(c: Context, lambda: ResolvedLambdaArgument): Boolean {
if (lambda.analyzed) return false
lambda.receiver?.let {
if (!c.canBeProper(it)) return false
}
return lambda.parameters.all { c.canBeProper(it) }
}
}
class SmartCastDiagnostic(val expressionArgument: ExpressionKotlinCallArgument, val smartCastType: UnwrappedType): KotlinCallDiagnostic(ResolutionCandidateApplicability.RESOLVED) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(expressionArgument, this)
}
@@ -0,0 +1,85 @@
/*
* Copyright 2010-2016 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.KotlinBuiltIns
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector
import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver
import org.jetbrains.kotlin.resolve.calls.inference.components.SimpleConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature.Companion.argumentValueType
import org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
import org.jetbrains.kotlin.types.KotlinType
import java.util.*
class NewOverloadingConflictResolver(
builtIns: KotlinBuiltIns,
specificityComparator: TypeSpecificityComparator,
isDescriptorFromSourcePredicate: IsDescriptorFromSourcePredicate,
constraintInjector: ConstraintInjector,
typeResolver: ResultTypeResolver
) : OverloadingConflictResolver<KotlinResolutionCandidate>(
builtIns,
specificityComparator,
{
(it as? VariableAsFunctionKotlinResolutionCandidate)?.invokeCandidate?.descriptorWithFreshTypes ?:
(it as SimpleKotlinResolutionCandidate).descriptorWithFreshTypes
},
{ SimpleConstraintSystemImpl(constraintInjector, typeResolver) },
Companion::createFlatSignature,
{ (it as? VariableAsFunctionKotlinResolutionCandidate)?.resolvedVariable },
isDescriptorFromSourcePredicate
) {
companion object {
private fun createFlatSignature(candidate: KotlinResolutionCandidate): FlatSignature<KotlinResolutionCandidate> {
val simpleCandidate = (candidate as? VariableAsFunctionKotlinResolutionCandidate)?.invokeCandidate ?: (candidate as SimpleKotlinResolutionCandidate)
val originalDescriptor = simpleCandidate.descriptorWithFreshTypes.original
val originalValueParameters = originalDescriptor.valueParameters
var numDefaults = 0
val valueArgumentToParameterType = HashMap<KotlinCallArgument, KotlinType>()
for ((valueParameter, resolvedValueArgument) in simpleCandidate.argumentMappingByOriginal) {
if (resolvedValueArgument is ResolvedCallArgument.DefaultArgument) {
numDefaults++
}
else {
val originalValueParameter = originalValueParameters[valueParameter.index]
val parameterType = originalValueParameter.argumentValueType
for (valueArgument in resolvedValueArgument.arguments) {
valueArgumentToParameterType[valueArgument] = parameterType
}
}
}
return FlatSignature.create(candidate,
originalDescriptor,
numDefaults,
listOfNotNull(originalDescriptor.extensionReceiverParameter?.type) +
simpleCandidate.kotlinCall.argumentsInParenthesis.map { valueArgumentToParameterType[it] } +
listOfNotNull(simpleCandidate.kotlinCall.externalArgument?.let { valueArgumentToParameterType[it] })
)
}
}
}
@@ -0,0 +1,243 @@
/*
* 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.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments
import org.jetbrains.kotlin.resolve.calls.inference.model.DeclaredUpperBoundConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.ExplicitTypeParameterConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableFromCallableDescriptor
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE
import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError
import org.jetbrains.kotlin.types.IndexedParametersSubstitution
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
internal object CheckVisibility : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
val receiverValue = dispatchReceiverArgument?.receiver?.receiverValue
val invisibleMember = Visibilities.findInvisibleMember(receiverValue, candidateDescriptor, containingDescriptor) ?: return emptyList()
if (dispatchReceiverArgument is ExpressionKotlinCallArgument) {
val smartCastReceiver = getReceiverValueWithSmartCast(receiverValue, dispatchReceiverArgument.stableType)
if (Visibilities.findInvisibleMember(smartCastReceiver, candidateDescriptor, containingDescriptor) == null) {
return listOf(SmartCastDiagnostic(dispatchReceiverArgument, dispatchReceiverArgument.stableType))
}
}
return listOf(VisibilityError(invisibleMember))
}
private val SimpleKotlinResolutionCandidate.containingDescriptor: DeclarationDescriptor get() = callContext.scopeTower.lexicalScope.ownerDescriptor
}
internal object MapTypeArguments : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
typeArgumentMappingByOriginal = callContext.typeArgumentsToParametersMapper.mapTypeArguments(kotlinCall, candidateDescriptor.original)
return typeArgumentMappingByOriginal.diagnostics
}
}
internal object NoTypeArguments : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
assert(kotlinCall.typeArguments.isEmpty()) {
"Variable call cannot has explicit type arguments: ${kotlinCall.typeArguments}. Call: $kotlinCall"
}
typeArgumentMappingByOriginal = NoExplicitArguments
return typeArgumentMappingByOriginal.diagnostics
}
}
internal object MapArguments : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
val mapping = callContext.argumentsToParametersMapper.mapArguments(kotlinCall, candidateDescriptor.original)
argumentMappingByOriginal = mapping.parameterToCallArgumentMap
return mapping.diagnostics
}
}
internal object NoArguments : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
assert(kotlinCall.argumentsInParenthesis.isEmpty()) {
"Variable call cannot has arguments: ${kotlinCall.argumentsInParenthesis}. Call: $kotlinCall"
}
assert(kotlinCall.externalArgument == null) {
"Variable call cannot has external argument: ${kotlinCall.externalArgument}. Call: $kotlinCall"
}
argumentMappingByOriginal = emptyMap()
return emptyList()
}
}
internal object CreteDescriptorWithFreshTypeVariables : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
if (candidateDescriptor.typeParameters.isEmpty()) {
descriptorWithFreshTypes = candidateDescriptor
return emptyList()
}
val typeParameters = candidateDescriptor.typeParameters
val freshTypeVariables = typeParameters.map { TypeVariableFromCallableDescriptor(kotlinCall, it) }
val toFreshVariables = IndexedParametersSubstitution(typeParameters,
freshTypeVariables.map { it.defaultType.asTypeProjection() }).buildSubstitutor()
for (freshVariable in freshTypeVariables) {
csBuilder.registerVariable(freshVariable)
}
for (index in typeParameters.indices) {
val typeParameter = typeParameters[index]
val freshVariable = freshTypeVariables[index]
val position = DeclaredUpperBoundConstraintPosition(typeParameter)
for (upperBound in typeParameter.upperBounds) {
csBuilder.addSubtypeConstraint(freshVariable.defaultType, upperBound.unwrap().substitute(toFreshVariables), position)
}
}
// bad function -- error on declaration side
if (csBuilder.hasContradiction) {
descriptorWithFreshTypes = candidateDescriptor
return emptyList()
}
// optimization
if (typeArgumentMappingByOriginal == NoExplicitArguments) {
descriptorWithFreshTypes = candidateDescriptor.safeSubstitute(toFreshVariables)
csBuilder.simplify().let { assert(it.isEmpty) { "Substitutor should be empty: $it, call: $kotlinCall" } }
return emptyList()
}
// add explicit type parameter
for (index in typeParameters.indices) {
val typeParameter = typeParameters[index]
val typeArgument = typeArgumentMappingByOriginal.getTypeArgument(typeParameter)
if (typeArgument is SimpleTypeArgument) {
val freshVariable = freshTypeVariables[index]
csBuilder.addEqualityConstraint(freshVariable.defaultType, typeArgument.type, ExplicitTypeParameterConstraintPosition(typeArgument))
}
else {
assert(typeArgument == TypeArgumentPlaceholder) {
"Unexpected typeArgument: $typeArgument, ${typeArgument.javaClass.canonicalName}"
}
}
}
/**
* Note: here we can fix also placeholders arguments.
* Example:
* fun <X : Array<Y>, Y> foo()
*
* foo<Array<String>, *>()
*/
val toFixedTypeParameters = csBuilder.simplify()
// todo optimize -- composite substitutions before run safeSubstitute
descriptorWithFreshTypes = candidateDescriptor.safeSubstitute(toFreshVariables).safeSubstitute(toFixedTypeParameters)
return emptyList()
}
}
internal object CheckExplicitReceiverKindConsistency : ResolutionPart {
private fun SimpleKotlinResolutionCandidate.hasError(): Nothing =
error("Inconsistent call: $kotlinCall. \n" +
"Candidate: $candidateDescriptor, explicitReceiverKind: $explicitReceiverKind.\n" +
"Explicit receiver: ${kotlinCall.explicitReceiver}, dispatchReceiverForInvokeExtension: ${kotlinCall.dispatchReceiverForInvokeExtension}")
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
when (explicitReceiverKind) {
NO_EXPLICIT_RECEIVER -> if (kotlinCall.explicitReceiver is SimpleKotlinCallArgument || kotlinCall.dispatchReceiverForInvokeExtension != null) hasError()
DISPATCH_RECEIVER, EXTENSION_RECEIVER -> if (kotlinCall.explicitReceiver == null || kotlinCall.dispatchReceiverForInvokeExtension != null) hasError()
BOTH_RECEIVERS -> if (kotlinCall.explicitReceiver == null || kotlinCall.dispatchReceiverForInvokeExtension == null) hasError()
}
return emptyList()
}
}
internal object CheckReceivers : ResolutionPart {
private fun SimpleKotlinResolutionCandidate.checkReceiver(
receiverArgument: SimpleKotlinCallArgument?,
receiverParameter: ReceiverParameterDescriptor?
): KotlinCallDiagnostic? {
if ((receiverArgument == null) != (receiverParameter == null)) {
error("Inconsistency receiver state for call $kotlinCall and candidate descriptor: $candidateDescriptor")
}
if (receiverArgument == null || receiverParameter == null) return null
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)
}
}
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))
}
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()
class UnstableSmartCast(val expressionArgument: ExpressionKotlinCallArgument, val targetType: UnwrappedType) :
KotlinCallDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(expressionArgument, this)
}
class UnsafeCallError(val receiver: SimpleKotlinCallArgument) : KotlinCallDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallReceiver(receiver, this)
}
class ExpectedLambdaParametersCountMismatch(
val lambdaArgument: LambdaKotlinCallArgument,
val expected: Int,
val actual: Int
) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(lambdaArgument, this)
}
class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this)
}
class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this)
}
class ErrorCallableMapping(val functionReference: ResolvedFunctionReference) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionReference.argument, this)
}
@@ -0,0 +1,44 @@
/*
* 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.FunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolutionPart
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.tower.InfixCallNoInfixModifier
import org.jetbrains.kotlin.resolve.calls.tower.InvokeConventionCallNoOperatorModifier
object CheckInfixResolutionPart : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
if (kotlinCall.isInfixCall && (candidateDescriptor !is FunctionDescriptor || !candidateDescriptor.isInfix)) {
return listOf(InfixCallNoInfixModifier)
}
return emptyList()
}
}
object CheckOperatorResolutionPart : ResolutionPart {
override fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic> {
if (kotlinCall.isOperatorCall && (candidateDescriptor !is FunctionDescriptor || !candidateDescriptor.isOperator)) {
return listOf(InvokeConventionCallNoOperatorModifier)
}
return emptyList()
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2016 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.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
class TypeArgumentsToParametersMapper {
sealed class TypeArgumentsMapping(val diagnostics: List<KotlinCallDiagnostic>) {
abstract fun getTypeArgument(typeParameterDescriptor: TypeParameterDescriptor): TypeArgument
object NoExplicitArguments : TypeArgumentsMapping(emptyList()) {
override fun getTypeArgument(typeParameterDescriptor: TypeParameterDescriptor): TypeArgument {
return TypeArgumentPlaceholder
}
}
class TypeArgumentsMappingImpl(
diagnostics: List<KotlinCallDiagnostic>,
private val typeParameterToArgumentMap: Map<TypeParameterDescriptor, TypeArgument>
): TypeArgumentsMapping(diagnostics) {
override fun getTypeArgument(typeParameterDescriptor: TypeParameterDescriptor): TypeArgument {
return typeParameterToArgumentMap[typeParameterDescriptor] ?:
error("No argument for parameter: $typeParameterDescriptor. Reported diagnostics: $diagnostics")
}
}
}
fun mapTypeArguments(call: KotlinCall, descriptor: CallableDescriptor): TypeArgumentsMapping {
if (call.typeArguments.isEmpty()) {
return TypeArgumentsMapping.NoExplicitArguments
}
if (call.typeArguments.size != descriptor.typeParameters.size) {
return TypeArgumentsMapping.TypeArgumentsMappingImpl(
listOf(WrongCountOfTypeArguments(descriptor, call.typeArguments.size)), emptyMap())
}
else {
val typeParameterToArgumentMap = descriptor.typeParameters.zip(call.typeArguments).associate { it }
return TypeArgumentsMapping.TypeArgumentsMappingImpl(listOf(), typeParameterToArgumentMap)
}
}
}
class WrongCountOfTypeArguments(val descriptor: CallableDescriptor, val currentCount: Int) :
KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.onTypeArguments(this)
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2016 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.inference
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
interface ConstraintSystemBuilder {
val hasContradiction: Boolean
fun registerVariable(variable: NewTypeVariable)
fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition)
fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition)
fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall)
fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument)
fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean
fun isProperType(type: UnwrappedType): Boolean
/**
* This function removes variables for which we know exact type.
* @return substitutor from typeVariable to result
*/
fun simplify(): TypeSubstitutor
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2016 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.inference
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
fun ConstraintStorage.buildCurrentSubstitutor() = TypeConstructorSubstitution.createByConstructorsMap(fixedTypeVariables.entries.associate {
it.key to it.value.asTypeProjection()
}).buildSubstitutor()
val CallableDescriptor.returnTypeOrNothing: UnwrappedType
get() {
returnType?.let { return it.unwrap() }
return builtIns.nothingType
}
fun TypeSubstitutor.substitute(type: UnwrappedType): UnwrappedType = safeSubstitute(type, Variance.INVARIANT).unwrap()
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2016 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.inference
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
interface NewConstraintSystem {
val diagnostics: List<KotlinCallDiagnostic>
fun getBuilder(): ConstraintSystemBuilder
// after this method we shouldn't mutate system via ConstraintSystemBuilder
fun asReadOnlyStorage(): ConstraintStorage
fun asCallCompleterContext(): KotlinCallCompleter.Context
}
@@ -0,0 +1,161 @@
/*
* Copyright 2010-2016 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.inference.components
import org.jetbrains.kotlin.types.TypeApproximator
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.CaptureStatus
import org.jetbrains.kotlin.types.checker.NewCapturedType
import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
// todo problem: intersection types in constrains: A <: Number, B <: Inv<A & Any> =>? B <: Inv<out Number & Any>
class ConstraintIncorporator(val typeApproximator: TypeApproximator) {
interface Context {
val allTypeVariablesWithConstraints: Collection<VariableWithConstraints>
// if such type variable is fixed then it is error
fun getTypeVariable(typeConstructor: TypeConstructor): NewTypeVariable?
fun getConstraintsForVariable(typeVariable: NewTypeVariable): Collection<Constraint>
fun addNewIncorporatedConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: IncorporationConstraintPosition)
}
// \alpha is typeVariable, \beta -- other type variable registered in ConstraintStorage
fun incorporate(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) {
// we shouldn't incorporate recursive constraint -- It is too dangerous
if (constraint.type.contains { it.constructor == typeVariable.freshTypeConstructor }) return
directWithVariable(c, typeVariable, constraint, position)
otherInsideMyConstraint(c, typeVariable, constraint, position)
insideOtherConstraint(c, typeVariable, constraint, position)
}
// A <:(=) \alpha <:(=) B => A <: B
private fun directWithVariable(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) {
// \alpha <: constraint.type
if (constraint.kind != ConstraintKind.LOWER) {
c.getConstraintsForVariable(typeVariable).forEach {
if (it.kind != ConstraintKind.UPPER) {
c.addNewIncorporatedConstraint(it.type, constraint.type, position)
}
}
}
// constraint.type <: \alpha
if (constraint.kind != ConstraintKind.UPPER) {
c.getConstraintsForVariable(typeVariable).forEach {
if (it.kind != ConstraintKind.LOWER) {
c.addNewIncorporatedConstraint(constraint.type, it.type, position)
}
}
}
}
// \alpha <: Inv<\beta>, \beta <: Number => \alpha <: Inv<out Number>
private fun otherInsideMyConstraint(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) {
val otherInMyConstraint = SmartSet.create<NewTypeVariable>()
constraint.type.contains {
otherInMyConstraint.addIfNotNull(c.getTypeVariable(it.constructor))
false
}
for (otherTypeVariable in otherInMyConstraint) {
// to avoid ConcurrentModificationException
val otherConstraints = ArrayList(c.getConstraintsForVariable(otherTypeVariable))
for (otherConstraint in otherConstraints) {
generateNewConstraint(c, typeVariable, constraint, otherTypeVariable, otherConstraint, position)
}
}
}
// \alpha <: Number, \beta <: Inv<\alpha> => \beta <: Inv<out Number>
private fun insideOtherConstraint(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) {
for (typeVariableWithConstraint in c.allTypeVariablesWithConstraints) {
val constraintsWhichConstraintMyVariable = typeVariableWithConstraint.constraints.filter {
it.type.contains { it.constructor == typeVariable.freshTypeConstructor }
}
constraintsWhichConstraintMyVariable.forEach {
generateNewConstraint(c, typeVariableWithConstraint.typeVariable, it, typeVariable, constraint, position)
}
}
}
private fun generateNewConstraint(
c: Context,
targetVariable: NewTypeVariable,
baseConstraint: Constraint,
otherVariable: NewTypeVariable,
otherConstraint: Constraint,
position: IncorporationConstraintPosition
) {
val typeForApproximation = when (otherConstraint.kind) {
ConstraintKind.EQUALITY -> {
baseConstraint.type.substitute(otherVariable, otherConstraint.type)
}
ConstraintKind.UPPER -> {
val newCapturedTypeConstructor = NewCapturedTypeConstructor(TypeProjectionImpl(Variance.OUT_VARIANCE, otherConstraint.type),
listOf(otherConstraint.type))
val temporaryCapturedType = NewCapturedType(CaptureStatus.FOR_INCORPORATION,
newCapturedTypeConstructor,
lowerType = null)
baseConstraint.type.substitute(otherVariable, temporaryCapturedType)
}
ConstraintKind.LOWER -> {
val newCapturedTypeConstructor = NewCapturedTypeConstructor(TypeProjectionImpl(Variance.IN_VARIANCE, otherConstraint.type),
emptyList())
val temporaryCapturedType = NewCapturedType(CaptureStatus.FOR_INCORPORATION,
newCapturedTypeConstructor,
lowerType = otherConstraint.type)
baseConstraint.type.substitute(otherVariable, temporaryCapturedType)
}
}
if (baseConstraint.kind != ConstraintKind.UPPER) {
c.addNewIncorporatedConstraint(approximateCapturedTypes(typeForApproximation, toSuper = false), targetVariable.defaultType, position)
}
if (baseConstraint.kind != ConstraintKind.LOWER) {
c.addNewIncorporatedConstraint(targetVariable.defaultType, approximateCapturedTypes(typeForApproximation, toSuper = true), position)
}
}
private fun UnwrappedType.substitute(typeVariable: NewTypeVariable, value: UnwrappedType): UnwrappedType {
val substitutor = TypeSubstitutor.create(mapOf(typeVariable.freshTypeConstructor to value.asTypeProjection()))
val type = substitutor.substitute(this, Variance.INVARIANT) ?: error("Impossible to substitute in $this: $typeVariable -> $value")
return type.unwrap()
}
private fun approximateCapturedTypes(type: UnwrappedType, toSuper: Boolean): UnwrappedType =
if (toSuper) typeApproximator.approximateToSuperType(type, CapturedTypesApproximatorConfiguration) ?: type
else typeApproximator.approximateToSubType(type, CapturedTypesApproximatorConfiguration) ?: type
private object CapturedTypesApproximatorConfiguration : TypeApproximatorConfiguration.AllFlexibleSameValue() {
override val allFlexible get() = true
override val capturedType get() = { it: NewCapturedType -> it.captureStatus != CaptureStatus.FOR_INCORPORATION }
override val intersection get() = IntersectionStrategy.ALLOWED
override val typeVariable: (TypeVariableTypeConstructor) -> Boolean get() = { true }
}
}
@@ -0,0 +1,172 @@
/*
* Copyright 2010-2016 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.inference.components
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.types.FlexibleType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.checker.CaptureStatus
import org.jetbrains.kotlin.types.checker.NewCapturedType
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.contains
import java.util.*
class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator) {
private val ALLOWED_DEPTH_DELTA_FOR_INCORPORATION = 3
interface Context {
val allTypeVariables: Map<TypeConstructor, NewTypeVariable>
var maxTypeDepthFromInitialConstraints: Int
val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints>
fun addInitialConstraint(initialConstraint: InitialConstraint)
fun addError(error: KotlinCallDiagnostic)
}
fun addInitialSubtypeConstraint(c: Context, lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) {
c.addInitialConstraint(InitialConstraint(lowerType, upperType, ConstraintKind.UPPER, position))
updateAllowedTypeDepth(c, lowerType)
updateAllowedTypeDepth(c, upperType)
addSubTypeConstraintAndIncorporateIt(c, lowerType, upperType, position)
}
fun addInitialEqualityConstraint(c: Context, a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) {
c.addInitialConstraint(InitialConstraint(a, b, ConstraintKind.EQUALITY, position))
updateAllowedTypeDepth(c, a)
updateAllowedTypeDepth(c, b)
addSubTypeConstraintAndIncorporateIt(c, a, b, position)
addSubTypeConstraintAndIncorporateIt(c, b, a, position)
}
private fun addSubTypeConstraintAndIncorporateIt(c: Context, lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) {
val incorporatePosition = IncorporationConstraintPosition(position)
val possibleNewConstraints = Stack<Pair<NewTypeVariable, Constraint>>()
val typeCheckerContext = TypeCheckerContext(c, position, lowerType, upperType, possibleNewConstraints)
typeCheckerContext.runIsSubtypeOf(lowerType, upperType)
while (possibleNewConstraints.isNotEmpty()) {
val (typeVariable, constraint) = possibleNewConstraints.pop()
val constraints = c.notFixedTypeVariables[typeVariable.freshTypeConstructor] ?: typeCheckerContext.fixedTypeVariable(typeVariable)
// it is important, that we add constraint here(not inside TypeCheckerContext), because inside incorporation we read constraints
constraints.addConstraint(constraint)?.let {
constraintIncorporator.incorporate(typeCheckerContext, typeVariable, it, incorporatePosition)
}
}
}
private fun updateAllowedTypeDepth(c: Context, initialType: UnwrappedType) {
c.maxTypeDepthFromInitialConstraints = Math.max(c.maxTypeDepthFromInitialConstraints, initialType.typeDepth())
}
private fun UnwrappedType.typeDepth() =
when (this) {
is SimpleType -> typeDepth()
is FlexibleType -> Math.max(lowerBound.typeDepth(), upperBound.typeDepth())
}
private fun SimpleType.typeDepth(): Int {
val maxInArguments = arguments.asSequence().map {
if (it.isStarProjection) 1 else it.type.unwrap().typeDepth()
}.max() ?: 0
return maxInArguments + 1
}
private fun Context.isAllowedType(type: UnwrappedType) = type.typeDepth() <= maxTypeDepthFromInitialConstraints + ALLOWED_DEPTH_DELTA_FOR_INCORPORATION
private inner class TypeCheckerContext(
val c: Context,
val position: ConstraintPosition,
val baseLowerType: UnwrappedType,
val baseUpperType: UnwrappedType,
val possibleNewConstraints: MutableList<Pair<NewTypeVariable, Constraint>> = ArrayList()
) : TypeCheckerContextForConstraintSystem(), ConstraintIncorporator.Context {
fun runIsSubtypeOf(lowerType: UnwrappedType, upperType: UnwrappedType) {
with(NewKotlinTypeChecker) {
if (!this@TypeCheckerContext.isSubtypeOf(lowerType, upperType)) {
// todo improve error reporting -- add information about base types
c.addError(NewConstraintError(lowerType, upperType, position))
}
}
}
// from TypeCheckerContextForConstraintSystem
override fun isMyTypeVariable(type: SimpleType): Boolean = c.allTypeVariables.containsKey(type.constructor)
override fun addUpperConstraint(typeVariable: TypeConstructor, superType: UnwrappedType) =
addConstraint(typeVariable, superType, ConstraintKind.UPPER)
override fun addLowerConstraint(typeVariable: TypeConstructor, subType: UnwrappedType) =
addConstraint(typeVariable, subType, ConstraintKind.LOWER)
private fun addConstraint(typeVariableConstructor: TypeConstructor, type: UnwrappedType, kind: ConstraintKind) {
val typeVariable = c.allTypeVariables[typeVariableConstructor]
?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}")
if (type.contains {
val captureStatus = (it as? NewCapturedType)?.captureStatus
assert(captureStatus != CaptureStatus.FOR_INCORPORATION) {
"Captured type for incorporation shouldn't escape from incorporation: $type\n" + renderBaseConstraint()
}
captureStatus != null && captureStatus != CaptureStatus.FROM_EXPRESSION
}) {
c.addError(CapturedTypeFromSubtyping(typeVariable, type, position))
return
}
if (!c.isAllowedType(type)) return
val newConstraint = Constraint(kind, type, position)
possibleNewConstraints.add(typeVariable to newConstraint)
}
// from ConstraintIncorporator.Context
override fun addNewIncorporatedConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: IncorporationConstraintPosition) {
if (c.isAllowedType(lowerType) && c.isAllowedType(upperType)) {
runIsSubtypeOf(lowerType, upperType)
}
}
override val allTypeVariablesWithConstraints: Collection<VariableWithConstraints>
get() = c.notFixedTypeVariables.values
override fun getTypeVariable(typeConstructor: TypeConstructor): NewTypeVariable? {
val typeVariable = c.allTypeVariables[typeConstructor]
if (typeVariable != null && !c.notFixedTypeVariables.containsKey(typeConstructor)) {
fixedTypeVariable(typeVariable)
}
return typeVariable
}
override fun getConstraintsForVariable(typeVariable: NewTypeVariable) =
c.notFixedTypeVariables[typeVariable.freshTypeConstructor]?.constraints
?: fixedTypeVariable(typeVariable)
fun fixedTypeVariable(variable: NewTypeVariable): Nothing {
error("Type variable $variable should not be fixed!\n" +
renderBaseConstraint())
}
private fun renderBaseConstraint() = "Base constraint: $baseLowerType <: $baseUpperType from position: $position"
}
}
@@ -0,0 +1,209 @@
/*
* Copyright 2010-2016 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.inference.components
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.LambdaTypeVariable
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.utils.DFS
import org.jetbrains.kotlin.utils.SmartList
import java.util.*
private typealias Variable = VariableWithConstraints
class FixationOrderCalculator {
enum class ResolveDirection {
TO_SUBTYPE,
TO_SUPERTYPE,
UNKNOWN
}
data class NodeWithDirection(val variableWithConstraints: VariableWithConstraints, val direction: ResolveDirection) {
override fun toString() = "$variableWithConstraints to $direction"
}
interface Context {
val notFixedTypeVariables: Map<TypeConstructor, VariableWithConstraints>
val lambdaArguments: List<ResolvedLambdaArgument>
}
fun computeCompletionOrder(
c: Context,
topReturnType: UnwrappedType
): List<NodeWithDirection> = DependencyGraph(c).getCompletionOrder(topReturnType)
private class DependencyGraph(val c: Context) {
private val directions = HashMap<Variable, ResolveDirection>()
// first in the list -- first fix
fun getCompletionOrder(topReturnType: UnwrappedType): List<NodeWithDirection> {
setupDirections(topReturnType)
return topologicalOrderWith0Priority().map { NodeWithDirection(it, directions[it] ?: ResolveDirection.UNKNOWN) }
}
private fun topologicalOrderWith0Priority(): List<Variable> {
val handler = object : DFS.CollectingNodeHandler<Variable, Variable, LinkedHashSet<Variable>>(LinkedHashSet()) {
override fun afterChildren(current: Variable) {
// we have guaranty that from end of 0 edge there is no other edges with priority 0
result.addAll(get0Edges(current))
result.add(current)
}
}
for (typeVariable in c.notFixedTypeVariables.values) {
DFS.doDfs(typeVariable, DFS.Neighbors(this::getEdges), DFS.VisitedWithSet<Variable>(), handler)
}
return handler.result().toList()
}
private fun setupDirections(topReturnType: UnwrappedType) {
topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction ->
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)
}
}
}
}
private fun enterToNode(variable: Variable, direction: ResolveDirection) {
if (direction == ResolveDirection.UNKNOWN) return
val previous = directions[variable]
if (previous != null) {
if (previous != direction) {
directions[variable] = ResolveDirection.UNKNOWN
}
return
}
directions[variable] = direction
for ((otherVariable, otherDirection) in get12Edges(variable, direction)) {
enterToNode(otherVariable, otherDirection)
}
}
private fun getEdges(variable: Variable): List<Variable> {
val direction = directions[variable] ?: ResolveDirection.UNKNOWN
return get12Edges(variable, direction).map(NodeWithDirection::variableWithConstraints) + get0Edges(variable)
}
/**
* Now we use only priority 0 and {1, 2}.
* Current vision of edge priority for type variable \alpha to variable \beta:
* 0 -- { \beta -> \alpha } i.e. return type depend of all parameters types of lambda
* 1 -- \alpha <: Inv<\beta> or \alpha >: Pair<Inv<\beta & Any>, Int> ot \alpha <: \beta & Any
* 2 -- \alpha <: \beta or \alpha >: \beta?
*/
private fun get12Edges(variableWithConstraints: Variable, direction: ResolveDirection, include2: Boolean = true): List<NodeWithDirection> {
fun isNotInterestingConstraint(direction: ResolveDirection, constraint: Constraint): Boolean {
return (direction == ResolveDirection.TO_SUBTYPE && constraint.kind == ConstraintKind.UPPER) ||
(direction == ResolveDirection.TO_SUPERTYPE && constraint.kind == ConstraintKind.LOWER)
}
val result = SmartList<NodeWithDirection>()
for (constraint in variableWithConstraints.constraints) {
if (isNotInterestingConstraint(direction, constraint)) continue
if (include2 || !c.notFixedTypeVariables.containsKey(constraint.type.constructor)) { // because we collect only type 1 of edges
constraint.type.visitType(direction) { variable, direction ->
result.add(NodeWithDirection(variable, direction))
}
}
}
return result
}
private fun get0Edges(variable: Variable): List<Variable> {
val typeVariable = variable.typeVariable
if (typeVariable !is LambdaTypeVariable || typeVariable.kind != LambdaTypeVariable.Kind.RETURN_TYPE) return emptyList()
val resolvedLambdaArgument = c.lambdaArguments.find { it.argument == typeVariable.lambdaArgument } ?:
error("Missing resolved lambda argument for ${typeVariable.lambdaArgument}")
return resolvedLambdaArgument.myTypeVariables.mapNotNull {
if (it.kind == LambdaTypeVariable.Kind.RETURN_TYPE) return@mapNotNull null
c.notFixedTypeVariables[it.freshTypeConstructor]
}
}
private fun UnwrappedType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) =
when (this) {
is SimpleType -> visitType(startDirection, action)
is FlexibleType -> {
lowerBound.visitType(startDirection, action)
upperBound.visitType(startDirection, action)
}
}
private fun SimpleType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) {
if (isIntersectionType) {
constructor.supertypes.forEach {
it.unwrap().visitType(startDirection, action)
}
return
}
if (arguments.isEmpty()) {
c.notFixedTypeVariables[constructor]?.let {
action(it, startDirection)
}
return
}
val parameters = constructor.parameters
if (parameters.size != arguments.size) return // incorrect type
fun ResolveDirection.opposite() = when (this) {
ResolveDirection.UNKNOWN -> ResolveDirection.UNKNOWN
ResolveDirection.TO_SUPERTYPE -> ResolveDirection.TO_SUBTYPE
ResolveDirection.TO_SUBTYPE -> ResolveDirection.TO_SUPERTYPE
}
for ((argument, parameter) in arguments.zip(parameters)) {
if (argument.isStarProjection) continue
val variance = NewKotlinTypeChecker.effectiveVariance(parameter.variance, argument.projectionKind) ?: Variance.INVARIANT
val innerDirection = when (variance) {
Variance.INVARIANT -> ResolveDirection.UNKNOWN
Variance.OUT_VARIANCE -> startDirection
Variance.IN_VARIANCE -> startDirection.opposite()
}
argument.type.unwrap().visitType(innerDirection, action)
}
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright 2010-2016 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.inference.components
import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator
import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator.ResolveDirection
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.constants.IntegerValueTypeConstructor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.checker.intersectTypes
import org.jetbrains.kotlin.types.singleBestRepresentative
import java.util.*
class ResultTypeResolver(val commonSupertypeCalculator: CommonSupertypeCalculator) {
interface Context {
fun isProperType(type: UnwrappedType): Boolean
}
fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? {
findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it }
if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) {
val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && c.isProperType(it.type) }
if (lowerConstraints.isNotEmpty()) {
return commonSupertypeCalculator(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints))
}
}
// direction == TO_LOWER or there is no LOWER bounds
val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && c.isProperType(it.type) }
if (upperConstraints.isNotEmpty()) {
return intersectTypes(upperConstraints.map { it.type })
}
return null
}
fun findResultIfThereIsEqualsConstraint(
c: Context,
variableWithConstraints: VariableWithConstraints,
allowedFixToNotProperType: Boolean = false
): UnwrappedType? {
val properEqualsConstraint = variableWithConstraints.constraints.filter {
it.kind == ConstraintKind.EQUALITY && c.isProperType(it.type)
}
if (properEqualsConstraint.isNotEmpty()) {
return properEqualsConstraint.map { it.type }.singleBestRepresentative()?.unwrap()
?: properEqualsConstraint.first().type // seems like constraint system has contradiction
}
if (!allowedFixToNotProperType) return null
val notProperEqualsConstraint = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.EQUALITY }
// may be we should just firstOrNull
return notProperEqualsConstraint.singleOrNull()?.type
}
private fun convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints: Collection<Constraint>): Collection<UnwrappedType> {
if (lowerConstraints.isEmpty()) return emptyList()
if (lowerConstraints.size == 1) return listOf(lowerConstraints.first().type)
val (numberLowerBounds, generalLowerBounds) = lowerConstraints.map { it.type }.partition { it.constructor is IntegerValueTypeConstructor }
val numberType = commonSupertypeForNumberTypes(numberLowerBounds) ?: return generalLowerBounds
return generalLowerBounds + numberType
}
private fun commonSupertypeForNumberTypes(numberLowerBounds: Collection<UnwrappedType>): UnwrappedType? {
if (numberLowerBounds.isEmpty()) return null
val intersectionOfSupertypes = getIntersectionOfSupertypes(numberLowerBounds)
return TypeUtils.getDefaultPrimitiveNumberType(intersectionOfSupertypes)?.unwrap() ?:
commonSupertypeCalculator(numberLowerBounds)
}
private fun getIntersectionOfSupertypes(types: Collection<UnwrappedType>): Set<UnwrappedType> {
val upperBounds = HashSet<UnwrappedType>()
for (type in types) {
val supertypes = type.constructor.supertypes.map { it.unwrap() }
if (upperBounds.isEmpty()) {
upperBounds.addAll(supertypes)
}
else {
upperBounds.retainAll(supertypes)
}
}
return upperBounds
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2016 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.inference.components
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallKind
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableFromCallableDescriptor
import org.jetbrains.kotlin.resolve.calls.model.KotlinCall
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.ReceiverKotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.TypeArgument
import org.jetbrains.kotlin.resolve.calls.results.SimpleConstraintSystem
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import java.lang.UnsupportedOperationException
class SimpleConstraintSystemImpl(constraintInjector: ConstraintInjector, resultTypeResolver: ResultTypeResolver) : SimpleConstraintSystem {
val csBuilder: ConstraintSystemBuilder = NewConstraintSystemImpl(constraintInjector, resultTypeResolver).getBuilder()
override fun registerTypeVariables(typeParameters: Collection<TypeParameterDescriptor>): TypeSubstitutor {
val substitutionMap = typeParameters.associate {
val variable = TypeVariableFromCallableDescriptor(ThrowableKotlinCall, it)
csBuilder.registerVariable(variable)
it.defaultType.constructor to variable.defaultType.asTypeProjection()
}
return TypeConstructorSubstitution.createByConstructorsMap(substitutionMap).buildSubstitutor()
}
override fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType) {
csBuilder.addSubtypeConstraint(subType, superType, SimpleConstraintSystemConstraintPosition)
}
override fun hasContradiction() = csBuilder.hasContradiction
private object ThrowableKotlinCall : KotlinCall {
override val callKind: KotlinCallKind get() = throw UnsupportedOperationException()
override val explicitReceiver: ReceiverKotlinCallArgument? get() = throw UnsupportedOperationException()
override val name: Name get() = throw UnsupportedOperationException()
override val typeArguments: List<TypeArgument> get() = throw UnsupportedOperationException()
override val argumentsInParenthesis: List<KotlinCallArgument> get() = throw UnsupportedOperationException()
override val externalArgument: KotlinCallArgument? get() = throw UnsupportedOperationException()
override val isInfixCall: Boolean get() = throw UnsupportedOperationException()
override val isOperatorCall: Boolean get() = throw UnsupportedOperationException()
}
}
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2016 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.inference.components
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.*
import org.jetbrains.kotlin.types.typeUtil.builtIns
abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorTypeEqualsToAnything = true, allowedTypeVariable = false) {
abstract fun isMyTypeVariable(type: SimpleType): Boolean
// super and sub type isSingleClassifierType
abstract fun addUpperConstraint(typeVariable: TypeConstructor, superType: UnwrappedType)
abstract fun addLowerConstraint(typeVariable: TypeConstructor, subType: UnwrappedType)
override final fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? {
assertInputTypes(subType, superType)
var answer: Boolean? = null
if (superType.anyBound(this::isMyTypeVariable)) {
answer = simplifyLowerConstraint(superType, subType)
}
if (subType.anyBound(this::isMyTypeVariable)) {
return simplifyUpperConstraint(subType, superType) && (answer ?: true)
}
else {
return simplifyConstraintForPossibleIntersectionSubType(subType, superType) ?: answer
}
}
/**
* Foo <: T! <=> Foo <: T? <=> Foo & Any <: T
* Foo <: T? <=> Foo & Any <: T
* Foo <: T -- leave as is
*/
fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean {
@Suppress("NAME_SHADOWING")
val typeVariable = typeVariable.upperIfFlexible()
if (typeVariable.isMarkedNullable) {
addLowerConstraint(typeVariable.constructor, intersectTypes(listOf(subType, subType.builtIns.anyType)))
}
else {
addLowerConstraint(typeVariable.constructor, subType)
}
return true
}
/**
* T! <: Foo <=> T <: Foo
* T? <: Foo <=> T <: Foo && Nothing? <: Foo
* T <: Foo -- leave as is
*/
fun simplifyUpperConstraint(typeVariable: UnwrappedType, superType: UnwrappedType): Boolean {
@Suppress("NAME_SHADOWING")
val typeVariable = typeVariable.lowerIfFlexible()
addUpperConstraint(typeVariable.constructor, superType)
if (typeVariable.isMarkedNullable) {
// here is important that superType is singleClassifierType
return if (superType.anyBound(this::isMyTypeVariable)) {
simplifyLowerConstraint(superType, typeVariable)
}
else {
isSubtypeOfByTypeChecker(typeVariable.builtIns.nullableNothingType, superType)
}
}
return true
}
fun simplifyConstraintForPossibleIntersectionSubType(subType: UnwrappedType, superType: UnwrappedType): Boolean? {
@Suppress("NAME_SHADOWING")
val subType = subType.lowerIfFlexible()
if (!subType.isIntersectionType) return null
assert(!subType.isMarkedNullable) { "Intersection type should not be marked nullable!: $subType" }
// TODO: may be we lose flexibility here
val subIntersectionTypes = (subType.constructor as IntersectionTypeConstructor).supertypes.map { it.lowerIfFlexible() }
val typeVariables = subIntersectionTypes.filter(this::isMyTypeVariable).takeIf { it.isNotEmpty() } ?: return null
val notTypeVariables = subIntersectionTypes.filterNot(this::isMyTypeVariable)
// todo: may be we can do better then that.
if (notTypeVariables.isNotEmpty() && NewKotlinTypeChecker.isSubtypeOf(intersectTypes(notTypeVariables), superType)) {
return true
}
return typeVariables.all { simplifyUpperConstraint(it, superType) }
}
private fun isSubtypeOfByTypeChecker(subType: UnwrappedType, superType: UnwrappedType) =
with(NewKotlinTypeChecker) { this@TypeCheckerContextForConstraintSystem.isSubtypeOf(subType, superType) }
private fun assertInputTypes(subType: UnwrappedType, superType: UnwrappedType) {
fun correctSubType(subType: SimpleType) = subType.isSingleClassifierType || subType.isIntersectionType || isMyTypeVariable(subType)
fun correctSuperType(superType: SimpleType) = superType.isSingleClassifierType || isMyTypeVariable(superType)
assert(subType.bothBounds(::correctSubType)) {
"Not singleClassifierType and not intersection subType: $subType"
}
assert(superType.bothBounds(::correctSuperType)) {
"Not singleClassifierType superType: $superType"
}
}
private inline fun UnwrappedType.bothBounds(f: (SimpleType) -> Boolean) = when (this) {
is SimpleType -> f(this)
is FlexibleType -> f(lowerBound) && f(upperBound)
}
private inline fun UnwrappedType.anyBound(f: (SimpleType) -> Boolean) = when (this) {
is SimpleType -> f(this)
is FlexibleType -> f(lowerBound) || f(upperBound)
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2016 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.inference.model
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
import org.jetbrains.kotlin.types.UnwrappedType
sealed class ConstraintPosition
class ExplicitTypeParameterConstraintPosition(val typeArgument: SimpleTypeArgument) : ConstraintPosition() {
override fun toString() = "TypeParameter $typeArgument"
}
class ExpectedTypeConstraintPosition(val topLevelCall: KotlinCall) : ConstraintPosition() {
override fun toString() = "ExpectedType for call $topLevelCall"
}
class DeclaredUpperBoundConstraintPosition(val typeParameterDescriptor: TypeParameterDescriptor) : ConstraintPosition() {
override fun toString() = "DeclaredUpperBound ${typeParameterDescriptor.name} from ${typeParameterDescriptor.containingDeclaration}"
}
class ArgumentConstraintPosition(val argument: KotlinCallArgument) : ConstraintPosition() {
override fun toString() = "Argument $argument"
}
class FixVariableConstraintPosition(val variable: NewTypeVariable) : ConstraintPosition() {
override fun toString() = "Fix variable $variable"
}
class IncorporationConstraintPosition(val from: ConstraintPosition) : ConstraintPosition() {
override fun toString() = "Incorporate $from"
}
@Deprecated("Should be used only in SimpleConstraintSystemImpl")
object SimpleConstraintSystemConstraintPosition : ConstraintPosition()
class NewConstraintError(val lowerType: UnwrappedType, val upperType: UnwrappedType, val position: ConstraintPosition):
KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.constraintError(this)
}
class CapturedTypeFromSubtyping(val typeVariable: NewTypeVariable, val constraintType: UnwrappedType, val position: ConstraintPosition) :
KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.constraintError(this)
}
class NotEnoughInformationForTypeParameter(val typeVariable: NewTypeVariable) : KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) {
override fun report(reporter: DiagnosticReporter) = reporter.constraintError(this)
}
@@ -0,0 +1,134 @@
/*
* Copyright 2010-2016 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.inference.model
import org.jetbrains.kotlin.resolve.calls.inference.substitute
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
/**
* Every type variable can be in the following states:
* - not fixed => there is several constraints for this type variable(possible no one).
* for this type variable we have VariableWithConstraints in map notFixedTypeVariables
* - fixed to proper type or not proper type. For such type variable there is no VariableWithConstraints in notFixedTypeVariables.
* Also we should guaranty that there is no other constraints in other VariableWithConstraints which depends on this fixed type variable.
*
* Note: fixedTypeVariables can contains a proper and not proper type.
*
* Fixing procedure(to proper types). First of all we should determinate fixing order.
* After it, for every type variable we do the following:
* - determinate result proper type
* - add equality constraint, for example: T = Int
* - run incorporation and generate all new constraints
* - after is we remove VariableWithConstraints for type variable T from map notFixedTypeVariables
* - also we remove all constraint in other variable which contains T
* - add result type to fixedTypeVariables.
*
* Note fixing procedure to not proper type the same. The only difference in determination result type.
*
*/
interface ConstraintStorage {
val allTypeVariables: Map<TypeConstructor, NewTypeVariable>
val notFixedTypeVariables: Map<TypeConstructor, VariableWithConstraints>
val initialConstraints: List<InitialConstraint>
val maxTypeDepthFromInitialConstraints: Int
val errors: List<KotlinCallDiagnostic>
val fixedTypeVariables: Map<TypeConstructor, UnwrappedType>
val lambdaArguments: List<ResolvedLambdaArgument>
val innerCalls: List<ResolvedKotlinCall.OnlyResolvedKotlinCall>
object Empty : ConstraintStorage {
override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() = emptyMap()
override val notFixedTypeVariables: Map<TypeConstructor, VariableWithConstraints> get() = emptyMap()
override val initialConstraints: List<InitialConstraint> get() = emptyList()
override val maxTypeDepthFromInitialConstraints: Int get() = 1
override val errors: List<KotlinCallDiagnostic> get() = emptyList()
override val fixedTypeVariables: Map<TypeConstructor, UnwrappedType> get() = emptyMap()
override val lambdaArguments: List<ResolvedLambdaArgument> get() = emptyList()
override val innerCalls: List<ResolvedKotlinCall.OnlyResolvedKotlinCall> get() = emptyList()
}
}
enum class ConstraintKind {
LOWER,
UPPER,
EQUALITY
}
class Constraint(
val kind: ConstraintKind,
val type: UnwrappedType, // flexible types here is allowed
val position: ConstraintPosition,
val typeHashCode: Int = type.hashCode()
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Constraint
if (typeHashCode != other.typeHashCode) return false
if (kind != other.kind) return false
if (position != other.position) return false
if (type != other.type) return false
return true
}
override fun hashCode() = typeHashCode
override fun toString() = "$kind($type) from $position"
}
interface VariableWithConstraints {
val typeVariable: NewTypeVariable
val constraints: List<Constraint>
}
class InitialConstraint(
val a: UnwrappedType,
val b: UnwrappedType,
val constraintKind: ConstraintKind, // see [checkConstraint]
val position: ConstraintPosition
) {
override fun toString(): String {
val sign =
when (constraintKind) {
ConstraintKind.EQUALITY -> "=="
ConstraintKind.LOWER -> ":>"
ConstraintKind.UPPER -> "<:"
}
return "$a $sign $b from $position"
}
}
fun InitialConstraint.checkConstraint(substitutor: TypeSubstitutor): Boolean {
val newA = substitutor.substitute(a)
val newB = substitutor.substitute(a)
val typeChecker = KotlinTypeChecker.DEFAULT
return when (constraintKind) {
ConstraintKind.EQUALITY -> typeChecker.equalTypes(newA, newB)
ConstraintKind.UPPER -> typeChecker.isSubtypeOf(newA, newB)
ConstraintKind.LOWER -> typeChecker.isSubtypeOf(newB, newA)
}
}
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2016 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.inference.model
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType
import java.util.*
class MutableVariableWithConstraints(
override val typeVariable: NewTypeVariable,
constraints: Collection<Constraint> = emptyList()
) : VariableWithConstraints {
override val constraints: List<Constraint> get() = mutableConstraints
private val mutableConstraints = MyArrayList(constraints)
// return new actual constraint, if this constraint is new
fun addConstraint(constraint: Constraint): Constraint? {
val previousConstraintWithSameType = constraints.filter { it.typeHashCode == constraint.typeHashCode && it.type == constraint.type }
if (previousConstraintWithSameType.any { newConstraintIsUseless(it.kind, constraint.kind) }) {
return null
}
val actualConstraint = if (previousConstraintWithSameType.isNotEmpty()) {
// i.e. previous is LOWER and new is UPPER or opposite situation
Constraint(ConstraintKind.EQUALITY, constraint.type, constraint.position, constraint.typeHashCode)
}
else {
constraint
}
mutableConstraints.add(actualConstraint)
return actualConstraint
}
fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) {
mutableConstraints.removeLast(shouldRemove)
}
// todo optimize it!
fun removeConstrains(shouldRemove: (Constraint) -> Boolean) {
val newConstraints = mutableConstraints.filter { !shouldRemove(it) }
mutableConstraints.clear()
mutableConstraints.addAll(newConstraints)
}
private fun newConstraintIsUseless(oldKind: ConstraintKind, newKind: ConstraintKind) =
when (oldKind) {
ConstraintKind.EQUALITY -> true
ConstraintKind.LOWER -> newKind == ConstraintKind.LOWER
ConstraintKind.UPPER -> newKind == ConstraintKind.UPPER
}
private class MyArrayList<E>(c: Collection<E>): ArrayList<E>(c) {
fun removeLast(predicate: (E) -> Boolean) {
val newSize = indexOfLast { !predicate(it) } + 1
if (newSize != size) {
removeRange(newSize, size)
}
}
}
override fun toString(): String {
return "Constraints for $typeVariable"
}
}
// todo may be we should use LinkedHasMap
class MutableConstraintStorage : ConstraintStorage {
override val allTypeVariables: MutableMap<TypeConstructor, NewTypeVariable> = HashMap()
override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> = HashMap()
override val initialConstraints: MutableList<InitialConstraint> = ArrayList()
override var maxTypeDepthFromInitialConstraints: Int = 1
override val errors: MutableList<KotlinCallDiagnostic> = ArrayList()
override val fixedTypeVariables: MutableMap<TypeConstructor, UnwrappedType> = HashMap()
override val lambdaArguments: MutableList<ResolvedLambdaArgument> = ArrayList()
override val innerCalls: MutableList<ResolvedKotlinCall.OnlyResolvedKotlinCall> = ArrayList()
}
@@ -0,0 +1,254 @@
/*
* Copyright 2010-2016 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.inference.model
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.buildCurrentSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector
import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator
import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.contains
import java.util.*
class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val resultTypeResolver: ResultTypeResolver):
NewConstraintSystem,
ConstraintSystemBuilder,
ConstraintInjector.Context,
ResultTypeResolver.Context,
KotlinCallCompleter.Context,
FixationOrderCalculator.Context
{
val storage = MutableConstraintStorage()
private var state = State.BUILDING
private enum class State {
BUILDING,
FREEZED,
COMPLETION
}
private fun checkState(vararg allowedState: State) {
assert(state in allowedState) {
"State $state is not allowed. AllowedStates: ${allowedState.joinToString()}"
}
}
override val diagnostics: List<KotlinCallDiagnostic>
get() = storage.errors
override fun getBuilder() = apply { checkState(State.BUILDING, State.COMPLETION) }
override fun asConstraintInjectorContext() = apply { checkState(State.BUILDING, State.COMPLETION) }
override fun asReadOnlyStorage(): ConstraintStorage {
checkState(State.BUILDING, State.FREEZED)
state = State.FREEZED
return storage
}
override fun asCallCompleterContext(): KotlinCallCompleter.Context {
checkState(State.BUILDING, State.COMPLETION)
state = State.COMPLETION
return this
}
// ConstraintSystemBuilder
override fun registerVariable(variable: NewTypeVariable) {
checkState(State.BUILDING, State.COMPLETION)
storage.allTypeVariables[variable.freshTypeConstructor] = variable
storage.notFixedTypeVariables[variable.freshTypeConstructor] = MutableVariableWithConstraints(variable)
}
override fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) =
constraintInjector.addInitialSubtypeConstraint(apply { checkState(State.BUILDING, State.COMPLETION) }, lowerType, upperType, position)
override fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) =
constraintInjector.addInitialEqualityConstraint(apply { checkState(State.BUILDING, State.COMPLETION) }, a, b, position)
override fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) {
checkState(State.BUILDING, State.COMPLETION)
storage.lambdaArguments.add(resolvedLambdaArgument)
}
override fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean {
checkState(State.BUILDING, State.COMPLETION)
if (hasContradiction) return false
addSubtypeConstraint(lowerType, upperType, position)
if (!hasContradiction) return true
val shouldRemove = { c: Constraint -> c.position === position ||
(c.position is IncorporationConstraintPosition && c.position.from === position) }
for (variableWithConstraint in storage.notFixedTypeVariables.values) {
variableWithConstraint.removeLastConstraints(shouldRemove)
}
storage.errors.clear()
storage.initialConstraints.removeAt(storage.initialConstraints.lastIndex)
return false
}
private fun getVariablesForFixation(): Map<NewTypeVariable, UnwrappedType> {
val fixedVariables = LinkedHashMap<NewTypeVariable, UnwrappedType>()
for (variableWithConstrains in storage.notFixedTypeVariables.values) {
val resultType = resultTypeResolver.findResultIfThereIsEqualsConstraint(apply { checkState(State.BUILDING) }, variableWithConstrains,
allowedFixToNotProperType = false)
if (resultType != null) {
fixedVariables[variableWithConstrains.typeVariable] = resultType
}
}
return fixedVariables
}
override fun simplify(): TypeSubstitutor {
checkState(State.BUILDING)
var fixedVariables = getVariablesForFixation()
while (fixedVariables.isNotEmpty()) {
for ((variable, resultType) in fixedVariables) {
fixVariable(variable, resultType)
}
fixedVariables = getVariablesForFixation()
}
return storage.buildCurrentSubstitutor()
}
// ConstraintSystemBuilder, KotlinCallCompleter.Context
override val hasContradiction: Boolean
get() = diagnostics.any { !it.candidateApplicability.isSuccess }.apply { checkState(State.BUILDING, State.COMPLETION) }
override fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall) {
checkState(State.BUILDING, State.COMPLETION)
storage.innerCalls.add(innerCall)
val otherSystem = innerCall.candidate.lastCall.constraintSystem.asReadOnlyStorage()
storage.allTypeVariables.putAll(otherSystem.allTypeVariables)
for ((variable, constraints) in otherSystem.notFixedTypeVariables) {
notFixedTypeVariables[variable] = MutableVariableWithConstraints(constraints.typeVariable, constraints.constraints)
}
storage.initialConstraints.addAll(otherSystem.initialConstraints)
storage.maxTypeDepthFromInitialConstraints = Math.max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints)
storage.errors.addAll(otherSystem.errors)
storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables)
storage.lambdaArguments.addAll(otherSystem.lambdaArguments)
storage.innerCalls.addAll(otherSystem.innerCalls)
}
// ResultTypeResolver.Context, ConstraintSystemBuilder
override fun isProperType(type: UnwrappedType): Boolean {
checkState(State.BUILDING, State.COMPLETION)
return !type.contains {
storage.allTypeVariables.containsKey(it.constructor)
}
}
// ConstraintInjector.Context
override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() {
checkState(State.BUILDING, State.COMPLETION)
return storage.allTypeVariables
}
override var maxTypeDepthFromInitialConstraints: Int
get() = storage.maxTypeDepthFromInitialConstraints
set(value) {
checkState(State.BUILDING, State.COMPLETION)
storage.maxTypeDepthFromInitialConstraints = value
}
override fun addInitialConstraint(initialConstraint: InitialConstraint) {
checkState(State.BUILDING, State.COMPLETION)
storage.initialConstraints.add(initialConstraint)
}
// ConstraintInjector.Context, FixationOrderCalculator.Context
override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> get() {
checkState(State.BUILDING, State.COMPLETION)
return storage.notFixedTypeVariables
}
// ConstraintInjector.Context, KotlinCallCompleter.Context
override fun addError(error: KotlinCallDiagnostic) {
checkState(State.BUILDING, State.COMPLETION)
storage.errors.add(error)
}
// FixationOrderCalculator.Context, KotlinCallCompleter.Context
override val lambdaArguments: List<ResolvedLambdaArgument> get() {
checkState(State.COMPLETION)
return storage.lambdaArguments
}
// KotlinCallCompleter.Context
override fun asResultTypeResolverContext() = apply { checkState(State.COMPLETION) }
override fun asFixationOrderCalculatorContext() = apply { checkState(State.COMPLETION) }
override fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) {
checkState(State.BUILDING, State.COMPLETION)
constraintInjector.addInitialEqualityConstraint(this, variable.defaultType, resultType, FixVariableConstraintPosition(variable))
notFixedTypeVariables.remove(variable.freshTypeConstructor)
for (variableWithConstraint in notFixedTypeVariables.values) {
variableWithConstraint.removeConstrains {
it.type.contains { it.constructor == variable.freshTypeConstructor }
}
}
storage.fixedTypeVariables[variable.freshTypeConstructor] = resultType
}
override val innerCalls: List<ResolvedKotlinCall.OnlyResolvedKotlinCall> get() {
checkState(State.COMPLETION)
return storage.innerCalls
}
override fun canBeProper(type: UnwrappedType): Boolean {
checkState(State.COMPLETION)
return !type.contains { storage.notFixedTypeVariables.containsKey(it.constructor) }
}
override fun buildCurrentSubstitutor(): TypeSubstitutor {
checkState(State.COMPLETION)
return storage.buildCurrentSubstitutor()
}
override fun buildResultingSubstitutor(): TypeSubstitutor {
checkState(State.COMPLETION)
val currentSubstitutorMap = storage.fixedTypeVariables.entries.associate {
it.key to it.value.asTypeProjection()
}
val uninferredSubstitutorMap = storage.notFixedTypeVariables.entries.associate { (freshTypeConstructor, typeVariable) ->
freshTypeConstructor to ErrorUtils.createErrorTypeWithCustomConstructor("Uninferred type", typeVariable.typeVariable.freshTypeConstructor).asTypeProjection()
}
return TypeConstructorSubstitution.createByConstructorsMap(currentSubstitutorMap + uninferredSubstitutorMap).buildSubstitutor()
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2016 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.inference.model
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
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
class TypeVariableTypeConstructor(private val builtIns: KotlinBuiltIns, val debugName: String): TypeConstructor, NewTypeVariableConstructor {
override fun getParameters(): List<TypeParameterDescriptor> = emptyList()
override fun getSupertypes(): Collection<KotlinType> = emptyList()
override fun isFinal(): Boolean = false
override fun isDenotable(): Boolean = false
override fun getDeclarationDescriptor(): ClassifierDescriptor? = null
override fun getBuiltIns() = builtIns
override fun toString() = "TypeVariable($debugName)"
}
sealed class NewTypeVariable(builtIns: KotlinBuiltIns, name: String) {
val freshTypeConstructor: TypeConstructor = TypeVariableTypeConstructor(builtIns, name)
val defaultType: SimpleType = KotlinTypeFactory.simpleType(
Annotations.EMPTY, freshTypeConstructor, arguments = emptyList(),
nullable = false, memberScope = ErrorUtils.createErrorScope("Type variable", true))
override fun toString() = freshTypeConstructor.toString()
}
class TypeVariableFromCallableDescriptor(
val call: KotlinCall,
val originalTypeParameter: TypeParameterDescriptor
) : 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]"
}
}
@@ -0,0 +1,65 @@
/*
* 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.model
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.types.checker.prepareArgumentTypeRegardingCaptureTypes
class FakeKotlinCallArgumentForCallableReference(
val callableReference: ChosenCallableReferenceDescriptor,
val index: Int
) : KotlinCallArgument {
override val isSpread: Boolean get() = false
override val argumentName: Name? get() = null
}
class ReceiverExpressionKotlinCallArgument private constructor(
override val receiver: ReceiverValueWithSmartCastInfo,
override val isSafeCall: Boolean = false,
val isVariableReceiverForInvoke: Boolean = false
) : ExpressionKotlinCallArgument {
override val isSpread: Boolean get() = false
override val argumentName: Name? get() = null
override fun toString() = "$receiver" + if(isSafeCall) "?" else ""
companion object {
// we create ReceiverArgument and fix capture types
operator fun invoke(
receiver: ReceiverValueWithSmartCastInfo,
isSafeCall: Boolean = false,
isVariableReceiverForInvoke: Boolean = false
): ReceiverExpressionKotlinCallArgument {
val newType = prepareArgumentTypeRegardingCaptureTypes(receiver.receiverValue.type.unwrap())
val newReceiver = if (newType != null) {
ReceiverValueWithSmartCastInfo(receiver.receiverValue.replaceType(newType), receiver.possibleTypes, receiver.isStable)
} else receiver
return ReceiverExpressionKotlinCallArgument(newReceiver, isSafeCall, isVariableReceiverForInvoke)
}
}
}
class EmptyLabeledReturn(builtIns: KotlinBuiltIns) : ExpressionKotlinCallArgument {
override val isSpread: Boolean get() = false
override val argumentName: Name? get() = null
override val receiver = ReceiverValueWithSmartCastInfo(TransientReceiver(builtIns.unitType), emptySet(), true)
override val isSafeCall: Boolean get() = false
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2016 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.model
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.INAPPLICABLE
import org.jetbrains.kotlin.types.KotlinType
abstract class KotlinCallDiagnostic(val candidateApplicability: ResolutionCandidateApplicability) {
abstract fun report(reporter: DiagnosticReporter)
}
interface DiagnosticReporter {
fun onExplicitReceiver(diagnostic: KotlinCallDiagnostic)
fun onCall(diagnostic: KotlinCallDiagnostic)
fun onTypeArguments(diagnostic: KotlinCallDiagnostic)
fun onCallName(diagnostic: KotlinCallDiagnostic)
fun onTypeArgument(typeArgument: TypeArgument, diagnostic: KotlinCallDiagnostic)
fun onCallReceiver(callReceiver: SimpleKotlinCallArgument, diagnostic: KotlinCallDiagnostic)
fun onCallArgument(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic)
fun onCallArgumentName(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic)
fun onCallArgumentSpread(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic)
fun constraintError(diagnostic: KotlinCallDiagnostic)
}
@@ -0,0 +1,83 @@
/*
* Copyright 2010-2016 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.model
import org.jetbrains.kotlin.name.Name
interface KotlinCall {
val callKind: KotlinCallKind
val explicitReceiver: ReceiverKotlinCallArgument?
// a.(foo)() -- (foo) is dispatchReceiverForInvoke
val dispatchReceiverForInvokeExtension: SimpleKotlinCallArgument? get() = null
val name: Name
val typeArguments: List<TypeArgument>
val argumentsInParenthesis: List<KotlinCallArgument>
val externalArgument: KotlinCallArgument?
val isInfixCall: Boolean
val isOperatorCall: Boolean
}
private fun SimpleKotlinCallArgument.checkReceiverInvariants() {
assert(!isSpread) {
"Receiver cannot be a spread: $this"
}
assert(argumentName == null) {
"Argument name should be null for receiver: $this, but it is $argumentName"
}
}
fun KotlinCall.checkCallInvariants() {
assert(explicitReceiver !is LambdaKotlinCallArgument && explicitReceiver !is CallableReferenceKotlinCallArgument) {
"Lambda argument or callable reference is not allowed as explicit receiver: $explicitReceiver"
}
(explicitReceiver as? SimpleKotlinCallArgument)?.checkReceiverInvariants()
dispatchReceiverForInvokeExtension?.checkReceiverInvariants()
if (callKind != KotlinCallKind.FUNCTION) {
assert(externalArgument == null) {
"External argument is not allowed not for function call: $externalArgument."
}
assert(argumentsInParenthesis.isEmpty()) {
"Arguments in parenthesis should be empty for not function call: $this "
}
assert(dispatchReceiverForInvokeExtension == null) {
"Dispatch receiver for invoke should be null for not function call: $dispatchReceiverForInvokeExtension"
}
}
else {
assert(externalArgument == null || !externalArgument!!.isSpread) {
"External argument cannot nave spread element: $externalArgument"
}
assert(externalArgument?.argumentName == null) {
"Illegal external argument with name: $externalArgument"
}
assert(dispatchReceiverForInvokeExtension == null || !dispatchReceiverForInvokeExtension!!.isSafeCall) {
"Dispatch receiver for invoke cannot be safe: $dispatchReceiverForInvokeExtension"
}
}
}
@@ -0,0 +1,98 @@
/*
* 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.model
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.tower.CandidateWithBoundDispatchReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.DetailedReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.UnwrappedType
interface ReceiverKotlinCallArgument {
val receiver: DetailedReceiver
}
class QualifierReceiverKotlinCallArgument(override val receiver: QualifierReceiver) : ReceiverKotlinCallArgument {
override fun toString() = "$receiver"
}
interface KotlinCallArgument {
val isSpread: Boolean
val argumentName: Name?
}
interface SimpleKotlinCallArgument : KotlinCallArgument, ReceiverKotlinCallArgument {
override val receiver: ReceiverValueWithSmartCastInfo
val isSafeCall: Boolean
}
interface ExpressionKotlinCallArgument : SimpleKotlinCallArgument
interface SubKotlinCallArgument : SimpleKotlinCallArgument {
val resolvedCall: ResolvedKotlinCall.OnlyResolvedKotlinCall
}
interface LambdaKotlinCallArgument : KotlinCallArgument {
override val isSpread: Boolean
get() = false
/**
* parametersTypes == null means, that there is no declared arguments
* null inside array means that this type is not declared explicitly
*/
val parametersTypes: Array<UnwrappedType?>?
}
interface FunctionExpression : LambdaKotlinCallArgument {
override val parametersTypes: Array<UnwrappedType?>
// null means that there function can not have receiver
val receiverType: UnwrappedType?
// null means that return type is not declared, for fun(){ ... } returnType == Unit
val returnType: UnwrappedType?
}
interface CallableReferenceKotlinCallArgument : KotlinCallArgument {
override val isSpread: Boolean
get() = false
// Foo::bar lhsType = Foo. For a::bar where a is expression, this type is null
val lhsType: UnwrappedType?
val constraintStorage: ConstraintStorage
}
interface ChosenCallableReferenceDescriptor : CallableReferenceKotlinCallArgument {
val candidate: CandidateWithBoundDispatchReceiver
val extensionReceiver: ReceiverValueWithSmartCastInfo?
}
interface TypeArgument
// todo allow '_' in frontend
object TypeArgumentPlaceholder : TypeArgument
interface SimpleTypeArgument: TypeArgument {
val type: UnwrappedType
}
@@ -0,0 +1,102 @@
/*
* 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.model
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.components.CheckArguments
import org.jetbrains.kotlin.resolve.calls.components.LambdaAnalyzer
import org.jetbrains.kotlin.resolve.calls.components.*
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector
import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory
import org.jetbrains.kotlin.resolve.calls.tower.CandidateWithBoundDispatchReceiver
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.TypeSubstitutor
class KotlinCallContext(
val scopeTower: ImplicitScopeTower,
val lambdaAnalyzer: LambdaAnalyzer,
val argumentsToParametersMapper: ArgumentsToParametersMapper,
val typeArgumentsToParametersMapper: TypeArgumentsToParametersMapper,
val resultTypeResolver: ResultTypeResolver,
val callableReferenceResolver: CallableReferenceResolver,
val constraintInjector: ConstraintInjector
)
class SimpleCandidateFactory(val callContext: KotlinCallContext, val kotlinCall: KotlinCall): CandidateFactory<SimpleKotlinResolutionCandidate> {
// todo: try something else, because current method is ugly and unstable
private fun createReceiverArgument(
explicitReceiver: ReceiverKotlinCallArgument?,
fromResolution: ReceiverValueWithSmartCastInfo?
): SimpleKotlinCallArgument? =
explicitReceiver as? SimpleKotlinCallArgument ?: // qualifier receiver cannot be safe
fromResolution?.let { ReceiverExpressionKotlinCallArgument(it, isSafeCall = false) } // todo smartcast implicit this
override fun createCandidate(
towerCandidate: CandidateWithBoundDispatchReceiver,
explicitReceiverKind: ExplicitReceiverKind,
extensionReceiver: ReceiverValueWithSmartCastInfo?
): SimpleKotlinResolutionCandidate {
val dispatchArgumentReceiver = createReceiverArgument(kotlinCall.getExplicitDispatchReceiver(explicitReceiverKind),
towerCandidate.dispatchReceiver)
val extensionArgumentReceiver = createReceiverArgument(kotlinCall.getExplicitExtensionReceiver(explicitReceiverKind), extensionReceiver)
if (ErrorUtils.isError(towerCandidate.descriptor)) {
return ErrorKotlinResolutionCandidate(callContext, kotlinCall, explicitReceiverKind, dispatchArgumentReceiver, extensionArgumentReceiver, towerCandidate.descriptor)
}
return SimpleKotlinResolutionCandidate(callContext, kotlinCall, explicitReceiverKind, dispatchArgumentReceiver, extensionArgumentReceiver,
towerCandidate.descriptor, towerCandidate.diagnostics)
}
}
enum class KotlinCallKind(vararg resolutionPart: ResolutionPart) {
VARIABLE(
CheckVisibility,
CheckInfixResolutionPart,
CheckOperatorResolutionPart,
NoTypeArguments,
NoArguments,
CreteDescriptorWithFreshTypeVariables,
CheckExplicitReceiverKindConsistency,
CheckReceivers
),
FUNCTION(
CheckVisibility,
MapTypeArguments,
MapArguments,
CreteDescriptorWithFreshTypeVariables,
CheckExplicitReceiverKindConsistency,
CheckReceivers,
CheckArguments
),
UNSUPPORTED();
val resolutionSequence = resolutionPart.asList()
}
class GivenCandidate(
val descriptor: FunctionDescriptor,
val dispatchReceiver: ReceiverValueWithSmartCastInfo?,
val knownTypeParametersResultingSubstitutor: TypeSubstitutor?
)
@@ -0,0 +1,145 @@
/*
* Copyright 2010-2016 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.model
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.Candidate
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateStatus
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import java.util.*
interface ResolutionPart {
fun SimpleKotlinResolutionCandidate.process(): List<KotlinCallDiagnostic>
}
sealed class KotlinResolutionCandidate : Candidate {
abstract val kotlinCall: KotlinCall
abstract val lastCall: SimpleKotlinResolutionCandidate
}
class VariableAsFunctionKotlinResolutionCandidate(
override val kotlinCall: KotlinCall,
val resolvedVariable: SimpleKotlinResolutionCandidate,
val invokeCandidate: SimpleKotlinResolutionCandidate
) : KotlinResolutionCandidate() {
override val isSuccessful: Boolean get() = resolvedVariable.isSuccessful && invokeCandidate.isSuccessful
override val status: ResolutionCandidateStatus
get() = ResolutionCandidateStatus(resolvedVariable.status.diagnostics + invokeCandidate.status.diagnostics)
override val lastCall: SimpleKotlinResolutionCandidate get() = invokeCandidate
}
sealed class AbstractSimpleKotlinResolutionCandidate(
val constraintSystem: NewConstraintSystem,
initialDiagnostics: Collection<KotlinCallDiagnostic> = emptyList()
) : KotlinResolutionCandidate() {
override val isSuccessful: Boolean
get() {
process(stopOnFirstError = true)
return !hasErrors
}
private var _status: ResolutionCandidateStatus? = null
override val status: ResolutionCandidateStatus
get() {
if (_status == null) {
process(stopOnFirstError = false)
_status = ResolutionCandidateStatus(diagnostics + constraintSystem.diagnostics)
}
return _status!!
}
private val diagnostics = ArrayList<KotlinCallDiagnostic>()
protected var step = 0
private set
protected var hasErrors = false
private set
private fun process(stopOnFirstError: Boolean) {
while (step < resolutionSequence.size && (!stopOnFirstError || !hasErrors)) {
addDiagnostics(resolutionSequence[step].run { lastCall.process() })
step++
}
}
private fun addDiagnostics(diagnostics: Collection<KotlinCallDiagnostic>) {
hasErrors = hasErrors || diagnostics.any { !it.candidateApplicability.isSuccess } ||
constraintSystem.diagnostics.any { !it.candidateApplicability.isSuccess }
this.diagnostics.addAll(diagnostics)
}
init {
addDiagnostics(initialDiagnostics)
}
abstract val resolutionSequence: List<ResolutionPart>
}
open class SimpleKotlinResolutionCandidate(
val callContext: KotlinCallContext,
override val kotlinCall: KotlinCall,
val explicitReceiverKind: ExplicitReceiverKind,
val dispatchReceiverArgument: SimpleKotlinCallArgument?,
val extensionReceiver: SimpleKotlinCallArgument?,
val candidateDescriptor: CallableDescriptor,
initialDiagnostics: Collection<KotlinCallDiagnostic>
) : AbstractSimpleKotlinResolutionCandidate(NewConstraintSystemImpl(callContext.constraintInjector, callContext.resultTypeResolver), initialDiagnostics) {
val csBuilder: ConstraintSystemBuilder get() = constraintSystem.getBuilder()
lateinit var typeArgumentMappingByOriginal: TypeArgumentsToParametersMapper.TypeArgumentsMapping
lateinit var argumentMappingByOriginal: Map<ValueParameterDescriptor, ResolvedCallArgument>
lateinit var descriptorWithFreshTypes: CallableDescriptor
override val lastCall: SimpleKotlinResolutionCandidate get() = this
override val resolutionSequence: List<ResolutionPart> get() = kotlinCall.callKind.resolutionSequence
override fun toString(): String {
val descriptor = DescriptorRenderer.COMPACT.render(candidateDescriptor)
val okOrFail = if (hasErrors) "FAIL" else "OK"
val step = "$step/${resolutionSequence.size}"
return "$okOrFail($step): $descriptor"
}
}
class ErrorKotlinResolutionCandidate(
callContext: KotlinCallContext,
kotlinCall: KotlinCall,
explicitReceiverKind: ExplicitReceiverKind,
dispatchReceiverArgument: SimpleKotlinCallArgument?,
extensionReceiver: SimpleKotlinCallArgument?,
candidateDescriptor: CallableDescriptor
) : SimpleKotlinResolutionCandidate(callContext, kotlinCall, explicitReceiverKind, dispatchReceiverArgument, extensionReceiver, candidateDescriptor, listOf()) {
override val resolutionSequence: List<ResolutionPart> get() = emptyList()
init {
typeArgumentMappingByOriginal = TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments
argumentMappingByOriginal = emptyMap()
descriptorWithFreshTypes = candidateDescriptor
}
}
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2016 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.model
import org.jetbrains.kotlin.builtins.createFunctionType
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.components.ArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.UnwrappedType
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 outputType: UnwrappedType?
var analyzed: Boolean = false
}
class ResolvedLambdaArgument(
override val outerCall: KotlinCall,
override val argument: LambdaKotlinCallArgument,
override val myTypeVariables: Collection<LambdaTypeVariable>,
val receiver: UnwrappedType?,
val parameters: List<UnwrappedType>,
val returnType: UnwrappedType
) : ArgumentWithPostponeResolution() {
val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType) // todo support annotations
override val inputType: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
override val outputType: UnwrappedType get() = returnType
}
class ResolvedPropertyReference(
val outerCall: KotlinCall,
val argument: ChosenCallableReferenceDescriptor,
val reflectionType: UnwrappedType
) {
val boundDispatchReceiver: ReceiverValue? get() = argument.candidate.dispatchReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference }
val boundExtensionReceiver: ReceiverValue? get() = argument.extensionReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference }
}
class ResolvedFunctionReference(
val outerCall: KotlinCall,
val argument: ChosenCallableReferenceDescriptor,
val reflectionType: UnwrappedType,
val argumentsMapping: ArgumentsToParametersMapper.ArgumentMapping?
) {
val boundDispatchReceiver: ReceiverValue? get() = argument.candidate.dispatchReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference }
val boundExtensionReceiver: ReceiverValue? get() = argument.extensionReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference }
}
fun KotlinCall.getExplicitDispatchReceiver(explicitReceiverKind: ExplicitReceiverKind) = when (explicitReceiverKind) {
ExplicitReceiverKind.DISPATCH_RECEIVER -> explicitReceiver
ExplicitReceiverKind.BOTH_RECEIVERS -> dispatchReceiverForInvokeExtension
else -> null
}
fun KotlinCall.getExplicitExtensionReceiver(explicitReceiverKind: ExplicitReceiverKind) = when (explicitReceiverKind) {
ExplicitReceiverKind.EXTENSION_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> explicitReceiver
else -> null
}
class MockReceiverForCallableReference(val lhsOrDeclaredType: UnwrappedType) : ReceiverValue {
override fun getType() = lhsOrDeclaredType
override fun replaceType(newType: KotlinType) = MockReceiverForCallableReference(newType.unwrap())
}
val ChosenCallableReferenceDescriptor.dispatchNotBoundReceiver : UnwrappedType?
get() = (candidate.dispatchReceiver?.receiverValue as? MockReceiverForCallableReference)?.lhsOrDeclaredType
val ChosenCallableReferenceDescriptor.extensionNotBoundReceiver : UnwrappedType?
get() = (extensionReceiver as? MockReceiverForCallableReference)?.lhsOrDeclaredType
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2016 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.model
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateStatus
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.UnwrappedType
sealed class ResolvedKotlinCall {
class CompletedResolvedKotlinCall(
val completedCall: CompletedKotlinCall,
val allInnerCalls: Collection<CompletedKotlinCall>
): ResolvedKotlinCall()
class OnlyResolvedKotlinCall(
val candidate: KotlinResolutionCandidate
) : ResolvedKotlinCall() {
val currentReturnType: UnwrappedType = candidate.lastCall.descriptorWithFreshTypes.returnTypeOrNothing
}
}
sealed class CompletedKotlinCall {
abstract val resolutionStatus: ResolutionCandidateStatus
class Simple(
val kotlinCall: KotlinCall,
val candidateDescriptor: CallableDescriptor,
val resultingDescriptor: CallableDescriptor,
override val resolutionStatus: ResolutionCandidateStatus,
val explicitReceiverKind: ExplicitReceiverKind,
val dispatchReceiver: ReceiverValueWithSmartCastInfo?,
val extensionReceiver: ReceiverValueWithSmartCastInfo?,
val typeArguments: List<UnwrappedType>,
val argumentMappingByOriginal: Map<ValueParameterDescriptor, ResolvedCallArgument>
): CompletedKotlinCall()
class VariableAsFunction(
val kotlinCall: KotlinCall,
val variableCall: Simple,
val invokeCall: Simple
): CompletedKotlinCall() {
override val resolutionStatus: ResolutionCandidateStatus =
ResolutionCandidateStatus(variableCall.resolutionStatus.diagnostics + invokeCall.resolutionStatus.diagnostics)
}
}
sealed class ResolvedCallArgument {
abstract val arguments: List<KotlinCallArgument>
object DefaultArgument : ResolvedCallArgument() {
override val arguments: List<KotlinCallArgument>
get() = emptyList()
}
class SimpleArgument(val callArgument: KotlinCallArgument): ResolvedCallArgument() {
override val arguments: List<KotlinCallArgument>
get() = listOf(callArgument)
}
class VarargArgument(override val arguments: List<KotlinCallArgument>): ResolvedCallArgument()
}
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import java.util.*
class OverloadingConflictResolver<C : Any>(
open class OverloadingConflictResolver<C : Any>(
private val builtIns: KotlinBuiltIns,
private val specificityComparator: TypeSpecificityComparator,
private val getResultingDescriptor: (C) -> CallableDescriptor,
@@ -21,6 +21,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.DiagnosticReporter
import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.*
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
@@ -59,9 +62,9 @@ interface CandidateWithBoundDispatchReceiver {
fun copy(newDescriptor: CallableDescriptor): CandidateWithBoundDispatchReceiver
}
data class ResolutionCandidateStatus(val diagnostics: List<ResolutionDiagnostic>) {
val resultingApplicability: ResolutionCandidateApplicability = diagnostics.asSequence().map { it.candidateLevel }.max()
?: ResolutionCandidateApplicability.RESOLVED
data class ResolutionCandidateStatus(val diagnostics: List<KotlinCallDiagnostic>) {
val resultingApplicability: ResolutionCandidateApplicability = diagnostics.asSequence().map { it.candidateApplicability }.max()
?: RESOLVED
}
enum class ResolutionCandidateApplicability {
@@ -77,22 +80,31 @@ enum class ResolutionCandidateApplicability {
HIDDEN, // removed from resolve
}
abstract class ResolutionDiagnostic(val candidateLevel: ResolutionCandidateApplicability)
abstract class ResolutionDiagnostic(candidateApplicability: ResolutionCandidateApplicability): KotlinCallDiagnostic(candidateApplicability) {
override fun report(reporter: DiagnosticReporter) {
// do nothing
}
}
// todo error for this access from nested class
class VisibilityError(val invisibleMember: DeclarationDescriptorWithVisibility): ResolutionDiagnostic(ResolutionCandidateApplicability.RUNTIME_ERROR)
class NestedClassViaInstanceReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE)
class InnerClassViaStaticReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE)
class UnsupportedInnerClassCall(val message: String): ResolutionDiagnostic(ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE)
class UsedSmartCastForDispatchReceiver(val smartCastType: KotlinType): ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED)
class VisibilityError(val invisibleMember: DeclarationDescriptorWithVisibility): ResolutionDiagnostic(RUNTIME_ERROR) {
override fun report(reporter: DiagnosticReporter) {
reporter.onCall(this)
}
}
object ErrorDescriptorDiagnostic : ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED) // todo discuss and change to INAPPLICABLE
object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY)
object DynamicDescriptorDiagnostic: ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY)
object UnstableSmartCastDiagnostic: ResolutionDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR)
object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN)
object HiddenDescriptor: ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN)
class NestedClassViaInstanceReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(IMPOSSIBLE_TO_GENERATE)
class InnerClassViaStaticReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(IMPOSSIBLE_TO_GENERATE)
class UnsupportedInnerClassCall(val message: String): ResolutionDiagnostic(IMPOSSIBLE_TO_GENERATE)
class UsedSmartCastForDispatchReceiver(val smartCastType: KotlinType): ResolutionDiagnostic(RESOLVED)
object InvokeConventionCallNoOperatorModifier : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR)
object InfixCallNoInfixModifier : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR)
object DeprecatedUnaryPlusAsPlus : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR)
object ErrorDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED) // todo discuss and change to INAPPLICABLE
object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
object DynamicDescriptorDiagnostic: ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
object UnstableSmartCastDiagnostic: ResolutionDiagnostic(MAY_THROW_RUNTIME_ERROR)
object HiddenExtensionRelatedToDynamicTypes: ResolutionDiagnostic(HIDDEN)
object HiddenDescriptor: ResolutionDiagnostic(HIDDEN)
object InvokeConventionCallNoOperatorModifier : ResolutionDiagnostic(CONVENTION_ERROR)
object InfixCallNoInfixModifier : ResolutionDiagnostic(CONVENTION_ERROR)
object DeprecatedUnaryPlusAsPlus : ResolutionDiagnostic(CONVENTION_ERROR)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.tower
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.USE_NEW_INFERENCE
import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject
@@ -63,13 +64,15 @@ internal abstract class AbstractScopeTowerLevel(
if (descriptor.hasLowPriorityInOverloadResolution()) diagnostics.add(LowPriorityDescriptorDiagnostic)
if (dispatchReceiverSmartCastType != null) diagnostics.add(UsedSmartCastForDispatchReceiver(dispatchReceiverSmartCastType))
val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext
if (!shouldSkipVisibilityCheck) {
Visibilities.findInvisibleMember(
getReceiverValueWithSmartCast(dispatchReceiver?.receiverValue, dispatchReceiverSmartCastType),
descriptor,
scopeTower.lexicalScope.ownerDescriptor
)?.let { diagnostics.add(VisibilityError(it)) }
if (!USE_NEW_INFERENCE) {
val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext
if (!shouldSkipVisibilityCheck) {
Visibilities.findInvisibleMember(
getReceiverValueWithSmartCast(dispatchReceiver?.receiverValue, dispatchReceiverSmartCastType),
descriptor,
scopeTower.lexicalScope.ownerDescriptor
)?.let { diagnostics.add(VisibilityError(it)) }
}
}
}
return CandidateWithBoundDispatchReceiverImpl(dispatchReceiver, descriptor, diagnostics)
@@ -41,6 +41,7 @@ interface CandidateFactory<out C: Candidate> {
interface CandidateFactoryProviderForInvoke<C : Candidate> {
// variable here is resolved, invoke -- only chosen
fun transformCandidate(variable: C, invoke: C): C
fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<C>
@@ -72,5 +72,6 @@ open class FakeCallableDescriptorForObject(
override fun hashCode() = classDescriptor.hashCode()
override fun getContainingDeclaration() = classDescriptor.getClassObjectReferenceTarget().containingDeclaration
override fun substitute(substitutor: TypeSubstitutor) = TODO("Substitution of FakeCallableDescriptorForObject is not supported")
override fun substitute(substitutor: TypeSubstitutor) = this
}
@@ -27,8 +27,9 @@ class ReceiverValueWithSmartCastInfo(
val receiverValue: ReceiverValue,
val possibleTypes: Set<KotlinType>, // doesn't include receiver.type
val isStable: Boolean
): DetailedReceiver
): DetailedReceiver {
override fun toString() = receiverValue.toString()
}
interface QualifierReceiver : Receiver, DetailedReceiver {
val descriptor: DeclarationDescriptor
@@ -0,0 +1,343 @@
/*
* 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.types
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration.IntersectionStrategy.*
import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor
import org.jetbrains.kotlin.types.checker.NewCapturedType
import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
import org.jetbrains.kotlin.types.checker.intersectTypes
import org.jetbrains.kotlin.types.typeUtil.*
open class TypeApproximatorConfiguration {
enum class IntersectionStrategy {
ALLOWED,
TO_FIRST,
TO_COMMON_SUPERTYPE
}
open val flexible get() = false // simple flexible types (FlexibleTypeImpl)
open val dynamic get() = false // DynamicType
open val rawType get() = false // RawTypeImpl
open val errorType get() = false
open val intersection: IntersectionStrategy = TO_COMMON_SUPERTYPE
open val typeVariable: (TypeVariableTypeConstructor) -> Boolean = { false }
open val capturedType: (NewCapturedType) -> Boolean = { false } // true means that this type we can leave as is
abstract class AllFlexibleSameValue : TypeApproximatorConfiguration() {
abstract val allFlexible: Boolean
override val flexible get() = allFlexible
override val dynamic get() = allFlexible
override val rawType get() = allFlexible
}
object LocalDeclaration : AllFlexibleSameValue() {
override val allFlexible get() = true
override val intersection get() = ALLOWED
override val errorType get() = true
}
object PublicDeclaration : AllFlexibleSameValue() {
override val allFlexible get() = true
}
}
class TypeApproximator(private val commonSupertypeCalculator: CommonSupertypeCalculator) {
private val referenceApproximateToSuperType = this::approximateToSuperType
private val referenceApproximateToSubType = this::approximateToSubType
// null means that this input type is the result, i.e. input type not contains not-allowed kind of types
// type <: resultType
fun approximateToSuperType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? {
if (type is TypeUtils.SpecialType) return null
return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::upperBound, referenceApproximateToSuperType)
}
// resultType <: type
fun approximateToSubType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? {
if (type is TypeUtils.SpecialType) return null
return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::lowerBound, referenceApproximateToSubType)
}
// comments for case bound = upperBound, approximateTo = toSuperType
private fun approximateTo(
type: UnwrappedType,
conf: TypeApproximatorConfiguration,
bound: FlexibleType.() -> SimpleType,
approximateTo: (SimpleType, TypeApproximatorConfiguration) -> UnwrappedType?
): UnwrappedType? {
when (type) {
is SimpleType -> return approximateTo(type, conf)
is FlexibleType -> {
if (type is DynamicType) {
return if (conf.dynamic) null else type.bound()
}
else if (type is RawType) {
return if (conf.rawType) null else type.bound()
}
assert(type is FlexibleTypeImpl) {
"Unexpected subclass of FlexibleType: ${type::class.java.canonicalName}, type = $type"
}
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,
* 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.
* I.e. for every type B such as L_2 <: B, L_1 <: B. For example B = L_2.
*/
val lowerResult = approximateTo(type.lowerBound, conf)
val upperResult = approximateTo(type.upperBound, conf)
if (lowerResult == null && upperResult == null) return null
/**
* If C <: L..U then C <: L.
* inputType.lower <: lowerResult => inputType.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.
*/
return FlexibleTypeImpl(lowerResult?.lowerIfFlexible() ?: type.lowerBound,
upperResult?.upperIfFlexible() ?: type.upperBound)
}
else {
return type.bound().let { approximateTo(it, conf) ?: it }
}
}
}
}
private fun approximateIntersectionType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? {
val typeConstructor = type.constructor
assert(typeConstructor is IntersectionTypeConstructor) {
"Should be intersection type: $type, typeConstructor class: ${typeConstructor::class.java.canonicalName}"
}
assert(typeConstructor.supertypes.isNotEmpty()) {
"Supertypes for intersection type should not be empty: $type"
}
var thereIsApproximation = false
val newTypes = typeConstructor.supertypes.map {
val newType = if (toSuper) approximateToSuperType(it.unwrap(), conf) else approximateToSubType(it.unwrap(), conf)
if (newType != null) {
thereIsApproximation = true
newType
} else it.unwrap()
}
/**
* For case ALLOWED:
* A <: A', B <: B' => A & B <: A' & B'
*
* For other case -- it's impossible to find some type except Nothing as subType for intersection type.
*/
val baseResult = when (conf.intersection) {
ALLOWED -> if (!thereIsApproximation) return null else intersectTypes(newTypes)
TO_FIRST -> if (toSuper) newTypes.first() else return type.defaultResult(toSuper = false)
// commonSupertypeCalculator should handle flexible types correctly
TO_COMMON_SUPERTYPE -> if (toSuper) commonSupertypeCalculator(newTypes) else return type.defaultResult(toSuper = false)
}
return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult
}
private fun approximateCapturedType(type: NewCapturedType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? {
val supertypes = type.constructor.supertypes
val baseSuperType = when (supertypes.size) {
0 -> type.builtIns.nullableAnyType // Let C = in Int, then superType for C and C? is Any?
1 -> supertypes.single()
else -> intersectTypes(supertypes)
}
val baseSubType = type.lowerType ?: type.builtIns.nothingType
if (conf.capturedType(type)) {
/**
* Here everything is ok if bounds for this captured type should not be approximated.
* But. If such bounds contains some unauthorized types, then we cannot leave this captured type "as is".
* And we cannot create new capture type, because meaning of new captured type is not clear.
* So, we will just approximate such types
*
* todo handle flexible types
*/
if (approximateToSuperType(baseSuperType, conf) == null && approximateToSubType(baseSubType, conf) == null) {
return null
}
}
val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf) ?: baseSuperType else approximateToSubType(baseSubType, conf) ?: baseSubType
// C = in Int, Int <: C => Int? <: C?
// C = out Number, C <: Number => C? <: Number?
return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult
}
private fun approximateToSuperType(type: SimpleType, conf: TypeApproximatorConfiguration) = approximateTo(type, conf, toSuper = true)
private fun approximateToSubType(type: SimpleType, conf: TypeApproximatorConfiguration) = approximateTo(type, conf, toSuper = false)
private fun approximateTo(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? {
if (type.isError) {
// todo -- fix builtIns. Now builtIns here is DefaultBuiltIns
return if (conf.errorType) null else type.defaultResult(toSuper)
}
if (type.arguments.isNotEmpty()) {
return approximateParametrizedType(type, conf, toSuper)
}
val typeConstructor = type.constructor
if (typeConstructor is NewCapturedTypeConstructor) {
assert(type is NewCapturedType) { // KT-16147
"Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " +
"and class: ${type::class.java.canonicalName}. type.toString() = $type"
}
return approximateCapturedType(type as NewCapturedType, conf, toSuper)
}
if (typeConstructor is IntersectionTypeConstructor) {
return approximateIntersectionType(type, conf, toSuper)
}
if (typeConstructor is TypeVariableTypeConstructor) {
return if (conf.typeVariable(typeConstructor)) null else type.defaultResult(toSuper)
}
return null // simple classifier type
}
private fun isApproximateDirectionToSuper(effectiveVariance: Variance, toSuper: Boolean) =
when (effectiveVariance) {
Variance.OUT_VARIANCE -> toSuper
Variance.IN_VARIANCE -> !toSuper
Variance.INVARIANT -> throw AssertionError("Incorrect variance $effectiveVariance")
}
private fun approximateParametrizedType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): SimpleType? {
val parameters = type.constructor.parameters
val arguments = type.arguments
if (parameters.size != arguments.size) {
return if (conf.errorType) {
ErrorUtils.createErrorType("Inconsistent type: $type (parameters.size = ${parameters.size}, arguments.size = ${arguments.size})")
}
else type.defaultResult(toSuper)
}
val newArguments = arrayOfNulls<TypeProjection?>(arguments.size)
loop@ for (index in arguments.indices) {
val parameter = parameters[index]
val argument = arguments[index]
if (argument.isStarProjection) continue
val argumentType = argument.type.unwrap()
val effectiveVariance = NewKotlinTypeChecker.effectiveVariance(parameter.variance, argument.projectionKind)
when (effectiveVariance) {
null -> {
return if (conf.errorType) {
ErrorUtils.createErrorType("Inconsistent type: $type ($index parameter has declared variance: ${parameter.variance}, " +
"but argument variance is ${argument.projectionKind})")
} else type.defaultResult(toSuper)
}
Variance.OUT_VARIANCE, Variance.IN_VARIANCE -> {
/**
* Out<Foo> <: Out<superType(Foo)>
* Inv<out Foo> <: Inv<out superType(Foo)>
* In<Foo> <: In<subType(Foo)>
* Inv<in Foo> <: Inv<in subType(Foo)>
*/
val approximatedArgument = argumentType.let {
if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) approximateToSuperType(it, conf) else approximateToSubType(it, conf)
} ?: continue@loop
if (parameter.variance == Variance.INVARIANT) {
newArguments[index] = TypeProjectionImpl(effectiveVariance, approximatedArgument)
} else {
newArguments[index] = approximatedArgument.asTypeProjection()
}
}
Variance.INVARIANT -> {
if (!toSuper) {
// Inv<Foo> cannot be approximated to subType
val toSubType = approximateToSubType(argumentType, conf) ?: continue@loop
// Inv<Foo!> is supertype for Inv<Foo?>
if (!NewKotlinTypeChecker.equalTypes(argumentType, toSubType)) return type.defaultResult(toSuper)
newArguments[index] = argumentType.asTypeProjection()
continue@loop
}
/**
* Example with non-trivial both type approximations:
* Inv<In<C>> where C = in Int
* Inv<In<C>> <: Inv<out In<Int>>
* Inv<In<C>> <: Inv<in In<Any?>>
*
* So such case is rare and we will chose Inv<out In<Int>> for now.
*
* Note that for case Inv<C> we will chose Inv<in Int>, because it is more informative then Inv<out Any?>.
* May be we should do the same for deeper types, but not now.
*/
if (argumentType is NewCapturedType) {
val subType = approximateToSubType(argumentType, conf) ?: continue@loop
if (!subType.isTrivialSub()) {
newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, subType)
continue@loop
}
}
val approximatedSuperType = approximateToSuperType(argumentType, conf) ?: continue@loop // null means that this type we can leave as is
if (approximatedSuperType.isTrivialSuper()) {
val approximatedSubType = approximateToSubType(argumentType, conf) ?: continue@loop // seems like this is never null
if (!approximatedSubType.isTrivialSub()) {
newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, approximatedSubType)
continue@loop
}
}
newArguments[index] = TypeProjectionImpl(Variance.OUT_VARIANCE, approximatedSuperType)
}
}
}
if (newArguments.all { it == null }) return null
val newArgumentsList = arguments.mapIndexed { index, oldArgument -> newArguments[index] ?: oldArgument }
return type.replace(newArgumentsList)
}
private fun SimpleType.defaultResult(toSuper: Boolean) = if (toSuper) builtIns.nullableAnyType else {
if (isMarkedNullable) builtIns.nullableNothingType else builtIns.nothingType
}
// Any? or Any!
private fun UnwrappedType.isTrivialSuper() = upperIfFlexible().isNullableAny()
// Nothing or Nothing!
private fun UnwrappedType.isTrivialSub() = lowerIfFlexible().isNothing()
}