Improve inference on generics for callable references

#KT-10711 Fixed
 #KT-12802 Fixed
 #KT-12964 Fixed
 #KT-15439 Fixed

Analyze callable references in `dependent` mode, then complete them with
respect to expected types
This commit is contained in:
Mikhail Zarechenskiy
2017-01-17 02:30:16 +03:00
parent 07bb7ef4d3
commit 2cac6a9e7d
50 changed files with 938 additions and 22 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve.calls
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings
@@ -157,14 +158,13 @@ class CallCompleter(
val expectedReturnType =
if (call.isCallableReference()) {
// TODO: compute generic type argument for R in the kotlin.Function<R> supertype (KT-12963)
// TODO: also add constraints for parameter types (KT-12964)
if (!TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionType) expectedType.getReturnTypeFromFunctionType()
else TypeUtils.NO_EXPECTED_TYPE
}
else expectedType
fun ConstraintSystem.Builder.returnTypeInSystem(): KotlinType? =
returnType?.let {
fun ConstraintSystem.Builder.typeInSystem(type: KotlinType?): KotlinType? =
type?.let {
val substitutor = typeVariableSubstitutors[call.toHandle()] ?: error("No substitutor for call: $call")
substitutor.substitute(it, Variance.INVARIANT)
}
@@ -178,7 +178,7 @@ class CallCompleter(
if (returnType != null && !TypeUtils.noExpectedType(expectedReturnType)) {
updateSystemIfNeeded { builder ->
val returnTypeInSystem = builder.returnTypeInSystem()
val returnTypeInSystem = builder.typeInSystem(returnType)
if (returnTypeInSystem != null) {
builder.addSubtypeConstraint(returnTypeInSystem, expectedReturnType, EXPECTED_TYPE_POSITION.position())
builder.build()
@@ -202,7 +202,7 @@ class CallCompleter(
if (returnType != null && expectedReturnType === TypeUtils.UNIT_EXPECTED_TYPE) {
updateSystemIfNeeded { builder ->
val returnTypeInSystem = builder.returnTypeInSystem()
val returnTypeInSystem = builder.typeInSystem(returnType)
if (returnTypeInSystem != null) {
builder.addSubtypeConstraint(returnTypeInSystem, builtIns.unitType, EXPECTED_TYPE_POSITION.position())
val system = builder.build()
@@ -212,6 +212,16 @@ class CallCompleter(
}
}
if (call.isCallableReference() && !TypeUtils.noExpectedType(expectedType) && expectedType.isFunctionType) {
updateSystemIfNeeded { builder ->
candidateDescriptor.valueParameters.zip(expectedType.getValueParameterTypesFromFunctionType()).forEach { (parameter, argument) ->
val valueParameterInSystem = builder.typeInSystem(parameter.type)
builder.addSubtypeConstraint(valueParameterInSystem, argument.type, VALUE_PARAMETER_POSITION.position(parameter.index))
}
builder.build()
}
}
val builder = constraintSystem!!.toBuilder()
builder.fixVariables()
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.*
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS
@@ -51,8 +52,11 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.ResolveConstruct
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
private val SPECIAL_FUNCTION_NAMES = ResolveConstruct.values().map { it.specialFunctionName }.toSet()
class GenericCandidateResolver(
private val argumentTypeResolver: ArgumentTypeResolver,
private val coroutineInferenceSupport: CoroutineInferenceSupport
@@ -186,6 +190,9 @@ class GenericCandidateResolver(
if (addConstraintForNestedCall(argumentExpression, constraintPosition, builder, newContext, effectiveExpectedType)) return
val type = updateResultTypeForSmartCasts(typeInfoForCall.type, argumentExpression, context.replaceDataFlowInfo(dataFlowInfoForArgument))
if (argumentExpression is KtCallableReferenceExpression && type == null) return
builder.addSubtypeConstraint(
type,
builder.compositeSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT),
@@ -273,8 +280,28 @@ class GenericCandidateResolver(
addConstraintForFunctionLiteralArgument(functionLiteral, valueArgument, valueParameterDescriptor, constraintSystem, newContext,
resolvedCall.candidateDescriptor.returnType)
}
// as inference for callable references depends on expected type,
// we should postpone reporting errors on them until all types will be inferred
// We do not replace trace for special calls (e.g. if-expressions) because of their specific analysis
// For example, type info for arguments is needed before call will be completed (See ControlStructureTypingVisitor.visitIfExpression)
val temporaryContextForCall = if (resolvedCall.candidateDescriptor.name in SPECIAL_FUNCTION_NAMES) {
newContext
}
else {
val temporaryBindingTrace = TemporaryBindingTrace.create(
newContext.trace, "Trace to complete argument for call that might be not resulting call")
newContext.replaceBindingTrace(temporaryBindingTrace)
}
ArgumentTypeResolver.getCallableReferenceExpressionIfAny(argumentExpression, newContext)?.let { callableReference ->
addConstraintForCallableReference(callableReference, valueArgument, valueParameterDescriptor, constraintSystem, newContext)
addConstraintForCallableReference(
callableReference,
valueArgument,
valueParameterDescriptor,
constraintSystem,
temporaryContextForCall)
}
}
}
@@ -376,7 +403,7 @@ class GenericCandidateResolver(
val expectedType = getExpectedTypeForCallableReference(callableReference, constraintSystem, context, effectiveExpectedType)
?: return
if (!ReflectionTypes.isCallableType(expectedType)) return
val resolvedType = getResolvedTypeForCallableReference(callableReference, context, expectedType, valueArgument)
val resolvedType = getResolvedTypeForCallableReference(callableReference, context, expectedType, valueArgument) ?: return
val position = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index)
constraintSystem.addSubtypeConstraint(
resolvedType,
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -77,6 +77,14 @@ public class ControlStructureTypingUtils {
public String getName() {
return name;
}
public Name getSpecialFunctionName() {
return Name.identifier("<SPECIAL-FUNCTION-FOR-" + name.toUpperCase() + "-RESOLVE>");
}
public Name getSpecialTypeParameterName() {
return Name.identifier("<TYPE-PARAMETER-FOR-" + name.toUpperCase() + "-RESOLVE>");
}
}
private final CallResolver callResolver;
@@ -139,16 +147,14 @@ public class ControlStructureTypingUtils {
) {
assert argumentNames.size() == isArgumentNullable.size();
String constructionName = construct.getName().toUpperCase();
Name specialFunctionName = Name.identifier("<SPECIAL-FUNCTION-FOR-" + constructionName + "-RESOLVE>");
SimpleFunctionDescriptorImpl function = SimpleFunctionDescriptorImpl.create(
moduleDescriptor, Annotations.Companion.getEMPTY(), specialFunctionName, CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE
moduleDescriptor, Annotations.Companion.getEMPTY(), construct.getSpecialFunctionName(),
CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE
);
TypeParameterDescriptor typeParameter = TypeParameterDescriptorImpl.createWithDefaultBound(
function, Annotations.Companion.getEMPTY(), false, Variance.INVARIANT,
Name.identifier("<TYPE-PARAMETER-FOR-" + constructionName + "-RESOLVE>"), 0);
construct.getSpecialTypeParameterName(), 0);
KotlinType type = typeParameter.getDefaultType();
KotlinType nullableType = TypeUtils.makeNullable(type);
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -606,11 +606,16 @@ class DoubleColonExpressionResolver(
outerContext: ResolutionContext<*>,
resolutionMode: ResolveArgumentsMode
): OverloadResolutionResults<CallableDescriptor>? {
val call = CallMaker.makeCall(reference, receiver, null, reference, emptyList())
// we should preserve information about `call` because callable references are analyzed two times,
// otherwise there will be not completed calls in trace
val call = outerContext.trace[BindingContext.CALL, reference] ?: CallMaker.makeCall(reference, receiver, null, reference, emptyList())
val temporaryTrace = TemporaryTraceAndCache.create(outerContext, traceTitle, reference)
val newContext =
if (resolutionMode == ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS)
outerContext.replaceTraceAndCache(temporaryTrace).replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)
outerContext
.replaceTraceAndCache(temporaryTrace)
.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)
.replaceContextDependency(ContextDependency.DEPENDENT)
else
outerContext.replaceTraceAndCache(temporaryTrace)