JET-51 Check generic bounds in method calls

+
A bug in parseCallExpression() heuristics fixed
This commit is contained in:
Andrey Breslav
2011-05-24 21:22:55 +04:00
parent 139af5b36a
commit 2a2a697953
7 changed files with 105 additions and 32 deletions
@@ -26,7 +26,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD, TRY_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD, CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD, TRY_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD,
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, MUL, PLUS, MINUS, EXCL, DIV, PERC, LTEQ, WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, MUL, PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
// TODO GTEQ, foo<bar, baz>=x // TODO GTEQ, foo<bar, baz>=x
EQEQEQ, ARROW, DOUBLE_ARROW, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR, SAFE_ACCESS, ELVIS, QUEST, EQEQEQ, ARROW, DOUBLE_ARROW, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR, SAFE_ACCESS, ELVIS,
SEMICOLON, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, NOT_IN, NOT_IS, HASH, SEMICOLON, RANGE, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, NOT_IN, NOT_IS, HASH,
COLON COLON
); );
@@ -398,7 +398,9 @@ public class JetExpressionParsing extends AbstractJetParsing {
int gtPos = matchTokenStreamPredicate(new FirstBefore( int gtPos = matchTokenStreamPredicate(new FirstBefore(
new At(GT), new At(GT),
new AtSet(TYPE_ARGUMENT_LIST_STOPPERS, TokenSet.create(RPAR, RBRACE, RBRACKET)) new AtSet(TYPE_ARGUMENT_LIST_STOPPERS, TokenSet.create(RPAR, RBRACE, RBRACKET))
.or(new AtFirstTokenOfTokens(IDENTIFIER, LPAR)) .or(new AtFirstTokenOfTokens(IDENTIFIER, LPAR)
// .or(new AtFirstTokenOfTokens(QUEST, IDENTIFIER))
)
) { ) {
@Override @Override
public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) { public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) {
@@ -412,7 +414,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
} }
}); });
if (gtPos >= 0) { if (gtPos >= 0) {
myJetParsing.parseTypeArgumentList(); myJetParsing.parseTypeArgumentList(gtPos);
if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList(); if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
parseCallWithClosure(); parseCallWithClosure();
} }
@@ -1249,7 +1249,7 @@ public class JetParsing extends AbstractJetParsing {
expect(IDENTIFIER, "Type name expected", TokenSet.orSet(JetExpressionParsing.EXPRESSION_FIRST, JetExpressionParsing.EXPRESSION_FOLLOW)); expect(IDENTIFIER, "Type name expected", TokenSet.orSet(JetExpressionParsing.EXPRESSION_FIRST, JetExpressionParsing.EXPRESSION_FOLLOW));
reference.done(REFERENCE_EXPRESSION); reference.done(REFERENCE_EXPRESSION);
parseTypeArgumentList(); parseTypeArgumentList(-1);
if (!at(DOT)) { if (!at(DOT)) {
break; break;
} }
@@ -1268,7 +1268,7 @@ public class JetParsing extends AbstractJetParsing {
/* /*
* (optionalProjection type){","} * (optionalProjection type){","}
*/ */
public PsiBuilder.Marker parseTypeArgumentList() { public PsiBuilder.Marker parseTypeArgumentList(int expectedGtOffset) {
if (!at(LT)) return null; if (!at(LT)) return null;
PsiBuilder.Marker list = mark(); PsiBuilder.Marker list = mark();
@@ -1292,6 +1292,14 @@ public class JetParsing extends AbstractJetParsing {
advance(); // COMMA advance(); // COMMA
} }
if (expectedGtOffset >= 0 && myBuilder.getCurrentOffset() < expectedGtOffset) {
final PsiBuilder.Marker error = mark();
while (myBuilder.getCurrentOffset() < expectedGtOffset) {
advance();
}
error.error("Expecting a '>'");
}
expect(GT, "Expecting a '>'"); expect(GT, "Expecting a '>'");
myBuilder.restoreNewlinesState(); myBuilder.restoreNewlinesState();
@@ -63,10 +63,6 @@ public class OverloadResolver {
if (receiverType != null) { if (receiverType != null) {
// ASSERT : either the receiver in not present or we are in a scope with no top-level functions // ASSERT : either the receiver in not present or we are in a scope with no top-level functions
final JetType functionReceiverType = descriptor.getReceiverType(); final JetType functionReceiverType = descriptor.getReceiverType();
// final ClassDescriptor containingDeclaration = (ClassDescriptor) descriptor.getContainingDeclaration();
// assert functionReceiverType != null || containingDeclaration != null &&
// containingDeclaration.getTypeConstructor().equals(receiverType.getConstructor());
if (functionReceiverType != null && !typeChecker.isSubtypeOf(receiverType, functionReceiverType)) { if (functionReceiverType != null && !typeChecker.isSubtypeOf(receiverType, functionReceiverType)) {
continue; continue;
} }
@@ -122,11 +122,19 @@ public class JetTypeInferrer {
@NotNull List<JetType> argumentTypes, @NotNull List<JetType> argumentTypes,
boolean reportUnresolved) { boolean reportUnresolved) {
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
// No generics. Guaranteed
overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved); overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved);
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), argumentTypes); OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), argumentTypes);
return resolutionResult.isSuccess() ? resolutionResult.getFunctionDescriptor() : null; return resolutionResult.isSuccess() ? resolutionResult.getFunctionDescriptor() : null;
} }
@NotNull
private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) {
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
// No generics. Guaranteed
return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), Collections.<JetType>emptyList());
}
private OverloadDomain getOverloadDomain( private OverloadDomain getOverloadDomain(
@Nullable final JetType receiverType, @Nullable final JetType receiverType,
@NotNull final JetScope scope, @NotNull final JetScope scope,
@@ -163,6 +171,7 @@ public class JetTypeInferrer {
String referencedName = referenceExpression.getReferencedName(); String referencedName = referenceExpression.getReferencedName();
if (receiverType != null && referencedName != null) { if (receiverType != null && referencedName != null) {
// No generics. Guaranteed
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
reference[0] = referenceExpression; reference[0] = referenceExpression;
} }
@@ -176,6 +185,7 @@ public class JetTypeInferrer {
// a -- create a hierarchical lookup domain for this.a // a -- create a hierarchical lookup domain for this.a
String referencedName = expression.getReferencedName(); String referencedName = expression.getReferencedName();
if (referencedName != null) { if (referencedName != null) {
// No generics. Guaranteed
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
reference[0] = expression; reference[0] = expression;
} }
@@ -215,12 +225,10 @@ public class JetTypeInferrer {
} }
private OverloadDomain wrapForTracing( private OverloadDomain wrapForTracing(
@Nullable final OverloadDomain overloadDomain, @NotNull final OverloadDomain overloadDomain,
final JetReferenceExpression referenceExpression, @NotNull final JetReferenceExpression referenceExpression,
@Nullable final PsiElement argumentList, @Nullable final PsiElement argumentList,
final boolean reportErrors) { final boolean reportErrors) {
if (overloadDomain == null) return OverloadDomain.EMPTY;
assert referenceExpression != null;
return new OverloadDomain() { return new OverloadDomain() {
@NotNull @NotNull
@Override @Override
@@ -400,9 +408,9 @@ public class JetTypeInferrer {
// 1) ends with a name -> (scope, name) to look up // 1) ends with a name -> (scope, name) to look up
// 2) ends with something else -> just check types // 2) ends with something else -> just check types
final List<JetTypeProjection> typeArguments = call.getTypeArguments(); final List<JetTypeProjection> jetTypeArguments = call.getTypeArguments();
for (JetTypeProjection typeArgument : typeArguments) { for (JetTypeProjection typeArgument : jetTypeArguments) {
if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) { if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) {
trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning
} }
@@ -429,12 +437,12 @@ public class JetTypeInferrer {
// result = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArguments, functionLiteralArgument); // result = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArguments, functionLiteralArgument);
} else { } else {
List<JetType> types = new ArrayList<JetType>(); List<JetType> typeArguments = new ArrayList<JetType>();
for (JetTypeProjection projection : typeArguments) { for (JetTypeProjection projection : jetTypeArguments) {
// TODO : check that there's no projection // TODO : check that there's no projection
JetTypeReference typeReference = projection.getTypeReference(); JetTypeReference typeReference = projection.getTypeReference();
if (typeReference != null) { if (typeReference != null) {
types.add(typeResolver.resolveType(scope, typeReference)); typeArguments.add(typeResolver.resolveType(scope, typeReference));
} }
} }
@@ -453,9 +461,20 @@ public class JetTypeInferrer {
valueArgumentTypes.add(safeGetType(scope, valueArgument, false)); valueArgumentTypes.add(safeGetType(scope, valueArgument, false));
} }
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(types, valueArgumentTypes); OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes);
if (resolutionResult.isSuccess()) { if (resolutionResult.isSuccess()) {
return resolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType(); final FunctionDescriptor functionDescriptor = resolutionResult.getFunctionDescriptor();
List<TypeParameterDescriptor> typeParameters = functionDescriptor.getOriginal().getTypeParameters();
for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) {
TypeParameterDescriptor typeParameterDescriptor = typeParameters.get(i);
final JetType typeArgument = typeArguments.get(i);
if (!semanticServices.getTypeChecker().isSubtypeOf(typeArgument, typeParameterDescriptor.getBoundsAsType())) {
trace.getErrorHandler().genericError(jetTypeArguments.get(i).getNode(), "Bound of the type parameter " + DescriptorRenderer.TEXT.render(typeParameterDescriptor) + " is not respected by the type " + typeArgument);
}
}
return functionDescriptor.getUnsubstitutedReturnType();
} }
} }
return null; return null;
@@ -1340,12 +1359,6 @@ public class JetTypeInferrer {
return true; return true;
} }
@NotNull
private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) {
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), Collections.<JetType>emptyList());
}
@Override @Override
public void visitNewExpression(JetNewExpression expression) { public void visitNewExpression(JetNewExpression expression) {
// TODO : type argument inference // TODO : type argument inference
@@ -1386,11 +1399,14 @@ public class JetTypeInferrer {
} }
} }
private JetType getCallExpressionType(@Nullable JetType receiverType, @NotNull JetCallExpression callExpression) {
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
return resolveCall(scope, overloadDomain, callExpression);
}
private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression) { private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression) {
if (selectorExpression instanceof JetCallExpression) { if (selectorExpression instanceof JetCallExpression) {
JetCallExpression callExpression = (JetCallExpression) selectorExpression; return getCallExpressionType(receiverType, (JetCallExpression) selectorExpression);
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
return resolveCall(scope, overloadDomain, callExpression);
} }
else if (selectorExpression instanceof JetSimpleNameExpression) { else if (selectorExpression instanceof JetSimpleNameExpression) {
JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker()); JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker());
@@ -1405,9 +1421,7 @@ public class JetTypeInferrer {
@Override @Override
public void visitCallExpression(JetCallExpression expression) { public void visitCallExpression(JetCallExpression expression) {
JetExpression calleeExpression = expression.getCalleeExpression(); result = getCallExpressionType(null, expression);
OverloadDomain overloadDomain = getOverloadDomain(null, scope, calleeExpression, expression.getValueArgumentList());
result = resolveCall(scope, overloadDomain, expression);
} }
@Override @Override
+15
View File
@@ -23,3 +23,18 @@ namespace boundsWithSubstitutors {
val x : {(B<<error>Char</error>>) : B<<error>Any</error>>} val x : {(B<<error>Char</error>>) : B<<error>Any</error>>}
} }
fun test() {
foo<<error>Int?</error>>()
foo<Int>()
bar<Int?>()
bar<Int>()
bar<<error>Double?</error>>()
bar<<error>Double</error>>()
1.buzz<<error>Double</error>>()
}
fun foo<T : Any>() {}
fun bar<T : Int?>() {}
fun <T : Int> Int.buzz() : Unit {}
@@ -1,4 +1,6 @@
fun foo() { fun foo() {
foo<Int?>()
fooo<Double?addddd>()
dd<(Int, Int, Int)>(if (true) (1, 1, 1) else (2, 2, 2)) dd<(Int, Int, Int)>(if (true) (1, 1, 1) else (2, 2, 2))
foo(bar<a, b, c>(d)) foo(bar<a, b, c>(d))
foo(bar<a, b+1, c>(d)) foo(bar<a, b+1, c>(d))
@@ -11,6 +11,42 @@ JetFile: TypeExpressionAmbiguities_ERR.jet
BLOCK BLOCK
PsiElement(LBRACE)('{') PsiElement(LBRACE)('{')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
CALL_EXPRESSION
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('foo')
TYPE_ARGUMENT_LIST
PsiElement(LT)('<')
TYPE_PROJECTION
TYPE_REFERENCE
NULLABLE_TYPE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('Int')
PsiElement(QUEST)('?')
PsiElement(GT)('>')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiWhiteSpace('\n ')
CALL_EXPRESSION
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('fooo')
TYPE_ARGUMENT_LIST
PsiElement(LT)('<')
TYPE_PROJECTION
TYPE_REFERENCE
NULLABLE_TYPE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('Double')
PsiElement(QUEST)('?')
PsiErrorElement:Expecting a '>'
PsiElement(IDENTIFIER)('addddd')
PsiElement(GT)('>')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiWhiteSpace('\n ')
CALL_EXPRESSION CALL_EXPRESSION
REFERENCE_EXPRESSION REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('dd') PsiElement(IDENTIFIER)('dd')