changeset 1696:950e8ac120f0

8010923: Avoid redundant speculative attribution Summary: Add optimization to avoid speculative attribution for certain argument expressions Reviewed-by: jjg
author mcimadamore
date Mon, 15 Apr 2013 14:18:30 +0100
parents c2315af9cc28
children 49d32c84dfea
files src/share/classes/com/sun/tools/javac/comp/Attr.java src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java src/share/classes/com/sun/tools/javac/comp/Resolve.java src/share/classes/com/sun/tools/javac/tree/TreeInfo.java
diffstat 4 files changed, 225 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Apr 15 14:17:30 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Apr 15 14:18:30 2013 +0100
@@ -148,6 +148,7 @@
         varInfo = new ResultInfo(VAR, Type.noType);
         unknownExprInfo = new ResultInfo(VAL, Type.noType);
         unknownTypeInfo = new ResultInfo(TYP, Type.noType);
+        unknownTypeExprInfo = new ResultInfo(Kinds.TYP | Kinds.VAL, Type.noType);
         recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
     }
 
@@ -559,6 +560,7 @@
     final ResultInfo varInfo;
     final ResultInfo unknownExprInfo;
     final ResultInfo unknownTypeInfo;
+    final ResultInfo unknownTypeExprInfo;
     final ResultInfo recoveryInfo;
 
     Type pt() {
@@ -667,7 +669,7 @@
     List<Type> attribArgs(List<JCExpression> trees, Env<AttrContext> env) {
         ListBuffer<Type> argtypes = new ListBuffer<Type>();
         for (JCExpression arg : trees) {
-            Type argtype = allowPoly && TreeInfo.isPoly(arg, env.tree) ?
+            Type argtype = allowPoly && deferredAttr.isDeferred(env, arg) ?
                     deferredAttr.new DeferredType(arg, env) :
                     chk.checkNonVoid(arg, attribExpr(arg, env, Infer.anyPoly));
             argtypes.append(argtype);
@@ -2989,7 +2991,8 @@
         Env<AttrContext> localEnv = env.dup(tree);
         //should we propagate the target type?
         final ResultInfo castInfo;
-        final boolean isPoly = TreeInfo.isPoly(tree.expr, tree);
+        JCExpression expr = TreeInfo.skipParens(tree.expr);
+        boolean isPoly = expr.hasTag(LAMBDA) || expr.hasTag(REFERENCE);
         if (isPoly) {
             //expression is a poly - we need to propagate target type info
             castInfo = new ResultInfo(VAL, clazztype, new Check.NestedCheckContext(resultInfo.checkContext) {
--- a/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Mon Apr 15 14:17:30 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Mon Apr 15 14:18:30 2013 +0100
@@ -800,4 +800,219 @@
             }
         }
     }
+
+    /**
+     * Does the argument expression {@code expr} need speculative type-checking?
+     */
+    boolean isDeferred(Env<AttrContext> env, JCExpression expr) {
+        DeferredChecker dc = new DeferredChecker(env);
+        dc.scan(expr);
+        return dc.result.isPoly();
+    }
+
+    /**
+     * The kind of an argument expression. This is used by the analysis that
+     * determines as to whether speculative attribution is necessary.
+     */
+    enum ArgumentExpressionKind {
+
+        /** kind that denotes poly argument expression */
+        POLY,
+        /** kind that denotes a standalone expression */
+        NO_POLY,
+        /** kind that denotes a primitive/boxed standalone expression */
+        PRIMITIVE;
+
+        /**
+         * Does this kind denote a poly argument expression
+         */
+        public final boolean isPoly() {
+            return this == POLY;
+        }
+
+        /**
+         * Does this kind denote a primitive standalone expression
+         */
+        public final boolean isPrimitive() {
+            return this == PRIMITIVE;
+        }
+
+        /**
+         * Compute the kind of a standalone expression of a given type
+         */
+        static ArgumentExpressionKind standaloneKind(Type type, Types types) {
+            return types.unboxedTypeOrType(type).isPrimitive() ?
+                    ArgumentExpressionKind.PRIMITIVE :
+                    ArgumentExpressionKind.NO_POLY;
+        }
+
+        /**
+         * Compute the kind of a method argument expression given its symbol
+         */
+        static ArgumentExpressionKind methodKind(Symbol sym, Types types) {
+            Type restype = sym.type.getReturnType();
+            if (sym.type.hasTag(FORALL) &&
+                    restype.containsAny(((ForAll)sym.type).tvars)) {
+                return ArgumentExpressionKind.POLY;
+            } else {
+                return ArgumentExpressionKind.standaloneKind(restype, types);
+            }
+        }
+    }
+
+    /**
+     * Tree scanner used for checking as to whether an argument expression
+     * requires speculative attribution
+     */
+    final class DeferredChecker extends FilterScanner {
+
+        Env<AttrContext> env;
+        ArgumentExpressionKind result;
+
+        public DeferredChecker(Env<AttrContext> env) {
+            super(deferredCheckerTags);
+            this.env = env;
+        }
+
+        @Override
+        public void visitLambda(JCLambda tree) {
+            //a lambda is always a poly expression
+            result = ArgumentExpressionKind.POLY;
+        }
+
+        @Override
+        public void visitReference(JCMemberReference tree) {
+            //a method reference is always a poly expression
+            result = ArgumentExpressionKind.POLY;
+        }
+
+        @Override
+        public void visitTypeCast(JCTypeCast tree) {
+            //a cast is always a standalone expression
+            result = ArgumentExpressionKind.NO_POLY;
+        }
+
+        @Override
+        public void visitConditional(JCConditional tree) {
+            scan(tree.truepart);
+            if (!result.isPrimitive()) {
+                result = ArgumentExpressionKind.POLY;
+                return;
+            }
+            scan(tree.falsepart);
+            result = reduce(ArgumentExpressionKind.PRIMITIVE);
+        }
+
+        @Override
+        public void visitNewClass(JCNewClass tree) {
+            result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ?
+                    ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY;
+        }
+
+        @Override
+        public void visitApply(JCMethodInvocation tree) {
+            Name name = TreeInfo.name(tree.meth);
+
+            //fast path
+            if (tree.typeargs.nonEmpty() ||
+                    name == name.table.names._this ||
+                    name == name.table.names._super) {
+                result = ArgumentExpressionKind.NO_POLY;
+                return;
+            }
+
+            //slow path
+            final JCExpression rec = tree.meth.hasTag(SELECT) ?
+                    ((JCFieldAccess)tree.meth).selected :
+                    null;
+
+            if (rec != null && !isSimpleReceiver(rec)) {
+                //give up if receiver is too complex (to cut down analysis time)
+                result = ArgumentExpressionKind.POLY;
+                return;
+            }
+
+            Type site = rec != null ?
+                    attribSpeculative(rec, env, attr.unknownTypeExprInfo).type :
+                    env.enclClass.sym.type;
+
+            ListBuffer<Type> args = ListBuffer.lb();
+            for (int i = 0; i < tree.args.length(); i ++) {
+                args.append(Type.noType);
+            }
+
+            Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args.toList(), List.<Type>nil(), MethodResolutionPhase.VARARITY) {
+                @Override
+                Symbol lookup(Env<AttrContext> env, MethodResolutionPhase phase) {
+                    return rec == null ?
+                        rs.findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) :
+                        rs.findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired(), false);
+                }
+                @Override
+                Symbol access(Env<AttrContext> env, DiagnosticPosition pos, Symbol location, Symbol sym) {
+                    return sym;
+                }
+            };
+
+            Symbol sym = rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh);
+
+            if (sym.kind == Kinds.AMBIGUOUS) {
+                Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol();
+                result = ArgumentExpressionKind.PRIMITIVE;
+                for (List<Symbol> ambigousSyms = err.ambiguousSyms ;
+                        ambigousSyms.nonEmpty() && !result.isPoly() ;
+                        ambigousSyms = ambigousSyms.tail) {
+                    Symbol s = ambigousSyms.head;
+                    if (s.kind == Kinds.MTH) {
+                        result = reduce(ArgumentExpressionKind.methodKind(s, types));
+                    }
+                }
+            } else {
+                result = (sym.kind == Kinds.MTH) ?
+                    ArgumentExpressionKind.methodKind(sym, types) :
+                    ArgumentExpressionKind.NO_POLY;
+            }
+        }
+        //where
+            private boolean isSimpleReceiver(JCTree rec) {
+                switch (rec.getTag()) {
+                    case IDENT:
+                        return true;
+                    case SELECT:
+                        return isSimpleReceiver(((JCFieldAccess)rec).selected);
+                    case TYPEAPPLY:
+                    case TYPEARRAY:
+                        return true;
+                    case ANNOTATED_TYPE:
+                        return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType);
+                    default:
+                        return false;
+                }
+            }
+            private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) {
+                switch (result) {
+                    case PRIMITIVE: return kind;
+                    case NO_POLY: return kind.isPoly() ? kind : result;
+                    case POLY: return result;
+                    default:
+                        Assert.error();
+                        return null;
+                }
+            }
+
+        @Override
+        public void visitLiteral(JCLiteral tree) {
+            Type litType = attr.litType(tree.typetag);
+            result = ArgumentExpressionKind.standaloneKind(litType, types);
+        }
+
+        @Override
+        void skip(JCTree tree) {
+            result = ArgumentExpressionKind.NO_POLY;
+        }
+    }
+    //where
+    private EnumSet<JCTree.Tag> deferredCheckerTags =
+            EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST,
+                    CONDEXPR, NEWCLASS, APPLY, LITERAL);
 }
--- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Mon Apr 15 14:17:30 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Mon Apr 15 14:18:30 2013 +0100
@@ -3604,6 +3604,11 @@
         }
 
         @Override
+        public Symbol baseSymbol() {
+            return delegatedError.baseSymbol();
+        }
+
+        @Override
         protected Symbol access(Name name, TypeSymbol location) {
             return delegatedError.access(name, location);
         }
--- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Mon Apr 15 14:17:30 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Mon Apr 15 14:18:30 2013 +0100
@@ -249,23 +249,6 @@
         }
     }
 
-    /** Return true if a a tree corresponds to a poly expression. */
-    public static boolean isPoly(JCTree tree, JCTree origin) {
-        switch (tree.getTag()) {
-            case APPLY:
-            case NEWCLASS:
-            case CONDEXPR:
-                return !origin.hasTag(TYPECAST);
-            case LAMBDA:
-            case REFERENCE:
-                return true;
-            case PARENS:
-                return isPoly(((JCParens)tree).expr, origin);
-            default:
-                return false;
-        }
-    }
-
     /** set 'polyKind' on given tree */
     public static void setPolyKind(JCTree tree, PolyKind pkind) {
         switch (tree.getTag()) {