Mercurial > hg > openjdk > lambda > langtools
changeset 2244:d34073d069c8
8016177: structural most specific and stuckness
8016178: Order of unsticking functional expressions
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Wed Jul 24 16:54:37 2013 -0700 +++ b/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Thu Jul 25 16:29:24 2013 +0100 @@ -34,18 +34,19 @@ 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.comp.Resolve.ReferenceLookupHelper; import com.sun.tools.javac.tree.JCTree.*; - import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import static com.sun.tools.javac.code.TypeTag.*; -import com.sun.tools.javac.comp.Resolve.ReferenceLookupHelper; import static com.sun.tools.javac.tree.JCTree.Tag.*; /** @@ -98,7 +99,7 @@ emptyDeferredAttrContext = new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) { @Override - void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) { + void addDeferredAttrNode(DeferredType dt, ResultInfo ri, Set<Type> stuckVars, Set<Type> outVars) { Assert.error("Empty deferred context!"); } @Override @@ -144,15 +145,15 @@ class Entry { JCTree speculativeTree; - Resolve.MethodResolutionPhase phase; + ResultInfo resultInfo; - public Entry(JCTree speculativeTree, MethodResolutionPhase phase) { + public Entry(JCTree speculativeTree, ResultInfo resultInfo) { this.speculativeTree = speculativeTree; - this.phase = phase; + this.resultInfo = resultInfo; } - boolean matches(Resolve.MethodResolutionPhase phase) { - return this.phase == phase; + boolean matches(MethodResolutionPhase phase) { + return resultInfo.checkContext.deferredAttrContext().phase == phase; } } @@ -173,12 +174,13 @@ * Stores a speculative cache entry corresponding to given symbol * and resolution phase */ - void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) { + void put(JCTree speculativeTree, ResultInfo resultInfo) { + Symbol msym = resultInfo.checkContext.deferredAttrContext().msym; List<Entry> entries = cache.get(msym); if (entries == null) { entries = List.nil(); } - cache.put(msym, entries.prepend(new Entry(speculativeTree, phase))); + cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo))); } } @@ -198,15 +200,17 @@ * attribution round must follow one or more speculative rounds. */ Type check(ResultInfo resultInfo) { - return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter); + Pair<Set<Type>, Set<Type>> stuckRes = stuckVars(tree, env, resultInfo); + return check(resultInfo, stuckRes.fst, stuckRes.snd, basicCompleter); } - Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) { + private Type check(ResultInfo resultInfo, Set<Type> stuckVars, Set<Type> depVars, + DeferredTypeCompleter deferredTypeCompleter) { DeferredAttrContext deferredAttrContext = resultInfo.checkContext.deferredAttrContext(); Assert.check(deferredAttrContext != emptyDeferredAttrContext); - if (stuckVars.nonEmpty()) { - deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars); + if (!stuckVars.isEmpty()) { + deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars, depVars); return Type.noType; } else { try { @@ -244,7 +248,7 @@ //speculative rounds... Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE); JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); - dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); + dt.speculativeCache.put(speculativeTree, resultInfo); return speculativeTree.type; case CHECK: Assert.check(dt.mode != null); @@ -383,8 +387,9 @@ * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable * Nodes added this way act as 'roots' for the out-of-order method checking process. */ - void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) { - deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars)); + void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, + Set<Type> stuckVars, Set<Type> depVars) { + deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars, depVars)); } /** @@ -395,13 +400,26 @@ */ void complete() { while (!deferredAttrNodes.isEmpty()) { - Set<Type> stuckVars = new LinkedHashSet<Type>(); + Map<Type, Set<Type>> depVarsMap = new LinkedHashMap<Type, Set<Type>>(); + List<Type> stuckVars = List.nil(); 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.process(this)) { - stuckVars.addAll(deferredAttrNode.stuckVars); + List<Type> restStuckVars = List.from(deferredAttrNode.stuckVars) + .intersect(inferenceContext.restvars()); + stuckVars = stuckVars.prependList(restStuckVars); + //update dependency map + for (Type t : List.from(deferredAttrNode.depVars) + .intersect(inferenceContext.restvars())) { + Set<Type> prevDeps = depVarsMap.get(t); + if (prevDeps == null) { + prevDeps = new LinkedHashSet<Type>(); + depVarsMap.put(t, prevDeps); + } + prevDeps.addAll(restStuckVars); + } } else { deferredAttrNodes.remove(deferredAttrNode); progress = true; @@ -410,11 +428,39 @@ if (!progress) { //remove all variables that have already been instantiated //from the list of stuck variables - inferenceContext.solveAny(List.from(stuckVars), warn); - inferenceContext.notifyChange(); + try { + inferenceContext.solveAny(stuckVars, depVarsMap, + mode == AttrMode.SPECULATIVE ? targetFreevars() : List.<Type>nil(), + warn); + inferenceContext.notifyChange(); + } catch (Infer.GraphStrategy.NodeNotFoundException ex) { + //this means that we are in speculative mode and the + //set of contraints are too tight for progess to be made. + //Just leave the remaining expressions as stuck. + break; + } } } } + + /** + * Get the list of stuck variables that do not depend on the target + * type - this means that inference will lead to same results during + * both OVERLOAD and CHECK modes. + */ + List<Type> targetFreevars() { + List<Type> freevars = msym.type.getTypeArguments(); + ListBuffer<Type> targetVars = ListBuffer.lb(); + outer: for (Type t : inferenceContext.inferencevars) { + for (Type t2 : freevars) { + if (msym.type.getReturnType().contains(t2) && + t.tsym == t2.tsym) { + targetVars.append(t); + } + } + } + return targetVars.toList(); + } } /** @@ -429,21 +475,26 @@ /** underlying target type information */ ResultInfo resultInfo; - /** list of uninferred inference variables causing this node to be stuck */ - List<Type> stuckVars; + /** set of uninferred inference variables causing this node to be stuck */ + Set<Type> stuckVars; + + /** set of uninferred inference variables that depends on this node */ + Set<Type> depVars; + - DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) { + DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, Set<Type> stuckVars, Set<Type> depVars) { this.dt = dt; this.resultInfo = resultInfo; this.stuckVars = stuckVars; + this.depVars = depVars; if (!stuckVars.isEmpty()) { - resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this); + resultInfo.checkContext.inferenceContext().addFreeTypeListener(List.from(stuckVars), this); } } @Override public void typesInferred(InferenceContext inferenceContext) { - stuckVars = List.nil(); + stuckVars.clear(); resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt)); } @@ -452,24 +503,42 @@ * Invariant: a stuck node cannot be processed. */ @SuppressWarnings("fallthrough") - boolean process(DeferredAttrContext deferredAttrContext) { + boolean process(final DeferredAttrContext deferredAttrContext) { switch (deferredAttrContext.mode) { case SPECULATIVE: - dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker()); - return true; + if (!stuckVars.isEmpty()) { + dt.check(resultInfo, Collections.<Type>emptySet(), + Collections.<Type>emptySet(), new StructuralStuckChecker()); + return false; + } else { + dt.check(resultInfo, stuckVars, depVars, basicCompleter); + return true; + } case CHECK: - if (stuckVars.nonEmpty()) { + if (!stuckVars.isEmpty()) { //stuck expression - see if we can propagate if (deferredAttrContext.parent != emptyDeferredAttrContext && - Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, List.from(stuckVars))) { - deferredAttrContext.parent.deferredAttrNodes.add(this); - dt.check(resultInfo, List.<Type>nil(), dummyCompleter); + Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, + List.from(stuckVars))) { + deferredAttrContext.parent.addDeferredAttrNode(dt, + resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) { + @Override + public InferenceContext inferenceContext() { + return deferredAttrContext.parent.inferenceContext; + } + @Override + public DeferredAttrContext deferredAttrContext() { + return deferredAttrContext.parent; + } + }), stuckVars, depVars); + dt.check(resultInfo, Collections.<Type>emptySet(), + Collections.<Type>emptySet(), dummyCompleter); return true; } else { return false; } } else { - dt.check(resultInfo, stuckVars, basicCompleter); + dt.check(resultInfo, stuckVars, depVars, basicCompleter); return true; } default: @@ -489,7 +558,7 @@ this.resultInfo = resultInfo; this.inferenceContext = deferredAttrContext.inferenceContext; dt.tree.accept(this); - dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase); + dt.speculativeCache.put(stuckTree, resultInfo); return Type.noType; } @@ -643,21 +712,22 @@ /** * Retrieves the list of inference variables that need to be inferred before - * an AST node can be type-checked + * an AST node can be type-checked, along with the inference variables that + * will get constraints should the stuck expression be type-checked. */ @SuppressWarnings("fallthrough") - List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) { - if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { - return List.nil(); + Pair<Set<Type>, Set<Type>> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) { + if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { + return new Pair<Set<Type>, Set<Type>>(Collections.<Type>emptySet(), Collections.<Type>emptySet()); } else { return stuckVarsInternal(tree, resultInfo.pt, env, resultInfo.checkContext.inferenceContext()); } } //where - private List<Type> stuckVarsInternal(JCTree tree, Type pt, Env<AttrContext> env, Infer.InferenceContext inferenceContext) { + private Pair<Set<Type>, Set<Type>> stuckVarsInternal(JCTree tree, Type pt, Env<AttrContext> env, Infer.InferenceContext inferenceContext) { StuckChecker sc = new StuckChecker(pt, env, inferenceContext); sc.scan(tree); - return List.from(sc.stuckVars); + return new Pair<Set<Type>, Set<Type>>(sc.stuckVars, sc.depVars); } /** @@ -738,6 +808,7 @@ Env<AttrContext> env; Infer.InferenceContext inferenceContext; Set<Type> stuckVars = new LinkedHashSet<Type>(); + Set<Type> depVars = new LinkedHashSet<Type>(); StuckChecker(Type pt, Env<AttrContext> env, Infer.InferenceContext inferenceContext) { this.pt = pt; @@ -758,6 +829,7 @@ if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT && freeArgVars.nonEmpty()) { stuckVars.addAll(freeArgVars); + depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); } scanLambdaBody(tree, descType.getReturnType()); } @@ -797,19 +869,26 @@ (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) && exprTree.type.isRaw())) { stuckVars.addAll(freeArgVars); + depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); } } } void scanLambdaBody(JCLambda lambda, final Type pt) { if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { - stuckVars.addAll(stuckVarsInternal(lambda.body, pt, env, inferenceContext)); + Pair<Set<Type>, Set<Type>> stuckRes = + stuckVarsInternal(lambda.body, pt, env, inferenceContext); + stuckVars.addAll(stuckRes.fst); + depVars.addAll(stuckRes.snd); } else { LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() { @Override public void visitReturn(JCReturn tree) { if (tree.expr != null) { - stuckVars.addAll(stuckVarsInternal(tree.expr, pt, env, inferenceContext)); + Pair<Set<Type>, Set<Type>> stuckRes = + stuckVarsInternal(tree.expr, pt, env, inferenceContext); + stuckVars.addAll(stuckRes.fst); + depVars.addAll(stuckRes.snd); } } };
--- a/src/share/classes/com/sun/tools/javac/comp/Infer.java Wed Jul 24 16:54:37 2013 -0700 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java Thu Jul 25 16:29:24 2013 +0100 @@ -40,17 +40,17 @@ import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import com.sun.tools.javac.util.GraphUtils.TarjanNode; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumMap; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; import static com.sun.tools.javac.code.TypeTag.*; @@ -114,6 +114,12 @@ } @Override + InapplicableMethodException setMessage() { + //no message to set + return this; + } + + @Override InapplicableMethodException setMessage(JCDiagnostic diag) { messages = messages.append(diag); return this; @@ -171,7 +177,7 @@ return mt; } } - + deferredAttrContext.complete(); // minimize as yet undetermined type variables @@ -1006,10 +1012,24 @@ * and (ii) tell th engine when we are done fixing inference variables */ interface GraphStrategy { + + /** + * A NodeNotFoundException is thrown whenever an inference strategy fails + * to pick the next node to solve in the inference graph. + */ + public static class NodeNotFoundException extends RuntimeException { + private static final long serialVersionUID = 0; + + InferenceGraph graph; + + public NodeNotFoundException(InferenceGraph graph) { + this.graph = graph; + } + } /** * Pick the next node (leaf) to solve in the graph */ - Node pickNode(InferenceGraph g); + Node pickNode(InferenceGraph g) throws NodeNotFoundException; /** * Is this the last step? */ @@ -1022,7 +1042,10 @@ */ abstract class LeafSolver implements GraphStrategy { public Node pickNode(InferenceGraph g) { - Assert.check(!g.nodes.isEmpty(), "No nodes to solve!"); + if (g.nodes.isEmpty()) { + //should not happen + throw new NodeNotFoundException(g); + }; return g.nodes.get(0); } } @@ -1034,61 +1057,84 @@ */ abstract class BestLeafSolver extends LeafSolver { + /** list of ivars of which at least one must be solved */ List<Type> varsToSolve; + + /** list of ivars that the solver should not touch */ + List<Type> varsToAvoid; - BestLeafSolver(List<Type> varsToSolve) { + BestLeafSolver(List<Type> varsToSolve, List<Type> varsToAvoid) { this.varsToSolve = varsToSolve; + this.varsToAvoid = varsToAvoid; } /** - * Computes the minimum path that goes from a given node to any of the nodes - * containing a variable in {@code varsToSolve}. For any given path, the cost - * is computed as the total number of type-variables that should be eagerly - * instantiated across that path. + * Computes a path that goes from a given node to the leafs in the graph. + * Typically this will start from a node containing a variable in + * {@code varsToSolve}. For any given path, the cost is computed as the total + * number of type-variables that should be eagerly instantiated across that path. */ - int computeMinPath(InferenceGraph g, Node n) { - return computeMinPath(g, n, List.<Node>nil(), 0); + Pair<List<Node>, Integer> computeTreeToLeafs(Node n) { + Pair<List<Node>, Integer> cachedPath = treeCache.get(n); + if (cachedPath == null) { + //cache miss + if (n.isLeaf()) { + //if leaf, stop + cachedPath = new Pair<List<Node>, Integer>(List.of(n), n.data.length()); + } else { + //if non-leaf, proceed recursively + Pair<List<Node>, Integer> path = new Pair<List<Node>, Integer>(List.of(n), n.data.length()); + for (Node n2 : n.getAllDependencies()) { + if (n2 == n) continue; + Pair<List<Node>, Integer> subpath = computeTreeToLeafs(n2); + path = new Pair<List<Node>, Integer>( + path.fst.prependList(subpath.fst), + path.snd + subpath.snd); + } + cachedPath = path; + } + //save results in cache + treeCache.put(n, cachedPath); + } + return cachedPath; } - int computeMinPath(InferenceGraph g, Node n, List<Node> path, int cost) { - if (path.contains(n)) return Integer.MAX_VALUE; - List<Node> path2 = path.prepend(n); - int cost2 = cost + n.data.size(); - if (!Collections.disjoint(n.data, varsToSolve)) { - return cost2; - } else { - int bestPath = Integer.MAX_VALUE; - for (Node n2 : g.nodes) { - if (n2.deps.contains(n)) { - int res = computeMinPath(g, n2, path2, cost2); - if (res < bestPath) { - bestPath = res; - } - } - } - return bestPath; - } - } + /** cache used to avoid redundant computation of tree costs */ + final Map<Node, Pair<List<Node>, Integer>> treeCache = + new HashMap<Node, Pair<List<Node>, Integer>>(); + + /** constant value used to mark non-existent paths */ + final Pair<List<Node>, Integer> noPath = + new Pair<List<Node>, Integer>(null, Integer.MAX_VALUE); /** * Pick the leaf that minimize cost */ @Override public Node pickNode(final InferenceGraph g) { - final Map<Node, Integer> leavesMap = new HashMap<Node, Integer>(); + treeCache.clear(); //graph changes at every step - cache must be cleared + Pair<List<Node>, Integer> bestPath = noPath; + Set<Node> avoidClosure = new LinkedHashSet<Node>(); + //compute closure of all variables to avoid + for (Type t : varsToAvoid) { + avoidClosure.addAll(g.findNode(t).closure(DependencyKind.BOUND)); + } for (Node n : g.nodes) { - if (n.isLeaf(n)) { - leavesMap.put(n, computeMinPath(g, n)); + if (!Collections.disjoint(n.data, varsToSolve)) { + Pair<List<Node>, Integer> path = computeTreeToLeafs(n); + //discard all paths containing at least a node in the + //closure computed above + if (Collections.disjoint(path.fst, avoidClosure) && + path.snd < bestPath.snd) { + bestPath = path; + } } } - Assert.check(!leavesMap.isEmpty(), "No nodes to solve!"); - TreeSet<Node> orderedLeaves = new TreeSet<Node>(new Comparator<Node>() { - public int compare(Node n1, Node n2) { - return leavesMap.get(n1) - leavesMap.get(n2); - } - }); - orderedLeaves.addAll(leavesMap.keySet()); - return orderedLeaves.first(); + if (bestPath == noPath) { + //no path leads there + throw new NodeNotFoundException(g); + } + return bestPath.fst.head; } } @@ -1286,6 +1332,33 @@ this.steps = steps; } } + + /** + * There are two kinds of dependencies between inference variables. The basic + * kind of dependency (or bound dependency) arises when a variable mention + * another variable in one of its bounds. There's also a more subtle kind + * of dependency that arises when a variable 'might' lead to better constraints + * on another variable (this is typically the case with variables holding up + * stuck expressions). + */ + enum DependencyKind implements GraphUtils.DependencyKind { + + /** bound dependency */ + BOUND("dotted"), + /** stuck dependency */ + STUCK("dashed"); + + final String dotSyle; + + private DependencyKind(String dotSyle) { + this.dotSyle = dotSyle; + } + + @Override + public String toDotStyle() { + return dotSyle; + } + } /** * This is the graph inference solver - the solver organizes all inference variables in @@ -1298,10 +1371,12 @@ class GraphSolver { InferenceContext inferenceContext; + Map<Type, Set<Type>> stuckDeps; Warner warn; - GraphSolver(InferenceContext inferenceContext, Warner warn) { + GraphSolver(InferenceContext inferenceContext, Map<Type, Set<Type>> stuckDeps, Warner warn) { this.inferenceContext = inferenceContext; + this.stuckDeps = stuckDeps; this.warn = warn; } @@ -1312,7 +1387,7 @@ */ void solve(GraphStrategy sstrategy) { checkWithinBounds(inferenceContext, warn); //initial propagation of bounds - InferenceGraph inferenceGraph = new InferenceGraph(); + InferenceGraph inferenceGraph = new InferenceGraph(stuckDeps); while (!sstrategy.done()) { InferenceGraph.Node nodeToSolve = sstrategy.pickNode(inferenceGraph); List<Type> varsToSolve = List.from(nodeToSolve.data); @@ -1357,64 +1432,172 @@ */ class Node extends GraphUtils.TarjanNode<ListBuffer<Type>> { - Set<Node> deps; + /** map listing all dependencies (grouped by kind) */ + EnumMap<DependencyKind, Set<Node>> deps; Node(Type ivar) { super(ListBuffer.of(ivar)); - this.deps = new HashSet<Node>(); + this.deps = new EnumMap<DependencyKind, Set<Node>>(DependencyKind.class); } @Override - public Iterable<? extends Node> getDependencies() { - return deps; + public GraphUtils.DependencyKind[] getSupportedDependencyKinds() { + return DependencyKind.values(); + } + + @Override + public String getDependencyName(GraphUtils.Node<ListBuffer<Type>> to, GraphUtils.DependencyKind dk) { + if (dk == DependencyKind.STUCK) return ""; + else { + StringBuilder buf = new StringBuilder(); + String sep = ""; + for (Type from : data) { + UndetVar uv = (UndetVar)inferenceContext.asFree(from); + for (Type bound : uv.getBounds(InferenceBound.values())) { + if (bound.containsAny(List.from(to.data))) { + buf.append(sep); + buf.append(bound); + sep = ","; + } + } + } + return buf.toString(); + } + } + + @Override + public Iterable<? extends Node> getAllDependencies() { + return getDependencies(DependencyKind.values()); } @Override - public String printDependency(GraphUtils.Node<ListBuffer<Type>> to) { - StringBuilder buf = new StringBuilder(); - String sep = ""; - for (Type from : data) { - UndetVar uv = (UndetVar)inferenceContext.asFree(from); - for (Type bound : uv.getBounds(InferenceBound.values())) { - if (bound.containsAny(List.from(to.data))) { - buf.append(sep); - buf.append(bound); - sep = ","; - } + public Iterable<? extends TarjanNode<ListBuffer<Type>>> getDependenciesByKind(GraphUtils.DependencyKind dk) { + return getDependencies((DependencyKind)dk); + } + + /** + * Retrieves all dependencies with given kind(s). + */ + protected Set<Node> getDependencies(DependencyKind... depKinds) { + Set<Node> buf = new LinkedHashSet<Node>(); + for (DependencyKind dk : depKinds) { + Set<Node> depsByKind = deps.get(dk); + if (depsByKind != null) { + buf.addAll(depsByKind); } } - return buf.toString(); + return buf; + } + + /** + * Adds dependency with given kind. + */ + protected void addDependency(DependencyKind dk, Node depToAdd) { + Set<Node> depsByKind = deps.get(dk); + if (depsByKind == null) { + depsByKind = new LinkedHashSet<Node>(); + deps.put(dk, depsByKind); + } + depsByKind.add(depToAdd); + } + + /** + * Add multiple dependencies of same given kind. + */ + protected void addDependencies(DependencyKind dk, Set<Node> depsToAdd) { + for (Node n : depsToAdd) { + addDependency(dk, n); + } + } + + /** + * Remove a dependency, regardless of its kind. + */ + protected Set<DependencyKind> removeDependency(Node n) { + Set<DependencyKind> removedKinds = new HashSet<>(); + for (DependencyKind dk : DependencyKind.values()) { + Set<Node> depsByKind = deps.get(dk); + if (depsByKind == null) continue; + if (depsByKind.remove(n)) { + removedKinds.add(dk); + } + } + return removedKinds; + } + + /** + * Compute closure of a give node, by recursively walking + * through all its dependencies (of given kinds) + */ + protected Set<Node> closure(DependencyKind... depKinds) { + boolean progress = true; + Set<Node> closure = new HashSet<Node>(); + closure.add(this); + while (progress) { + progress = false; + for (Node n1 : new HashSet<Node>(closure)) { + progress = closure.addAll(n1.getDependencies(depKinds)); + } + } + return closure; } - boolean isLeaf(Node n) { + /** + * Is this node a leaf? This means either the node has no dependencies, + * or it just has self-dependencies. + */ + protected boolean isLeaf() { //no deps, or only one self dep - return (n.deps.isEmpty() || - n.deps.size() == 1 && n.deps.contains(n)); + Set<Node> allDeps = getDependencies(DependencyKind.BOUND, DependencyKind.STUCK); + if (allDeps.isEmpty()) return true; + for (Node n : allDeps) { + if (n != this) { + return false; + } + } + return true; } - void mergeWith(List<? extends Node> nodes) { + /** + * Merge this node with another node, acquiring its dependencies. + * This routine is used to merge all cyclic node together and + * form an acyclic graph. + */ + protected void mergeWith(List<? extends Node> nodes) { for (Node n : nodes) { Assert.check(n.data.length() == 1, "Attempt to merge a compound node!"); data.appendList(n.data); - deps.addAll(n.deps); + for (DependencyKind dk : DependencyKind.values()) { + addDependencies(dk, n.getDependencies(dk)); + } } //update deps - Set<Node> deps2 = new HashSet<Node>(); - for (Node d : deps) { - if (data.contains(d.data.first())) { - deps2.add(this); - } else { - deps2.add(d); + EnumMap<DependencyKind, Set<Node>> deps2 = new EnumMap<DependencyKind, Set<Node>>(DependencyKind.class); + for (DependencyKind dk : DependencyKind.values()) { + for (Node d : getDependencies(dk)) { + Set<Node> depsByKind = deps2.get(dk); + if (depsByKind == null) { + depsByKind = new LinkedHashSet<Node>(); + deps2.put(dk, depsByKind); + } + if (data.contains(d.data.first())) { + depsByKind.add(this); + } else { + depsByKind.add(d); + } } } deps = deps2; } - void graphChanged(Node from, Node to) { - if (deps.contains(from)) { - deps.remove(from); + /** + * Notify all nodes that something has changed in the graph + * topology. + */ + private void graphChanged(Node from, Node to) { + for (DependencyKind dk : removeDependency(from)) { if (to != null) { - deps.add(to); + addDependency(dk, to); } } } @@ -1423,8 +1606,21 @@ /** the nodes in the inference graph */ ArrayList<Node> nodes; - InferenceGraph() { - initNodes(); + InferenceGraph(Map<Type, Set<Type>> optDeps) { + initNodes(optDeps); + } + + /** + * Basic lookup helper for retrieving a graph node given an inference + * variable type. + */ + public Node findNode(Type t) { + for (Node n : nodes) { + if (n.data.contains(t)) { + return n; + } + } + return null; } /** @@ -1451,24 +1647,32 @@ * Create the graph nodes. First a simple node is created for every inference * variables to be solved. Then Tarjan is used to found all connected components * in the graph. For each component containing more than one node, a super node is - * created, effectively replacing the original cyclic nodes. + * created, effectively replacing the original cyclic nodes. */ - void initNodes() { + void initNodes(Map<Type, Set<Type>> stuckDeps) { + //add nodes nodes = new ArrayList<Node>(); for (Type t : inferenceContext.restvars()) { nodes.add(new Node(t)); } + //add dependencies for (Node n_i : nodes) { Type i = n_i.data.first(); + Set<Type> optDepsByNode = stuckDeps.get(i); for (Node n_j : nodes) { Type j = n_j.data.first(); UndetVar uv_i = (UndetVar)inferenceContext.asFree(i); if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) { - //update i's deps - n_i.deps.add(n_j); + //update i's bound dependencies + n_i.addDependency(DependencyKind.BOUND, n_j); + } + if (optDepsByNode != null && optDepsByNode.contains(j)) { + //update i's stuck dependencies + n_i.addDependency(DependencyKind.STUCK, n_j); } } } + //merge cyclic nodes ArrayList<Node> acyclicNodes = new ArrayList<Node>(); for (List<? extends Node> conSubGraph : GraphUtils.tarjan(nodes)) { if (conSubGraph.length() > 1) { @@ -1596,12 +1800,12 @@ return filterVars(new Filter<UndetVar>() { public boolean accepts(UndetVar uv) { return uv.getBounds(InferenceBound.UPPER) - .diff(uv.getDeclaredBounds()) - .appendList(uv.getBounds(InferenceBound.EQ, InferenceBound.LOWER)).nonEmpty(); + .diff(uv.getDeclaredBounds()) + .appendList(uv.getBounds(InferenceBound.EQ, InferenceBound.LOWER)).nonEmpty(); } }); } - + private List<Type> filterVars(Filter<UndetVar> fu) { ListBuffer<Type> res = ListBuffer.lb(); for (Type t : undetvars) { @@ -1786,12 +1990,16 @@ }, List.of(t)); } } + + private void solve(GraphStrategy ss, Warner warn) { + solve(ss, new HashMap<Type, Set<Type>>(), warn); + } /** * Solve with given graph strategy. */ - private void solve(GraphStrategy ss, Warner warn) { - GraphSolver s = new GraphSolver(this, warn); + private void solve(GraphStrategy ss, Map<Type, Set<Type>> stuckDeps, Warner warn) { + GraphSolver s = new GraphSolver(this, stuckDeps, warn); s.solve(ss); } @@ -1810,7 +2018,7 @@ * Solve all variables in the given list. */ public void solve(final List<Type> vars, Warner warn) { - solve(new BestLeafSolver(vars) { + solve(new BestLeafSolver(vars, List.<Type>nil()) { public boolean done() { return !free(asInstTypes(vars)); } @@ -1820,18 +2028,12 @@ /** * Solve at least one variable in given list. */ - public void solveAny(List<Type> varsToSolve, Warner warn) { - checkWithinBounds(this, warn); //propagate bounds - List<Type> boundedVars = boundedVars().intersect(restvars()).intersect(varsToSolve); - if (boundedVars.isEmpty()) { - throw inferenceException.setMessage("cyclic.inference", - freeVarsIn(varsToSolve)); - } - solve(new BestLeafSolver(boundedVars) { + public void solveAny(List<Type> varsToSolve, Map<Type, Set<Type>> optDeps, List<Type> varsToAvoid, Warner warn) { + solve(new BestLeafSolver(varsToSolve.intersect(restvars()), varsToAvoid) { public boolean done() { return instvars().intersect(varsToSolve).nonEmpty(); } - }, warn); + }, optDeps, warn); } /**
--- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Wed Jul 24 16:54:37 2013 -0700 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Thu Jul 25 16:29:24 2013 +0100 @@ -643,7 +643,7 @@ * Retrieve the method check object that will be used during a * most specific check. */ - MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict); + MethodCheck mostSpecificCheck(Symbol other, List<Type> actuals, boolean strict); } /** @@ -689,7 +689,7 @@ //do nothing - method always applicable regardless of actuals } - public MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict) { + public MethodCheck mostSpecificCheck(Symbol other, List<Type> actuals, boolean strict) { return this; } }; @@ -764,7 +764,7 @@ throw ex.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); } - public MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict) { + public MethodCheck mostSpecificCheck(Symbol other, List<Type> actuals, boolean strict) { return nilMethodCheck; } } @@ -860,8 +860,8 @@ } @Override - public MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict) { - return new MostSpecificCheck(strict, actuals); + public MethodCheck mostSpecificCheck(Symbol other, List<Type> actuals, boolean strict) { + return new MostSpecificCheck(other, strict, actuals); } }; @@ -907,8 +907,8 @@ } @Override - public MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict) { - return new MostSpecificCheck(strict, actuals); + public MethodCheck mostSpecificCheck(Symbol other, List<Type> actuals, boolean strict) { + return new MostSpecificCheck(other, strict, actuals); } }; @@ -1005,8 +1005,10 @@ boolean strict; List<Type> actuals; - - MostSpecificCheck(boolean strict, List<Type> actuals) { + Symbol other; + + MostSpecificCheck(Symbol other, boolean strict, List<Type> actuals) { + this.other = other; this.strict = strict; this.actuals = actuals; } @@ -1057,14 +1059,32 @@ switch (actual.getTag()) { case DEFERRED: DeferredType dt = (DeferredType) actual; - DeferredType.SpeculativeCache.Entry e = dt.speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase); - return (e == null || e.speculativeTree == deferredAttr.stuckTree) - ? false : mostSpecific(found, req, e.speculativeTree, warn); + DeferredType.SpeculativeCache.Entry e1 = dt.speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase); + DeferredType.SpeculativeCache.Entry e2 = dt.speculativeCache.get(other, deferredAttrContext.phase); + return (e1 == null || e2 == null || + e1.speculativeTree == deferredAttr.stuckTree || + e2.speculativeTree == deferredAttr.stuckTree) ? + super.compatible(found, req, warn) : + mostSpecific(e2.resultInfo.pt, patchPt(e1), e1.speculativeTree, warn); default: return standaloneMostSpecific(found, req, actual, warn); } } } + + /** + * We need to fixup inference variables in the speculative cache and + * replace them the inference variables bring used by the most specific check. + */ + Type patchPt(DeferredType.SpeculativeCache.Entry e) { + List<Type> inferenceVarsFrom = + e.resultInfo.checkContext.inferenceContext().inferencevars; + List<Type> inferenceVarsTo = + deferredAttrContext.inferenceContext.inferencevars; + return types.subst(e.resultInfo.pt, + inferenceVarsFrom.at(inferenceVarsTo.length()), //original method vars are in front + inferenceVarsTo); + } private boolean mostSpecific(Type t, Type s, JCTree tree, Warner warn) { MostSpecificChecker msc = new MostSpecificChecker(t, s, warn); @@ -1201,7 +1221,7 @@ } } - public MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict) { + public MethodCheck mostSpecificCheck(Symbol other, List<Type> actuals, boolean strict) { Assert.error("Cannot get here!"); return null; } @@ -1526,7 +1546,7 @@ currentResolutionContext = new MethodResolutionContext(); currentResolutionContext.step = prevResolutionContext.step; currentResolutionContext.methodCheck = - prevResolutionContext.methodCheck.mostSpecificCheck(actuals, !allowBoxing); + prevResolutionContext.methodCheck.mostSpecificCheck(m1, actuals, !allowBoxing); Type mst = instantiate(env, site, m2, null, adjustArgs(types.lowerBounds(types.memberType(site, m1).getParameterTypes()), m1, maxLength, useVarargs), null, allowBoxing, useVarargs, noteWarner); @@ -3905,6 +3925,16 @@ return (JCDiagnostic)((JCDiagnostic)d.getArgs()[0]).getArgs()[1]; } }); + + rewriters.put(new Template(argMismatchRegex, skip, new Template("(.*)(bad.arg.types.in.lambda)", skip, skip)), + new DiagnosticRewriter() { + @Override + public JCDiagnostic rewriteDiagnostic(JCDiagnostic.Factory diags, + DiagnosticPosition preferedPos, DiagnosticSource preferredSource, + DiagnosticType preferredKind, JCDiagnostic d) { + return (JCDiagnostic)((JCDiagnostic)d.getArgs()[1]).getArgs()[1]; + } + }); rewriters.put(new Template(argMismatchRegex, skip), new DiagnosticRewriter() {
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Jul 24 16:54:37 2013 -0700 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Jul 25 16:29:24 2013 +0100 @@ -1901,10 +1901,6 @@ inferred: {0}\n\ equality constraints(s): {1} -# 0: list of type -compiler.misc.cyclic.inference=\ - Cannot instantiate inference variables {0} because of an inference loop - # 0: symbol compiler.misc.diamond=\ {0}<>
--- a/src/share/classes/com/sun/tools/javac/util/GraphUtils.java Wed Jul 24 16:54:37 2013 -0700 +++ b/src/share/classes/com/sun/tools/javac/util/GraphUtils.java Thu Jul 25 16:29:24 2013 +0100 @@ -31,6 +31,18 @@ * deletion without notice.</b> */ public class GraphUtils { + + /** + * Basic interface for defining various dependency kinds. All dependency kinds + * must at least support basic capabilities to tell the DOT engine how to render them. + */ + public interface DependencyKind { + /** + * Returns the DOT representation (to be used in a {@code style} attribute + * that's most suited for this dependency kind. + */ + String toDotStyle(); + } /** * This class is a basic abstract class for representing a node. @@ -42,10 +54,21 @@ public Node(D data) { this.data = data; } + + /** + * Get an array of the dependency kinds supported by this node. + */ + public abstract DependencyKind[] getSupportedDependencyKinds(); - public abstract Iterable<? extends Node<D>> getDependencies(); + /** + * Get all dependencies, regardless of their kind. + */ + public abstract Iterable<? extends Node<D>> getAllDependencies(); - public abstract String printDependency(Node<D> to); + /** + * Get a name for the dependency (of given kind) linking this node to a given node + */ + public abstract String getDependencyName(Node<D> to, DependencyKind dk); @Override public String toString() { @@ -66,7 +89,9 @@ super(data); } - public abstract Iterable<? extends TarjanNode<D>> getDependencies(); + public abstract Iterable<? extends TarjanNode<D>> getAllDependencies(); + + public abstract Iterable<? extends TarjanNode<D>> getDependenciesByKind(DependencyKind dk); public int compareTo(TarjanNode<D> o) { return (index < o.index) ? -1 : (index == o.index) ? 0 : 1; @@ -95,7 +120,7 @@ index++; stack.prepend(v); v.active = true; - for (TarjanNode<D> nd: v.getDependencies()) { + for (TarjanNode<D> nd: v.getAllDependencies()) { @SuppressWarnings("unchecked") N n = (N)nd; if (n.index == -1) { @@ -134,9 +159,11 @@ } //dump arcs for (TarjanNode<D> from : nodes) { - for (TarjanNode<D> to : from.getDependencies()) { - buf.append(String.format("%s -> %s [label = \" %s \"];\n", - from.hashCode(), to.hashCode(), from.printDependency(to))); + for (DependencyKind dk : from.getSupportedDependencyKinds()) { + for (TarjanNode<D> to : from.getDependenciesByKind(dk)) { + buf.append(String.format("%s -> %s [label = \" %s \" style = %s ];\n", + from.hashCode(), to.hashCode(), from.getDependencyName(to, dk), dk.toDotStyle())); + } } } buf.append("}\n");
--- a/src/share/classes/com/sun/tools/javac/util/List.java Wed Jul 24 16:54:37 2013 -0700 +++ b/src/share/classes/com/sun/tools/javac/util/List.java Thu Jul 25 16:29:24 2013 +0100 @@ -115,6 +115,16 @@ } return buf.toList(); } + + public List<A> at(int pos) { + ListBuffer<A> buf = ListBuffer.lb(); + int count = 0; + for (A el : this) { + if (count++ == pos) break; + buf.append(el); + } + return buf.toList(); + } /** Construct a list consisting of given element. */
--- a/test/tools/javac/diags/examples/CyclicInference.java Wed Jul 24 16:54:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -// key: compiler.err.prob.found.req -// key: compiler.misc.cyclic.inference - -class CyclicInference { - interface SAM<X> { - void m(X x); - } - - <Z> void g(SAM<Z> sz) { } - - void test() { - g(x-> {}); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177a.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,45 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8016177 8016178 + * @summary structural most specific and stuckness + * @compile/fail/ref=T8016177a.out -XDrawDiagnostics T8016177a.java + */ +import java.util.List; + +class T8016177a { + + interface ToIntFunction<X> { + int m(X x); + } + + interface Function<X, Y> { + Y m(X x); + } + + <T,R> void m1(List<T> s, Function<T,R> f) { } + <T,R> void m1(List<T> s, ToIntFunction<T> f) { } + + <T,R> List<R> m2(List<T> s, Function<T,R> f) { return null; } + <T,R> List<R> m2(List<T> s, ToIntFunction<T> f) { return null; } + + <T,R> List<T> m3(List<T> s, Function<T,R> f) { return null; } + <T,R> List<R> m3(List<T> s, ToIntFunction<T> f) { return null; } + + <T,R> List<T> m4(List<T> s, Function<T,R> f) { return null; } + <T,R> List<T> m4(List<T> s, ToIntFunction<T> f) { return null; } + + <T,R> List<R> m5(List<T> s, Function<T,R> f) { return null; } + <T,R> List<T> m5(List<T> s, ToIntFunction<T> f) { return null; } + + <T extends R,R> List<R> m6(List<T> s, Function<T,R> f) { return null; } + <T extends R,R> List<T> m6(List<T> s, ToIntFunction<T> f) { return null; } + + void test(List<String> ss) { + m1(ss, s->s.length()); //ok + m2(ss, s->s.length()); //ok + m3(ss, s->s.length()); //ambiguous + m4(ss, s->s.length()); //ambiguous + m5(ss, s->s.length()); //ambiguous + m6(ss, s->s.length()); //ambiguous + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177a.out Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,6 @@ +T8016177a.java:40:10: compiler.err.ref.ambiguous: m3, kindname.method, <T,R>m3(java.util.List<T>,T8016177a.Function<T,R>), T8016177a, kindname.method, <T,R>m3(java.util.List<T>,T8016177a.ToIntFunction<T>), T8016177a +T8016177a.java:41:10: compiler.err.ref.ambiguous: m4, kindname.method, <T,R>m4(java.util.List<T>,T8016177a.Function<T,R>), T8016177a, kindname.method, <T,R>m4(java.util.List<T>,T8016177a.ToIntFunction<T>), T8016177a +T8016177a.java:42:10: compiler.err.ref.ambiguous: m5, kindname.method, <T,R>m5(java.util.List<T>,T8016177a.Function<T,R>), T8016177a, kindname.method, <T,R>m5(java.util.List<T>,T8016177a.ToIntFunction<T>), T8016177a +T8016177a.java:43:10: compiler.err.ref.ambiguous: m6, kindname.method, <T,R>m6(java.util.List<T>,T8016177a.Function<T,R>), T8016177a, kindname.method, <T,R>m6(java.util.List<T>,T8016177a.ToIntFunction<T>), T8016177a +T8016177a.java:43:12: compiler.err.prob.found.req: (compiler.misc.infer.no.conforming.assignment.exists: T,R, (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.inconvertible.types: int, java.lang.String))) +5 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177b.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,34 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8016177 8016178 + * @summary structural most specific and stuckness + * @compile/fail/ref=T8016177b.out -XDrawDiagnostics T8016177b.java + */ +class T8016177b { + interface ToIntFunction<X> { + int m(X x); + } + + interface Function<X, Y> { + Y m(X x); + } + + <U, V> Function<U, V> id(Function<U, V> arg) { return null; } + + <U, V> Function<U, V> id2(Function<U, V> arg) { return null; } + <U> ToIntFunction<U> id2(ToIntFunction<U> arg) { return null; } + + + <X,Y,Z> X f(Y arg, Function<Y, Z> f) { return null; } + + <X,Y,Z> X f2(Y arg, Function<Y, Z> f) { return null; } + <X,Y> X f2(Y arg, ToIntFunction<Y> f) { return null; } + + <T> T g(T arg) { return null; } + + void test() { + g(f("hi", id(x->1))); //ok + g(f("hi", id2(x->1))); //ambiguous + g(f2("hi", id(x->1))); //ok + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177b.out Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,2 @@ +T8016177b.java:31:19: compiler.err.ref.ambiguous: id2, kindname.method, <U,V>id2(T8016177b.Function<U,V>), T8016177b, kindname.method, <U>id2(T8016177b.ToIntFunction<U>), T8016177b +1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177c.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8016081 8016178 + * @summary structural most specific and stuckness + * @compile T8016177c.java + */ + +class T8016177c { + + interface Function<X, Y> { + Y m(X x); + } + + interface ExtFunction<X, Y> extends Function<X, Y> { } + + <U, V> U m(Function<U, V> f) { return null; } + <U, V> U m(ExtFunction<U, V> f) { return null; } + + void test() { m(x->1); } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177d.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8016081 8016178 + * @summary structural most specific and stuckness + * @compile T8016177d.java + */ +import java.util.*; + +class T8016177d { + + interface UnaryOperator<X> { + X m(X x); + } + + interface IntStream { + IntStream sorted(); + IntStream distinct(); + IntStream limit(int i); + } + + abstract class WrappingUnaryOperator<S> implements UnaryOperator<S> { } + + <S1> WrappingUnaryOperator<S1> wrap1(UnaryOperator<S1> uo) { return null; } + <S2> WrappingUnaryOperator<S2> wrap2(UnaryOperator<S2> uo) { return null; } + <S3> WrappingUnaryOperator<S3> wrap3(UnaryOperator<S3> uo) { return null; } + + <P> List<List<P>> perm(List<P> l) { return null; } + + List<List<WrappingUnaryOperator<IntStream>>> intPermutationOfFunctions = + perm(Arrays.asList( + wrap1(s -> s.sorted()), + wrap2(s -> s.distinct()), + wrap3(s -> s.limit(5)) + )); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177e.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8016081 8016178 + * @summary structural most specific and stuckness + * @compile T8016177e.java + */ +import java.util.*; + +class T8016177e { + + interface TerminalOp<X, Y> { } + + interface Consumer<X> { + void m(X x); + } + + <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action) { return null; } + + <T> void test() { + Map<T, Boolean> map = null; + TerminalOp<T, Void> forEachOp = makeRef(t -> { map.put(t, null); }); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/8016177/T8016177f.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8016081 8016178 + * @summary structural most specific and stuckness + * @compile T8016177f.java + */ +import java.util.*; + +class T8016177f { + + interface Function<S, T> { + T apply(S s); + } + + interface IntFunction<T> { + T apply(int s); + } + + + interface BiConsumer<X,Y> { + void m(X x, Y y); + } + + interface Consumer<X> { + void m(X x); + } + + interface Supplier<X> { + X make(); + } + + interface TestData<T, S extends BaseStream<T, S>> { + interface OfRef<T> extends TestData<T, Stream<T>> { } + interface OfDouble extends TestData<Double, DoubleStream> { } + } + + interface BaseStream<T, S extends BaseStream<T, S>> { } + + interface Stream<T> extends BaseStream<T, Stream<T>> { + <M> Stream<M> map(Function<T, M> s); + <R> R collect(Supplier<R> resultFactory, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner); + <Z> Z[] toArray(IntFunction<Z[]> s); + } + + interface DoubleStream extends BaseStream<Double, DoubleStream> { + DoubleStream filter(DoublePredicate dp); + double[] toArray(); + } + + interface DoublePredicate { + boolean p(double d); + } + + <T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> + R exerciseTerminalOps(TestData<T, S_IN> data, + Function<S_IN, S_OUT> streamF, + Function<S_OUT, R> terminalF) { return null; } + + <O> TestData.OfRef<O> ofCollection(Collection<O> collection) { return null; } + + void test1(TestData.OfDouble data, DoublePredicate dp) { + exerciseTerminalOps(data, s -> s.filter(dp), s -> s.toArray()); + } + + void test2(Function<Double, Integer> fdi, TestData.OfRef<Double> td, Stream<Integer> si) { + exerciseTerminalOps( + ofCollection((List<Double>)null), + s -> s.map(fdi), + s -> s.toArray(Integer[]::new)); + } +}
--- a/test/tools/javac/lambda/MethodReference70.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/MethodReference70.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,3 +1,2 @@ -MethodReference70.java:26:10: compiler.err.ref.ambiguous: g, kindname.method, <Z>g(MethodReference70.F<Z>), MethodReference70, kindname.method, <Z>g(MethodReference70.G<Z>), MethodReference70 -MethodReference70.java:26:11: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: Z) -2 errors +MethodReference70.java:26:10: compiler.err.cant.apply.symbols: kindname.method, g, @553,{(compiler.misc.inapplicable.method: kindname.method, MethodReference70, <Z>g(MethodReference70.F<Z>), (compiler.misc.infer.no.conforming.assignment.exists: Z, (compiler.misc.invalid.mref: kindname.method, (compiler.misc.cant.apply.symbols: kindname.method, m2, java.lang.Object,{(compiler.misc.inapplicable.method: kindname.method, MethodReference70, m2(java.lang.Integer), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer))),(compiler.misc.inapplicable.method: kindname.method, MethodReference70, m2(java.lang.String), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.String)))})))),(compiler.misc.inapplicable.method: kindname.method, MethodReference70, <Z>g(MethodReference70.G<Z>), (compiler.misc.infer.no.conforming.assignment.exists: Z, (compiler.misc.invalid.mref: kindname.method, (compiler.misc.cant.apply.symbols: kindname.method, m2, java.lang.Object,{(compiler.misc.inapplicable.method: kindname.method, MethodReference70, m2(java.lang.Integer), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer))),(compiler.misc.inapplicable.method: kindname.method, MethodReference70, m2(java.lang.String), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.String)))}))))} +1 error
--- a/test/tools/javac/lambda/MethodReference71.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/MethodReference71.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,3 +1,2 @@ -MethodReference71.java:24:10: compiler.err.ref.ambiguous: g, kindname.method, <Z>g(MethodReference71.F<Z>), MethodReference71, kindname.method, <Z>g(MethodReference71.G<Z>), MethodReference71 -MethodReference71.java:24:11: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: Z) -2 errors +MethodReference71.java:24:10: compiler.err.cant.apply.symbols: kindname.method, g, @523,{(compiler.misc.inapplicable.method: kindname.method, MethodReference71, <Z>g(MethodReference71.F<Z>), (compiler.misc.infer.no.conforming.assignment.exists: Z, (compiler.misc.invalid.mref: kindname.method, (compiler.misc.cant.apply.symbol: kindname.method, m2, java.lang.Integer[], java.lang.Object, kindname.class, MethodReference71, (compiler.misc.varargs.argument.mismatch: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer)))))),(compiler.misc.inapplicable.method: kindname.method, MethodReference71, <Z>g(MethodReference71.G<Z>), (compiler.misc.infer.no.conforming.assignment.exists: Z, (compiler.misc.invalid.mref: kindname.method, (compiler.misc.cant.apply.symbol: kindname.method, m2, java.lang.Integer[], java.lang.Object, kindname.class, MethodReference71, (compiler.misc.varargs.argument.mismatch: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer))))))} +1 error
--- a/test/tools/javac/lambda/TargetType10.java Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType10.java Thu Jul 25 16:29:24 2013 +0100 @@ -1,10 +1,10 @@ /* * @test /nodynamiccopyright/ - * @bug 8003280 + * @bug 8003280 8016177 * @summary Add lambda tests * check that wildcards in the target method of a lambda conversion is handled correctly * @author Maurizio Cimadamore - * @compile/fail/ref=TargetType10.out -XDrawDiagnostics TargetType10.java + * @compile TargetType10.java */ class TargetType10 {
--- a/test/tools/javac/lambda/TargetType10.out Wed Jul 24 16:54:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -TargetType10.java:17:18: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: B,A) -1 error
--- a/test/tools/javac/lambda/TargetType21.java Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType21.java Thu Jul 25 16:29:24 2013 +0100 @@ -26,8 +26,8 @@ void test() { call(x -> { throw new Exception(); }); //ambiguous - call(x -> { System.out.println(""); }); //ambiguous - call(x -> { return (Object) null; }); //cyclic inference + call(x -> { System.out.println(""); }); //ok (only one is void) + call(x -> { return (Object) null; }); //ok (only one returns Object) call(x -> { return null; }); //ambiguous } }
--- a/test/tools/javac/lambda/TargetType21.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType21.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,6 +1,4 @@ TargetType21.java:28:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM2), TargetType21, kindname.method, <R,A>call(TargetType21.SAM3<R,A>), TargetType21 TargetType21.java:28:14: compiler.err.incompatible.thrown.types.in.lambda: java.lang.Exception -TargetType21.java:29:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM2), TargetType21, kindname.method, <R,A>call(TargetType21.SAM3<R,A>), TargetType21 -TargetType21.java:30:13: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: A) TargetType21.java:31:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM1), TargetType21, kindname.method, <R,A>call(TargetType21.SAM3<R,A>), TargetType21 -5 errors +3 errors
--- a/test/tools/javac/lambda/TargetType26.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType26.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,2 +1,2 @@ -TargetType26.java:16:11: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: Z) +TargetType26.java:16:7: compiler.err.cant.apply.symbol: kindname.method, call, Z, @340, kindname.class, TargetType26, (compiler.misc.infer.no.conforming.assignment.exists: Z, (compiler.misc.not.a.functional.intf: java.lang.Object)) 1 error
--- a/test/tools/javac/lambda/TargetType27.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType27.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,2 +1,2 @@ -TargetType27.java:18:10: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: R) +TargetType27.java:18:10: compiler.err.prob.found.req: (compiler.misc.infer.no.conforming.assignment.exists: A,R, (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.not.a.functional.intf: java.lang.Object))) 1 error
--- a/test/tools/javac/lambda/TargetType39.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType39.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,3 +1,3 @@ -TargetType39.java:19:13: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: U) -TargetType39.java:20:13: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: V) +TargetType39.java:19:9: compiler.err.cant.apply.symbol: kindname.method, call, TargetType39.SAM<U,V>, @442, kindname.class, TargetType39, (compiler.misc.infer.no.conforming.assignment.exists: U,V, (compiler.misc.incompatible.type.in.conditional: (compiler.misc.inconvertible.types: TargetType39.SAM<java.lang.String,java.lang.Void>, TargetType39.SAM<java.lang.Object,V>))) +TargetType39.java:20:9: compiler.err.cant.apply.symbol: kindname.method, call, TargetType39.SAM<U,V>, @479, kindname.class, TargetType39, (compiler.misc.infer.no.conforming.assignment.exists: U,V, (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.incompatible.type.in.conditional: (compiler.misc.not.a.functional.intf: java.lang.Object)))) 2 errors
--- a/test/tools/javac/lambda/TargetType57.out Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/TargetType57.out Thu Jul 25 16:29:24 2013 +0100 @@ -1,2 +1,2 @@ -TargetType57.java:14:42: compiler.err.cant.resolve.location.args: kindname.method, nonExistentMethod, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer) +TargetType57.java:14:9: compiler.err.cant.apply.symbol: kindname.method, m, java.util.List<S_IN>,java.util.function.Function<S_IN,S_OUT>,java.util.function.Function<S_OUT,R>, java.util.List<java.lang.Integer>,@340,@359, kindname.class, TargetType57, (compiler.misc.infer.no.conforming.assignment.exists: U,R,S_IN,S_OUT, (compiler.misc.bad.arg.types.in.lambda: java.lang.Integer, (compiler.err.cant.resolve.location.args: kindname.method, nonExistentMethod, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)))) 1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/typeInference/InferenceTest6.java Thu Jul 25 16:29:24 2013 +0100 @@ -0,0 +1,26 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8003280 8016177 + * @summary Add lambda tests + * Missing cast to SAM type that causes type inference to not work. + * @compile -XDrawDiagnostics InferenceTest6.java + */ + +import java.util.*; + +public class InferenceTest6 { + public static void main(String[] args) { + InferenceTest6 test = new InferenceTest6(); + test.method1(n -> {}); + test.method1((SAM1<String>)n -> {}); + test.method1((SAM1<Integer>)n -> {n++;}); + test.method1((SAM1<Comparator<String>>)n -> {List<String> list = Arrays.asList("string1", "string2"); Collections.sort(list,n);}); + test.method1((SAM1<Thread>)n -> {n.start();}); + } + + interface SAM1<X> { + void m1(X arg); + } + + <X> void method1(SAM1<X> s) {} +}
--- a/test/tools/javac/lambda/typeInference/InferenceTest_neg5.java Wed Jul 24 16:54:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * @test /nodynamiccopyright/ - * @bug 8003280 - * @summary Add lambda tests - * Missing cast to SAM type that causes type inference to not work. - * @compile/fail/ref=InferenceTest_neg5.out -XDrawDiagnostics InferenceTest_neg5.java - */ - -import java.util.*; - -public class InferenceTest_neg5 { - public static void main(String[] args) { - InferenceTest_neg5 test = new InferenceTest_neg5(); - test.method1(n -> {}); - test.method1((SAM1<String>)n -> {}); - test.method1((SAM1<Integer>)n -> {n++;}); - test.method1((SAM1<Comparator<String>>)n -> {List<String> list = Arrays.asList("string1", "string2"); Collections.sort(list,n);}); - test.method1((SAM1<Thread>)n -> {n.start();}); - } - - interface SAM1<X> { - void m1(X arg); - } - - <X> void method1(SAM1<X> s) {} -}
--- a/test/tools/javac/lambda/typeInference/InferenceTest_neg5.out Wed Jul 24 16:54:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -InferenceTest_neg5.java:14:21: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: X) -1 error
--- a/test/tools/javac/lambda/typeInference/combo/TypeInferenceComboTest.java Wed Jul 24 16:54:37 2013 -0700 +++ b/test/tools/javac/lambda/typeInference/combo/TypeInferenceComboTest.java Thu Jul 25 16:29:24 2013 +0100 @@ -227,12 +227,7 @@ } else if (lambdaBodyType != LambdaBody.RETURN_ARG) return false; - if ( genericDeclKind == GenericDeclKind.GENERIC_NOBOUND || - genericDeclKind == GenericDeclKind.GENERIC_BOUND ) { - if ( parameterType == TypeKind.GENERIC && - parameterKind == ParameterKind.IMPLICIT) //cyclic inference - return false; - } + return true; }