# HG changeset patch # User mcimadamore # Date 1442321024 -3600 # Node ID 286fc927040413d2ebeb5edd9f4d96a75fa1f456 # Parent 8fa8045bbd4e5a08dedaae25d4569f71837ca2ad 8078093: Severe compiler performance regression Java 7 to 8 for nested method invocations Summary: Add infrastructure to avoid combinatorial explosion of method argument attributions Reviewed-by: jlahoda, vromero, dlsmith diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Tue Sep 15 13:43:44 2015 +0100 @@ -3152,10 +3152,20 @@ throw new IllegalArgumentException("Not a method type: " + t); } public Type visitMethodType(MethodType t, Type newReturn) { - return new MethodType(t.argtypes, newReturn, t.thrown, t.tsym); + return new MethodType(t.argtypes, newReturn, t.thrown, t.tsym) { + @Override + public Type baseType() { + return t; + } + }; } public Type visitForAll(ForAll t, Type newReturn) { - return new ForAll(t.tvars, t.qtype.accept(this, newReturn)); + return new ForAll(t.tvars, t.qtype.accept(this, newReturn)) { + @Override + public Type baseType() { + return t; + } + }; } }; diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.comp; + +import com.sun.source.tree.LambdaExpressionTree.BodyKind; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError; +import com.sun.tools.javac.comp.Attr.ResultInfo; +import com.sun.tools.javac.comp.Attr.TargetInfo; +import com.sun.tools.javac.comp.Check.CheckContext; +import com.sun.tools.javac.comp.DeferredAttr.AttrMode; +import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext; +import com.sun.tools.javac.comp.DeferredAttr.DeferredType; +import com.sun.tools.javac.comp.DeferredAttr.DeferredTypeCompleter; +import com.sun.tools.javac.comp.DeferredAttr.LambdaReturnScanner; +import com.sun.tools.javac.comp.Infer.PartiallyInferredMethodType; +import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCConditional; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCLambda; +import com.sun.tools.javac.tree.JCTree.JCLambda.ParameterKind; +import com.sun.tools.javac.tree.JCTree.JCMemberReference; +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCParens; +import com.sun.tools.javac.tree.JCTree.JCReturn; +import com.sun.tools.javac.tree.TreeCopier; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.util.Assert; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.DiagnosticSource; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +import static com.sun.tools.javac.code.TypeTag.DEFERRED; +import static com.sun.tools.javac.code.TypeTag.FORALL; +import static com.sun.tools.javac.code.TypeTag.METHOD; +import static com.sun.tools.javac.code.TypeTag.VOID; + +/** + * This class performs attribution of method/constructor arguments when target-typing is enabled + * (source >= 8); for each argument that is potentially a poly expression, this class builds + * a rich representation (see {@link ArgumentType} which can then be used for performing fast overload + * checks without requiring multiple attribution passes over the same code. + * + * The attribution strategy for a given method/constructor argument A is as follows: + * + * - if A is potentially a poly expression (i.e. diamond instance creation expression), a speculative + * pass over A is performed; the results of such speculative attribution are then saved in a special + * type, so that enclosing overload resolution can be carried by simply checking compatibility against the + * type determined during this speculative pass. + * + * - if A is a standalone expression, regular atributtion takes place. + * + * To minimize the speculative work, a cache is used, so that already computed argument types + * associated with a given unique source location are never recomputed multiple times. + */ +public class ArgumentAttr extends JCTree.Visitor { + + protected static final Context.Key methodAttrKey = new Context.Key<>(); + + private final DeferredAttr deferredAttr; + private final Attr attr; + private final Symtab syms; + private final Log log; + + /** Attribution environment to be used. */ + private Env env; + + /** Result of method attribution. */ + private Type result; + + /** Cache for argument types; behavior is influences by the currrently selected cache policy. */ + Map> argumentTypeCache = new LinkedHashMap<>(); + + /** Cache policy: should argument types be cached? */ + private CachePolicy cachePolicy = CachePolicy.CACHE; + + public static ArgumentAttr instance(Context context) { + ArgumentAttr instance = context.get(methodAttrKey); + if (instance == null) + instance = new ArgumentAttr(context); + return instance; + } + + protected ArgumentAttr(Context context) { + context.put(methodAttrKey, this); + deferredAttr = DeferredAttr.instance(context); + attr = Attr.instance(context); + syms = Symtab.instance(context); + log = Log.instance(context); + } + + /** + * Set the results of method attribution. + */ + void setResult(JCExpression tree, Type type) { + result = type; + if (env.info.isSpeculative) { + //if we are in a speculative branch we can save the type in the tree itself + //as there's no risk of polluting the original tree. + tree.type = result; + } + } + + /** + * Checks a type in the speculative tree against a given result; the type can be either a plain + * type or an argument type,in which case a more complex check is required. + */ + Type checkSpeculative(JCExpression expr, ResultInfo resultInfo) { + return checkSpeculative(expr, expr.type, resultInfo); + } + + /** + * Checks a type in the speculative tree against a given result; the type can be either a plain + * type or an argument type,in which case a more complex check is required. + */ + Type checkSpeculative(DiagnosticPosition pos, Type t, ResultInfo resultInfo) { + if (t.hasTag(DEFERRED)) { + return ((DeferredType)t).check(resultInfo); + } else { + return resultInfo.check(pos, t); + } + } + + /** + * Sets given ache policy and returns current policy. + */ + CachePolicy withCachePolicy(CachePolicy newPolicy) { + CachePolicy oldPolicy = this.cachePolicy; + this.cachePolicy = newPolicy; + return oldPolicy; + } + + /** + * Main entry point for attributing an argument with given tree and attribution environment. + */ + Type attribArg(JCTree tree, Env env) { + Env prevEnv = this.env; + try { + this.env = env; + tree.accept(this); + return result; + } finally { + this.env = prevEnv; + } + } + + @Override + public void visitTree(JCTree that) { + //delegates to Attr + that.accept(attr); + result = attr.result; + } + + /** + * Process a method argument; this method takes care of performing a speculative pass over the + * argument tree and calling a well-defined entry point to build the argument type associated + * with such tree. + */ + @SuppressWarnings("unchecked") + > void processArg(T that, Function argumentTypeFactory) { + UniquePos pos = new UniquePos(that); + processArg(that, () -> { + T speculativeTree = (T)deferredAttr.attribSpeculative(that, env, attr.new MethodAttrInfo() { + @Override + protected void attr(JCTree tree, Env env) { + //avoid speculative attribution loops + if (!new UniquePos(tree).equals(pos)) { + super.attr(tree, env); + } else { + visitTree(tree); + } + } + }); + return argumentTypeFactory.apply(speculativeTree); + }); + } + + /** + * Process a method argument; this method allows the caller to specify a custom speculative attribution + * logic (this is used e.g. for lambdas). + */ + @SuppressWarnings("unchecked") + > void processArg(T that, Supplier argumentTypeFactory) { + UniquePos pos = new UniquePos(that); + Z cached = (Z)argumentTypeCache.get(pos); + if (cached != null) { + //dup existing speculative type + setResult(that, cached.dup(that, env)); + } else { + Z res = argumentTypeFactory.get(); + if (cachePolicy == CachePolicy.CACHE) { + argumentTypeCache.put(pos, res); + } + setResult(that, res); + } + } + + @Override + public void visitParens(JCParens that) { + processArg(that, speculativeTree -> new ParensType(that, env, speculativeTree)); + } + + @Override + public void visitConditional(JCConditional that) { + processArg(that, speculativeTree -> new ConditionalType(that, env, speculativeTree)); + } + + @Override + public void visitReference(JCMemberReference tree) { + //perform arity-based check + Env localEnv = env.dup(tree); + JCExpression exprTree = (JCExpression)deferredAttr.attribSpeculative(tree.getQualifierExpression(), localEnv, + attr.memberReferenceQualifierResult(tree)); + JCMemberReference mref2 = new TreeCopier(attr.make).copy(tree); + mref2.expr = exprTree; + Symbol res = + attr.rs.getMemberReference(tree, localEnv, mref2, + exprTree.type, tree.name); + if (!res.kind.isResolutionError()) { + tree.sym = res; + } + if (res.kind.isResolutionTargetError() || + res.type != null && res.type.hasTag(FORALL) || + (res.flags() & Flags.VARARGS) != 0 || + (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) && + exprTree.type.isRaw())) { + tree.overloadKind = JCMemberReference.OverloadKind.OVERLOADED; + } else { + tree.overloadKind = JCMemberReference.OverloadKind.UNOVERLOADED; + } + //return a plain old deferred type for this + setResult(tree, deferredAttr.new DeferredType(tree, env)); + } + + @Override + public void visitLambda(JCLambda that) { + if (that.paramKind == ParameterKind.EXPLICIT) { + //if lambda is explicit, we can save info in the corresponding argument type + processArg(that, () -> { + JCLambda speculativeLambda = + deferredAttr.attribSpeculativeLambda(that, env, attr.methodAttrInfo); + return new ExplicitLambdaType(that, env, speculativeLambda); + }); + } else { + //otherwise just use a deferred type + setResult(that, deferredAttr.new DeferredType(that, env)); + } + } + + @Override + public void visitApply(JCMethodInvocation that) { + if (that.getTypeArguments().isEmpty()) { + processArg(that, speculativeTree -> new ResolvedMethodType(that, env, speculativeTree)); + } else { + //not a poly expression, just call Attr + setResult(that, attr.attribTree(that, env, attr.unknownExprInfo)); + } + } + + @Override + public void visitNewClass(JCNewClass that) { + if (TreeInfo.isDiamond(that)) { + processArg(that, speculativeTree -> new ResolvedConstructorType(that, env, speculativeTree)); + } else { + //not a poly expression, just call Attr + setResult(that, attr.attribTree(that, env, attr.unknownExprInfo)); + } + } + + /** + * An argument type is similar to a plain deferred type; the most important difference is that + * the completion logic associated with argument types allows speculative attribution to be skipped + * during overload resolution - that is, an argument type always has enough information to + * perform an overload check without the need of calling back to Attr. This extra information + * is typically stored in the form of a speculative tree. + */ + abstract class ArgumentType extends DeferredType implements DeferredTypeCompleter { + + /** The speculative tree carrying type information. */ + T speculativeTree; + + /** Types associated with this argument (one type per possible target result). */ + Map speculativeTypes; + + public ArgumentType(JCExpression tree, Env env, T speculativeTree, Map speculativeTypes) { + deferredAttr.super(tree, env); + this.speculativeTree = speculativeTree; + this.speculativeTypes = speculativeTypes; + } + + @Override + final DeferredTypeCompleter completer() { + return this; + } + + @Override + final public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { + Assert.check(dt == this); + if (deferredAttrContext.mode == AttrMode.SPECULATIVE) { + Type t = (resultInfo.pt == Type.recoveryType) ? + deferredAttr.basicCompleter.complete(dt, resultInfo, deferredAttrContext) : + overloadCheck(resultInfo, deferredAttrContext); + speculativeTypes.put(resultInfo, t); + return t; + } else { + if (!env.info.isSpeculative && cachePolicy == CachePolicy.CACHE) { + argumentTypeCache.remove(new UniquePos(dt.tree)); + } + return deferredAttr.basicCompleter.complete(dt, resultInfo, deferredAttrContext); + } + } + + @Override + Type speculativeType(Symbol msym, MethodResolutionPhase phase) { + for (Map.Entry _entry : speculativeTypes.entrySet()) { + DeferredAttrContext deferredAttrContext = _entry.getKey().checkContext.deferredAttrContext(); + if (deferredAttrContext.phase == phase && deferredAttrContext.msym == msym) { + return _entry.getValue(); + } + } + return Type.noType; + } + + @Override + JCTree speculativeTree(DeferredAttrContext deferredAttrContext) { + return speculativeTree; + } + + /** + * Performs an overload check against a given target result. + */ + abstract Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext); + + /** + * Creates a copy of this argument type with given tree and environment. + */ + abstract ArgumentType dup(T tree, Env env); + } + + /** + * Argument type for parenthesized expression. + */ + class ParensType extends ArgumentType { + ParensType(JCExpression tree, Env env, JCParens speculativeParens) { + this(tree, env, speculativeParens, new HashMap<>()); + } + + ParensType(JCExpression tree, Env env, JCParens speculativeParens, Map speculativeTypes) { + super(tree, env, speculativeParens, speculativeTypes); + } + + @Override + Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { + return checkSpeculative(speculativeTree.expr, resultInfo); + } + + @Override + ArgumentType dup(JCParens tree, Env env) { + return new ParensType(tree, env, speculativeTree, speculativeTypes); + } + } + + /** + * Argument type for conditionals. + */ + class ConditionalType extends ArgumentType { + ConditionalType(JCExpression tree, Env env, JCConditional speculativeCond) { + this(tree, env, speculativeCond, new HashMap<>()); + } + + ConditionalType(JCExpression tree, Env env, JCConditional speculativeCond, Map speculativeTypes) { + super(tree, env, speculativeCond, speculativeTypes); + } + + @Override + Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { + ResultInfo localInfo = resultInfo.dup(attr.conditionalContext(resultInfo.checkContext)); + if (speculativeTree.isStandalone()) { + return localInfo.check(speculativeTree, speculativeTree.type); + } else if (resultInfo.pt.hasTag(VOID)) { + //this means we are returning a poly conditional from void-compatible lambda expression + resultInfo.checkContext.report(tree, attr.diags.fragment("conditional.target.cant.be.void")); + return attr.types.createErrorType(resultInfo.pt); + } else { + //poly + checkSpeculative(speculativeTree.truepart, localInfo); + checkSpeculative(speculativeTree.falsepart, localInfo); + return localInfo.pt; + } + } + + @Override + ArgumentType dup(JCConditional tree, Env env) { + return new ConditionalType(tree, env, speculativeTree, speculativeTypes); + } + } + + /** + * Argument type for explicit lambdas. + */ + class ExplicitLambdaType extends ArgumentType { + + /** List of argument types (lazily populated). */ + Optional> argtypes = Optional.empty(); + + /** List of return expressions (lazily populated). */ + Optional> returnExpressions = Optional.empty(); + + ExplicitLambdaType(JCLambda originalLambda, Env env, JCLambda speculativeLambda) { + this(originalLambda, env, speculativeLambda, new HashMap<>()); + } + + ExplicitLambdaType(JCLambda originalLambda, Env env, JCLambda speculativeLambda, Map speculativeTypes) { + super(originalLambda, env, speculativeLambda, speculativeTypes); + } + + /** Compute argument types (if needed). */ + List argtypes() { + return argtypes.orElseGet(() -> { + List res = TreeInfo.types(speculativeTree.params); + argtypes = Optional.of(res); + return res; + }); + } + + /** Compute return expressions (if needed). */ + List returnExpressions() { + return returnExpressions.orElseGet(() -> { + final List res; + if (speculativeTree.getBodyKind() == BodyKind.EXPRESSION) { + res = List.of(attr.make.Return((JCExpression)speculativeTree.body)); + } else { + ListBuffer returnExpressions = new ListBuffer<>(); + new LambdaReturnScanner() { + @Override + public void visitReturn(JCReturn tree) { + returnExpressions.add(tree); + } + }.scan(speculativeTree.body); + res = returnExpressions.toList(); + } + returnExpressions = Optional.of(res); + return res; + }); + } + + @Override + Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { + try { + //compute target-type; this logic could be shared with Attr + TargetInfo targetInfo = attr.getTargetInfo(speculativeTree, resultInfo, argtypes()); + Type lambdaType = targetInfo.descriptor; + Type currentTarget = targetInfo.target; + //check compatibility + checkLambdaCompatible(lambdaType, resultInfo); + return currentTarget; + } catch (FunctionDescriptorLookupError ex) { + resultInfo.checkContext.report(null, ex.getDiagnostic()); + return null; //cannot get here + } + } + + /** Check lambda against given target result */ + private void checkLambdaCompatible(Type descriptor, ResultInfo resultInfo) { + CheckContext checkContext = resultInfo.checkContext; + ResultInfo bodyResultInfo = attr.lambdaBodyResult(speculativeTree, descriptor, resultInfo); + for (JCReturn ret : returnExpressions()) { + Type t = getReturnType(ret); + if (speculativeTree.getBodyKind() == BodyKind.EXPRESSION || !t.hasTag(VOID)) { + checkSpeculative(ret.expr, t, bodyResultInfo); + } + } + + attr.checkLambdaCompatible(speculativeTree, descriptor, checkContext); + } + + /** Get the type associated with given return expression. */ + Type getReturnType(JCReturn ret) { + if (ret.expr == null) { + return syms.voidType; + } else { + return ret.expr.type; + } + } + + @Override + ArgumentType dup(JCLambda tree, Env env) { + return new ExplicitLambdaType(tree, env, speculativeTree, speculativeTypes); + } + } + + /** + * Argument type for methods/constructors. + */ + abstract class ResolvedMemberType extends ArgumentType { + + public ResolvedMemberType(JCExpression tree, Env env, E speculativeMethod, Map speculativeTypes) { + super(tree, env, speculativeMethod, speculativeTypes); + } + + @Override + Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { + Type mtype = methodType(); + ResultInfo localInfo = resultInfo(resultInfo); + if (mtype != null && mtype.hasTag(METHOD) && mtype.isPartial()) { + Type t = ((PartiallyInferredMethodType)mtype).check(localInfo); + if (!deferredAttrContext.inferenceContext.free(localInfo.pt)) { + speculativeTypes.put(localInfo, t); + return localInfo.check(tree.pos(), t); + } else { + return t; + } + } else { + Type t = localInfo.check(tree.pos(), speculativeTree.type); + speculativeTypes.put(localInfo, t); + return t; + } + } + + /** + * Get the result info to be used for performing an overload check. + */ + abstract ResultInfo resultInfo(ResultInfo resultInfo); + + /** + * Get the method type to be used for performing an overload check. + */ + abstract Type methodType(); + } + + /** + * Argument type for methods. + */ + class ResolvedMethodType extends ResolvedMemberType { + + public ResolvedMethodType(JCExpression tree, Env env, JCMethodInvocation speculativeTree) { + this(tree, env, speculativeTree, new HashMap<>()); + } + + public ResolvedMethodType(JCExpression tree, Env env, JCMethodInvocation speculativeTree, Map speculativeTypes) { + super(tree, env, speculativeTree, speculativeTypes); + } + + @Override + ResultInfo resultInfo(ResultInfo resultInfo) { + return resultInfo; + } + + @Override + Type methodType() { + return speculativeTree.meth.type; + } + + @Override + ArgumentType dup(JCMethodInvocation tree, Env env) { + return new ResolvedMethodType(tree, env, speculativeTree, speculativeTypes); + } + } + + /** + * Argument type for constructors. + */ + class ResolvedConstructorType extends ResolvedMemberType { + + public ResolvedConstructorType(JCExpression tree, Env env, JCNewClass speculativeTree) { + this(tree, env, speculativeTree, new HashMap<>()); + } + + public ResolvedConstructorType(JCExpression tree, Env env, JCNewClass speculativeTree, Map speculativeTypes) { + super(tree, env, speculativeTree, speculativeTypes); + } + + @Override + ResultInfo resultInfo(ResultInfo resultInfo) { + return resultInfo.dup(attr.diamondContext(speculativeTree, speculativeTree.clazz.type.tsym, resultInfo.checkContext)); + } + + @Override + Type methodType() { + return (speculativeTree.constructorType != null) ? + speculativeTree.constructorType.baseType() : syms.errType; + } + + @Override + ArgumentType dup(JCNewClass tree, Env env) { + return new ResolvedConstructorType(tree, env, speculativeTree, speculativeTypes); + } + } + + /** + * An instance of this class represents a unique position in a compilation unit. A unique + * position is made up of (i) a unique position in a source file (char offset) and (ii) + * a source file info. + */ + class UniquePos { + + /** Char offset. */ + int pos; + + /** Source info. */ + DiagnosticSource source; + + UniquePos(JCTree tree) { + this.pos = tree.pos; + this.source = log.currentSource(); + } + + @Override + public int hashCode() { + return pos << 16 + source.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UniquePos) { + UniquePos that = (UniquePos)obj; + return pos == that.pos && source == that.source; + } else { + return false; + } + } + + @Override + public String toString() { + return source.getFile().getName() + " @ " + source.getLineNumber(pos); + } + } + + /** + * Argument type caching policy. + */ + enum CachePolicy { + /** Cache argument types. */ + CACHE, + /** + * Don't cache argument types. This is useful when performing speculative attribution on + * a tree that is known to contain erroneous info. + */ + NO_CACHE; + } +} diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Tue Sep 15 13:43:44 2015 +0100 @@ -108,6 +108,7 @@ final TypeEnvs typeEnvs; final Dependencies dependencies; final Annotate annotate; + final ArgumentAttr argumentAttr; public static Attr instance(Context context) { Attr instance = context.get(attrKey); @@ -142,6 +143,7 @@ deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); + argumentAttr = ArgumentAttr.instance(context); Options options = Options.instance(context); @@ -160,7 +162,7 @@ statInfo = new ResultInfo(KindSelector.NIL, Type.noType); varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType); unknownExprInfo = new ResultInfo(KindSelector.VAL, Type.noType); - unknownAnyPolyInfo = new ResultInfo(KindSelector.VAL, Infer.anyPoly); + methodAttrInfo = new MethodAttrInfo(); unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType); unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType); recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext); @@ -488,6 +490,10 @@ this.checkMode = checkMode; } + protected void attr(JCTree tree, Env env) { + tree.accept(Attr.this); + } + protected Type check(final DiagnosticPosition pos, final Type found) { return chk.checkType(pos, found, pt, checkContext); } @@ -522,6 +528,41 @@ } } + class MethodAttrInfo extends ResultInfo { + public MethodAttrInfo() { + this(chk.basicHandler); + } + + public MethodAttrInfo(CheckContext checkContext) { + super(KindSelector.VAL, Infer.anyPoly, checkContext); + } + + @Override + protected void attr(JCTree tree, Env env) { + result = argumentAttr.attribArg(tree, env); + } + + protected ResultInfo dup(Type newPt) { + throw new IllegalStateException(); + } + + protected ResultInfo dup(CheckContext newContext) { + return new MethodAttrInfo(newContext); + } + + protected ResultInfo dup(Type newPt, CheckContext newContext) { + throw new IllegalStateException(); + } + + protected ResultInfo dup(Type newPt, CheckContext newContext, CheckMode newMode) { + throw new IllegalStateException(); + } + + protected ResultInfo dup(CheckMode newMode) { + throw new IllegalStateException(); + } + } + class RecoveryInfo extends ResultInfo { public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext) { @@ -545,7 +586,7 @@ final ResultInfo statInfo; final ResultInfo varAssignmentInfo; - final ResultInfo unknownAnyPolyInfo; + final ResultInfo methodAttrInfo; final ResultInfo unknownExprInfo; final ResultInfo unknownTypeInfo; final ResultInfo unknownTypeExprInfo; @@ -588,7 +629,7 @@ try { this.env = env; this.resultInfo = resultInfo; - tree.accept(this); + resultInfo.attr(tree, env); if (tree == breakTree && resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) { throw new BreakAttr(copyEnv(env)); @@ -684,12 +725,9 @@ KindSelector attribArgs(KindSelector initialKind, List trees, Env env, ListBuffer argtypes) { KindSelector kind = initialKind; for (JCExpression arg : trees) { - Type argtype; - if (allowPoly && deferredAttr.isDeferred(env, arg)) { - argtype = deferredAttr.new DeferredType(arg, env); + Type argtype = chk.checkNonVoid(arg, attribTree(arg, env, allowPoly ? methodAttrInfo : unknownExprInfo)); + if (argtype.hasTag(DEFERRED)) { kind = KindSelector.of(KindSelector.POLY, kind); - } else { - argtype = chk.checkNonVoid(arg, attribTree(arg, env, unknownAnyPolyInfo)); } argtypes.append(argtype); } @@ -1426,12 +1464,12 @@ Type condtype = attribExpr(tree.cond, env, syms.booleanType); tree.polyKind = (!allowPoly || - pt().hasTag(NONE) && pt() != Type.recoveryType || + pt().hasTag(NONE) && pt() != Type.recoveryType && pt() != Infer.anyPoly || isBooleanOrNumeric(env, tree)) ? PolyKind.STANDALONE : PolyKind.POLY; if (tree.polyKind == PolyKind.POLY && resultInfo.pt.hasTag(VOID)) { - //cannot get here (i.e. it means we are returning from void method - which is already an error) + //this means we are returning a poly conditional from void-compatible lambda expression resultInfo.checkContext.report(tree, diags.fragment("conditional.target.cant.be.void")); result = tree.type = types.createErrorType(resultInfo.pt); return; @@ -1439,15 +1477,7 @@ ResultInfo condInfo = tree.polyKind == PolyKind.STANDALONE ? unknownExprInfo : - resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) { - //this will use enclosing check context to check compatibility of - //subexpression against target type; if we are in a method check context, - //depending on whether boxing is allowed, we could have incompatibilities - @Override - public void report(DiagnosticPosition pos, JCDiagnostic details) { - enclosingContext.report(pos, diags.fragment("incompatible.type.in.conditional", details)); - } - }); + resultInfo.dup(conditionalContext(resultInfo.checkContext)); Type truetype = attribTree(tree.truepart, env, condInfo); Type falsetype = attribTree(tree.falsepart, env, condInfo); @@ -1506,6 +1536,18 @@ } }; + CheckContext conditionalContext(CheckContext checkContext) { + return new Check.NestedCheckContext(checkContext) { + //this will use enclosing check context to check compatibility of + //subexpression against target type; if we are in a method check context, + //depending on whether boxing is allowed, we could have incompatibilities + @Override + public void report(DiagnosticPosition pos, JCDiagnostic details) { + enclosingContext.report(pos, diags.fragment("incompatible.type.in.conditional", details)); + } + }; + } + /** Compute the type of a conditional expression, after * checking that it exists. See JLS 15.25. Does not take into * account the special case where condition and both arms @@ -2070,13 +2112,8 @@ tree.constructor = constructor.baseSymbol(); final TypeSymbol csym = clazztype.tsym; - ResultInfo diamondResult = new ResultInfo(pkind, newMethodTemplate(resultInfo.pt, argtypes, typeargtypes), new Check.NestedCheckContext(resultInfo.checkContext) { - @Override - public void report(DiagnosticPosition _unused, JCDiagnostic details) { - enclosingContext.report(tree.clazz, - diags.fragment("cant.apply.diamond.1", diags.fragment("diamond", csym), details)); - } - }, CheckMode.NO_TREE_UPDATE); + ResultInfo diamondResult = new ResultInfo(pkind, newMethodTemplate(resultInfo.pt, argtypes, typeargtypes), + diamondContext(tree, csym, resultInfo.checkContext), CheckMode.NO_TREE_UPDATE); Type constructorType = tree.constructorType = types.createErrorType(clazztype); constructorType = checkId(tree, site, constructor, @@ -2261,6 +2298,16 @@ chk.validate(tree.typeargs, localEnv); } + CheckContext diamondContext(JCNewClass clazz, TypeSymbol tsym, CheckContext checkContext) { + return new Check.NestedCheckContext(checkContext) { + @Override + public void report(DiagnosticPosition _unused, JCDiagnostic details) { + enclosingContext.report(clazz.clazz, + diags.fragment("cant.apply.diamond.1", diags.fragment("diamond", tsym), details)); + } + }; + } + /** Make an attributed null check tree. */ public JCExpression makeNullCheck(JCExpression arg) { @@ -2330,8 +2377,7 @@ boolean needsRecovery = resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK; try { - Type currentTarget = pt(); - if (needsRecovery && isSerializable(currentTarget)) { + if (needsRecovery && isSerializable(pt())) { localEnv.info.isSerializable = true; } List explicitParamTypes = null; @@ -2341,22 +2387,13 @@ explicitParamTypes = TreeInfo.types(that.params); } - Type lambdaType; - if (pt() != Type.recoveryType) { - /* We need to adjust the target. If the target is an - * intersection type, for example: SAM & I1 & I2 ... - * the target will be updated to SAM - */ - currentTarget = targetChecker.visit(currentTarget, that); - if (explicitParamTypes != null) { - currentTarget = infer.instantiateFunctionalInterface(that, - currentTarget, explicitParamTypes, resultInfo.checkContext); - } - currentTarget = types.removeWildcards(currentTarget); - lambdaType = types.findDescriptorType(currentTarget); - } else { - currentTarget = Type.recoveryType; - lambdaType = fallbackDescriptorType(that); + TargetInfo targetInfo = getTargetInfo(that, resultInfo, explicitParamTypes); + Type currentTarget = targetInfo.target; + Type lambdaType = targetInfo.descriptor; + + if (currentTarget.isErroneous()) { + result = that.type = currentTarget; + return; } setFunctionalInfo(localEnv, that, pt(), lambdaType, currentTarget, resultInfo.checkContext); @@ -2409,15 +2446,8 @@ //with the target-type, it will be recovered anyway in Attr.checkId needsRecovery = false; - FunctionalReturnContext funcContext = that.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? - new ExpressionLambdaReturnContext((JCExpression)that.getBody(), resultInfo.checkContext) : - new FunctionalReturnContext(resultInfo.checkContext); - - ResultInfo bodyResultInfo = lambdaType.getReturnType() == Type.recoveryType ? - recoveryInfo : - new ResultInfo(KindSelector.VAL, - lambdaType.getReturnType(), funcContext); - localEnv.info.returnResult = bodyResultInfo; + ResultInfo bodyResultInfo = localEnv.info.returnResult = + lambdaBodyResult(that, lambdaType, resultInfo); if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { attribTree(that.getBody(), localEnv, bodyResultInfo); @@ -2467,6 +2497,44 @@ } } //where + class TargetInfo { + Type target; + Type descriptor; + + public TargetInfo(Type target, Type descriptor) { + this.target = target; + this.descriptor = descriptor; + } + } + + TargetInfo getTargetInfo(JCPolyExpression that, ResultInfo resultInfo, List explicitParamTypes) { + Type lambdaType; + Type currentTarget = resultInfo.pt; + if (resultInfo.pt != Type.recoveryType) { + /* We need to adjust the target. If the target is an + * intersection type, for example: SAM & I1 & I2 ... + * the target will be updated to SAM + */ + currentTarget = targetChecker.visit(currentTarget, that); + if (explicitParamTypes != null) { + currentTarget = infer.instantiateFunctionalInterface(that, + currentTarget, explicitParamTypes, resultInfo.checkContext); + } + currentTarget = types.removeWildcards(currentTarget); + lambdaType = types.findDescriptorType(currentTarget); + } else { + currentTarget = Type.recoveryType; + lambdaType = fallbackDescriptorType(that); + } + if (that.hasTag(LAMBDA) && lambdaType.hasTag(FORALL)) { + //lambda expression target desc cannot be a generic method + resultInfo.checkContext.report(that, diags.fragment("invalid.generic.lambda.target", + lambdaType, kindName(currentTarget.tsym), currentTarget.tsym)); + currentTarget = types.createErrorType(pt()); + } + return new TargetInfo(currentTarget, lambdaType); + } + void preFlow(JCLambda tree) { new PostAttrAnalyzer() { @Override @@ -2612,13 +2680,24 @@ } } + ResultInfo lambdaBodyResult(JCLambda that, Type descriptor, ResultInfo resultInfo) { + FunctionalReturnContext funcContext = that.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? + new ExpressionLambdaReturnContext((JCExpression)that.getBody(), resultInfo.checkContext) : + new FunctionalReturnContext(resultInfo.checkContext); + + return descriptor.getReturnType() == Type.recoveryType ? + recoveryInfo : + new ResultInfo(KindSelector.VAL, + descriptor.getReturnType(), funcContext); + } + /** * Lambda compatibility. Check that given return types, thrown types, parameter types * are compatible with the expected functional interface descriptor. This means that: * (i) parameter types must be identical to those of the target descriptor; (ii) return * types must be compatible with the return type of the expected descriptor. */ - private void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext) { + void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext) { Type returnType = checkContext.inferenceContext().asUndetVar(descriptor.getReturnType()); //return values have already been checked - but if lambda has no return @@ -2746,18 +2825,12 @@ typeargtypes = attribTypes(that.typeargs, localEnv); } - Type desc; - Type currentTarget = pt(); boolean isTargetSerializable = resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK && - isSerializable(currentTarget); - if (currentTarget != Type.recoveryType) { - currentTarget = types.removeWildcards(targetChecker.visit(currentTarget, that)); - desc = types.findDescriptorType(currentTarget); - } else { - currentTarget = Type.recoveryType; - desc = fallbackDescriptorType(that); - } + isSerializable(pt()); + TargetInfo targetInfo = getTargetInfo(that, resultInfo, null); + Type currentTarget = targetInfo.target; + Type desc = targetInfo.descriptor; setFunctionalInfo(localEnv, that, pt(), desc, currentTarget, resultInfo.checkContext); List argtypes = desc.getParameterTypes(); @@ -3279,7 +3352,7 @@ } // Attribute the qualifier expression, and determine its symbol (if any). - Type site = attribTree(tree.selected, env, new ResultInfo(skind, Infer.anyPoly)); + Type site = attribTree(tree.selected, env, new ResultInfo(skind, Type.noType)); if (!pkind().contains(KindSelector.TYP_PCK)) site = capture(site); // Capture field access @@ -3884,8 +3957,15 @@ syms.methodClass); } - return chk.checkMethod(owntype, sym, env, argtrees, argtypes, env.info.lastResolveVarargs(), - resultInfo.checkContext.inferenceContext()); + PolyKind pkind = (sym.type.hasTag(FORALL) && + sym.type.getReturnType().containsAny(((ForAll)sym.type).tvars)) ? + PolyKind.POLY : PolyKind.STANDALONE; + TreeInfo.setPolyKind(env.tree, pkind); + + return (resultInfo.pt == Infer.anyPoly) ? + owntype : + chk.checkMethod(owntype, sym, env, argtrees, argtypes, env.info.lastResolveVarargs(), + resultInfo.checkContext.inferenceContext()); } catch (Infer.InferenceException ex) { //invalid target type - propagate exception outwards or report error //depending on the current check context diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Tue Sep 15 13:43:44 2015 +0100 @@ -49,7 +49,6 @@ import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext; import com.sun.tools.javac.comp.Infer.FreeTypeListener; import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.ANNOTATION; @@ -976,10 +975,6 @@ TreeInfo.setVarargsElement(env.tree, types.elemtype(argtype)); } } - PolyKind pkind = (sym.type.hasTag(FORALL) && - sym.type.getReturnType().containsAny(((ForAll)sym.type).tvars)) ? - PolyKind.POLY : PolyKind.STANDALONE; - TreeInfo.setPolyKind(env.tree, pkind); return owntype; } //where diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Tue Sep 15 13:43:44 2015 +0100 @@ -29,6 +29,7 @@ import com.sun.source.tree.NewClassTree; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Type.TypeMapping; +import com.sun.tools.javac.comp.ArgumentAttr.CachePolicy; import com.sun.tools.javac.comp.Resolve.ResolveError; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.tree.*; @@ -36,7 +37,6 @@ import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.code.Symbol.*; -import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.comp.Attr.ResultInfo; import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; import com.sun.tools.javac.tree.JCTree.*; @@ -55,8 +55,6 @@ import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; -import static com.sun.tools.javac.code.Kinds.*; -import static com.sun.tools.javac.code.Kinds.Kind.*; /** * This is an helper class that is used to perform deferred type-analysis. @@ -73,6 +71,7 @@ protected static final Context.Key deferredAttrKey = new Context.Key<>(); final Attr attr; + final ArgumentAttr argumentAttr; final Check chk; final JCDiagnostic.Factory diags; final Enter enter; @@ -98,6 +97,7 @@ protected DeferredAttr(Context context) { context.put(deferredAttrKey, this); attr = Attr.instance(context); + argumentAttr = ArgumentAttr.instance(context); chk = Check.instance(context); diags = JCDiagnostic.Factory.instance(context); enter = Enter.instance(context); @@ -255,6 +255,15 @@ return e != null ? e.speculativeTree.type : Type.noType; } + JCTree speculativeTree(DeferredAttrContext deferredAttrContext) { + DeferredType.SpeculativeCache.Entry e = speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase); + return e != null ? e.speculativeTree : stuckTree; + } + + DeferredTypeCompleter completer() { + return basicCompleter; + } + /** * Check a deferred type against a potential target-type. Depending on * the current attribution mode, a normal vs. speculative attribution @@ -272,7 +281,7 @@ } else { deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this); } - return check(resultInfo, deferredStuckPolicy, basicCompleter); + return check(resultInfo, deferredStuckPolicy, completer()); } private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy, @@ -389,6 +398,42 @@ } /** + * Performs speculative attribution of a lambda body and returns the speculative lambda tree, + * in the absence of a target-type. Since {@link Attr#visitLambda(JCLambda)} cannot type-check + * lambda bodies w/o a suitable target-type, this routine 'unrolls' the lambda by turning it + * into a regular block, speculatively type-checks the block and then puts back the pieces. + */ + JCLambda attribSpeculativeLambda(JCLambda that, Env env, ResultInfo resultInfo) { + ListBuffer stats = new ListBuffer<>(); + stats.addAll(that.params); + if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { + stats.add(make.Return((JCExpression)that.body)); + } else { + stats.add((JCBlock)that.body); + } + JCBlock lambdaBlock = make.Block(0, stats.toList()); + Env localEnv = attr.lambdaEnv(that, env); + try { + localEnv.info.returnResult = resultInfo; + JCBlock speculativeTree = (JCBlock)attribSpeculative(lambdaBlock, localEnv, resultInfo); + List args = speculativeTree.getStatements().stream() + .filter(s -> s.hasTag(Tag.VARDEF)) + .map(t -> (JCVariableDecl)t) + .collect(List.collector()); + JCTree lambdaBody = speculativeTree.getStatements().last(); + if (lambdaBody.hasTag(Tag.RETURN)) { + lambdaBody = ((JCReturn)lambdaBody).expr; + } + JCLambda speculativeLambda = make.Lambda(args, lambdaBody); + attr.preFlow(speculativeLambda); + flow.analyzeLambda(env, speculativeLambda, make, false); + return speculativeLambda; + } finally { + localEnv.info.scope.leave(); + } + } + + /** * Routine that performs speculative type-checking; the input AST node is * cloned (to avoid side-effects cause by Attr) and compiler state is * restored after type-checking. All diagnostics (but critical ones) are @@ -572,7 +617,7 @@ } } - private boolean insideOverloadPhase() { + public boolean insideOverloadPhase() { DeferredAttrContext dac = this; if (dac == emptyDeferredAttrContext) { return false; @@ -731,56 +776,16 @@ } boolean canLambdaBodyCompleteNormally(JCLambda tree) { - JCLambda newTree = new TreeCopier<>(make).copy(tree); - /* attr.lambdaEnv will create a meaningful env for the - * lambda expression. This is specially useful when the - * lambda is used as the init of a field. But we need to - * remove any added symbol. - */ - Env localEnv = attr.lambdaEnv(newTree, env); + List oldParams = tree.params; + CachePolicy prevPolicy = argumentAttr.withCachePolicy(CachePolicy.NO_CACHE); try { - List tmpParams = newTree.params; - while (tmpParams.nonEmpty()) { - tmpParams.head.vartype = make.at(tmpParams.head).Type(syms.errType); - tmpParams = tmpParams.tail; - } - - attr.attribStats(newTree.params, localEnv); - - /* set pt to Type.noType to avoid generating any bound - * which may happen if lambda's return type is an - * inference variable - */ - Attr.ResultInfo bodyResultInfo = attr.new ResultInfo(KindSelector.VAL, Type.noType); - localEnv.info.returnResult = bodyResultInfo; - - // discard any log output - Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); - try { - JCBlock body = (JCBlock)newTree.body; - /* we need to attribute the lambda body before - * doing the aliveness analysis. This is because - * constant folding occurs during attribution - * and the reachability of some statements depends - * on constant values, for example: - * - * while (true) {...} - */ - attr.attribStats(body.stats, localEnv); - - attr.preFlow(newTree); - /* make an aliveness / reachability analysis of the lambda - * to determine if it can complete normally - */ - flow.analyzeLambda(localEnv, newTree, make, true); - } finally { - log.popDiagnosticHandler(diagHandler); - } - return newTree.canCompleteNormally; + tree.params = tree.params.stream() + .map(vd -> make.VarDef(vd.mods, vd.name, make.Erroneous(), null)) + .collect(List.collector()); + return attribSpeculativeLambda(tree, env, attr.unknownExprInfo).canCompleteNormally; } finally { - JCBlock body = (JCBlock)newTree.body; - unenterScanner.scan(body.stats); - localEnv.info.scope.leave(); + argumentAttr.withCachePolicy(prevPolicy); + tree.params = oldParams; } } @@ -951,7 +956,7 @@ * A special tree scanner that would only visit portions of a given tree. * The set of nodes visited by the scanner can be customized at construction-time. */ - abstract static class FilterScanner extends TreeScanner { + abstract static class FilterScanner extends com.sun.tools.javac.tree.TreeScanner { final Filter treeFilter; @@ -1148,330 +1153,4 @@ } } } - - /** - * Does the argument expression {@code expr} need speculative type-checking? - */ - boolean isDeferred(Env 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 env; - ArgumentExpressionKind result; - - public DeferredChecker(Env 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) { - //perform arity-based check - Env localEnv = env.dup(tree); - JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, - attr.memberReferenceQualifierResult(tree)); - JCMemberReference mref2 = new TreeCopier(make).copy(tree); - mref2.expr = exprTree; - Symbol res = - rs.getMemberReference(tree, localEnv, mref2, - exprTree.type, tree.name); - tree.sym = res; - if (res.kind.isResolutionTargetError() || - res.type != null && res.type.hasTag(FORALL) || - (res.flags() & Flags.VARARGS) != 0 || - (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) && - exprTree.type.isRaw())) { - tree.overloadKind = JCMemberReference.OverloadKind.OVERLOADED; - } else { - tree.overloadKind = JCMemberReference.OverloadKind.UNOVERLOADED; - } - //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).isPrimitive() ? - ArgumentExpressionKind.PRIMITIVE : - ArgumentExpressionKind.POLY; - - } - - @Override - public void visitNewClass(JCNewClass tree) { - result = TreeInfo.isDiamond(tree) ? - 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 - Symbol sym = quicklyResolveMethod(env, tree); - - if (sym == null) { - result = ArgumentExpressionKind.POLY; - return; - } - - result = analyzeCandidateMethods(sym, ArgumentExpressionKind.PRIMITIVE, - argumentKindAnalyzer); - } - //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); - case APPLY: - return true; - case NEWCLASS: - JCNewClass nc = (JCNewClass) rec; - return nc.encl == null && nc.def == null && !TreeInfo.isDiamond(nc); - default: - return false; - } - } - private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) { - return argumentKindAnalyzer.reduce(result, kind); - } - MethodAnalyzer argumentKindAnalyzer = - new MethodAnalyzer() { - @Override - public ArgumentExpressionKind process(MethodSymbol ms) { - return ArgumentExpressionKind.methodKind(ms, types); - } - @Override - public ArgumentExpressionKind reduce(ArgumentExpressionKind kind1, - ArgumentExpressionKind kind2) { - switch (kind1) { - case PRIMITIVE: return kind2; - case NO_POLY: return kind2.isPoly() ? kind2 : kind1; - case POLY: return kind1; - default: - Assert.error(); - return null; - } - } - @Override - public boolean shouldStop(ArgumentExpressionKind result) { - return result.isPoly(); - } - }; - - @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; - } - - private Symbol quicklyResolveMethod(Env env, final JCMethodInvocation tree) { - final JCExpression rec = tree.meth.hasTag(SELECT) ? - ((JCFieldAccess)tree.meth).selected : - null; - - if (rec != null && !isSimpleReceiver(rec)) { - return null; - } - - Type site; - - if (rec != null) { - switch (rec.getTag()) { - case APPLY: - Symbol recSym = quicklyResolveMethod(env, (JCMethodInvocation) rec); - if (recSym == null) - return null; - Symbol resolvedReturnType = - analyzeCandidateMethods(recSym, syms.errSymbol, returnSymbolAnalyzer); - if (resolvedReturnType == null) - return null; - site = resolvedReturnType.type; - break; - case NEWCLASS: - JCNewClass nc = (JCNewClass) rec; - site = attribSpeculative(nc.clazz, env, attr.unknownTypeExprInfo).type; - break; - default: - site = attribSpeculative(rec, env, attr.unknownTypeExprInfo).type; - break; - } - } else { - site = env.enclClass.sym.type; - } - - site = types.skipTypeVars(site, true); - - List args = rs.dummyArgs(tree.args.length()); - Name name = TreeInfo.name(tree.meth); - - Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args, List.nil(), MethodResolutionPhase.VARARITY) { - @Override - Symbol lookup(Env 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()); - } - @Override - Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { - return sym; - } - }; - - return rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh); - } - //where: - MethodAnalyzer returnSymbolAnalyzer = new MethodAnalyzer() { - @Override - public Symbol process(MethodSymbol ms) { - ArgumentExpressionKind kind = ArgumentExpressionKind.methodKind(ms, types); - if (kind == ArgumentExpressionKind.POLY || ms.getReturnType().hasTag(TYPEVAR)) - return null; - return ms.getReturnType().tsym; - } - @Override - public Symbol reduce(Symbol s1, Symbol s2) { - return s1 == syms.errSymbol ? s2 : s1 == s2 ? s1 : null; - } - @Override - public boolean shouldStop(Symbol result) { - return result == null; - } - }; - - /** - * Process the result of Resolve.lookupMethod. If sym is a method symbol, the result of - * MethodAnalyzer.process is returned. If sym is an ambiguous symbol, all the candidate - * methods are inspected one by one, using MethodAnalyzer.process. The outcomes are - * reduced using MethodAnalyzer.reduce (using defaultValue as the first value over which - * the reduction runs). MethodAnalyzer.shouldStop can be used to stop the inspection early. - */ - E analyzeCandidateMethods(Symbol sym, E defaultValue, MethodAnalyzer analyzer) { - switch (sym.kind) { - case MTH: - return analyzer.process((MethodSymbol) sym); - case AMBIGUOUS: - Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol(); - E res = defaultValue; - for (Symbol s : err.ambiguousSyms) { - if (s.kind == MTH) { - res = analyzer.reduce(res, analyzer.process((MethodSymbol) s)); - if (analyzer.shouldStop(res)) - return res; - } - } - return res; - default: - return defaultValue; - } - } - } - - /** Analyzer for methods - used by analyzeCandidateMethods. */ - interface MethodAnalyzer { - E process(MethodSymbol ms); - E reduce(E e1, E e2); - boolean shouldStop(E result); - } - - //where - private EnumSet deferredCheckerTags = - EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST, - CONDEXPR, NEWCLASS, APPLY, LITERAL); } diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java Tue Sep 15 13:43:44 2015 +0100 @@ -37,6 +37,7 @@ import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.comp.DeferredAttr.AttrMode; +import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext; import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph; import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; @@ -179,7 +180,11 @@ resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext, //B2 argtypes, mt.getParameterTypes(), warn); - if (allowGraphInference && + + if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) { + //we are inside method attribution - just return a partially inferred type + return new PartiallyInferredMethodType(mt, inferenceContext, env, warn); + } else if (allowGraphInference && resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { //inject return constraints earlier @@ -238,6 +243,73 @@ } } + /** + * A partially infered method/constructor type; such a type can be checked multiple times + * against different targets. + */ + public class PartiallyInferredMethodType extends MethodType { + public PartiallyInferredMethodType(MethodType mtype, InferenceContext inferenceContext, Env env, Warner warn) { + super(mtype.getParameterTypes(), mtype.getReturnType(), mtype.getThrownTypes(), mtype.tsym); + this.inferenceContext = inferenceContext; + this.env = env; + this.warn = warn; + } + + /** The inference context. */ + final InferenceContext inferenceContext; + + /** The attribution environment. */ + Env env; + + /** The warner. */ + final Warner warn; + + @Override + public boolean isPartial() { + return true; + } + + /** + * Checks this type against a target; this means generating return type constraints, solve + * and then roll back the results (to avoid poolluting the context). + */ + Type check(Attr.ResultInfo resultInfo) { + Warner noWarnings = new Warner(null); + inferenceException.clear(); + List saved_undet = null; + try { + /** we need to save the inference context before generating target type constraints. + * This constraints may pollute the inference context and make it useless in case we + * need to use it several times: with several targets. + */ + saved_undet = inferenceContext.save(); + if (allowGraphInference && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { + //inject return constraints earlier + checkWithinBounds(inferenceContext, noWarnings); //propagation + Type res = generateReturnConstraints(env.tree, resultInfo, //B3 + this, inferenceContext); + + if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) { + //propagate inference context outwards and exit + inferenceContext.dupTo(resultInfo.checkContext.inferenceContext(), + resultInfo.checkContext.deferredAttrContext().insideOverloadPhase()); + return res; + } + } + inferenceContext.solve(noWarnings); + return inferenceContext.asInstType(this).getReturnType(); + } catch (InferenceException ex) { + resultInfo.checkContext.report(null, ex.getDiagnostic()); + Assert.error(); //cannot get here (the above should throw) + return null; + } finally { + if (saved_undet != null) { + inferenceContext.rollback(saved_undet); + } + } + } + } + private void dumpGraphsIfNeeded(DiagnosticPosition pos, Symbol msym, Resolve.MethodResolutionContext rsContext) { int round = 0; try { diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java Tue Sep 15 13:43:44 2015 +0100 @@ -315,31 +315,46 @@ return buf.toList(); } - /** - * Restore the state of this inference context to the previous known checkpoint - */ + /** Restore the state of this inference context to the previous known checkpoint. + * Consider that the number of saved undetermined variables can be different to the current + * amount. This is because new captured variables could have been added. + */ void rollback(List saved_undet) { - Assert.check(saved_undet != null && saved_undet.length() == undetvars.length()); + Assert.check(saved_undet != null); //restore bounds (note: we need to preserve the old instances) - for (Type t : undetvars) { - UndetVar uv = (UndetVar)t; + ListBuffer newUndetVars = new ListBuffer<>(); + ListBuffer newInferenceVars = new ListBuffer<>(); + while (saved_undet.nonEmpty() && undetvars.nonEmpty()) { + UndetVar uv = (UndetVar)undetvars.head; UndetVar uv_saved = (UndetVar)saved_undet.head; - for (InferenceBound ib : InferenceBound.values()) { - uv.setBounds(ib, uv_saved.getBounds(ib)); + if (uv.qtype == uv_saved.qtype) { + for (InferenceBound ib : InferenceBound.values()) { + uv.setBounds(ib, uv_saved.getBounds(ib)); + } + uv.inst = uv_saved.inst; + undetvars = undetvars.tail; + saved_undet = saved_undet.tail; + newUndetVars.add(uv); + newInferenceVars.add(uv.qtype); + } else { + undetvars = undetvars.tail; } - uv.inst = uv_saved.inst; - saved_undet = saved_undet.tail; } + undetvars = newUndetVars.toList(); + inferencevars = newInferenceVars.toList(); } /** * Copy variable in this inference context to the given context */ void dupTo(final InferenceContext that) { - that.inferencevars = that.inferencevars.appendList( - inferencevars.diff(that.inferencevars)); - that.undetvars = that.undetvars.appendList( - undetvars.diff(that.undetvars)); + dupTo(that, false); + } + + void dupTo(final InferenceContext that, boolean clone) { + that.inferencevars = that.inferencevars.appendList(inferencevars.diff(that.inferencevars)); + List undetsToPropagate = clone ? save() : undetvars; + that.undetvars = that.undetvars.appendList(undetsToPropagate.diff(that.undetvars)); //propagate cloned undet!! //set up listeners to notify original inference contexts as //propagated vars are inferred in new context for (Type t : inferencevars) { diff -r 8fa8045bbd4e -r 286fc9270404 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java Mon Sep 14 11:26:14 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java Tue Sep 15 13:43:44 2015 +0100 @@ -588,7 +588,8 @@ MethodResolutionContext prevContext = currentResolutionContext; try { currentResolutionContext = new MethodResolutionContext(); - currentResolutionContext.attrMode = DeferredAttr.AttrMode.CHECK; + currentResolutionContext.attrMode = (resultInfo.pt == Infer.anyPoly) ? + AttrMode.SPECULATIVE : DeferredAttr.AttrMode.CHECK; if (env.tree.hasTag(JCTree.Tag.REFERENCE)) { //method/constructor references need special check class //to handle inference variables in 'argtypes' (might happen @@ -1032,6 +1033,11 @@ protected ResultInfo dup(CheckContext newContext) { return new MethodResultInfo(pt, newContext); } + + @Override + protected ResultInfo dup(Type newPt, CheckContext newContext) { + return new MethodResultInfo(newPt, newContext); + } } /** @@ -1092,10 +1098,9 @@ unrelatedFunctionalInterfaces(found, req) && (actual != null && actual.getTag() == DEFERRED)) { DeferredType dt = (DeferredType) actual; - DeferredType.SpeculativeCache.Entry e = - dt.speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase); - if (e != null && e.speculativeTree != deferredAttr.stuckTree) { - return functionalInterfaceMostSpecific(found, req, e.speculativeTree); + JCTree speculativeTree = dt.speculativeTree(deferredAttrContext); + if (speculativeTree != deferredAttr.stuckTree) { + return functionalInterfaceMostSpecific(found, req, speculativeTree); } } return compatibleBySubtyping(found, req); @@ -1147,8 +1152,8 @@ @Override public void visitConditional(JCConditional tree) { - scan(tree.truepart); - scan(tree.falsepart); + scan(asExpr(tree.truepart)); + scan(asExpr(tree.falsepart)); } @Override @@ -1180,6 +1185,11 @@ } @Override + public void visitParens(JCParens tree) { + scan(asExpr(tree.expr)); + } + + @Override public void visitLambda(JCLambda tree) { Type desc_t = types.findDescriptorType(t); Type desc_s = types.findDescriptorType(s); @@ -1214,7 +1224,7 @@ private List lambdaResults(JCLambda lambda) { if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { - return List.of((JCExpression) lambda.body); + return List.of(asExpr((JCExpression) lambda.body)); } else { final ListBuffer buffer = new ListBuffer<>(); DeferredAttr.LambdaReturnScanner lambdaScanner = @@ -1222,7 +1232,7 @@ @Override public void visitReturn(JCReturn tree) { if (tree.expr != null) { - buffer.append(tree.expr); + buffer.append(asExpr(tree.expr)); } } }; @@ -1230,6 +1240,16 @@ return buffer.toList(); } } + + private JCExpression asExpr(JCExpression expr) { + if (expr.type.hasTag(DEFERRED)) { + JCTree speculativeTree = ((DeferredType)expr.type).speculativeTree(deferredAttrContext); + if (speculativeTree != deferredAttr.stuckTree) { + expr = (JCExpression)speculativeTree; + } + } + return expr; + } } } diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/generics/wildcards/neg/Readonly.out --- a/test/tools/javac/generics/wildcards/neg/Readonly.out Mon Sep 14 11:26:14 2015 +0100 +++ b/test/tools/javac/generics/wildcards/neg/Readonly.out Tue Sep 15 13:43:44 2015 +0100 @@ -1,2 +1,2 @@ -Readonly.java:15:10: compiler.err.cant.apply.symbol: kindname.method, put, Err, Err, kindname.class, Err, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: Err, Err)) +Readonly.java:15:10: compiler.err.cant.apply.symbol: kindname.method, put, Err, Err, kindname.class, Err, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: Err, Err)) 1 error diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/8019480/T8019480.out --- a/test/tools/javac/lambda/8019480/T8019480.out Mon Sep 14 11:26:14 2015 +0100 +++ b/test/tools/javac/lambda/8019480/T8019480.out Tue Sep 15 13:43:44 2015 +0100 @@ -1,3 +1,3 @@ +T8019480.java:21:34: compiler.err.cant.apply.symbols: kindname.method, add, java.lang.Object,{(compiler.misc.inapplicable.method: kindname.method, java.util.Collection, add(U), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, U))),(compiler.misc.inapplicable.method: kindname.method, java.util.List, add(U), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, U))),(compiler.misc.inapplicable.method: kindname.method, java.util.List, add(int,U), (compiler.misc.arg.length.mismatch))} T8019480.java:21:46: compiler.err.report.access: clone(), protected, java.lang.Object -T8019480.java:21:34: compiler.err.cant.apply.symbols: kindname.method, add, java.lang.Object,{(compiler.misc.inapplicable.method: kindname.method, java.util.Collection, add(U), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, U))),(compiler.misc.inapplicable.method: kindname.method, java.util.List, add(U), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, U))),(compiler.misc.inapplicable.method: kindname.method, java.util.List, add(int,U), (compiler.misc.arg.length.mismatch))} 2 errors diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/InferStrict.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/InferStrict.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8078093 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile InferStrict.java + */ +import java.util.HashSet; +import java.util.Set; + +class InferStrict { + public Set compute(Set t) { return t; } + public T join(Set t1, Set t2) { return null; } + public T compute() { return null; } + public void test() { + join( + compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(new HashSet<>()))))))))))))))))), + compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(compute(new HashSet()))))))))))))))))) + ).length(); + } +} diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/InferWeak.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/InferWeak.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8078093 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile InferWeak.java + */ +class InferWeak { + private void test() { + GroupLayout l = new GroupLayout(); + l.setHorizontalGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGroup(l.createParallelGroup().addGroup( + l.createParallelGroup().addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())).addGap(1).addComponent(new Object()).addGap(1) + .addComponent(new Object())); + } + + static class GroupLayout { + T createParallelGroup() {return null;} + T createParallelGroup(int i) {return null;} + T createParallelGroup(int i, int j) {return null;} + void setHorizontalGroup(ParallelGroup g) { } + } + + static class ParallelGroup { + T addGroup(ParallelGroup g) { return null; } + T addGroup(int i, ParallelGroup g) { return null; } + T addGap(int i) { return null; } + T addGap(int i, int j) { return null; } + T addComponent(Object c) { return null; } + T addComponent(int i, Object c) { return null; } + } + } diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/NestedLambdaGenerics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/NestedLambdaGenerics.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8078093 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile NestedLambdaGenerics.java + */ +import java.util.concurrent.Callable; + +class NestedLambdaGenerics { + void test() { + m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + (Callable)null))))))))))))))))))))))))))))))); + } + static class A0 { } + static class A1 { } + static class A2 { } + static class A3 { } + static class A4 { } + Z m(A0 t, Callable ct) { return null; } + Z m(A1 t, Callable ct) { return null; } + Z m(A2 t, Callable ct) { return null; } + Z m(A3 t, Callable ct) { return null; } + Z m(A4 t, Callable ct) { return null; } + Z m(Object o, Callable co) { return null; } +} diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/NestedLambdaNoGenerics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/NestedLambdaNoGenerics.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8078093 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile NestedLambdaNoGenerics.java + */ +import java.util.concurrent.Callable; + +class NestedLambdaNoGenerics { + void test() { + m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, () -> m(null, + (Callable)null))))))))))))))))))))))))))))))); + } + static class A0 { } + static class A1 { } + static class A2 { } + static class A3 { } + static class A4 { } + String m(A0 t, Callable ct) { return ""; } + String m(A1 t, Callable ct) { return ""; } + String m(A2 t, Callable ct) { return ""; } + String m(A3 t, Callable ct) { return ""; } + String m(A4 t, Callable ct) { return ""; } + String m(Object o, Callable co) { return ""; } +} diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/T8055984.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/T8055984.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8078093 8055894 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile T8055984.java + */ + +class T8055984 { + static class C { + U fu; + + C() { } + + C(C other) { + this.fu = other.fu; + } + + C(U fu) { + this.fu = fu; + } + } + + static C m(C src) { return new C(src); } + + static void test() { + C c2 = m(new C<>(m(new C<>() )) ); + C c3 = m(new C<>(m(new C<>(m(new C<>() )) )) ); + C c4 = m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) ); + C c5 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) ); + C c6 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) )) ); + C c7 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) )) )) ); + C c8 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) )) )) )) ); + C c9 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) )) )) )) )) ); + C c10 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>()))))))))))))))))))); + } +} + diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/T8077247.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/T8077247.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8078093 8077247 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile T8077247.java + */ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class T8077247 { + public static void test() { + int x = add(add(add(add(add(add(add(add(add(add(1, 2), 3), 4), 5), 6), 7), 8), 9), 10), 11); + } + + public static int add(int x, int y) { + long rslt = (long)x + (long)y; + if (Integer.MIN_VALUE <= rslt && rslt <= Integer.MAX_VALUE) { + return (int)rslt; + } + + String msg = String.format("Integer overflow: %d + %d.", x, y); + throw new IllegalArgumentException(msg); + } + + public static double add(double x, double y) { + double rslt = x + y; + if (Double.isInfinite(rslt)) { + String msg = String.format("Real overflow: %s + %s.", x, y); + throw new IllegalArgumentException(msg); + } + return (rslt == -0.0) ? 0.0 : rslt; + } + + public static List add(List x, List y) { + List rslt = new ArrayList<>(x.size() + y.size()); + rslt.addAll(x); + rslt.addAll(y); + return rslt; + } + + public static String add(String x, String y) { + return x + y; + } + + public static Map add(Map x, Map y) { + Map rslt = new HashMap<>(x); + rslt.putAll(y); + return rslt; + } +} diff -r 8fa8045bbd4e -r 286fc9270404 test/tools/javac/lambda/speculative/T8078093.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/speculative/T8078093.java Tue Sep 15 13:43:44 2015 +0100 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8078093 + * @summary Exponential performance regression Java 8 compiler compared to Java 7 compiler + * @compile T8078093.java + */ + +import java.util.LinkedHashMap; +import java.util.Map; + +class T8078093 { + public static void test() { + Map a = x(x(x(x(x(x(x(x(x(x(x(x( + new LinkedHashMap(), + 1, "a"), 2, "b"), 3, "c"), 4, "d"), + 5, "e"), 6, "f"), 7, "g"), 8, "h"), + 9, "i"), 10, "j"), 11, "k"), 12, "l"); + } + + @SuppressWarnings("unused") + public static Map x(Map m, K k, V v) { + // Replaced actual code by dummy implementation. + return null; + } +}