From 07937a27eb5767c29d28a75eb03cbd2d7f2cac31 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Fri, 26 Jun 2015 22:07:34 +0300 Subject: [PATCH] Added GenericCandidateResolver --- .../kotlin/resolve/calls/CallCompleter.kt | 54 ++-- .../kotlin/resolve/calls/CallResolver.java | 8 +- .../resolve/calls/CallResolverUtil.java | 31 ++- .../resolve/calls/CandidateResolver.java | 249 ++---------------- .../resolve/calls/GenericCandidateResolver.kt | 234 ++++++++++++++++ 5 files changed, 307 insertions(+), 269 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index 03ac7c51fa4..10f47212712 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -16,35 +16,34 @@ package org.jetbrains.kotlin.resolve.calls -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext -import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl -import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy -import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode -import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem -import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext.CONSTRAINT_SYSTEM_COMPLETER -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl -import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext -import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus -import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext -import org.jetbrains.kotlin.resolve.calls.callUtil.* import org.jetbrains.kotlin.resolve.BindingContextUtils -import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.TemporaryBindingTrace +import org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS +import org.jetbrains.kotlin.resolve.calls.CallResolverUtil.getEffectiveExpectedType +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl +import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl +import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.DataFlowUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils -import org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS -import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import java.util.ArrayList -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.* -import org.jetbrains.kotlin.resolve.calls.model.* public class CallCompleter( val argumentTypeResolver: ArgumentTypeResolver, @@ -211,7 +210,7 @@ public class CallCompleter( for (valueArgument in context.call.getValueArguments()) { val argumentMapping = getArgumentMapping(valueArgument!!) val expectedType = when (argumentMapping) { - is ArgumentMatch -> CandidateResolver.getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument) + is ArgumentMatch -> getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument) else -> TypeUtils.NO_EXPECTED_TYPE } val newContext = context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument)).replaceExpectedType(expectedType) @@ -226,8 +225,7 @@ public class CallCompleter( if (valueArgument.isExternal()) return val expression = valueArgument.getArgumentExpression() ?: return - val deparenthesized = ArgumentTypeResolver.getLastElementDeparenthesized(expression, context) - if (deparenthesized == null) return + val deparenthesized = ArgumentTypeResolver.getLastElementDeparenthesized(expression, context) ?: return val recordedType = expression.let { context.trace.getType(it) } var updatedType: JetType? = recordedType @@ -263,11 +261,9 @@ public class CallCompleter( ): OverloadResolutionResultsImpl<*>? { if (!ExpressionTypingUtils.dependsOnExpectedType(expression)) return null - val argumentCall = expression.getCall(context.trace.getBindingContext()) - if (argumentCall == null) return null + val argumentCall = expression.getCall(context.trace.getBindingContext()) ?: return null - val cachedDataForCall = context.resolutionResultsCache[argumentCall] - if (cachedDataForCall == null) return null + val cachedDataForCall = context.resolutionResultsCache[argumentCall] ?: return null val (cachedResolutionResults, cachedContext, tracing) = cachedDataForCall @suppress("UNCHECKED_CAST") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index befe4e08b78..80c19ad9380 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -71,6 +71,7 @@ public class CallResolver { private TypeResolver typeResolver; private CandidateResolver candidateResolver; private ArgumentTypeResolver argumentTypeResolver; + private GenericCandidateResolver genericCandidateResolver; private CallCompleter callCompleter; private TaskPrioritizer taskPrioritizer; private AdditionalCheckerProvider additionalCheckerProvider; @@ -98,6 +99,11 @@ public class CallResolver { this.argumentTypeResolver = argumentTypeResolver; } + @Inject + public void setGenericCandidateResolver(GenericCandidateResolver genericCandidateResolver) { + this.genericCandidateResolver = genericCandidateResolver; + } + @Inject public void setCallCompleter(@NotNull CallCompleter callCompleter) { this.callCompleter = callCompleter; @@ -480,7 +486,7 @@ public class CallResolver { CallCandidateResolutionContext candidateContext = CallCandidateResolutionContext.createForCallBeingAnalyzed( results.getResultingCall(), context, tracing); - candidateResolver.completeTypeInferenceDependentOnFunctionLiteralsForCall(candidateContext); + genericCandidateResolver.completeTypeInferenceDependentOnFunctionLiteralsForCall(candidateContext); } private static void cacheResults( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.java index fbea050b811..d4884e629f9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.java @@ -21,14 +21,8 @@ import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; -import org.jetbrains.kotlin.descriptors.CallableDescriptor; -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; -import org.jetbrains.kotlin.psi.Call; -import org.jetbrains.kotlin.psi.JetExpression; -import org.jetbrains.kotlin.psi.JetSimpleNameExpression; -import org.jetbrains.kotlin.psi.JetSuperExpression; +import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; @@ -165,4 +159,25 @@ public class CallResolverUtil { } return null; } + + @NotNull + public static JetType getEffectiveExpectedType(@NotNull ValueParameterDescriptor parameterDescriptor, @NotNull ValueArgument argument) { + if (argument.getSpreadElement() != null) { + if (parameterDescriptor.getVarargElementType() == null) { + // Spread argument passed to a non-vararg parameter, an error is already reported by ValueArgumentsToParametersMapper + return DONT_CARE; + } + else { + return parameterDescriptor.getType(); + } + } + else { + JetType varargElementType = parameterDescriptor.getVarargElementType(); + if (varargElementType != null) { + return varargElementType; + } + + return parameterDescriptor.getType(); + } + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java index 8dd1a19901b..8491a71e8f5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.resolve.calls; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; @@ -28,12 +27,12 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; -import org.jetbrains.kotlin.resolve.calls.context.*; -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem; -import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl; +import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext; +import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext; +import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode; +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus; -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory; import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils; @@ -49,29 +48,35 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils; import org.jetbrains.kotlin.types.expressions.JetTypeInfo; import javax.inject.Inject; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; import static org.jetbrains.kotlin.diagnostics.Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT; import static org.jetbrains.kotlin.diagnostics.Errors.SUPER_CANT_BE_EXTENSION_RECEIVER; -import static org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver.getLastElementDeparenthesized; -import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS; import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS; +import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.getEffectiveExpectedType; import static org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke; -import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT; -import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION; -import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION; import static org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.*; -import static org.jetbrains.kotlin.types.TypeUtils.*; +import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType; public class CandidateResolver { @NotNull private ArgumentTypeResolver argumentTypeResolver; + @NotNull + private GenericCandidateResolver genericCandidateResolver; @Inject public void setArgumentTypeResolver(@NotNull ArgumentTypeResolver argumentTypeResolver) { this.argumentTypeResolver = argumentTypeResolver; } + @Inject + public void setGenericCandidateResolver(@NotNull GenericCandidateResolver genericCandidateResolver) { + this.genericCandidateResolver = genericCandidateResolver; + } + public void performResolutionForCandidateCall( @NotNull CallCandidateResolutionContext context, @NotNull ResolutionTask task) { @@ -160,7 +165,7 @@ public class CandidateResolver { if (jetTypeArguments.isEmpty() && !candidate.getTypeParameters().isEmpty() && candidateCall.getKnownTypeParametersSubstitutor() == null) { - candidateCall.addStatus(inferTypeArguments(context)); + candidateCall.addStatus(genericCandidateResolver.inferTypeArguments(context)); } else { candidateCall.addStatus(checkAllValueArguments(context, SHAPE_FUNCTION_ARGUMENTS).status); @@ -287,203 +292,6 @@ public class CandidateResolver { return descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null; } - public void completeTypeInferenceDependentOnFunctionLiteralsForCall( - CallCandidateResolutionContext context - ) { - MutableResolvedCall resolvedCall = context.candidateCall; - ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem(); - if (constraintSystem == null) return; - - // constraints for function literals - // Value parameters - for (Map.Entry entry : resolvedCall.getValueArguments().entrySet()) { - ResolvedValueArgument resolvedValueArgument = entry.getValue(); - ValueParameterDescriptor valueParameterDescriptor = entry.getKey(); - - for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) { - addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context); - } - } - resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor()); - } - - private void addConstraintForFunctionLiteral( - @NotNull ValueArgument valueArgument, - @NotNull ValueParameterDescriptor valueParameterDescriptor, - @NotNull ConstraintSystem constraintSystem, - @NotNull CallCandidateResolutionContext context - ) { - JetExpression argumentExpression = valueArgument.getArgumentExpression(); - if (argumentExpression == null) return; - if (!ArgumentTypeResolver.isFunctionLiteralArgument(argumentExpression, context)) return; - - JetFunction functionLiteral = ArgumentTypeResolver.getFunctionLiteralArgument(argumentExpression, context); - - JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument); - JetType expectedType = constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT); - if (expectedType == null || TypeUtils.isDontCarePlaceholder(expectedType)) { - expectedType = argumentTypeResolver.getShapeTypeOfFunctionLiteral(functionLiteral, context.scope, context.trace, false); - } - if (expectedType == null || !KotlinBuiltIns.isFunctionOrExtensionFunctionType(expectedType) - || CallResolverUtil.hasUnknownFunctionParameter(expectedType)) { - return; - } - MutableDataFlowInfoForArguments dataFlowInfoForArguments = context.candidateCall.getDataFlowInfoForArguments(); - DataFlowInfo dataFlowInfoForArgument = dataFlowInfoForArguments.getInfo(valueArgument); - - //todo analyze function literal body once in 'dependent' mode, then complete it with respect to expected type - boolean hasExpectedReturnType = !CallResolverUtil.hasUnknownReturnType(expectedType); - if (hasExpectedReturnType) { - TemporaryTraceAndCache temporaryToResolveFunctionLiteral = TemporaryTraceAndCache.create( - context, "trace to resolve function literal with expected return type", argumentExpression); - - JetExpression statementExpression = JetPsiUtil.getExpressionOrLastStatementInBlock(functionLiteral.getBodyExpression()); - if (statementExpression == null) return; - boolean[] mismatch = new boolean[1]; - ObservableBindingTrace errorInterceptingTrace = ExpressionTypingUtils.makeTraceInterceptingTypeMismatch( - temporaryToResolveFunctionLiteral.trace, statementExpression, mismatch); - CallCandidateResolutionContext newContext = context - .replaceBindingTrace(errorInterceptingTrace).replaceExpectedType(expectedType) - .replaceDataFlowInfo(dataFlowInfoForArgument).replaceResolutionResultsCache(temporaryToResolveFunctionLiteral.cache) - .replaceContextDependency(INDEPENDENT); - JetType type = argumentTypeResolver.getFunctionLiteralTypeInfo( - argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS).getType(); - if (!mismatch[0]) { - constraintSystem.addSubtypeConstraint( - type, effectiveExpectedType, VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.getIndex())); - temporaryToResolveFunctionLiteral.commit(); - return; - } - } - JetType expectedTypeWithoutReturnType = hasExpectedReturnType ? CallResolverUtil.replaceReturnTypeByUnknown(expectedType) : expectedType; - CallCandidateResolutionContext newContext = context - .replaceExpectedType(expectedTypeWithoutReturnType).replaceDataFlowInfo(dataFlowInfoForArgument) - .replaceContextDependency(INDEPENDENT); - JetType type = argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, - RESOLVE_FUNCTION_ARGUMENTS).getType(); - constraintSystem.addSubtypeConstraint( - type, effectiveExpectedType, VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.getIndex())); - } - - private ResolutionStatus inferTypeArguments(CallCandidateResolutionContext context) { - MutableResolvedCall candidateCall = context.candidateCall; - final D candidate = candidateCall.getCandidateDescriptor(); - - ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl(); - - // If the call is recursive, e.g. - // fun foo(t : T) : T = foo(t) - // we can't use same descriptor objects for T's as actual type values and same T's as unknowns, - // because constraints become trivial (T :< T), and inference fails - // - // Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion) - CallableDescriptor candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate); - - Map typeVariables = Maps.newLinkedHashMap(); - for (TypeParameterDescriptor typeParameterDescriptor : candidateWithFreshVariables.getTypeParameters()) { - typeVariables.put(typeParameterDescriptor, Variance.INVARIANT); // TODO: variance of the occurrences - } - constraintSystem.registerTypeVariables(typeVariables); - - TypeSubstitutor substituteDontCare = - makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE); - - // Value parameters - for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) { - ResolvedValueArgument resolvedValueArgument = entry.getValue(); - ValueParameterDescriptor valueParameterDescriptor = candidateWithFreshVariables.getValueParameters().get(entry.getKey().getIndex()); - - - for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) { - // TODO : more attempts, with different expected types - - // Here we type check expecting an error type (DONT_CARE, substitution with substituteDontCare) - // and throw the results away - // We'll type check the arguments later, with the inferred types expected - addConstraintForValueArgument(valueArgument, valueParameterDescriptor, substituteDontCare, constraintSystem, - context, SHAPE_FUNCTION_ARGUMENTS); - } - } - - // Receiver - // Error is already reported if something is missing - ReceiverValue receiverArgument = candidateCall.getExtensionReceiver(); - ReceiverParameterDescriptor receiverParameter = candidateWithFreshVariables.getExtensionReceiverParameter(); - if (receiverArgument.exists() && receiverParameter != null) { - JetType receiverType = - context.candidateCall.isSafeCall() - ? TypeUtils.makeNotNullable(receiverArgument.getType()) - : receiverArgument.getType(); - if (receiverArgument instanceof ExpressionReceiver) { - receiverType = updateResultTypeForSmartCasts( - receiverType, ((ExpressionReceiver) receiverArgument).getExpression(), context); - } - constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), RECEIVER_POSITION.position()); - } - - // Restore type variables before alpha-conversion - ConstraintSystem constraintSystemWithRightTypeParameters = constraintSystem.substituteTypeVariables( - new Function1() { - @Override - public TypeParameterDescriptor invoke(@NotNull TypeParameterDescriptor typeParameterDescriptor) { - return candidate.getTypeParameters().get(typeParameterDescriptor.getIndex()); - } - } - ); - candidateCall.setConstraintSystem(constraintSystemWithRightTypeParameters); - - - // Solution - boolean hasContradiction = constraintSystem.getStatus().hasContradiction(); - if (!hasContradiction) { - return INCOMPLETE_TYPE_INFERENCE; - } - return OTHER_ERROR; - } - - private void addConstraintForValueArgument( - @NotNull ValueArgument valueArgument, - @NotNull ValueParameterDescriptor valueParameterDescriptor, - @NotNull TypeSubstitutor substitutor, - @NotNull ConstraintSystem constraintSystem, - @NotNull CallCandidateResolutionContext context, - @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) { - - JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument); - JetExpression argumentExpression = valueArgument.getArgumentExpression(); - - JetType expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT); - DataFlowInfo dataFlowInfoForArgument = context.candidateCall.getDataFlowInfoForArguments().getInfo(valueArgument); - CallResolutionContext newContext = context.replaceExpectedType(expectedType).replaceDataFlowInfo(dataFlowInfoForArgument); - - JetTypeInfo typeInfoForCall = argumentTypeResolver.getArgumentTypeInfo( - argumentExpression, newContext, resolveFunctionArgumentBodies); - context.candidateCall.getDataFlowInfoForArguments().updateInfo(valueArgument, typeInfoForCall.getDataFlowInfo()); - - JetType type = updateResultTypeForSmartCasts( - typeInfoForCall.getType(), argumentExpression, context.replaceDataFlowInfo(dataFlowInfoForArgument)); - constraintSystem.addSubtypeConstraint( - type, effectiveExpectedType, VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.getIndex())); - } - - @Nullable - private static JetType updateResultTypeForSmartCasts( - @Nullable JetType type, - @Nullable JetExpression argumentExpression, - @NotNull ResolutionContext context - ) { - JetExpression deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context); - if (deparenthesizedArgument == null || type == null) return type; - - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context); - if (!dataFlowValue.isPredictable()) return type; - - Set possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue); - if (possibleTypes.isEmpty()) return type; - - return TypeUtils.intersect(JetTypeChecker.DEFAULT, possibleTypes); - } - @NotNull private ValueArgumentsCheckingResult checkAllValueArguments( @NotNull CallCandidateResolutionContext context, @@ -696,27 +504,6 @@ public class CandidateResolver { } } - @NotNull - public static JetType getEffectiveExpectedType(ValueParameterDescriptor parameterDescriptor, ValueArgument argument) { - if (argument.getSpreadElement() != null) { - if (parameterDescriptor.getVarargElementType() == null) { - // Spread argument passed to a non-vararg parameter, an error is already reported by ValueArgumentsToParametersMapper - return DONT_CARE; - } - else { - return parameterDescriptor.getType(); - } - } - else { - JetType varargElementType = parameterDescriptor.getVarargElementType(); - if (varargElementType != null) { - return varargElementType; - } - - return parameterDescriptor.getType(); - } - } - private static void checkGenericBoundsInAFunctionCall( @NotNull List jetTypeArguments, @NotNull List typeArguments, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt new file mode 100644 index 00000000000..86b56658ea0 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -0,0 +1,234 @@ +/* + * Copyright 2010-2015 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 com.google.common.collect.Maps +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetPsiUtil +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil +import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver.getLastElementDeparenthesized +import org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS +import org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS +import org.jetbrains.kotlin.resolve.calls.CallResolverUtil.getEffectiveExpectedType +import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION +import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus +import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE +import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE +import org.jetbrains.kotlin.types.TypeUtils.makeConstantSubstitutor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.checker.JetTypeChecker +import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils + + +class GenericCandidateResolver( + val argumentTypeResolver: ArgumentTypeResolver +) { + + fun inferTypeArguments(context: CallCandidateResolutionContext): ResolutionStatus { + val candidateCall = context.candidateCall + val candidate = candidateCall.getCandidateDescriptor() + + val constraintSystem = ConstraintSystemImpl() + + // If the call is recursive, e.g. + // fun foo(t : T) : T = foo(t) + // we can't use same descriptor objects for T's as actual type values and same T's as unknowns, + // because constraints become trivial (T :< T), and inference fails + // + // Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion) + val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate) + + val typeVariables = Maps.newLinkedHashMap() + for (typeParameterDescriptor in candidateWithFreshVariables.getTypeParameters()) { + typeVariables.put(typeParameterDescriptor, Variance.INVARIANT) // TODO: variance of the occurrences + } + constraintSystem.registerTypeVariables(typeVariables) + + val substituteDontCare = makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE) + + // Value parameters + for (entry in candidateCall.getValueArguments().entrySet()) { + val resolvedValueArgument = entry.getValue() + val valueParameterDescriptor = candidateWithFreshVariables.getValueParameters().get(entry.getKey().getIndex()) + + + for (valueArgument in resolvedValueArgument.getArguments()) { + // TODO : more attempts, with different expected types + + // Here we type check expecting an error type (DONT_CARE, substitution with substituteDontCare) + // and throw the results away + // We'll type check the arguments later, with the inferred types expected + addConstraintForValueArgument(valueArgument, valueParameterDescriptor, substituteDontCare, + constraintSystem, context, SHAPE_FUNCTION_ARGUMENTS) + } + } + + // Receiver + // Error is already reported if something is missing + val receiverArgument = candidateCall.getExtensionReceiver() + val receiverParameter = candidateWithFreshVariables.getExtensionReceiverParameter() + if (receiverArgument.exists() && receiverParameter != null) { + var receiverType: JetType? = if (context.candidateCall.isSafeCall()) + TypeUtils.makeNotNullable(receiverArgument.getType()) + else + receiverArgument.getType() + if (receiverArgument is ExpressionReceiver) { + receiverType = updateResultTypeForSmartCasts(receiverType, receiverArgument.getExpression(), context) + } + constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), RECEIVER_POSITION.position()) + } + + // Restore type variables before alpha-conversion + val constraintSystemWithRightTypeParameters = constraintSystem.substituteTypeVariables { + candidate.getTypeParameters().get(it.getIndex()) + } + candidateCall.setConstraintSystem(constraintSystemWithRightTypeParameters) + + + // Solution + val hasContradiction = constraintSystem.getStatus().hasContradiction() + if (!hasContradiction) { + return INCOMPLETE_TYPE_INFERENCE + } + return OTHER_ERROR + } + + fun addConstraintForValueArgument( + valueArgument: ValueArgument, + valueParameterDescriptor: ValueParameterDescriptor, + substitutor: TypeSubstitutor, + constraintSystem: ConstraintSystem, + context: CallCandidateResolutionContext<*>, + resolveFunctionArgumentBodies: CallResolverUtil.ResolveArgumentsMode + ) { + + val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument) + val argumentExpression = valueArgument.getArgumentExpression() + + val expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT) + val dataFlowInfoForArgument = context.candidateCall.getDataFlowInfoForArguments().getInfo(valueArgument) + val newContext = context.replaceExpectedType(expectedType).replaceDataFlowInfo(dataFlowInfoForArgument) + + val typeInfoForCall = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, resolveFunctionArgumentBodies) + context.candidateCall.getDataFlowInfoForArguments().updateInfo(valueArgument, typeInfoForCall.dataFlowInfo) + + val type = updateResultTypeForSmartCasts(typeInfoForCall.type, argumentExpression, context.replaceDataFlowInfo(dataFlowInfoForArgument)) + constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.getIndex())) + } + + private fun updateResultTypeForSmartCasts(type: JetType?, argumentExpression: JetExpression?, context: ResolutionContext<*>): JetType? { + val deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context) + if (deparenthesizedArgument == null || type == null) return type + + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context) + if (!dataFlowValue.isPredictable()) return type + + val possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue) + if (possibleTypes.isEmpty()) return type + + return TypeUtils.intersect(JetTypeChecker.DEFAULT, possibleTypes) + } + + public fun completeTypeInferenceDependentOnFunctionLiteralsForCall( + context: CallCandidateResolutionContext + ) { + val resolvedCall = context.candidateCall + val constraintSystem = resolvedCall.getConstraintSystem() ?: return + + // constraints for function literals + // Value parameters + for (entry in resolvedCall.getValueArguments().entrySet()) { + val resolvedValueArgument = entry.getValue() + val valueParameterDescriptor = entry.getKey() + + for (valueArgument in resolvedValueArgument.getArguments()) { + addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context) + } + } + resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor()) + } + + private fun addConstraintForFunctionLiteral( + valueArgument: ValueArgument, + valueParameterDescriptor: ValueParameterDescriptor, + constraintSystem: ConstraintSystem, + context: CallCandidateResolutionContext + ) { + val argumentExpression = valueArgument.getArgumentExpression() ?: return + if (!ArgumentTypeResolver.isFunctionLiteralArgument(argumentExpression, context)) return + + val functionLiteral = ArgumentTypeResolver.getFunctionLiteralArgument(argumentExpression, context) + + val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument) + var expectedType = constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT) + if (expectedType == null || TypeUtils.isDontCarePlaceholder(expectedType)) { + expectedType = argumentTypeResolver.getShapeTypeOfFunctionLiteral(functionLiteral, context.scope, context.trace, false) + } + if (expectedType == null || !KotlinBuiltIns.isFunctionOrExtensionFunctionType(expectedType) + || CallResolverUtil.hasUnknownFunctionParameter(expectedType)) { + return + } + val dataFlowInfoForArguments = context.candidateCall.getDataFlowInfoForArguments() + val dataFlowInfoForArgument = dataFlowInfoForArguments.getInfo(valueArgument) + + //todo analyze function literal body once in 'dependent' mode, then complete it with respect to expected type + val hasExpectedReturnType = !CallResolverUtil.hasUnknownReturnType(expectedType) + val position = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.getIndex()) + if (hasExpectedReturnType) { + val temporaryToResolveFunctionLiteral = TemporaryTraceAndCache.create( + context, "trace to resolve function literal with expected return type", argumentExpression) + + val statementExpression = JetPsiUtil.getExpressionOrLastStatementInBlock(functionLiteral.getBodyExpression()) ?: return + val mismatch = BooleanArray(1) + val errorInterceptingTrace = ExpressionTypingUtils.makeTraceInterceptingTypeMismatch( + temporaryToResolveFunctionLiteral.trace, statementExpression, mismatch) + val newContext = context.replaceBindingTrace(errorInterceptingTrace).replaceExpectedType(expectedType) + .replaceDataFlowInfo(dataFlowInfoForArgument).replaceResolutionResultsCache(temporaryToResolveFunctionLiteral.cache) + .replaceContextDependency(INDEPENDENT) + val type = argumentTypeResolver.getFunctionLiteralTypeInfo( + argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS).type + if (!mismatch[0]) { + constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, position) + temporaryToResolveFunctionLiteral.commit() + return + } + } + val expectedTypeWithoutReturnType = if (hasExpectedReturnType) CallResolverUtil.replaceReturnTypeByUnknown(expectedType) else expectedType + val newContext = context.replaceExpectedType(expectedTypeWithoutReturnType).replaceDataFlowInfo(dataFlowInfoForArgument) + .replaceContextDependency(INDEPENDENT) + val type = argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS).type + constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, position) + } +} \ No newline at end of file