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:
Maurizio Cimadamore 2012-10-04 13:04:53 +01:00
parent ed2bca8140
commit c0e2ed86c1
27 changed files with 1538 additions and 467 deletions

View File

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

View File

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

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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.
*/

View File

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

View File

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

View File

@ -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(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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");
/**************************************************************************

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
* @summary Conditional operator applies assignment conversion
* @author Tim Hanson, BEA
*
* @compile -XDallowPoly Conditional.java
* @compile/fail Conditional.java
*/

View File

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

View File

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

View File

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