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 1cec5e776b2..f1016276085 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 @@ -20,7 +20,6 @@ import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,10 +29,7 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities; -import org.jetbrains.jet.lang.resolve.calls.inference.DebugConstraintResolutionListener; +import org.jetbrains.jet.lang.resolve.calls.inference.*; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; @@ -53,7 +49,6 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.*; import static org.jetbrains.jet.lang.resolve.calls.ResolutionStatus.*; import static org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl.MAP_TO_CANDIDATE; import static org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl.MAP_TO_RESULT; -import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType.*; import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor.NO_RECEIVER; import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; @@ -61,8 +56,6 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; * @author abreslav */ public class CallResolver { - private static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE"); - private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; @NotNull @@ -274,14 +267,153 @@ public class CallResolver { } } TemporaryBindingTrace delegatingBindingTrace = TemporaryBindingTrace.create(context.trace); - OverloadResolutionResults results = doResolveCall(context.replaceTrace(delegatingBindingTrace), + BasicResolutionContext newContext = context.replaceTrace(delegatingBindingTrace); + OverloadResolutionResults results = doResolveCall(newContext, prioritizedTasks, callTransformer, reference); DelegatingBindingTrace cloneDelta = new DelegatingBindingTrace(new BindingTraceContext().getBindingContext()); delegatingBindingTrace.addAllMyDataTo(cloneDelta); cacheResults(resolutionResultsSlice, context, results, cloneDelta); + + TemporaryBindingTrace temporaryBindingTrace = null; + if (results instanceof OverloadResolutionResultsImpl) { + temporaryBindingTrace = ((OverloadResolutionResultsImpl) results).getTrace(); + if (temporaryBindingTrace != null) { + newContext = newContext.replaceTrace(temporaryBindingTrace); + } + } + OverloadResolutionResults completeResults = completeTypeInferenceDependentOnExpectedType(newContext, results, + !prioritizedTasks.isEmpty() ? prioritizedTasks.iterator().next().tracing : null); + if (temporaryBindingTrace != null) { + temporaryBindingTrace.commit(); + } delegatingBindingTrace.commit(); - return results; + return completeResults; + } + + private OverloadResolutionResults completeTypeInferenceDependentOnExpectedType( + BasicResolutionContext context, + OverloadResolutionResults results, + @Nullable TracingStrategy tracing) { + if (results.getResultCode() != OverloadResolutionResults.Code.DIRTY) return results; + Set> successful = Sets.newLinkedHashSet(); + Set> failed = Sets.newLinkedHashSet(); + for (ResolvedCall resolvedCall : results.getResultingCalls()) { + if (!(resolvedCall instanceof ResolvedCallImpl)) continue; + ResolvedCallImpl call = (ResolvedCallImpl) resolvedCall; + if (!call.hasUnknownTypeParameters()) { + if (call.getStatus().isSuccess()) { + successful.add(call); + } + else { + failed.add(call); + } + continue; + } + + ConstraintSystem constraintSystem = new ConstraintSystemImpl(); + D descriptor = call.getCandidateDescriptor(); + Map typeArgumentBounds = call.getTypeArgumentBounds(); + for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) { + if (typeArgumentBounds.containsKey(typeParameterDescriptor)) { + constraintSystem.registerTypeVariable(typeParameterDescriptor, typeArgumentBounds.get(typeParameterDescriptor)); + } + else { + constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); + } + } + constraintSystem.addConstraint(ConstraintSystemImpl.ConstraintType.SUPER_TYPE, context.expectedType, descriptor.getReturnType()); + + + // constraints for function literals + // Value parameters + for (Map.Entry entry : call.getValueArguments().entrySet()) { + ResolvedValueArgument resolvedValueArgument = entry.getValue(); + ValueParameterDescriptor valueParameterDescriptor = entry.getKey(); + + for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) { + if (!JetPsiUtil.isFunctionLiteralWithoutDeclaredParameterTypes(valueArgument.getArgumentExpression())) continue; + + // TODO : more attempts, with different expected types + + JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument); + + // Here we type check expecting an error type (that is a subtype of any type and a supertype of any type + // and throw the results away + // We'll type check the arguments later, with the inferred types expected + TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create(context.trace); + JetExpression argumentExpression = valueArgument.getArgumentExpression(); + JetType type = argumentExpression != null ? expressionTypingServices.getType(context.scope, argumentExpression, + constraintSystem.getSubstitutor().substitute(valueParameterDescriptor.getType(), Variance.INVARIANT), + context.dataFlowInfo, traceForUnknown) : null; + if (type != null && !ErrorUtils.isErrorType(type)) { + constraintSystem.addSubtypingConstraint(type, effectiveExpectedType); + } + } + } + + + if (constraintSystem.isSuccessful()) { + D substitute = (D) descriptor.substitute(constraintSystem.getSubstitutor()); + assert substitute != null; + replaceValueParametersWithSubstitutedOnes(call, substitute); + call.setResultingDescriptor(substitute); //replacement + + // Here we type check the arguments with inferred types expected + checkValueArgumentTypes(context, call, context.trace); + + checkBounds(call, constraintSystem, context); + call.setHasUnknownTypeParameters(false); + if (call.getStatus().isSuccess() || call.getStatus() == ResolutionStatus.UNKNOWN_STATUS) { + call.addStatus(ResolutionStatus.SUCCESS); + successful.add(call); + } + else { + failed.add(call); + } + } + else { + context.trace.report(TYPE_INFERENCE_FAILED.on(context.call.getCallElement(), new SolutionStatus() { + @Override + public boolean isSuccessful() { + return false; + } + + @Override + public String toString() { + return "Type inference failed"; + } + })); + + call.addStatus(ResolutionStatus.TYPE_INFERENCE_ERROR); + checkValueArgumentTypes(context, call, context.trace); + failed.add(call); + } + } + if (results.getResultingCalls().size() > 1) { + for (ResolvedCallWithTrace call : successful) { + if (call instanceof ResolvedCallImpl) { + ((ResolvedCallImpl)call).addStatus(ResolutionStatus.OTHER_ERROR); + failed.add(call); + } + } + successful.clear(); + } + return computeResultAndReportErrors(context.trace, tracing, successful, failed); + } + + private void checkBounds(ResolvedCallImpl call, ConstraintSystem constraintSystem, BasicResolutionContext context) { + for (TypeParameterDescriptor typeParameter : call.getCandidateDescriptor().getTypeParameters()) { + JetType type = constraintSystem.getValue(typeParameter); + JetType upperBound = typeParameter.getUpperBoundsAsType(); + JetType substitute = constraintSystem.getSubstitutor().substitute(upperBound, Variance.INVARIANT); + + if (type != null) { + if (substitute == null || !JetTypeChecker.INSTANCE.isSubtypeOf(type, substitute)) { + context.trace.report(Errors.TYPE_INFERENCE_UPPER_BOUND_VIOLATED.on(context.call.getCallElement(), type, substitute != null ? substitute : upperBound)); + } + } + } } private void cacheResults(@NotNull WritableSlice> resolutionResultsSlice, @@ -338,6 +470,10 @@ public class CallResolver { return results; } + if (results.getResultCode() == OverloadResolutionResults.Code.DIRTY) { + results.setTrace(taskTrace); + return results; + } if (traceForFirstNonemptyCandidateSet == null && !task.getCandidates().isEmpty() && !results.isNothing()) { traceForFirstNonemptyCandidateSet = taskTrace; resultsForFirstNonemptyCandidateSet = results; @@ -454,8 +590,9 @@ public class CallResolver { } } - OverloadResolutionResultsImpl results = computeResultAndReportErrors(task.trace, task.tracing, successfulCandidates, failedCandidates); - if (!results.isSingleResult()) { + OverloadResolutionResultsImpl results = computeResultAndReportErrors(task.trace, task.tracing, successfulCandidates, + failedCandidates); + if (!results.isSingleResult() && results.getResultCode() != OverloadResolutionResults.Code.DIRTY) { checkTypesWithNoCallee(task.toBasic()); } return results; @@ -490,7 +627,8 @@ public class CallResolver { else { candidateCall.addStatus(OTHER_ERROR); } - if (argumentMappingStatus != ValueArgumentsToParametersMapper.Status.WEAK_ERROR) { + if ((argumentMappingStatus == ValueArgumentsToParametersMapper.Status.ERROR && candidate.getTypeParameters().isEmpty()) || + argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR) { checkTypesWithNoCallee(context.toBasic()); return; } @@ -500,7 +638,8 @@ public class CallResolver { List jetTypeArguments = context.call.getTypeArguments(); if (jetTypeArguments.isEmpty()) { if (!candidate.getTypeParameters().isEmpty()) { - candidateCall.addStatus(inferTypeArguments(context)); + ResolutionStatus status = inferTypeArguments(context); + candidateCall.addStatus(status); } else { candidateCall.addStatus(checkAllValueArguments(context)); @@ -567,7 +706,7 @@ public class CallResolver { ResolutionDebugInfo.Data debugInfo = context.trace.get(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, context.call.getCallElement()); - ConstraintSystem constraintSystem = new ConstraintSystemWithPriorities(new DebugConstraintResolutionListener(candidateCall, debugInfo)); + ConstraintSystem constraintSystem = new ConstraintSystemImpl(); // If the call is recursive, e.g. // fun foo(t : T) : T = foo(t) @@ -583,7 +722,7 @@ public class CallResolver { } TypeSubstitutor substituteDontCare = ConstraintSystemWithPriorities - .makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE); + .makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), ConstraintSystem.DONT_CARE); // Value parameters for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) { @@ -592,6 +731,7 @@ public class CallResolver { for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) { + if (JetPsiUtil.isFunctionLiteralWithoutDeclaredParameterTypes(valueArgument.getArgumentExpression())) continue; // TODO : more attempts, with different expected types JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument); @@ -605,7 +745,7 @@ public class CallResolver { context.scope, argumentExpression, substituteDontCare.substitute(valueParameterDescriptor.getType(), Variance.INVARIANT), context.dataFlowInfo, traceForUnknown) : null; if (type != null && !ErrorUtils.isErrorType(type)) { - constraintSystem.addSubtypingConstraint(VALUE_ARGUMENT.assertSubtyping(type, effectiveExpectedType)); + constraintSystem.addSubtypingConstraint(type, effectiveExpectedType); } else { candidateCall.argumentHasNoType(); @@ -618,34 +758,32 @@ public class CallResolver { ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument(); ReceiverDescriptor receiverParameter = candidateWithFreshVariables.getReceiverParameter(); if (receiverArgument.exists() && receiverParameter.exists()) { - constraintSystem.addSubtypingConstraint(RECEIVER.assertSubtyping(receiverArgument.getType(), receiverParameter.getType())); + constraintSystem.addSubtypingConstraint(receiverArgument.getType(), receiverParameter.getType()); } - // Return type - if (context.expectedType != NO_EXPECTED_TYPE) { - constraintSystem.addSubtypingConstraint(EXPECTED_TYPE.assertSubtyping(candidateWithFreshVariables.getReturnType(), context.expectedType)); + for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { + candidateCall.recordTypeBounds(typeParameterDescriptor, constraintSystem.getTypeBounds( + candidateWithFreshVariables.getTypeParameters().get(typeParameterDescriptor.getIndex()))); } // Solution - ConstraintSystemSolution solution = constraintSystem.solve(); - if (solution.getStatus().isSuccessful()) { - D substitute = (D) candidateWithFreshVariables.substitute(solution.getSubstitutor()); - assert substitute != null; - replaceValueParametersWithSubstitutedOnes(candidateCall, substitute); - candidateCall.setResultingDescriptor(substitute); - - for (TypeParameterDescriptor typeParameterDescriptor : candidateCall.getCandidateDescriptor().getTypeParameters()) { - candidateCall.recordTypeArgument(typeParameterDescriptor, solution.getValue(candidateWithFreshVariables.getTypeParameters().get(typeParameterDescriptor.getIndex()))); - } - - // Here we type check the arguments with inferred types expected - checkValueArgumentTypes(context); - + if (!constraintSystem.hasContradiction()) { + candidateCall.setHasUnknownTypeParameters(true); return SUCCESS; } else { - context.tracing.typeInferenceFailed(context.trace, solution.getStatus()); - return OTHER_ERROR.combine(checkAllValueArguments(context)); + context.tracing.typeInferenceFailed(context.trace, new SolutionStatus() { + @Override + public boolean isSuccessful() { + return false; + } + + @Override + public String toString() { + return "Type inference failed"; + } + }); + return TYPE_INFERENCE_ERROR.combine(checkAllValueArguments(context)); } } @@ -719,7 +857,7 @@ public class CallResolver { } private ResolutionStatus checkAllValueArguments(CallResolutionContext context) { - ResolutionStatus result = checkValueArgumentTypes(context); + ResolutionStatus result = checkValueArgumentTypes(context, context.candidateCall); ResolvedCall candidateCall = context.candidateCall; // Comment about a very special case. @@ -767,10 +905,14 @@ public class CallResolver { return result; } - private ResolutionStatus checkValueArgumentTypes(CallResolutionContext context) { + private ResolutionStatus checkValueArgumentTypes(ResolutionContext context, ResolvedCallImpl candidateCall) { + return checkValueArgumentTypes(context, candidateCall, candidateCall.getTrace()); + } + + private ResolutionStatus checkValueArgumentTypes(ResolutionContext context, ResolvedCallImpl candidateCall, BindingTrace trace) { ResolutionStatus result = SUCCESS; DataFlowInfo dataFlowInfo = context.dataFlowInfo; - for (Map.Entry entry : context.candidateCall.getValueArguments().entrySet()) { + for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) { ValueParameterDescriptor parameterDescriptor = entry.getKey(); ResolvedValueArgument resolvedArgument = entry.getValue(); @@ -780,11 +922,12 @@ public class CallResolver { if (expression == null) continue; JetType expectedType = getEffectiveExpectedType(parameterDescriptor, argument); - JetTypeInfo typeInfo = expressionTypingServices.getTypeInfo(context.scope, expression, expectedType, dataFlowInfo, context.candidateCall.getTrace()); + JetTypeInfo typeInfo = expressionTypingServices.getTypeInfo(context.scope, expression, expectedType, dataFlowInfo, + trace); JetType type = typeInfo.getType(); dataFlowInfo = dataFlowInfo.and(typeInfo.getDataFlowInfo()); if (type == null || ErrorUtils.isErrorType(type)) { - context.candidateCall.argumentHasNoType(); + candidateCall.argumentHasNoType(); } else if (!typeChecker.isSubtypeOf(type, expectedType)) { // VariableDescriptor variableDescriptor = AutoCastUtils.getVariableDescriptorFromSimpleName(temporaryTrace.getBindingContext(), argument); @@ -819,7 +962,7 @@ public class CallResolver { if (argument.getSpreadElement() != null) { if (parameterDescriptor.getVarargElementType() == null) { // Spread argument passed to a non-vararg parameter, an error is already reported by ValueArgumentsToParametersMapper - return ErrorUtils.createErrorType("Don't care"); + return ConstraintSystem.DONT_CARE; } else { return parameterDescriptor.getType(); @@ -898,6 +1041,9 @@ public class CallResolver { ResolvedCallWithTrace failed = failedCandidates.iterator().next(); failed.getTrace().commit(); + if (failed.getStatus() != ResolutionStatus.STRONG_ERROR && failed.hasUnknownTypeParameters()) { + return OverloadResolutionResultsImpl.dirty(failed); + } return OverloadResolutionResultsImpl.singleFailedCandidate(failed); } else { @@ -915,9 +1061,13 @@ public class CallResolver { private OverloadResolutionResultsImpl chooseAndReportMaximallySpecific(Set> candidates, boolean discriminateGenerics) { if (candidates.size() != 1) { + boolean dirty = false; Set> cleanCandidates = Sets.newLinkedHashSet(candidates); for (Iterator> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) { ResolvedCallWithTrace candidate = iterator.next(); + if (candidate.hasUnknownTypeParameters()) { + dirty = true; + } if (candidate.isDirty()) { iterator.remove(); } @@ -940,6 +1090,10 @@ public class CallResolver { Set> noOverrides = OverridingUtil.filterOverrides(candidates, MAP_TO_RESULT); + if (dirty) { + return OverloadResolutionResultsImpl.dirty(candidates); + } + return OverloadResolutionResultsImpl.ambiguity(noOverrides); } else { @@ -947,6 +1101,9 @@ public class CallResolver { TemporaryBindingTrace temporaryTrace = result.getTrace(); temporaryTrace.commit(); + if (result.hasUnknownTypeParameters()) { + return OverloadResolutionResultsImpl.dirty(result); + } return OverloadResolutionResultsImpl.success(result); }