From 9b2c666c1f02635ccb199a881c4367186f2ab51b Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Thu, 7 Feb 2013 20:46:43 +0400 Subject: [PATCH] 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 --- .../jetbrains/jet/lang/psi/JetPsiUtil.java | 7 ++ .../lang/resolve/calls/CallResolverUtil.java | 45 ++++++++++++- .../lang/resolve/calls/CandidateResolver.java | 67 ++++++++++++++----- 3 files changed, 100 insertions(+), 19 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java index 64d66416c95..6b1869e0833 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -527,4 +527,11 @@ public class JetPsiUtil { 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 statements = blockExpression.getStatements(); + return statements.isEmpty() ? null : statements.get(statements.size() - 1); + } } 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 808cde90f96..4f99177a7e4 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 @@ -16,7 +16,9 @@ package org.jetbrains.jet.lang.resolve.calls; +import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 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.ResolvedValueArgument; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate; -import org.jetbrains.jet.lang.types.ErrorUtils; -import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import java.util.List; import java.util.Map; +import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; + public class CallResolverUtil { public static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE"); @@ -74,4 +79,40 @@ public class CallResolverUtil { return copy; } + + public static boolean containsUnknownFunctionArgument(@NotNull JetType type) { + assert KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(type); + List 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 arguments = type.getArguments(); + List 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()); + } } 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 887abc2d10a..bc00399d827 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,7 +29,6 @@ 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.ResolutionContext; 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.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.ReceiverValue; 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 java.util.*; @@ -193,9 +194,6 @@ public class CandidateResolver { ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem(); assert constraintSystem != null; - TypeSubstitutor substituteDontCare = ConstraintSystemWithPriorities - .makeConstantSubstitutor(resolvedCall.getCandidateDescriptor().getTypeParameters(), DONT_CARE); - // constraints for function literals // Value parameters for (Map.Entry entry : resolvedCall.getValueArguments().entrySet()) { @@ -205,16 +203,7 @@ public class CandidateResolver { for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) { if (!(valueArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression)) continue; - ConstraintSystem systemWithCurrentSubstitution = constraintSystem.copy(); - 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; - } + addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context); } } @@ -260,6 +249,49 @@ public class CandidateResolver { } } + private void addConstraintForFunctionLiteral( + @NotNull ValueArgument valueArgument, + @NotNull ValueParameterDescriptor valueParameterDescriptor, + @NotNull ConstraintSystem constraintSystem, + @NotNull CallCandidateResolutionContext 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 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 newContext = context.replaceExpectedType(expectedTypeWithoutReturnType); + JetType type = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, RESOLVE_FUNCTION_ARGUMENTS).getType(); + constraintSystem.addSubtypeConstraint( + type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex())); + } + private ResolutionStatus inferTypeArguments(CallCandidateResolutionContext context) { ResolvedCallImpl candidateCall = context.candidateCall; final D candidate = candidateCall.getCandidateDescriptor(); @@ -361,15 +393,16 @@ public class CandidateResolver { JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument); JetExpression argumentExpression = valueArgument.getArgumentExpression(); - TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create( + TemporaryBindingTrace traceToResolveArgument = TemporaryBindingTrace.create( context.trace, "transient trace to resolve argument", argumentExpression); JetType expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT); 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(); constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition( valueParameterDescriptor.getIndex())); - BindingContextUtils.commitResolutionCacheData(traceForUnknown, context.trace); + BindingContextUtils.commitResolutionCacheData(traceToResolveArgument, context.trace); if (isErrorType != null) { isErrorType[0] = type == null || ErrorUtils.isErrorType(type); }