8046685: Uncompilable large expressions involving generics.
authormcimadamore
Fri, 13 Nov 2015 12:29:23 +0000
changeset 33906 91b6bee06dc1
parent 33905 1a0b6de8abc2
child 33907 9ee2b1641949
8046685: Uncompilable large expressions involving generics. Summary: Improve inference propagation logic so that unnecessary inference variables are not propagated. Reviewed-by: vromero
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java
langtools/test/tools/javac/lambda/speculative/T8046685.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java	Thu Nov 12 14:13:17 2015 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java	Fri Nov 13 12:29:23 2015 +0000
@@ -182,6 +182,7 @@
                     argtypes, mt.getParameterTypes(), warn);
 
             if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) {
+                checkWithinBounds(inferenceContext, warn);
                 //we are inside method attribution - just return a partially inferred type
                 return new PartiallyInferredMethodType(mt, inferenceContext, env, warn);
             } else if (allowGraphInference &&
@@ -189,13 +190,21 @@
                     !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
                 //inject return constraints earlier
                 checkWithinBounds(inferenceContext, warn); //propagation
+
+                boolean shouldPropagate = resultInfo.checkContext.inferenceContext().free(resultInfo.pt);
+
+                InferenceContext minContext = shouldPropagate ?
+                        inferenceContext.min(roots(mt, deferredAttrContext), true, warn) :
+                        inferenceContext;
+
                 Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
-                        mt, inferenceContext);
+                        mt, minContext);
                 mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype);
+
                 //propagate outwards if needed
-                if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
+                if (shouldPropagate) {
                     //propagate inference context outwards and exit
-                    inferenceContext.dupTo(resultInfo.checkContext.inferenceContext());
+                    minContext.dupTo(resultInfo.checkContext.inferenceContext());
                     deferredAttrContext.complete();
                     return mt;
                 }
@@ -242,6 +251,19 @@
             dumpGraphsIfNeeded(env.tree, msym, resolveContext);
         }
     }
+    //where
+        private List<Type> roots(MethodType mt, DeferredAttrContext deferredAttrContext) {
+            ListBuffer<Type> roots = new ListBuffer<>();
+            roots.add(mt.getReturnType());
+            if (deferredAttrContext != null && deferredAttrContext.mode == AttrMode.CHECK) {
+                roots.addAll(mt.getThrownTypes());
+                for (DeferredAttr.DeferredAttrNode n : deferredAttrContext.deferredAttrNodes) {
+                    roots.addAll(n.deferredStuckPolicy.stuckVars());
+                    roots.addAll(n.deferredStuckPolicy.depVars());
+                }
+            }
+            return roots.toList();
+        }
 
     /**
      * A partially infered method/constructor type; such a type can be checked multiple times
@@ -284,16 +306,21 @@
                  */
                 saved_undet = inferenceContext.save();
                 if (allowGraphInference && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
-                    //inject return constraints earlier
-                    checkWithinBounds(inferenceContext, noWarnings); //propagation
-                    Type res = generateReturnConstraints(env.tree, resultInfo,  //B3
-                        this, inferenceContext);
+                    boolean shouldPropagate = resultInfo.checkContext.inferenceContext().free(resultInfo.pt);
+
+                    InferenceContext minContext = shouldPropagate ?
+                            inferenceContext.min(roots(asMethodType(), null), false, warn) :
+                            inferenceContext;
 
-                    if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
+                    MethodType other = (MethodType)minContext.update(asMethodType());
+                    Type newRestype = generateReturnConstraints(env.tree, resultInfo,  //B3
+                            other, minContext);
+
+                    if (shouldPropagate) {
                         //propagate inference context outwards and exit
-                        inferenceContext.dupTo(resultInfo.checkContext.inferenceContext(),
+                        minContext.dupTo(resultInfo.checkContext.inferenceContext(),
                                 resultInfo.checkContext.deferredAttrContext().insideOverloadPhase());
-                        return res;
+                        return newRestype;
                     }
                 }
                 inferenceContext.solve(noWarnings);
@@ -589,6 +616,18 @@
             }
         }
 
