8161708: javac, consider a different way to handle access code for operators
authorvromero
Mon, 01 Aug 2016 08:36:02 -0700
changeset 39920 4923274643f2
parent 39919 3d3573afc062
child 39921 69e23f5892a7
8161708: javac, consider a different way to handle access code for operators Reviewed-by: mcimadamore
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Operators.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Mon Aug 01 08:36:02 2016 -0700
@@ -55,7 +55,9 @@
 import com.sun.tools.javac.comp.AttrContext;
 import com.sun.tools.javac.comp.Env;
 import com.sun.tools.javac.jvm.*;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.DefinedBy.Api;
 import com.sun.tools.javac.util.Name;
@@ -64,9 +66,15 @@
 import static com.sun.tools.javac.code.Kinds.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
+import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.FIRSTASGOP;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.code.TypeTag.FORALL;
 import static com.sun.tools.javac.code.TypeTag.TYPEVAR;
+import static com.sun.tools.javac.jvm.ByteCodes.iadd;
+import static com.sun.tools.javac.jvm.ByteCodes.ishll;
+import static com.sun.tools.javac.jvm.ByteCodes.lushrl;
+import static com.sun.tools.javac.jvm.ByteCodes.lxor;
+import static com.sun.tools.javac.jvm.ByteCodes.string_add;
 
 /** Root class for Java symbols. It contains subclasses
  *  for specific sorts of symbols, such as variables, methods and operators,
@@ -1950,15 +1958,90 @@
     public static class OperatorSymbol extends MethodSymbol {
 
         public int opcode;
+        private int accessCode = Integer.MIN_VALUE;
 
         public OperatorSymbol(Name name, Type type, int opcode, Symbol owner) {
             super(PUBLIC | STATIC, name, type, owner);
             this.opcode = opcode;
         }
 
+        @Override
         public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
             return v.visitOperatorSymbol(this, p);
         }
+
+        public int getAccessCode(Tag tag) {
+            if (accessCode != Integer.MIN_VALUE && !tag.isIncOrDecUnaryOp()) {
+                return accessCode;
+            }
+            accessCode = AccessCode.from(tag, opcode);
+            return accessCode;
+        }
+
+        /** Access codes for dereferencing, assignment,
+         *  and pre/post increment/decrement.
+
+         *  All access codes for accesses to the current class are even.
+         *  If a member of the superclass should be accessed instead (because
+         *  access was via a qualified super), add one to the corresponding code
+         *  for the current class, making the number odd.
+         *  This numbering scheme is used by the backend to decide whether
+         *  to issue an invokevirtual or invokespecial call.
+         *
+         *  @see Gen#visitSelect(JCFieldAccess tree)
+         */
+        public enum AccessCode {
+            UNKNOWN(-1, Tag.NO_TAG),
+            DEREF(0, Tag.NO_TAG),
+            ASSIGN(2, Tag.ASSIGN),
+            PREINC(4, Tag.PREINC),
+            PREDEC(6, Tag.PREDEC),
+            POSTINC(8, Tag.POSTINC),
+            POSTDEC(10, Tag.POSTDEC),
+            FIRSTASGOP(12, Tag.NO_TAG);
+
+            public final int code;
+            public final Tag tag;
+            public static final int numberOfAccessCodes = (lushrl - ishll + lxor + 2 - iadd) * 2 + FIRSTASGOP.code + 2;
+
+            AccessCode(int code, Tag tag) {
+                this.code = code;
+                this.tag = tag;
+            }
+
+            static public AccessCode getFromCode(int code) {
+                for (AccessCode aCodes : AccessCode.values()) {
+                    if (aCodes.code == code) {
+                        return aCodes;
+                    }
+                }
+                return UNKNOWN;
+            }
+
+            static int from(Tag tag, int opcode) {
+                /** Map bytecode of binary operation to access code of corresponding
+                *  assignment operation. This is always an even number.
+                */
+                switch (tag) {
+                    case PREINC:
+                        return AccessCode.PREINC.code;
+                    case PREDEC:
+                        return AccessCode.PREDEC.code;
+                    case POSTINC:
+                        return AccessCode.POSTINC.code;
+                    case POSTDEC:
+                        return AccessCode.POSTDEC.code;
+                }
+                if (iadd <= opcode && opcode <= lxor) {
+                    return (opcode - iadd) * 2 + FIRSTASGOP.code;
+                } else if (opcode == string_add) {
+                    return (lxor + 1 - iadd) * 2 + FIRSTASGOP.code;
+                } else if (ishll <= opcode && opcode <= lushrl) {
+                    return (opcode - ishll + lxor + 2 - iadd) * 2 + FIRSTASGOP.code;
+                }
+                return -1;
+            }
+        }
     }
 
     /** Symbol completer interface.
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Aug 01 08:36:02 2016 -0700
@@ -3159,7 +3159,7 @@
         Type operand = attribExpr(tree.rhs, env);
         // Find operator.
         Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag().noAssignOp(), owntype, operand);
-        if (operator.kind == MTH &&
+        if (operator != operators.noOpSymbol &&
                 !owntype.isErroneous() &&
                 !operand.isErroneous()) {
             chk.checkDivZero(tree.rhs.pos(), operator, operand);
@@ -3179,7 +3179,7 @@
         // Find operator.
         Symbol operator = tree.operator = operators.resolveUnary(tree, tree.getTag(), argtype);
         Type owntype = types.createErrorType(tree.type);
-        if (operator.kind == MTH &&
+        if (operator != operators.noOpSymbol &&
                 !argtype.isErroneous()) {
             owntype = (tree.getTag().isIncOrDecUnaryOp())
                 ? tree.arg.type
@@ -3204,7 +3204,7 @@
         // Find operator.
         Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right);
         Type owntype = types.createErrorType(tree.type);
-        if (operator.kind == MTH &&
+        if (operator != operators.noOpSymbol &&
                 !left.isErroneous() &&
                 !right.isErroneous()) {
             owntype = operator.type.getReturnType();
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Mon Aug 01 08:36:02 2016 -0700
@@ -38,6 +38,7 @@
 import com.sun.tools.javac.util.List;
 
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode;
 import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.code.Type.*;
 
@@ -49,6 +50,7 @@
 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
+import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.DEREF;
 import static com.sun.tools.javac.jvm.ByteCodes.*;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 
@@ -816,33 +818,6 @@
  * Access methods
  *************************************************************************/
 
