langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
changeset 38516 b643c42e9d25
parent 38512 c71e1cdd6674
child 42827 36468b5fa7f4
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Tue May 17 01:35:36 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Tue May 17 17:53:18 2016 +0100
@@ -35,6 +35,7 @@
 import com.sun.tools.javac.tree.*;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.GraphUtils.DependencyKind;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.comp.Attr.ResultInfo;
@@ -44,9 +45,10 @@
 import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.LinkedHashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
@@ -576,28 +578,11 @@
          */
         void complete() {
             while (!deferredAttrNodes.isEmpty()) {
-                Map<Type, Set<Type>> depVarsMap = new LinkedHashMap<>();
-                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)) {
-                        List<Type> restStuckVars =
-                                List.from(deferredAttrNode.deferredStuckPolicy.stuckVars())
-                                .intersect(inferenceContext.restvars());
-                        stuckVars = stuckVars.prependList(restStuckVars);
-                        //update dependency map
-                        for (Type t : List.from(deferredAttrNode.deferredStuckPolicy.depVars())
-                                .intersect(inferenceContext.restvars())) {
-                            Set<Type> prevDeps = depVarsMap.get(t);
-                            if (prevDeps == null) {
-                                prevDeps = new LinkedHashSet<>();
-                                depVarsMap.put(t, prevDeps);
-                            }
-                            prevDeps.addAll(restStuckVars);
-                        }
-                    } else {
+                    if (deferredAttrNode.process(this)) {
                         deferredAttrNodes.remove(deferredAttrNode);
                         progress = true;
                     }
@@ -612,7 +597,9 @@
                     //remove all variables that have already been instantiated
                     //from the list of stuck variables
                     try {
-                        inferenceContext.solveAny(stuckVars, depVarsMap, warn);
+                        //find stuck expression to unstuck
+                        DeferredAttrNode toUnstuck = pickDeferredNode();
+                        inferenceContext.solveAny(List.from(toUnstuck.deferredStuckPolicy.stuckVars()), warn);
                         inferenceContext.notifyChange();
                     } catch (Infer.GraphStrategy.NodeNotFoundException ex) {
                         //this means that we are in speculative mode and the
@@ -634,6 +621,59 @@
             }
             return dac.parent.insideOverloadPhase();
         }
+
+        /**
+         * Pick the deferred node to be unstuck. The chosen node is the first strongly connected
+         * component containing exactly one node found in the dependency graph induced by deferred nodes.
+         * If no such component is found, the first deferred node is returned.
+         */
+        DeferredAttrNode pickDeferredNode() {
+            List<StuckNode> nodes = deferredAttrNodes.stream()
+                    .map(StuckNode::new)
+                    .collect(List.collector());
+            //init stuck expression graph; a deferred node A depends on a deferred node B iff
+            //the intersection between A's input variable and B's output variable is non-empty.
+            for (StuckNode sn1 : nodes) {
+                for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) {
+                    for (StuckNode sn2 : nodes) {
+                        if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) {
+                            sn1.deps.add(sn2);
+                        }
+                    }
+                }
+            }
+            //compute tarjan on the stuck graph
+            List<? extends StuckNode> csn = GraphUtils.tarjan(nodes).get(0);
+            return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0);
+        }
+
+        class StuckNode extends GraphUtils.TarjanNode<DeferredAttrNode, StuckNode> {
+
+            Set<StuckNode> deps = new HashSet<>();
+
+            StuckNode(DeferredAttrNode data) {
+                super(data);
+            }
+
+            @Override
+            public DependencyKind[] getSupportedDependencyKinds() {
+                return new DependencyKind[] { Infer.DependencyKind.STUCK };
+            }
+
+            @Override
+            public Collection<? extends StuckNode> getDependenciesByKind(DependencyKind dk) {
+                if (dk == Infer.DependencyKind.STUCK) {
+                    return deps;
+                } else {
+                    throw new IllegalStateException();
+                }
+            }
+
+            @Override
+            public Iterable<? extends StuckNode> getAllDependencies() {
+                return deps;
+            }
+        }
     }
 
     /**