+    TypeMapping<Void> fromTypeVarFun = new TypeMapping<Void>() {
+        @Override
+        public Type visitTypeVar(TypeVar tv, Void aVoid) {
+            return new UndetVar(tv, types);
+        }
+
+        @Override
+        public Type visitCapturedType(CapturedType t, Void aVoid) {
+            return new CapturedUndetVar(t, types);
+        }
+    };
+
     /**
       * This method is used to infer a suitable target SAM in case the original
       * SAM type contains one or more wildcards. An inference process is applied
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java	Thu Nov 12 14:13:17 2015 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java	Fri Nov 13 12:29:23 2015 +0000
@@ -25,39 +25,35 @@
 
 package com.sun.tools.javac.comp;
 
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-import com.sun.tools.javac.code.Symtab;
 import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.code.Type.CapturedType;
-import com.sun.tools.javac.code.Type.CapturedUndetVar;
-import com.sun.tools.javac.code.Type.TypeMapping;
+import com.sun.tools.javac.code.Type.ArrayType;
+import com.sun.tools.javac.code.Type.ClassType;
 import com.sun.tools.javac.code.Type.TypeVar;
 import com.sun.tools.javac.code.Type.UndetVar;
 import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
+import com.sun.tools.javac.code.Type.WildcardType;
 import com.sun.tools.javac.code.Types;
-import com.sun.tools.javac.comp.Infer.BestLeafSolver;
 import com.sun.tools.javac.comp.Infer.FreeTypeListener;
 import com.sun.tools.javac.comp.Infer.GraphSolver;
 import com.sun.tools.javac.comp.Infer.GraphStrategy;
 import com.sun.tools.javac.comp.Infer.InferenceException;
 import com.sun.tools.javac.comp.Infer.InferenceStep;
-import com.sun.tools.javac.comp.Infer.LeafSolver;
 import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.TreeMaker;
 import com.sun.tools.javac.util.Assert;
-import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.Filter;
-import com.sun.tools.javac.util.JCDiagnostic;
-import com.sun.tools.javac.util.JCDiagnostic.Factory;
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.util.ListBuffer;
-import com.sun.tools.javac.util.Log;
 import com.sun.tools.javac.util.Warner;
 
+import static com.sun.tools.javac.code.TypeTag.UNDETVAR;
+
 /**
  * An inference context keeps track of the set of variables that are free
  * in the current context. It provides utility methods for opening/closing
@@ -76,43 +72,34 @@
     /** list of inference vars as undet vars */
     List<Type> undetvars;
 
+    Type update(Type t) {
+        return t;
+    }
+
     /** list of inference vars in this context */
     List<Type> inferencevars;
 
     Map<FreeTypeListener, List<Type>> freeTypeListeners = new HashMap<>();
 
-    List<FreeTypeListener> freetypeListeners = List.nil();
-
     Types types;
     Infer infer;
 
     public InferenceContext(Infer infer, List<Type> inferencevars) {
+        this(infer, inferencevars, inferencevars.map(infer.fromTypeVarFun));
+    }
+
+    public InferenceContext(Infer infer, List<Type> inferencevars, List<Type> undetvars) {
         this.inferencevars = inferencevars;
-
+        this.undetvars = undetvars;
         this.infer = infer;
         this.types = infer.types;
-
-        fromTypeVarFun = new TypeMapping<Void>() {
-            @Override
-            public Type visitTypeVar(TypeVar tv, Void aVoid) {
-                return new UndetVar(tv, types);
-            }
-
-            @Override
-            public Type visitCapturedType(CapturedType t, Void aVoid) {
-                return new CapturedUndetVar(t, types);
-            }
-        };
-        this.undetvars = inferencevars.map(fromTypeVarFun);
     }
 
-    TypeMapping<Void> fromTypeVarFun;
-
     /**
      * add a new inference var to this inference context
      */
     void addVar(TypeVar t) {
-        this.undetvars = this.undetvars.prepend(fromTypeVarFun.apply(t));
+        this.undetvars = this.undetvars.prepend(infer.fromTypeVarFun.apply(t));
         this.inferencevars = this.inferencevars.prepend(t);
     }
 
@@ -366,6 +353,124 @@
         }
     }
 
