diff --git a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/ClassUseWriter.java b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/ClassUseWriter.java
index f96bd93ab42..d0cea943d6a 100644
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/ClassUseWriter.java
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/ClassUseWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -195,10 +195,7 @@ public class ClassUseWriter extends SubWriterHolderWriter {
ClassUseWriter clsgen;
String path = DirectoryManager.getDirectoryPath(classdoc.
containingPackage());
- if (path.length() > 0) {
- path += File.separator;
- }
- path += "class-use";
+ path += "class-use" + DirectoryManager.URL_FILE_SEPARATOR;
String filename = classdoc.name() + ".html";
String pkgname = classdoc.containingPackage().name();
pkgname += (pkgname.length() > 0)? ".class-use": "class-use";
diff --git a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HelpWriter.java b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HelpWriter.java
index 7bc17858762..7f15c842276 100644
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HelpWriter.java
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HelpWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -302,7 +302,9 @@ public class HelpWriter extends HtmlDocletWriter {
Content constHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
getResource("doclet.Constants_Summary"));
Content liConst = HtmlTree.LI(HtmlStyle.blockList, constHead);
- Content line29 = getResource("doclet.Help_line_29");
+ Content line29 = getResource("doclet.Help_line_29",
+ getHyperLinkString("constant-values.html",
+ configuration.getText("doclet.Constants_Summary")));
Content constPara = HtmlTree.P(line29);
liConst.addContent(constPara);
ul.addContent(liConst);
diff --git a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties
index b9bef57f746..e38bb2bf9ea 100644
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties
@@ -160,7 +160,7 @@ doclet.Help_line_25=Frames/No Frames
doclet.Help_line_26=These links show and hide the HTML frames. All pages are available with or without frames.
doclet.Help_line_27=The {0} link shows all classes and interfaces except non-static nested types.
doclet.Help_line_28=Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
-doclet.Help_line_29=The Constant Field Values page lists the static final fields and their values.
+doclet.Help_line_29=The {0} page lists the static final fields and their values.
doclet.Help_line_30=This help file applies to API documentation generated using the standard doclet.
doclet.Help_enum_line_1=Each enum has its own separate page with the following sections:
doclet.Help_enum_line_2=Enum declaration
diff --git a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
index 9c08b5c0cf1..e3d15ebd768 100644
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
@@ -488,17 +488,18 @@ public abstract class Configuration {
}
/**
- * Add a traliling file separator, if not found or strip off extra trailing
- * file separators if any.
+ * Add a trailing file separator, if not found. Remove superfluous
+ * file separators if any. Preserve the front double file separator for
+ * UNC paths.
*
* @param path Path under consideration.
* @return String Properly constructed path string.
*/
- String addTrailingFileSep(String path) {
+ public static String addTrailingFileSep(String path) {
String fs = System.getProperty("file.separator");
String dblfs = fs + fs;
int indexDblfs;
- while ((indexDblfs = path.indexOf(dblfs)) >= 0) {
+ while ((indexDblfs = path.indexOf(dblfs, 1)) >= 0) {
path = path.substring(0, indexDblfs) +
path.substring(indexDblfs + fs.length());
}
diff --git a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java
index 330243e84d9..70cee399648 100644
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java
@@ -262,11 +262,7 @@ public class TagletManager {
urls[count++] = url;
}
}
- if (urls.length != count) {
- URL[] tmp = new URL[count];
- System.arraycopy(urls, 0, tmp, 0, count);
- urls = tmp;
- }
+ urls = Arrays.copyOf(urls, count);
return urls;
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java b/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java
index c366281e6e8..01838e9c196 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java
@@ -136,4 +136,11 @@ public class BasicJavacTask extends JavacTask {
throw new IllegalStateException();
}
+ /**
+ * For internal use only. This method will be
+ * removed without warning.
+ */
+ public void updateContext(Context newContext) {
+ context = newContext;
+ }
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/api/MultiTaskListener.java b/langtools/src/share/classes/com/sun/tools/javac/api/MultiTaskListener.java
index 759dff38710..f1c21f99d54 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/api/MultiTaskListener.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/MultiTaskListener.java
@@ -79,10 +79,8 @@ public class MultiTaskListener implements TaskListener {
if (ccw.unwrap(l) == listener)
throw new IllegalStateException();
}
- TaskListener[] newListeners = new TaskListener[listeners.length + 1];
- System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
- newListeners[newListeners.length - 1] = ccw.wrap(listener);
- listeners = newListeners;
+ listeners = Arrays.copyOf(listeners, listeners.length + 1);
+ listeners[listeners.length - 1] = ccw.wrap(listener);
}
public void remove(TaskListener listener) {
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Kinds.java b/langtools/src/share/classes/com/sun/tools/javac/code/Kinds.java
index 9f9273e3eb5..9495ddf1be4 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Kinds.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Kinds.java
@@ -28,6 +28,7 @@ package com.sun.tools.javac.code;
import java.util.EnumSet;
import java.util.Locale;
+import com.sun.source.tree.MemberReferenceTree;
import com.sun.tools.javac.api.Formattable;
import com.sun.tools.javac.api.Messages;
@@ -85,11 +86,12 @@ public class Kinds {
public static final int AMBIGUOUS = ERRONEOUS+1; // ambiguous reference
public static final int HIDDEN = ERRONEOUS+2; // hidden method or field
public static final int STATICERR = ERRONEOUS+3; // nonstatic member from static context
- public static final int ABSENT_VAR = ERRONEOUS+4; // missing variable
- public static final int WRONG_MTHS = ERRONEOUS+5; // methods with wrong arguments
- public static final int WRONG_MTH = ERRONEOUS+6; // one method with wrong arguments
- public static final int ABSENT_MTH = ERRONEOUS+7; // missing method
- public static final int ABSENT_TYP = ERRONEOUS+8; // missing type
+ public static final int MISSING_ENCL = ERRONEOUS+4; // missing enclosing class
+ public static final int ABSENT_VAR = ERRONEOUS+5; // missing variable
+ public static final int WRONG_MTHS = ERRONEOUS+6; // methods with wrong arguments
+ public static final int WRONG_MTH = ERRONEOUS+7; // one method with wrong arguments
+ public static final int ABSENT_MTH = ERRONEOUS+8; // missing method
+ public static final int ABSENT_TYP = ERRONEOUS+9; // missing type
public enum KindName implements Formattable {
ANNOTATION("kindname.annotation"),
@@ -140,6 +142,14 @@ public class Kinds {
}
}
+ public static KindName kindName(MemberReferenceTree.ReferenceMode mode) {
+ switch (mode) {
+ case INVOKE: return KindName.METHOD;
+ case NEW: return KindName.CONSTRUCTOR;
+ default : throw new AssertionError("Unexpected mode: "+ mode);
+ }
+ }
+
/** A KindName representing a given symbol
*/
public static KindName kindName(Symbol sym) {
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Printer.java b/langtools/src/share/classes/com/sun/tools/javac/code/Printer.java
index f639caeef45..644955c646a 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Printer.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Printer.java
@@ -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;
@@ -51,6 +55,8 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi
List seenCaptured = List.nil();
static final int PRIME = 997; // largest prime less than 1000
+ protected Printer() { }
+
/**
* This method should be overriden in order to provide proper i18n support.
*
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Source.java b/langtools/src/share/classes/com/sun/tools/javac/code/Source.java
index e17d1a1fa81..f83823af270 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Source.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Source.java
@@ -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;
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
index fc651b4a1f3..010d441cb71 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
@@ -168,6 +168,10 @@ public abstract class Symbol implements Element {
return owner;
}
+ public Symbol baseSymbol() {
+ return this;
+ }
+
/** The symbol's erased type.
*/
public Type erasure(Types types) {
@@ -918,7 +922,12 @@ public abstract class Symbol implements Element {
/** Clone this symbol with new owner.
*/
public VarSymbol clone(Symbol newOwner) {
- VarSymbol v = new VarSymbol(flags_field, name, type, newOwner);
+ VarSymbol v = new VarSymbol(flags_field, name, type, newOwner) {
+ @Override
+ public Symbol baseSymbol() {
+ return VarSymbol.this;
+ }
+ };
v.pos = pos;
v.adr = adr;
v.data = data;
@@ -1045,7 +1054,12 @@ public abstract class Symbol implements Element {
/** Clone this symbol with new owner.
*/
public MethodSymbol clone(Symbol newOwner) {
- MethodSymbol m = new MethodSymbol(flags_field, name, type, newOwner);
+ MethodSymbol m = new MethodSymbol(flags_field, name, type, newOwner) {
+ @Override
+ public Symbol baseSymbol() {
+ return MethodSymbol.this;
+ }
+ };
m.code = code;
return m;
}
@@ -1068,6 +1082,10 @@ public abstract class Symbol implements Element {
}
}
+ public boolean isDynamic() {
+ return false;
+ }
+
/** find a symbol that this (proxy method) symbol implements.
* @param c The class whose members are searched for
* implementations
@@ -1356,6 +1374,27 @@ public abstract class Symbol implements Element {
}
}
+ /** A class for invokedynamic method calls.
+ */
+ public static class DynamicMethodSymbol extends MethodSymbol {
+
+ public Object[] staticArgs;
+ public Symbol bsm;
+ public int bsmKind;
+
+ public DynamicMethodSymbol(Name name, Symbol owner, int bsmKind, MethodSymbol bsm, Type type, Object[] staticArgs) {
+ super(0, name, type, owner);
+ this.bsm = bsm;
+ this.bsmKind = bsmKind;
+ this.staticArgs = staticArgs;
+ }
+
+ @Override
+ public boolean isDynamic() {
+ return true;
+ }
+ }
+
/** A class for predefined operators.
*/
public static class OperatorSymbol extends MethodSymbol {
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
index 32a9170daf9..c09d31eb083 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
@@ -126,6 +126,7 @@ public class Symtab {
public final Type cloneableType;
public final Type serializableType;
public final Type methodHandleType;
+ public final Type methodTypeType;
public final Type nativeHeaderType;
public final Type throwableType;
public final Type errorType;
@@ -182,6 +183,10 @@ public class Symtab {
*/
public final Name[] boxedName = new Name[TypeTags.TypeTagCount];
+ /** A set containing all operator names.
+ */
+ public final Set operatorNames = new HashSet();
+
/** 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
@@ -243,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.nil(), methodClass),
opcode,
@@ -274,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.nil(),
@@ -285,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.
*/
@@ -440,6 +455,7 @@ public class Symtab {
throwableType = enterClass("java.lang.Throwable");
serializableType = enterClass("java.io.Serializable");
methodHandleType = enterClass("java.lang.invoke.MethodHandle");
+ methodTypeType = enterClass("java.lang.invoke.MethodType");
errorType = enterClass("java.lang.Error");
illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException");
interruptedExceptionType = enterClass("java.lang.InterruptedException");
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java
index 86d2c631018..eefbf90c85d 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java
@@ -27,14 +27,19 @@ package com.sun.tools.javac.code;
import java.util.Collections;
-import com.sun.tools.javac.util.*;
import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.util.*;
+
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
import javax.lang.model.type.*;
+import static com.sun.tools.javac.code.BoundKind.*;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.*;
-import static com.sun.tools.javac.code.BoundKind.*;
import static com.sun.tools.javac.code.TypeTags.*;
/** This class represents Java types. The class itself defines the behavior of
@@ -70,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.
*/
@@ -1168,22 +1176,59 @@ public class Type implements PrimitiveType {
}
}
- /** A class for instantiatable variables, for use during type
- * inference.
+ /** A class for inference variables, for use during method/diamond type
+ * inference. An inference variable has upper/lower bounds and a set
+ * of equality constraints. Such bounds are set during subtyping, type-containment,
+ * type-equality checks, when the types being tested contain inference variables.
+ * A change listener can be attached to an inference variable, to receive notifications
+ * whenever the bounds of an inference variable change.
*/
public static class UndetVar extends DelegatedType {
- public List lobounds = List.nil();
- public List hibounds = List.nil();
- public List eq = List.nil();
+
+ /** Inference variable change listener. The listener method is called
+ * whenever a change to the inference variable's bounds occurs
+ */
+ public interface UndetVarListener {
+ /** called when some inference variable bounds (of given kinds ibs) change */
+ void varChanged(UndetVar uv, Set ibs);
+ }
+
+ /**
+ * Inference variable bound kinds
+ */
+ public enum InferenceBound {
+ /** upper bounds */
+ UPPER,
+ /** lower bounds */
+ LOWER,
+ /** equality constraints */
+ EQ;
+ }
+
+ /** inference variable bounds */
+ private Map> bounds;
+
+ /** inference variable's inferred type (set from Infer.java) */
public Type inst = null;
+ /** inference variable's change listener */
+ public UndetVarListener listener = null;
+
@Override
public R accept(Type.Visitor v, S s) {
return v.visitUndetVar(this, s);
}
- public UndetVar(Type origin) {
+ public UndetVar(TypeVar origin, Types types) {
+ this(origin, types, true);
+ }
+
+ public UndetVar(TypeVar origin, Types types, boolean includeBounds) {
super(UNDETVAR, origin);
+ bounds = new EnumMap>(InferenceBound.class);
+ bounds.put(InferenceBound.UPPER, includeBounds ? types.getBounds(origin) : List.nil());
+ bounds.put(InferenceBound.LOWER, List.nil());
+ bounds.put(InferenceBound.EQ, List.nil());
}
public String toString() {
@@ -1195,6 +1240,48 @@ public class Type implements PrimitiveType {
if (inst != null) return inst.baseType();
else return this;
}
+
+ /** get all bounds of a given kind */
+ public List getBounds(InferenceBound ib) {
+ return bounds.get(ib);
+ }
+
+ /** add a bound of a given kind - this might trigger listener notification */
+ public void addBound(InferenceBound ib, Type bound, Types types) {
+ List prevBounds = bounds.get(ib);
+ for (Type b : prevBounds) {
+ if (types.isSameType(b, bound)) {
+ return;
+ }
+ }
+ bounds.put(ib, prevBounds.prepend(bound));
+ notifyChange(EnumSet.of(ib));
+ }
+
+ /** replace types in all bounds - this might trigger listener notification */
+ public void substBounds(List from, List to, Types types) {
+ EnumSet changed = EnumSet.noneOf(InferenceBound.class);
+ Map> bounds2 = new EnumMap>(InferenceBound.class);
+ for (Map.Entry> _entry : bounds.entrySet()) {
+ InferenceBound ib = _entry.getKey();
+ List prevBounds = _entry.getValue();
+ List newBounds = types.subst(prevBounds, from, to);
+ bounds2.put(ib, newBounds);
+ if (prevBounds != newBounds) {
+ changed.add(ib);
+ }
+ }
+ if (!changed.isEmpty()) {
+ bounds = bounds2;
+ notifyChange(changed);
+ }
+ }
+
+ private void notifyChange(EnumSet ibs) {
+ if (listener != null) {
+ listener.varChanged(this, ibs);
+ }
+ }
}
/** Represents VOID or NONE.
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/TypeTags.java b/langtools/src/share/classes/com/sun/tools/javac/code/TypeTags.java
index 463def22ef8..d46286ade81 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/TypeTags.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/TypeTags.java
@@ -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 .
*/
- public static final int BOT = FORALL+1;
+ public static final int BOT = DEFERRED+1;
/** The tag of a missing type.
*/
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java
index 4fdfd4d1ef8..0cb2b07b248 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java
@@ -34,6 +34,7 @@ import com.sun.tools.javac.util.List;
import com.sun.tools.javac.jvm.ClassReader;
import com.sun.tools.javac.code.Attribute.RetentionPolicy;
import com.sun.tools.javac.code.Lint.LintCategory;
+import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
import com.sun.tools.javac.comp.Check;
import static com.sun.tools.javac.code.Scope.*;
@@ -78,8 +79,10 @@ public class Types {
final boolean allowObjectToPrimitiveCast;
final ClassReader reader;
final Check chk;
+ JCDiagnostic.Factory diags;
List warnStack = List.nil();
final Name capturedName;
+ private final FunctionDescriptorLookupError functionDescriptorLookupError;
//
public static Types instance(Context context) {
@@ -101,6 +104,8 @@ public class Types {
chk = Check.instance(context);
capturedName = names.fromString("");
messages = JavacMessages.instance(context);
+ diags = JCDiagnostic.Factory.instance(context);
+ functionDescriptorLookupError = new FunctionDescriptorLookupError();
}
//
@@ -295,6 +300,294 @@ public class Types {
}
//
+ //
+
+ /**
+ * Exception used to report a function descriptor lookup failure. The exception
+ * wraps a diagnostic that can be used to generate more details error
+ * messages.
+ */
+ public static class FunctionDescriptorLookupError extends RuntimeException {
+ private static final long serialVersionUID = 0;
+
+ JCDiagnostic diagnostic;
+
+ FunctionDescriptorLookupError() {
+ this.diagnostic = null;
+ }
+
+ FunctionDescriptorLookupError setMessage(JCDiagnostic diag) {
+ this.diagnostic = diag;
+ return this;
+ }
+
+ public JCDiagnostic getDiagnostic() {
+ return diagnostic;
+ }
+ }
+
+ /**
+ * A cache that keeps track of function descriptors associated with given
+ * functional interfaces.
+ */
+ class DescriptorCache {
+
+ private WeakHashMap _map = new WeakHashMap();
+
+ class FunctionDescriptor {
+ Symbol descSym;
+
+ FunctionDescriptor(Symbol descSym) {
+ this.descSym = descSym;
+ }
+
+ public Symbol getSymbol() {
+ return descSym;
+ }
+
+ public Type getType(Type origin) {
+ return memberType(origin, descSym);
+ }
+ }
+
+ class Entry {
+ final FunctionDescriptor cachedDescRes;
+ final int prevMark;
+
+ public Entry(FunctionDescriptor cachedDescRes,
+ int prevMark) {
+ this.cachedDescRes = cachedDescRes;
+ this.prevMark = prevMark;
+ }
+
+ boolean matches(int mark) {
+ return this.prevMark == mark;
+ }
+ }
+
+ FunctionDescriptor get(TypeSymbol origin) throws FunctionDescriptorLookupError {
+ Entry e = _map.get(origin);
+ CompoundScope members = membersClosure(origin.type, false);
+ if (e == null ||
+ !e.matches(members.getMark())) {
+ FunctionDescriptor descRes = findDescriptorInternal(origin, members);
+ _map.put(origin, new Entry(descRes, members.getMark()));
+ return descRes;
+ }
+ else {
+ return e.cachedDescRes;
+ }
+ }
+
+ /**
+ * Scope filter used to skip methods that should be ignored during
+ * function interface conversion (such as methods overridden by
+ * j.l.Object)
+ */
+ class DescriptorFilter implements Filter {
+
+ TypeSymbol origin;
+
+ DescriptorFilter(TypeSymbol origin) {
+ this.origin = origin;
+ }
+
+ @Override
+ public boolean accepts(Symbol sym) {
+ return sym.kind == Kinds.MTH &&
+ (sym.flags() & ABSTRACT) != 0 &&
+ !overridesObjectMethod(origin, sym) &&
+ notOverridden(sym);
+ }
+
+ private boolean notOverridden(Symbol msym) {
+ Symbol impl = ((MethodSymbol)msym).implementation(origin, Types.this, false);
+ return impl == null || (impl.flags() & ABSTRACT) != 0;
+ }
+ };
+
+ /**
+ * Compute the function descriptor associated with a given functional interface
+ */
+ public FunctionDescriptor findDescriptorInternal(TypeSymbol origin, CompoundScope membersCache) throws FunctionDescriptorLookupError {
+ if (!origin.isInterface()) {
+ //t must be an interface
+ throw failure("not.a.functional.intf");
+ }
+
+ final ListBuffer abstracts = ListBuffer.lb();
+ for (Symbol sym : membersCache.getElements(new DescriptorFilter(origin))) {
+ Type mtype = memberType(origin.type, sym);
+ if (abstracts.isEmpty() ||
+ (sym.name == abstracts.first().name &&
+ overrideEquivalent(mtype, memberType(origin.type, abstracts.first())))) {
+ abstracts.append(sym);
+ } else {
+ //the target method(s) should be the only abstract members of t
+ throw failure("not.a.functional.intf.1",
+ diags.fragment("incompatible.abstracts", Kinds.kindName(origin), origin));
+ }
+ }
+ if (abstracts.isEmpty()) {
+ //t must define a suitable non-generic method
+ throw failure("not.a.functional.intf.1",
+ diags.fragment("no.abstracts", Kinds.kindName(origin), origin));
+ } else if (abstracts.size() == 1) {
+ if (abstracts.first().type.tag == FORALL) {
+ throw failure("invalid.generic.desc.in.functional.intf",
+ abstracts.first(),
+ Kinds.kindName(origin),
+ origin);
+ } else {
+ return new FunctionDescriptor(abstracts.first());
+ }
+ } else { // size > 1
+ for (Symbol msym : abstracts) {
+ if (msym.type.tag == FORALL) {
+ throw failure("invalid.generic.desc.in.functional.intf",
+ abstracts.first(),
+ Kinds.kindName(origin),
+ origin);
+ }
+ }
+ FunctionDescriptor descRes = mergeDescriptors(origin, abstracts.toList());
+ if (descRes == null) {
+ //we can get here if the functional interface is ill-formed
+ ListBuffer descriptors = ListBuffer.lb();
+ for (Symbol desc : abstracts) {
+ String key = desc.type.getThrownTypes().nonEmpty() ?
+ "descriptor.throws" : "descriptor";
+ descriptors.append(diags.fragment(key, desc.name,
+ desc.type.getParameterTypes(),
+ desc.type.getReturnType(),
+ desc.type.getThrownTypes()));
+ }
+ JCDiagnostic.MultilineDiagnostic incompatibleDescriptors =
+ new JCDiagnostic.MultilineDiagnostic(diags.fragment("incompatible.descs.in.functional.intf",
+ Kinds.kindName(origin), origin), descriptors.toList());
+ throw failure(incompatibleDescriptors);
+ }
+ return descRes;
+ }
+ }
+
+ /**
+ * Compute a synthetic type for the target descriptor given a list
+ * of override-equivalent methods in the functional interface type.
+ * The resulting method type is a method type that is override-equivalent
+ * and return-type substitutable with each method in the original list.
+ */
+ private FunctionDescriptor mergeDescriptors(TypeSymbol origin, List methodSyms) {
+ //pick argument types - simply take the signature that is a
+ //subsignature of all other signatures in the list (as per JLS 8.4.2)
+ List mostSpecific = List.nil();
+ outer: for (Symbol msym1 : methodSyms) {
+ Type mt1 = memberType(origin.type, msym1);
+ for (Symbol msym2 : methodSyms) {
+ Type mt2 = memberType(origin.type, msym2);
+ if (!isSubSignature(mt1, mt2)) {
+ continue outer;
+ }
+ }
+ mostSpecific = mostSpecific.prepend(msym1);
+ }
+ if (mostSpecific.isEmpty()) {
+ return null;
+ }
+
+
+ //pick return types - this is done in two phases: (i) first, the most
+ //specific return type is chosen using strict subtyping; if this fails,
+ //a second attempt is made using return type substitutability (see JLS 8.4.5)
+ boolean phase2 = false;
+ Symbol bestSoFar = null;
+ while (bestSoFar == null) {
+ outer: for (Symbol msym1 : mostSpecific) {
+ Type mt1 = memberType(origin.type, msym1);
+ for (Symbol msym2 : methodSyms) {
+ Type mt2 = memberType(origin.type, msym2);
+ if (phase2 ?
+ !returnTypeSubstitutable(mt1, mt2) :
+ !isSubtypeInternal(mt1.getReturnType(), mt2.getReturnType())) {
+ continue outer;
+ }
+ }
+ bestSoFar = msym1;
+ }
+ if (phase2) {
+ break;
+ } else {
+ phase2 = true;
+ }
+ }
+ if (bestSoFar == null) return null;
+
+ //merge thrown types - form the intersection of all the thrown types in
+ //all the signatures in the list
+ List thrown = null;
+ for (Symbol msym1 : methodSyms) {
+ Type mt1 = memberType(origin.type, msym1);
+ thrown = (thrown == null) ?
+ mt1.getThrownTypes() :
+ chk.intersect(mt1.getThrownTypes(), thrown);
+ }
+
+ final List thrown1 = thrown;
+ return new FunctionDescriptor(bestSoFar) {
+ @Override
+ public Type getType(Type origin) {
+ Type mt = memberType(origin, getSymbol());
+ return new MethodType(mt.getParameterTypes(), mt.getReturnType(), thrown1, syms.methodClass);
+ }
+ };
+ }
+
+ boolean isSubtypeInternal(Type s, Type t) {
+ return (s.isPrimitive() && t.isPrimitive()) ?
+ isSameType(t, s) :
+ isSubtype(s, t);
+ }
+
+ FunctionDescriptorLookupError failure(String msg, Object... args) {
+ return failure(diags.fragment(msg, args));
+ }
+
+ FunctionDescriptorLookupError failure(JCDiagnostic diag) {
+ return functionDescriptorLookupError.setMessage(diag);
+ }
+ }
+
+ private DescriptorCache descCache = new DescriptorCache();
+
+ /**
+ * Find the method descriptor associated to this class symbol - if the
+ * symbol 'origin' is not a functional interface, an exception is thrown.
+ */
+ public Symbol findDescriptorSymbol(TypeSymbol origin) throws FunctionDescriptorLookupError {
+ return descCache.get(origin).getSymbol();
+ }
+
+ /**
+ * Find the type of the method descriptor associated to this class symbol -
+ * if the symbol 'origin' is not a functional interface, an exception is thrown.
+ */
+ public Type findDescriptorType(Type origin) throws FunctionDescriptorLookupError {
+ return descCache.get(origin.tsym).getType(origin);
+ }
+
+ /**
+ * Is given type a functional interface?
+ */
+ public boolean isFunctionalInterface(TypeSymbol tsym) {
+ try {
+ findDescriptorSymbol(tsym);
+ return true;
+ } catch (FunctionDescriptorLookupError ex) {
+ return false;
+ }
+ }
+ //
+
//
/**
* Is t an unchecked subtype of s?
@@ -510,7 +803,7 @@ public class Types {
return false;
}
- t.hibounds = t.hibounds.prepend(s);
+ t.addBound(InferenceBound.UPPER, s, Types.this);
return true;
}
@@ -578,7 +871,7 @@ public class Types {
undet.qtype == s ||
s.tag == ERROR ||
s.tag == BOT) return true;
- undet.lobounds = undet.lobounds.prepend(s);
+ undet.addBound(InferenceBound.LOWER, s, this);
return true;
}
default:
@@ -723,7 +1016,7 @@ public class Types {
if (t == s || t.qtype == s || s.tag == ERROR || s.tag == UNKNOWN)
return true;
- t.eq = t.eq.prepend(s);
+ t.addBound(InferenceBound.EQ, s, Types.this);
return true;
}
@@ -735,19 +1028,6 @@ public class Types {
};
//
- //
- /**
- * A mapping that turns all unknown types in this type to fresh
- * unknown variables.
- */
- public Mapping fromUnknownFun = new Mapping("fromUnknownFun") {
- public Type apply(Type t) {
- if (t.tag == UNKNOWN) return new UndetVar(t);
- else return t.map(this);
- }
- };
- //
-
//
public boolean containedBy(Type t, Type s) {
switch (t.tag) {
@@ -759,12 +1039,12 @@ public class Types {
case UNBOUND: //similar to ? extends Object
case EXTENDS: {
Type bound = upperBound(s);
- undetvar.hibounds = undetvar.hibounds.prepend(bound);
+ undetvar.addBound(InferenceBound.UPPER, bound, this);
break;
}
case SUPER: {
Type bound = lowerBound(s);
- undetvar.lobounds = undetvar.lobounds.prepend(bound);
+ undetvar.addBound(InferenceBound.LOWER, bound, this);
break;
}
}
@@ -1227,7 +1507,10 @@ public class Types {
* Returns the lower bounds of the formals of a method.
*/
public List lowerBoundArgtypes(Type t) {
- return map(t.getParameterTypes(), lowerBoundMapping);
+ return lowerBounds(t.getParameterTypes());
+ }
+ public List lowerBounds(List ts) {
+ return map(ts, lowerBoundMapping);
}
private final Mapping lowerBoundMapping = new Mapping("lowerBound") {
public Type apply(Type t) {
@@ -2019,6 +2302,15 @@ public class Types {
hasSameArgs(t, erasure(s)) || hasSameArgs(erasure(t), s);
}
+ public boolean overridesObjectMethod(TypeSymbol origin, Symbol msym) {
+ for (Scope.Entry e = syms.objectType.tsym.members().lookup(msym.name) ; e.scope != null ; e = e.next()) {
+ if (msym.overrides(e.sym, origin, Types.this, true)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
//
class ImplementationCache {
@@ -3166,6 +3458,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;
+ }
//
//
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
index eb68899a9bc..e9cad76d473 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
@@ -514,22 +514,6 @@ public class Annotate {
expectedType);
fatalError = true;
}
-
- // validate that all other elements of containing type has defaults
- scope = targetContainerType.tsym.members();
- error = false;
- for(Symbol elm : scope.getElements()) {
- if (elm.name != names.value &&
- elm.kind == Kinds.MTH &&
- ((MethodSymbol)elm).defaultValue == null) {
- log.error(pos,
- "invalid.containedby.annotation.elem.nondefault",
- targetContainerType,
- elm);
- containerValueSymbol = null;
- error = true;
- }
- }
if (error) {
fatalError = true;
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
index b9613fa31c1..e11297309c3 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -25,12 +25,10 @@
package com.sun.tools.javac.comp;
-import java.util.*;
-import java.util.Set;
-import javax.lang.model.element.ElementKind;
-import javax.tools.JavaFileObject;
-
import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
+import com.sun.tools.javac.comp.Infer.InferenceContext;
+import com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.util.*;
@@ -45,10 +43,16 @@ import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.comp.Check.CheckContext;
import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.TreeVisitor;
import com.sun.source.util.SimpleTreeVisitor;
+import java.util.*;
+import java.util.Set;
+import javax.lang.model.element.ElementKind;
+import javax.tools.JavaFileObject;
+
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Flags.ANNOTATION;
import static com.sun.tools.javac.code.Flags.BLOCK;
@@ -80,7 +84,9 @@ public class Attr extends JCTree.Visitor {
final Symtab syms;
final Resolve rs;
final Infer infer;
+ final DeferredAttr deferredAttr;
final Check chk;
+ final Flow flow;
final MemberEnter memberEnter;
final TreeMaker make;
final ConstFold cfolder;
@@ -106,10 +112,12 @@ public class Attr extends JCTree.Visitor {
syms = Symtab.instance(context);
rs = Resolve.instance(context);
chk = Check.instance(context);
+ flow = Flow.instance(context);
memberEnter = MemberEnter.instance(context);
make = TreeMaker.instance(context);
enter = Enter.instance(context);
infer = Infer.instance(context);
+ deferredAttr = DeferredAttr.instance(context);
cfolder = ConstFold.instance(context);
target = Target.instance(context);
types = Types.instance(context);
@@ -127,23 +135,31 @@ public class Attr extends JCTree.Visitor {
allowCovariantReturns = source.allowCovariantReturns();
allowAnonOuterThis = source.allowAnonOuterThis();
allowStringsInSwitch = source.allowStringsInSwitch();
+ allowPoly = source.allowPoly() && options.isSet("allowPoly");
+ allowLambda = source.allowLambda();
sourceName = source.name;
relax = (options.isSet("-retrofit") ||
options.isSet("-relax"));
findDiamonds = options.get("findDiamond") != null &&
source.allowDiamond();
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
+ identifyLambdaCandidate = options.getBoolean("identifyLambdaCandidate", false);
statInfo = new ResultInfo(NIL, Type.noType);
varInfo = new ResultInfo(VAR, Type.noType);
unknownExprInfo = new ResultInfo(VAL, Type.noType);
unknownTypeInfo = new ResultInfo(TYP, Type.noType);
+ recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
}
/** Switch: relax some constraints for retrofit mode.
*/
boolean relax;
+ /** Switch: support target-typing inference
+ */
+ boolean allowPoly;
+
/** Switch: support generics?
*/
boolean allowGenerics;
@@ -164,6 +180,10 @@ public class Attr extends JCTree.Visitor {
*/
boolean allowCovariantReturns;
+ /** Switch: support lambda expressions ?
+ */
+ boolean allowLambda;
+
/** Switch: allow references to surrounding object from anonymous
* objects during constructor call?
*/
@@ -185,6 +205,12 @@ public class Attr extends JCTree.Visitor {
*/
boolean useBeforeDeclarationWarning;
+ /**
+ * Switch: generate warnings whenever an anonymous inner class that is convertible
+ * to a lambda expression is found
+ */
+ boolean identifyLambdaCandidate;
+
/**
* Switch: allow strings in switch?
*/
@@ -207,15 +233,29 @@ public class Attr extends JCTree.Visitor {
* @param ownkind The computed kind of the tree
* @param resultInfo The expected result of the tree
*/
- Type check(JCTree tree, Type owntype, int ownkind, ResultInfo resultInfo) {
+ Type check(final JCTree tree, final Type found, final int ownkind, final ResultInfo resultInfo) {
+ InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext();
+ Type owntype = found;
if (owntype.tag != ERROR && resultInfo.pt.tag != METHOD && resultInfo.pt.tag != FORALL) {
- if ((ownkind & ~resultInfo.pkind) == 0) {
- owntype = resultInfo.check(tree, owntype);
+ if (inferenceContext.free(found)) {
+ inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt), new FreeTypeListener() {
+ @Override
+ public void typesInferred(InferenceContext inferenceContext) {
+ ResultInfo pendingResult =
+ resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
+ check(tree, inferenceContext.asInstType(found, types), ownkind, pendingResult);
+ }
+ });
+ return tree.type = resultInfo.pt;
} else {
- log.error(tree.pos(), "unexpected.type",
- kindNames(resultInfo.pkind),
- kindName(ownkind));
- owntype = types.createErrorType(owntype);
+ if ((ownkind & ~resultInfo.pkind) == 0) {
+ owntype = resultInfo.check(tree, owntype);
+ } else {
+ log.error(tree.pos(), "unexpected.type",
+ kindNames(resultInfo.pkind),
+ kindName(ownkind));
+ owntype = types.createErrorType(owntype);
+ }
}
}
tree.type = owntype;
@@ -262,6 +302,9 @@ public class Attr extends JCTree.Visitor {
case CLASSDEF:
//class def is always an owner
return ((JCClassDecl)env.tree).sym;
+ case LAMBDA:
+ //a lambda is an owner - return a fresh synthetic method symbol
+ return new MethodSymbol(0, names.empty, null, syms.methodClass);
case BLOCK:
//static/instance init blocks are owner
Symbol blockSym = env.info.scope.owner;
@@ -297,8 +340,6 @@ public class Attr extends JCTree.Visitor {
} else {
log.error(pos, "cant.assign.val.to.final.var", v);
}
- } else if ((v.flags() & EFFECTIVELY_FINAL) != 0) {
- v.flags_field &= ~EFFECTIVELY_FINAL;
}
}
@@ -431,14 +472,38 @@ public class Attr extends JCTree.Visitor {
static final long serialVersionUID = -6924771130405446405L;
private Env env;
private BreakAttr(Env env) {
- this.env = env;
+ this.env = copyEnv(env);
+ }
+
+ private Env copyEnv(Env env) {
+ Env newEnv =
+ env.dup(env.tree, env.info.dup(copyScope(env.info.scope)));
+ if (newEnv.outer != null) {
+ newEnv.outer = copyEnv(newEnv.outer);
+ }
+ return newEnv;
+ }
+
+ private Scope copyScope(Scope sc) {
+ Scope newScope = new Scope(sc.owner);
+ List elemsList = List.nil();
+ while (sc != null) {
+ for (Scope.Entry e = sc.elems ; e != null ; e = e.sibling) {
+ elemsList = elemsList.prepend(e.sym);
+ }
+ sc = sc.next;
+ }
+ for (Symbol s : elemsList) {
+ newScope.enter(s);
+ }
+ return newScope;
}
}
class ResultInfo {
- int pkind;
- Type pt;
- CheckContext checkContext;
+ final int pkind;
+ final Type pt;
+ final CheckContext checkContext;
ResultInfo(int pkind, Type pt) {
this(pkind, pt, chk.basicHandler);
@@ -450,15 +515,45 @@ public class Attr extends JCTree.Visitor {
this.checkContext = checkContext;
}
- protected Type check(DiagnosticPosition pos, Type found) {
+ protected Type check(final DiagnosticPosition pos, final Type found) {
return chk.checkType(pos, found, pt, checkContext);
}
+
+ protected ResultInfo dup(Type newPt) {
+ return new ResultInfo(pkind, newPt, checkContext);
+ }
}
- private final ResultInfo statInfo;
- private final ResultInfo varInfo;
- private final ResultInfo unknownExprInfo;
- private final ResultInfo unknownTypeInfo;
+ class RecoveryInfo extends ResultInfo {
+
+ public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext) {
+ super(Kinds.VAL, Type.recoveryType, new Check.NestedCheckContext(chk.basicHandler) {
+ @Override
+ public DeferredAttr.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));
+ }
+ }
+
+ final ResultInfo statInfo;
+ final ResultInfo varInfo;
+ final ResultInfo unknownExprInfo;
+ final ResultInfo unknownTypeInfo;
+ final ResultInfo recoveryInfo;
Type pt() {
return resultInfo.pt;
@@ -491,7 +586,7 @@ public class Attr extends JCTree.Visitor {
* @param env The environment visitor argument.
* @param resultInfo The result info visitor argument.
*/
- private Type attribTree(JCTree tree, Env env, ResultInfo resultInfo) {
+ Type attribTree(JCTree tree, Env env, ResultInfo resultInfo) {
Env prevEnv = this.env;
ResultInfo prevResult = this.resultInfo;
try {
@@ -563,9 +658,12 @@ public class Attr extends JCTree.Visitor {
*/
List attribArgs(List trees, Env env) {
ListBuffer argtypes = new ListBuffer();
- for (List l = trees; l.nonEmpty(); l = l.tail)
- argtypes.append(chk.checkNonVoid(
- l.head.pos(), types.upperBound(attribExpr(l.head, env, Infer.anyPoly))));
+ for (JCExpression arg : trees) {
+ Type argtype = allowPoly && TreeInfo.isPoly(arg, env.tree) ?
+ deferredAttr.new DeferredType(arg, env) :
+ chk.checkNonVoid(arg, attribExpr(arg, env, Infer.anyPoly));
+ argtypes.append(argtype);
+ }
return argtypes.toList();
}
@@ -934,7 +1032,10 @@ public class Attr extends JCTree.Visitor {
chk.checkDeprecatedAnnotation(tree.pos(), v);
if (tree.init != null) {
- if ((v.flags_field & FINAL) != 0 && !tree.init.hasTag(NEWCLASS)) {
+ if ((v.flags_field & FINAL) != 0 &&
+ !tree.init.hasTag(NEWCLASS) &&
+ !tree.init.hasTag(LAMBDA) &&
+ !tree.init.hasTag(REFERENCE)) {
// In this case, `v' is final. Ensure that it's initializer is
// evaluated.
v.getConstValue(); // ensure initializer is evaluated
@@ -979,8 +1080,11 @@ public class Attr extends JCTree.Visitor {
// Create a new local environment with a local scope.
Env localEnv =
env.dup(tree, env.info.dup(env.info.scope.dup()));
- attribStats(tree.stats, localEnv);
- localEnv.info.scope.leave();
+ try {
+ attribStats(tree.stats, localEnv);
+ } finally {
+ localEnv.info.scope.leave();
+ }
}
result = null;
}
@@ -1000,43 +1104,51 @@ public class Attr extends JCTree.Visitor {
public void visitForLoop(JCForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
- attribStats(tree.init, loopEnv);
- if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType);
- loopEnv.tree = tree; // before, we were not in loop!
- attribStats(tree.step, loopEnv);
- attribStat(tree.body, loopEnv);
- loopEnv.info.scope.leave();
- result = null;
+ try {
+ attribStats(tree.init, loopEnv);
+ if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType);
+ loopEnv.tree = tree; // before, we were not in loop!
+ attribStats(tree.step, loopEnv);
+ attribStat(tree.body, loopEnv);
+ result = null;
+ }
+ finally {
+ loopEnv.info.scope.leave();
+ }
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
- attribStat(tree.var, loopEnv);
- Type exprType = types.upperBound(attribExpr(tree.expr, loopEnv));
- chk.checkNonVoid(tree.pos(), exprType);
- Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
- if (elemtype == null) {
- // or perhaps expr implements Iterable?
- Type base = types.asSuper(exprType, syms.iterableType.tsym);
- if (base == null) {
- log.error(tree.expr.pos(),
- "foreach.not.applicable.to.type",
- exprType,
- diags.fragment("type.req.array.or.iterable"));
- elemtype = types.createErrorType(exprType);
- } else {
- List iterableParams = base.allparams();
- elemtype = iterableParams.isEmpty()
- ? syms.objectType
- : types.upperBound(iterableParams.head);
+ try {
+ attribStat(tree.var, loopEnv);
+ Type exprType = types.upperBound(attribExpr(tree.expr, loopEnv));
+ chk.checkNonVoid(tree.pos(), exprType);
+ Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
+ if (elemtype == null) {
+ // or perhaps expr implements Iterable?
+ Type base = types.asSuper(exprType, syms.iterableType.tsym);
+ if (base == null) {
+ log.error(tree.expr.pos(),
+ "foreach.not.applicable.to.type",
+ exprType,
+ diags.fragment("type.req.array.or.iterable"));
+ elemtype = types.createErrorType(exprType);
+ } else {
+ List iterableParams = base.allparams();
+ elemtype = iterableParams.isEmpty()
+ ? syms.objectType
+ : types.upperBound(iterableParams.head);
+ }
}
+ chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
+ loopEnv.tree = tree; // before, we were not in loop!
+ attribStat(tree.body, loopEnv);
+ result = null;
+ }
+ finally {
+ loopEnv.info.scope.leave();
}
- chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
- loopEnv.tree = tree; // before, we were not in loop!
- attribStat(tree.body, loopEnv);
- loopEnv.info.scope.leave();
- result = null;
}
public void visitLabelled(JCLabeledStatement tree) {
@@ -1062,61 +1174,69 @@ public class Attr extends JCTree.Visitor {
Env switchEnv =
env.dup(tree, env.info.dup(env.info.scope.dup()));
- boolean enumSwitch =
- allowEnums &&
- (seltype.tsym.flags() & Flags.ENUM) != 0;
- boolean stringSwitch = false;
- if (types.isSameType(seltype, syms.stringType)) {
- if (allowStringsInSwitch) {
- stringSwitch = true;
- } else {
- log.error(tree.selector.pos(), "string.switch.not.supported.in.source", sourceName);
- }
- }
- if (!enumSwitch && !stringSwitch)
- seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType);
+ try {
- // Attribute all cases and
- // check that there are no duplicate case labels or default clauses.
- Set
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/AttrContext.java b/langtools/src/share/classes/com/sun/tools/javac/comp/AttrContext.java
index 017d6bcf2c0..568ad6766a3 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/AttrContext.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/AttrContext.java
@@ -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() + "]";
}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
index d3a9e3bc73e..87651481548 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
@@ -40,6 +40,9 @@ 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;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Flags.ANNOTATION;
@@ -66,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;
@@ -98,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);
@@ -416,7 +421,7 @@ public class Check {
* checks - depending on the check context, meaning of 'compatibility' might
* vary significantly.
*/
- interface CheckContext {
+ public interface CheckContext {
/**
* Is type 'found' compatible with type 'req' in given context
*/
@@ -429,6 +434,12 @@ public class Check {
* Obtain a warner for this check context
*/
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req);
+
+ public Infer.InferenceContext inferenceContext();
+
+ public DeferredAttr.DeferredAttrContext deferredAttrContext();
+
+ public boolean allowBoxing();
}
/**
@@ -455,6 +466,18 @@ public class Check {
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
return enclosingContext.checkWarner(pos, found, req);
}
+
+ public Infer.InferenceContext inferenceContext() {
+ return enclosingContext.inferenceContext();
+ }
+
+ public DeferredAttrContext deferredAttrContext() {
+ return enclosingContext.deferredAttrContext();
+ }
+
+ public boolean allowBoxing() {
+ return enclosingContext.allowBoxing();
+ }
}
/**
@@ -471,6 +494,18 @@ public class Check {
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
return convertWarner(pos, found, req);
}
+
+ public InferenceContext inferenceContext() {
+ return infer.emptyContext;
+ }
+
+ public DeferredAttrContext deferredAttrContext() {
+ return deferredAttr.emptyDeferredAttrContext;
+ }
+
+ public boolean allowBoxing() {
+ return true;
+ }
};
/** Check that a given type is assignable to a given proto-type.
@@ -483,7 +518,16 @@ public class Check {
return checkType(pos, found, req, basicHandler);
}
- Type checkType(final DiagnosticPosition pos, Type found, Type req, CheckContext checkContext) {
+ Type checkType(final DiagnosticPosition pos, final Type found, final Type req, final CheckContext checkContext) {
+ final Infer.InferenceContext inferenceContext = checkContext.inferenceContext();
+ if (inferenceContext.free(req)) {
+ inferenceContext.addFreeTypeListener(List.of(req), new FreeTypeListener() {
+ @Override
+ public void typesInferred(InferenceContext inferenceContext) {
+ checkType(pos, found, inferenceContext.asInstType(req, types), checkContext);
+ }
+ });
+ }
if (req.tag == ERROR)
return req;
if (req.tag == NONE)
@@ -523,9 +567,9 @@ public class Check {
*/
public void checkRedundantCast(Env env, JCTypeCast tree) {
if (!tree.type.isErroneous() &&
- (env.info.lint == null || env.info.lint.isEnabled(Lint.LintCategory.CAST))
- && types.isSameType(tree.expr.type, tree.clazz.type)
- && !is292targetTypeCast(tree)) {
+ (env.info.lint == null || env.info.lint.isEnabled(Lint.LintCategory.CAST))
+ && types.isSameType(tree.expr.type, tree.clazz.type)
+ && !is292targetTypeCast(tree)) {
log.warning(Lint.LintCategory.CAST,
tree.pos(), "redundant.cast", tree.expr.type);
}
@@ -604,6 +648,22 @@ public class Check {
return t;
}
+ /** Check that type is a valid qualifier for a constructor reference expression
+ */
+ Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
+ t = checkClassType(pos, t);
+ if (t.tag == CLASS) {
+ if ((t.tsym.flags() & (ABSTRACT | INTERFACE)) != 0) {
+ log.error(pos, "abstract.cant.be.instantiated");
+ t = types.createErrorType(t);
+ } else if ((t.tsym.flags() & ENUM) != 0) {
+ log.error(pos, "enum.cant.be.instantiated");
+ t = types.createErrorType(t);
+ }
+ }
+ return t;
+ }
+
/** Check that type is a class or interface type.
* @param pos Position to be used for error reporting.
* @param t The type to be checked.
@@ -796,29 +856,34 @@ public class Check {
sym.owner == syms.enumSym)
formals = formals.tail.tail;
List args = argtrees;
- while (formals.head != last) {
- JCTree arg = args.head;
- Warner warn = convertWarner(arg.pos(), arg.type, formals.head);
- assertConvertible(arg, arg.type, formals.head, warn);
- args = args.tail;
- formals = formals.tail;
- }
- if (useVarargs) {
- Type varArg = types.elemtype(last);
- while (args.tail != null) {
+ DeferredAttr.DeferredTypeMap checkDeferredMap =
+ deferredAttr.new DeferredTypeMap(DeferredAttr.AttrMode.CHECK, sym, env.info.pendingResolutionPhase);
+ if (args != null) {
+ //this is null when type-checking a method reference
+ while (formals.head != last) {
JCTree arg = args.head;
- Warner warn = convertWarner(arg.pos(), arg.type, varArg);
- assertConvertible(arg, arg.type, varArg, warn);
+ Warner warn = convertWarner(arg.pos(), arg.type, formals.head);
+ assertConvertible(arg, arg.type, formals.head, warn);
args = args.tail;
+ formals = formals.tail;
+ }
+ if (useVarargs) {
+ Type varArg = types.elemtype(last);
+ while (args.tail != null) {
+ JCTree arg = args.head;
+ Warner warn = convertWarner(arg.pos(), arg.type, varArg);
+ assertConvertible(arg, arg.type, varArg, warn);
+ args = args.tail;
+ }
+ } else if ((sym.flags() & VARARGS) != 0 && allowVarargs) {
+ // non-varargs call to varargs method
+ Type varParam = owntype.getParameterTypes().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",
+ types.elemtype(varParam), varParam);
}
- } else if ((sym.flags() & VARARGS) != 0 && allowVarargs) {
- // non-varargs call to varargs method
- Type varParam = owntype.getParameterTypes().last();
- Type lastArg = 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",
- types.elemtype(varParam), varParam);
}
if (unchecked) {
warnUnchecked(env.tree.pos(),
@@ -826,7 +891,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(),
@@ -853,6 +918,9 @@ public class Check {
case NEWCLASS:
((JCNewClass) tree).varargsElement = elemtype;
break;
+ case REFERENCE:
+ ((JCMemberReference) tree).varargsElement = elemtype;
+ break;
default:
throw new AssertionError(""+tree);
}
@@ -870,6 +938,65 @@ public class Check {
return;
}
+ void checkAccessibleFunctionalDescriptor(DiagnosticPosition pos, Env env, Type desc) {
+ AccessChecker accessChecker = new AccessChecker(env);
+ //check args accessibility (only if implicit parameter types)
+ for (Type arg : desc.getParameterTypes()) {
+ if (!accessChecker.visit(arg)) {
+ log.error(pos, "cant.access.arg.type.in.functional.desc", arg);
+ return;
+ }
+ }
+ //check return type accessibility
+ if (!accessChecker.visit(desc.getReturnType())) {
+ log.error(pos, "cant.access.return.in.functional.desc", desc.getReturnType());
+ return;
+ }
+ //check thrown types accessibility
+ for (Type thrown : desc.getThrownTypes()) {
+ if (!accessChecker.visit(thrown)) {
+ log.error(pos, "cant.access.thrown.in.functional.desc", thrown);
+ return;
+ }
+ }
+ }
+
+ class AccessChecker extends Types.UnaryVisitor {
+
+ Env env;
+
+ AccessChecker(Env env) {
+ this.env = env;
+ }
+
+ Boolean visit(List ts) {
+ for (Type t : ts) {
+ if (!visit(t))
+ return false;
+ }
+ return true;
+ }
+
+ public Boolean visitType(Type t, Void s) {
+ return true;
+ }
+
+ @Override
+ public Boolean visitArrayType(ArrayType t, Void s) {
+ return visit(t.elemtype);
+ }
+
+ @Override
+ public Boolean visitClassType(ClassType t, Void s) {
+ return rs.isAccessible(env, t, true) &&
+ visit(t.getTypeArguments());
+ }
+
+ @Override
+ public Boolean visitWildcardType(WildcardType t, Void s) {
+ return visit(t.type);
+ }
+ };
/**
* Check that type 't' is a valid instantiation of a generic class
* (see JLS 4.5)
@@ -2470,6 +2597,7 @@ public class Check {
validateDocumented(t.tsym, s, pos);
validateInherited(t.tsym, s, pos);
validateTarget(t.tsym, s, pos);
+ validateDefault(t.tsym, s, pos);
}
/**
@@ -2650,6 +2778,21 @@ public class Check {
return true;
}
+ private void validateDefault(Symbol container, Symbol contained, DiagnosticPosition pos) {
+ // validate that all other elements of containing type has defaults
+ Scope scope = container.members();
+ for(Symbol elm : scope.getElements()) {
+ if (elm.name != names.value &&
+ elm.kind == Kinds.MTH &&
+ ((MethodSymbol)elm).defaultValue == null) {
+ log.error(pos,
+ "invalid.containedby.annotation.elem.nondefault",
+ container,
+ elm);
+ }
+ }
+ }
+
/** Is s a method symbol that overrides a method in a superclass? */
boolean isOverrider(Symbol s) {
if (s.kind != MTH || s.isStatic())
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
new file mode 100644
index 00000000000..7405ca109a3
--- /dev/null
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
@@ -0,0 +1,640 @@
+/*
+ * 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.
+ *
+ *
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.
+ */
+public class DeferredAttr extends JCTree.Visitor {
+ protected static final Context.Key deferredAttrKey =
+ new Context.Key();
+
+ 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 env;
+ AttrMode mode;
+ SpeculativeCache speculativeCache;
+
+ DeferredType(JCExpression tree, Env 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> cache =
+ new WeakHashMap>();
+
+ 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 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 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 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 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 env, ResultInfo resultInfo) {
+ JCTree newTree = new TreeCopier(make).copy(tree);
+ Env speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
+ speculativeEnv.info.scope.owner = env.info.scope.owner;
+ Filter prevDeferDiagsFilter = log.deferredDiagFilter;
+ Queue prevDeferredDiags = log.deferredDiagnostics;
+ final JavaFileObject currentSource = log.currentSourceFile();
+ try {
+ log.deferredDiagnostics = new ListBuffer();
+ log.deferredDiagFilter = new Filter() {
+ 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 deferredAttrNodes = new ArrayList();
+
+ 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 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 stuckVars = new HashSet();
+ 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 stuckVars;
+
+ DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List 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 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(attr.new RecoveryInfo(deferredAttrContext));
+ 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);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the list of inference variables that need to be inferred before
+ * an AST node can be type-checked
+ */
+ @SuppressWarnings("fallthrough")
+ List stuckVars(JCTree tree, ResultInfo resultInfo) {
+ if (resultInfo.pt.tag == NONE || resultInfo.pt.isErroneous()) {
+ return List.nil();
+ } else {
+ StuckChecker sc = new StuckChecker(resultInfo);
+ sc.scan(tree);
+ return List.from(sc.stuckVars);
+ }
+ }
+
+ /**
+ * This visitor is used to check that structural expressions conform
+ * to their target - this step is required as inference could end up
+ * inferring types that make some of the nested expressions incompatible
+ * with their corresponding instantiated target
+ */
+ class StuckChecker extends TreeScanner {
+
+ Type pt;
+ Filter treeFilter;
+ Infer.InferenceContext inferenceContext;
+ Set stuckVars = new HashSet();
+
+ final Filter argsFilter = new Filter() {
+ public boolean accepts(JCTree t) {
+ switch (t.getTag()) {
+ case CONDEXPR:
+ case LAMBDA:
+ case PARENS:
+ case REFERENCE:
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
+
+ final Filter lambdaBodyFilter = new Filter() {
+ public boolean accepts(JCTree t) {
+ switch (t.getTag()) {
+ case BLOCK: case CASE: case CATCH: case DOLOOP:
+ case FOREACHLOOP: case FORLOOP: case RETURN:
+ case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP:
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
+
+ StuckChecker(ResultInfo resultInfo) {
+ this.pt = resultInfo.pt;
+ this.inferenceContext = resultInfo.checkContext.inferenceContext();
+ this.treeFilter = argsFilter;
+ }
+
+ @Override
+ public void scan(JCTree tree) {
+ if (tree != null && treeFilter.accepts(tree)) {
+ super.scan(tree);
+ }
+ }
+
+ @Override
+ public void visitLambda(JCLambda tree) {
+ Type prevPt = pt;
+ Filter prevFilter = treeFilter;
+ try {
+ if (inferenceContext.inferenceVars().contains(pt)) {
+ stuckVars.add(pt);
+ }
+ if (!types.isFunctionalInterface(pt.tsym)) {
+ return;
+ }
+ Type descType = types.findDescriptorType(pt);
+ List freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
+ if (!TreeInfo.isExplicitLambda(tree) &&
+ freeArgVars.nonEmpty()) {
+ stuckVars.addAll(freeArgVars);
+ }
+ pt = descType.getReturnType();
+ if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
+ scan(tree.getBody());
+ } else {
+ treeFilter = lambdaBodyFilter;
+ super.visitLambda(tree);
+ }
+ } finally {
+ pt = prevPt;
+ treeFilter = prevFilter;
+ }
+ }
+
+ @Override
+ public void visitReference(JCMemberReference tree) {
+ scan(tree.expr);
+ if (inferenceContext.inferenceVars().contains(pt)) {
+ stuckVars.add(pt);
+ return;
+ }
+ if (!types.isFunctionalInterface(pt.tsym)) {
+ return;
+ }
+ Type descType = types.findDescriptorType(pt);
+ List freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
+ stuckVars.addAll(freeArgVars);
+ }
+
+ @Override
+ public void visitReturn(JCReturn tree) {
+ Filter prevFilter = treeFilter;
+ try {
+ treeFilter = argsFilter;
+ if (tree.expr != null) {
+ scan(tree.expr);
+ }
+ } finally {
+ treeFilter = prevFilter;
+ }
+ }
+ }
+}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java
index 3dad6e3b2c3..55e2e7144f1 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java
@@ -50,8 +50,8 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
* (see AssignAnalyzer) ensures that each variable is assigned when used. Definite
* unassignment analysis (see AssignAnalyzer) in ensures that no final variable
* is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer)
- * determines that local variables accessed within the scope of an inner class are
- * either final or effectively-final.
+ * determines that local variables accessed within the scope of an inner class/lambda
+ * are either final or effectively-final.
*
*
The JLS has a number of problems in the
* specification of these flow analysis problems. This implementation
@@ -211,6 +211,29 @@ public class Flow {
new CaptureAnalyzer().analyzeTree(env, make);
}
+ public void analyzeLambda(Env env, JCLambda that, TreeMaker make, boolean speculative) {
+ java.util.Queue prevDeferredDiagnostics = log.deferredDiagnostics;
+ Filter prevDeferDiagsFilter = log.deferredDiagFilter;
+ //we need to disable diagnostics temporarily; the problem is that if
+ //a lambda expression contains e.g. an unreachable statement, an error
+ //message will be reported and will cause compilation to skip the flow analyis
+ //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
+ //related errors, which will allow for more errors to be detected
+ if (!speculative) {
+ log.deferAll();
+ log.deferredDiagnostics = ListBuffer.lb();
+ }
+ try {
+ new AliveAnalyzer().analyzeTree(env, that, make);
+ new FlowAnalyzer().analyzeTree(env, that, make);
+ } finally {
+ if (!speculative) {
+ log.deferredDiagFilter = prevDeferDiagsFilter;
+ log.deferredDiagnostics = prevDeferredDiagnostics;
+ }
+ }
+ }
+
/**
* Definite assignment scan mode
*/
@@ -659,6 +682,27 @@ public class Flow {
}
}
+ @Override
+ public void visitLambda(JCLambda tree) {
+ if (tree.type != null &&
+ tree.type.isErroneous()) {
+ return;
+ }
+
+ ListBuffer prevPending = pendingExits;
+ boolean prevAlive = alive;
+ try {
+ pendingExits = ListBuffer.lb();
+ alive = true;
+ scanStat(tree.body);
+ tree.canCompleteNormally = alive;
+ }
+ finally {
+ pendingExits = prevPending;
+ alive = prevAlive;
+ }
+ }
+
public void visitTopLevel(JCCompilationUnit tree) {
// Do nothing for TopLevel since each class is visited individually
}
@@ -670,6 +714,9 @@ public class Flow {
/** Perform definite assignment/unassignment analysis on a tree.
*/
public void analyzeTree(Env env, TreeMaker make) {
+ analyzeTree(env, env.tree, make);
+ }
+ public void analyzeTree(Env env, JCTree tree, TreeMaker make) {
try {
attrEnv = env;
Flow.this.make = make;
@@ -1185,6 +1232,29 @@ public class Flow {
}
}
+ @Override
+ public void visitLambda(JCLambda tree) {
+ if (tree.type != null &&
+ tree.type.isErroneous()) {
+ return;
+ }
+ List prevCaught = caught;
+ List prevThrown = thrown;
+ ListBuffer prevPending = pendingExits;
+ try {
+ pendingExits = ListBuffer.lb();
+ caught = List.of(syms.throwableType); //inhibit exception checking
+ thrown = List.nil();
+ scan(tree.body);
+ tree.inferredThrownTypes = thrown;
+ }
+ finally {
+ pendingExits = prevPending;
+ caught = prevCaught;
+ thrown = prevThrown;
+ }
+ }
+
public void visitTopLevel(JCCompilationUnit tree) {
// Do nothing for TopLevel since each class is visited individually
}
@@ -1267,6 +1337,10 @@ public class Flow {
*/
int nextadr;
+ /** The first variable sequence number in a block that can return.
+ */
+ int returnadr;
+
/** The list of unreferenced automatic resources.
*/
Scope unrefdResources;
@@ -1296,8 +1370,8 @@ public class Flow {
@Override
void markDead() {
- inits.inclRange(firstadr, nextadr);
- uninits.inclRange(firstadr, nextadr);
+ inits.inclRange(returnadr, nextadr);
+ uninits.inclRange(returnadr, nextadr);
}
/*-------------- Processing variables ----------------------*/
@@ -1318,11 +1392,7 @@ public class Flow {
* index into the vars array.
*/
void newVar(VarSymbol sym) {
- if (nextadr == vars.length) {
- VarSymbol[] newvars = new VarSymbol[nextadr * 2];
- System.arraycopy(vars, 0, newvars, 0, nextadr);
- vars = newvars;
- }
+ vars = ArrayUtils.ensureCapacity(vars, nextadr);
if ((sym.flags() & FINAL) == 0) {
sym.flags_field |= EFFECTIVELY_FINAL;
}
@@ -1556,6 +1626,7 @@ public class Flow {
Bits uninitsPrev = uninits.dup();
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
+ int returnadrPrev = returnadr;
Lint lintPrev = lint;
lint = lint.augment(tree.sym.annotations);
@@ -1600,6 +1671,7 @@ public class Flow {
uninits = uninitsPrev;
nextadr = nextadrPrev;
firstadr = firstadrPrev;
+ returnadr = returnadrPrev;
lint = lintPrev;
}
}
@@ -1984,6 +2056,35 @@ public class Flow {
scan(tree.def);
}
+ @Override
+ public void visitLambda(JCLambda tree) {
+ Bits prevUninits = uninits;
+ Bits prevInits = inits;
+ int returnadrPrev = returnadr;
+ ListBuffer prevPending = pendingExits;
+ try {
+ returnadr = nextadr;
+ pendingExits = new ListBuffer();
+ for (List l = tree.params; l.nonEmpty(); l = l.tail) {
+ JCVariableDecl def = l.head;
+ scan(def);
+ inits.incl(def.sym.adr);
+ uninits.excl(def.sym.adr);
+ }
+ if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
+ scanExpr(tree.body);
+ } else {
+ scan(tree.body);
+ }
+ }
+ finally {
+ returnadr = returnadrPrev;
+ uninits = prevUninits;
+ inits = prevInits;
+ pendingExits = prevPending;
+ }
+ }
+
public void visitNewArray(JCNewArray tree) {
scanExprs(tree.dims);
scanExprs(tree.elems);
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java
index 659eeda274d..548bb9d8479 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java
@@ -25,18 +25,24 @@
package com.sun.tools.javac.comp;
+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;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.code.*;
-import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.code.Symbol.*;
-import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
-import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
import static com.sun.tools.javac.code.TypeTags.*;
/** Helper class for type parameter inference, used by the attribution phase.
@@ -57,6 +63,7 @@ public class Infer {
Types types;
Check chk;
Resolve rs;
+ DeferredAttr deferredAttr;
Log log;
JCDiagnostic.Factory diags;
@@ -72,45 +79,44 @@ 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);
inferenceException = new InferenceException(diags);
-
}
+ /**
+ * This exception class is design to store a list of diagnostics corresponding
+ * to inference errors that can arise during a method applicability check.
+ */
public static class InferenceException extends InapplicableMethodException {
private static final long serialVersionUID = 0;
+ List messages = List.nil();
+
InferenceException(JCDiagnostic.Factory diags) {
super(diags);
}
+
+ @Override
+ InapplicableMethodException setMessage(JCDiagnostic diag) {
+ messages = messages.append(diag);
+ return this;
+ }
+
+ @Override
+ public JCDiagnostic getDiagnostic() {
+ return messages.head;
+ }
+
+ void clear() {
+ messages = List.nil();
+ }
}
private final InferenceException inferenceException;
-/***************************************************************************
- * Auxiliary type values and classes
- ***************************************************************************/
-
- /** A mapping that turns type variables into undetermined type variables.
- */
- List makeUndetvars(List tvars) {
- List undetvars = Type.map(tvars, fromTypeVarFun);
- for (Type t : undetvars) {
- UndetVar uv = (UndetVar)t;
- uv.hibounds = types.getBounds((TypeVar)uv.qtype);
- }
- return undetvars;
- }
- //where
- Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
- public Type apply(Type t) {
- if (t.tag == TYPEVAR) return new UndetVar(t);
- else return t.map(this);
- }
- };
-
/***************************************************************************
* Mini/Maximization of UndetVars
***************************************************************************/
@@ -118,9 +124,9 @@ public class Infer {
/** Instantiate undetermined type variable to its minimal upper bound.
* Throw a NoInstanceException if this not possible.
*/
- void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
- List hibounds = Type.filter(that.hibounds, errorFilter);
- if (that.eq.isEmpty()) {
+ void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
+ List hibounds = Type.filter(that.getBounds(InferenceBound.UPPER), boundFilter);
+ if (that.getBounds(InferenceBound.EQ).isEmpty()) {
if (hibounds.isEmpty())
that.inst = syms.objectType;
else if (hibounds.tail.isEmpty())
@@ -128,7 +134,7 @@ public class Infer {
else
that.inst = types.glb(hibounds);
} else {
- that.inst = that.eq.head;
+ that.inst = that.getBounds(InferenceBound.EQ).head;
}
if (that.inst == null ||
that.inst.isErroneous())
@@ -137,10 +143,10 @@ public class Infer {
that.qtype, hibounds);
}
- private Filter errorFilter = new Filter() {
+ private Filter boundFilter = new Filter() {
@Override
public boolean accepts(Type t) {
- return !t.isErroneous();
+ return !t.isErroneous() && t.tag != BOT;
}
};
@@ -148,11 +154,12 @@ public class Infer {
* Throw a NoInstanceException if this not possible.
*/
void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
- List lobounds = Type.filter(that.lobounds, errorFilter);
- if (that.eq.isEmpty()) {
- if (lobounds.isEmpty())
- that.inst = syms.botType;
- else if (lobounds.tail.isEmpty())
+ List lobounds = Type.filter(that.getBounds(InferenceBound.LOWER), boundFilter);
+ if (that.getBounds(InferenceBound.EQ).isEmpty()) {
+ if (lobounds.isEmpty()) {
+ //do nothing - the inference variable is under-constrained
+ return;
+ } else if (lobounds.tail.isEmpty())
that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
else {
that.inst = types.lub(lobounds);
@@ -162,120 +169,99 @@ public class Infer {
.setMessage("no.unique.minimal.instance.exists",
that.qtype, lobounds);
} else {
- that.inst = that.eq.head;
+ that.inst = that.getBounds(InferenceBound.EQ).head;
}
}
- Type asUndetType(Type t, List undetvars) {
- return types.subst(t, inferenceVars(undetvars), undetvars);
- }
-
- List inferenceVars(List undetvars) {
- ListBuffer tvars = ListBuffer.lb();
- for (Type uv : undetvars) {
- tvars.append(((UndetVar)uv).qtype);
- }
- return tvars.toList();
- }
-
/***************************************************************************
* Exported Methods
***************************************************************************/
- /** Try to instantiate expression type `that' to given type `to'.
- * If a maximal instantiation exists which makes this type
- * a subtype of type `to', return the instantiated type.
- * If no instantiation exists, or if several incomparable
- * best instantiations exist throw a NoInstanceException.
+ /**
+ * Instantiate uninferred inference variables (JLS 15.12.2.8). First
+ * if the method return type is non-void, we derive constraints from the
+ * expected type - then we use declared bound well-formedness to derive additional
+ * constraints. If no instantiation exists, or if several incomparable
+ * best instantiations exist throw a NoInstanceException.
*/
- public List instantiateUninferred(DiagnosticPosition pos,
- List undetvars,
- List tvars,
- MethodType mtype,
- Attr.ResultInfo resultInfo,
- Warner warn) throws InferenceException {
+ public void instantiateUninferred(DiagnosticPosition pos,
+ InferenceContext inferenceContext,
+ MethodType mtype,
+ 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;
}
- Type qtype1 = types.subst(mtype.getReturnType(), tvars, undetvars);
+ Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types);
if (!types.isSubtype(qtype1,
qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
throw inferenceException
- .setMessage("infer.no.conforming.instance.exists",
- tvars, mtype.getReturnType(), to);
+ .setMessage("infer.no.conforming.instance.exists",
+ inferenceContext.restvars(), mtype.getReturnType(), to);
}
- List insttypes;
while (true) {
boolean stuck = true;
- insttypes = List.nil();
- for (Type t : undetvars) {
+ for (Type t : inferenceContext.undetvars) {
UndetVar uv = (UndetVar)t;
- if (uv.inst == null && (uv.eq.nonEmpty() || !Type.containsAny(uv.hibounds, tvars))) {
+ if (uv.inst == null && (uv.getBounds(InferenceBound.EQ).nonEmpty() ||
+ !inferenceContext.free(uv.getBounds(InferenceBound.UPPER)))) {
maximizeInst((UndetVar)t, warn);
stuck = false;
}
- insttypes = insttypes.append(uv.inst == null ? uv.qtype : uv.inst);
}
- if (!Type.containsAny(insttypes, tvars)) {
+ if (inferenceContext.restvars().isEmpty()) {
//all variables have been instantiated - exit
break;
} else if (stuck) {
//some variables could not be instantiated because of cycles in
//upper bounds - provide a (possibly recursive) default instantiation
- insttypes = types.subst(insttypes,
- tvars,
- instantiateAsUninferredVars(undetvars, tvars));
+ instantiateAsUninferredVars(inferenceContext);
break;
} else {
//some variables have been instantiated - replace newly instantiated
//variables in remaining upper bounds and continue
- for (Type t : undetvars) {
+ for (Type t : inferenceContext.undetvars) {
UndetVar uv = (UndetVar)t;
- uv.hibounds = types.subst(uv.hibounds, tvars, insttypes);
+ uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
}
}
}
- return insttypes;
}
/**
* Infer cyclic inference variables as described in 15.12.2.8.
*/
- private List instantiateAsUninferredVars(List undetvars, List tvars) {
- Assert.check(undetvars.length() == tvars.length());
- ListBuffer insttypes = ListBuffer.lb();
+ private void instantiateAsUninferredVars(InferenceContext inferenceContext) {
ListBuffer todo = ListBuffer.lb();
//step 1 - create fresh tvars
- for (Type t : undetvars) {
+ for (Type t : inferenceContext.undetvars) {
UndetVar uv = (UndetVar)t;
if (uv.inst == null) {
TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
- fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.hibounds), null);
+ fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), null);
todo.append(uv);
uv.inst = fresh_tvar.type;
}
- insttypes.append(uv.inst);
}
//step 2 - replace fresh tvars in their bounds
- List formals = tvars;
+ List formals = inferenceContext.inferenceVars();
for (Type t : todo) {
UndetVar uv = (UndetVar)t;
TypeVar ct = (TypeVar)uv.inst;
- ct.bound = types.glb(types.subst(types.getBounds(ct), tvars, insttypes.toList()));
+ ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types));
if (ct.bound.isErroneous()) {
//report inference error if glb fails
reportBoundError(uv, BoundErrorKind.BAD_UPPER);
}
formals = formals.tail;
}
- return insttypes.toList();
}
- /** Instantiate method type `mt' by finding instantiations of
- * `tvars' so that method can be applied to `argtypes'.
+ /** Instantiate a generic method type by finding instantiations for all its
+ * inference variables so that it can be applied to a given argument type list.
*/
public Type instantiateMethod(Env env,
List tvars,
@@ -285,85 +271,65 @@ public class Infer {
List argtypes,
boolean allowBoxing,
boolean useVarargs,
+ Resolve.MethodResolutionContext resolveContext,
Warner warn) throws InferenceException {
//-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
- List undetvars = makeUndetvars(tvars);
+ final InferenceContext inferenceContext = new InferenceContext(tvars, this, true);
+ inferenceException.clear();
- List capturedArgs =
- rs.checkRawArgumentsAcceptable(env, undetvars, argtypes, mt.getParameterTypes(),
- allowBoxing, useVarargs, warn, new InferenceCheckHandler(undetvars));
+ try {
+ 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 : undetvars)
- minimizeInst((UndetVar) t, warn);
-
- /** Type variables instantiated to bottom */
- ListBuffer restvars = new ListBuffer();
-
- /** Undet vars instantiated to bottom */
- final ListBuffer restundet = new ListBuffer();
-
- /** Instantiated types or TypeVars if under-constrained */
- ListBuffer insttypes = new ListBuffer();
-
- /** Instantiated types or UndetVars if under-constrained */
- ListBuffer undettypes = new ListBuffer();
-
- for (Type t : undetvars) {
- UndetVar uv = (UndetVar)t;
- if (uv.inst.tag == BOT) {
- restvars.append(uv.qtype);
- restundet.append(uv);
- insttypes.append(uv.qtype);
- undettypes.append(uv);
- uv.inst = null;
- } else {
- insttypes.append(uv.inst);
- undettypes.append(uv.inst);
+ // minimize as yet undetermined type variables
+ for (Type t : inferenceContext.undetvars) {
+ minimizeInst((UndetVar)t, warn);
}
- }
- checkWithinBounds(tvars, undetvars, insttypes.toList(), warn);
- mt = (MethodType)types.subst(mt, tvars, insttypes.toList());
+ checkWithinBounds(inferenceContext, warn);
- if (!restvars.isEmpty() && resultInfo != null) {
- List restInferred =
- instantiateUninferred(env.tree.pos(), restundet.toList(), restvars.toList(), mt, resultInfo, warn);
- checkWithinBounds(tvars, undetvars,
- types.subst(insttypes.toList(), restvars.toList(), restInferred), warn);
- mt = (MethodType)types.subst(mt, restvars.toList(), restInferred);
- if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
- log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
+ mt = (MethodType)inferenceContext.asInstType(mt, types);
+
+ List restvars = inferenceContext.restvars();
+
+ if (!restvars.isEmpty()) {
+ if (resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
+ instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
+ checkWithinBounds(inferenceContext, warn);
+ mt = (MethodType)inferenceContext.asInstType(mt, types);
+ if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
+ log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
+ }
+ }
}
- }
- if (restvars.isEmpty() || resultInfo != null) {
- // check that actuals conform to inferred formals
- checkArgumentsAcceptable(env, capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn);
+ // return instantiated version of method type
+ return mt;
+ } finally {
+ inferenceContext.notifyChange(types);
}
- // return instantiated version of method type
- return mt;
}
//where
/** inference check handler **/
class InferenceCheckHandler implements Resolve.MethodCheckHandler {
- List undetvars;
+ InferenceContext inferenceContext;
- public InferenceCheckHandler(List undetvars) {
- this.undetvars = undetvars;
+ public InferenceCheckHandler(InferenceContext inferenceContext) {
+ this.inferenceContext = inferenceContext;
}
public InapplicableMethodException arityMismatch() {
- return inferenceException.setMessage("infer.arg.length.mismatch", inferenceVars(undetvars));
+ return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars());
}
public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
String key = varargs ?
"infer.varargs.argument.mismatch" :
"infer.no.conforming.assignment.exists";
return inferenceException.setMessage(key,
- inferenceVars(undetvars), details);
+ inferenceContext.inferenceVars(), details);
}
public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
return inferenceException.setMessage("inaccessible.varargs.type",
@@ -371,51 +337,61 @@ public class Infer {
}
}
- private void checkArgumentsAcceptable(Env env, List actuals, List formals,
- boolean allowBoxing, boolean useVarargs, Warner warn) {
- try {
- rs.checkRawArgumentsAcceptable(env, actuals, formals,
- allowBoxing, useVarargs, warn);
- }
- catch (InapplicableMethodException ex) {
- // inferred method is not applicable
- throw inferenceException.setMessage(ex.getDiagnostic());
- }
- }
-
/** check that type parameters are within their bounds.
*/
- void checkWithinBounds(List tvars,
- List undetvars,
- List arguments,
- Warner warn)
- throws InferenceException {
- List args = arguments;
- for (Type t : undetvars) {
+ void checkWithinBounds(InferenceContext inferenceContext,
+ Warner warn) throws InferenceException {
+ //step 1 - check compatibility of instantiated type w.r.t. initial bounds
+ for (Type t : inferenceContext.undetvars) {
UndetVar uv = (UndetVar)t;
- uv.hibounds = types.subst(uv.hibounds, tvars, arguments);
- uv.lobounds = types.subst(uv.lobounds, tvars, arguments);
- uv.eq = types.subst(uv.eq, tvars, arguments);
- checkCompatibleUpperBounds(uv, tvars);
- if (args.head.tag != TYPEVAR || !args.head.containsAny(tvars)) {
- Type inst = args.head;
- for (Type u : uv.hibounds) {
- if (!types.isSubtypeUnchecked(inst, types.subst(u, tvars, undetvars), warn)) {
+ uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
+ checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
+ if (!inferenceContext.restvars().contains(uv.qtype)) {
+ Type inst = inferenceContext.asInstType(t, types);
+ for (Type u : uv.getBounds(InferenceBound.UPPER)) {
+ if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
reportBoundError(uv, BoundErrorKind.UPPER);
}
}
- for (Type l : uv.lobounds) {
- if (!types.isSubtypeUnchecked(types.subst(l, tvars, undetvars), inst, warn)) {
+ for (Type l : uv.getBounds(InferenceBound.LOWER)) {
+ Assert.check(!inferenceContext.free(l));
+ if (!types.isSubtypeUnchecked(l, inst, warn)) {
reportBoundError(uv, BoundErrorKind.LOWER);
}
}
- for (Type e : uv.eq) {
- if (!types.isSameType(inst, types.subst(e, tvars, undetvars))) {
+ for (Type e : uv.getBounds(InferenceBound.EQ)) {
+ Assert.check(!inferenceContext.free(e));
+ if (!types.isSameType(inst, e)) {
reportBoundError(uv, BoundErrorKind.EQ);
}
}
}
- args = args.tail;
+ }
+
+ //step 2 - check that eq bounds are consistent w.r.t. eq/lower bounds
+ for (Type t : inferenceContext.undetvars) {
+ UndetVar uv = (UndetVar)t;
+ //check eq bounds consistency
+ Type eq = null;
+ for (Type e : uv.getBounds(InferenceBound.EQ)) {
+ Assert.check(!inferenceContext.free(e));
+ if (eq != null && !types.isSameType(e, eq)) {
+ reportBoundError(uv, BoundErrorKind.EQ);
+ }
+ eq = e;
+ for (Type l : uv.getBounds(InferenceBound.LOWER)) {
+ Assert.check(!inferenceContext.free(l));
+ if (!types.isSubtypeUnchecked(l, e, warn)) {
+ reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
+ }
+ }
+ for (Type u : uv.getBounds(InferenceBound.UPPER)) {
+ if (inferenceContext.free(u)) continue;
+ if (!types.isSubtypeUnchecked(e, u, warn)) {
+ reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
+ }
+ }
+ }
}
}
@@ -423,7 +399,7 @@ public class Infer {
// VGJ: sort of inlined maximizeInst() below. Adding
// bounds can cause lobounds that are above hibounds.
ListBuffer hiboundsNoVars = ListBuffer.lb();
- for (Type t : Type.filter(uv.hibounds, errorFilter)) {
+ for (Type t : Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter)) {
if (!t.containsAny(tvars)) {
hiboundsNoVars.append(t);
}
@@ -444,25 +420,43 @@ public class Infer {
BAD_UPPER() {
@Override
InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("incompatible.upper.bounds", uv.qtype, uv.hibounds);
+ return ex.setMessage("incompatible.upper.bounds", uv.qtype,
+ uv.getBounds(InferenceBound.UPPER));
+ }
+ },
+ BAD_EQ_UPPER() {
+ @Override
+ InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
+ return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
+ uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
+ }
+ },
+ BAD_EQ_LOWER() {
+ @Override
+ InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
+ return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
+ uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
}
},
UPPER() {
@Override
InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst, uv.hibounds);
+ return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
+ uv.getBounds(InferenceBound.UPPER));
}
},
LOWER() {
@Override
InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst, uv.lobounds);
+ return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
+ uv.getBounds(InferenceBound.LOWER));
}
},
EQ() {
@Override
InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
- return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst, uv.eq);
+ return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
+ uv.getBounds(InferenceBound.EQ));
}
};
@@ -473,6 +467,75 @@ public class Infer {
throw bk.setMessage(inferenceException, uv);
}
+ //
+ /**
+ * This method is used to infer a suitable target functional interface in case
+ * the original parameterized interface contains wildcards. An inference process
+ * is applied so that wildcard bounds, as well as explicit lambda/method ref parameters
+ * (where applicable) are used to constraint the solution.
+ */
+ public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
+ List paramTypes, Check.CheckContext checkContext) {
+ if (types.capture(funcInterface) == funcInterface) {
+ //if capture doesn't change the type then return the target unchanged
+ //(this means the target contains no wildcards!)
+ return funcInterface;
+ } else {
+ Type formalInterface = funcInterface.tsym.type;
+ InferenceContext funcInterfaceContext =
+ new InferenceContext(funcInterface.tsym.type.getTypeArguments(), this, false);
+ if (paramTypes != null) {
+ //get constraints from explicit params (this is done by
+ //checking that explicit param types are equal to the ones
+ //in the functional interface descriptors)
+ List descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
+ if (descParameterTypes.size() != paramTypes.size()) {
+ checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
+ return types.createErrorType(funcInterface);
+ }
+ for (Type p : descParameterTypes) {
+ if (!types.isSameType(funcInterfaceContext.asFree(p, types), paramTypes.head)) {
+ checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
+ return types.createErrorType(funcInterface);
+ }
+ paramTypes = paramTypes.tail;
+ }
+ for (Type t : funcInterfaceContext.undetvars) {
+ UndetVar uv = (UndetVar)t;
+ minimizeInst(uv, Warner.noWarnings);
+ if (uv.inst == null &&
+ Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter).nonEmpty()) {
+ maximizeInst(uv, Warner.noWarnings);
+ }
+ }
+
+ formalInterface = funcInterfaceContext.asInstType(formalInterface, types);
+ }
+ ListBuffer typeargs = ListBuffer.lb();
+ List actualTypeargs = funcInterface.getTypeArguments();
+ //for remaining uninferred type-vars in the functional interface type,
+ //simply replace the wildcards with its bound
+ for (Type t : formalInterface.getTypeArguments()) {
+ if (actualTypeargs.head.tag == WILDCARD) {
+ WildcardType wt = (WildcardType)actualTypeargs.head;
+ typeargs.append(wt.type);
+ } else {
+ typeargs.append(actualTypeargs.head);
+ }
+ actualTypeargs = actualTypeargs.tail;
+ }
+ Type owntype = types.subst(formalInterface, funcInterfaceContext.inferenceVars(), typeargs.toList());
+ if (!chk.checkValidGenericType(owntype)) {
+ //if the inferred functional interface type is not well-formed,
+ //or if it's not a subtype of the original target, issue an error
+ checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
+ return types.createErrorType(funcInterface);
+ }
+ return owntype;
+ }
+ }
+ //
+
/**
* Compute a synthetic method type corresponding to the requested polymorphic
* method signature. The target return type is computed from the immediately
@@ -480,6 +543,7 @@ public class Infer {
*/
Type instantiatePolymorphicSignatureInstance(Env env,
MethodSymbol spMethod, // sig. poly. method or null if none
+ Resolve.MethodResolutionContext resolveContext,
List argtypes) {
final Type restype;
@@ -509,7 +573,7 @@ public class Infer {
restype = syms.objectType;
}
- List paramtypes = Type.map(argtypes, implicitArgType);
+ List paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
List exType = spMethod != null ?
spMethod.getThrownTypes() :
List.of(syms.throwableType); // make it throw all exceptions
@@ -521,14 +585,234 @@ 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
+ * (used by inference context)
+ */
+ class FromTypeVarFun extends Mapping {
+
+ boolean includeBounds;
+
+ FromTypeVarFun(boolean includeBounds) {
+ super("fromTypeVarFunWithBounds");
+ this.includeBounds = includeBounds;
+ }
+
+ public Type apply(Type t) {
+ if (t.tag == TYPEVAR) return new UndetVar((TypeVar)t, types, includeBounds);
+ else return t.map(this);
+ }
+ };
+
+ /**
+ * An inference context keeps track of the set of variables that are free
+ * in the current context. It provides utility methods for opening/closing
+ * types to their corresponding free/closed forms. It also provide hooks for
+ * attaching deferred post-inference action (see PendingCheck). Finally,
+ * it can be used as an entry point for performing upper/lower bound inference
+ * (see InferenceKind).
+ */
+ static class InferenceContext {
+
+ /**
+ * Single-method-interface for defining inference callbacks. Certain actions
+ * (i.e. subtyping checks) might need to be redone after all inference variables
+ * have been fixed.
+ */
+ interface FreeTypeListener {
+ void typesInferred(InferenceContext inferenceContext);
+ }
+
+ /** list of inference vars as undet vars */
+ List undetvars;
+
+ /** list of inference vars in this context */
+ List inferencevars;
+
+ java.util.Map> freeTypeListeners =
+ new java.util.HashMap>();
+
+ List freetypeListeners = List.nil();
+
+ public InferenceContext(List inferencevars, Infer infer, boolean includeBounds) {
+ this.undetvars = Type.map(inferencevars, infer.new FromTypeVarFun(includeBounds));
+ this.inferencevars = inferencevars;
+ }
+
+ /**
+ * returns the list of free variables (as type-variables) in this
+ * inference context
+ */
+ List inferenceVars() {
+ return inferencevars;
+ }
+
+ /**
+ * returns the list of uninstantiated variables (as type-variables) in this
+ * inference context (usually called after instantiate())
+ */
+ List restvars() {
+ List undetvars = this.undetvars;
+ ListBuffer restvars = ListBuffer.lb();
+ for (Type t : instTypes()) {
+ UndetVar uv = (UndetVar)undetvars.head;
+ if (uv.qtype == t) {
+ restvars.append(t);
}
- };
+ undetvars = undetvars.tail;
+ }
+ return restvars.toList();
+ }
+
+ /**
+ * is this type free?
+ */
+ final boolean free(Type t) {
+ return t.containsAny(inferencevars);
+ }
+
+ final boolean free(List ts) {
+ for (Type t : ts) {
+ if (free(t)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a list of free variables in a given type
+ */
+ final List freeVarsIn(Type t) {
+ ListBuffer buf = ListBuffer.lb();
+ for (Type iv : inferenceVars()) {
+ if (t.contains(iv)) {
+ buf.add(iv);
+ }
+ }
+ return buf.toList();
+ }
+
+ final List freeVarsIn(List ts) {
+ ListBuffer buf = ListBuffer.lb();
+ for (Type t : ts) {
+ buf.appendList(freeVarsIn(t));
+ }
+ ListBuffer buf2 = ListBuffer.lb();
+ for (Type t : buf) {
+ if (!buf2.contains(t)) {
+ buf2.add(t);
+ }
+ }
+ return buf2.toList();
+ }
+
+ /**
+ * Replace all free variables in a given type with corresponding
+ * undet vars (used ahead of subtyping/compatibility checks to allow propagation
+ * of inference constraints).
+ */
+ final Type asFree(Type t, Types types) {
+ return types.subst(t, inferencevars, undetvars);
+ }
+
+ final List asFree(List ts, Types types) {
+ ListBuffer buf = ListBuffer.lb();
+ for (Type t : ts) {
+ buf.append(asFree(t, types));
+ }
+ return buf.toList();
+ }
+
+ List instTypes() {
+ ListBuffer buf = ListBuffer.lb();
+ for (Type t : undetvars) {
+ UndetVar uv = (UndetVar)t;
+ buf.append(uv.inst != null ? uv.inst : uv.qtype);
+ }
+ return buf.toList();
+ }
+
+ /**
+ * Replace all free variables in a given type with corresponding
+ * instantiated types - if one or more free variable has not been
+ * fully instantiated, it will still be available in the resulting type.
+ */
+ Type asInstType(Type t, Types types) {
+ return types.subst(t, inferencevars, instTypes());
+ }
+
+ List asInstTypes(List ts, Types types) {
+ ListBuffer buf = ListBuffer.lb();
+ for (Type t : ts) {
+ buf.append(asInstType(t, types));
+ }
+ return buf.toList();
+ }
+
+ /**
+ * Add custom hook for performing post-inference action
+ */
+ void addFreeTypeListener(List types, FreeTypeListener ftl) {
+ freeTypeListeners.put(ftl, freeVarsIn(types));
+ }
+
+ /**
+ * Mark the inference context as complete and trigger evaluation
+ * of all deferred checks.
+ */
+ void notifyChange(Types types) {
+ InferenceException thrownEx = null;
+ for (Map.Entry> entry :
+ new HashMap>(freeTypeListeners).entrySet()) {
+ if (!Type.containsAny(entry.getValue(), restvars())) {
+ try {
+ entry.getKey().typesInferred(this);
+ freeTypeListeners.remove(entry.getKey());
+ } catch (InferenceException ex) {
+ if (thrownEx == null) {
+ thrownEx = ex;
+ }
+ }
+ }
+ }
+ //inference exception multiplexing - present any inference exception
+ //thrown when processing listeners as a single one
+ if (thrownEx != null) {
+ throw thrownEx;
+ }
+ }
+
+ void solveAny(List 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.nil(), this, false);
+}
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
index f7674052cbc..57604cbe557 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -1998,7 +1998,7 @@ public class Lower extends TreeTranslator {
// replace with .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)
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java
index 70f021da099..a57f3ba8019 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java
@@ -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;
}
@@ -642,7 +646,9 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
tree.sym = v;
if (tree.init != null) {
v.flags_field |= HASINIT;
- if ((v.flags_field & FINAL) != 0 && !tree.init.hasTag(NEWCLASS)) {
+ if ((v.flags_field & FINAL) != 0 &&
+ !tree.init.hasTag(NEWCLASS) &&
+ !tree.init.hasTag(LAMBDA)) {
Env initEnv = getInitEnv(tree, env);
initEnv.info.enclVar = v;
v.setLazyConstValue(initEnv(tree, initEnv), attr, tree.init);
@@ -667,7 +673,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
Env initEnv(JCVariableDecl tree, Env env) {
Env localEnv = env.dupto(new AttrContextEnv(tree, env.info.dup()));
if (tree.sym.owner.kind == TYP) {
- localEnv.info.scope = new Scope.DelegatedScope(env.info.scope);
+ localEnv.info.scope = env.info.scope.dupUnshared();
localEnv.info.scope.owner = tree.sym;
}
if ((tree.mods.flags & STATIC) != 0 ||
@@ -970,9 +976,11 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
List thrown = List.nil();
long ctorFlags = 0;
boolean based = false;
+ boolean addConstructor = true;
if (c.name.isEmpty()) {
JCNewClass nc = (JCNewClass)env.next.tree;
if (nc.constructor != null) {
+ addConstructor = nc.constructor.kind != ERR;
Type superConstrType = types.memberType(c.type,
nc.constructor);
argtypes = superConstrType.getParameterTypes();
@@ -985,10 +993,12 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
thrown = superConstrType.getThrownTypes();
}
}
- JCTree constrDef = DefaultConstructor(make.at(tree.pos), c,
- typarams, argtypes, thrown,
- ctorFlags, based);
- tree.defs = tree.defs.prepend(constrDef);
+ if (addConstructor) {
+ JCTree constrDef = DefaultConstructor(make.at(tree.pos), c,
+ typarams, argtypes, thrown,
+ ctorFlags, based);
+ tree.defs = tree.defs.prepend(constrDef);
+ }
}
// If this is a class, enter symbols for this and super into
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
index b10f4b7da7a..ba02bb77e80 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
@@ -27,14 +27,20 @@ package com.sun.tools.javac.comp;
import com.sun.tools.javac.api.Formattable.LocalizedString;
import com.sun.tools.javac.code.*;
-import com.sun.tools.javac.code.Type.*;
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;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@@ -44,9 +50,8 @@ 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;
@@ -73,6 +78,7 @@ public class Resolve {
Log log;
Symtab syms;
Attr attr;
+ DeferredAttr deferredAttr;
Check chk;
Infer infer;
ClassReader reader;
@@ -93,10 +99,6 @@ public class Resolve {
varNotFound = new
SymbolNotFoundError(ABSENT_VAR);
- wrongMethod = new
- InapplicableSymbolError();
- wrongMethods = new
- InapplicableSymbolsError();
methodNotFound = new
SymbolNotFoundError(ABSENT_MTH);
typeNotFound = new
@@ -105,6 +107,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);
@@ -127,8 +130,6 @@ public class Resolve {
/** error symbols, which are returned when resolution fails
*/
private final SymbolNotFoundError varNotFound;
- private final InapplicableSymbolError wrongMethod;
- private final InapplicableSymbolsError wrongMethods;
private final SymbolNotFoundError methodNotFound;
private final SymbolNotFoundError typeNotFound;
@@ -215,9 +216,12 @@ public class Resolve {
}
}
String key = success ? "verbose.resolve.multi" : "verbose.resolve.multi.1";
+ List 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);
}
@@ -247,7 +251,7 @@ public class Resolve {
/** An environment is "static" if its static level is greater than
* the one of its outer environment
*/
- static boolean isStatic(Env env) {
+ protected static boolean isStatic(Env env) {
return env.info.staticLevel > env.outer.info.staticLevel;
}
@@ -445,8 +449,18 @@ public class Resolve {
boolean useVarargs,
Warner warn)
throws Infer.InferenceException {
- if (useVarargs && (m.flags() & VARARGS) == 0)
- throw inapplicableMethodException.setMessage();
+ if (useVarargs && (m.flags() & VARARGS) == 0) {
+ //better error recovery - if we stumbled upon a non-varargs method
+ //during varargs applicability phase, the method should be treated as
+ //not applicable; the reason for inapplicability can be found in the
+ //candidate for 'm' that was created during the BOX phase.
+ Candidate prevCandidate = currentResolutionContext.getCandidate(m, BOX);
+ JCDiagnostic details = null;
+ if (prevCandidate != null && !prevCandidate.isApplicable()) {
+ details = prevCandidate.details;
+ }
+ throw inapplicableMethodException.setMessage(details);
+ }
Type mt = types.memberType(site, m);
// tvars is the list of formal type variables for which type arguments
@@ -497,13 +511,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 env,
+ Type site,
+ Symbol m,
+ ResultInfo resultInfo,
+ List argtypes,
+ List 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 env,
@@ -526,13 +561,14 @@ public class Resolve {
/** Check if a parameter list accepts a list of args.
*/
boolean argumentsAcceptable(Env env,
+ Symbol msym,
List argtypes,
List 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;
@@ -579,12 +615,13 @@ public class Resolve {
};
void checkRawArgumentsAcceptable(Env env,
+ Symbol msym,
List argtypes,
List formals,
boolean allowBoxing,
boolean useVarargs,
Warner warn) {
- checkRawArgumentsAcceptable(env, List.nil(), argtypes, formals,
+ checkRawArgumentsAcceptable(env, msym, currentResolutionContext.attrMode(), infer.emptyContext, argtypes, formals,
allowBoxing, useVarargs, warn, resolveHandler);
}
@@ -594,35 +631,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.
*/
- List checkRawArgumentsAcceptable(Env env,
- List undetvars,
+ void checkRawArgumentsAcceptable(final Env env,
+ Symbol msym,
+ DeferredAttr.AttrMode mode,
+ final Infer.InferenceContext inferenceContext,
List argtypes,
List formals,
boolean allowBoxing,
boolean useVarargs,
Warner warn,
- MethodCheckHandler handler) {
+ final MethodCheckHandler handler) {
Type varargsFormal = useVarargs ? formals.last() : null;
- ListBuffer 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, undetvars, 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;
}
@@ -634,19 +677,33 @@ 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, undetvars, handler, warn);
- checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head));
+ mresult.check(null, argtypes.head);
argtypes = argtypes.tail;
}
//check varargs element type accessibility
- if (undetvars.isEmpty() && !isAccessible(env, elt)) {
+ varargsAccessible(env, elt, handler, inferenceContext);
+ }
+
+ deferredAttrContext.complete();
+ }
+
+ void varargsAccessible(final Env env, final Type t, final Resolve.MethodCheckHandler handler, final InferenceContext inferenceContext) {
+ if (inferenceContext.free(t)) {
+ inferenceContext.addFreeTypeListener(List.of(t), new FreeTypeListener() {
+ @Override
+ public void typesInferred(InferenceContext inferenceContext) {
+ varargsAccessible(env, inferenceContext.asInstType(t, types), handler, inferenceContext);
+ }
+ });
+ } else {
+ if (!isAccessible(env, t)) {
Symbol location = env.enclClass.sym;
- throw handler.inaccessibleVarargs(location, elt);
+ throw handler.inaccessibleVarargs(location, t);
}
}
- return checkedArgs.toList();
}
/**
@@ -657,13 +714,16 @@ public class Resolve {
MethodCheckHandler handler;
boolean useVarargs;
- List undetvars;
+ Infer.InferenceContext inferenceContext;
+ DeferredAttrContext deferredAttrContext;
Warner rsWarner;
- public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs, List undetvars, Warner rsWarner) {
+ public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs,
+ Infer.InferenceContext inferenceContext, DeferredAttrContext deferredAttrContext, Warner rsWarner) {
this.handler = handler;
this.useVarargs = useVarargs;
- this.undetvars = undetvars;
+ this.inferenceContext = inferenceContext;
+ this.deferredAttrContext = deferredAttrContext;
this.rsWarner = rsWarner;
}
@@ -674,6 +734,14 @@ public class Resolve {
public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
return rsWarner;
}
+
+ public InferenceContext inferenceContext() {
+ return inferenceContext;
+ }
+
+ public DeferredAttrContext deferredAttrContext() {
+ return deferredAttrContext;
+ }
}
/**
@@ -682,12 +750,17 @@ public class Resolve {
*/
class StrictMethodContext extends MethodCheckContext {
- public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs, List undetvars, Warner rsWarner) {
- super(handler, useVarargs, undetvars, 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) {
- return types.isSubtypeUnchecked(found, infer.asUndetType(req, undetvars), warn);
+ return types.isSubtypeUnchecked(found, inferenceContext.asFree(req, types), warn);
+ }
+
+ public boolean allowBoxing() {
+ return false;
}
}
@@ -697,12 +770,17 @@ public class Resolve {
*/
class LooseMethodContext extends MethodCheckContext {
- public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs, List undetvars, Warner rsWarner) {
- super(handler, useVarargs, undetvars, 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) {
- return types.isConvertible(found, infer.asUndetType(req, undetvars), warn);
+ return types.isConvertible(found, inferenceContext.asFree(req, types), warn);
+ }
+
+ public boolean allowBoxing() {
+ return true;
}
}
@@ -710,16 +788,37 @@ public class Resolve {
* Create a method check context to be used during method applicability check
*/
ResultInfo methodCheckResult(Type to, boolean allowBoxing, boolean useVarargs,
- List undetvars, MethodCheckHandler methodHandler, Warner rsWarner) {
+ Infer.InferenceContext inferenceContext, DeferredAttr.DeferredAttrContext deferredAttrContext,
+ MethodCheckHandler methodHandler, Warner rsWarner) {
MethodCheckContext checkContext = allowBoxing ?
- new LooseMethodContext(methodHandler, useVarargs, undetvars, rsWarner) :
- new StrictMethodContext(methodHandler, useVarargs, undetvars, rsWarner);
- return attr.new ResultInfo(VAL, to, checkContext) {
- @Override
- protected Type check(DiagnosticPosition pos, Type found) {
- return super.check(pos, chk.checkNonVoid(pos, types.capture(types.upperBound(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 {
@@ -733,16 +832,13 @@ public class Resolve {
this.diags = diags;
}
InapplicableMethodException setMessage() {
- this.diagnostic = null;
- return this;
+ return setMessage((JCDiagnostic)null);
}
InapplicableMethodException setMessage(String key) {
- this.diagnostic = key != null ? diags.fragment(key) : null;
- return this;
+ return setMessage(key != null ? diags.fragment(key) : null);
}
InapplicableMethodException setMessage(String key, Object... args) {
- this.diagnostic = key != null ? diags.fragment(key, args) : null;
- return this;
+ return setMessage(key != null ? diags.fragment(key, args) : null);
}
InapplicableMethodException setMessage(JCDiagnostic diag) {
this.diagnostic = diag;
@@ -937,11 +1033,10 @@ public class Resolve {
currentResolutionContext.addInapplicableCandidate(sym, ex.getDiagnostic());
switch (bestSoFar.kind) {
case ABSENT_MTH:
- return wrongMethod;
+ return new InapplicableSymbolError(currentResolutionContext);
case WRONG_MTH:
if (operator) return bestSoFar;
- case WRONG_MTHS:
- return wrongMethods;
+ bestSoFar = new InapplicableSymbolsError(currentResolutionContext);
default:
return bestSoFar;
}
@@ -953,7 +1048,7 @@ public class Resolve {
}
return (bestSoFar.kind > AMBIGUOUS)
? sym
- : mostSpecific(sym, bestSoFar, env, site,
+ : mostSpecific(argtypes, sym, bestSoFar, env, site,
allowBoxing && operator, useVarargs);
}
@@ -967,7 +1062,7 @@ public class Resolve {
* @param allowBoxing Allow boxing conversions of arguments.
* @param useVarargs Box trailing arguments into an array for varargs.
*/
- Symbol mostSpecific(Symbol m1,
+ Symbol mostSpecific(List argtypes, Symbol m1,
Symbol m2,
Env env,
final Type site,
@@ -976,8 +1071,10 @@ public class Resolve {
switch (m2.kind) {
case MTH:
if (m1 == m2) return m1;
- boolean m1SignatureMoreSpecific = signatureMoreSpecific(env, site, m1, m2, allowBoxing, useVarargs);
- boolean m2SignatureMoreSpecific = signatureMoreSpecific(env, site, m2, m1, allowBoxing, useVarargs);
+ boolean m1SignatureMoreSpecific =
+ signatureMoreSpecific(argtypes, env, site, m1, m2, allowBoxing, useVarargs);
+ boolean m2SignatureMoreSpecific =
+ signatureMoreSpecific(argtypes, env, site, m2, m1, allowBoxing, useVarargs);
if (m1SignatureMoreSpecific && m2SignatureMoreSpecific) {
Type mt1 = types.memberType(site, m1);
Type mt2 = types.memberType(site, m2);
@@ -1035,8 +1132,8 @@ public class Resolve {
return this;
else
return super.implementation(origin, types, checkResult);
- }
- };
+ }
+ };
return result;
}
if (m1SignatureMoreSpecific) return m1;
@@ -1044,8 +1141,8 @@ public class Resolve {
return ambiguityError(m1, m2);
case AMBIGUOUS:
AmbiguityError e = (AmbiguityError)m2;
- Symbol err1 = mostSpecific(m1, e.sym, env, site, allowBoxing, useVarargs);
- Symbol err2 = mostSpecific(m1, e.sym2, env, site, allowBoxing, useVarargs);
+ Symbol err1 = mostSpecific(argtypes, m1, e.sym, env, site, allowBoxing, useVarargs);
+ Symbol err2 = mostSpecific(argtypes, m1, e.sym2, env, site, allowBoxing, useVarargs);
if (err1 == err2) return err1;
if (err1 == e.sym && err2 == e.sym2) return m2;
if (err1 instanceof AmbiguityError &&
@@ -1059,13 +1156,83 @@ public class Resolve {
}
}
//where
- private boolean signatureMoreSpecific(Env env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) {
+ private boolean signatureMoreSpecific(List actuals, Env env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) {
+ Symbol m12 = adjustVarargs(m1, m2, useVarargs);
+ Symbol m22 = adjustVarargs(m2, m1, useVarargs);
+ Type mtype1 = types.memberType(site, m12);
+ Type mtype2 = types.memberType(site, m22);
+
+ //check if invocation is more specific
+ if (invocationMoreSpecific(env, site, m22, mtype1.getParameterTypes(), allowBoxing, useVarargs)) {
+ return true;
+ }
+
+ //perform structural check
+
+ List formals1 = mtype1.getParameterTypes();
+ Type lastFormal1 = formals1.last();
+ List formals2 = mtype2.getParameterTypes();
+ Type lastFormal2 = formals2.last();
+ ListBuffer newFormals = ListBuffer.lb();
+
+ boolean hasStructuralPoly = false;
+ for (Type actual : actuals) {
+ //perform formal argument adaptation in case actuals > formals (varargs)
+ Type f1 = formals1.isEmpty() ?
+ lastFormal1 : formals1.head;
+ Type f2 = formals2.isEmpty() ?
+ lastFormal2 : formals2.head;
+
+ //is this a structural actual argument?
+ boolean isStructuralPoly = actual.tag == DEFERRED &&
+ (((DeferredType)actual).tree.hasTag(LAMBDA) ||
+ ((DeferredType)actual).tree.hasTag(REFERENCE));
+
+ Type newFormal = f1;
+
+ if (isStructuralPoly) {
+ //for structural arguments only - check that corresponding formals
+ //are related - if so replace formal with
+ hasStructuralPoly = true;
+ DeferredType dt = (DeferredType)actual;
+ Type t1 = deferredAttr.new DeferredTypeMap(AttrMode.SPECULATIVE, m1, currentResolutionContext.step).apply(dt);
+ Type t2 = deferredAttr.new DeferredTypeMap(AttrMode.SPECULATIVE, m2, currentResolutionContext.step).apply(dt);
+ if (t1.isErroneous() || t2.isErroneous() || !isStructuralSubtype(t1, t2)) {
+ //not structural subtypes - simply fail
+ return false;
+ } else {
+ newFormal = syms.botType;
+ }
+ }
+
+ newFormals.append(newFormal);
+ if (newFormals.length() > mtype2.getParameterTypes().length()) {
+ //expand m2's type so as to fit the new formal arity (varargs)
+ m22.type = types.createMethodTypeWithParameters(m22.type, m22.type.getParameterTypes().append(f2));
+ }
+
+ formals1 = formals1.isEmpty() ? formals1 : formals1.tail;
+ formals2 = formals2.isEmpty() ? formals2 : formals2.tail;
+ }
+
+ if (!hasStructuralPoly) {
+ //if no structural actual was found, we're done
+ return false;
+ }
+ //perform additional adaptation if actuals < formals (varargs)
+ for (Type t : formals1) {
+ newFormals.append(t);
+ }
+ //check if invocation (with tweaked args) is more specific
+ return invocationMoreSpecific(env, site, m22, newFormals.toList(), allowBoxing, useVarargs);
+ }
+ //where
+ private boolean invocationMoreSpecific(Env env, Type site, Symbol m2, List argtypes1, boolean allowBoxing, boolean useVarargs) {
noteWarner.clear();
- Type mtype1 = types.memberType(site, adjustVarargs(m1, m2, useVarargs));
- Type mtype2 = instantiate(env, site, adjustVarargs(m2, m1, useVarargs), null,
- types.lowerBoundArgtypes(mtype1), null,
+ Type mst = instantiate(env, site, m2, null,
+ types.lowerBounds(argtypes1), null,
allowBoxing, false, noteWarner);
- return mtype2 != null &&
+ return mst != null &&
!noteWarner.hasLint(Lint.LintCategory.UNCHECKED);
}
//where
@@ -1104,6 +1271,32 @@ public class Resolve {
}
}
//where
+ boolean isStructuralSubtype(Type s, Type t) {
+
+ Type ret_s = types.findDescriptorType(s).getReturnType();
+ Type ret_t = types.findDescriptorType(t).getReturnType();
+
+ //covariant most specific check for function descriptor return type
+ if (!types.isSubtype(ret_s, ret_t)) {
+ return false;
+ }
+
+ List args_s = types.findDescriptorType(s).getParameterTypes();
+ List args_t = types.findDescriptorType(t).getParameterTypes();
+
+ //arity must be identical
+ if (args_s.length() != args_t.length()) {
+ return false;
+ }
+
+ //invariant most specific check for function descriptor parameter types
+ if (!types.isSameTypes(args_t, args_s)) {
+ return false;
+ }
+
+ return true;
+ }
+ //where
Type mostSpecificReturnType(Type mt1, Type mt2) {
Type rt1 = mt1.getReturnType();
Type rt2 = mt2.getReturnType();
@@ -1160,12 +1353,10 @@ public class Resolve {
argtypes,
typeargtypes,
site.tsym.type,
- true,
bestSoFar,
allowBoxing,
useVarargs,
- operator,
- new HashSet());
+ operator);
reportVerboseResolutionDiagnostic(env.tree.pos(), name, site, argtypes, typeargtypes, bestSoFar);
return bestSoFar;
}
@@ -1176,56 +1367,134 @@ public class Resolve {
List argtypes,
List typeargtypes,
Type intype,
- boolean abstractok,
Symbol bestSoFar,
boolean allowBoxing,
boolean useVarargs,
- boolean operator,
- Set seen) {
- for (Type ct = intype; ct.tag == CLASS || ct.tag == TYPEVAR; ct = types.supertype(ct)) {
- while (ct.tag == TYPEVAR)
- ct = ct.getUpperBound();
- ClassSymbol c = (ClassSymbol)ct.tsym;
- if (!seen.add(c)) return bestSoFar;
- if ((c.flags() & (ABSTRACT | INTERFACE | ENUM)) == 0)
- abstractok = false;
- for (Scope.Entry e = c.members().lookup(name);
- e.scope != null;
- e = e.next()) {
- //- System.out.println(" e " + e.sym);
- if (e.sym.kind == MTH &&
- (e.sym.flags_field & SYNTHETIC) == 0) {
- bestSoFar = selectBest(env, site, argtypes, typeargtypes,
- e.sym, bestSoFar,
- allowBoxing,
- useVarargs,
- operator);
+ boolean operator) {
+ boolean abstractOk = true;
+ List itypes = List.nil();
+ for (TypeSymbol s : superclasses(intype)) {
+ bestSoFar = lookupMethod(env, site, name, argtypes, typeargtypes,
+ s.members(), bestSoFar, allowBoxing, useVarargs, operator, true);
+ //We should not look for abstract methods if receiver is a concrete class
+ //(as concrete classes are expected to implement all abstracts coming
+ //from superinterfaces)
+ abstractOk &= (s.flags() & (ABSTRACT | INTERFACE | ENUM)) != 0;
+ if (abstractOk) {
+ for (Type itype : types.interfaces(s.type)) {
+ itypes = types.union(types.closure(itype), itypes);
}
}
- if (name == names.init)
- break;
- //- System.out.println(" - " + bestSoFar);
- if (abstractok) {
- Symbol concrete = methodNotFound;
- if ((bestSoFar.flags() & ABSTRACT) == 0)
- concrete = bestSoFar;
- for (List l = types.interfaces(c.type);
- l.nonEmpty();
- l = l.tail) {
- bestSoFar = findMethod(env, site, name, argtypes,
- typeargtypes,
- l.head, abstractok, bestSoFar,
- allowBoxing, useVarargs, operator, seen);
- }
- if (concrete != bestSoFar &&
- concrete.kind < ERR && bestSoFar.kind < ERR &&
- types.isSubSignature(concrete.type, bestSoFar.type))
- bestSoFar = concrete;
+ if (name == names.init) break;
+ }
+
+ Symbol concrete = bestSoFar.kind < ERR &&
+ (bestSoFar.flags() & ABSTRACT) == 0 ?
+ bestSoFar : methodNotFound;
+
+ if (name != names.init) {
+ //keep searching for abstract methods
+ for (Type itype : itypes) {
+ if (!itype.isInterface()) continue; //skip j.l.Object (included by Types.closure())
+ bestSoFar = lookupMethod(env, site, name, argtypes, typeargtypes,
+ itype.tsym.members(), bestSoFar, allowBoxing, useVarargs, operator, true);
+ if (concrete != bestSoFar &&
+ concrete.kind < ERR && bestSoFar.kind < ERR &&
+ types.isSubSignature(concrete.type, bestSoFar.type)) {
+ //this is an hack - as javac does not do full membership checks
+ //most specific ends up comparing abstract methods that might have
+ //been implemented by some concrete method in a subclass and,
+ //because of raw override, it is possible for an abstract method
+ //to be more specific than the concrete method - so we need
+ //to explicitly call that out (see CR 6178365)
+ bestSoFar = concrete;
+ }
}
}
return bestSoFar;
}
+ /**
+ * Return an Iterable object to scan the superclasses of a given type.
+ * It's crucial that the scan is done lazily, as we don't want to accidentally
+ * access more supertypes than strictly needed (as this could trigger completion
+ * errors if some of the not-needed supertypes are missing/ill-formed).
+ */
+ Iterable superclasses(final Type intype) {
+ return new Iterable() {
+ public Iterator iterator() {
+ return new Iterator() {
+
+ List seen = List.nil();
+ TypeSymbol currentSym = symbolFor(intype);
+ TypeSymbol prevSym = null;
+
+ public boolean hasNext() {
+ if (currentSym == syms.noSymbol) {
+ currentSym = symbolFor(types.supertype(prevSym.type));
+ }
+ return currentSym != null;
+ }
+
+ public TypeSymbol next() {
+ prevSym = currentSym;
+ currentSym = syms.noSymbol;
+ Assert.check(prevSym != null || prevSym != syms.noSymbol);
+ return prevSym;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ TypeSymbol symbolFor(Type t) {
+ if (t.tag != CLASS &&
+ t.tag != TYPEVAR) {
+ return null;
+ }
+ while (t.tag == TYPEVAR)
+ t = t.getUpperBound();
+ if (seen.contains(t.tsym)) {
+ //degenerate case in which we have a circular
+ //class hierarchy - because of ill-formed classfiles
+ return null;
+ }
+ seen = seen.prepend(t.tsym);
+ return t.tsym;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Lookup a method with given name and argument types in a given scope
+ */
+ Symbol lookupMethod(Env env,
+ Type site,
+ Name name,
+ List argtypes,
+ List typeargtypes,
+ Scope sc,
+ Symbol bestSoFar,
+ boolean allowBoxing,
+ boolean useVarargs,
+ boolean operator,
+ boolean abstractok) {
+ for (Symbol s : sc.getElementsByName(name, lookupFilter)) {
+ bestSoFar = selectBest(env, site, argtypes, typeargtypes, s,
+ bestSoFar, allowBoxing, useVarargs, operator);
+ }
+ return bestSoFar;
+ }
+ //where
+ Filter lookupFilter = new Filter() {
+ public boolean accepts(Symbol s) {
+ return s.kind == MTH &&
+ (s.flags() & SYNTHETIC) == 0;
+ }
+ };
+
/** Find unqualified method matching given name, type and value arguments.
* @param env The current environment.
* @param name The method's name.
@@ -1521,68 +1790,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 argtypes,
- List typeargtypes) {
+ List 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 argtypes,
+ List 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 argtypes,
List 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.nil(), null);
- else
- return sym;
+ return accessInternal(sym, pos, location, site, name, qualified, List.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 argtypes, List typeargtypes);
+ List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes);
+ }
+
+ LogResolveHelper basicLogResolveHelper = new LogResolveHelper() {
+ public boolean resolveDiagnosticNeeded(Type site, List