-    /** Access codes for dereferencing, assignment,
-     *  and pre/post increment/decrement.
-     *  Access codes for assignment operations are determined by method accessCode
-     *  below.
-     *
-     *  All access codes for accesses to the current class are even.
-     *  If a member of the superclass should be accessed instead (because
-     *  access was via a qualified super), add one to the corresponding code
-     *  for the current class, making the number odd.
-     *  This numbering scheme is used by the backend to decide whether
-     *  to issue an invokevirtual or invokespecial call.
-     *
-     *  @see Gen#visitSelect(JCFieldAccess tree)
-     */
-    private static final int
-        DEREFcode = 0,
-        ASSIGNcode = 2,
-        PREINCcode = 4,
-        PREDECcode = 6,
-        POSTINCcode = 8,
-        POSTDECcode = 10,
-        FIRSTASGOPcode = 12;
-
-    /** Number of access codes
-     */
-    private static final int NCODES = accessCode(ByteCodes.lushrl) + 2;
-
     /** A mapping from symbols to their access numbers.
      */
     private Map<Symbol,Integer> accessNums;
@@ -864,20 +839,6 @@
      */
     private ListBuffer<Symbol> accessed;
 
-    /** Map bytecode of binary operation to access code of corresponding
-     *  assignment operation. This is always an even number.
-     */
-    private static int accessCode(int bytecode) {
-        if (ByteCodes.iadd <= bytecode && bytecode <= ByteCodes.lxor)
-            return (bytecode - iadd) * 2 + FIRSTASGOPcode;
-        else if (bytecode == ByteCodes.string_add)
-            return (ByteCodes.lxor + 1 - iadd) * 2 + FIRSTASGOPcode;
-        else if (ByteCodes.ishll <= bytecode && bytecode <= ByteCodes.lushrl)
-            return (bytecode - ishll + ByteCodes.lxor + 2 - iadd) * 2 + FIRSTASGOPcode;
-        else
-            return -1;
-    }
-
     /** return access code for identifier,
      *  @param tree     The tree representing the identifier use.
      *  @param enclOp   The closest enclosing operation node of tree,
@@ -885,24 +846,24 @@
      */
     private static int accessCode(JCTree tree, JCTree enclOp) {
         if (enclOp == null)
-            return DEREFcode;
+            return AccessCode.DEREF.code;
         else if (enclOp.hasTag(ASSIGN) &&
                  tree == TreeInfo.skipParens(((JCAssign) enclOp).lhs))
-            return ASSIGNcode;
+            return AccessCode.ASSIGN.code;
         else if (enclOp.getTag().isIncOrDecUnaryOp() &&
                  tree == TreeInfo.skipParens(((JCUnary) enclOp).arg))
-            return mapTagToUnaryOpCode(enclOp.getTag());
+            return (((JCUnary) enclOp).operator).getAccessCode(enclOp.getTag());
         else if (enclOp.getTag().isAssignop() &&
                  tree == TreeInfo.skipParens(((JCAssignOp) enclOp).lhs))
-            return accessCode(((OperatorSymbol) ((JCAssignOp) enclOp).operator).opcode);
+            return (((JCAssignOp) enclOp).operator).getAccessCode(enclOp.getTag());
         else
-            return DEREFcode;
+            return AccessCode.DEREF.code;
     }
 
     /** Return binary operator that corresponds to given access code.
      */
