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:
parent
379f2540aa
commit
981c6dc298
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
82
langtools/test/tools/javac/lambda/speculative/InferWeak.java
Normal file
82
langtools/test/tools/javac/lambda/speculative/InferWeak.java
Normal 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; }
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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 ""; }
|
||||
}
|
62
langtools/test/tools/javac/lambda/speculative/T8055984.java
Normal file
62
langtools/test/tools/javac/lambda/speculative/T8055984.java
Normal 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<>())))))))))))))))))));
|
||||
}
|
||||
}
|
||||
|
77
langtools/test/tools/javac/lambda/speculative/T8077247.java
Normal file
77
langtools/test/tools/javac/lambda/speculative/T8077247.java
Normal 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;
|
||||
}
|
||||
}
|
50
langtools/test/tools/javac/lambda/speculative/T8078093.java
Normal file
50
langtools/test/tools/javac/lambda/speculative/T8078093.java
Normal 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user