--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Fri Nov 30 15:14:36 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Fri Nov 30 15:14:48 2012 +0000
@@ -124,6 +124,9 @@
this.allowLambda = source.allowLambda();
this.allowMethodReferences = source.allowMethodReferences();
this.allowDefaultMethods = source.allowDefaultMethods();
+ this.allowIntersectionTypesInCast =
+ source.allowIntersectionTypesInCast() &&
+ fac.options.isSet("allowIntersectionTypes");
this.keepDocComments = keepDocComments;
docComments = newDocCommentTable(keepDocComments, fac);
this.keepLineMap = keepLineMap;
@@ -197,6 +200,10 @@
*/
boolean allowDefaultMethods;
+ /** Switch: should we allow intersection types in cast?
+ */
+ boolean allowIntersectionTypesInCast;
+
/** Switch: should we keep docComments?
*/
boolean keepDocComments;
@@ -239,22 +246,38 @@
}
protected boolean peekToken(TokenKind tk) {
- return S.token(1).kind == tk;
+ return peekToken(0, tk);
+ }
+
+ protected boolean peekToken(int lookahead, TokenKind tk) {
+ return S.token(lookahead + 1).kind == tk;
}
protected boolean peekToken(TokenKind tk1, TokenKind tk2) {
- return S.token(1).kind == tk1 &&
- S.token(2).kind == tk2;
+ return peekToken(0, tk1, tk2);
+ }
+
+ protected boolean peekToken(int lookahead, TokenKind tk1, TokenKind tk2) {
+ return S.token(lookahead + 1).kind == tk1 &&
+ S.token(lookahead + 2).kind == tk2;
}
protected boolean peekToken(TokenKind tk1, TokenKind tk2, TokenKind tk3) {
- return S.token(1).kind == tk1 &&
- S.token(2).kind == tk2 &&
- S.token(3).kind == tk3;
+ return peekToken(0, tk1, tk2, tk3);
+ }
+
+ protected boolean peekToken(int lookahead, TokenKind tk1, TokenKind tk2, TokenKind tk3) {
+ return S.token(lookahead + 1).kind == tk1 &&
+ S.token(lookahead + 2).kind == tk2 &&
+ S.token(lookahead + 3).kind == tk3;
}
protected boolean peekToken(TokenKind... kinds) {
- for (int lookahead = 0 ; lookahead < kinds.length ; lookahead++) {
+ return peekToken(0, kinds);
+ }
+
+ protected boolean peekToken(int lookahead, TokenKind... kinds) {
+ for (; lookahead < kinds.length ; lookahead++) {
if (S.token(lookahead + 1).kind != kinds[lookahead]) {
return false;
}
@@ -966,102 +989,40 @@
break;
case LPAREN:
if (typeArgs == null && (mode & EXPR) != 0) {
- if (peekToken(MONKEYS_AT) ||
- peekToken(FINAL) ||
- peekToken(RPAREN) ||
- peekToken(IDENTIFIER, COMMA) ||
- peekToken(IDENTIFIER, RPAREN, ARROW)) {
- //implicit n-ary lambda
- t = lambdaExpressionOrStatement(true, peekToken(MONKEYS_AT) || peekToken(FINAL), pos);
- break;
- } else {
- nextToken();
- mode = EXPR | TYPE | NOPARAMS;
- t = term3();
- if ((mode & TYPE) != 0 && token.kind == LT) {
- // Could be a cast to a parameterized type
- JCTree.Tag op = JCTree.Tag.LT;
- int pos1 = token.pos;
- nextToken();
- mode &= (EXPR | TYPE);
- mode |= TYPEARG;
- JCExpression t1 = term3();
- if ((mode & TYPE) != 0 &&
- (token.kind == COMMA || token.kind == GT)) {
- mode = TYPE;
- ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
- args.append(t1);
- while (token.kind == COMMA) {
- nextToken();
- args.append(typeArgument());
- }
- accept(GT);
- t = toP(F.at(pos1).TypeApply(t, args.toList()));
- checkGenerics();
- mode = EXPR | TYPE; //could be a lambda or a method ref or a cast to a type
- t = term3Rest(t, typeArgs);
- if (token.kind == IDENTIFIER || token.kind == ELLIPSIS) {
- //explicit lambda (w/ generic type)
- mode = EXPR;
- JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
- if (token.kind == ELLIPSIS) {
- mods.flags = Flags.VARARGS;
- t = to(F.at(token.pos).TypeArray(t));
- nextToken();
- }
- t = lambdaExpressionOrStatement(variableDeclaratorId(mods, t), pos);
- break;
- }
- } else if ((mode & EXPR) != 0) {
- mode = EXPR;
- JCExpression e = term2Rest(t1, TreeInfo.shiftPrec);
- t = F.at(pos1).Binary(op, t, e);
- t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
- } else {
- accept(GT);
- }
- } else if ((mode & TYPE) != 0 &&
- (token.kind == IDENTIFIER || token.kind == ELLIPSIS)) {
- //explicit lambda (w/ non-generic type)
+ ParensResult pres = analyzeParens();
+ switch (pres) {
+ case CAST:
+ accept(LPAREN);
+ mode = TYPE;
+ int pos1 = pos;
+ List<JCExpression> targets = List.of(t = term3());
+ while (token.kind == AMP) {
+ checkIntersectionTypesInCast();
+ accept(AMP);
+ targets = targets.prepend(term3());
+ }
+ if (targets.length() > 1) {
+ t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
+ }
+ accept(RPAREN);
+ mode = EXPR;
+ JCExpression t1 = term3();
+ return F.at(pos).TypeCast(t, t1);
+ case IMPLICIT_LAMBDA:
+ case EXPLICIT_LAMBDA:
+ t = lambdaExpressionOrStatement(true, pres == ParensResult.EXPLICIT_LAMBDA, pos);
+ break;
+ default: //PARENS
+ accept(LPAREN);
mode = EXPR;
- JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
- if (token.kind == ELLIPSIS) {
- mods.flags = Flags.VARARGS;
- t = to(F.at(token.pos).TypeArray(t));
- nextToken();
- }
- t = lambdaExpressionOrStatement(variableDeclaratorId(mods, t), pos);
+ t = termRest(term1Rest(term2Rest(term3(), TreeInfo.orPrec)));
+ accept(RPAREN);
+ t = toP(F.at(pos).Parens(t));
break;
- } else {
- t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
- }
- }
-
- accept(RPAREN);
- lastmode = mode;
- mode = EXPR;
- if ((lastmode & EXPR) == 0) {
- JCExpression t1 = term3();
- return F.at(pos).TypeCast(t, t1);
- } else if ((lastmode & TYPE) != 0) {
- switch (token.kind) {
- /*case PLUSPLUS: case SUBSUB: */
- case BANG: case TILDE:
- case LPAREN: case THIS: case SUPER:
- case INTLITERAL: case LONGLITERAL: case FLOATLITERAL:
- case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL:
- case TRUE: case FALSE: case NULL:
- case NEW: case IDENTIFIER: case ASSERT: case ENUM:
- case BYTE: case SHORT: case CHAR: case INT:
- case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
- JCExpression t1 = term3();
- return F.at(pos).TypeCast(t, t1);
- }
}
} else {
return illegal();
}
- t = toP(F.at(pos).Parens(t));
break;
case THIS:
if ((mode & EXPR) != 0) {
@@ -1346,6 +1307,138 @@
}
}
+ /**
+ * If we see an identifier followed by a '<' it could be an unbound
+ * method reference or a binary expression. To disambiguate, look for a
+ * matching '>' and see if the subsequent terminal is either '.' or '#'.
+ */
+ @SuppressWarnings("fallthrough")
+ ParensResult analyzeParens() {
+ int depth = 0;
+ boolean type = false;
+ for (int lookahead = 0 ; ; lookahead++) {
+ TokenKind tk = S.token(lookahead).kind;
+ switch (tk) {
+ case EXTENDS: case SUPER: case COMMA:
+ type = true;
+ case QUES: case DOT: case AMP:
+ //skip
+ break;
+ case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+ case DOUBLE: case BOOLEAN: case CHAR:
+ if (peekToken(lookahead, RPAREN)) {
+ //Type, ')' -> cast
+ return ParensResult.CAST;
+ } else if (peekToken(lookahead, IDENTIFIER)) {
+ //Type, 'Identifier -> explicit lambda
+ return ParensResult.EXPLICIT_LAMBDA;
+ }
+ break;
+ case LPAREN:
+ if (lookahead != 0) {
+ // '(' in a non-starting position -> parens
+ return ParensResult.PARENS;
+ } else if (peekToken(lookahead, RPAREN)) {
+ // '(', ')' -> explicit lambda
+ return ParensResult.EXPLICIT_LAMBDA;
+ }
+ break;
+ case RPAREN:
+ // if we have seen something that looks like a type,
+ // then it's a cast expression
+ if (type) return ParensResult.CAST;
+ // otherwise, disambiguate cast vs. parenthesized expression
+ // based on subsequent token.
+ switch (S.token(lookahead + 1).kind) {
+ /*case PLUSPLUS: case SUBSUB: */
+ case BANG: case TILDE:
+ case LPAREN: case THIS: case SUPER:
+ case INTLITERAL: case LONGLITERAL: case FLOATLITERAL:
+ case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL:
+ case TRUE: case FALSE: case NULL:
+ case NEW: case IDENTIFIER: case ASSERT: case ENUM:
+ case BYTE: case SHORT: case CHAR: case INT:
+ case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
+ return ParensResult.CAST;
+ default:
+ return ParensResult.PARENS;
+ }
+ case IDENTIFIER:
+ if (peekToken(lookahead, IDENTIFIER)) {
+ // Identifier, Identifier -> explicit lambda
+ return ParensResult.EXPLICIT_LAMBDA;
+ } else if (peekToken(lookahead, RPAREN, ARROW)) {
+ // Identifier, ')' '->' -> implicit lambda
+ return ParensResult.IMPLICIT_LAMBDA;
+ }
+ break;
+ case FINAL:
+ case ELLIPSIS:
+ case MONKEYS_AT:
+ //those can only appear in explicit lambdas
+ return ParensResult.EXPLICIT_LAMBDA;
+ case LBRACKET:
+ if (peekToken(lookahead, RBRACKET, IDENTIFIER)) {
+ // '[', ']', Identifier -> explicit lambda
+ return ParensResult.EXPLICIT_LAMBDA;
+ } else if (peekToken(lookahead, RBRACKET, RPAREN) ||
+ peekToken(lookahead, RBRACKET, AMP)) {
+ // '[', ']', ')' -> cast
+ // '[', ']', '&' -> cast (intersection type)
+ return ParensResult.CAST;
+ } else if (peekToken(lookahead, RBRACKET)) {
+ //consume the ']' and skip
+ type = true;
+ lookahead++;
+ break;
+ } else {
+ return ParensResult.PARENS;
+ }
+ case LT:
+ depth++; break;
+ case GTGTGT:
+ depth--;
+ case GTGT:
+ depth--;
+ case GT:
+ depth--;
+ if (depth == 0) {
+ if (peekToken(lookahead, RPAREN) ||
+ peekToken(lookahead, AMP)) {
+ // '>', ')' -> cast
+ // '>', '&' -> cast
+ return ParensResult.CAST;
+ } else if (peekToken(lookahead, IDENTIFIER, COMMA) ||
+ peekToken(lookahead, IDENTIFIER, RPAREN, ARROW) ||
+ peekToken(lookahead, ELLIPSIS)) {
+ // '>', Identifier, ',' -> explicit lambda
+ // '>', Identifier, ')', '->' -> explicit lambda
+ // '>', '...' -> explicit lambda
+ return ParensResult.EXPLICIT_LAMBDA;
+ }
+ //it looks a type, but could still be (i) a cast to generic type,
+ //(ii) an unbound method reference or (iii) an explicit lambda
+ type = true;
+ break;
+ } else if (depth < 0) {
+ //unbalanced '<', '>' - not a generic type
+ return ParensResult.PARENS;
+ }
+ break;
+ default:
+ //this includes EOF
+ return ParensResult.PARENS;
+ }
+ }
+ }
+
+ enum ParensResult {
+ CAST,
+ EXPLICIT_LAMBDA,
+ IMPLICIT_LAMBDA,
+ PARENS;
+ }
+
JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
params.append(firstParam);
@@ -3386,6 +3479,12 @@
allowDefaultMethods = true;
}
}
+ void checkIntersectionTypesInCast() {
+ if (!allowIntersectionTypesInCast) {
+ log.error(token.pos, "intersection.types.in.cast.not.supported.in.source", source.name);
+ allowIntersectionTypesInCast = true;
+ }
+ }
/*
* a functional source tree and end position mappings