+    InferenceContext min(List<Type> roots, boolean shouldSolve, Warner warn) {
+        ReachabilityVisitor rv = new ReachabilityVisitor();
+        rv.scan(roots);
+        if (rv.min.size() == inferencevars.length()) {
+            return this;
+        }
+
+        List<Type> minVars = List.from(rv.min);
+        List<Type> redundantVars = inferencevars.diff(minVars);
+
+        //compute new undet variables (bounds associated to redundant variables are dropped)
+        ListBuffer<Type> minUndetVars = new ListBuffer<>();
+        for (Type minVar : minVars) {
+            UndetVar uv = (UndetVar)asUndetVar(minVar);
+            UndetVar uv2 = new UndetVar((TypeVar)minVar, types);
+            for (InferenceBound ib : InferenceBound.values()) {
+                List<Type> newBounds = uv.getBounds(ib).stream()
+                        .filter(b -> !redundantVars.contains(b))
+                        .collect(List.collector());
+                uv2.setBounds(ib, newBounds);
+            }
+            minUndetVars.add(uv2);
+        }
+
+        //compute new minimal inference context
+        InferenceContext minContext = new InferenceContext(infer, minVars, minUndetVars.toList());
+        for (Type t : minContext.inferencevars) {
+            //add listener that forwards notifications to original context
+            minContext.addFreeTypeListener(List.of(t), (inferenceContext) -> {
+                    List<Type> depVars = List.from(rv.minMap.get(t));
+                    solve(depVars, warn);
+                    notifyChange();
+            });
+        }
+        if (shouldSolve) {
+            //solve definitively unreachable variables
+            List<Type> unreachableVars = redundantVars.diff(List.from(rv.equiv));
+            solve(unreachableVars, warn);
+        }
+        return minContext;
+    }
+
+    class ReachabilityVisitor extends Types.UnaryVisitor<Void> {
+
+        Set<Type> equiv = new HashSet<>();
+        Set<Type> min = new HashSet<>();
+        Map<Type, Set<Type>> minMap = new HashMap<>();
+
+        void scan(List<Type> roots) {
+            roots.stream().forEach(this::visit);
+        }
+
+        @Override
+        public Void visitType(Type t, Void _unused) {
+            return null;
+        }
+
+        @Override
+        public Void visitUndetVar(UndetVar t, Void _unused) {
+            if (min.add(t.qtype)) {
+                Set<Type> deps = minMap.getOrDefault(t.qtype, new HashSet<>(Collections.singleton(t.qtype)));
+                for (Type b : t.getBounds(InferenceBound.values())) {
+                    Type undet = asUndetVar(b);
+                    if (!undet.hasTag(UNDETVAR)) {
+                        visit(undet);
+                    } else if (isEquiv((UndetVar)undet, b)){
+                        deps.add(b);
+                        equiv.add(b);
+                    } else {
+                        visit(undet);
+                    }
+                }
+                minMap.put(t.qtype, deps);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitWildcardType(WildcardType t, Void _unused) {
+            return visit(t.type);
+        }
+
+        @Override
+        public Void visitTypeVar(TypeVar t, Void aVoid) {
+            Type undet = asUndetVar(t);
+            if (undet.hasTag(UNDETVAR)) {
+                visitUndetVar((UndetVar)undet, null);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitArrayType(ArrayType t, Void _unused) {
+            return visit(t.elemtype);
+        }
+
+        @Override
+        public Void visitClassType(ClassType t, Void _unused) {
+            visit(t.getEnclosingType());
+            for (Type targ : t.getTypeArguments()) {
+                visit(targ);
+            }
+            return null;
+        }
+
+        boolean isEquiv(UndetVar from, Type t) {
+            UndetVar uv = (UndetVar)asUndetVar(t);
+            for (InferenceBound ib : InferenceBound.values()) {
+                List<Type> b1 = uv.getBounds(ib);
+                List<Type> b2 = from.getBounds(ib);
+                if (!b1.containsAll(b2) || !b2.containsAll(b1)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
     private void solve(GraphStrategy ss, Warner warn) {
         solve(ss, new HashMap<Type, Set<Type>>(), warn);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/speculative/T8046685.java	Fri Nov 13 12:29:23 2015 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 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 8046685
+ * @summary Uncompilable large expressions involving generics.
+ * @compile T8046685.java
+ */
+class T8046685 {
+
+    interface Predicate<T, U> {
+        public boolean apply(T t, U u);
+        public boolean equals(Object o);
+    }
+
+    static <X1, X2> Predicate<X1, X2> and(final Predicate<? super X1, ? super X2> first, final Predicate<? super X1, ? super X2> second) {
+        return null;
+    }
+
+    public static void test(Predicate<Integer, Integer> even) {
+        and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even,
+                and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, and(even, even)
+                ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
+                )))))))))))))))))))))))))))))))))));
+    }
+}