mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-19 07:29:08 +00:00
7177387: Add target-typing support in method context
Add support for deferred types and speculative attribution Reviewed-by: jjg, dlsmith
This commit is contained in:
parent
ed2bca8140
commit
c0e2ed86c1
@ -30,6 +30,10 @@ import java.util.Locale;
|
||||
import com.sun.tools.javac.api.Messages;
|
||||
import com.sun.tools.javac.code.Type.*;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.comp.DeferredAttr.DeferredType;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.Pretty;
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
|
||||
@ -50,6 +54,11 @@ public abstract class Printer implements Type.Visitor<String, Locale>, Symbol.Vi
|
||||
|
||||
List<Type> seenCaptured = List.nil();
|
||||
static final int PRIME = 997; // largest prime less than 1000
|
||||
boolean raw;
|
||||
|
||||
protected Printer(boolean raw) {
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be overriden in order to provide proper i18n support.
|
||||
@ -78,7 +87,7 @@ public abstract class Printer implements Type.Visitor<String, Locale>, Symbol.Vi
|
||||
* @return printer visitor instance
|
||||
*/
|
||||
public static Printer createStandardPrinter(final Messages messages) {
|
||||
return new Printer() {
|
||||
return new Printer(false) {
|
||||
@Override
|
||||
protected String localize(Locale locale, String key, Object... args) {
|
||||
return messages.getLocalizedString(locale, key, args);
|
||||
@ -165,6 +174,34 @@ public abstract class Printer implements Type.Visitor<String, Locale>, Symbol.Vi
|
||||
return "<" + visitTypes(t.tvars, locale) + ">" + visit(t.qtype, locale);
|
||||
}
|
||||
|
||||
public String visitDeferredType(DeferredType t, Locale locale) {
|
||||
return raw ? localize(locale, getDeferredKey(t.tree)) :
|
||||
deferredTypeTree2String(t.tree);
|
||||
}
|
||||
//where
|
||||
private String deferredTypeTree2String(JCTree tree) {
|
||||
switch(tree.getTag()) {
|
||||
case PARENS:
|
||||
return deferredTypeTree2String(((JCTree.JCParens)tree).expr);
|
||||
case CONDEXPR:
|
||||
return Pretty.toSimpleString(tree, 15);
|
||||
default:
|
||||
Assert.error("unexpected tree kind " + tree.getKind());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private String getDeferredKey (JCTree tree) {
|
||||
switch (tree.getTag()) {
|
||||
case PARENS:
|
||||
return getDeferredKey(((JCTree.JCParens)tree).expr);
|
||||
case CONDEXPR:
|
||||
return "compiler.misc.type.conditional";
|
||||
default:
|
||||
Assert.error("unexpected tree kind " + tree.getKind());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitUndetVar(UndetVar t, Locale locale) {
|
||||
if (t.inst != null) {
|
||||
@ -228,10 +265,14 @@ public abstract class Printer implements Type.Visitor<String, Locale>, Symbol.Vi
|
||||
}
|
||||
|
||||
public String visitType(Type t, Locale locale) {
|
||||
String s = (t.tsym == null || t.tsym.name == null)
|
||||
? localize(locale, "compiler.misc.type.none")
|
||||
: t.tsym.name.toString();
|
||||
return s;
|
||||
if (t.tag == DEFERRED) {
|
||||
return visitDeferredType((DeferredType)t, locale);
|
||||
} else {
|
||||
String s = (t.tsym == null || t.tsym.name == null)
|
||||
? localize(locale, "compiler.misc.type.none")
|
||||
: t.tsym.name.toString();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -194,6 +194,9 @@ public enum Source {
|
||||
public boolean allowObjectToPrimitiveCast() {
|
||||
return compareTo(JDK1_7) >= 0;
|
||||
}
|
||||
public boolean allowPoly() {
|
||||
return compareTo(JDK1_8) >= 0;
|
||||
}
|
||||
public boolean allowLambda() {
|
||||
return compareTo(JDK1_8) >= 0;
|
||||
}
|
||||
|
||||
@ -183,6 +183,10 @@ public class Symtab {
|
||||
*/
|
||||
public final Name[] boxedName = new Name[TypeTags.TypeTagCount];
|
||||
|
||||
/** A set containing all operator names.
|
||||
*/
|
||||
public final Set<Name> operatorNames = new HashSet<Name>();
|
||||
|
||||
/** A hashtable containing the encountered top-level and member classes,
|
||||
* indexed by flat names. The table does not contain local classes.
|
||||
* It should be updated from the outside to reflect classes defined
|
||||
@ -244,7 +248,7 @@ public class Symtab {
|
||||
int opcode) {
|
||||
predefClass.members().enter(
|
||||
new OperatorSymbol(
|
||||
names.fromString(name),
|
||||
makeOperatorName(name),
|
||||
new MethodType(List.of(left, right), res,
|
||||
List.<Type>nil(), methodClass),
|
||||
opcode,
|
||||
@ -275,7 +279,7 @@ public class Symtab {
|
||||
Type res,
|
||||
int opcode) {
|
||||
OperatorSymbol sym =
|
||||
new OperatorSymbol(names.fromString(name),
|
||||
new OperatorSymbol(makeOperatorName(name),
|
||||
new MethodType(List.of(arg),
|
||||
res,
|
||||
List.<Type>nil(),
|
||||
@ -286,6 +290,16 @@ public class Symtab {
|
||||
return sym;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new operator name from corresponding String representation
|
||||
* and add the name to the set of known operator names.
|
||||
*/
|
||||
private Name makeOperatorName(String name) {
|
||||
Name opName = names.fromString(name);
|
||||
operatorNames.add(opName);
|
||||
return opName;
|
||||
}
|
||||
|
||||
/** Enter a class into symbol table.
|
||||
* @param The name of the class.
|
||||
*/
|
||||
|
||||
@ -75,6 +75,9 @@ public class Type implements PrimitiveType {
|
||||
/** Constant type: no type at all. */
|
||||
public static final JCNoType noType = new JCNoType(NONE);
|
||||
|
||||
/** Constant type: special type to be used during recovery of deferred expressions. */
|
||||
public static final JCNoType recoveryType = new JCNoType(NONE);
|
||||
|
||||
/** If this switch is turned on, the names of type variables
|
||||
* and anonymous classes are printed with hashcodes appended.
|
||||
*/
|
||||
|
||||
@ -102,9 +102,13 @@ public class TypeTags {
|
||||
*/
|
||||
public static final int FORALL = WILDCARD+1;
|
||||
|
||||
/** The tag of deferred expression types in method context
|
||||
*/
|
||||
public static final int DEFERRED = FORALL+1;
|
||||
|
||||
/** The tag of the bottom type <null>.
|
||||
*/
|
||||
public static final int BOT = FORALL+1;
|
||||
public static final int BOT = DEFERRED+1;
|
||||
|
||||
/** The tag of a missing type.
|
||||
*/
|
||||
|
||||
@ -3154,6 +3154,14 @@ public class Types {
|
||||
}
|
||||
return Type.noType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unboxed type if 't' is a boxed class, otherwise return 't' itself.
|
||||
*/
|
||||
public Type unboxedTypeOrType(Type t) {
|
||||
Type unboxedType = unboxedType(t);
|
||||
return unboxedType.tag == NONE ? t : unboxedType;
|
||||
}
|
||||
// </editor-fold>
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="Capture conversion">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -56,7 +56,7 @@ public class AttrContext {
|
||||
|
||||
/** Are arguments to current function applications boxed into an array for varargs?
|
||||
*/
|
||||
boolean varArgs = false;
|
||||
Resolve.MethodResolutionPhase pendingResolutionPhase = null;
|
||||
|
||||
/** A record of the lint/SuppressWarnings currently in effect
|
||||
*/
|
||||
@ -67,6 +67,11 @@ public class AttrContext {
|
||||
*/
|
||||
Symbol enclVar = null;
|
||||
|
||||
/** ResultInfo to be used for attributing 'return' statement expressions
|
||||
* (set by Attr.visitMethod and Attr.visitLambda)
|
||||
*/
|
||||
Attr.ResultInfo returnResult = null;
|
||||
|
||||
/** Duplicate this context, replacing scope field and copying all others.
|
||||
*/
|
||||
AttrContext dup(Scope scope) {
|
||||
@ -75,9 +80,10 @@ public class AttrContext {
|
||||
info.staticLevel = staticLevel;
|
||||
info.isSelfCall = isSelfCall;
|
||||
info.selectSuper = selectSuper;
|
||||
info.varArgs = varArgs;
|
||||
info.pendingResolutionPhase = pendingResolutionPhase;
|
||||
info.lint = lint;
|
||||
info.enclVar = enclVar;
|
||||
info.returnResult = returnResult;
|
||||
return info;
|
||||
}
|
||||
|
||||
@ -93,6 +99,11 @@ public class AttrContext {
|
||||
return scope.getElements();
|
||||
}
|
||||
|
||||
boolean lastResolveVarargs() {
|
||||
return pendingResolutionPhase != null &&
|
||||
pendingResolutionPhase.isVarargsRequired();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "AttrContext[" + scope.toString() + "]";
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ import com.sun.tools.javac.code.Lint;
|
||||
import com.sun.tools.javac.code.Lint.LintCategory;
|
||||
import com.sun.tools.javac.code.Type.*;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
|
||||
import com.sun.tools.javac.comp.Infer.InferenceContext;
|
||||
import com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener;
|
||||
|
||||
@ -68,6 +69,7 @@ public class Check {
|
||||
private final Resolve rs;
|
||||
private final Symtab syms;
|
||||
private final Enter enter;
|
||||
private final DeferredAttr deferredAttr;
|
||||
private final Infer infer;
|
||||
private final Types types;
|
||||
private final JCDiagnostic.Factory diags;
|
||||
@ -100,6 +102,7 @@ public class Check {
|
||||
rs = Resolve.instance(context);
|
||||
syms = Symtab.instance(context);
|
||||
enter = Enter.instance(context);
|
||||
deferredAttr = DeferredAttr.instance(context);
|
||||
infer = Infer.instance(context);
|
||||
this.types = Types.instance(context);
|
||||
diags = JCDiagnostic.Factory.instance(context);
|
||||
@ -433,6 +436,8 @@ public class Check {
|
||||
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req);
|
||||
|
||||
public Infer.InferenceContext inferenceContext();
|
||||
|
||||
public DeferredAttr.DeferredAttrContext deferredAttrContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -463,6 +468,10 @@ public class Check {
|
||||
public Infer.InferenceContext inferenceContext() {
|
||||
return enclosingContext.inferenceContext();
|
||||
}
|
||||
|
||||
public DeferredAttrContext deferredAttrContext() {
|
||||
return enclosingContext.deferredAttrContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -483,6 +492,10 @@ public class Check {
|
||||
public InferenceContext inferenceContext() {
|
||||
return infer.emptyContext;
|
||||
}
|
||||
|
||||
public DeferredAttrContext deferredAttrContext() {
|
||||
return deferredAttr.emptyDeferredAttrContext;
|
||||
}
|
||||
};
|
||||
|
||||
/** Check that a given type is assignable to a given proto-type.
|
||||
@ -817,6 +830,8 @@ public class Check {
|
||||
sym.owner == syms.enumSym)
|
||||
formals = formals.tail.tail;
|
||||
List<JCExpression> args = argtrees;
|
||||
DeferredAttr.DeferredTypeMap checkDeferredMap =
|
||||
deferredAttr.new DeferredTypeMap(DeferredAttr.AttrMode.CHECK, sym, env.info.pendingResolutionPhase);
|
||||
while (formals.head != last) {
|
||||
JCTree arg = args.head;
|
||||
Warner warn = convertWarner(arg.pos(), arg.type, formals.head);
|
||||
@ -835,7 +850,7 @@ public class Check {
|
||||
} else if ((sym.flags() & VARARGS) != 0 && allowVarargs) {
|
||||
// non-varargs call to varargs method
|
||||
Type varParam = owntype.getParameterTypes().last();
|
||||
Type lastArg = argtypes.last();
|
||||
Type lastArg = checkDeferredMap.apply(argtypes.last());
|
||||
if (types.isSubtypeUnchecked(lastArg, types.elemtype(varParam)) &&
|
||||
!types.isSameType(types.erasure(varParam), types.erasure(lastArg)))
|
||||
log.warning(argtrees.last().pos(), "inexact.non-varargs.call",
|
||||
@ -847,7 +862,7 @@ public class Check {
|
||||
kindName(sym),
|
||||
sym.name,
|
||||
rs.methodArguments(sym.type.getParameterTypes()),
|
||||
rs.methodArguments(argtypes),
|
||||
rs.methodArguments(Type.map(argtypes, checkDeferredMap)),
|
||||
kindName(sym.location()),
|
||||
sym.location());
|
||||
owntype = new MethodType(owntype.getParameterTypes(),
|
||||
|
||||
@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.tools.javac.code.*;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.util.*;
|
||||
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.Infer.InferenceContext;
|
||||
import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import static com.sun.tools.javac.code.TypeTags.*;
|
||||
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
|
||||
/**
|
||||
* This is an helper class that is used to perform deferred type-analysis.
|
||||
* Each time a poly expression occurs in argument position, javac attributes it
|
||||
* with a temporary 'deferred type' that is checked (possibly multiple times)
|
||||
* against an expected formal type.
|
||||
*
|
||||
* <p><b>This is NOT part of any supported API.
|
||||
* If you write code that depends on this, you do so at your own risk.
|
||||
* This code and its internal interfaces are subject to change or
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class DeferredAttr extends JCTree.Visitor {
|
||||
protected static final Context.Key<DeferredAttr> deferredAttrKey =
|
||||
new Context.Key<DeferredAttr>();
|
||||
|
||||
final Attr attr;
|
||||
final Check chk;
|
||||
final Enter enter;
|
||||
final Infer infer;
|
||||
final Log log;
|
||||
final Symtab syms;
|
||||
final TreeMaker make;
|
||||
final Types types;
|
||||
|
||||
public static DeferredAttr instance(Context context) {
|
||||
DeferredAttr instance = context.get(deferredAttrKey);
|
||||
if (instance == null)
|
||||
instance = new DeferredAttr(context);
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected DeferredAttr(Context context) {
|
||||
context.put(deferredAttrKey, this);
|
||||
attr = Attr.instance(context);
|
||||
chk = Check.instance(context);
|
||||
enter = Enter.instance(context);
|
||||
infer = Infer.instance(context);
|
||||
log = Log.instance(context);
|
||||
syms = Symtab.instance(context);
|
||||
make = TreeMaker.instance(context);
|
||||
types = Types.instance(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* This type represents a deferred type. A deferred type starts off with
|
||||
* no information on the underlying expression type. Such info needs to be
|
||||
* discovered through type-checking the deferred type against a target-type.
|
||||
* Every deferred type keeps a pointer to the AST node from which it originated.
|
||||
*/
|
||||
public class DeferredType extends Type {
|
||||
|
||||
public JCExpression tree;
|
||||
Env<AttrContext> env;
|
||||
AttrMode mode;
|
||||
SpeculativeCache speculativeCache;
|
||||
|
||||
DeferredType(JCExpression tree, Env<AttrContext> env) {
|
||||
super(DEFERRED, null);
|
||||
this.tree = tree;
|
||||
this.env = env.dup(tree, env.info.dup());
|
||||
this.speculativeCache = new SpeculativeCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* A speculative cache is used to keep track of all overload resolution rounds
|
||||
* that triggered speculative attribution on a given deferred type. Each entry
|
||||
* stores a pointer to the speculative tree and the resolution phase in which the entry
|
||||
* has been added.
|
||||
*/
|
||||
class SpeculativeCache {
|
||||
|
||||
private Map<Symbol, List<Entry>> cache =
|
||||
new WeakHashMap<Symbol, List<Entry>>();
|
||||
|
||||
class Entry {
|
||||
JCTree speculativeTree;
|
||||
Resolve.MethodResolutionPhase phase;
|
||||
|
||||
public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
|
||||
this.speculativeTree = speculativeTree;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
boolean matches(Resolve.MethodResolutionPhase phase) {
|
||||
return this.phase == phase;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a speculative cache entry as a fresh entry associated
|
||||
* with a new method (this maybe required to fixup speculative cache
|
||||
* misses after Resolve.access())
|
||||
*/
|
||||
void dupAllTo(Symbol from, Symbol to) {
|
||||
Assert.check(cache.get(to) == null);
|
||||
List<Entry> entries = cache.get(from);
|
||||
if (entries != null) {
|
||||
cache.put(to, entries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a speculative cache entry corresponding to given symbol
|
||||
* and resolution phase
|
||||
*/
|
||||
Entry get(Symbol msym, MethodResolutionPhase phase) {
|
||||
List<Entry> entries = cache.get(msym);
|
||||
if (entries == null) return null;
|
||||
for (Entry e : entries) {
|
||||
if (e.matches(phase)) return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a speculative cache entry corresponding to given symbol
|
||||
* and resolution phase
|
||||
*/
|
||||
void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
|
||||
List<Entry> entries = cache.get(msym);
|
||||
if (entries == null) {
|
||||
entries = List.nil();
|
||||
}
|
||||
cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type that has been computed during a speculative attribution round
|
||||
*/
|
||||
Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
|
||||
SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
|
||||
return e != null ? e.speculativeTree.type : Type.noType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a deferred type against a potential target-type. Depending on
|
||||
* the current attribution mode, a normal vs. speculative attribution
|
||||
* round is performed on the underlying AST node. There can be only one
|
||||
* speculative round for a given target method symbol; moreover, a normal
|
||||
* attribution round must follow one or more speculative rounds.
|
||||
*/
|
||||
Type check(ResultInfo resultInfo) {
|
||||
DeferredAttrContext deferredAttrContext =
|
||||
resultInfo.checkContext.deferredAttrContext();
|
||||
Assert.check(deferredAttrContext != emptyDeferredAttrContext);
|
||||
List<Type> stuckVars = stuckVars(tree, resultInfo);
|
||||
if (stuckVars.nonEmpty()) {
|
||||
deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
|
||||
return Type.noType;
|
||||
} else {
|
||||
try {
|
||||
switch (deferredAttrContext.mode) {
|
||||
case SPECULATIVE:
|
||||
Assert.check(mode == null ||
|
||||
(mode == AttrMode.SPECULATIVE &&
|
||||
speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).tag == NONE));
|
||||
JCTree speculativeTree = attribSpeculative(tree, env, resultInfo);
|
||||
speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
|
||||
return speculativeTree.type;
|
||||
case CHECK:
|
||||
Assert.check(mode == AttrMode.SPECULATIVE);
|
||||
return attr.attribTree(tree, env, resultInfo);
|
||||
}
|
||||
Assert.error();
|
||||
return null;
|
||||
} finally {
|
||||
mode = deferredAttrContext.mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The 'mode' in which the deferred type is to be type-checked
|
||||
*/
|
||||
public enum AttrMode {
|
||||
/**
|
||||
* A speculative type-checking round is used during overload resolution
|
||||
* mainly to generate constraints on inference variables. Side-effects
|
||||
* arising from type-checking the expression associated with the deferred
|
||||
* type are reversed after the speculative round finishes. This means the
|
||||
* expression tree will be left in a blank state.
|
||||
*/
|
||||
SPECULATIVE,
|
||||
/**
|
||||
* This is the plain type-checking mode. Produces side-effects on the underlying AST node
|
||||
*/
|
||||
CHECK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routine that performs speculative type-checking; the input AST node is
|
||||
* cloned (to avoid side-effects cause by Attr) and compiler state is
|
||||
* restored after type-checking. All diagnostics (but critical ones) are
|
||||
* disabled during speculative type-checking.
|
||||
*/
|
||||
JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
|
||||
JCTree newTree = new TreeCopier<Object>(make).copy(tree);
|
||||
Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
|
||||
speculativeEnv.info.scope.owner = env.info.scope.owner;
|
||||
Filter<JCDiagnostic> prevDeferDiagsFilter = log.deferredDiagFilter;
|
||||
Queue<JCDiagnostic> prevDeferredDiags = log.deferredDiagnostics;
|
||||
final JavaFileObject currentSource = log.currentSourceFile();
|
||||
try {
|
||||
log.deferredDiagnostics = new ListBuffer<JCDiagnostic>();
|
||||
log.deferredDiagFilter = new Filter<JCDiagnostic>() {
|
||||
public boolean accepts(JCDiagnostic t) {
|
||||
return t.getDiagnosticSource().getFile().equals(currentSource);
|
||||
}
|
||||
};
|
||||
attr.attribTree(newTree, speculativeEnv, resultInfo);
|
||||
unenterScanner.scan(newTree);
|
||||
return newTree;
|
||||
} catch (Abort ex) {
|
||||
//if some very bad condition occurred during deferred attribution
|
||||
//we should dump all errors before killing javac
|
||||
log.reportDeferredDiagnostics();
|
||||
throw ex;
|
||||
} finally {
|
||||
unenterScanner.scan(newTree);
|
||||
log.deferredDiagFilter = prevDeferDiagsFilter;
|
||||
log.deferredDiagnostics = prevDeferredDiags;
|
||||
}
|
||||
}
|
||||
//where
|
||||
protected TreeScanner unenterScanner = new TreeScanner() {
|
||||
@Override
|
||||
public void visitClassDef(JCClassDecl tree) {
|
||||
ClassSymbol csym = tree.sym;
|
||||
enter.typeEnvs.remove(csym);
|
||||
chk.compiled.remove(csym.flatname);
|
||||
syms.classes.remove(csym.flatname);
|
||||
super.visitClassDef(tree);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A deferred context is created on each method check. A deferred context is
|
||||
* used to keep track of information associated with the method check, such as
|
||||
* the symbol of the method being checked, the overload resolution phase,
|
||||
* the kind of attribution mode to be applied to deferred types and so forth.
|
||||
* As deferred types are processed (by the method check routine) stuck AST nodes
|
||||
* are added (as new deferred attribution nodes) to this context. The complete()
|
||||
* routine makes sure that all pending nodes are properly processed, by
|
||||
* progressively instantiating all inference variables on which one or more
|
||||
* deferred attribution node is stuck.
|
||||
*/
|
||||
class DeferredAttrContext {
|
||||
|
||||
/** attribution mode */
|
||||
final AttrMode mode;
|
||||
|
||||
/** symbol of the method being checked */
|
||||
final Symbol msym;
|
||||
|
||||
/** method resolution step */
|
||||
final Resolve.MethodResolutionPhase phase;
|
||||
|
||||
/** inference context */
|
||||
final InferenceContext inferenceContext;
|
||||
|
||||
/** list of deferred attribution nodes to be processed */
|
||||
ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
|
||||
|
||||
DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
|
||||
this.mode = mode;
|
||||
this.msym = msym;
|
||||
this.phase = phase;
|
||||
this.inferenceContext = inferenceContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
|
||||
* Nodes added this way act as 'roots' for the out-of-order method checking process.
|
||||
*/
|
||||
void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
|
||||
deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrementally process all nodes, by skipping 'stuck' nodes and attributing
|
||||
* 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
|
||||
* some inference variable might get eagerly instantiated so that all nodes
|
||||
* can be type-checked.
|
||||
*/
|
||||
void complete() {
|
||||
while (!deferredAttrNodes.isEmpty()) {
|
||||
Set<Type> stuckVars = new HashSet<Type>();
|
||||
boolean progress = false;
|
||||
//scan a defensive copy of the node list - this is because a deferred
|
||||
//attribution round can add new nodes to the list
|
||||
for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
|
||||
if (!deferredAttrNode.isStuck()) {
|
||||
deferredAttrNode.process();
|
||||
deferredAttrNodes.remove(deferredAttrNode);
|
||||
progress = true;
|
||||
} else {
|
||||
stuckVars.addAll(deferredAttrNode.stuckVars);
|
||||
}
|
||||
}
|
||||
if (!progress) {
|
||||
//remove all variables that have already been instantiated
|
||||
//from the list of stuck variables
|
||||
inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
|
||||
inferenceContext.notifyChange(types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a deferred attribution node. It keeps track of
|
||||
* a deferred type, along with the expected target type information.
|
||||
*/
|
||||
class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
|
||||
|
||||
/** underlying deferred type */
|
||||
DeferredType dt;
|
||||
|
||||
/** underlying target type information */
|
||||
ResultInfo resultInfo;
|
||||
|
||||
/** list of uninferred inference variables causing this node to be stuck */
|
||||
List<Type> stuckVars;
|
||||
|
||||
DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
|
||||
this.dt = dt;
|
||||
this.resultInfo = resultInfo;
|
||||
this.stuckVars = stuckVars;
|
||||
if (!stuckVars.isEmpty()) {
|
||||
resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void typesInferred(InferenceContext inferenceContext) {
|
||||
stuckVars = List.nil();
|
||||
resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
|
||||
}
|
||||
|
||||
/**
|
||||
* is this node stuck?
|
||||
*/
|
||||
boolean isStuck() {
|
||||
return stuckVars.nonEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a deferred attribution node.
|
||||
* Invariant: a stuck node cannot be processed.
|
||||
*/
|
||||
void process() {
|
||||
if (isStuck()) {
|
||||
throw new IllegalStateException("Cannot process a stuck deferred node");
|
||||
}
|
||||
dt.check(resultInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** an empty deferred attribution context - all methods throw exceptions */
|
||||
final DeferredAttrContext emptyDeferredAttrContext =
|
||||
new DeferredAttrContext(null, null, null, null) {
|
||||
@Override
|
||||
void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
|
||||
Assert.error("Empty deferred context!");
|
||||
}
|
||||
@Override
|
||||
void complete() {
|
||||
Assert.error("Empty deferred context!");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Map a list of types possibly containing one or more deferred types
|
||||
* into a list of ordinary types. Each deferred type D is mapped into a type T,
|
||||
* where T is computed by retrieving the type that has already been
|
||||
* computed for D during a previous deferred attribution round of the given kind.
|
||||
*/
|
||||
class DeferredTypeMap extends Type.Mapping {
|
||||
|
||||
DeferredAttrContext deferredAttrContext;
|
||||
|
||||
protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
|
||||
super(String.format("deferredTypeMap[%s]", mode));
|
||||
this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
|
||||
}
|
||||
|
||||
protected boolean validState(DeferredType dt) {
|
||||
return dt.mode != null &&
|
||||
deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type apply(Type t) {
|
||||
if (t.tag != DEFERRED) {
|
||||
return t.map(this);
|
||||
} else {
|
||||
DeferredType dt = (DeferredType)t;
|
||||
Assert.check(validState(dt));
|
||||
return typeOf(dt);
|
||||
}
|
||||
}
|
||||
|
||||
protected Type typeOf(DeferredType dt) {
|
||||
switch (deferredAttrContext.mode) {
|
||||
case CHECK:
|
||||
return dt.tree.type == null ? Type.noType : dt.tree.type;
|
||||
case SPECULATIVE:
|
||||
return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
|
||||
}
|
||||
Assert.error();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized recovery deferred mapping.
|
||||
* Each deferred type D is mapped into a type T, where T is computed either by
|
||||
* (i) retrieving the type that has already been computed for D during a previous
|
||||
* attribution round (as before), or (ii) by synthesizing a new type R for D
|
||||
* (the latter step is useful in a recovery scenario).
|
||||
*/
|
||||
public class RecoveryDeferredTypeMap extends DeferredTypeMap {
|
||||
|
||||
public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
|
||||
super(mode, msym, phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Type typeOf(DeferredType dt) {
|
||||
Type owntype = super.typeOf(dt);
|
||||
return owntype.tag == NONE ?
|
||||
recover(dt) : owntype;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean validState(DeferredType dt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a type for a deferred type that hasn't been previously
|
||||
* reduced to an ordinary type. Functional deferred types and conditionals
|
||||
* are mapped to themselves, in order to have a richer diagnostic
|
||||
* representation. Remaining deferred types are attributed using
|
||||
* a default expected type (j.l.Object).
|
||||
*/
|
||||
private Type recover(DeferredType dt) {
|
||||
dt.check(new RecoveryInfo());
|
||||
switch (TreeInfo.skipParens(dt.tree).getTag()) {
|
||||
case LAMBDA:
|
||||
case REFERENCE:
|
||||
case CONDEXPR:
|
||||
//propagate those deferred types to the
|
||||
//diagnostic formatter
|
||||
return dt;
|
||||
default:
|
||||
return super.apply(dt);
|
||||
}
|
||||
}
|
||||
|
||||
class RecoveryInfo extends ResultInfo {
|
||||
|
||||
public RecoveryInfo() {
|
||||
attr.super(Kinds.VAL, Type.recoveryType, new Check.NestedCheckContext(chk.basicHandler) {
|
||||
@Override
|
||||
public DeferredAttrContext deferredAttrContext() {
|
||||
return deferredAttrContext;
|
||||
}
|
||||
@Override
|
||||
public boolean compatible(Type found, Type req, Warner warn) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public void report(DiagnosticPosition pos, JCDiagnostic details) {
|
||||
//do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Type check(DiagnosticPosition pos, Type found) {
|
||||
return chk.checkNonVoid(pos, super.check(pos, found));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of inference variables that need to be inferred before
|
||||
* an AST node can be type-checked
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
List<Type> stuckVars(JCExpression tree, ResultInfo resultInfo) {
|
||||
switch (tree.getTag()) {
|
||||
case LAMBDA:
|
||||
case REFERENCE:
|
||||
Assert.error("not supported yet");
|
||||
default:
|
||||
return List.nil();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,7 @@ import com.sun.tools.javac.code.*;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.code.Type.*;
|
||||
import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
|
||||
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
|
||||
import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
|
||||
import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
@ -62,6 +63,7 @@ public class Infer {
|
||||
Types types;
|
||||
Check chk;
|
||||
Resolve rs;
|
||||
DeferredAttr deferredAttr;
|
||||
Log log;
|
||||
JCDiagnostic.Factory diags;
|
||||
|
||||
@ -77,6 +79,7 @@ public class Infer {
|
||||
syms = Symtab.instance(context);
|
||||
types = Types.instance(context);
|
||||
rs = Resolve.instance(context);
|
||||
deferredAttr = DeferredAttr.instance(context);
|
||||
log = Log.instance(context);
|
||||
chk = Check.instance(context);
|
||||
diags = JCDiagnostic.Factory.instance(context);
|
||||
@ -187,7 +190,7 @@ public class Infer {
|
||||
Attr.ResultInfo resultInfo,
|
||||
Warner warn) throws InferenceException {
|
||||
Type to = resultInfo.pt;
|
||||
if (to.tag == NONE) {
|
||||
if (to.tag == NONE || resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
|
||||
to = mtype.getReturnType().tag <= VOID ?
|
||||
mtype.getReturnType() : syms.objectType;
|
||||
}
|
||||
@ -268,14 +271,16 @@ public class Infer {
|
||||
List<Type> argtypes,
|
||||
boolean allowBoxing,
|
||||
boolean useVarargs,
|
||||
Resolve.MethodResolutionContext resolveContext,
|
||||
Warner warn) throws InferenceException {
|
||||
//-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
|
||||
final InferenceContext inferenceContext = new InferenceContext(tvars, this);
|
||||
inferenceException.clear();
|
||||
|
||||
try {
|
||||
rs.checkRawArgumentsAcceptable(env, inferenceContext, argtypes, mt.getParameterTypes(),
|
||||
allowBoxing, useVarargs, warn, new InferenceCheckHandler(inferenceContext));
|
||||
rs.checkRawArgumentsAcceptable(env, msym, resolveContext.attrMode(), inferenceContext,
|
||||
argtypes, mt.getParameterTypes(), allowBoxing, useVarargs, warn,
|
||||
new InferenceCheckHandler(inferenceContext));
|
||||
|
||||
// minimize as yet undetermined type variables
|
||||
for (Type t : inferenceContext.undetvars) {
|
||||
@ -469,6 +474,7 @@ public class Infer {
|
||||
*/
|
||||
Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
|
||||
MethodSymbol spMethod, // sig. poly. method or null if none
|
||||
Resolve.MethodResolutionContext resolveContext,
|
||||
List<Type> argtypes) {
|
||||
final Type restype;
|
||||
|
||||
@ -498,7 +504,7 @@ public class Infer {
|
||||
restype = syms.objectType;
|
||||
}
|
||||
|
||||
List<Type> paramtypes = Type.map(argtypes, implicitArgType);
|
||||
List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
|
||||
List<Type> exType = spMethod != null ?
|
||||
spMethod.getThrownTypes() :
|
||||
List.of(syms.throwableType); // make it throw all exceptions
|
||||
@ -510,16 +516,21 @@ public class Infer {
|
||||
return mtype;
|
||||
}
|
||||
//where
|
||||
Mapping implicitArgType = new Mapping ("implicitArgType") {
|
||||
public Type apply(Type t) {
|
||||
t = types.erasure(t);
|
||||
if (t.tag == BOT)
|
||||
// nulls type as the marker type Null (which has no instances)
|
||||
// infer as java.lang.Void for now
|
||||
t = types.boxedClass(syms.voidType).type;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
|
||||
|
||||
public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
|
||||
deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
|
||||
}
|
||||
|
||||
public Type apply(Type t) {
|
||||
t = types.erasure(super.apply(t));
|
||||
if (t.tag == BOT)
|
||||
// nulls type as the marker type Null (which has no instances)
|
||||
// infer as java.lang.Void for now
|
||||
t = types.boxedClass(syms.voidType).type;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping that turns inference variables into undet vars
|
||||
@ -708,6 +719,22 @@ public class Infer {
|
||||
throw thrownEx;
|
||||
}
|
||||
}
|
||||
|
||||
void solveAny(List<Type> varsToSolve, Types types, Infer infer) {
|
||||
boolean progress = false;
|
||||
for (Type t : varsToSolve) {
|
||||
UndetVar uv = (UndetVar)asFree(t, types);
|
||||
if (uv.inst == null) {
|
||||
infer.minimizeInst(uv, Warner.noWarnings);
|
||||
if (uv.inst != null) {
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!progress) {
|
||||
throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this);
|
||||
|
||||
@ -1998,7 +1998,7 @@ public class Lower extends TreeTranslator {
|
||||
// replace with <BoxedClass>.TYPE
|
||||
ClassSymbol c = types.boxedClass(type);
|
||||
Symbol typeSym =
|
||||
rs.access(
|
||||
rs.accessBase(
|
||||
rs.findIdentInType(attrEnv, c.type, names.TYPE, VAR),
|
||||
pos, c.type, names.TYPE, true);
|
||||
if (typeSym.kind == VAR)
|
||||
|
||||
@ -604,6 +604,10 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
env.dup(tree, env.info.dup(env.info.scope.dupUnshared()));
|
||||
localEnv.enclMethod = tree;
|
||||
localEnv.info.scope.owner = tree.sym;
|
||||
if (tree.sym.type != null) {
|
||||
//when this is called in the enter stage, there's no type to be set
|
||||
localEnv.info.returnResult = attr.new ResultInfo(VAL, tree.sym.type.getReturnType());
|
||||
}
|
||||
if ((tree.mods.flags & STATIC) != 0) localEnv.info.staticLevel++;
|
||||
return localEnv;
|
||||
}
|
||||
|
||||
@ -31,6 +31,9 @@ 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.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.Infer.InferenceContext;
|
||||
import com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener;
|
||||
import com.sun.tools.javac.comp.Resolve.MethodResolutionContext.Candidate;
|
||||
@ -42,15 +45,12 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.ElementVisitor;
|
||||
|
||||
@ -77,6 +77,7 @@ public class Resolve {
|
||||
Log log;
|
||||
Symtab syms;
|
||||
Attr attr;
|
||||
DeferredAttr deferredAttr;
|
||||
Check chk;
|
||||
Infer infer;
|
||||
ClassReader reader;
|
||||
@ -109,6 +110,7 @@ public class Resolve {
|
||||
names = Names.instance(context);
|
||||
log = Log.instance(context);
|
||||
attr = Attr.instance(context);
|
||||
deferredAttr = DeferredAttr.instance(context);
|
||||
chk = Check.instance(context);
|
||||
infer = Infer.instance(context);
|
||||
reader = ClassReader.instance(context);
|
||||
@ -219,9 +221,12 @@ public class Resolve {
|
||||
}
|
||||
}
|
||||
String key = success ? "verbose.resolve.multi" : "verbose.resolve.multi.1";
|
||||
List<Type> argtypes2 = Type.map(argtypes,
|
||||
deferredAttr.new RecoveryDeferredTypeMap(AttrMode.SPECULATIVE, bestSoFar, currentResolutionContext.step));
|
||||
JCDiagnostic main = diags.note(log.currentSource(), dpos, key, name,
|
||||
site.tsym, mostSpecificPos, currentResolutionContext.step,
|
||||
methodArguments(argtypes), methodArguments(typeargtypes));
|
||||
methodArguments(argtypes2),
|
||||
methodArguments(typeargtypes));
|
||||
JCDiagnostic d = new JCDiagnostic.MultilineDiagnostic(main, subDiags.toList());
|
||||
log.report(d);
|
||||
}
|
||||
@ -501,13 +506,34 @@ public class Resolve {
|
||||
argtypes,
|
||||
allowBoxing,
|
||||
useVarargs,
|
||||
currentResolutionContext,
|
||||
warn);
|
||||
|
||||
checkRawArgumentsAcceptable(env, argtypes, mt.getParameterTypes(),
|
||||
checkRawArgumentsAcceptable(env, m, argtypes, mt.getParameterTypes(),
|
||||
allowBoxing, useVarargs, warn);
|
||||
return mt;
|
||||
}
|
||||
|
||||
Type checkMethod(Env<AttrContext> env,
|
||||
Type site,
|
||||
Symbol m,
|
||||
ResultInfo resultInfo,
|
||||
List<Type> argtypes,
|
||||
List<Type> typeargtypes,
|
||||
Warner warn) {
|
||||
MethodResolutionContext prevContext = currentResolutionContext;
|
||||
try {
|
||||
currentResolutionContext = new MethodResolutionContext();
|
||||
currentResolutionContext.attrMode = DeferredAttr.AttrMode.CHECK;
|
||||
MethodResolutionPhase step = currentResolutionContext.step = env.info.pendingResolutionPhase;
|
||||
return rawInstantiate(env, site, m, resultInfo, argtypes, typeargtypes,
|
||||
step.isBoxingRequired(), step.isVarargsRequired(), warn);
|
||||
}
|
||||
finally {
|
||||
currentResolutionContext = prevContext;
|
||||
}
|
||||
}
|
||||
|
||||
/** Same but returns null instead throwing a NoInstanceException
|
||||
*/
|
||||
Type instantiate(Env<AttrContext> env,
|
||||
@ -530,13 +556,14 @@ public class Resolve {
|
||||
/** Check if a parameter list accepts a list of args.
|
||||
*/
|
||||
boolean argumentsAcceptable(Env<AttrContext> env,
|
||||
Symbol msym,
|
||||
List<Type> argtypes,
|
||||
List<Type> formals,
|
||||
boolean allowBoxing,
|
||||
boolean useVarargs,
|
||||
Warner warn) {
|
||||
try {
|
||||
checkRawArgumentsAcceptable(env, argtypes, formals, allowBoxing, useVarargs, warn);
|
||||
checkRawArgumentsAcceptable(env, msym, argtypes, formals, allowBoxing, useVarargs, warn);
|
||||
return true;
|
||||
} catch (InapplicableMethodException ex) {
|
||||
return false;
|
||||
@ -583,12 +610,13 @@ public class Resolve {
|
||||
};
|
||||
|
||||
void checkRawArgumentsAcceptable(Env<AttrContext> env,
|
||||
Symbol msym,
|
||||
List<Type> argtypes,
|
||||
List<Type> formals,
|
||||
boolean allowBoxing,
|
||||
boolean useVarargs,
|
||||
Warner warn) {
|
||||
checkRawArgumentsAcceptable(env, infer.emptyContext, argtypes, formals,
|
||||
checkRawArgumentsAcceptable(env, msym, currentResolutionContext.attrMode(), infer.emptyContext, argtypes, formals,
|
||||
allowBoxing, useVarargs, warn, resolveHandler);
|
||||
}
|
||||
|
||||
@ -598,35 +626,41 @@ public class Resolve {
|
||||
* compatible (by method invocation conversion) with the types in F.
|
||||
*
|
||||
* Since this routine is shared between overload resolution and method
|
||||
* type-inference, it is crucial that actual types are converted to the
|
||||
* corresponding 'undet' form (i.e. where inference variables are replaced
|
||||
* with undetvars) so that constraints can be propagated and collected.
|
||||
* type-inference, a (possibly empty) inference context is used to convert
|
||||
* formal types to the corresponding 'undet' form ahead of a compatibility
|
||||
* check so that constraints can be propagated and collected.
|
||||
*
|
||||
* Moreover, if one or more types in A is a poly type, this routine calls
|
||||
* Infer.instantiateArg in order to complete the poly type (this might involve
|
||||
* deferred attribution).
|
||||
* Moreover, if one or more types in A is a deferred type, this routine uses
|
||||
* DeferredAttr in order to perform deferred attribution. If one or more actual
|
||||
* deferred types are stuck, they are placed in a queue and revisited later
|
||||
* after the remainder of the arguments have been seen. If this is not sufficient
|
||||
* to 'unstuck' the argument, a cyclic inference error is called out.
|
||||
*
|
||||
* A method check handler (see above) is used in order to report errors.
|
||||
*/
|
||||
void checkRawArgumentsAcceptable(final Env<AttrContext> env,
|
||||
Symbol msym,
|
||||
DeferredAttr.AttrMode mode,
|
||||
final Infer.InferenceContext inferenceContext,
|
||||
List<Type> argtypes,
|
||||
List<Type> formals,
|
||||
boolean allowBoxing,
|
||||
boolean useVarargs,
|
||||
Warner warn,
|
||||
MethodCheckHandler handler) {
|
||||
final MethodCheckHandler handler) {
|
||||
Type varargsFormal = useVarargs ? formals.last() : null;
|
||||
ListBuffer<Type> checkedArgs = ListBuffer.lb();
|
||||
|
||||
if (varargsFormal == null &&
|
||||
argtypes.size() != formals.size()) {
|
||||
throw handler.arityMismatch(); // not enough args
|
||||
}
|
||||
|
||||
DeferredAttr.DeferredAttrContext deferredAttrContext =
|
||||
deferredAttr.new DeferredAttrContext(mode, msym, currentResolutionContext.step, inferenceContext);
|
||||
|
||||
while (argtypes.nonEmpty() && formals.head != varargsFormal) {
|
||||
ResultInfo resultInfo = methodCheckResult(formals.head, allowBoxing, false, inferenceContext, handler, warn);
|
||||
checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head));
|
||||
ResultInfo mresult = methodCheckResult(formals.head, allowBoxing, false, inferenceContext, deferredAttrContext, handler, warn);
|
||||
mresult.check(null, argtypes.head);
|
||||
argtypes = argtypes.tail;
|
||||
formals = formals.tail;
|
||||
}
|
||||
@ -638,15 +672,17 @@ public class Resolve {
|
||||
if (useVarargs) {
|
||||
//note: if applicability check is triggered by most specific test,
|
||||
//the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
|
||||
Type elt = types.elemtype(varargsFormal);
|
||||
final Type elt = types.elemtype(varargsFormal);
|
||||
ResultInfo mresult = methodCheckResult(elt, allowBoxing, true, inferenceContext, deferredAttrContext, handler, warn);
|
||||
while (argtypes.nonEmpty()) {
|
||||
ResultInfo resultInfo = methodCheckResult(elt, allowBoxing, true, inferenceContext, handler, warn);
|
||||
checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head));
|
||||
mresult.check(null, argtypes.head);
|
||||
argtypes = argtypes.tail;
|
||||
}
|
||||
//check varargs element type accessibility
|
||||
varargsAccessible(env, elt, handler, inferenceContext);
|
||||
}
|
||||
|
||||
deferredAttrContext.complete();
|
||||
}
|
||||
|
||||
void varargsAccessible(final Env<AttrContext> env, final Type t, final Resolve.MethodCheckHandler handler, final InferenceContext inferenceContext) {
|
||||
@ -674,12 +710,15 @@ public class Resolve {
|
||||
MethodCheckHandler handler;
|
||||
boolean useVarargs;
|
||||
Infer.InferenceContext inferenceContext;
|
||||
DeferredAttrContext deferredAttrContext;
|
||||
Warner rsWarner;
|
||||
|
||||
public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) {
|
||||
public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs,
|
||||
Infer.InferenceContext inferenceContext, DeferredAttrContext deferredAttrContext, Warner rsWarner) {
|
||||
this.handler = handler;
|
||||
this.useVarargs = useVarargs;
|
||||
this.inferenceContext = inferenceContext;
|
||||
this.deferredAttrContext = deferredAttrContext;
|
||||
this.rsWarner = rsWarner;
|
||||
}
|
||||
|
||||
@ -694,6 +733,10 @@ public class Resolve {
|
||||
public InferenceContext inferenceContext() {
|
||||
return inferenceContext;
|
||||
}
|
||||
|
||||
public DeferredAttrContext deferredAttrContext() {
|
||||
return deferredAttrContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -702,8 +745,9 @@ public class Resolve {
|
||||
*/
|
||||
class StrictMethodContext extends MethodCheckContext {
|
||||
|
||||
public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) {
|
||||
super(handler, useVarargs, inferenceContext, rsWarner);
|
||||
public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs,
|
||||
Infer.InferenceContext inferenceContext, DeferredAttrContext deferredAttrContext, Warner rsWarner) {
|
||||
super(handler, useVarargs, inferenceContext, deferredAttrContext, rsWarner);
|
||||
}
|
||||
|
||||
public boolean compatible(Type found, Type req, Warner warn) {
|
||||
@ -717,8 +761,9 @@ public class Resolve {
|
||||
*/
|
||||
class LooseMethodContext extends MethodCheckContext {
|
||||
|
||||
public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) {
|
||||
super(handler, useVarargs, inferenceContext, rsWarner);
|
||||
public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs,
|
||||
Infer.InferenceContext inferenceContext, DeferredAttrContext deferredAttrContext, Warner rsWarner) {
|
||||
super(handler, useVarargs, inferenceContext, deferredAttrContext, rsWarner);
|
||||
}
|
||||
|
||||
public boolean compatible(Type found, Type req, Warner warn) {
|
||||
@ -730,16 +775,37 @@ public class Resolve {
|
||||
* Create a method check context to be used during method applicability check
|
||||
*/
|
||||
ResultInfo methodCheckResult(Type to, boolean allowBoxing, boolean useVarargs,
|
||||
Infer.InferenceContext inferenceContext, MethodCheckHandler methodHandler, Warner rsWarner) {
|
||||
Infer.InferenceContext inferenceContext, DeferredAttr.DeferredAttrContext deferredAttrContext,
|
||||
MethodCheckHandler methodHandler, Warner rsWarner) {
|
||||
MethodCheckContext checkContext = allowBoxing ?
|
||||
new LooseMethodContext(methodHandler, useVarargs, inferenceContext, rsWarner) :
|
||||
new StrictMethodContext(methodHandler, useVarargs, inferenceContext, rsWarner);
|
||||
return attr.new ResultInfo(VAL, to, checkContext) {
|
||||
@Override
|
||||
protected Type check(DiagnosticPosition pos, Type found) {
|
||||
new LooseMethodContext(methodHandler, useVarargs, inferenceContext, deferredAttrContext, rsWarner) :
|
||||
new StrictMethodContext(methodHandler, useVarargs, inferenceContext, deferredAttrContext, rsWarner);
|
||||
return new MethodResultInfo(to, checkContext, deferredAttrContext);
|
||||
}
|
||||
|
||||
class MethodResultInfo extends ResultInfo {
|
||||
|
||||
DeferredAttr.DeferredAttrContext deferredAttrContext;
|
||||
|
||||
public MethodResultInfo(Type pt, MethodCheckContext checkContext, DeferredAttr.DeferredAttrContext deferredAttrContext) {
|
||||
attr.super(VAL, pt, checkContext);
|
||||
this.deferredAttrContext = deferredAttrContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Type check(DiagnosticPosition pos, Type found) {
|
||||
if (found.tag == DEFERRED) {
|
||||
DeferredType dt = (DeferredType)found;
|
||||
return dt.check(this);
|
||||
} else {
|
||||
return super.check(pos, chk.checkNonVoid(pos, types.capture(types.upperBound(found.baseType()))));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodResultInfo dup(Type newPt) {
|
||||
return new MethodResultInfo(newPt, (MethodCheckContext)checkContext, deferredAttrContext);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InapplicableMethodException extends RuntimeException {
|
||||
@ -1614,68 +1680,133 @@ public class Resolve {
|
||||
*
|
||||
* @param sym The symbol that was found, or a ResolveError.
|
||||
* @param pos The position to use for error reporting.
|
||||
* @param location The symbol the served as a context for this lookup
|
||||
* @param site The original type from where the selection took place.
|
||||
* @param name The symbol's name.
|
||||
* @param qualified Did we get here through a qualified expression resolution?
|
||||
* @param argtypes The invocation's value arguments,
|
||||
* if we looked for a method.
|
||||
* @param typeargtypes The invocation's type arguments,
|
||||
* if we looked for a method.
|
||||
* @param logResolveHelper helper class used to log resolve errors
|
||||
*/
|
||||
Symbol access(Symbol sym,
|
||||
Symbol accessInternal(Symbol sym,
|
||||
DiagnosticPosition pos,
|
||||
Symbol location,
|
||||
Type site,
|
||||
Name name,
|
||||
boolean qualified,
|
||||
List<Type> argtypes,
|
||||
List<Type> typeargtypes) {
|
||||
List<Type> typeargtypes,
|
||||
LogResolveHelper logResolveHelper) {
|
||||
if (sym.kind >= AMBIGUOUS) {
|
||||
ResolveError errSym = (ResolveError)sym;
|
||||
if (!site.isErroneous() &&
|
||||
!Type.isErroneous(argtypes) &&
|
||||
(typeargtypes==null || !Type.isErroneous(typeargtypes)))
|
||||
logResolveError(errSym, pos, location, site, name, argtypes, typeargtypes);
|
||||
sym = errSym.access(name, qualified ? site.tsym : syms.noSymbol);
|
||||
argtypes = logResolveHelper.getArgumentTypes(errSym, sym, name, argtypes);
|
||||
if (logResolveHelper.resolveDiagnosticNeeded(site, argtypes, typeargtypes)) {
|
||||
logResolveError(errSym, pos, location, site, name, argtypes, typeargtypes);
|
||||
}
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
/** Same as original access(), but without location.
|
||||
/**
|
||||
* Variant of the generalized access routine, to be used for generating method
|
||||
* resolution diagnostics
|
||||
*/
|
||||
Symbol access(Symbol sym,
|
||||
Symbol accessMethod(Symbol sym,
|
||||
DiagnosticPosition pos,
|
||||
Symbol location,
|
||||
Type site,
|
||||
Name name,
|
||||
boolean qualified,
|
||||
List<Type> argtypes,
|
||||
List<Type> typeargtypes) {
|
||||
return accessInternal(sym, pos, location, site, name, qualified, argtypes, typeargtypes, methodLogResolveHelper);
|
||||
}
|
||||
|
||||
/** Same as original accessMethod(), but without location.
|
||||
*/
|
||||
Symbol accessMethod(Symbol sym,
|
||||
DiagnosticPosition pos,
|
||||
Type site,
|
||||
Name name,
|
||||
boolean qualified,
|
||||
List<Type> argtypes,
|
||||
List<Type> typeargtypes) {
|
||||
return access(sym, pos, site.tsym, site, name, qualified, argtypes, typeargtypes);
|
||||
return accessMethod(sym, pos, site.tsym, site, name, qualified, argtypes, typeargtypes);
|
||||
}
|
||||
|
||||
/** Same as original access(), but without type arguments and arguments.
|
||||
/**
|
||||
* Variant of the generalized access routine, to be used for generating variable,
|
||||
* type resolution diagnostics
|
||||
*/
|
||||
Symbol access(Symbol sym,
|
||||
Symbol accessBase(Symbol sym,
|
||||
DiagnosticPosition pos,
|
||||
Symbol location,
|
||||
Type site,
|
||||
Name name,
|
||||
boolean qualified) {
|
||||
if (sym.kind >= AMBIGUOUS)
|
||||
return access(sym, pos, location, site, name, qualified, List.<Type>nil(), null);
|
||||
else
|
||||
return sym;
|
||||
return accessInternal(sym, pos, location, site, name, qualified, List.<Type>nil(), null, basicLogResolveHelper);
|
||||
}
|
||||
|
||||
/** Same as original access(), but without location, type arguments and arguments.
|
||||
/** Same as original accessBase(), but without location.
|
||||
*/
|
||||
Symbol access(Symbol sym,
|
||||
Symbol accessBase(Symbol sym,
|
||||
DiagnosticPosition pos,
|
||||
Type site,
|
||||
Name name,
|
||||
boolean qualified) {
|
||||
return access(sym, pos, site.tsym, site, name, qualified);
|
||||
return accessBase(sym, pos, site.tsym, site, name, qualified);
|
||||
}
|
||||
|
||||
interface LogResolveHelper {
|
||||
boolean resolveDiagnosticNeeded(Type site, List<Type> argtypes, List<Type> typeargtypes);
|
||||
List<Type> getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List<Type> argtypes);
|
||||
}
|
||||
|
||||
LogResolveHelper basicLogResolveHelper = new LogResolveHelper() {
|
||||
public boolean resolveDiagnosticNeeded(Type site, List<Type> argtypes, List<Type> typeargtypes) {
|
||||
return !site.isErroneous();
|
||||
}
|
||||
public List<Type> getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List<Type> argtypes) {
|
||||
return argtypes;
|
||||
}
|
||||
};
|
||||
|
||||
LogResolveHelper methodLogResolveHelper = new LogResolveHelper() {
|
||||
public boolean resolveDiagnosticNeeded(Type site, List<Type> argtypes, List<Type> typeargtypes) {
|
||||
return !site.isErroneous() &&
|
||||
!Type.isErroneous(argtypes) &&
|
||||
(typeargtypes == null || !Type.isErroneous(typeargtypes));
|
||||
}
|
||||
public List<Type> getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List<Type> argtypes) {
|
||||
if (syms.operatorNames.contains(name)) {
|
||||
return argtypes;
|
||||
} else {
|
||||
Symbol msym = errSym.kind == WRONG_MTH ?
|
||||
((InapplicableSymbolError)errSym).errCandidate().sym : accessedSym;
|
||||
|
||||
List<Type> argtypes2 = Type.map(argtypes,
|
||||
deferredAttr.new RecoveryDeferredTypeMap(AttrMode.SPECULATIVE, msym, currentResolutionContext.firstErroneousResolutionPhase()));
|
||||
|
||||
if (msym != accessedSym) {
|
||||
//fixup deferred type caches - this 'hack' is required because the symbol
|
||||
//returned by InapplicableSymbolError.access() will hide the candidate
|
||||
//method symbol that can be used for lookups in the speculative cache,
|
||||
//causing problems in Attr.checkId()
|
||||
for (Type t : argtypes) {
|
||||
if (t.tag == DEFERRED) {
|
||||
DeferredType dt = (DeferredType)t;
|
||||
dt.speculativeCache.dupAllTo(msym, accessedSym);
|
||||
}
|
||||
}
|
||||
}
|
||||
return argtypes2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Check that sym is not an abstract method.
|
||||
*/
|
||||
void checkNonAbstract(DiagnosticPosition pos, Symbol sym) {
|
||||
@ -1734,7 +1865,7 @@ public class Resolve {
|
||||
*/
|
||||
Symbol resolveIdent(DiagnosticPosition pos, Env<AttrContext> env,
|
||||
Name name, int kind) {
|
||||
return access(
|
||||
return accessBase(
|
||||
findIdent(env, name, kind),
|
||||
pos, env.enclClass.sym.type, name, false);
|
||||
}
|
||||
@ -1759,19 +1890,19 @@ public class Resolve {
|
||||
while (steps.nonEmpty() &&
|
||||
steps.head.isApplicable(boxingEnabled, varargsEnabled) &&
|
||||
sym.kind >= ERRONEOUS) {
|
||||
currentResolutionContext.step = steps.head;
|
||||
currentResolutionContext.step = env.info.pendingResolutionPhase = steps.head;
|
||||
sym = findFun(env, name, argtypes, typeargtypes,
|
||||
steps.head.isBoxingRequired,
|
||||
env.info.varArgs = steps.head.isVarargsRequired);
|
||||
steps.head.isVarargsRequired);
|
||||
currentResolutionContext.resolutionCache.put(steps.head, sym);
|
||||
steps = steps.tail;
|
||||
}
|
||||
if (sym.kind >= AMBIGUOUS) {//if nothing is found return the 'first' error
|
||||
MethodResolutionPhase errPhase =
|
||||
currentResolutionContext.firstErroneousResolutionPhase();
|
||||
sym = access(currentResolutionContext.resolutionCache.get(errPhase),
|
||||
sym = accessMethod(currentResolutionContext.resolutionCache.get(errPhase),
|
||||
pos, env.enclClass.sym.type, name, false, argtypes, typeargtypes);
|
||||
env.info.varArgs = errPhase.isVarargsRequired;
|
||||
env.info.pendingResolutionPhase = errPhase;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
@ -1811,10 +1942,10 @@ public class Resolve {
|
||||
while (steps.nonEmpty() &&
|
||||
steps.head.isApplicable(boxingEnabled, varargsEnabled) &&
|
||||
sym.kind >= ERRONEOUS) {
|
||||
currentResolutionContext.step = steps.head;
|
||||
currentResolutionContext.step = env.info.pendingResolutionPhase = steps.head;
|
||||
sym = findMethod(env, site, name, argtypes, typeargtypes,
|
||||
steps.head.isBoxingRequired(),
|
||||
env.info.varArgs = steps.head.isVarargsRequired(), false);
|
||||
steps.head.isVarargsRequired(), false);
|
||||
currentResolutionContext.resolutionCache.put(steps.head, sym);
|
||||
steps = steps.tail;
|
||||
}
|
||||
@ -1822,13 +1953,13 @@ public class Resolve {
|
||||
//if nothing is found return the 'first' error
|
||||
MethodResolutionPhase errPhase =
|
||||
currentResolutionContext.firstErroneousResolutionPhase();
|
||||
sym = access(currentResolutionContext.resolutionCache.get(errPhase),
|
||||
sym = accessMethod(currentResolutionContext.resolutionCache.get(errPhase),
|
||||
pos, location, site, name, true, argtypes, typeargtypes);
|
||||
env.info.varArgs = errPhase.isVarargsRequired;
|
||||
env.info.pendingResolutionPhase = errPhase;
|
||||
} else if (allowMethodHandles) {
|
||||
MethodSymbol msym = (MethodSymbol)sym;
|
||||
if (msym.isSignaturePolymorphic(types)) {
|
||||
env.info.varArgs = false;
|
||||
env.info.pendingResolutionPhase = BASIC;
|
||||
return findPolymorphicSignatureInstance(env, sym, argtypes);
|
||||
}
|
||||
}
|
||||
@ -1850,7 +1981,7 @@ public class Resolve {
|
||||
Symbol spMethod,
|
||||
List<Type> argtypes) {
|
||||
Type mtype = infer.instantiatePolymorphicSignatureInstance(env,
|
||||
(MethodSymbol)spMethod, argtypes);
|
||||
(MethodSymbol)spMethod, currentResolutionContext, argtypes);
|
||||
for (Symbol sym : polymorphicSignatureScope.getElementsByName(spMethod.name)) {
|
||||
if (types.isSameType(mtype, sym.type)) {
|
||||
return sym;
|
||||
@ -1918,18 +2049,18 @@ public class Resolve {
|
||||
while (steps.nonEmpty() &&
|
||||
steps.head.isApplicable(boxingEnabled, varargsEnabled) &&
|
||||
sym.kind >= ERRONEOUS) {
|
||||
currentResolutionContext.step = steps.head;
|
||||
currentResolutionContext.step = env.info.pendingResolutionPhase = steps.head;
|
||||
sym = findConstructor(pos, env, site, argtypes, typeargtypes,
|
||||
steps.head.isBoxingRequired(),
|
||||
env.info.varArgs = steps.head.isVarargsRequired());
|
||||
steps.head.isVarargsRequired());
|
||||
currentResolutionContext.resolutionCache.put(steps.head, sym);
|
||||
steps = steps.tail;
|
||||
}
|
||||
if (sym.kind >= AMBIGUOUS) {//if nothing is found return the 'first' error
|
||||
MethodResolutionPhase errPhase = currentResolutionContext.firstErroneousResolutionPhase();
|
||||
sym = access(currentResolutionContext.resolutionCache.get(errPhase),
|
||||
sym = accessMethod(currentResolutionContext.resolutionCache.get(errPhase),
|
||||
pos, site, names.init, true, argtypes, typeargtypes);
|
||||
env.info.varArgs = errPhase.isVarargsRequired();
|
||||
env.info.pendingResolutionPhase = errPhase;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
@ -1961,10 +2092,10 @@ public class Resolve {
|
||||
while (steps.nonEmpty() &&
|
||||
steps.head.isApplicable(boxingEnabled, varargsEnabled) &&
|
||||
sym.kind >= ERRONEOUS) {
|
||||
currentResolutionContext.step = steps.head;
|
||||
currentResolutionContext.step = env.info.pendingResolutionPhase = steps.head;
|
||||
sym = findDiamond(env, site, argtypes, typeargtypes,
|
||||
steps.head.isBoxingRequired(),
|
||||
env.info.varArgs = steps.head.isVarargsRequired());
|
||||
steps.head.isVarargsRequired());
|
||||
currentResolutionContext.resolutionCache.put(steps.head, sym);
|
||||
steps = steps.tail;
|
||||
}
|
||||
@ -1986,8 +2117,8 @@ public class Resolve {
|
||||
}
|
||||
};
|
||||
MethodResolutionPhase errPhase = currentResolutionContext.firstErroneousResolutionPhase();
|
||||
sym = access(errSym, pos, site, names.init, true, argtypes, typeargtypes);
|
||||
env.info.varArgs = errPhase.isVarargsRequired();
|
||||
sym = accessMethod(errSym, pos, site, names.init, true, argtypes, typeargtypes);
|
||||
env.info.pendingResolutionPhase = errPhase;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
@ -2115,7 +2246,7 @@ public class Resolve {
|
||||
if (boxingEnabled && sym.kind >= WRONG_MTHS)
|
||||
sym = findMethod(env, syms.predefClass.type, name, argtypes,
|
||||
null, true, false, true);
|
||||
return access(sym, pos, env.enclClass.sym.type, name,
|
||||
return accessMethod(sym, pos, env.enclClass.sym.type, name,
|
||||
false, argtypes, null);
|
||||
}
|
||||
finally {
|
||||
@ -2167,7 +2298,7 @@ public class Resolve {
|
||||
Symbol sym = env1.info.scope.lookup(name).sym;
|
||||
if (sym != null) {
|
||||
if (staticOnly) sym = new StaticError(sym);
|
||||
return access(sym, pos, env.enclClass.sym.type,
|
||||
return accessBase(sym, pos, env.enclClass.sym.type,
|
||||
name, true);
|
||||
}
|
||||
}
|
||||
@ -2199,7 +2330,7 @@ public class Resolve {
|
||||
Symbol sym = env1.info.scope.lookup(name).sym;
|
||||
if (sym != null) {
|
||||
if (staticOnly) sym = new StaticError(sym);
|
||||
return access(sym, pos, env.enclClass.sym.type,
|
||||
return accessBase(sym, pos, env.enclClass.sym.type,
|
||||
name, true);
|
||||
}
|
||||
}
|
||||
@ -2322,17 +2453,6 @@ public class Resolve {
|
||||
Name name,
|
||||
List<Type> argtypes,
|
||||
List<Type> typeargtypes);
|
||||
|
||||
/**
|
||||
* A name designates an operator if it consists
|
||||
* of a non-empty sequence of operator symbols {@literal +-~!/*%&|^<>= }
|
||||
*/
|
||||
boolean isOperator(Name name) {
|
||||
int i = 0;
|
||||
while (i < name.getByteLength() &&
|
||||
"+-~!*/%&|^<>=".indexOf(name.getByteAt(i)) >= 0) i++;
|
||||
return i > 0 && i == name.getByteLength();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2393,7 +2513,7 @@ public class Resolve {
|
||||
if (name == names.error)
|
||||
return null;
|
||||
|
||||
if (isOperator(name)) {
|
||||
if (syms.operatorNames.contains(name)) {
|
||||
boolean isUnaryOp = argtypes.size() == 1;
|
||||
String key = argtypes.size() == 1 ?
|
||||
"operator.cant.be.applied" :
|
||||
@ -2415,8 +2535,7 @@ public class Resolve {
|
||||
hasLocation = !location.name.equals(names._this) &&
|
||||
!location.name.equals(names._super);
|
||||
}
|
||||
boolean isConstructor = kind == ABSENT_MTH &&
|
||||
name == names.table.names.init;
|
||||
boolean isConstructor = kind == ABSENT_MTH && name == names.init;
|
||||
KindName kindname = isConstructor ? KindName.CONSTRUCTOR : absentKind(kind);
|
||||
Name idname = isConstructor ? site.tsym.name : name;
|
||||
String errKey = getErrorKey(kindname, typeargtypes.nonEmpty(), hasLocation);
|
||||
@ -2496,7 +2615,7 @@ public class Resolve {
|
||||
if (name == names.error)
|
||||
return null;
|
||||
|
||||
if (isOperator(name)) {
|
||||
if (syms.operatorNames.contains(name)) {
|
||||
boolean isUnaryOp = argtypes.size() == 1;
|
||||
String key = argtypes.size() == 1 ?
|
||||
"operator.cant.be.applied" :
|
||||
@ -2774,9 +2893,10 @@ public class Resolve {
|
||||
private Map<MethodResolutionPhase, Symbol> resolutionCache =
|
||||
new EnumMap<MethodResolutionPhase, Symbol>(MethodResolutionPhase.class);
|
||||
|
||||
private MethodResolutionPhase step = null;
|
||||
MethodResolutionPhase step = null;
|
||||
|
||||
private boolean internalResolution = false;
|
||||
private DeferredAttr.AttrMode attrMode = DeferredAttr.AttrMode.SPECULATIVE;
|
||||
|
||||
private MethodResolutionPhase firstErroneousResolutionPhase() {
|
||||
MethodResolutionPhase bestSoFar = BASIC;
|
||||
@ -2842,6 +2962,14 @@ public class Resolve {
|
||||
return mtype != null;
|
||||
}
|
||||
}
|
||||
|
||||
DeferredAttr.AttrMode attrMode() {
|
||||
return attrMode;
|
||||
}
|
||||
|
||||
boolean internal() {
|
||||
return internalResolution;
|
||||
}
|
||||
}
|
||||
|
||||
MethodResolutionContext currentResolutionContext = null;
|
||||
|
||||
@ -1041,7 +1041,7 @@ public class JavaCompiler implements ClassReader.SourceCompleter {
|
||||
genEndPos = true;
|
||||
if (!taskListener.isEmpty())
|
||||
taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
|
||||
log.deferDiagnostics = true;
|
||||
log.deferAll();
|
||||
} else { // free resources
|
||||
procEnvImpl.close();
|
||||
}
|
||||
@ -1151,7 +1151,7 @@ public class JavaCompiler implements ClassReader.SourceCompleter {
|
||||
if (c != this)
|
||||
annotationProcessingOccurred = c.annotationProcessingOccurred = true;
|
||||
// doProcessing will have handled deferred diagnostics
|
||||
Assert.check(c.log.deferDiagnostics == false
|
||||
Assert.check(c.log.deferredDiagFilter == null
|
||||
&& c.log.deferredDiagnostics.size() == 0);
|
||||
return c;
|
||||
} finally {
|
||||
|
||||
@ -806,7 +806,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
|
||||
log = Log.instance(context);
|
||||
log.nerrors = priorErrors;
|
||||
log.nwarnings += priorWarnings;
|
||||
log.deferDiagnostics = true;
|
||||
log.deferAll();
|
||||
|
||||
// the following is for the benefit of JavacProcessingEnvironment.getContext()
|
||||
JavacProcessingEnvironment.this.context = context;
|
||||
|
||||
@ -635,6 +635,10 @@ compiler.err.neither.conditional.subtype=\
|
||||
second operand: {0}\n\
|
||||
third operand : {1}
|
||||
|
||||
# 0: message segment
|
||||
compiler.misc.incompatible.type.in.conditional=\
|
||||
bad type in conditional expression; {0}
|
||||
|
||||
compiler.err.new.not.allowed.in.annotation=\
|
||||
''new'' not allowed in an annotation
|
||||
|
||||
@ -1701,6 +1705,10 @@ compiler.misc.inferred.do.not.conform.to.eq.bounds=\
|
||||
inferred: {0}\n\
|
||||
equality constraints(s): {1}
|
||||
|
||||
# 0: list of type
|
||||
compiler.misc.cyclic.inference=\
|
||||
Cannot instantiate inference variables {0} because of an inference loop
|
||||
|
||||
# 0: symbol
|
||||
compiler.misc.diamond=\
|
||||
{0}<>
|
||||
@ -2086,6 +2094,9 @@ compiler.note.deferred.method.inst=\
|
||||
compiler.misc.type.null=\
|
||||
<null>
|
||||
|
||||
compiler.misc.type.conditional=\
|
||||
conditional expression
|
||||
|
||||
# X#n (where n is an int id) is disambiguated tvar name
|
||||
# 0: name, 1: number
|
||||
compiler.misc.type.var=\
|
||||
|
||||
@ -81,6 +81,12 @@ public class Pretty extends JCTree.Visitor {
|
||||
*/
|
||||
DocCommentTable docComments = null;
|
||||
|
||||
/**
|
||||
* A string sequence to be used when Pretty output should be constrained
|
||||
* to fit into a given size
|
||||
*/
|
||||
private final static String trimSequence = "[...]";
|
||||
|
||||
/** Align code to be indented to left margin.
|
||||
*/
|
||||
void align() throws IOException {
|
||||
@ -129,6 +135,27 @@ public class Pretty extends JCTree.Visitor {
|
||||
out.write(lineSep);
|
||||
}
|
||||
|
||||
public static String toSimpleString(JCTree tree, int maxLength) {
|
||||
StringWriter s = new StringWriter();
|
||||
try {
|
||||
new Pretty(s, false).printExpr(tree);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// should never happen, because StringWriter is defined
|
||||
// never to throw any IOExceptions
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
//we need to (i) replace all line terminators with a space and (ii) remove
|
||||
//occurrences of 'missing' in the Pretty output (generated when types are missing)
|
||||
String res = s.toString().replaceAll("\\s+", " ").replaceAll("/\\*missing\\*/", "");
|
||||
if (res.length() < maxLength) {
|
||||
return res;
|
||||
} else {
|
||||
int split = (maxLength - trimSequence.length()) * 2 / 3;
|
||||
return res.substring(0, split) + trimSequence + res.substring(split);
|
||||
}
|
||||
}
|
||||
|
||||
String lineSep = System.getProperty("line.separator");
|
||||
|
||||
/**************************************************************************
|
||||
|
||||
@ -245,6 +245,23 @@ public class TreeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true if a a tree corresponds to a poly expression. */
|
||||
public static boolean isPoly(JCTree tree, JCTree origin) {
|
||||
switch (tree.getTag()) {
|
||||
case APPLY:
|
||||
case NEWCLASS:
|
||||
case CONDEXPR:
|
||||
return !origin.hasTag(TYPECAST);
|
||||
case LAMBDA:
|
||||
case REFERENCE:
|
||||
return true;
|
||||
case PARENS:
|
||||
return isPoly(((JCParens)tree).expr, origin);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the AST corresponds to a static select of the kind A.B
|
||||
*/
|
||||
|
||||
@ -489,7 +489,8 @@ public abstract class AbstractDiagnosticFormatter implements DiagnosticFormatter
|
||||
* type referred by a given captured type C contains C itself) which might
|
||||
* lead to infinite loops.
|
||||
*/
|
||||
protected Printer printer = new Printer() {
|
||||
protected Printer printer = new Printer(isRaw()) {
|
||||
|
||||
@Override
|
||||
protected String localize(Locale locale, String key, Object... args) {
|
||||
return AbstractDiagnosticFormatter.this.localize(locale, key, args);
|
||||
|
||||
@ -83,6 +83,19 @@ public class List<A> extends AbstractCollection<A> implements java.util.List<A>
|
||||
}
|
||||
};
|
||||
|
||||
/** Returns the list obtained from 'l' after removing all elements 'elem'
|
||||
*/
|
||||
public static <A> List<A> filter(List<A> l, A elem) {
|
||||
Assert.checkNonNull(elem);
|
||||
List<A> res = List.nil();
|
||||
for (A a : l) {
|
||||
if (a != null && !a.equals(elem)) {
|
||||
res = res.prepend(a);
|
||||
}
|
||||
}
|
||||
return res.reverse();
|
||||
}
|
||||
|
||||
/** Construct a list consisting of given element.
|
||||
*/
|
||||
public static <A> List<A> of(A x1) {
|
||||
@ -120,6 +133,14 @@ public class List<A> extends AbstractCollection<A> implements java.util.List<A>
|
||||
return xs;
|
||||
}
|
||||
|
||||
public static <A> List<A> from(Iterable<? extends A> coll) {
|
||||
List<A> xs = nil();
|
||||
for (A a : coll) {
|
||||
xs = new List<A>(a, xs);
|
||||
}
|
||||
return xs;
|
||||
}
|
||||
|
||||
/** Construct a list consisting of a given number of identical elements.
|
||||
* @param len The number of elements in the list.
|
||||
* @param init The value of each element.
|
||||
|
||||
@ -130,7 +130,7 @@ public class Log extends AbstractLog {
|
||||
/**
|
||||
* Deferred diagnostics
|
||||
*/
|
||||
public boolean deferDiagnostics;
|
||||
public Filter<JCDiagnostic> deferredDiagFilter;
|
||||
public Queue<JCDiagnostic> deferredDiagnostics = new ListBuffer<JCDiagnostic>();
|
||||
|
||||
/** Construct a log with given I/O redirections.
|
||||
@ -450,7 +450,7 @@ public class Log extends AbstractLog {
|
||||
|
||||
/** Report selected deferred diagnostics, and clear the deferDiagnostics flag. */
|
||||
public void reportDeferredDiagnostics(Set<JCDiagnostic.Kind> kinds) {
|
||||
deferDiagnostics = false;
|
||||
deferredDiagFilter = null;
|
||||
JCDiagnostic d;
|
||||
while ((d = deferredDiagnostics.poll()) != null) {
|
||||
if (kinds.contains(d.getKind()))
|
||||
@ -464,7 +464,7 @@ public class Log extends AbstractLog {
|
||||
* reported so far, the diagnostic may be handed off to writeDiagnostic.
|
||||
*/
|
||||
public void report(JCDiagnostic diagnostic) {
|
||||
if (deferDiagnostics) {
|
||||
if (deferredDiagFilter != null && deferredDiagFilter.accepts(diagnostic)) {
|
||||
deferredDiagnostics.add(diagnostic);
|
||||
return;
|
||||
}
|
||||
@ -551,6 +551,18 @@ public class Log extends AbstractLog {
|
||||
}
|
||||
}
|
||||
|
||||
public void deferAll() {
|
||||
deferredDiagFilter = new Filter<JCDiagnostic>() {
|
||||
public boolean accepts(JCDiagnostic t) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void deferNone() {
|
||||
deferredDiagFilter = null;
|
||||
}
|
||||
|
||||
/** Find a localized string in the resource bundle.
|
||||
* Because this method is static, it ignores the locale.
|
||||
* Use localize(key, args) when possible.
|
||||
|
||||
@ -325,6 +325,10 @@ public class RichDiagnosticFormatter extends
|
||||
*/
|
||||
protected class RichPrinter extends Printer {
|
||||
|
||||
public RichPrinter() {
|
||||
super(formatter.isRaw());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String localize(Locale locale, String key, Object... args) {
|
||||
return formatter.localize(locale, key, args);
|
||||
@ -392,11 +396,6 @@ public class RichDiagnosticFormatter extends
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String printMethodArgs(List<Type> args, boolean varArgs, Locale locale) {
|
||||
return super.printMethodArgs(args, varArgs, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitClassSymbol(ClassSymbol s, Locale locale) {
|
||||
String name = nameSimplifier.simplify(s);
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
* @summary Conditional operator applies assignment conversion
|
||||
* @author Tim Hanson, BEA
|
||||
*
|
||||
* @compile -XDallowPoly Conditional.java
|
||||
* @compile/fail Conditional.java
|
||||
*/
|
||||
|
||||
|
||||
@ -58,6 +58,7 @@ compiler.misc.fatal.err.cant.locate.meth # Resolve, from Lower
|
||||
compiler.misc.fatal.err.cant.close # JavaCompiler
|
||||
compiler.misc.file.does.not.contain.package
|
||||
compiler.misc.illegal.start.of.class.file
|
||||
compiler.misc.cyclic.inference # Cannot happen w/o lambdas
|
||||
compiler.misc.kindname.annotation
|
||||
compiler.misc.kindname.enum
|
||||
compiler.misc.kindname.package
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.prob.found.req
|
||||
// key: compiler.misc.incompatible.type.in.conditional
|
||||
// key: compiler.misc.inconvertible.types
|
||||
// options: -XDallowPoly
|
||||
|
||||
class IncompatibleTypesInConditional {
|
||||
|
||||
interface A { }
|
||||
interface B { }
|
||||
|
||||
B b = true ? (A)null : (B)null;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.cant.apply.symbol.1
|
||||
// key: compiler.misc.type.conditional
|
||||
// key: compiler.misc.no.args
|
||||
// key: compiler.misc.arg.length.mismatch
|
||||
// options: -XDallowPoly
|
||||
// run: simple
|
||||
|
||||
class TypeConditional {
|
||||
|
||||
void m() { }
|
||||
|
||||
void test() {
|
||||
m(true ? 1 : 2);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user