From e0bccf746b1d7fc901c4c5e4fd124c3d1a52a4fe Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 22 Aug 2011 18:05:43 +0400 Subject: [PATCH] Overload resolution with type inference --- idea/src/org/jetbrains/jet/JetNodeTypes.java | 1 + .../descriptors/ValueParameterDescriptor.java | 3 + .../lang/parsing/JetExpressionParsing.java | 4 + .../jet/lang/psi/JetValueArgument.java | 17 +- .../jet/lang/psi/JetValueArgumentName.java | 22 ++ .../jet/lang/psi/ValueArgumentPsi.java | 48 --- .../jet/lang/types/JetTypeInferrer.java | 279 ++++++++++++++---- 7 files changed, 263 insertions(+), 111 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/psi/JetValueArgumentName.java delete mode 100644 idea/src/org/jetbrains/jet/lang/psi/ValueArgumentPsi.java diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index a9add75e4e2..8899a0ce9c6 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -44,6 +44,7 @@ public interface JetNodeTypes { JetNodeType TYPE_ARGUMENT_LIST = new JetNodeType("TYPE_ARGUMENT_LIST", JetTypeArgumentList.class); JetNodeType VALUE_ARGUMENT_LIST = new JetNodeType("VALUE_ARGUMENT_LIST", JetValueArgumentList.class); JetNodeType VALUE_ARGUMENT = new JetNodeType("VALUE_ARGUMENT", JetValueArgument.class); + JetNodeType VALUE_ARGUMENT_NAME = new JetNodeType("VALUE_ARGUMENT_LIST", JetValueArgumentName.class); JetNodeType TYPE_REFERENCE = new JetNodeType("TYPE_REFERENCE", JetTypeReference.class); JetNodeType LABELED_TUPLE_ENTRY = new JetNodeType("LABELED_TUPLE_ENTRY"); JetNodeType LABELED_TUPLE_TYPE_ENTRY = new JetNodeType("LABELED_TUPLE_TYPE_ENTRY"); diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/ValueParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/ValueParameterDescriptor.java index 0f76ee969ab..7ac25d5dc5e 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/ValueParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/ValueParameterDescriptor.java @@ -20,4 +20,7 @@ public interface ValueParameterDescriptor extends VariableDescriptor { @Override @NotNull JetType getOutType(); + + @Override + ValueParameterDescriptor getOriginal(); } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 484f354c181..b0cbbb879f0 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -1625,7 +1625,11 @@ public class JetExpressionParsing extends AbstractJetParsing { private void parseValueArgument() { PsiBuilder.Marker argument = mark(); if (at(IDENTIFIER) && lookahead(1) == EQ) { + PsiBuilder.Marker argName = mark(); + PsiBuilder.Marker reference = mark(); advance(); // IDENTIFIER + reference.done(REFERENCE_EXPRESSION); + argName.done(VALUE_ARGUMENT_NAME); advance(); // EQ } if (at(OUT_KEYWORD) || at(REF_KEYWORD)) advance(); // REF or OUT diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetValueArgument.java b/idea/src/org/jetbrains/jet/lang/psi/JetValueArgument.java index 3d4628cb026..15fada704b3 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetValueArgument.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetValueArgument.java @@ -1,15 +1,15 @@ package org.jetbrains.jet.lang.psi; import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lexer.JetTokens; /** * @author max */ -public class JetValueArgument extends JetElement implements ValueArgumentPsi { +public class JetValueArgument extends JetElement { public JetValueArgument(@NotNull ASTNode node) { super(node); } @@ -28,18 +28,9 @@ public class JetValueArgument extends JetElement implements ValueArgumentPsi { return findChildByClass(JetExpression.class); } - @Override - public PsiElement asElement() { - return this; - } - @Nullable - public String getArgumentName() { - ASTNode firstChildNode = getNode().getFirstChildNode(); - if (firstChildNode == null) { - return null; - } - return firstChildNode.getElementType() == JetTokens.IDENTIFIER ? firstChildNode.getText() : null; + public JetValueArgumentName getArgumentName() { + return (JetValueArgumentName) findChildByType(JetNodeTypes.VALUE_ARGUMENT_NAME); } public boolean isNamed() { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetValueArgumentName.java b/idea/src/org/jetbrains/jet/lang/psi/JetValueArgumentName.java new file mode 100644 index 00000000000..87c4e8504d6 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetValueArgumentName.java @@ -0,0 +1,22 @@ +package org.jetbrains.jet.lang.psi; + +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JetNodeTypes; + +/** + * @author abreslav + */ +public class JetValueArgumentName extends JetElement { + public JetValueArgumentName(@NotNull ASTNode node) { + super(node); + } + + @Nullable @IfNotParsed + public JetSimpleNameExpression getReferenceExpression() { + return (JetSimpleNameExpression) findChildByType(JetNodeTypes.REFERENCE_EXPRESSION); + } + + +} diff --git a/idea/src/org/jetbrains/jet/lang/psi/ValueArgumentPsi.java b/idea/src/org/jetbrains/jet/lang/psi/ValueArgumentPsi.java deleted file mode 100644 index 613431c6b09..00000000000 --- a/idea/src/org/jetbrains/jet/lang/psi/ValueArgumentPsi.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.jetbrains.jet.lang.psi; - -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.Nullable; - -/** - * @author abreslav - */ -public interface ValueArgumentPsi { - - class ArgumentExpressionWrapper implements ValueArgumentPsi { - - private final JetExpression expression; - - public ArgumentExpressionWrapper(JetExpression expression) { - this.expression = expression; - } - - @Override - public PsiElement asElement() { - return expression; - } - - @Override - public String getArgumentName() { - return null; - } - - @Override - public boolean isNamed() { - return false; - } - - @Override - public JetExpression getArgumentExpression() { - return expression; - } - } - - PsiElement asElement(); - @Nullable - String getArgumentName(); - - boolean isNamed(); - - @Nullable - public JetExpression getArgumentExpression(); -} diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index a03f9500ca3..c9bb75676b5 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -27,6 +27,7 @@ import org.jetbrains.jet.util.WritableSlice; import java.util.*; +import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; import static org.jetbrains.jet.lang.resolve.BindingContext.STATEMENT; /** @@ -542,62 +543,240 @@ public class JetTypeInferrer { @NotNull JetCall call, @NotNull JetType expectedType ) { - if (call.getTypeArguments().isEmpty()) { - JetExpression calleeExpression = call.getCalleeExpression(); - Collection candidates; - if (calleeExpression instanceof JetSimpleNameExpression) { - JetSimpleNameExpression expression = (JetSimpleNameExpression) calleeExpression; - candidates = scope.getFunctionGroup(expression.getReferencedName()).getFunctionDescriptors(); + JetExpression calleeExpression = call.getCalleeExpression(); + Collection candidates; + JetReferenceExpression functionReference; + if (calleeExpression instanceof JetSimpleNameExpression) { + JetSimpleNameExpression expression = (JetSimpleNameExpression) calleeExpression; + functionReference = expression; + candidates = scope.getFunctionGroup(expression.getReferencedName()).getFunctionDescriptors(); + } + else if (calleeExpression instanceof JetConstructorCalleeExpression) { + JetConstructorCalleeExpression expression = (JetConstructorCalleeExpression) calleeExpression; + functionReference = expression.getConstructorReferenceExpression(); + JetType constructedType = typeResolver.resolveType(scope, expression.getTypeReference()); + DeclarationDescriptor declarationDescriptor = constructedType.getConstructor().getDeclarationDescriptor(); + if (declarationDescriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; + candidates = classDescriptor.getConstructors().getFunctionDescriptors(); } else { - throw new UnsupportedOperationException("Type argument inference not implemented"); - } - - assert candidates.size() == 1; - - FunctionDescriptor candidate = candidates.iterator().next(); - - assert candidate.getTypeParameters().size() == call.getTypeArguments().size(); - - ConstraintSystem constraintSystem = new ConstraintSystem(); - for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { - constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO - } - - Iterator parameters = candidate.getValueParameters().iterator(); - for (JetValueArgument valueArgument : call.getValueArguments()) { - assert !valueArgument.isNamed(); - ValueParameterDescriptor valueParameterDescriptor = parameters.next(); - JetExpression expression = valueArgument.getArgumentExpression(); - JetType type = getType(scope, expression, false, NO_EXPECTED_TYPE); - constraintSystem.addSubtypingConstraint(type, valueParameterDescriptor.getOutType()); - } - - if (expectedType != NO_EXPECTED_TYPE) { - System.out.println("expectedType = " + expectedType); - constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType); - } - - ConstraintSystem.Solution solution = constraintSystem.solve(); - if (!solution.isSuccessful()) { - trace.getErrorHandler().genericError(calleeExpression.getNode(), "Type inference failed"); -// for (Inconsistency inconsistency : solution.getInconsistencies()) { -// System.out.println("inconsistency = " + inconsistency); -// } + trace.getErrorHandler().genericError(calleeExpression.getNode(), "Not a class"); return null; } - else { - for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { - JetType value = solution.getValue(typeParameterDescriptor); - System.out.println("typeParameterDescriptor = " + typeParameterDescriptor); - System.out.println("value = " + value); - } - return solution.getSubstitutor().substitute(candidate.getReturnType(), Variance.INVARIANT); // TODO - } -// return null; } else { - throw new UnsupportedOperationException("Explicit type arguments not implemented"); + throw new UnsupportedOperationException("Type argument inference not implemented"); + } + + Map successfulCandidates = Maps.newHashMap(); + Set failedCandidates = Sets.newHashSet(); + Map solutions = Maps.newHashMap(); + Map traces = Maps.newHashMap(); + + for (FunctionDescriptor candidate : candidates) { + TemporaryBindingTrace temporaryTrace = new TemporaryBindingTrace(trace.getBindingContext()); + traces.put(candidate, temporaryTrace); + Services temporaryServices = getServices(temporaryTrace); + + temporaryTrace.record(BindingContext.REFERENCE_TARGET, functionReference, candidate); + + // Argument to parameter matching + Map argumentsToParameters = Maps.newHashMap(); + Set usedParameters = Sets.newHashSet(); + + List valueParameters = candidate.getValueParameters(); + Map parameterByName = Maps.newHashMap(); + for (ValueParameterDescriptor valueParameter : valueParameters) { + parameterByName.put(valueParameter.getName(), valueParameter); + } + + List valueArguments = call.getValueArguments(); + boolean someNamed = false; + boolean somePositioned = false; + boolean error = false; + for (int i = 0; i < valueArguments.size(); i++) { + JetValueArgument valueArgument = valueArguments.get(i); + if (valueArgument.isNamed()) { + someNamed = true; + if (somePositioned) { + temporaryTrace.getErrorHandler().genericError(valueArgument.getArgumentName().getNode(), "Mixing named and positioned arguments in not allowed"); + error = true; + } + else { + ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(valueArgument.getArgumentName().getName()); + usedParameters.add(valueParameterDescriptor); + if (valueParameterDescriptor == null) { + temporaryTrace.getErrorHandler().genericError(valueArgument.getArgumentName().getNode(), "Cannot find a parameter with this name"); + error = true; + } + else { + trace.record(REFERENCE_TARGET, valueArgument.getArgumentName().getReferenceExpression(), valueParameterDescriptor); + argumentsToParameters.put(valueArgument, valueParameterDescriptor); + } + } + } + else { + somePositioned = true; + if (someNamed) { + temporaryTrace.getErrorHandler().genericError(valueArgument.getNode(), "Mixing named and positioned arguments in not allowed"); + error = true; + } + else { + if (i < valueParameters.size()) { + ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(i); + usedParameters.add(valueParameterDescriptor); + argumentsToParameters.put(valueArgument, valueParameterDescriptor); + } + else { + ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1); + if (valueParameterDescriptor.isVararg()) { + argumentsToParameters.put(valueArgument, valueParameterDescriptor); + usedParameters.add(valueParameterDescriptor); + } + else { + trace.getErrorHandler().genericError(valueArgument.getNode(), "Too many arguments"); + } + } + } + } + } + + for (ValueParameterDescriptor valueParameter : valueParameters) { + if (!usedParameters.contains(valueParameter)) { + if (!valueParameter.hasDefaultValue()) { + trace.getErrorHandler().genericError(call.getValueArgumentList().getNode(), "No value passed for parameter " + valueParameter.getName()); + error = true; + } + } + } + + if (error) continue; + + if (call.getTypeArguments().isEmpty()) { + // Type argument inference + + ConstraintSystem constraintSystem = new ConstraintSystem(); + for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { + constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO + } + + for (Map.Entry entry : argumentsToParameters.entrySet()) { + JetValueArgument valueArgument = entry.getKey(); + ValueParameterDescriptor valueParameterDescriptor = entry.getValue(); + + JetExpression expression = valueArgument.getArgumentExpression(); + // TODO : more attempts, with different expected types + JetType type = temporaryServices.getType(scope, expression, false, NO_EXPECTED_TYPE); + constraintSystem.addSubtypingConstraint(type, valueParameterDescriptor.getOutType()); + } + + if (expectedType != NO_EXPECTED_TYPE) { + constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType); + } + + ConstraintSystem.Solution solution = constraintSystem.solve(); + solutions.put(candidate, solution); + if (solution.isSuccessful()) { + successfulCandidates.put(candidate, candidate.substitute(solution.getSubstitutor())); + } + else { + temporaryTrace.getErrorHandler().genericError(calleeExpression.getNode(), "Type inference failed"); + failedCandidates.add(candidate); + } + } + else { + final List jetTypeArguments = call.getTypeArguments(); + + for (JetTypeProjection typeArgument : jetTypeArguments) { + if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) { + trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning + } + } + + List typeArguments = new ArrayList(); + for (JetTypeProjection projection : jetTypeArguments) { + // TODO : check that there's no projection + JetTypeReference typeReference = projection.getTypeReference(); + if (typeReference != null) { + typeArguments.add(new TypeResolver(semanticServices, temporaryTrace, true).resolveType(scope, typeReference)); + } + } + + int typeArgCount = typeArguments.size(); + if (candidate.getTypeParameters().size() == typeArgCount) { + FunctionDescriptor substitutedFunctionDescriptor = FunctionDescriptorUtil.substituteFunctionDescriptor(typeArguments, candidate); + + assert substitutedFunctionDescriptor != null; + Map parameterMap = Maps.newHashMap(); + for (ValueParameterDescriptor valueParameterDescriptor : substitutedFunctionDescriptor.getValueParameters()) { + parameterMap.put(valueParameterDescriptor.getOriginal(), valueParameterDescriptor); + } + + boolean localError = false; + for (Map.Entry entry : argumentsToParameters.entrySet()) { + JetValueArgument valueArgument = entry.getKey(); + ValueParameterDescriptor valueParameterDescriptor = entry.getValue(); + + ValueParameterDescriptor substitutedParameter = parameterMap.get(valueParameterDescriptor.getOriginal()); + + JetType parameterType = substitutedParameter.getOutType(); + JetType type = temporaryServices.getType(scope, valueArgument.getArgumentExpression(), false, parameterType); + if (type == null) { + localError = true; + } + } + if (localError) { + failedCandidates.add(candidate); + } + else { + successfulCandidates.put(candidate, substitutedFunctionDescriptor); + } + + } + else { + failedCandidates.add(candidate); + } + } + } + + if (successfulCandidates.size() > 0) { + if (successfulCandidates.size() == 1) { + Map.Entry entry = successfulCandidates.entrySet().iterator().next(); + FunctionDescriptor functionDescriptor = entry.getKey(); + FunctionDescriptor result = entry.getValue(); + + TemporaryBindingTrace temporaryTrace = traces.get(functionDescriptor); + temporaryTrace.addAllMyDataTo(trace); + return result.getReturnType(); + } + else { + // TODO : Choose more specific + + StringBuilder stringBuilder = new StringBuilder(); + for (FunctionDescriptor functionDescriptor : successfulCandidates.keySet()) { + stringBuilder.append(DescriptorRenderer.HTML.render(functionDescriptor)).append("
"); + } + + trace.getErrorHandler().genericError(calleeExpression.getNode(), "Overload resolution ambiguity:
" + stringBuilder); + return null; + } + } + else { + if (failedCandidates.size() == 1) { + FunctionDescriptor functionDescriptor = failedCandidates.iterator().next(); + TemporaryBindingTrace temporaryTrace = traces.get(functionDescriptor); + temporaryTrace.addAllMyDataTo(trace); + } + else { + StringBuilder stringBuilder = new StringBuilder(); + for (FunctionDescriptor functionDescriptor : failedCandidates) { + stringBuilder.append(DescriptorRenderer.HTML.render(functionDescriptor)).append("
"); + } + + trace.getErrorHandler().genericError(calleeExpression.getNode(), "None of the following functions can be called with the arguments supplied:
" + stringBuilder); + } + return null; } }