From 3eee19578a82b2b61e0fd1252067bd733cf6e6b3 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Tue, 5 Feb 2013 20:04:13 +0400 Subject: [PATCH] complete inference for inner calls after resolve&inference of outer call finishes deferredComputationsForArguments stored in ResolvedCallImpl, 'completeValueArgumentsInference' completes inference for inner calls recursively --- .../resolve/calls/CallExpressionResolver.java | 20 ++- .../jet/lang/resolve/calls/CallResolver.java | 98 ++++++------- .../lang/resolve/calls/CallResolverUtil.java | 33 +++++ .../lang/resolve/calls/CandidateResolver.java | 129 ++++++++++++------ .../calls/context/TypeInfoForCall.java | 3 +- .../resolve/calls/model/ResolvedCallImpl.java | 40 +++++- .../completeValueArgumentsInference.kt | 19 +++ .../checkers/JetDiagnosticsTestGenerated.java | 16 ++- 8 files changed, 252 insertions(+), 106 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/complicated/completeValueArgumentsInference.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallExpressionResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallExpressionResolver.java index 55975a4a522..69b3d8dae43 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallExpressionResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallExpressionResolver.java @@ -157,8 +157,10 @@ public class CallExpressionResolver { if (!results.isNothing()) { checkSuper(receiver, results, context.trace, callExpression); result[0] = true; - if (results.isIncomplete()) { - return null; + if (results.isSingleResult() && resolveMode == ResolveMode.NORMAL) { + if (CallResolverUtil.hasReturnTypeDependentOnNotInferredParams(results.getResultingCall())) { + return null; + } } return results.isSingleResult() ? results.getResultingCall() : null; } @@ -246,12 +248,14 @@ public class CallExpressionResolver { @Nullable ASTNode callOperationNode, @NotNull ResolutionContext context, @NotNull ResolveMode resolveMode ) { TypeInfoForCall typeInfoForCall = getCallExpressionExtendedTypeInfoWithoutFinalTypeCheck(callExpression, receiver, callOperationNode, context, resolveMode); - DataFlowUtils.checkType(typeInfoForCall.getType(), callExpression, context, typeInfoForCall.getDataFlowInfo()); + if (resolveMode == ResolveMode.NORMAL) { + DataFlowUtils.checkType(typeInfoForCall.getType(), callExpression, context, typeInfoForCall.getDataFlowInfo()); + } return typeInfoForCall; } @NotNull - private TypeInfoForCall getCallExpressionExtendedTypeInfoWithoutFinalTypeCheck( + public TypeInfoForCall getCallExpressionExtendedTypeInfoWithoutFinalTypeCheck( @NotNull JetCallExpression callExpression, @NotNull ReceiverValue receiver, @Nullable ASTNode callOperationNode, @NotNull ResolutionContext context, @NotNull ResolveMode resolveMode ) { @@ -385,8 +389,10 @@ public class CallExpressionResolver { if (selectorReturnType != null) { context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, selectorReturnType); } - return TypeInfoForCall.create( - DataFlowUtils.checkType(selectorReturnType, expression, context, selectorReturnTypeInfo.getDataFlowInfo()), - selectorReturnTypeInfo); + JetTypeInfo typeInfo = JetTypeInfo.create(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo()); + if (resolveMode == ResolveMode.NORMAL) { + DataFlowUtils.checkType(typeInfo.getType(), expression, context, typeInfo.getDataFlowInfo()); + } + return TypeInfoForCall.create(typeInfo, selectorReturnTypeInfo); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 58ab290c7f1..4e17429b1f4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -278,85 +278,76 @@ public class CallResolver { } if (results == null) { BasicCallResolutionContext newContext = context.replaceBindingTrace(traceToResolveCall); - OverloadResolutionResults resultsWithFixedResolve = doResolveCall(newContext, prioritizedTasks, callTransformer, reference); - if (resultsWithFixedResolve instanceof OverloadResolutionResultsImpl) { - DelegatingBindingTrace deltasTraceForTypeInference = ((OverloadResolutionResultsImpl) resultsWithFixedResolve).getTrace(); + results = doResolveCall(newContext, prioritizedTasks, callTransformer, reference); + if (results instanceof OverloadResolutionResultsImpl) { + DelegatingBindingTrace deltasTraceForTypeInference = ((OverloadResolutionResultsImpl) results).getTrace(); if (deltasTraceForTypeInference != null) { deltasTraceForTypeInference.addAllMyDataTo(traceToResolveCall); } } - results = completeTypeInferenceDependentOnFunctionLiterals(newContext, resultsWithFixedResolve, TracingStrategy.EMPTY); + completeTypeInferenceDependentOnFunctionLiterals(newContext, results, TracingStrategy.EMPTY); cacheResults(resolutionResultsSlice, context, results, traceToResolveCall); } + traceToResolveCall.commit(); if (prioritizedTasks.isEmpty()) { - traceToResolveCall.commit(); return results; } + if (context.resolveMode == ResolveMode.INTERNAL) return results; + TracingStrategy tracing = prioritizedTasks.iterator().next().tracing; - OverloadResolutionResults completeResults = completeTypeInferenceDependentOnExpectedType( - context.replaceBindingTrace(traceToResolveCall), results, tracing); - traceToResolveCall.commit(); - return completeResults; + return completeTypeInferenceDependentOnExpectedType(context, results, tracing); } - private OverloadResolutionResults completeTypeInferenceDependentOnFunctionLiterals( + private void completeTypeInferenceDependentOnFunctionLiterals( @NotNull BasicCallResolutionContext context, - @NotNull OverloadResolutionResults resultsWithIncompleteTypeInference, + @NotNull OverloadResolutionResults results, @NotNull TracingStrategy tracing ) { - if (resultsWithIncompleteTypeInference.getResultCode() != OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) { - return resultsWithIncompleteTypeInference; - } + if (results.getResultCode() != OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) return; - Set> candidates = Sets.newLinkedHashSet( - (Collection>) resultsWithIncompleteTypeInference.getResultingCalls()); - ResolvedCallWithTrace maximallySpecific = OverloadingConflictResolver.INSTANCE.findMaximallySpecific(candidates, false); - if (maximallySpecific != null && maximallySpecific instanceof ResolvedCallImpl) { - candidateResolver.completeTypeInferenceDependentOnFunctionLiteralsForCall( - CallCandidateResolutionContext.createForCallBeingAnalyzed((ResolvedCallImpl) maximallySpecific, context, tracing)); - for (ResolvedCallWithTrace callWithUnknownTypeParameters : candidates) { - if (callWithUnknownTypeParameters != maximallySpecific) { - ((ResolvedCallImpl) callWithUnknownTypeParameters).addStatus(ResolutionStatus.OTHER_ERROR); - } - } - return OverloadResolutionResultsImpl.incompleteTypeInference(Collections.singleton(maximallySpecific)); + //todo[ResolvedCallImpl] get rid of instanceof & cast + if (!results.isSingleResult() || !(results.getResultingCall() instanceof ResolvedCallImpl)) { + argumentTypeResolver.checkTypesWithNoCallee(context, RESOLVE_FUNCTION_ARGUMENTS); + return; } - for (ResolvedCallWithTrace callWithUnknownTypeParameters : candidates) { - ResolvedCallImpl resolvedCall = (ResolvedCallImpl) callWithUnknownTypeParameters; - resolvedCall.addStatus(ResolutionStatus.INCOMPLETE_TYPE_INFERENCE); - } - return OverloadResolutionResultsImpl.incompleteTypeInference(candidates); + CallCandidateResolutionContext candidateContext = CallCandidateResolutionContext.createForCallBeingAnalyzed( + (ResolvedCallImpl) results.getResultingCall(), context, tracing); + candidateResolver.completeTypeInferenceDependentOnFunctionLiteralsForCall(candidateContext); } private OverloadResolutionResults completeTypeInferenceDependentOnExpectedType( @NotNull BasicCallResolutionContext context, - @NotNull OverloadResolutionResults resultsWithIncompleteTypeInference, + @NotNull OverloadResolutionResults results, @NotNull TracingStrategy tracing ) { - if (resultsWithIncompleteTypeInference.getResultCode() != OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) { - return resultsWithIncompleteTypeInference; - } - - Set> resultingCalls = null; - if (resultsWithIncompleteTypeInference.isSingleResult()) { - ResolvedCallWithTrace resolvedCall = (ResolvedCallWithTrace) resultsWithIncompleteTypeInference.getResultingCall(); - if (resolvedCall.hasUnknownTypeParameters() && resolvedCall instanceof ResolvedCallImpl) { - ResolvedCallImpl copy = CallResolverUtil.copy((ResolvedCallImpl) resolvedCall, context); - candidateResolver.completeTypeInferenceDependentOnExpectedTypeForCall( - CallCandidateResolutionContext.createForCallBeingAnalyzed(copy, context, tracing)); - resultingCalls = Collections.>singleton(copy); + if (results.isSingleResult()) { + ResolvedCall call = results.getResultingCall(); + if (call instanceof ResolvedCallImpl) { + argumentTypeResolver.checkUnmappedArgumentTypes(context, ((ResolvedCallImpl)call).getUnmappedArguments()); } } - if (resultingCalls == null) { - resultingCalls = (Set>) resultsWithIncompleteTypeInference.getResultingCalls(); + + if (!results.isSingleResult()) return results; + + ResolvedCallWithTrace resolvedCall = (ResolvedCallWithTrace) results.getResultingCall(); + //todo[ResolvedCallImpl] + if (!(resolvedCall instanceof ResolvedCallImpl)) return results; + if (!resolvedCall.hasUnknownTypeParameters()) { + CallCandidateResolutionContext callCandidateResolutionContext = + CallCandidateResolutionContext.createForCallBeingAnalyzed((ResolvedCallImpl) resolvedCall, context, tracing); + candidateResolver.completeValueArgumentsInference(callCandidateResolutionContext); + return results; } - OverloadResolutionResultsImpl results = ResolutionResultsHandler.INSTANCE.computeResultAndReportErrors( - context.trace, tracing, resultingCalls); - if (!results.isSingleResult()) { - argumentTypeResolver.checkTypesWithNoCallee(context, RESOLVE_FUNCTION_ARGUMENTS); + ResolvedCallImpl copy = CallResolverUtil.copy((ResolvedCallImpl) resolvedCall, context); + CallCandidateResolutionContext callCandidateResolutionContext = + CallCandidateResolutionContext.createForCallBeingAnalyzed(copy, context, tracing); + candidateResolver.completeTypeInferenceDependentOnExpectedTypeForCall(callCandidateResolutionContext); + + if (copy.getStatus().isSuccess()) { + return OverloadResolutionResultsImpl.success(copy); } - return results; + return OverloadResolutionResultsImpl.incompleteTypeInference(copy); } private void cacheResults(@NotNull WritableSlice> resolutionResultsSlice, @@ -407,9 +398,8 @@ public class CallResolver { OverloadResolutionResultsImpl resultsForFirstNonemptyCandidateSet = null; for (ResolutionTask task : prioritizedTasks) { TemporaryBindingTrace taskTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve a task for", task.reference); - OverloadResolutionResultsImpl results = performResolutionGuardedForExtraFunctionLiteralArguments(task.replaceBindingTrace( - taskTrace), - callTransformer, context.trace); + OverloadResolutionResultsImpl results = performResolutionGuardedForExtraFunctionLiteralArguments( + task.replaceBindingTrace(taskTrace), callTransformer, context.trace); if (results.isSuccess() || results.isAmbiguity()) { taskTrace.commit(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java index 933d73bfda6..a78b76452d6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolverUtil.java @@ -21,18 +21,24 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; +import org.jetbrains.jet.lang.psi.ValueArgument; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace; import org.jetbrains.jet.lang.resolve.calls.context.CallResolutionContext; import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil; +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl; +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; public class CallResolverUtil { @@ -74,6 +80,11 @@ public class CallResolverUtil { copy.recordValueArgument(entry.getKey(), entry.getValue()); } copy.setInitialDataFlowInfo(call.getDataFlowInfo()); + for (ResolvedValueArgument resolvedArgument : call.getValueArguments().values()) { + for (ValueArgument argument : resolvedArgument.getArguments()) { + copy.addDeferredComputationForArgument(argument, call.getDeferredComputationForArgument(argument)); + } + } return copy; } @@ -113,4 +124,26 @@ public class CallResolverUtil { newArguments.add(new TypeProjection(Variance.INVARIANT, DONT_CARE)); return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), type.isNullable(), newArguments, type.getMemberScope()); } + + public static boolean hasReturnTypeDependentOnNotInferredParams(@NotNull ResolvedCall resolvedCall) { + //todo[ResolvedCallImpl] + if (!(resolvedCall instanceof ResolvedCallImpl)) return false; + ResolvedCallImpl call = (ResolvedCallImpl) resolvedCall; + ConstraintSystem constraintSystem = call.getConstraintSystem(); + if (constraintSystem == null) return false; + + CallableDescriptor candidateDescriptor = call.getCandidateDescriptor(); + JetType returnType = candidateDescriptor.getReturnType(); + if (returnType == null) return false; + + for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) { + JetType inferredValueForTypeVariable = ConstraintsUtil.getValue(constraintSystem.getTypeConstraints(typeVariable)); + if (inferredValueForTypeVariable == null) { + if (TypeUtils.dependsOnTypeParameters(returnType, Collections.singleton(typeVariable))) { + return true; + } + } + } + return false; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index cf32ff33766..7e1e620bad8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -29,6 +29,7 @@ import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.*; import org.jetbrains.jet.lang.resolve.calls.context.CallCandidateResolutionContext; import org.jetbrains.jet.lang.resolve.calls.context.CallResolutionContext; +import org.jetbrains.jet.lang.resolve.calls.context.ResolveMode; import org.jetbrains.jet.lang.resolve.calls.context.TypeInfoForCall; import org.jetbrains.jet.lang.resolve.calls.inference.*; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; @@ -55,6 +56,7 @@ import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.ResolveArgum import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SKIP_FUNCTION_ARGUMENTS; import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.*; import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; +import static org.jetbrains.jet.lang.types.TypeUtils.canHaveSubtypes; public class CandidateResolver { @NotNull @@ -107,7 +109,7 @@ public class CandidateResolver { argumentTypeResolver.checkTypesWithNoCallee(context.toBasic()); return; } - argumentTypeResolver.checkUnmappedArgumentTypes(context.toBasic(), unmappedArguments); + candidateCall.setUnmappedArguments(unmappedArguments); } List jetTypeArguments = context.call.getTypeArguments(); @@ -138,7 +140,8 @@ public class CandidateResolver { Map substitutionContext = FunctionDescriptorUtil.createSubstitutionContext((FunctionDescriptor)candidate, typeArguments); - candidateCall.setResultingSubstitutor(TypeSubstitutor.create(substitutionContext)); + TypeSubstitutor substitutor = TypeSubstitutor.create(substitutionContext); + candidateCall.setResultingSubstitutor(substitutor); List typeParameters = candidateCall.getCandidateDescriptor().getTypeParameters(); for (int i = 0; i < typeParameters.size(); i++) { @@ -206,6 +209,7 @@ public class CandidateResolver { addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context); } } + resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor()); } public void completeTypeInferenceDependentOnExpectedTypeForCall( @@ -224,10 +228,11 @@ public class CandidateResolver { if (!constraintSystem.isSuccessful()) { resolvedCall.setResultingSubstitutor(constraintSystemWithoutExpectedTypeConstraint.getResultingSubstitutor()); - List argumentTypes = checkValueArgumentTypes(context, resolvedCall, resolvedCall.getTrace(), + completeValueArgumentsInference(context); + List argumentTypes = checkValueArgumentTypes(context, resolvedCall, context.trace, RESOLVE_FUNCTION_ARGUMENTS).argumentTypes; JetType receiverType = resolvedCall.getReceiverArgument().exists() ? resolvedCall.getReceiverArgument().getType() : null; - context.tracing.typeInferenceFailed(resolvedCall.getTrace(), + context.tracing.typeInferenceFailed(context.trace, InferenceErrorData .create(descriptor, constraintSystem, argumentTypes, receiverType, context.expectedType), @@ -246,16 +251,52 @@ public class CandidateResolver { } } if (!boundsAreSatisfied) { - context.tracing.upperBoundViolated(resolvedCall.getTrace(), InferenceErrorData.create(resolvedCall.getCandidateDescriptor(), constraintSystem)); + context.tracing.upperBoundViolated(context.trace, InferenceErrorData.create(resolvedCall.getCandidateDescriptor(), constraintSystem)); } resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor()); + completeValueArgumentsInference(context); // Here we type check the arguments with inferred types expected - checkAllValueArguments(context, RESOLVE_FUNCTION_ARGUMENTS); + checkAllValueArguments(context, context.trace, RESOLVE_FUNCTION_ARGUMENTS); resolvedCall.setHasUnknownTypeParameters(false); - if (resolvedCall.getStatus() == ResolutionStatus.UNKNOWN_STATUS) { - resolvedCall.addStatus(ResolutionStatus.SUCCESS); + ResolutionStatus status = resolvedCall.getStatus(); + if (status == ResolutionStatus.UNKNOWN_STATUS || status == ResolutionStatus.INCOMPLETE_TYPE_INFERENCE) { + resolvedCall.setStatusToSuccess(); + } + } + + public void completeValueArgumentsInference( + @NotNull CallCandidateResolutionContext context + ) { + ResolvedCallImpl resolvedCall = context.candidateCall; + ConstraintSystem constraintSystem = context.candidateCall.getConstraintSystem(); + for (Map.Entry entry : resolvedCall.getValueArguments().entrySet()) { + ValueParameterDescriptor parameterDescriptor = entry.getKey(); + ResolvedValueArgument resolvedArgument = entry.getValue(); + + for (ValueArgument argument : resolvedArgument.getArguments()) { + JetExpression expression = argument.getArgumentExpression(); + if (expression == null) continue; + + JetType effectiveExpectedType = getEffectiveExpectedType(parameterDescriptor, argument); + JetType expectedType = constraintSystem != null + ? constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT) + : effectiveExpectedType; + + CallCandidateResolutionContext storedContextForArgument = + resolvedCall.getDeferredComputationForArgument(argument); + if (storedContextForArgument == null) continue; + + CallCandidateResolutionContext contextForArgument = + storedContextForArgument.replaceResolveMode(ResolveMode.NORMAL).replaceBindingTrace(context.trace).replaceExpectedType(expectedType); + if (contextForArgument.candidateCall.hasUnknownTypeParameters()) { + completeTypeInferenceDependentOnExpectedTypeForCall(contextForArgument); + } + else { + completeValueArgumentsInference(contextForArgument); + } + } } } @@ -373,31 +414,21 @@ public class CandidateResolver { // Solution boolean hasContradiction = constraintSystem.hasContradiction(); boolean boundsAreSatisfied = ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, /*substituteOtherTypeParametersInBounds=*/false); + candidateCall.setHasUnknownTypeParameters(true); if (!hasContradiction && boundsAreSatisfied) { - candidateCall.setHasUnknownTypeParameters(true); - return SUCCESS; + return INCOMPLETE_TYPE_INFERENCE; } ValueArgumentsCheckingResult checkingResult = checkAllValueArguments(context, SKIP_FUNCTION_ARGUMENTS); ResolutionStatus argumentsStatus = checkingResult.status; - List argumentTypes = checkingResult.argumentTypes; - JetType receiverType = candidateCall.getReceiverArgument().exists() ? candidateCall.getReceiverArgument().getType() : null; - InferenceErrorData.ExtendedInferenceErrorData inferenceErrorData = InferenceErrorData - .create(candidate, constraintSystemWithRightTypeParameters, argumentTypes, receiverType, context.expectedType); - if (hasContradiction) { - context.tracing.typeInferenceFailed(candidateCall.getTrace(), inferenceErrorData, constraintSystemWithRightTypeParameters); - } - else { - context.tracing.upperBoundViolated(candidateCall.getTrace(), inferenceErrorData); - } return OTHER_ERROR.combine(argumentsStatus); } - private > void addConstraintForValueArgument( + private void addConstraintForValueArgument( @NotNull ValueArgument valueArgument, @NotNull ValueParameterDescriptor valueParameterDescriptor, @NotNull TypeSubstitutor substitutor, @NotNull ConstraintSystem constraintSystem, - @NotNull CallResolutionContext context, + @NotNull CallCandidateResolutionContext context, @Nullable boolean[] isErrorType, @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) { @@ -406,13 +437,17 @@ public class CandidateResolver { TemporaryBindingTrace traceToResolveArgument = TemporaryBindingTrace.create( context.trace, "transient trace to resolve argument", argumentExpression); JetType expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT); - CallResolutionContext newContext = - context.replaceBindingTrace(traceToResolveArgument).replaceExpectedType( - expectedType != null ? expectedType : NO_EXPECTED_TYPE); - JetType type = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, resolveFunctionArgumentBodies).getType(); + CallResolutionContext newContext = context.replaceBindingTrace(traceToResolveArgument).replaceExpectedType(expectedType); + TypeInfoForCall typeInfoForCall = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, + resolveFunctionArgumentBodies); + CallCandidateResolutionContext contextForArgument = typeInfoForCall.getCallCandidateResolutionContext(); + if (contextForArgument != null) { + context.candidateCall.addDeferredComputationForArgument(valueArgument, contextForArgument); + traceToResolveArgument.commit(); + } + JetType type = typeInfoForCall.getType(); constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition( valueParameterDescriptor.getIndex())); - BindingContextUtils.commitResolutionCacheData(traceToResolveArgument, context.trace); if (isErrorType != null) { isErrorType[0] = type == null || ErrorUtils.isErrorType(type); } @@ -421,8 +456,16 @@ public class CandidateResolver { private ValueArgumentsCheckingResult checkAllValueArguments( @NotNull CallCandidateResolutionContext context, @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) { - ValueArgumentsCheckingResult checkingResult = checkValueArgumentTypes(context, context.candidateCall, - context.candidateCall.getTrace(), resolveFunctionArgumentBodies); + return checkAllValueArguments(context, context.candidateCall.getTrace(), resolveFunctionArgumentBodies); + } + + private ValueArgumentsCheckingResult checkAllValueArguments( + @NotNull CallCandidateResolutionContext context, + @NotNull BindingTrace trace, + @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies + ) { + ValueArgumentsCheckingResult checkingResult = checkValueArgumentTypes( + context, context.candidateCall, trace, resolveFunctionArgumentBodies); ResolutionStatus resultStatus = checkingResult.status; ResolvedCall candidateCall = context.candidateCall; @@ -431,13 +474,17 @@ public class CandidateResolver { // both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'. // Class 'CallForImplicitInvoke' helps up to recognise this case, and parameter 'implicitInvokeCheck' helps us to distinguish whether we check receiver or this object. - resultStatus = resultStatus.combine(checkReceiver(context, candidateCall, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(), - candidateCall.getExplicitReceiverKind().isReceiver(), false)); + resultStatus = resultStatus.combine(checkReceiver( + context, candidateCall, trace, + candidateCall.getResultingDescriptor().getReceiverParameter(), + candidateCall.getReceiverArgument(), candidateCall.getExplicitReceiverKind().isReceiver(), false)); - resultStatus = resultStatus.combine(checkReceiver(context, candidateCall, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(), - candidateCall.getExplicitReceiverKind().isThisObject(), - // for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error - context.call instanceof CallTransformer.CallForImplicitInvoke)); + resultStatus = resultStatus.combine(checkReceiver( + context, candidateCall, trace, + candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(), + candidateCall.getExplicitReceiverKind().isThisObject(), + // for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error + context.call instanceof CallTransformer.CallForImplicitInvoke)); return new ValueArgumentsCheckingResult(resultStatus, checkingResult.argumentTypes); } @@ -467,6 +514,7 @@ public class CandidateResolver { expression, newContext, resolveFunctionArgumentBodies); JetType type = typeInfoForCall.getType(); candidateCall.addDataFlowInfo(typeInfoForCall.getDataFlowInfo()); + candidateCall.addDeferredComputationForArgument(argument, typeInfoForCall.getCallCandidateResolutionContext()); if (type == null || (ErrorUtils.isErrorType(type) && type != PLACEHOLDER_FUNCTION_TYPE)) { candidateCall.argumentHasNoType(); @@ -511,11 +559,12 @@ public class CandidateResolver { return null; } - private ResolutionStatus checkReceiver(CallCandidateResolutionContext context, ResolvedCall candidateCall, + private ResolutionStatus checkReceiver( + CallCandidateResolutionContext context, ResolvedCall candidateCall, BindingTrace trace, ReceiverParameterDescriptor receiverParameter, ReceiverValue receiverArgument, boolean isExplicitReceiver, boolean implicitInvokeCheck) { - BindingContext bindingContext = context.candidateCall.getTrace().getBindingContext(); + BindingContext bindingContext = trace.getBindingContext(); ResolutionStatus result = SUCCESS; if (receiverParameter != null && receiverArgument.exists()) { @@ -524,7 +573,7 @@ public class CandidateResolver { AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, bindingContext); if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) { - context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType, implicitInvokeCheck); + context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck); result = UNSAFE_CALL_ERROR; } else { @@ -534,13 +583,13 @@ public class CandidateResolver { if (!TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateCall.getCandidateDescriptor().getTypeParameters()) && !argumentTypeResolver.isSubtypeOfForArgumentType(effectiveReceiverArgumentType, receiverParameter.getType())) { - context.tracing.wrongReceiverType(context.candidateCall.getTrace(), receiverParameter, receiverArgument); + context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument); result = OTHER_ERROR; } } DataFlowValue receiverValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiverArgument, bindingContext); if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) { - context.tracing.unnecessarySafeCall(context.candidateCall.getTrace(), receiverArgumentType); + context.tracing.unnecessarySafeCall(trace, receiverArgumentType); } } return result; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/TypeInfoForCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/TypeInfoForCall.java index bc1eefe7baa..e8636abd8bf 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/TypeInfoForCall.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/TypeInfoForCall.java @@ -58,8 +58,9 @@ public class TypeInfoForCall { if (resolvedCall instanceof ResolvedCallImpl) { BasicCallResolutionContext basicCallResolutionContext = BasicCallResolutionContext.create( TRACE_STUB, context.scope, call, TypeUtils.NO_EXPECTED_TYPE, typeInfo.getDataFlowInfo(), resolveMode, context.namespacesAllowed); + //todo[ResolvedCallImpl] callCandidateResolutionContext = CallCandidateResolutionContext.createForCallBeingAnalyzed( - (ResolvedCallImpl) resolvedCall, basicCallResolutionContext, TracingStrategy.EMPTY); + (ResolvedCallImpl) resolvedCall, basicCallResolutionContext, ((ResolvedCallImpl) resolvedCall).getTracing()); } else { callCandidateResolutionContext = null; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/model/ResolvedCallImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/model/ResolvedCallImpl.java index 688c6589b48..9045b5f1d69 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/model/ResolvedCallImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/model/ResolvedCallImpl.java @@ -17,14 +17,18 @@ package org.jetbrains.jet.lang.resolve.calls.model; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; +import org.jetbrains.jet.lang.psi.ValueArgument; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.context.CallCandidateResolutionContext; import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate; import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus; @@ -34,10 +38,9 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeSubstitutor; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; +import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE; import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.UNKNOWN_STATUS; public class ResolvedCallImpl implements ResolvedCallWithTrace { @@ -71,6 +74,8 @@ public class ResolvedCallImpl implements ResolvedC private final Map typeArguments = Maps.newLinkedHashMap(); private final Map valueArguments = Maps.newLinkedHashMap(); + private final Map> deferredComputationsForArguments = Maps.newLinkedHashMap(); + private final Set unmappedArguments = Sets.newLinkedHashSet(); private boolean someArgumentHasNoType = false; private final DelegatingBindingTrace trace; private final TracingStrategy tracing; @@ -99,6 +104,11 @@ public class ResolvedCallImpl implements ResolvedC this.status = this.status.combine(status); } + public void setStatusToSuccess() { + assert status == INCOMPLETE_TYPE_INFERENCE || status == UNKNOWN_STATUS; + status = ResolutionStatus.SUCCESS; + } + @Override public boolean hasUnknownTypeParameters() { return hasUnknownTypeParameters; @@ -168,6 +178,16 @@ public class ResolvedCallImpl implements ResolvedC valueArguments.put(valueParameter, valueArgument); } + public void setUnmappedArguments(@NotNull Collection unmappedArguments) { + this.unmappedArguments.addAll(unmappedArguments); + + } + + @NotNull + public Set getUnmappedArguments() { + return unmappedArguments; + } + @Override @NotNull public ReceiverValue getReceiverArgument() { @@ -250,4 +270,18 @@ public class ResolvedCallImpl implements ResolvedC assert dataFlowInfo != null; dataFlowInfo = dataFlowInfo.and(info); } + + public void addDeferredComputationForArgument( + @NotNull ValueArgument valueArgument, + @Nullable CallCandidateResolutionContext callCandidateResolutionContext + ) { + if (callCandidateResolutionContext != null) { + deferredComputationsForArguments.put(valueArgument, callCandidateResolutionContext); + } + } + + @Nullable + public CallCandidateResolutionContext getDeferredComputationForArgument(@NotNull ValueArgument valueArgument) { + return deferredComputationsForArguments.get(valueArgument); + } } diff --git a/compiler/testData/diagnostics/tests/inference/complicated/completeValueArgumentsInference.kt b/compiler/testData/diagnostics/tests/inference/complicated/completeValueArgumentsInference.kt new file mode 100644 index 00000000000..eea79c7bf82 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/complicated/completeValueArgumentsInference.kt @@ -0,0 +1,19 @@ +package a + +import java.util.ArrayList + +public fun Iterable.withIndices(): List> { + val answer = ArrayList>() + var nextIndex = 1 + for (e in this) { + answer.add(Pair(nextIndex, e)) + nextIndex++ + } + return answer +} + +//from standard library +public class Pair( + public val first: A, + public val second: B +) \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index cd3c80e2af5..4c7bae87690 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1993,7 +1993,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } @TestMetadata("compiler/testData/diagnostics/tests/inference") - @InnerTestClasses({Inference.Regressions.class, Inference.ReportingImprovements.class, Inference.UpperBounds.class, Inference.Varargs.class}) + @InnerTestClasses({Inference.Complicated.class, Inference.Regressions.class, Inference.ReportingImprovements.class, Inference.UpperBounds.class, Inference.Varargs.class}) public static class Inference extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInInference() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/inference"), Pattern.compile("^(.+)\\.kt$"), true); @@ -2119,6 +2119,19 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/inference/typeInferenceExpectedTypeMismatch.kt"); } + @TestMetadata("compiler/testData/diagnostics/tests/inference/complicated") + public static class Complicated extends AbstractDiagnosticsTestWithEagerResolve { + public void testAllFilesPresentInComplicated() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/inference/complicated"), "kt", true); + } + + @TestMetadata("completeValueArgumentsInference.kt") + public void testCompleteValueArgumentsInference() throws Exception { + doTest("compiler/testData/diagnostics/tests/inference/complicated/completeValueArgumentsInference.kt"); + } + + } + @TestMetadata("compiler/testData/diagnostics/tests/inference/regressions") public static class Regressions extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInRegressions() throws Exception { @@ -2399,6 +2412,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage public static Test innerSuite() { TestSuite suite = new TestSuite("Inference"); suite.addTestSuite(Inference.class); + suite.addTestSuite(Complicated.class); suite.addTestSuite(Regressions.class); suite.addTestSuite(ReportingImprovements.class); suite.addTestSuite(UpperBounds.class);