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
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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;
+ }
+ };
}
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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<ArgumentAttr> 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<AttrContext> env;
+
+ /** Result of method attribution. */
+ private Type result;
+
+ /** Cache for argument types; behavior is influences by the currrently selected cache policy. */
+ Map<UniquePos, ArgumentType<?>> 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<AttrContext> env) {
+ Env<AttrContext> 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")
+ <T extends JCExpression, Z extends ArgumentType<T>> void processArg(T that, Function<T, Z> 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<AttrContext> 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")
+ <T extends JCExpression, Z extends ArgumentType<T>> void processArg(T that, Supplier<Z> 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<AttrContext> localEnv = env.dup(tree);
+ JCExpression exprTree = (JCExpression)deferredAttr.attribSpeculative(tree.getQualifierExpression(), localEnv,
+ attr.memberReferenceQualifierResult(tree));
+ JCMemberReference mref2 = new TreeCopier<Void>(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<T extends JCExpression> extends DeferredType implements DeferredTypeCompleter {
+
+ /** The speculative tree carrying type information. */
+ T speculativeTree;
+
+ /** Types associated with this argument (one type per possible target result). */
+ Map<ResultInfo, Type> speculativeTypes;
+
+ public ArgumentType(JCExpression tree, Env<AttrContext> env, T speculativeTree, Map<ResultInfo, Type> 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<ResultInfo, Type> _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<T> dup(T tree, Env<AttrContext> env);
+ }
+
+ /**
+ * Argument type for parenthesized expression.
+ */
+ class ParensType extends ArgumentType<JCParens> {
+ ParensType(JCExpression tree, Env<AttrContext> env, JCParens speculativeParens) {
+ this(tree, env, speculativeParens, new HashMap<>());
+ }
+
+ ParensType(JCExpression tree, Env<AttrContext> env, JCParens speculativeParens, Map<ResultInfo, Type> speculativeTypes) {
+ super(tree, env, speculativeParens, speculativeTypes);
+ }
+
+ @Override
+ Type overloadCheck(ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
+ return checkSpeculative(speculativeTree.expr, resultInfo);
+ }
+
+ @Override
+ ArgumentType<JCParens> dup(JCParens tree, Env<AttrContext> env) {
+ return new ParensType(tree, env, speculativeTree, speculativeTypes);
+ }
+ }
+
+ /**
+ * Argument type for conditionals.
+ */
+ class ConditionalType extends ArgumentType<JCConditional> {
+ ConditionalType(JCExpression tree, Env<AttrContext> env, JCConditional speculativeCond) {
+ this(tree, env, speculativeCond, new HashMap<>());
+ }
+
+ ConditionalType(JCExpression tree, Env<AttrContext> env, JCConditional speculativeCond, Map<ResultInfo, Type> 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<JCConditional> dup(JCConditional tree, Env<AttrContext> env) {
+ return new ConditionalType(tree, env, speculativeTree, speculativeTypes);
+ }
+ }
+
+ /**
+ * Argument type for explicit lambdas.
+ */
+ class ExplicitLambdaType extends ArgumentType<JCLambda> {
+
+ /** List of argument types (lazily populated). */
+ Optional<List<Type>> argtypes = Optional.empty();
+
+ /** List of return expressions (lazily populated). */
+ Optional<List<JCReturn>> returnExpressions = Optional.empty();
+
+ ExplicitLambdaType(JCLambda originalLambda, Env<AttrContext> env, JCLambda speculativeLambda) {
+ this(originalLambda, env, speculativeLambda, new HashMap<>());
+ }
+
+ ExplicitLambdaType(JCLambda originalLambda, Env<AttrContext> env, JCLambda speculativeLambda, Map<ResultInfo, Type> speculativeTypes) {
+ super(originalLambda, env, speculativeLambda, speculativeTypes);
+ }
+
+ /** Compute argument types (if needed). */
+ List<Type> argtypes() {
+ return argtypes.orElseGet(() -> {
+ List<Type> res = TreeInfo.types(speculativeTree.params);
+ argtypes = Optional.of(res);
+ return res;
+ });
+ }
+
+ /** Compute return expressions (if needed). */
+ List<JCReturn> returnExpressions() {
+ return returnExpressions.orElseGet(() -> {
+ final List<JCReturn> res;
+ if (speculativeTree.getBodyKind() == BodyKind.EXPRESSION) {
+ res = List.of(attr.make.Return((JCExpression)speculativeTree.body));
+ } else {
+ ListBuffer<JCReturn> 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<JCLambda> dup(JCLambda tree, Env<AttrContext> env) {
+ return new ExplicitLambdaType(tree, env, speculativeTree, speculativeTypes);
+ }
+ }
+
+ /**
+ * Argument type for methods/constructors.
+ */
+ abstract class ResolvedMemberType<E extends JCExpression> extends ArgumentType<E> {
+
+ public ResolvedMemberType(JCExpression tree, Env<AttrContext> env, E speculativeMethod, Map<ResultInfo, Type> 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<JCMethodInvocation> {
+
+ public ResolvedMethodType(JCExpression tree, Env<AttrContext> env, JCMethodInvocation speculativeTree) {
+ this(tree, env, speculativeTree, new HashMap<>());
+ }
+
+ public ResolvedMethodType(JCExpression tree, Env<AttrContext> env, JCMethodInvocation speculativeTree, Map<ResultInfo, Type> speculativeTypes) {
+ super(tree, env, speculativeTree, speculativeTypes);
+ }
+
+ @Override
+ ResultInfo resultInfo(ResultInfo resultInfo) {
+ return resultInfo;
+ }
+
+ @Override
+ Type methodType() {
+ return speculativeTree.meth.type;
+ }
+
+ @Override
+ ArgumentType<JCMethodInvocation> dup(JCMethodInvocation tree, Env<AttrContext> env) {
+ return new ResolvedMethodType(tree, env, speculativeTree, speculativeTypes);
+ }
+ }
+
+ /**
+ * Argument type for constructors.
+ */
+ class ResolvedConstructorType extends ResolvedMemberType<JCNewClass> {
+
+ public ResolvedConstructorType(JCExpression tree, Env<AttrContext> env, JCNewClass speculativeTree) {
+ this(tree, env, speculativeTree, new HashMap<>());
+ }
+
+ public ResolvedConstructorType(JCExpression tree, Env<AttrContext> env, JCNewClass speculativeTree, Map<ResultInfo, Type> 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<JCNewClass> dup(JCNewClass tree, Env<AttrContext> 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;
+ }
+}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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<AttrContext> 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<AttrContext> 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<JCExpression> trees, Env<AttrContext> env, ListBuffer<Type> 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<Type> 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<Type> 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<Type> 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
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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<DeferredAttr> 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<AttrContext> env, ResultInfo resultInfo) {
+ ListBuffer<JCStatement> 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<AttrContext> localEnv = attr.lambdaEnv(that, env);
+ try {
+ localEnv.info.returnResult = resultInfo;
+ JCBlock speculativeTree = (JCBlock)attribSpeculative(lambdaBlock, localEnv, resultInfo);
+ List<JCVariableDecl> 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<AttrContext> localEnv = attr.lambdaEnv(newTree, env);
+ List<JCVariableDecl> oldParams = tree.params;
+ CachePolicy prevPolicy = argumentAttr.withCachePolicy(CachePolicy.NO_CACHE);
try {
- List<JCVariableDecl> 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<JCTree> treeFilter;
@@ -1148,330 +1153,4 @@
}
}
}
-
- /**
- * 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) {
- //perform arity-based check
- Env<AttrContext> localEnv = env.dup(tree);
- JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv,
- attr.memberReferenceQualifierResult(tree));
- JCMemberReference mref2 = new TreeCopier<Void>(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<ArgumentExpressionKind> argumentKindAnalyzer =
- new MethodAnalyzer<ArgumentExpressionKind>() {
- @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<AttrContext> 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<Type> args = rs.dummyArgs(tree.args.length());
- Name name = TreeInfo.name(tree.meth);
-
- Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args, 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());
- }
- @Override
- Symbol access(Env<AttrContext> env, DiagnosticPosition pos, Symbol location, Symbol sym) {
- return sym;
- }
- };
-
- return rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh);
- }
- //where:
- MethodAnalyzer<Symbol> returnSymbolAnalyzer = new MethodAnalyzer<Symbol>() {
- @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> E analyzeCandidateMethods(Symbol sym, E defaultValue, MethodAnalyzer<E> 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> {
- E process(MethodSymbol ms);
- E reduce(E e1, E e2);
- boolean shouldStop(E result);
- }
-
- //where
- private EnumSet<JCTree.Tag> deferredCheckerTags =
- EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST,
- CONDEXPR, NEWCLASS, APPLY, LITERAL);
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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<AttrContext> 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<AttrContext> 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<Type> 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 {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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<Type> 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<Type> newUndetVars = new ListBuffer<>();
+ ListBuffer<Type> 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<Type> 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) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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<JCExpression> 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<JCExpression> 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;
+ }
}
}
--- a/langtools/test/tools/javac/generics/wildcards/neg/Readonly.out Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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<compiler.misc.type.captureof: 1, ? extends java.lang.String>, Err<compiler.misc.type.captureof: 2, ? extends java.lang.String>, kindname.class, Err<T>, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: Err<compiler.misc.type.captureof: 2, ? extends java.lang.String>, Err<compiler.misc.type.captureof: 1, ? extends java.lang.String>))
+Readonly.java:15:10: compiler.err.cant.apply.symbol: kindname.method, put, Err<compiler.misc.type.captureof: 1, ? extends java.lang.String>, Err<compiler.misc.type.captureof: 2, ? extends java.lang.String>, kindname.class, Err<T>, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: Err<compiler.misc.type.captureof: 3, ? extends java.lang.String>, Err<compiler.misc.type.captureof: 1, ? extends java.lang.String>))
1 error
--- a/langtools/test/tools/javac/lambda/8019480/T8019480.out Mon Sep 14 11:26:14 2015 +0100
+++ b/langtools/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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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 <T> Set<T> compute(Set<T> t) { return t; }
+ public <T> T join(Set<T> t1, Set<T> t2) { return null; }
+ public <T extends InferStrict> 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<String>())))))))))))))))))
+ ).length();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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 extends ParallelGroup> T createParallelGroup() {return null;}
+ <T extends ParallelGroup> T createParallelGroup(int i) {return null;}
+ <T extends ParallelGroup> T createParallelGroup(int i, int j) {return null;}
+ void setHorizontalGroup(ParallelGroup g) { }
+ }
+
+ static class ParallelGroup {
+ <T extends ParallelGroup> T addGroup(ParallelGroup g) { return null; }
+ <T extends ParallelGroup> T addGroup(int i, ParallelGroup g) { return null; }
+ <T extends ParallelGroup> T addGap(int i) { return null; }
+ <T extends ParallelGroup> T addGap(int i, int j) { return null; }
+ <T extends ParallelGroup> T addComponent(Object c) { return null; }
+ <T extends ParallelGroup> T addComponent(int i, Object c) { return null; }
+ }
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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<String>)null)))))))))))))))))))))))))))))));
+ }
+ static class A0 { }
+ static class A1 { }
+ static class A2 { }
+ static class A3 { }
+ static class A4 { }
+ <Z extends A0> Z m(A0 t, Callable<Z> ct) { return null; }
+ <Z extends A1> Z m(A1 t, Callable<Z> ct) { return null; }
+ <Z extends A2> Z m(A2 t, Callable<Z> ct) { return null; }
+ <Z extends A3> Z m(A3 t, Callable<Z> ct) { return null; }
+ <Z extends A4> Z m(A4 t, Callable<Z> ct) { return null; }
+ <Z> Z m(Object o, Callable<Z> co) { return null; }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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<String>)null)))))))))))))))))))))))))))))));
+ }
+ static class A0 { }
+ static class A1 { }
+ static class A2 { }
+ static class A3 { }
+ static class A4 { }
+ String m(A0 t, Callable<A0> ct) { return ""; }
+ String m(A1 t, Callable<A1> ct) { return ""; }
+ String m(A2 t, Callable<A2> ct) { return ""; }
+ String m(A3 t, Callable<A3> ct) { return ""; }
+ String m(A4 t, Callable<A4> ct) { return ""; }
+ String m(Object o, Callable<String> co) { return ""; }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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> {
+ U fu;
+
+ C() { }
+
+ C(C<U> other) {
+ this.fu = other.fu;
+ }
+
+ C(U fu) {
+ this.fu = fu;
+ }
+ }
+
+ static <U> C<U> m(C<U> src) { return new C<U>(src); }
+
+ static void test() {
+ C<String> c2 = m(new C<>(m(new C<>() )) );
+ C<String> c3 = m(new C<>(m(new C<>(m(new C<>() )) )) );
+ C<String> c4 = m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) );
+ C<String> c5 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) );
+ C<String> c6 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) )) );
+ C<String> c7 = m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>(m(new C<>() )) )) )) )) )) )) );
+ C<String> 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<String> 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<String> 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<>())))))))))))))))))));
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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 <T> List<T> add(List<T> x, List<T> y) {
+ List<T> 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 <K, V> Map<K, V> add(Map<K, V> x, Map<K, V> y) {
+ Map<K, V> rslt = new HashMap<>(x);
+ rslt.putAll(y);
+ return rslt;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/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<Integer, String> a = x(x(x(x(x(x(x(x(x(x(x(x(
+ new LinkedHashMap<Integer, String>(),
+ 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 <K, V> Map<K, V> x(Map<K, V> m, K k, V v) {
+ // Replaced actual code by dummy implementation.
+ return null;
+ }
+}