analyze function literals only if argument types are known
(through the first phase of overload resolution) use intercepting type mismatch trace to backtrace expected type errors
This commit is contained in:
@@ -527,4 +527,11 @@ public class JetPsiUtil {
|
|||||||
throw new IllegalArgumentException("Can't construct name for: " + expression.getClass().toString());
|
throw new IllegalArgumentException("Can't construct name for: " + expression.getClass().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static JetElement getLastStatementInABlock(@Nullable JetBlockExpression blockExpression) {
|
||||||
|
if (blockExpression == null) return null;
|
||||||
|
List<JetElement> statements = blockExpression.getStatements();
|
||||||
|
return statements.isEmpty() ? null : statements.get(statements.size() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.lang.resolve.calls;
|
package org.jetbrains.jet.lang.resolve.calls;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||||
@@ -27,11 +29,14 @@ import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
|||||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl;
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
|
import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
|
||||||
import org.jetbrains.jet.lang.types.ErrorUtils;
|
import org.jetbrains.jet.lang.types.*;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||||
|
|
||||||
public class CallResolverUtil {
|
public class CallResolverUtil {
|
||||||
|
|
||||||
public static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE");
|
public static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE");
|
||||||
@@ -74,4 +79,40 @@ public class CallResolverUtil {
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean containsUnknownFunctionArgument(@NotNull JetType type) {
|
||||||
|
assert KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(type);
|
||||||
|
List<TypeProjection> arguments = type.getArguments();
|
||||||
|
int index = 0;
|
||||||
|
for (TypeProjection argument : arguments) {
|
||||||
|
if (index < arguments.size() - 1) {
|
||||||
|
if (ErrorUtils.containsErrorType(argument.getType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasUnknownReturnType(@NotNull JetType type) {
|
||||||
|
assert KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(type);
|
||||||
|
JetType returnTypeFromFunctionType = KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(type);
|
||||||
|
return ErrorUtils.containsErrorType(returnTypeFromFunctionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JetType replaceReturnTypeToUnknown(@NotNull JetType type) {
|
||||||
|
assert KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(type);
|
||||||
|
List<TypeProjection> arguments = type.getArguments();
|
||||||
|
List<TypeProjection> newArguments = Lists.newArrayList();
|
||||||
|
int index = 0;
|
||||||
|
for (TypeProjection argument : arguments) {
|
||||||
|
if (index < arguments.size() - 1) {
|
||||||
|
newArguments.add(argument);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
newArguments.add(new TypeProjection(Variance.INVARIANT, DONT_CARE));
|
||||||
|
return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), type.isNullable(), newArguments, type.getMemberScope());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import org.jetbrains.jet.lang.resolve.*;
|
|||||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.*;
|
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.CallCandidateResolutionContext;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.context.CallResolutionContext;
|
import org.jetbrains.jet.lang.resolve.calls.context.CallResolutionContext;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.inference.*;
|
import org.jetbrains.jet.lang.resolve.calls.inference.*;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
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.ResolvedCallImpl;
|
||||||
@@ -41,6 +40,8 @@ import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer;
|
|||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
|
||||||
import org.jetbrains.jet.lang.types.*;
|
import org.jetbrains.jet.lang.types.*;
|
||||||
|
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
|
||||||
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -193,9 +194,6 @@ public class CandidateResolver {
|
|||||||
ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
|
ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
|
||||||
assert constraintSystem != null;
|
assert constraintSystem != null;
|
||||||
|
|
||||||
TypeSubstitutor substituteDontCare = ConstraintSystemWithPriorities
|
|
||||||
.makeConstantSubstitutor(resolvedCall.getCandidateDescriptor().getTypeParameters(), DONT_CARE);
|
|
||||||
|
|
||||||
// constraints for function literals
|
// constraints for function literals
|
||||||
// Value parameters
|
// Value parameters
|
||||||
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
|
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
|
||||||
@@ -205,16 +203,7 @@ public class CandidateResolver {
|
|||||||
for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) {
|
for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) {
|
||||||
if (!(valueArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression)) continue;
|
if (!(valueArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression)) continue;
|
||||||
|
|
||||||
ConstraintSystem systemWithCurrentSubstitution = constraintSystem.copy();
|
addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context);
|
||||||
addConstraintForValueArgument(valueArgument, valueParameterDescriptor, constraintSystem.getCurrentSubstitutor(),
|
|
||||||
systemWithCurrentSubstitution, context, null, RESOLVE_FUNCTION_ARGUMENTS);
|
|
||||||
if (systemWithCurrentSubstitution.hasContradiction() || systemWithCurrentSubstitution.hasErrorInConstrainingTypes()) {
|
|
||||||
addConstraintForValueArgument(valueArgument, valueParameterDescriptor, substituteDontCare, constraintSystem, context,
|
|
||||||
null, RESOLVE_FUNCTION_ARGUMENTS);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
constraintSystem = systemWithCurrentSubstitution;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +249,49 @@ public class CandidateResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <D extends CallableDescriptor> void addConstraintForFunctionLiteral(
|
||||||
|
@NotNull ValueArgument valueArgument,
|
||||||
|
@NotNull ValueParameterDescriptor valueParameterDescriptor,
|
||||||
|
@NotNull ConstraintSystem constraintSystem,
|
||||||
|
@NotNull CallCandidateResolutionContext<D, D> context
|
||||||
|
) {
|
||||||
|
JetExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||||
|
assert argumentExpression instanceof JetFunctionLiteralExpression;
|
||||||
|
JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument);
|
||||||
|
JetType expectedType = constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT);
|
||||||
|
if (expectedType == null || !KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(expectedType)
|
||||||
|
|| CallResolverUtil.containsUnknownFunctionArgument(expectedType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean hasExpectedReturnType = !CallResolverUtil.hasUnknownReturnType(expectedType);
|
||||||
|
if (hasExpectedReturnType) {
|
||||||
|
TemporaryBindingTrace traceToResolveFunctionLiteral = TemporaryBindingTrace.create(
|
||||||
|
context.trace, "trace to resolve function literal with expected return type", argumentExpression);
|
||||||
|
|
||||||
|
final boolean[] mismatch = new boolean[1];
|
||||||
|
JetElement statementExpression = JetPsiUtil.getLastStatementInABlock(((JetFunctionLiteralExpression) argumentExpression).getBodyExpression());
|
||||||
|
if (statementExpression == null) return;
|
||||||
|
ObservableBindingTrace errorInterceptingTrace = ExpressionTypingUtils.makeTraceInterceptingTypeMismatch(
|
||||||
|
traceToResolveFunctionLiteral, statementExpression, mismatch);
|
||||||
|
CallCandidateResolutionContext<D, D> newContext =
|
||||||
|
context.replaceBindingTrace(errorInterceptingTrace).replaceExpectedType(expectedType);
|
||||||
|
JetType type = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, RESOLVE_FUNCTION_ARGUMENTS).getType();
|
||||||
|
if (!mismatch[0]) {
|
||||||
|
constraintSystem.addSubtypeConstraint(
|
||||||
|
type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
|
||||||
|
traceToResolveFunctionLiteral.commit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BindingContextUtils.commitResolutionCacheData(traceToResolveFunctionLiteral, context.trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JetType expectedTypeWithoutReturnType = hasExpectedReturnType ? CallResolverUtil.replaceReturnTypeToUnknown(expectedType) : expectedType;
|
||||||
|
CallCandidateResolutionContext<D, D> newContext = context.replaceExpectedType(expectedTypeWithoutReturnType);
|
||||||
|
JetType type = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, RESOLVE_FUNCTION_ARGUMENTS).getType();
|
||||||
|
constraintSystem.addSubtypeConstraint(
|
||||||
|
type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
|
||||||
|
}
|
||||||
|
|
||||||
private <D extends CallableDescriptor, F extends D> ResolutionStatus inferTypeArguments(CallCandidateResolutionContext<D, F> context) {
|
private <D extends CallableDescriptor, F extends D> ResolutionStatus inferTypeArguments(CallCandidateResolutionContext<D, F> context) {
|
||||||
ResolvedCallImpl<D> candidateCall = context.candidateCall;
|
ResolvedCallImpl<D> candidateCall = context.candidateCall;
|
||||||
final D candidate = candidateCall.getCandidateDescriptor();
|
final D candidate = candidateCall.getCandidateDescriptor();
|
||||||
@@ -361,15 +393,16 @@ public class CandidateResolver {
|
|||||||
|
|
||||||
JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument);
|
JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument);
|
||||||
JetExpression argumentExpression = valueArgument.getArgumentExpression();
|
JetExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||||
TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create(
|
TemporaryBindingTrace traceToResolveArgument = TemporaryBindingTrace.create(
|
||||||
context.trace, "transient trace to resolve argument", argumentExpression);
|
context.trace, "transient trace to resolve argument", argumentExpression);
|
||||||
JetType expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT);
|
JetType expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT);
|
||||||
CallResolutionContext newContext =
|
CallResolutionContext newContext =
|
||||||
context.replaceBindingTrace(traceForUnknown).replaceExpectedType(expectedType != null ? expectedType : NO_EXPECTED_TYPE);
|
context.replaceBindingTrace(traceToResolveArgument).replaceExpectedType(
|
||||||
|
expectedType != null ? expectedType : NO_EXPECTED_TYPE);
|
||||||
JetType type = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, resolveFunctionArgumentBodies).getType();
|
JetType type = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, resolveFunctionArgumentBodies).getType();
|
||||||
constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(
|
constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(
|
||||||
valueParameterDescriptor.getIndex()));
|
valueParameterDescriptor.getIndex()));
|
||||||
BindingContextUtils.commitResolutionCacheData(traceForUnknown, context.trace);
|
BindingContextUtils.commitResolutionCacheData(traceToResolveArgument, context.trace);
|
||||||
if (isErrorType != null) {
|
if (isErrorType != null) {
|
||||||
isErrorType[0] = type == null || ErrorUtils.isErrorType(type);
|
isErrorType[0] = type == null || ErrorUtils.isErrorType(type);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user