< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java

Print this page

        

@@ -32,10 +32,11 @@
 import com.sun.source.tree.CaseTree.CaseKind;
 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
 import com.sun.source.tree.ModuleTree.ModuleKind;
 
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Flags.Flag;
 import com.sun.tools.javac.code.Source.Feature;
 import com.sun.tools.javac.parser.Tokens.*;
 import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.resources.CompilerProperties.Fragments;

@@ -46,18 +47,20 @@
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
 import com.sun.tools.javac.util.JCDiagnostic.Error;
 import com.sun.tools.javac.util.JCDiagnostic.Fragment;
 import com.sun.tools.javac.util.List;
 
+import static com.sun.tools.javac.code.Flags.asFlagSet;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.*;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.ASSERT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.CASE;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.CATCH;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.EQ;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.GT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.LT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.SYNCHRONIZED;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
 

@@ -182,10 +185,11 @@
         this.keepLineMap = keepLineMap;
         this.errorTree = F.Erroneous();
         endPosTable = newEndPosTable(keepEndPositions);
         this.allowYieldStatement = (!preview.isPreview(Feature.SWITCH_EXPRESSION) || preview.isEnabled()) &&
                 Feature.SWITCH_EXPRESSION.allowedInSource(source);
+        this.allowWithFieldOperator = fac.options.isSet("allowWithFieldOperator");
     }
 
     protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
         return  keepEndPositions
                 ? new SimpleEndPosTable(this)

@@ -198,10 +202,14 @@
 
     /** Switch: should we fold strings?
      */
     boolean allowStringFolding;
 
+    /** Switch: should we allow withField operator at source level ?
+    */
+    boolean allowWithFieldOperator;
+
     /** Switch: should we keep docComments?
      */
     boolean keepDocComments;
 
     /** Switch: should we keep line table?

@@ -225,17 +233,19 @@
      *     mode = EXPR        : an expression
      *     mode = TYPE        : a type
      *     mode = NOPARAMS    : no parameters allowed for type
      *     mode = TYPEARG     : type argument
      *     mode |= NOLAMBDA   : lambdas are not allowed
+     *     mode |= NOQUESTION   : type terminal ? is not allowed
      */
     protected static final int EXPR = 0x1;
     protected static final int TYPE = 0x2;
     protected static final int NOPARAMS = 0x4;
     protected static final int TYPEARG = 0x8;
     protected static final int DIAMOND = 0x10;
     protected static final int NOLAMBDA = 0x20;
+    protected static final int NOQUESTION = 0x40;
 
     protected void selectExprMode() {
         mode = (mode & NOLAMBDA) | EXPR;
     }
 

@@ -289,10 +299,17 @@
         return tk1.accepts(S.token(lookahead + 1).kind) &&
                 tk2.accepts(S.token(lookahead + 2).kind) &&
                 tk3.accepts(S.token(lookahead + 3).kind);
     }
 
+    protected boolean peekToken(int lookahead, Filter<TokenKind> tk1, Filter<TokenKind> tk2, Filter<TokenKind> tk3, Filter<TokenKind> tk4) {
+        return tk1.accepts(S.token(lookahead + 1).kind) &&
+                tk2.accepts(S.token(lookahead + 2).kind) &&
+                tk3.accepts(S.token(lookahead + 3).kind) &&
+                tk4.accepts(S.token(lookahead + 4).kind);
+    }
+
     @SuppressWarnings("unchecked")
     protected boolean peekToken(Filter<TokenKind>... kinds) {
         return peekToken(0, kinds);
     }
 

