8178150: Regression in logic for handling inference stuck constraints
Fix broken logic for input/output inference variable dependency Reviewed-by: vromero, bsrbnd
This commit is contained in:
parent
86636eba81
commit
da3ff94812
src/jdk.compiler/share/classes/com/sun/tools/javac/comp
test/langtools/tools/javac/generics/inference/8178150
@ -31,6 +31,7 @@ import com.sun.tools.javac.code.*;
|
||||
import com.sun.tools.javac.code.Type.StructuralTypeMapping;
|
||||
import com.sun.tools.javac.code.Types.TypeMapping;
|
||||
import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
|
||||
import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph;
|
||||
import com.sun.tools.javac.comp.Resolve.ResolveError;
|
||||
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
@ -663,28 +664,57 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Pick the deferred node to be unstuck. First, deferred nodes are organized into a graph
|
||||
* (see {@code DeferredAttrContext.buildStuckGraph()}, where a node N1 depends on another node N2
|
||||
* if its input variable depends (as per the inference graph) on the output variables of N2
|
||||
* (see {@code DeferredAttrContext.canInfluence()}.
|
||||
*
|
||||
* Then, the chosen deferred node is the first strongly connected component containing exactly
|
||||
* one node found in such a graph. If no such component is found, the first deferred node is chosen.
|
||||
*/
|
||||
DeferredAttrNode pickDeferredNode() {
|
||||
List<StuckNode> stuckGraph = buildStuckGraph();
|
||||
//compute tarjan on the stuck graph
|
||||
List<? extends StuckNode> csn = GraphUtils.tarjan(stuckGraph).get(0);
|
||||
return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0);
|
||||
}
|
||||
|
||||
List<StuckNode> buildStuckGraph() {
|
||||
//first, build inference graph
|
||||
infer.doIncorporation(inferenceContext, warn);
|
||||
InferenceGraph graph = infer.new GraphSolver(inferenceContext, types.noWarnings)
|
||||
.new InferenceGraph();
|
||||
//then, build stuck graph
|
||||
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.
|
||||
//B's output variables can influence A's input variables.
|
||||
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);
|
||||
}
|
||||
for (StuckNode sn2 : nodes) {
|
||||
if (sn1 != sn2 && canInfluence(graph, sn2, sn1)) {
|
||||
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);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
boolean canInfluence(InferenceGraph graph, StuckNode sn1, StuckNode sn2) {
|
||||
Set<Type> outputVars = sn1.data.deferredStuckPolicy.depVars();
|
||||
for (Type inputVar : sn2.data.deferredStuckPolicy.stuckVars()) {
|
||||
InferenceGraph.Node inputNode = graph.findNode(inputVar);
|
||||
//already solved stuck vars do not appear in the graph
|
||||
if (inputNode != null) {
|
||||
Set<InferenceGraph.Node> inputClosure = inputNode.closure();
|
||||
if (outputVars.stream()
|
||||
.map(graph::findNode)
|
||||
.anyMatch(inputClosure::contains)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class StuckNode extends GraphUtils.TarjanNode<DeferredAttrNode, StuckNode> {
|
||||
|
@ -61,6 +61,7 @@ import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
@ -1706,7 +1707,7 @@ public class Infer {
|
||||
|
||||
Node(Type ivar) {
|
||||
super(ListBuffer.of(ivar));
|
||||
this.deps = new HashSet<>();
|
||||
this.deps = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1750,6 +1751,24 @@ public class Infer {
|
||||
return deps.remove(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute closure of a give node, by recursively walking
|
||||
* through all its dependencies.
|
||||
*/
|
||||
protected Set<Node> closure() {
|
||||
Set<Node> closure = new HashSet<>();
|
||||
closureInternal(closure);
|
||||
return closure;
|
||||
}
|
||||
|
||||
private void closureInternal(Set<Node> closure) {
|
||||
if (closure.add(this)) {
|
||||
for (Node n : deps) {
|
||||
n.closureInternal(closure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this node a leaf? This means either the node has no dependencies,
|
||||
* or it just has self-dependencies.
|
||||
@ -1777,7 +1796,7 @@ public class Infer {
|
||||
addDependencies(n.deps);
|
||||
}
|
||||
//update deps
|
||||
Set<Node> deps2 = new HashSet<>();
|
||||
Set<Node> deps2 = new LinkedHashSet<>();
|
||||
for (Node d : deps) {
|
||||
if (data.contains(d.data.first())) {
|
||||
deps2.add(this);
|
||||
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8178150
|
||||
* @summary Regression in logic for handling inference stuck constraints
|
||||
* @compile T8178150.java
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
|
||||
class T8178150 {
|
||||
|
||||
public static void test(List<List<String>> testList, Logger LOGGER) {
|
||||
testList.forEach(T8178150.bind(cast(LOGGER::info), iterable -> ""));
|
||||
testList.forEach(T8178150.bind_transitive(cast_transitive(LOGGER::info), iterable -> ""));
|
||||
}
|
||||
|
||||
private static <T1, T2> TestProcedure<T1, T2> bind(Consumer<T2> delegate, Function<? super T1, T2> function) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <C> Consumer<C> cast(Consumer<C> consumer) {
|
||||
return consumer;
|
||||
}
|
||||
|
||||
private static <T1, T2, U extends T2> TestProcedure<T1, T2> bind_transitive(Consumer<U> delegate, Function<? super T1, T2> function) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <C> C cast_transitive(C consumer) {
|
||||
return consumer;
|
||||
}
|
||||
|
||||
private static final class TestProcedure<X1, X2> implements Consumer<X1> {
|
||||
@Override
|
||||
public void accept(final X1 t1) { }
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user