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,
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, MUL, PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
// 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,
COLON
);
@@ -398,7 +398,9 @@ public class JetExpressionParsing extends AbstractJetParsing {
int gtPos = matchTokenStreamPredicate(new FirstBefore(
new At(GT),
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
public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) {
@@ -412,7 +414,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
}
});
if (gtPos >= 0) {
myJetParsing.parseTypeArgumentList();
myJetParsing.parseTypeArgumentList(gtPos);
if (!myBuilder.newlineBeforeCurrentToken() && at(LPAR)) parseValueArgumentList();
parseCallWithClosure();
}
@@ -1249,7 +1249,7 @@ public class JetParsing extends AbstractJetParsing {
expect(IDENTIFIER, "Type name expected", TokenSet.orSet(JetExpressionParsing.EXPRESSION_FIRST, JetExpressionParsing.EXPRESSION_FOLLOW));
reference.done(REFERENCE_EXPRESSION);
parseTypeArgumentList();
parseTypeArgumentList(-1);
if (!at(DOT)) {
break;
}
@@ -1268,7 +1268,7 @@ public class JetParsing extends AbstractJetParsing {
/*
* (optionalProjection type){","}
*/
public PsiBuilder.Marker parseTypeArgumentList() {
public PsiBuilder.Marker parseTypeArgumentList(int expectedGtOffset) {
if (!at(LT)) return null;
PsiBuilder.Marker list = mark();
@@ -1292,6 +1292,14 @@ public class JetParsing extends AbstractJetParsing {
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 '>'");
myBuilder.restoreNewlinesState();
@@ -63,10 +63,6 @@ public class OverloadResolver {
if (receiverType != null) {
// ASSERT : either the receiver in not present or we are in a scope with no top-level functions
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)) {
continue;
}
@@ -122,11 +122,19 @@ public class JetTypeInferrer {
@NotNull List<JetType> argumentTypes,
boolean reportUnresolved) {
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
// No generics. Guaranteed
overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved);
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.<JetType>emptyList(), argumentTypes);
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(
@Nullable final JetType receiverType,
@NotNull final JetScope scope,
@@ -163,6 +171,7 @@ public class JetTypeInferrer {
String referencedName = referenceExpression.getReferencedName();
if (receiverType != null && referencedName != null) {
// No generics. Guaranteed
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
reference[0] = referenceExpression;
}
@@ -176,6 +185,7 @@ public class JetTypeInferrer {
// a -- create a hierarchical lookup domain for this.a
String referencedName = expression.getReferencedName();
if (referencedName != null) {
// No generics. Guaranteed
result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName);
reference[0] = expression;
}
@@ -215,12 +225,10 @@ public class JetTypeInferrer {
}
private OverloadDomain wrapForTracing(
@Nullable final OverloadDomain overloadDomain,
final JetReferenceExpression referenceExpression,
@NotNull final OverloadDomain overloadDomain,
@NotNull final JetReferenceExpression referenceExpression,
@Nullable final PsiElement argumentList,
final boolean reportErrors) {
if (overloadDomain == null) return OverloadDomain.EMPTY;
assert referenceExpression != null;
return new OverloadDomain() {
@NotNull
@Override
@@ -400,9 +408,9 @@ public class JetTypeInferrer {
// 1) ends with a name -> (scope, name) to look up
// 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) {
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);
} else {
List<JetType> types = new ArrayList<JetType>();
for (JetTypeProjection projection : typeArguments) {
List<JetType> typeArguments = new ArrayList<JetType>();
for (JetTypeProjection projection : jetTypeArguments) {
// TODO : check that there's no projection
JetTypeReference typeReference = projection.getTypeReference();
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));
}
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(types, valueArgumentTypes);
OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes);
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;
@@ -1340,12 +1359,6 @@ public class JetTypeInferrer {
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
public void visitNewExpression(JetNewExpression expression) {
// 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) {
if (selectorExpression instanceof JetCallExpression) {
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList());
return resolveCall(scope, overloadDomain, callExpression);
return getCallExpressionType(receiverType, (JetCallExpression) selectorExpression);
}
else if (selectorExpression instanceof JetSimpleNameExpression) {
JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker());
@@ -1405,9 +1421,7 @@ public class JetTypeInferrer {
@Override
public void visitCallExpression(JetCallExpression expression) {
JetExpression calleeExpression = expression.getCalleeExpression();
OverloadDomain overloadDomain = getOverloadDomain(null, scope, calleeExpression, expression.getValueArgumentList());
result = resolveCall(scope, overloadDomain, expression);
result = getCallExpressionType(null, expression);
}
@Override
+15
View File
@@ -23,3 +23,18 @@ namespace boundsWithSubstitutors {
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() {
foo<Int?>()
fooo<Double?addddd>()
dd<(Int, Int, Int)>(if (true) (1, 1, 1) else (2, 2, 2))
foo(bar<a, b, c>(d))
foo(bar<a, b+1, c>(d))
@@ -11,6 +11,42 @@ JetFile: TypeExpressionAmbiguities_ERR.jet
BLOCK
PsiElement(LBRACE)('{')
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
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('dd')