inference using new constraint system

final type parameters inference and substitution are made after expected type is known (should be redone with caches for performance)
This commit is contained in:
Svetlana Isakova
2012-07-03 12:43:01 +04:00
parent 83ec7d9c68
commit f270f9e90a
@@ -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<F> results = doResolveCall(context.replaceTrace(delegatingBindingTrace),
BasicResolutionContext newContext = context.replaceTrace(delegatingBindingTrace);
OverloadResolutionResults<F> 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<F> completeResults = completeTypeInferenceDependentOnExpectedType(newContext, results,
!prioritizedTasks.isEmpty() ? prioritizedTasks.iterator().next().tracing : null);
if (temporaryBindingTrace != null) {
temporaryBindingTrace.commit();
}
delegatingBindingTrace.commit();
return results;
return completeResults;
}
private <D extends CallableDescriptor> OverloadResolutionResults<D> completeTypeInferenceDependentOnExpectedType(
BasicResolutionContext context,
OverloadResolutionResults<D> results,
@Nullable TracingStrategy tracing) {
if (results.getResultCode() != OverloadResolutionResults.Code.DIRTY) return results;
Set<ResolvedCallWithTrace<D>> successful = Sets.newLinkedHashSet();
Set<ResolvedCallWithTrace<D>> failed = Sets.newLinkedHashSet();
for (ResolvedCall<? extends D> resolvedCall : results.getResultingCalls()) {
if (!(resolvedCall instanceof ResolvedCallImpl)) continue;
ResolvedCallImpl<D> call = (ResolvedCallImpl<D>) 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<TypeParameterDescriptor, TypeBounds> 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<ValueParameterDescriptor, ResolvedValueArgument> 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<D> 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 <D extends CallableDescriptor> void checkBounds(ResolvedCallImpl<D> 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 <F extends CallableDescriptor> void cacheResults(@NotNull WritableSlice<CallKey, OverloadResolutionResults<F>> 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<F> results = computeResultAndReportErrors(task.trace, task.tracing, successfulCandidates, failedCandidates);
if (!results.isSingleResult()) {
OverloadResolutionResultsImpl<F> 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<JetTypeProjection> 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) : 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<ValueParameterDescriptor, ResolvedValueArgument> 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 <D extends CallableDescriptor, F extends D> ResolutionStatus checkAllValueArguments(CallResolutionContext<D, F> context) {
ResolutionStatus result = checkValueArgumentTypes(context);
ResolutionStatus result = checkValueArgumentTypes(context, context.candidateCall);
ResolvedCall<D> candidateCall = context.candidateCall;
// Comment about a very special case.
@@ -767,10 +905,14 @@ public class CallResolver {
return result;
}
private <D extends CallableDescriptor, F extends D> ResolutionStatus checkValueArgumentTypes(CallResolutionContext<D, F> context) {
private <D extends CallableDescriptor> ResolutionStatus checkValueArgumentTypes(ResolutionContext context, ResolvedCallImpl<D> candidateCall) {
return checkValueArgumentTypes(context, candidateCall, candidateCall.getTrace());
}
private <D extends CallableDescriptor> ResolutionStatus checkValueArgumentTypes(ResolutionContext context, ResolvedCallImpl<D> candidateCall, BindingTrace trace) {
ResolutionStatus result = SUCCESS;
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : context.candidateCall.getValueArguments().entrySet()) {
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> 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<D> 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 <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> chooseAndReportMaximallySpecific(Set<ResolvedCallWithTrace<D>> candidates, boolean discriminateGenerics) {
if (candidates.size() != 1) {
boolean dirty = false;
Set<ResolvedCallWithTrace<D>> cleanCandidates = Sets.newLinkedHashSet(candidates);
for (Iterator<ResolvedCallWithTrace<D>> iterator = cleanCandidates.iterator(); iterator.hasNext(); ) {
ResolvedCallWithTrace<D> candidate = iterator.next();
if (candidate.hasUnknownTypeParameters()) {
dirty = true;
}
if (candidate.isDirty()) {
iterator.remove();
}
@@ -940,6 +1090,10 @@ public class CallResolver {
Set<ResolvedCallWithTrace<D>> 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);
}