@@ -597,11 +614,11 @@
             return names.error;
         }
     }
 
     /**
-     * Qualident = Ident { DOT [Annotations] Ident }
+     * Qualident = Ident { DOT [Annotations] Ident } {?}
      */
     public JCExpression qualident(boolean allowAnnos) {
         JCExpression t = toP(F.at(token.pos).Ident(ident()));
         while (token.kind == DOT) {
             int pos = token.pos;

@@ -613,10 +630,17 @@
             t = toP(F.at(pos).Select(t, ident()));
             if (tyannos != null && tyannos.nonEmpty()) {
                 t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
             }
         }
+        /* if the qualified identifier being parsed is for a type name (as indicated by allowAnnos),
+           also process any terminal ? to signal nullable projection for a value type.
+        */
+        if (allowAnnos && token.kind == QUES) {
+            t.setQuestioned();
+            nextToken();
+        }
         return t;
     }
 
     JCExpression literal(Name prefix) {
         return literal(prefix, token.pos);

@@ -756,27 +780,39 @@
      */
     public JCExpression parseType() {
         return parseType(false);
     }
 
+    public JCExpression parseTypeSansQuestion() {
+        List<JCAnnotation> annotations = typeAnnotationsOpt();
+        boolean questionOK = peekToken(0, QUES) && peekToken(1, LBRACKET)  && peekToken(2, RBRACKET);
+        JCExpression result = unannotatedType(false, TYPE | (questionOK ? 0 : NOQUESTION));
+        mode &= ~NOQUESTION;
+        if (annotations.nonEmpty()) {
+            result = insertAnnotationsToMostInner(result, annotations, false);
+        }
+
+        return result;
+    }
+
     public JCExpression parseType(boolean allowVar) {
         List<JCAnnotation> annotations = typeAnnotationsOpt();
         return parseType(allowVar, annotations);
     }
 
     public JCExpression parseType(boolean allowVar, List<JCAnnotation> annotations) {
-        JCExpression result = unannotatedType(allowVar);
+        JCExpression result = unannotatedType(allowVar, TYPE);
 
         if (annotations.nonEmpty()) {
             result = insertAnnotationsToMostInner(result, annotations, false);
         }
 
         return result;
     }
 
-    public JCExpression unannotatedType(boolean allowVar) {
-        JCExpression result = term(TYPE);
+    public JCExpression unannotatedType(boolean allowVar, int termMode) {
+        JCExpression result = term(termMode);
         Name restrictedTypeName;
 
         if (!allowVar && (restrictedTypeName = restrictedTypeName(result, true)) != null) {
             syntaxError(result.pos, Errors.RestrictedTypeNotAllowedHere(restrictedTypeName));
         }

@@ -916,11 +952,11 @@
         while (prec(token.kind) >= minprec) {
             opStack[top] = topOp;
             top++;
             topOp = token;
             nextToken();
-            odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3();
+            odStack[top] = (topOp.kind == INSTANCEOF) ? parseTypeSansQuestion() : term3();
             while (top > 0 && prec(topOp.kind) >= prec(token.kind)) {
                 odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1],
                                         odStack[top]);
                 top--;
                 topOp = opStack[top];

@@ -1102,10 +1138,25 @@
                     t = term3();
                     return F.at(pos).Unary(unoptag(tk), t);
                 }
             } else return illegal();
             break;
+        case WITHFIELD:
+            if (!allowWithFieldOperator) {
+                log.error(pos, Errors.WithFieldOperatorDisallowed);
+            }
+            if (typeArgs == null && (mode & EXPR) != 0) {
+                nextToken();
+                accept(LPAREN);
+                mode = EXPR;
+                t = term();
+                accept(COMMA);
+                mode = EXPR;
+                JCExpression v = term();
+                accept(RPAREN);
+                return F.at(pos).WithField(t, v);
+            } else return illegal();
         case LPAREN:
             if (typeArgs == null && (mode & EXPR) != 0) {
                 ParensResult pres = analyzeParens();
                 switch (pres) {
                     case CAST:

@@ -1221,10 +1272,11 @@
             if (typeArgs != null) return illegal();
             if ((mode & EXPR) != 0 && (mode & NOLAMBDA) == 0 && peekToken(ARROW)) {
                 t = lambdaExpressionOrStatement(false, false, pos);
             } else {
                 t = toP(F.at(token.pos).Ident(ident()));
+                handleQuestion(t);
                 loop: while (true) {
                     pos = token.pos;
                     final List<JCAnnotation> annos = typeAnnotationsOpt();
 
                     // need to report an error later if LBRACKET is for array

@@ -1270,10 +1322,16 @@
                         mode &= ~NOPARAMS;
                         typeArgs = typeArgumentsOpt(EXPR);
                         mode = oldmode;
                         if ((mode & EXPR) != 0) {
                             switch (token.kind) {
+                            case DEFAULT:
+                                if (typeArgs != null) return illegal();
+                                selectExprMode();
+                                t = F.at(pos).Select(t, names._default);
+                                nextToken();
+                                break loop;
                             case CLASS:
                                 if (typeArgs != null) return illegal();
                                 selectExprMode();
                                 t = to(F.at(pos).Select(t, names._class));
                                 nextToken();

@@ -1306,10 +1364,11 @@
                         if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
                             tyannos = typeAnnotationsOpt();
                         }
                         // typeArgs saved for next loop iteration.
                         t = toP(F.at(pos).Select(t, ident()));
+                        handleQuestion(t);
                         if (tyannos != null && tyannos.nonEmpty()) {
                             t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
                         }
                         break;
                     case ELLIPSIS:

@@ -1319,13 +1378,14 @@
                             // Don't return here -- error recovery attempt
                             illegal(annos.head.pos);
                         }
                         break loop;
                     case LT:
-                        if ((mode & TYPE) == 0 && isUnboundMemberRef()) {
-                            //this is an unbound method reference whose qualifier
-                            //is a generic type i.e. A<S>::m
+                        if ((mode & TYPE) == 0 && isParameterizedTypePrefix()) {
+                            //this is either an unbound method reference whose qualifier
+                            //is a generic type i.e. A<S>::m or a default value creation of
+                            //the form ValueType<S>.default
                             int pos1 = token.pos;
                             accept(LT);
                             ListBuffer<JCExpression> args = new ListBuffer<>();
                             args.append(typeArgument());
                             while (token.kind == COMMA) {

@@ -1334,10 +1394,16 @@
                             }
                             accept(GT);
                             t = toP(F.at(pos1).TypeApply(t, args.toList()));
                             while (token.kind == DOT) {
                                 nextToken();
+                                if (token.kind == DEFAULT) {
+                                    t =  toP(F.at(token.pos).Select(t, names._default));
+                                    nextToken();
+                                    selectExprMode();
+                                    return term3Rest(t, typeArgs);
+                                }
                                 selectTypeMode();
                                 t = toP(F.at(token.pos).Select(t, ident()));
                                 t = typeArgumentsOpt(t);
                             }
                             t = bracketsOpt(t);

@@ -1412,10 +1478,21 @@
             return illegal();
         }
         return term3Rest(t, typeArgs);
     }
 
+    // where
+        private void handleQuestion(JCExpression t) {
+            if (token.kind == QUES) {
+                if (((mode & NOQUESTION) == 0 && (mode & TYPE) != 0) ||
+                        (peekToken(0, LBRACKET) && peekToken(1, RBRACKET) && peekToken(2, DOT) && peekToken(3, CLASS))) {
+                    t.setQuestioned();
+                    nextToken();
+                }
+            }
+        }
+
     private List<JCCase> switchExpressionStatementGroup() {
         ListBuffer<JCCase> caseExprs = new ListBuffer<>();
         int casePos = token.pos;
         ListBuffer<JCExpression> pats = new ListBuffer<>();
 

@@ -1499,11 +1576,11 @@
                     selectExprMode();
                     t = to(F.at(pos1).Select(t, names._super));
                     nextToken();
                     t = arguments(typeArgs, t);
                     typeArgs = null;
-                } else if (token.kind == NEW && (mode & EXPR) != 0) {
+                } else if ((token.kind == NEW) && (mode & EXPR) != 0) {
                     if (typeArgs != null) return illegal();
                     selectExprMode();
                     int pos2 = token.pos;
                     nextToken();
                     if (token.kind == LT) typeArgs = typeArguments(false);

@@ -1546,15 +1623,16 @@
         return toP(t);
     }
 
     /**
      * If we see an identifier followed by a '&lt;' it could be an unbound
-     * method reference or a binary expression. To disambiguate, look for a
+     * method reference or a default value creation that uses a parameterized type
+     * or a binary expression. To disambiguate, look for a
      * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
      */
     @SuppressWarnings("fallthrough")
-    boolean isUnboundMemberRef() {
+    boolean isParameterizedTypePrefix() {
         int pos = 0, depth = 0;
         outer: for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
             switch (t.kind) {
                 case IDENTIFIER: case UNDERSCORE: case QUES: case EXTENDS: case SUPER:
                 case DOT: case RBRACKET: case LBRACKET: case COMMA:

@@ -1672,12 +1750,12 @@
                     }
                 case UNDERSCORE:
                 case ASSERT:
                 case ENUM:
                 case IDENTIFIER:
-                    if (peekToken(lookahead, LAX_IDENTIFIER)) {
-                        // Identifier, Identifier/'_'/'assert'/'enum' -> explicit lambda
+                    if (peekToken(lookahead, LAX_IDENTIFIER) || (peekToken(lookahead, QUES, LAX_IDENTIFIER) && (peekToken(lookahead + 2, RPAREN) || peekToken(lookahead + 2, COMMA)))) {
+                        // Identifier[?], Identifier/'_'/'assert'/'enum' -> explicit lambda
                         return ParensResult.EXPLICIT_LAMBDA;
                     } else if (peekToken(lookahead, RPAREN, ARROW)) {
                         // Identifier, ')' '->' -> implicit lambda
                         return (mode & NOLAMBDA) == 0 ? ParensResult.IMPLICIT_LAMBDA
                                                       : ParensResult.PARENS;

@@ -1749,10 +1827,12 @@
                             // '>', ')' -> cast
                             // '>', '&' -> cast
                             return ParensResult.CAST;
                         } else if (peekToken(lookahead, LAX_IDENTIFIER, COMMA) ||
                                 peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW) ||
+                                peekToken(lookahead, QUES, LAX_IDENTIFIER, COMMA) ||
+                                peekToken(lookahead, QUES, LAX_IDENTIFIER, RPAREN, ARROW) ||
                                 peekToken(lookahead, ELLIPSIS)) {
                             // '>', Identifier/'_'/'assert'/'enum', ',' -> explicit lambda
                             // '>', Identifier/'_'/'assert'/'enum', ')', '->' -> explicit lambda
                             // '>', '...' -> explicit lambda
                             return ParensResult.EXPLICIT_LAMBDA;

@@ -2081,17 +2161,20 @@
             result = toP(F.at(pos).Wildcard(t, null));
         }
         if (!annotations.isEmpty()) {
             result = toP(F.at(annotations.head.pos).AnnotatedType(annotations,result));
         }
+        handleQuestion(result);
         return result;
     }
 
     JCTypeApply typeArguments(JCExpression t, boolean diamondAllowed) {
         int pos = token.pos;
         List<JCExpression> args = typeArguments(diamondAllowed);
-        return toP(F.at(pos).TypeApply(t, args));
+        JCTypeApply ta = toP(F.at(pos).TypeApply(t, args));
+        handleQuestion(ta);
+        return ta;
     }
 
     /**
      * BracketsOpt = { [Annotations] "[" "]" }*
      *

@@ -2208,15 +2291,22 @@
     }
 
     /** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
      */
     JCExpression creator(int newpos, List<JCExpression> typeArgs) {
-        List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
-
+        final JCModifiers mods = modifiersOpt();
+        List<JCAnnotation> newAnnotations = mods.annotations;
+        if (!newAnnotations.isEmpty()) {
+            checkSourceLevel(newAnnotations.head.pos, Feature.TYPE_ANNOTATIONS);
+        }
         switch (token.kind) {
         case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
         case DOUBLE: case BOOLEAN:
+            if (mods.flags != 0) {
+                long badModifiers = (mods.flags & Flags.VALUE) != 0 ? mods.flags & ~Flags.FINAL : mods.flags;
+                log.error(token.pos, Errors.ModNotAllowedHere(asFlagSet(badModifiers)));
+            }
             if (typeArgs == null) {
                 if (newAnnotations.isEmpty()) {
                     return arrayCreatorRest(newpos, basicType());
                 } else {
                     return arrayCreatorRest(newpos, toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, basicType())));

@@ -2281,15 +2371,23 @@
                 reportSyntaxError(err, Errors.CannotCreateArrayWithTypeArguments);
                 return toP(err);
             }
             return e;
         } else if (token.kind == LPAREN) {
+            long badModifiers = mods.flags & ~(Flags.VALUE | Flags.FINAL);
+            if (badModifiers != 0)
+                log.error(token.pos, Errors.ModNotAllowedHere(asFlagSet(badModifiers)));
             // handle type annotations for instantiations and anonymous classes
             if (newAnnotations.nonEmpty()) {
                 t = insertAnnotationsToMostInner(t, newAnnotations, false);
             }
-            return classCreatorRest(newpos, null, typeArgs, t);
+            JCNewClass newClass = classCreatorRest(newpos, null, typeArgs, t, mods.flags);
+            if ((newClass.def == null) && (mods.flags != 0)) {
+                badModifiers = (mods.flags & Flags.VALUE) != 0 ? mods.flags & ~Flags.FINAL : mods.flags;
+                log.error(newClass.pos, Errors.ModNotAllowedHere(asFlagSet(badModifiers)));
+            }
+            return newClass;
         } else {
             setErrorEndPos(token.pos);
             reportSyntaxError(token.pos, Errors.Expected2(LPAREN, LBRACKET));
             t = toP(F.at(newpos).NewClass(null, typeArgs, t, List.nil(), null));
             return toP(F.at(newpos).Erroneous(List.<JCTree>of(t)));

@@ -2310,11 +2408,11 @@
         if (token.kind == LT) {
             int oldmode = mode;
             t = typeArguments(t, true);
             mode = oldmode;
         }
-        return classCreatorRest(newpos, encl, typeArgs, t);
+        return classCreatorRest(newpos, encl, typeArgs, t, 0);
     }
 
     /** ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer
      *                         | Expression "]" {[Annotations]  "[" Expression "]"} BracketsOpt )
      */

@@ -2392,21 +2490,23 @@
     /** ClassCreatorRest = Arguments [ClassBody]
      */
     JCNewClass classCreatorRest(int newpos,
                                   JCExpression encl,
                                   List<JCExpression> typeArgs,
-                                  JCExpression t)
+                                  JCExpression t,
+                                  long flags)
     {
         List<JCExpression> args = arguments();
         JCClassDecl body = null;
         if (token.kind == LBRACE) {
             int pos = token.pos;
             List<JCTree> defs = classOrInterfaceBody(names.empty, false);
-            JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+            JCModifiers mods = F.at(Position.NOPOS).Modifiers(flags);
             body = toP(F.at(pos).AnonymousClassDef(mods, defs));
         }
-        return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
+        JCNewClass newClass = toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
+        return newClass;
     }
 
     /** ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}] [","] "}"
      */
     JCExpression arrayInitializer(int newpos, JCExpression t) {

@@ -2533,19 +2633,21 @@
     /**This method parses a statement appearing inside a block.
      */
     @SuppressWarnings("fallthrough")
     List<JCStatement> blockStatement() {
         //todo: skip to anchor on error(?)
+        token = recastToken(token);
         int pos = token.pos;
         switch (token.kind) {
         case RBRACE: case CASE: case DEFAULT: case EOF:
             return List.nil();
         case LBRACE: case IF: case FOR: case WHILE: case DO: case TRY:
         case SWITCH: case SYNCHRONIZED: case RETURN: case THROW: case BREAK:
         case CONTINUE: case SEMI: case ELSE: case FINALLY: case CATCH:
         case ASSERT:
             return List.of(parseSimpleStatement());
+        case VALUE:
         case MONKEYS_AT:
         case FINAL: {
             Comment dc = token.comment(CommentStyle.JAVADOC);
             JCModifiers mods = modifiersOpt();
             if (token.kind == INTERFACE ||

@@ -2975,11 +3077,14 @@
         if (token.kind == FINAL || token.kind == MONKEYS_AT) {
             return variableDeclarators(optFinal(0), parseType(true), stats, true).toList();
         } else {
             JCExpression t = term(EXPR | TYPE);
             if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.accepts(token.kind)) {
-                return variableDeclarators(modifiersOpt(), t, stats, true).toList();
+                pos = token.pos;
+                JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+                F.at(pos);
+                return variableDeclarators(mods, t, stats, true).toList();
             } else if ((lastmode & TYPE) != 0 && token.kind == COLON) {
                 log.error(DiagnosticFlag.SYNTAX, pos, Errors.BadInitializer("for-loop"));
                 return List.of((JCStatement)F.at(pos).VarDef(modifiersOpt(), names.error, t, null));
             } else {
                 return moreStatementExpressions(pos, t, stats).toList();

@@ -3045,19 +3150,21 @@
         }
         int lastPos;
     loop:
         while (true) {
             long flag;
+            token = recastToken(token);
             switch (token.kind) {
             case PRIVATE     : flag = Flags.PRIVATE; break;
             case PROTECTED   : flag = Flags.PROTECTED; break;
             case PUBLIC      : flag = Flags.PUBLIC; break;
             case STATIC      : flag = Flags.STATIC; break;
             case TRANSIENT   : flag = Flags.TRANSIENT; break;
             case FINAL       : flag = Flags.FINAL; break;
             case ABSTRACT    : flag = Flags.ABSTRACT; break;
             case NATIVE      : flag = Flags.NATIVE; break;
+            case VALUE       : flag = Flags.VALUE; break;
             case VOLATILE    : flag = Flags.VOLATILE; break;
             case SYNCHRONIZED: flag = Flags.SYNCHRONIZED; break;
             case STRICTFP    : flag = Flags.STRICTFP; break;
             case MONKEYS_AT  : flag = Flags.ANNOTATION; break;
             case DEFAULT     : checkSourceLevel(Feature.DEFAULT_METHODS); flag = Flags.DEFAULT; break;

@@ -3071,12 +3178,17 @@
                 if (token.kind != INTERFACE) {
                     JCAnnotation ann = annotation(lastPos, Tag.ANNOTATION);
                     // if first modifier is an annotation, set pos to annotation's.
                     if (flags == 0 && annotations.isEmpty())
                         pos = ann.pos;
-                    annotations.append(ann);
-                    flag = 0;
+                    final Name name = TreeInfo.name(ann.annotationType);
+                    if (name == names.__inline__ || name == names.java_lang___inline__) {
+                        flag = Flags.VALUE;
+                    } else {
+                        annotations.append(ann);
+                        flag = 0;
+                    }
                 }
             }
             flags |= flag;
         }
         switch (token.kind) {

@@ -3088,10 +3200,15 @@
         /* A modifiers tree with no modifier tokens or annotations
          * has no text position. */
         if ((flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0 && annotations.isEmpty())
             pos = Position.NOPOS;
 
+        // Force value classes to be automatically final.
+        if ((flags & (Flags.VALUE | Flags.ABSTRACT | Flags.INTERFACE | Flags.ENUM)) == Flags.VALUE) {
+            flags |= Flags.FINAL;
+        }
+
         JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList());
         if (pos != Position.NOPOS)
             storeEnd(mods, S.prevToken().endPos);
         return mods;
     }

@@ -3275,10 +3392,46 @@
         attach(result, dc);
         result.startPos = startPos;
         return result;
     }
 
+    // Does the given token signal an inline modifier ? If yes, suitably reclassify token.
+    Token recastToken(Token token) {
+        if (token.kind != IDENTIFIER || token.name() != names.inline) {
+            return token;
+        }
+        if (peekToken(t->t == PRIVATE ||
+                         t == PROTECTED ||
+                         t == PUBLIC ||
+                         t == STATIC ||
+                         t == TRANSIENT ||
+                         t == FINAL ||
+                         t == ABSTRACT ||
+                         t == NATIVE ||
+                         t == VOLATILE ||
+                         t == SYNCHRONIZED ||
+                         t == STRICTFP ||
+                         t == MONKEYS_AT ||
+                         t == DEFAULT ||
+                         t == BYTE ||
+                         t == SHORT ||
+                         t == CHAR ||
+                         t == INT ||
+                         t == LONG ||
+                         t == FLOAT ||
+                         t == DOUBLE ||
+                         t == BOOLEAN ||
+                         t == CLASS ||
+                         t == INTERFACE ||
+                         t == ENUM ||
+                         t == IDENTIFIER)) { // new value Comparable() {}
+            checkSourceLevel(Feature.INLINE_TYPES);
+            return new Token(VALUE, token.pos, token.endPos, token.comments);
+        }
+        return token;
+    }
+
     Name restrictedTypeName(JCExpression e, boolean shouldWarn) {
         switch (e.getTag()) {
             case IDENT:
                 return isRestrictedTypeName(((JCIdent)e).name, e.pos, shouldWarn) ? ((JCIdent)e).name : null;
             case TYPEARRAY:

@@ -3928,11 +4081,11 @@
                 if (isVoid) {
                     type = to(F.at(pos).TypeIdent(TypeTag.VOID));
                     nextToken();
                 } else {
                     // method returns types are un-annotated types
-                    type = unannotatedType(false);
+                    type = unannotatedType(false, TYPE);
                 }
                 if (token.kind == LPAREN && !isInterface && type.hasTag(IDENT)) {
                     if (isInterface || tk.name() != className)
                         log.error(DiagnosticFlag.SYNTAX, pos, Errors.InvalidMethDeclRetTypeReq);
                     else if (annosAfterParams.nonEmpty())
< prev index next >