8078093: Severe compiler performance regression Java 7 to 8 for nested method invocations

Add infrastructure to avoid combinatorial explosion of method argument attributions

Reviewed-by: jlahoda, vromero, dlsmith
This commit is contained in:
Maurizio Cimadamore 2015-09-15 13:43:44 +01:00
parent 379f2540aa
commit 981c6dc298
17 changed files with 1454 additions and 480 deletions

View File

@ -3152,10 +3152,20 @@ public class Types {
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;
}
};
}
};

View File

@ -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;
}
}

View File

@ -108,6 +108,7 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
}
}
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
}
};
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
//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 @@ public class Attr extends JCTree.Visitor {
}
}
//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 @@ public class Attr extends JCTree.Visitor {
}
}
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 @@ public class Attr extends JCTree.Visitor {
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 @@ public class Attr extends JCTree.Visitor {
}
// 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 @@ public class Attr extends JCTree.Visitor {
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

View File

@ -49,7 +49,6 @@ import com.sun.tools.javac.code.Symbol.*;
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 @@ public class Check {
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

View File

@ -29,6 +29,7 @@ import com.sun.source.tree.LambdaExpressionTree.BodyKind;
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.*;
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 java.util.function.Function;
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 @@ public class DeferredAttr extends JCTree.Visitor {
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 @@ public class DeferredAttr extends JCTree.Visitor {
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 @@ public class DeferredAttr extends JCTree.Visitor {
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 @@ public class DeferredAttr extends JCTree.Visitor {
} else {
deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this);
}
return check(resultInfo, deferredStuckPolicy, basicCompleter);
return check(resultInfo, deferredStuckPolicy, completer());
}
private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy,
@ -388,6 +397,42 @@ public class DeferredAttr extends JCTree.Visitor {
CHECK
}
/**
* 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
@ -572,7 +617,7 @@ public class DeferredAttr extends JCTree.Visitor {
}
}
private boolean insideOverloadPhase() {
public boolean insideOverloadPhase() {
DeferredAttrContext dac = this;
if (dac == emptyDeferredAttrContext) {
return false;
@ -731,56 +776,16 @@ public class DeferredAttr extends JCTree.Visitor {
}
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 @@ public class DeferredAttr extends JCTree.Visitor {
* 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 @@ public class DeferredAttr extends JCTree.Visitor {
}
}
}
/**
* 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);
}

View File

@ -37,6 +37,7 @@ import com.sun.tools.javac.code.Type.*;
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 @@ public class Infer {
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 @@ public class Infer {
}
}
/**
* 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 {

View File

@ -315,31 +315,46 @@ class InferenceContext {
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) {

View File

@ -588,7 +588,8 @@ public class Resolve {
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 @@ public class Resolve {
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 @@ public class Resolve {
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 @@ public class Resolve {
@Override
public void visitConditional(JCConditional tree) {
scan(tree.truepart);
scan(tree.falsepart);
scan(asExpr(tree.truepart));
scan(asExpr(tree.falsepart));
}
@Override
@ -1179,6 +1184,11 @@ public class Resolve {
}
}
@Override
public void visitParens(JCParens tree) {
scan(asExpr(tree.expr));
}
@Override
public void visitLambda(JCLambda tree) {
Type desc_t = types.findDescriptorType(t);
@ -1214,7 +1224,7 @@ public class Resolve {
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 @@ public class Resolve {
@Override
public void visitReturn(JCReturn tree) {
if (tree.expr != null) {
buffer.append(tree.expr);
buffer.append(asExpr(tree.expr));
}
}
};
@ -1230,6 +1240,16 @@ public class Resolve {
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;
}
}
}

View File

@ -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

View File

@ -1,3 +1,3 @@
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))}
T8019480.java:21:46: compiler.err.report.access: clone(), protected, java.lang.Object
2 errors

View File

@ -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();
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}

View File

@ -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 ""; }
}

View File

@ -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<>())))))))))))))))))));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}