-    private OperatorSymbol binaryAccessOperator(int acode) {
-        return (OperatorSymbol)operators.lookupBinaryOp(sym -> accessCode(((OperatorSymbol)sym).opcode) == acode);
+    private OperatorSymbol binaryAccessOperator(int acode, Tag tag) {
+        return operators.lookupBinaryOp(op -> op.getAccessCode(tag) == acode);
     }
 
     /** Return tree tag for assignment operation corresponding
@@ -984,7 +945,7 @@
         if (anum == null) {
             anum = accessed.length();
             accessNums.put(vsym, anum);
-            accessSyms.put(vsym, new MethodSymbol[NCODES]);
+            accessSyms.put(vsym, new MethodSymbol[AccessCode.numberOfAccessCodes]);
             accessed.append(vsym);
             // System.out.println("accessing " + vsym + " in " + vsym.location());
         }
@@ -996,13 +957,13 @@
         switch (vsym.kind) {
         case VAR:
             acode = accessCode(tree, enclOp);
-            if (acode >= FIRSTASGOPcode) {
-                OperatorSymbol operator = binaryAccessOperator(acode);
+            if (acode >= AccessCode.FIRSTASGOP.code) {
+                OperatorSymbol operator = binaryAccessOperator(acode, enclOp.getTag());
                 if (operator.opcode == string_add)
                     argtypes = List.of(syms.objectType);
                 else
                     argtypes = operator.type.getParameterTypes().tail;
-            } else if (acode == ASSIGNcode)
+            } else if (acode == AccessCode.ASSIGN.code)
                 argtypes = List.of(vsym.erasure(types));
             else
                 argtypes = List.nil();
@@ -1010,7 +971,7 @@
             thrown = List.nil();
             break;
         case MTH:
-            acode = DEREFcode;
+            acode = AccessCode.DEREF.code;
             argtypes = vsym.erasure(types).getParameterTypes();
             restype = vsym.erasure(types).getReturnType();
             thrown = vsym.type.getThrownTypes();
@@ -1306,7 +1267,7 @@
                 accessConstructorDef(cdef.pos, sym, accessConstrs.get(sym)));
         } else {
             MethodSymbol[] accessors = accessSyms.get(sym);
-            for (int i = 0; i < NCODES; i++) {
+            for (int i = 0; i < AccessCode.numberOfAccessCodes; i++) {
                 if (accessors[i] != null)
                     cdef.defs = cdef.defs.prepend(
                         accessDef(cdef.pos, sym, accessors[i], i));
@@ -1314,42 +1275,6 @@
         }
     }
 
-    /** Maps unary operator integer codes to JCTree.Tag objects
-     *  @param unaryOpCode the unary operator code
-     */
-    private static Tag mapUnaryOpCodeToTag(int unaryOpCode){
-        switch (unaryOpCode){
-            case PREINCcode:
-                return PREINC;
-            case PREDECcode:
-                return PREDEC;
-            case POSTINCcode:
-                return POSTINC;
-            case POSTDECcode:
-                return POSTDEC;
-            default:
-                return NO_TAG;
-        }
-    }
-
-    /** Maps JCTree.Tag objects to unary operator integer codes
-     *  @param tag the JCTree.Tag
-     */
-    private static int mapTagToUnaryOpCode(Tag tag){
-        switch (tag){
-            case PREINC:
-                return PREINCcode;
-            case PREDEC:
-                return PREDECcode;
-            case POSTINC:
-                return POSTINCcode;
-            case POSTDEC:
-                return POSTDECcode;
-            default:
-                return -1;
-        }
-    }
-
     /** Construct definition of an access method.
      *  @param pos        The source code position of the definition.
      *  @param vsym       The private or protected symbol.
@@ -1388,20 +1313,21 @@
             int acode1 = acode - (acode & 1);
 
             JCExpression expr;      // The access method's return value.
-            switch (acode1) {
-            case DEREFcode:
+            AccessCode aCode = AccessCode.getFromCode(acode1);
+            switch (aCode) {
+            case DEREF:
                 expr = ref;
                 break;
-            case ASSIGNcode:
+            case ASSIGN:
                 expr = make.Assign(ref, args.head);
                 break;
-            case PREINCcode: case POSTINCcode: case PREDECcode: case POSTDECcode:
-                expr = makeUnary(mapUnaryOpCodeToTag(acode1), ref);
+            case PREINC: case POSTINC: case PREDEC: case POSTDEC:
+                expr = makeUnary(aCode.tag, ref);
                 break;
             default:
                 expr = make.Assignop(
-                    treeTag(binaryAccessOperator(acode1)), ref, args.head);
-                ((JCAssignOp) expr).operator = binaryAccessOperator(acode1);
+                    treeTag(binaryAccessOperator(acode1, JCTree.Tag.NO_TAG)), ref, args.head);
+                ((JCAssignOp) expr).operator = binaryAccessOperator(acode1, JCTree.Tag.NO_TAG);
             }
             stat = make.Return(expr.setType(sym.type));
         } else {
@@ -3275,7 +3201,7 @@
                         // tree.lhs.  However, we can still get the
                         // unerased type of tree.lhs as it is stored
                         // in tree.type in Attr.
-                        Symbol newOperator = operators.resolveBinary(tree,
+                        OperatorSymbol newOperator = operators.resolveBinary(tree,
                                                                       newTag,
                                                                       tree.type,
                                                                       tree.rhs.type);
@@ -3304,7 +3230,7 @@
             JCMethodInvocation app = (JCMethodInvocation)tree.lhs;
             // if operation is a += on strings,
             // make sure to convert argument to string
-            JCExpression rhs = (((OperatorSymbol)tree.operator).opcode == string_add)
+            JCExpression rhs = tree.operator.opcode == string_add
               ? makeString(tree.rhs)
               : tree.rhs;
             app.args = List.of(rhs).prependList(app.args);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Operators.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Operators.java	Mon Aug 01 08:36:02 2016 -0700
@@ -95,6 +95,7 @@
         names = Names.instance(context);
         log = Log.instance(context);
         types = Types.instance(context);
+        noOpSymbol = new OperatorSymbol(names.empty, Type.noType, -1, syms.noSymbol);
         initOperatorNames();
         initUnaryOperators();
         initBinaryOperators();
@@ -145,7 +146,7 @@
     /**
      * Entry point for resolving a unary operator given an operator tag and an argument type.
      */
-    Symbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) {
+    OperatorSymbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) {
         return resolve(tag,
                 unaryOperators,
                 unop -> unop.test(op),
@@ -156,7 +157,7 @@
     /**
      * Entry point for resolving a binary operator given an operator tag and a pair of argument types.
      */
-    Symbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) {
+    OperatorSymbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) {
         return resolve(tag,
                 binaryOperators,
                 binop -> binop.test(op1, op2),
@@ -169,8 +170,8 @@
      * map. If there's a matching operator, its resolve routine is called and the result is returned;
      * otherwise the result of a fallback function is returned.
      */
-    private <O> Symbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc,
-                       Function<O, Symbol> resolveFunc, Supplier<Symbol> noResultFunc) {
+    private <O> OperatorSymbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc,
+                       Function<O, OperatorSymbol> resolveFunc, Supplier<OperatorSymbol> noResultFunc) {
         return opMap.get(operatorName(tag)).stream()
                 .filter(opTestFunc)
                 .map(resolveFunc)
@@ -181,7 +182,7 @@
     /**
      * Creates an operator symbol.
      */
-    private Symbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) {
+    private OperatorSymbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) {
         MethodType opType = new MethodType(
                 formals.stream()
                         .map(o -> o.asType(syms))
@@ -201,10 +202,14 @@
                 ((opcodes[0] << ByteCodes.preShift) | opcodes[1]);
     }
 
+    /** A symbol that stands for a missing operator.
+     */
+    public final OperatorSymbol noOpSymbol;
+
     /**
      * Report an operator lookup error.
      */
-    private Symbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) {
+    private OperatorSymbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) {
         if (Stream.of(args).noneMatch(Type::isErroneous)) {
             Name opName = operatorName(tag);
             JCDiagnostic.Error opError = (args.length) == 1 ?
@@ -212,7 +217,7 @@
                     Errors.OperatorCantBeApplied1(opName, args[0], args[1]);
             log.error(pos, opError);
         }
-        return syms.noSymbol;
+        return noOpSymbol;
     }
 
     /**
@@ -263,10 +268,10 @@
         final Name name;
 
         /** The list of symbols associated with this operator (lazily populated). */
-        Optional<Symbol[]> alternatives = Optional.empty();
+        Optional<OperatorSymbol[]> alternatives = Optional.empty();
 
         /** An array of operator symbol suppliers (used to lazily populate the symbol list). */
-        List<Supplier<Symbol>> operatorSuppliers = List.nil();
+        List<Supplier<OperatorSymbol>> operatorSuppliers = List.nil();
 
         @SuppressWarnings("varargs")
         OperatorHelper(Tag tag) {
@@ -278,21 +283,21 @@
          * using an applicability predicate; if the test suceeds that same operator is returned,
          * otherwise a dummy symbol is returned.
          */
-        final Symbol doLookup(Predicate<Symbol> applicabilityTest) {
+        final OperatorSymbol doLookup(Predicate<OperatorSymbol> applicabilityTest) {
             return Stream.of(alternatives.orElseGet(this::initOperators))
                     .filter(applicabilityTest)
                     .findFirst()
-                    .orElse(syms.noSymbol);
+                    .orElse(noOpSymbol);
         }
 
         /**
          * This routine performs lazy instantiation of the operator symbols supported by this helper.
          * After initialization is done, the suppliers are cleared, to free up memory.
          */
-        private Symbol[] initOperators() {
-            Symbol[] operators = operatorSuppliers.stream()
+        private OperatorSymbol[] initOperators() {
+            OperatorSymbol[] operators = operatorSuppliers.stream()
                     .map(op -> op.get())
-                    .toArray(Symbol[]::new);
+                    .toArray(OperatorSymbol[]::new);
             alternatives = Optional.of(operators);
             operatorSuppliers = null; //let GC do its work
             return operators;
@@ -311,10 +316,10 @@
         /**
          * This routine implements the unary operator lookup process. It customizes the behavior
          * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
-         * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorSymbol, Type)}
+         * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorOperatorSymbol, Type)}
          */
-        final Symbol doLookup(Type t) {
-            return doLookup(op -> isUnaryOperatorApplicable((OperatorSymbol)op, t));
+        final OperatorSymbol doLookup(Type t) {
+            return doLookup(op -> isUnaryOperatorApplicable(op, t));
         }
 
         /**
@@ -336,7 +341,7 @@
          * This method will be overridden by unary operator helpers to provide custom resolution
          * logic.
          */
-        abstract Symbol resolve(Type t);
+        abstract OperatorSymbol resolve(Type t);
     }
 
     abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> {
@@ -350,8 +355,8 @@
          * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
          * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)}
          */
-        final Symbol doLookup(Type t1, Type t2) {
-            return doLookup(op -> isBinaryOperatorApplicable((OperatorSymbol)op, t1, t2));
+        final OperatorSymbol doLookup(Type t1, Type t2) {
+            return doLookup(op -> isBinaryOperatorApplicable(op, t1, t2));
         }
 
         /**
@@ -375,7 +380,7 @@
          * This method will be overridden by binary operator helpers to provide custom resolution
          * logic.
          */
-        abstract Symbol resolve(Type t1, Type t2);
+        abstract OperatorSymbol resolve(Type t1, Type t2);
     }
 
     /**
@@ -393,7 +398,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg) {
+        public OperatorSymbol resolve(Type arg) {
             return doLookup(syms.objectType);
         }
     }
@@ -421,7 +426,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg) {
+        public OperatorSymbol resolve(Type arg) {
             return doLookup(unaryPromotion(arg));
         }
     }
@@ -442,7 +447,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg) {
+        public OperatorSymbol resolve(Type arg) {
             return doLookup(syms.booleanType);
         }
     }
@@ -458,7 +463,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg) {
+        public OperatorSymbol resolve(Type arg) {
             return doLookup(types.unboxedTypeOrType(arg));
         }
     }
@@ -481,7 +486,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg1, Type arg2) {
+        public OperatorSymbol resolve(Type arg1, Type arg2) {
             Type t = binaryPromotion(arg1, arg2);
             return doLookup(t, t);
         }
@@ -504,7 +509,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg1, Type arg2) {
+        public OperatorSymbol resolve(Type arg1, Type arg2) {
             return doLookup(syms.booleanType, syms.booleanType);
         }
 
@@ -527,7 +532,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg1, Type arg2) {
+        public OperatorSymbol resolve(Type arg1, Type arg2) {
             return doLookup(stringPromotion(arg1), stringPromotion(arg2));
         }
 
@@ -570,7 +575,7 @@
         }
 
         @Override
-        public Symbol resolve(Type arg1, Type arg2) {
+        public OperatorSymbol resolve(Type arg1, Type arg2) {
             return doLookup(unaryPromotion(arg1), unaryPromotion(arg2));
         }
 
@@ -613,7 +618,7 @@
         }
 
         @Override
-        public Symbol resolve(Type t1, Type t2) {
+        public OperatorSymbol resolve(Type t1, Type t2) {
             ComparisonKind kind = getKind(t1, t2);
             Type t = (kind == ComparisonKind.NUMERIC_OR_BOOLEAN) ?
                     binaryPromotion(t1, t2) :
@@ -798,12 +803,12 @@
                     .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_or));
     }
 
-    Symbol lookupBinaryOp(Predicate<Symbol> applicabilityTest) {
+    OperatorSymbol lookupBinaryOp(Predicate<OperatorSymbol> applicabilityTest) {
         return binaryOperators.values().stream()
                 .flatMap(List::stream)
                 .map(helper -> helper.doLookup(applicabilityTest))
                 .distinct()
-                .filter(sym -> sym != syms.noSymbol)
+                .filter(sym -> sym != noOpSymbol)
                 .findFirst().get();
     }
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Mon Aug 01 08:36:02 2016 -0700
@@ -1795,7 +1795,7 @@
     }
 
     public void visitAssignop(JCAssignOp tree) {
-        OperatorSymbol operator = (OperatorSymbol) tree.operator;
+        OperatorSymbol operator = tree.operator;
         Item l;
         if (operator.opcode == string_add) {
             l = concat.makeConcat(tree);
@@ -1827,7 +1827,7 @@
     }
 
     public void visitUnary(JCUnary tree) {
-        OperatorSymbol operator = (OperatorSymbol)tree.operator;
+        OperatorSymbol operator = tree.operator;
         if (tree.hasTag(NOT)) {
             CondItem od = genCond(tree.arg, false);
             result = od.negate();
@@ -1909,7 +1909,7 @@
     }
 
     public void visitBinary(JCBinary tree) {
-        OperatorSymbol operator = (OperatorSymbol)tree.operator;
+        OperatorSymbol operator = tree.operator;
         if (operator.opcode == string_add) {
             result = concat.makeConcat(tree);
         } else if (tree.hasTag(AND)) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java	Mon Aug 01 08:36:02 2016 -0700
@@ -131,8 +131,7 @@
         tree = TreeInfo.skipParens(tree);
         if (tree.hasTag(PLUS) && tree.type.constValue() == null) {
             JCTree.JCBinary op = (JCTree.JCBinary) tree;
-            if (op.operator.kind == MTH &&
-                    ((Symbol.OperatorSymbol) op.operator).opcode == string_add) {
+            if (op.operator.kind == MTH && op.operator.opcode == string_add) {
                 return res
                         .appendList(collect(op.lhs, res))
                         .appendList(collect(op.rhs, res));
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Jul 29 12:27:08 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Mon Aug 01 08:36:02 2016 -0700
@@ -1876,8 +1876,8 @@
         private Tag opcode;
         public JCExpression lhs;
         public JCExpression rhs;
-        public Symbol operator;
-        protected JCAssignOp(Tag opcode, JCTree lhs, JCTree rhs, Symbol operator) {
+        public OperatorSymbol operator;
+        protected JCAssignOp(Tag opcode, JCTree lhs, JCTree rhs, OperatorSymbol operator) {
             this.opcode = opcode;
             this.lhs = (JCExpression)lhs;
             this.rhs = (JCExpression)rhs;
@@ -1892,7 +1892,7 @@
         public JCExpression getVariable() { return lhs; }
         @DefinedBy(Api.COMPILER_TREE)
         public JCExpression getExpression() { return rhs; }
-        public Symbol getOperator() {
+        public OperatorSymbol getOperator() {
             return operator;
         }
         @Override @DefinedBy(Api.COMPILER_TREE)
@@ -1911,7 +1911,7 @@
     public static class JCUnary extends JCExpression implements UnaryTree {
         private Tag opcode;
         public JCExpression arg;
-        public Symbol operator;
+        public OperatorSymbol operator;
         protected JCUnary(Tag opcode, JCExpression arg) {
             this.opcode = opcode;
             this.arg = arg;
@@ -1923,7 +1923,7 @@
         public Kind getKind() { return TreeInfo.tagToKind(getTag()); }
         @DefinedBy(Api.COMPILER_TREE)
         public JCExpression getExpression() { return arg; }
-        public Symbol getOperator() {
+        public OperatorSymbol getOperator() {
             return operator;
         }
         @Override @DefinedBy(Api.COMPILER_TREE)
@@ -1947,11 +1947,11 @@
         private Tag opcode;
         public JCExpression lhs;
         public JCExpression rhs;
-        public Symbol operator;
+        public OperatorSymbol operator;
         protected JCBinary(Tag opcode,
                          JCExpression lhs,
                          JCExpression rhs,
-                         Symbol operator) {
+                         OperatorSymbol operator) {
             this.opcode = opcode;
             this.lhs = lhs;
             this.rhs = rhs;
@@ -1966,7 +1966,7 @@
         public JCExpression getLeftOperand() { return lhs; }
         @DefinedBy(Api.COMPILER_TREE)
         public JCExpression getRightOperand() { return rhs; }
-        public Symbol getOperator() {
+        public OperatorSymbol getOperator() {
             return operator;
         }
         @Override @DefinedBy(Api.COMPILER_TREE)