You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/09/13 21:09:56 UTC

[groovy] branch GROOVY_2_5_X updated (fff9e4b5bb -> 934fe10009)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


 discard fff9e4b5bb GROOVY-10229, GROOVY-10230, GROOVY-10330, GROOVY-10358: LUB: generics
     new 934fe10009 GROOVY-10229, GROOVY-10230, GROOVY-10330, GROOVY-10358: LUB: generics

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (fff9e4b5bb)
            \
             N -- N -- N   refs/heads/GROOVY_2_5_X (934fe10009)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../groovy/ast/tools/WideningCategories.java       | 15 ++++++-------
 .../transform/stc/StaticTypeCheckingSupport.java   | 16 ++++++++------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 25 +++++++++-------------
 .../groovy/ast/tools/WideningCategoriesTest.groovy |  9 +++++++-
 4 files changed, 34 insertions(+), 31 deletions(-)


[groovy] 01/01: GROOVY-10229, GROOVY-10230, GROOVY-10330, GROOVY-10358: LUB: generics

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 934fe100091bc2aab6302bb98133d74fe24b4e55
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Sep 13 12:04:49 2022 -0500

    GROOVY-10229, GROOVY-10230, GROOVY-10330, GROOVY-10358: LUB: generics
    
    2_5_X backport
---
 .../groovy/ast/tools/WideningCategories.java       | 527 ++++++++++-----------
 .../transform/stc/StaticTypeCheckingSupport.java   |  16 +-
 src/spec/test/typing/TypeCheckingTest.groovy       |   3 +-
 src/test/gls/generics/GenericsBytecodeTest.groovy  |  28 +-
 .../groovy/transform/stc/ClosuresSTCTest.groovy    |   2 +-
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  27 +-
 .../transform/stc/TernaryOperatorSTCTest.groovy    |  80 +++-
 .../groovy/ast/tools/WideningCategoriesTest.groovy |  77 +--
 8 files changed, 416 insertions(+), 344 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
index a4ea1aef6a..5c11d0637f 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
@@ -18,7 +18,6 @@
  */
 package org.codehaus.groovy.ast.tools;
 
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.GenericsType;
 import org.codehaus.groovy.ast.MethodNode;
@@ -45,12 +44,14 @@ import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.getNextSuperClass;
 import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
 import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.isNumberType;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
 import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
 import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 
 /**
@@ -70,44 +71,24 @@ import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
  */
 public class WideningCategories {
 
-    private static final List<ClassNode> EMPTY_CLASSNODE_LIST = Collections.emptyList();
-
     private static final Map<ClassNode, Integer> NUMBER_TYPES_PRECEDENCE = Collections.unmodifiableMap(new HashMap<ClassNode, Integer>() {
         private static final long serialVersionUID = -5178744121420941913L;
 
         {
-        put(ClassHelper.double_TYPE, 0);
-        put(ClassHelper.float_TYPE, 1);
-        put(ClassHelper.long_TYPE, 2);
-        put(ClassHelper.int_TYPE, 3);
-        put(ClassHelper.short_TYPE, 4);
-        put(ClassHelper.byte_TYPE, 5);
-    }});
-
-    /**
-     * A comparator which is used in case we generate a virtual lower upper bound class node. In that case,
-     * since a concrete implementation should be used at compile time, we must ensure that interfaces are
-     * always sorted. It is not important what sort is used, as long as the result is constant.
-     */
-    private static final Comparator<ClassNode> INTERFACE_CLASSNODE_COMPARATOR = new Comparator<ClassNode>() {
-        public int compare(final ClassNode o1, final ClassNode o2) {
-            int interfaceCountForO1 = o1.getInterfaces().length;
-            int interfaceCountForO2 = o2.getInterfaces().length;
-            if (interfaceCountForO1 > interfaceCountForO2) return -1;
-            if (interfaceCountForO1 < interfaceCountForO2) return 1;
-            int methodCountForO1 = o1.getMethods().size();
-            int methodCountForO2 = o2.getMethods().size();
-            if (methodCountForO1 > methodCountForO2) return -1;
-            if (methodCountForO1 < methodCountForO2) return 1;
-            return o1.getName().compareTo(o2.getName());
+            put(double_TYPE, 0);
+            put(float_TYPE,  1);
+            put(long_TYPE,   2);
+            put(int_TYPE,    3);
+            put(short_TYPE,  4);
+            put(byte_TYPE,   5);
         }
-    };
+    });
 
     /**
      * Used to check if a type is an int or Integer.
      * @param type the type to check
      */
-    public static boolean isInt(ClassNode type) {
+    public static boolean isInt(final ClassNode type) {
         return int_TYPE == type;
     }
 
@@ -115,7 +96,7 @@ public class WideningCategories {
      * Used to check if a type is an double or Double.
      * @param type the type to check
      */
-    public static boolean isDouble(ClassNode type) {
+    public static boolean isDouble(final ClassNode type) {
         return double_TYPE == type;
     }
 
@@ -123,7 +104,7 @@ public class WideningCategories {
      * Used to check if a type is a float or Float.
      * @param type the type to check
      */
-    public static boolean isFloat(ClassNode type) {
+    public static boolean isFloat(final ClassNode type) {
         return float_TYPE == type;
     }
 
@@ -131,49 +112,51 @@ public class WideningCategories {
      * It is of an int category, if the provided type is a
      * byte, char, short, int.
      */
-    public static boolean isIntCategory(ClassNode type) {
-        return  type==byte_TYPE     ||  type==char_TYPE     ||
-                type==int_TYPE      ||  type==short_TYPE;
+    public static boolean isIntCategory(final ClassNode type) {
+        return type == byte_TYPE || type == char_TYPE || type == int_TYPE || type == short_TYPE;
     }
+
     /**
      * It is of a long category, if the provided type is a
-     * long, its wrapper or if it is a long category. 
+     * long, its wrapper or if it is a long category.
      */
-    public static boolean isLongCategory(ClassNode type) {
-        return  type==long_TYPE     ||  isIntCategory(type);
+    public static boolean isLongCategory(final ClassNode type) {
+        return type == long_TYPE || isIntCategory(type);
     }
+
     /**
      * It is of a BigInteger category, if the provided type is a
-     * long category or a BigInteger. 
+     * long category or a BigInteger.
      */
-    public static boolean isBigIntCategory(ClassNode type) {
-        return  type==BigInteger_TYPE || isLongCategory(type);
+    public static boolean isBigIntCategory(final ClassNode type) {
+        return type == BigInteger_TYPE || isLongCategory(type);
     }
+
     /**
      * It is of a BigDecimal category, if the provided type is a
-     * BigInteger category or a BigDecimal. 
+     * BigInteger category or a BigDecimal.
      */
-    public static boolean isBigDecCategory(ClassNode type) {
-        return  type==BigDecimal_TYPE || isBigIntCategory(type);
+    public static boolean isBigDecCategory(final ClassNode type) {
+        return type == BigDecimal_TYPE || isBigIntCategory(type);
     }
+
     /**
      * It is of a double category, if the provided type is a
      * BigDecimal, a float, double. C(type)=double
      */
-    public static boolean isDoubleCategory(ClassNode type) {
-        return  type==float_TYPE    ||  type==double_TYPE   ||
-                isBigDecCategory(type);
+    public static boolean isDoubleCategory(final ClassNode type) {
+        return type == float_TYPE || type == double_TYPE || isBigDecCategory(type);
     }
 
     /**
      * It is of a floating category, if the provided type is a
      * a float, double. C(type)=float
      */
-    public static boolean isFloatingCategory(ClassNode type) {
-        return  type==float_TYPE    ||  type==double_TYPE;
+    public static boolean isFloatingCategory(final ClassNode type) {
+        return type == float_TYPE || type == double_TYPE;
     }
 
-    public static boolean isNumberCategory(ClassNode type) {
+    public static boolean isNumberCategory(final ClassNode type) {
         return isBigDecCategory(type) || type.isDerivedFrom(Number_TYPE);
     }
 
@@ -184,9 +167,11 @@ public class WideningCategories {
      * @param nodes the list of nodes for which to find the first common super type.
      * @return first common supertype
      */
-    public static ClassNode lowestUpperBound(List<ClassNode> nodes) {
-        if (nodes.size()==1) return nodes.get(0);
-        return lowestUpperBound(nodes.get(0), lowestUpperBound(nodes.subList(1, nodes.size())));
+    public static ClassNode lowestUpperBound(final List<ClassNode> nodes) {
+        int n = nodes.size();
+        if (n == 1) return nodes.get(0);
+        if (n == 2) return lowestUpperBound(nodes.get(0), nodes.get(1));
+        return lowestUpperBound(nodes.get(0), lowestUpperBound(nodes.subList(1, n)));
     }
 
     /**
@@ -206,36 +191,29 @@ public class WideningCategories {
      * @param b second class node
      * @return first common supertype
      */
-    public static ClassNode lowestUpperBound(ClassNode a, ClassNode b) {
+    public static ClassNode lowestUpperBound(final ClassNode a, final ClassNode b) {
         ClassNode lub = lowestUpperBound(a, b, null, null);
-        if (lub==null || !lub.isUsingGenerics()) return lub;
-        // types may be parameterized. If so, we must ensure that generic type arguments
+        if (lub == null || !lub.isUsingGenerics()
+                || lub.isGenericsPlaceHolder()) { // GROOVY-10330
+            return lub;
+        }
+        // types may be parameterized; if so, ensure that generic type arguments
         // are made compatible
-
         if (lub instanceof LowestUpperBoundClassNode) {
-            // no parent super class representing both types could be found
-            // or both class nodes implement common interfaces which may have
-            // been parameterized differently.
-            // We must create a classnode for which the "superclass" is potentially parameterized
-            // plus the interfaces
-            ClassNode superClass = lub.getSuperClass();
-            ClassNode psc = superClass.isUsingGenerics()?parameterizeLowestUpperBound(superClass, a, b, lub):superClass;
-
-            ClassNode[] interfaces = lub.getInterfaces();
-            ClassNode[] pinterfaces = new ClassNode[interfaces.length];
-            for (int i = 0, interfacesLength = interfaces.length; i < interfacesLength; i++) {
-                final ClassNode icn = interfaces[i];
-                if (icn.isUsingGenerics()) {
-                    pinterfaces[i] = parameterizeLowestUpperBound(icn, a, b, lub);
-                } else {
-                    pinterfaces[i] = icn;
+            ClassNode superClass = lub.getUnresolvedSuperClass();
+            if (superClass.redirect().getGenericsTypes() != null) {
+                superClass = parameterizeLowestUpperBound(superClass, a, b, lub);
+            }
+            ClassNode[] interfaces = lub.getInterfaces().clone();
+            for (int i = 0, n = interfaces.length; i < n; i += 1) {
+                ClassNode icn = interfaces[i];
+                if (icn.redirect().getGenericsTypes() != null) {
+                    interfaces[i] = parameterizeLowestUpperBound(icn, a, b, lub);
                 }
             }
-
-            return new LowestUpperBoundClassNode(((LowestUpperBoundClassNode)lub).name, psc, pinterfaces);
+            return new LowestUpperBoundClassNode(((LowestUpperBoundClassNode)lub).name, superClass, interfaces);
         } else {
             return parameterizeLowestUpperBound(lub, a, b, lub);
-
         }
     }
 
@@ -258,72 +236,63 @@ public class WideningCategories {
         // it according to the types provided by the two class nodes
         ClassNode holderForA = findGenericsTypeHolderForClass(a, lub);
         ClassNode holderForB = findGenericsTypeHolderForClass(b, lub);
-        // let's compare their generics type
+        // let's compare their generics
         GenericsType[] agt = holderForA == null ? null : holderForA.getGenericsTypes();
         GenericsType[] bgt = holderForB == null ? null : holderForB.getGenericsTypes();
-        if (agt==null || bgt==null || agt.length!=bgt.length) {
+        if (agt == null || bgt == null || agt.length != bgt.length
+                || Arrays.toString(agt).equals(Arrays.toString(bgt))) {
             return lub;
         }
-        GenericsType[] lubgt = new GenericsType[agt.length];
-        for (int i = 0; i < agt.length; i++) {
-            ClassNode t1 = agt[i].getType();
-            ClassNode t2 = bgt[i].getType();
+        int n = agt.length; GenericsType[] lubGTs = new GenericsType[n];
+        for (int i = 0; i < n; i += 1) {
+            ClassNode t1 = upperBound(agt[i]);
+            ClassNode t2 = upperBound(bgt[i]);
             ClassNode basicType;
             if (areEqualWithGenerics(t1, isPrimitiveType(a)?getWrapper(a):a) && areEqualWithGenerics(t2, isPrimitiveType(b)?getWrapper(b):b)) {
-                // we are facing a self referencing type !
-                basicType = fallback;
+                // "String implements Comparable<String>" and "StringBuffer implements Comparable<StringBuffer>"
+                basicType = fallback; // do not loop
             } else {
-                 basicType = lowestUpperBound(t1, t2);
+                basicType = lowestUpperBound(t1, t2);
             }
-            if (t1.equals(t2)) {
-                lubgt[i] = new GenericsType(basicType);
+            if (agt[i].isWildcard() || bgt[i].isWildcard() || !t1.equals(t2)) {
+                lubGTs[i] = GenericsUtils.buildWildcardType(basicType);
             } else {
-                lubgt[i] = GenericsUtils.buildWildcardType(basicType);
+                lubGTs[i] = basicType.asGenericsType();
             }
         }
-        ClassNode plain = lub.getPlainNodeReference();
-        plain.setGenericsTypes(lubgt);
-        return plain;
+        return GenericsUtils.makeClassSafe0(lub, lubGTs);
     }
 
-    private static ClassNode findGenericsTypeHolderForClass(ClassNode source, ClassNode type) {
+    private static ClassNode findGenericsTypeHolderForClass(ClassNode source, final ClassNode target) {
         if (isPrimitiveType(source)) source = getWrapper(source);
-        if (source.equals(type)) return source;
-        if (type.isInterface()) {
-            for (ClassNode interfaceNode : source.getAllInterfaces()) {
-                if (interfaceNode.equals(type)) {
-                    ClassNode parameterizedInterface = GenericsUtils.parameterizeType(source, interfaceNode);
-                    return parameterizedInterface;
+        if (source.equals(target)) {
+            return source;
+        }
+        if (target.isInterface() ? source.implementsInterface(target) : source.isDerivedFrom(target)) {
+            ClassNode sc;
+            do {
+                sc = getNextSuperClass(source, target);
+                if (GenericsUtils.hasUnresolvedGenerics(sc)) {
+                    sc = GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(source), sc);
                 }
-            }
-        }
-        ClassNode superClass = source.getUnresolvedSuperClass();
-        // copy generic type information if available
-        if (superClass!=null && superClass.isUsingGenerics()) {
-            Map<GenericsType.GenericsTypeName, GenericsType> genericsTypeMap = GenericsUtils.extractPlaceholders(source);
-            GenericsType[] genericsTypes = superClass.getGenericsTypes();
-            if (genericsTypes!=null) {
-                GenericsType[] copyTypes = new GenericsType[genericsTypes.length];
-                for (int i = 0; i < genericsTypes.length; i++) {
-                    GenericsType genericsType = genericsTypes[i];
-                    GenericsType.GenericsTypeName gtn = new GenericsType.GenericsTypeName(genericsType.getName());
-                    if (genericsType.isPlaceholder() && genericsTypeMap.containsKey(gtn)) {
-                        copyTypes[i] = genericsTypeMap.get(gtn);
-                    } else {
-                        copyTypes[i] = genericsType;
-                    }
-                }
-                superClass = superClass.getPlainNodeReference();
-                superClass.setGenericsTypes(copyTypes);
-            }
+            } while (!(source = sc).equals(target));
+
+            return sc;
         }
-        if (superClass!=null) return findGenericsTypeHolderForClass(superClass, type);
         return null;
     }
 
-    private static ClassNode lowestUpperBound(ClassNode a, ClassNode b, List<ClassNode> interfacesImplementedByA, List<ClassNode> interfacesImplementedByB) {
+    private static ClassNode upperBound(final GenericsType gt) {
+        if (gt.isWildcard()) {
+            ClassNode[] ub = gt.getUpperBounds();
+            if (ub != null) return ub[0];
+        }
+        return gt.getType();
+    }
+
+    private static ClassNode lowestUpperBound(final ClassNode a, final ClassNode b, Set<ClassNode> interfacesImplementedByA, Set<ClassNode> interfacesImplementedByB) {
         // first test special cases
-        if (a==null || b==null) {
+        if (a == null || b == null) {
             // this is a corner case, you should not
             // compare two class nodes if one of them is null
             return null;
@@ -335,7 +304,7 @@ public class WideningCategories {
             // one of the objects is at the top of the hierarchy
             GenericsType[] gta = a.getGenericsTypes();
             GenericsType[] gtb = b.getGenericsTypes();
-            if (gta !=null && gtb !=null && gta.length==1 && gtb.length==1) {
+            if (gta != null && gtb != null && gta.length == 1 && gtb.length == 1) {
                 if (gta[0].getName().equals(gtb[0].getName())) {
                     return a;
                 }
@@ -362,20 +331,18 @@ public class WideningCategories {
         if (isPrimitiveA && isPrimitiveB) {
             Integer pa = NUMBER_TYPES_PRECEDENCE.get(a);
             Integer pb = NUMBER_TYPES_PRECEDENCE.get(b);
-            if (pa!=null && pb!=null) {
-                if (pa<=pb) return a;
-                return b;
+            if (pa != null && pb != null) {
+                return (pa <= pb ? a : b);
             }
-            return a.equals(b)?a:lowestUpperBound(getWrapper(a), getWrapper(b), null, null);
+            return a.equals(b) ? a : lowestUpperBound(getWrapper(a), getWrapper(b), null, null);
         }
         if (isNumberType(a.redirect()) && isNumberType(b.redirect())) {
             ClassNode ua = getUnwrapper(a);
             ClassNode ub = getUnwrapper(b);
             Integer pa = NUMBER_TYPES_PRECEDENCE.get(ua);
             Integer pb = NUMBER_TYPES_PRECEDENCE.get(ub);
-            if (pa!=null && pb!=null) {
-                if (pa<=pb) return a;
-                return b;
+            if (pa != null && pb != null) {
+                return (pa <= pb ? a : b);
             }
         }
 
@@ -390,19 +357,16 @@ public class WideningCategories {
             if (a.implementsInterface(b)) {
                 return b;
             }
-            // each interface may have one or more "extends", so we must find those
-            // which are common
-            ClassNode[] interfacesFromA = a.getInterfaces();
-            ClassNode[] interfacesFromB = b.getInterfaces();
-            Set<ClassNode> common = new HashSet<ClassNode>();
-            Collections.addAll(common, interfacesFromA);
-            Set<ClassNode> fromB = new HashSet<ClassNode>();
-            Collections.addAll(fromB, interfacesFromB);
-            common.retainAll(fromB);
-
-            if (common.size()==1) {
+            if (interfacesImplementedByA == null)
+                interfacesImplementedByA = GeneralUtils.getInterfacesAndSuperInterfaces(a);
+            if (interfacesImplementedByB == null)
+                interfacesImplementedByB = GeneralUtils.getInterfacesAndSuperInterfaces(b);
+
+            // each interface may have one or more "extends", so we must find those which are common
+            Collection<ClassNode> common = keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB);
+            if (common.size() == 1) {
                 return common.iterator().next();
-            } else if (common.size()>1) {
+            } else if (common.size() > 1) {
                 return buildTypeWithInterfaces(a, b, common);
             }
 
@@ -426,63 +390,53 @@ public class WideningCategories {
                 // no interface in common
                 return OBJECT_TYPE;
             }
-            if (matchingInterfaces.size()==1) {
+            if (matchingInterfaces.size() == 1) {
                 // a single match, which should be returned
                 return matchingInterfaces.get(0);
             }
-            return buildTypeWithInterfaces(a,b, matchingInterfaces);
+            return buildTypeWithInterfaces(a, b, matchingInterfaces);
         }
         // both classes do not represent interfaces
         if (a.equals(b)) {
-            return buildTypeWithInterfaces(a,b, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
+            return buildTypeWithInterfaces(a, b, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
         }
         // test if one class inherits from the other
         if (a.isDerivedFrom(b) || b.isDerivedFrom(a)) {
-            return buildTypeWithInterfaces(a,b, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
+            return buildTypeWithInterfaces(a, b, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
         }
 
-        // Look at the super classes
         ClassNode sa = a.getUnresolvedSuperClass();
         ClassNode sb = b.getUnresolvedSuperClass();
 
-        // extract implemented interfaces before "going up"
-        Set<ClassNode> ifa = new HashSet<ClassNode>();
-        extractInterfaces(a, ifa);
-        Set<ClassNode> ifb = new HashSet<ClassNode>();
-        extractInterfaces(b, ifb);
-        interfacesImplementedByA = interfacesImplementedByA==null?new LinkedList<ClassNode>(ifa):interfacesImplementedByA;
-        interfacesImplementedByB = interfacesImplementedByB==null?new LinkedList<ClassNode>(ifb):interfacesImplementedByB;
+        if (interfacesImplementedByA == null)
+            interfacesImplementedByA = GeneralUtils.getInterfacesAndSuperInterfaces(a);
+        if (interfacesImplementedByB == null)
+            interfacesImplementedByB = GeneralUtils.getInterfacesAndSuperInterfaces(b);
 
-        // check if no superclass is defined
-        // meaning that we reached the top of the object hierarchy
-        if (sa==null || sb==null) return buildTypeWithInterfaces(OBJECT_TYPE, OBJECT_TYPE, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
+        // check if no superclass is defined, meaning that we reached the top of the object hierarchy
+        if (sa == null || sb == null) {
+            return buildTypeWithInterfaces(OBJECT_TYPE, OBJECT_TYPE, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
+        }
 
+        if (GenericsUtils.hasUnresolvedGenerics(sa))
+            sa = GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(a), sa);
+        if (GenericsUtils.hasUnresolvedGenerics(sb))
+            sb = GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(b), sb);
 
-        // if one superclass is derived (or equals) another
-        // then it is the common super type
+        // if one superclass is derived (or equals) another then it is the common super type
         if (sa.isDerivedFrom(sb) || sb.isDerivedFrom(sa)) {
             return buildTypeWithInterfaces(sa, sb, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
         }
-        // superclasses are on distinct hierarchy branches, so we
-        // recurse on them
+        // superclasses are on distinct hierarchy branches, so we recurse on them
         return lowestUpperBound(sa, sb, interfacesImplementedByA, interfacesImplementedByB);
     }
 
-    private static void extractInterfaces(ClassNode node, Set<ClassNode> interfaces) {
-        if (node==null) return;
-        Collections.addAll(interfaces, node.getInterfaces());
-        extractInterfaces(node.getSuperClass(), interfaces);
-    }
-    
     /**
-     * Given the list of interfaces implemented by two class nodes, returns the list of the most specific common
-     * implemented interfaces.
-     * @param fromA
-     * @param fromB
-     * @return the list of the most specific common implemented interfaces
+     * Given the interfaces implemented by two types, returns a list of the most
+     * specific common implemented interfaces.
      */
-    private static List<ClassNode> keepLowestCommonInterfaces(List<ClassNode> fromA, List<ClassNode> fromB) {
-        if (fromA==null||fromB==null) return EMPTY_CLASSNODE_LIST;
+    private static List<ClassNode> keepLowestCommonInterfaces(final Set<ClassNode> fromA, final Set<ClassNode> fromB) {
+        if (fromA == null || fromB == null) return Collections.EMPTY_LIST;
         Set<ClassNode> common = new HashSet<ClassNode>(fromA);
         common.retainAll(fromB);
         List<ClassNode> result = new ArrayList<ClassNode>(common.size());
@@ -492,16 +446,15 @@ public class WideningCategories {
         return result;
     }
 
-    private static void addMostSpecificInterface(ClassNode interfaceNode, List<ClassNode> nodes) {
+    private static void addMostSpecificInterface(final ClassNode interfaceNode, final List<ClassNode> nodes) {
         if (nodes.isEmpty()) nodes.add(interfaceNode);
-        for (int i = 0, nodesSize = nodes.size(); i < nodesSize; i++) {
-            final ClassNode node = nodes.get(i);
-            if (node.equals(interfaceNode)||node.implementsInterface(interfaceNode)) {
+        for (int i = 0, n = nodes.size(); i < n; i += 1) { ClassNode node = nodes.get(i);
+            if (node.equals(interfaceNode) || node.implementsInterface(interfaceNode)) {
                 // a more specific interface exists in the list, keep it
                 return;
             }
             if (interfaceNode.implementsInterface(node)) {
-                // the interface beeing added is more specific than the one in the list, replace it
+                // the interface being added is more specific than the one in the list, replace it
                 nodes.set(i, interfaceNode);
                 return;
             }
@@ -517,7 +470,7 @@ public class WideningCategories {
             for (ClassNode interfaceNode : interfaces) {
                 if (type.implementsInterface(interfaceNode)) result.add(interfaceNode);
             }
-            if (result.isEmpty() && interfaces.length>0) {
+            if (result.isEmpty() && interfaces.length > 0) {
                 // none if the direct interfaces match, but we must check "upper" in the hierarchy
                 for (ClassNode interfaceNode : interfaces) {
                     extractMostSpecificImplementedInterfaces(type, interfaceNode, result);
@@ -534,52 +487,70 @@ public class WideningCategories {
      * @param interfaces interfaces both class nodes share, which their lowest common super class do not implement.
      * @return the class node representing the lowest upper bound
      */
-    private static ClassNode buildTypeWithInterfaces(ClassNode baseType1, ClassNode baseType2, Collection<ClassNode> interfaces) {
-        boolean noInterface = interfaces.isEmpty();
-        if (noInterface) {
-            if (baseType1.equals(baseType2)) return baseType1;
-            if (baseType1.isDerivedFrom(baseType2)) return baseType2;
+    private static ClassNode buildTypeWithInterfaces(final ClassNode baseType1, final ClassNode baseType2, final Collection<ClassNode> interfaces) {
+        if (interfaces.isEmpty()) {
             if (baseType2.isDerivedFrom(baseType1)) return baseType1;
+            if (baseType1.isDerivedFrom(baseType2)) return baseType2;
         }
-        if (OBJECT_TYPE.equals(baseType1) && OBJECT_TYPE.equals(baseType2) && interfaces.size()==1) {
-            if (interfaces instanceof List) {
-                return ((List<ClassNode>) interfaces).get(0);
-            }
+
+        if (baseType1.equals(OBJECT_TYPE) && baseType2.equals(OBJECT_TYPE) && interfaces.size() == 1) {
             return interfaces.iterator().next();
         }
-        LowestUpperBoundClassNode type;
-        ClassNode superClass;
-        String name;
+
+        String name; ClassNode superClass;
         if (baseType1.equals(baseType2)) {
-            if (OBJECT_TYPE.equals(baseType1)) {
-                superClass = baseType1;
+            superClass = baseType1;
+            if (baseType1.equals(OBJECT_TYPE)) {
                 name = "Virtual$Object";
             } else {
-                superClass = baseType1;
-                name = "Virtual$"+baseType1.getName();
+                name = "Virtual$" + baseType1.getName();
             }
         } else {
-            superClass = OBJECT_TYPE;
             if (baseType1.isDerivedFrom(baseType2)) {
                 superClass = baseType2;
             } else if (baseType2.isDerivedFrom(baseType1)) {
                 superClass = baseType1;
+            } else {
+                superClass = OBJECT_TYPE;
             }
-            name = "CommonAssignOf$"+baseType1.getName()+"$"+baseType2.getName();
+            name = "CommonAssignOf$" + baseType1.getName() + "$" + baseType2.getName();
         }
-        Iterator<ClassNode> itcn = interfaces.iterator();
-        while (itcn.hasNext()) {
-            ClassNode next = itcn.next();
-            if (superClass.isDerivedFrom(next) || superClass.implementsInterface(next)) {
-                itcn.remove();
+
+        for (Iterator<ClassNode> it = interfaces.iterator(); it.hasNext(); ) {
+            if (GeneralUtils.isOrImplements(superClass, it.next())) {
+                it.remove();
             }
         }
+
+        int nInterfaces = interfaces.size();
+        if (nInterfaces == 0) return superClass;
+        if (nInterfaces == 1 && superClass.equals(OBJECT_TYPE)) return interfaces.iterator().next();
+
         ClassNode[] interfaceArray = interfaces.toArray(ClassNode.EMPTY_ARRAY);
         Arrays.sort(interfaceArray, INTERFACE_CLASSNODE_COMPARATOR);
-        type = new LowestUpperBoundClassNode(name, superClass, interfaceArray);
-        return type;
+        return new LowestUpperBoundClassNode(name, superClass, interfaceArray);
     }
 
+    /**
+     * A comparator which is used in case we generate a virtual lower upper bound class node. In that case,
+     * since a concrete implementation should be used at compile time, we must ensure that interfaces are
+     * always sorted. It is not important what sort is used, as long as the result is constant.
+     */
+    private static final Comparator<ClassNode> INTERFACE_CLASSNODE_COMPARATOR = new Comparator<ClassNode>() {
+        @Override
+        public int compare(final ClassNode o1, final ClassNode o2) {
+            int interfaceCountForO1 = o1.getInterfaces().length;
+            int interfaceCountForO2 = o2.getInterfaces().length;
+            if (interfaceCountForO1 > interfaceCountForO2) return -1;
+            if (interfaceCountForO1 < interfaceCountForO2) return 1;
+            int methodCountForO1 = o1.getMethods().size();
+            int methodCountForO2 = o2.getMethods().size();
+            if (methodCountForO1 > methodCountForO2) return -1;
+            if (methodCountForO1 < methodCountForO2) return 1;
+            return o1.getName().compareTo(o2.getName());
+        }
+    };
+
     /**
      * This {@link ClassNode} specialization is used when the lowest upper bound of two types
      * cannot be represented by an existing type. For example, if B extends A,  C extends A
@@ -592,62 +563,68 @@ public class WideningCategories {
      *
      */
     public static class LowestUpperBoundClassNode extends ClassNode {
-        private static final Comparator<ClassNode> CLASS_NODE_COMPARATOR = new Comparator<ClassNode>() {
-            public int compare(final ClassNode o1, final ClassNode o2) {
-                String n1 = o1 instanceof LowestUpperBoundClassNode?((LowestUpperBoundClassNode)o1).name:o1.getName();
-                String n2 = o2 instanceof LowestUpperBoundClassNode?((LowestUpperBoundClassNode)o2).name:o2.getName();
-                return n1.compareTo(n2);
-            }
-        };
-        private final ClassNode compileTimeClassNode;
         private final String name;
         private final String text;
-
         private final ClassNode upper;
         private final ClassNode[] interfaces;
+        private final ClassNode compileTimeClassNode;
 
-        public LowestUpperBoundClassNode(String name, ClassNode upper, ClassNode... interfaces) {
-            super(name, ACC_PUBLIC|ACC_FINAL, upper, interfaces, null);
+        public LowestUpperBoundClassNode(final String name, final ClassNode upper, final ClassNode... interfaces) {
+            super(name, ACC_PUBLIC | ACC_FINAL, upper, interfaces, null);
+            this.name = name;
             this.upper = upper;
             this.interfaces = interfaces;
-            boolean usesGenerics;
-            Arrays.sort(interfaces, CLASS_NODE_COMPARATOR);
-            compileTimeClassNode = upper.equals(OBJECT_TYPE) && interfaces.length>0?interfaces[0]:upper;
-            this.name = name;
-            usesGenerics = upper.isUsingGenerics();
-            List<GenericsType[]> genericsTypesList = new LinkedList<GenericsType[]>();
+            Arrays.sort(interfaces, new Comparator<ClassNode>() {
+                @Override
+                public int compare(final ClassNode cn1, final ClassNode cn2) {
+                    String n1 = cn1 instanceof LowestUpperBoundClassNode ? ((LowestUpperBoundClassNode) cn1).name : cn1.getName();
+                    String n2 = cn2 instanceof LowestUpperBoundClassNode ? ((LowestUpperBoundClassNode) cn2).name : cn2.getName();
+                    return n1.compareTo(n2);
+                }
+            });
+            compileTimeClassNode = upper.equals(OBJECT_TYPE) && interfaces.length > 0 ? interfaces[0] : upper;
+
+            StringBuilder sb = new StringBuilder("(");
+            if (!upper.equals(OBJECT_TYPE)) sb.append(upper.getText());
+            for (ClassNode i : interfaces) {
+                if (sb.length() > 1) {
+                    sb.append(" or ");
+                }
+                sb.append(i.getText());
+            }
+            sb.append(")");
+            this.text = sb.toString();
+
+            boolean usesGenerics = upper.isUsingGenerics();
+            List<GenericsType[]> genericsTypesList = new ArrayList<GenericsType[]>();
             genericsTypesList.add(upper.getGenericsTypes());
-			for (ClassNode anInterface : interfaces) {
+            for (ClassNode anInterface : interfaces) {
                 usesGenerics |= anInterface.isUsingGenerics();
                 genericsTypesList.add(anInterface.getGenericsTypes());
-				for (MethodNode methodNode : anInterface.getMethods()) {
+                for (MethodNode methodNode : anInterface.getMethods()) {
                     MethodNode method = addMethod(methodNode.getName(), methodNode.getModifiers(), methodNode.getReturnType(), methodNode.getParameters(), methodNode.getExceptions(), methodNode.getCode());
                     method.setDeclaringClass(anInterface); // important for static compilation!
                 }
-			}
+            }
             setUsingGenerics(usesGenerics);
             if (usesGenerics) {
-                List<GenericsType> asArrayList = new ArrayList<GenericsType>();
-                for (GenericsType[] genericsTypes : genericsTypesList) {
-                    if (genericsTypes!=null) {
-                        Collections.addAll(asArrayList, genericsTypes);
+                List<GenericsType> flatList = new ArrayList<GenericsType>();
+                for (GenericsType[] gts : genericsTypesList) {
+                    if (gts != null) {
+                        Collections.addAll(flatList, gts);
                     }
                 }
-                setGenericsTypes(asArrayList.toArray(GenericsType.EMPTY_ARRAY));
-            }
-            StringBuilder sb = new StringBuilder();
-            if (!upper.equals(OBJECT_TYPE)) sb.append(upper.getName());
-            for (ClassNode anInterface : interfaces) {
-                if (sb.length()>0) {
-                    sb.append(" or ");
-                }
-                sb.append(anInterface.getName());
+                setGenericsTypes(flatList.toArray(GenericsType.EMPTY_ARRAY));
             }
-            this.text = sb.toString();
         }
 
         public String getLubName() {
-            return this.name;
+            return name;
+        }
+
+        @Override
+        public String getText() {
+            return text;
         }
 
         @Override
@@ -662,15 +639,7 @@ public class WideningCategories {
 
         @Override
         public int hashCode() {
-            int result = super.hashCode();
-//            result = 31 * result + (compileTimeClassNode != null ? compileTimeClassNode.hashCode() : 0);
-            result = 31 * result + (name != null ? name.hashCode() : 0);
-            return result;
-        }
-
-        @Override
-        public String getText() {
-            return text;
+            return (31 * super.hashCode()) + (name != null ? name.hashCode() : 0);
         }
 
         @Override
@@ -682,21 +651,18 @@ public class WideningCategories {
                 ubs = new ClassNode[interfaces.length + 1]; ubs[0] = upper;
                 System.arraycopy(interfaces, 0, ubs, 1, interfaces.length);
             }
-            GenericsType gt = new GenericsType(ClassHelper.makeWithoutCaching("?"), ubs, null);
+            GenericsType gt = new GenericsType(makeWithoutCaching("?"), ubs, null);
             gt.setWildcard(true);
             return gt;
         }
 
         @Override
         public ClassNode getPlainNodeReference() {
-            ClassNode[] intf = interfaces==null?null:new ClassNode[interfaces.length];
-            if (intf!=null) {
-                for (int i = 0; i < interfaces.length; i++) {
-                    intf[i] = interfaces[i].getPlainNodeReference();
-                }
+            ClassNode[] faces = interfaces.clone();
+            for (int i = 0; i < interfaces.length; i += 1) {
+                faces[i] = interfaces[i].getPlainNodeReference();
             }
-            LowestUpperBoundClassNode plain = new LowestUpperBoundClassNode(name, upper.getPlainNodeReference(), intf);
-            return plain;
+            return new LowestUpperBoundClassNode(name, upper.getPlainNodeReference(), faces);
         }
     }
 
@@ -706,39 +672,42 @@ public class WideningCategories {
      * @param b
      * @return true if the class nodes are equal, false otherwise
      */
-    private static boolean areEqualWithGenerics(ClassNode a, ClassNode b) {
+    private static boolean areEqualWithGenerics(final ClassNode a, final ClassNode b) {
         if (a==null) return b==null;
         if (!a.equals(b)) return false;
         if (a.isUsingGenerics() && !b.isUsingGenerics()) return false;
         GenericsType[] gta = a.getGenericsTypes();
         GenericsType[] gtb = b.getGenericsTypes();
-        if (gta==null && gtb!=null) return false;
-        if (gtb==null && gta!=null) return false;
-        if (gta!=null && gtb!=null) {
-            if (gta.length!=gtb.length) return false;
-            for (int i = 0; i < gta.length; i++) {
-                GenericsType ga = gta[i];
-                GenericsType gb = gtb[i];
-                boolean result = ga.isPlaceholder()==gb.isPlaceholder() && ga.isWildcard()==gb.isWildcard();
-                result = result && ga.getName().equals(gb.getName());
-                result = result && areEqualWithGenerics(ga.getType(), gb.getType());
-                result = result && areEqualWithGenerics(ga.getLowerBound(), gb.getLowerBound());
-                if (result) {
-                    ClassNode[] upA = ga.getUpperBounds();
-                    if (upA!=null) {
-                        ClassNode[] upB = gb.getUpperBounds();
-                        if (upB==null || upB.length!=upA.length) return false;
-                        for (int j = 0; j < upA.length; j++) {
-                            if (!areEqualWithGenerics(upA[j],upB[j])) return false;
-                        }
-                    }
+        if (gta == null && gtb != null) return false;
+        if (gtb == null && gta != null) return false;
+        if (gta != null && gtb != null) {
+            if (gta.length != gtb.length) return false;
+            for (int i = 0, n = gta.length; i < n; i += 1) {
+                GenericsType gta_i = gta[i];
+                GenericsType gtb_i = gtb[i];
+                ClassNode[] upperA = gta_i.getUpperBounds();
+                ClassNode[] upperB = gtb_i.getUpperBounds();
+                if (gta_i.isPlaceholder() != gtb_i.isPlaceholder()
+                        || gta_i.isWildcard() != gtb_i.isWildcard()
+                        || !gta_i.getName().equals(gtb_i.getName())
+                        || !areEqualWithGenerics(gta_i.getType(), gtb_i.getType())
+                        || !areEqualWithGenerics(gta_i.getLowerBound(), gtb_i.getLowerBound())
+                        || (upperA == null ? upperB != null : !areEqualWithGenerics(upperA, upperB))) {
+                    return false;
                 }
-                if (!result) return false;
             }
         }
         return true;
     }
-    
+
+    private static boolean areEqualWithGenerics(final ClassNode[] upperA, final ClassNode[] upperB) {
+        int n; if ((n = upperA.length) != upperB.length) return false;
+        for (int i = 0; i < n; i += 1) {
+            if (!areEqualWithGenerics(upperA[i], upperB[i])) return false;
+        }
+        return true;
+    }
+
     /**
      * Determines if the source class implements an interface or subclasses the target type.
      * This method takes the {@link org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode lowest
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 45ecca7e51..0d8ebbdf86 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -109,7 +109,6 @@ import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
 import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
-import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL;
@@ -672,6 +671,11 @@ public abstract class StaticTypeCheckingSupport {
         return checkCompatibleAssignmentTypes(left, right, rightExpression, true);
     }
 
+    /**
+     * Everything that can be done by {@code castToType} should be allowed for assignment.
+     *
+     * @see org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation#castToType(Object,Class)
+     */
     public static boolean checkCompatibleAssignmentTypes(ClassNode left, ClassNode right, Expression rightExpression, boolean allowConstructorCoercion) {
         boolean rightExpressionIsNull = isNullConstant(rightExpression);
         if (rightExpressionIsNull && !isPrimitiveType(left)) {
@@ -864,7 +868,7 @@ public abstract class StaticTypeCheckingSupport {
                     return !Double.valueOf(val).equals(number);
                 }
                 default: // double
-                    return false; // no possible loose here
+                    return false; // no possible loss here
             }
         }
         return true; // possible loss of precision
@@ -930,7 +934,7 @@ public abstract class StaticTypeCheckingSupport {
         if (type.isArray() && superOrInterface.isArray()) {
             return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType());
         }
-        if (GROOVY_OBJECT_TYPE.equals(superOrInterface) && !type.isInterface() && isBeingCompiled(type)) {
+        if (superOrInterface.equals(GROOVY_OBJECT_TYPE) && !type.isInterface() && isBeingCompiled(type)) {
             return true;
         }
         return false;
@@ -1069,7 +1073,7 @@ public abstract class StaticTypeCheckingSupport {
      * @return zero or more results
      */
     public static List<MethodNode> chooseBestMethod(final ClassNode receiver, final Collection<MethodNode> methods, final ClassNode... argumentTypes) {
-        if (!asBoolean(methods)) {
+        if (methods == null || methods.isEmpty()) {
             return Collections.emptyList();
         }
         if (isUsingUncheckedGenerics(receiver)) {
@@ -1655,7 +1659,7 @@ public abstract class StaticTypeCheckingSupport {
     }
 
     static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final Map<GenericsTypeName, GenericsType> resolvedPlaceholders) {
-        if (!asBoolean(connections)) return;
+        if (connections == null || connections.isEmpty()) return;
 
         int count = 0;
         while (count++ < 10000) {
@@ -1947,7 +1951,7 @@ public abstract class StaticTypeCheckingSupport {
         }
         ClassNode newType = type.getPlainNodeReference();
         GenericsType[] gt = type.getGenericsTypes();
-        if (asBoolean(spec)) {
+        if (spec != null) {
             gt = applyGenericsContext(spec, gt);
         }
         newType.setGenericsTypes(gt);
diff --git a/src/spec/test/typing/TypeCheckingTest.groovy b/src/spec/test/typing/TypeCheckingTest.groovy
index 180d0b75a5..6d2018bca3 100644
--- a/src/spec/test/typing/TypeCheckingTest.groovy
+++ b/src/spec/test/typing/TypeCheckingTest.groovy
@@ -610,7 +610,8 @@ import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound
                 it.exit()                                       // <9>
             }
             // end::least_upper_bound_collection_inference[]
-        ''', '[Static type checking] - Cannot find matching method Greeter or Salute#exit()'
+        ''',
+        '[Static type checking] - Cannot find matching method (Greeter or Salute)#exit()'
     }
 
     void testInstanceOfInference() {
diff --git a/src/test/gls/generics/GenericsBytecodeTest.groovy b/src/test/gls/generics/GenericsBytecodeTest.groovy
index 897149cda8..cd6bfc14fd 100644
--- a/src/test/gls/generics/GenericsBytecodeTest.groovy
+++ b/src/test/gls/generics/GenericsBytecodeTest.groovy
@@ -205,13 +205,37 @@ class GenericsBytecodeTest extends GenericsTestBase {
         ]
     }
 
+    // GROOVY-10229
+    void testWildcard3() {
+        createClassInfo '''
+            @groovy.transform.CompileStatic
+            class C {
+                Map<String,?> a() {
+                }
+                Map<String,List<?>> b() {
+                    def c = {
+                        [
+                            a(), a()
+                        ]
+                    }
+                    return null
+                }
+            }
+        '''
+        assert signatures == [
+                'a()Ljava/util/Map;'                        : '()Ljava/util/Map<Ljava/lang/String;*>;',
+                'b()Ljava/util/Map;'                        : '()Ljava/util/Map<Ljava/lang/String;Ljava/util/List<*>;>;',
+                'doCall()Ljava/util/List;'                  : '()Ljava/util/List<Ljava/util/Map<Ljava/lang/String;+Ljava/lang/Object;>;>;',
+                'doCall(Ljava/lang/Object;)Ljava/util/List;': '(Ljava/lang/Object;)Ljava/util/List<Ljava/util/Map<Ljava/lang/String;+Ljava/lang/Object;>;>;'
+        ]
+    }
+
     void testParameterAsParameterForReturnTypeAndFieldClass() {
         createClassInfo """
                class B<T> {
                    private T owner;
                    Class<T> getOwnerClass(){}
-   
-            } 
+            }
         """
         assert signatures == [
                 "class"                           : "<T:Ljava/lang/Object;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;",
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 4ddd49edf9..87fd1cf2cc 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -321,7 +321,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
             { -> x = 123 }
             x.charAt(0) // available in String but not available in Integer
         ''',
-        'Cannot find matching method java.io.Serializable or java.lang.Comparable'
+        'Cannot find matching method (java.io.Serializable or java.lang.Comparable',')#charAt(int)'
     }
 
     // GROOVY-9516
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 991dd48adf..026c3751e4 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -906,7 +906,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented // GROOVY-10230
+    // GROOVY-10230
     void testDiamondInferrenceFromConstructor23() {
         assertScript '''
             class A {
@@ -2417,6 +2417,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-10235
     void testCompatibleArgumentsForPlaceholders4() {
+        if (!GroovyAssert.isAtLeastJdk('1.8')) return
+
         assertScript '''
             import static java.util.concurrent.ConcurrentHashMap.newKeySet
 
@@ -2486,7 +2488,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-10100
     void testCompatibleArgumentsForPlaceholders8() {
         assertScript '''
-            import java.util.function.Function
+            import org.apache.groovy.internal.util.Function
 
             class C<T> {
                 T m(Object... args) {
@@ -3222,10 +3224,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-10557
     void testReturnTypeInferenceWithClosure2() {
         assertScript '''
+            import org.apache.groovy.internal.util.Function
+
             class C {
-                interface Function<T, R> {
-                    R apply(T t)
-                }
                 def <T> T m(Function<Reader,T> fn)  {
                     new StringReader("").withCloseable { reader ->
                         fn.apply(reader)
@@ -3747,11 +3748,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-6731
     void testContravariantMethodResolution() {
         assertScript '''
-            interface Function<T, R> {
-                R apply(T t)
-            }
+            import org.apache.groovy.internal.util.Function
 
-            public <I, O> void transform(Function<? super I, ? extends O> function) {
+            def <I, O> void transform(Function<? super I, ? extends O> function) {
                 function.apply('')
             }
 
@@ -3768,11 +3767,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     void testContravariantMethodResolutionWithImplicitCoercion() {
         assertScript '''
-            interface Function<T, R> {
-                R apply(T t)
-            }
+            import org.apache.groovy.internal.util.Function
 
-            public <I, O> void transform(Function<? super I, ? extends O> function) {
+            def <I, O> void transform(Function<? super I, ? extends O> function) {
                 function.apply('')
             }
 
@@ -3926,9 +3923,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-9635
     void testCovariantReturnTypeInferredFromMethod3() {
         assertScript '''
-            interface Function<T, R> {
-                R apply(T t)
-            }
+            import org.apache.groovy.internal.util.Function
 
             class C<R extends Number> {
                 def <V> V m(Function<C, V> f) { // R from C is confused with R->V from Function
diff --git a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
index b477460409..5545f0a2c6 100644
--- a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
@@ -18,8 +18,6 @@
  */
 package groovy.transform.stc
 
-import groovy.transform.NotYetImplemented
-
 /**
  * Unit tests for static type checking : ternary operator.
  */
@@ -133,7 +131,41 @@ class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented // GROOVY-10357
+    // GROOVY-10330
+    void testTypeParameterTypeParameter1() {
+        assertScript '''
+            import org.apache.groovy.internal.util.Function
+
+            class C<T> {
+                T y
+                void m(T x, Function<T, T> f) {
+                    assert f.apply(x) == 'foo'
+                }
+                void test(T x, Function<T, T> f) {
+                    @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                        def type = node.getNodeMetaData(INFERRED_TYPE)
+                        assert type.isGenericsPlaceHolder()
+                        assert type.unresolvedName == 'T'
+                    })
+                    def z = true ? x : y
+                    m(z, f)
+                }
+            }
+            new C<String>().test('FOO', { it.toLowerCase() })
+        '''
+    }
+
+    // GROOVY-10363
+    void testTypeParameterTypeParameter2() {
+        assertScript '''
+            def <X extends java.util.concurrent.Callable<Number>> X m(X x, X y) {
+                X ecks = true ? x : y // infers as Callable<Object>
+            }
+            assert m(null,null) == null
+        '''
+    }
+
+    // GROOVY-10357
     void testAbstractMethodDefault() {
         assertScript '''
             import org.apache.groovy.internal.util.Function
@@ -145,13 +177,53 @@ class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
             def a = new A() {
                 @Override
                 long m(Function<Boolean,Integer> f) {
-                    f(true).longValue()
+                    f.apply(true).longValue()
                 }
             }
             assert a.m() == 1L
         '''
     }
 
+    // GROOVY-10358
+    void testCommonInterface() {
+        assertScript '''
+            interface I {
+                int m(int i)
+            }
+            abstract class A implements I {
+            }
+            class B<T> extends A {
+                int m(int i) {
+                    i + 1
+                }
+            }
+            class C<T> extends A {
+                int m(int i) {
+                    i - 1
+                }
+            }
+
+            C<String> c = null; int i = 1
+            int x = (false ? c : new B<String>()).m(i) // Cannot find matching method A#m(int)
+            assert x == 2
+        '''
+    }
+
+    // GROOVY-10603
+    void testCommonInterface2() {
+        assertScript '''
+            interface I {}
+            interface J extends I {}
+            class Foo implements I {}
+            class Bar implements J {}
+
+            I test(Foo x, Bar y) {
+                true ? x : y // Cannot return value of type GroovyObject for method returning I
+            }
+            test(null, null)
+        '''
+    }
+
     // GROOVY-5523
     void testNull1() {
         assertScript '''
diff --git a/src/test/org/codehaus/groovy/ast/tools/WideningCategoriesTest.groovy b/src/test/org/codehaus/groovy/ast/tools/WideningCategoriesTest.groovy
index 4be116256e..7ba751d062 100644
--- a/src/test/org/codehaus/groovy/ast/tools/WideningCategoriesTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/tools/WideningCategoriesTest.groovy
@@ -69,7 +69,7 @@ final class WideningCategoriesTest extends GenericsTestCase {
 
     void testBuildCommonTypeWithTwoIncompatibleInterfaces() {
         ClassNode a = make(Set)
-        ClassNode b = make(Map)
+        ClassNode b = MAP_TYPE
         assert lowestUpperBound(a,b) == OBJECT_TYPE
         assert lowestUpperBound(b,a) == OBJECT_TYPE
     }
@@ -82,7 +82,7 @@ final class WideningCategoriesTest extends GenericsTestCase {
     }
 
     void testBuildCommonTypeWithOneClassAndNoImplementedInterface() {
-        ClassNode a = make(Map)
+        ClassNode a = MAP_TYPE
         ClassNode b = make(HashSet)
         assert lowestUpperBound(a,b) == OBJECT_TYPE
         assert lowestUpperBound(b,a) == OBJECT_TYPE
@@ -91,8 +91,8 @@ final class WideningCategoriesTest extends GenericsTestCase {
     void testBuildCommonTypeWithTwoClassesWithoutSuperClass() {
         ClassNode a = make(ClassA)
         ClassNode b = make(ClassB)
-        assert lowestUpperBound(a,b) == make(GroovyObject) // GroovyObject because Groovy classes implicitly implement GroovyObject
-        assert lowestUpperBound(b,a) == make(GroovyObject)
+        assert lowestUpperBound(a,b) == GROOVY_OBJECT_TYPE // GroovyObject because Groovy classes implicitly implement GroovyObject
+        assert lowestUpperBound(b,a) == GROOVY_OBJECT_TYPE
     }
 
     void testBuildCommonTypeWithIdenticalPrimitiveTypes() {
@@ -127,7 +127,14 @@ final class WideningCategoriesTest extends GenericsTestCase {
         assert lowestUpperBound(b,a) == make(HashSet)
     }
 
-    void testBuildCommonTypeWithTwoInterfacesSharingOneParent() {
+    void testBuildCommonTypeWithTwoInterfacesSharingOneParent0() {
+        ClassNode a = make(Set).plainNodeReference
+        ClassNode b = LIST_TYPE.plainNodeReference
+        assert lowestUpperBound(a,b).toString(false) == 'java.util.Collection <java.lang.Object>'
+        assert lowestUpperBound(b,a).toString(false) == 'java.util.Collection <java.lang.Object>'
+    }
+
+    void testBuildCommonTypeWithTwoInterfacesSharingOneParent1() {
         ClassNode a = make(InterfaceCA)
         ClassNode b = make(InterfaceDA)
         assert lowestUpperBound(a,b) == make(InterfaceA)
@@ -151,15 +158,15 @@ final class WideningCategoriesTest extends GenericsTestCase {
     void testBuildCommonTypeFromTwoClassesInDifferentBranches() {
         ClassNode a = make(ClassA1)
         ClassNode b = make(ClassB1)
-        assert lowestUpperBound(a,b) == make(GroovyObject)
-        assert lowestUpperBound(b,a) == make(GroovyObject)
+        assert lowestUpperBound(a,b) == GROOVY_OBJECT_TYPE
+        assert lowestUpperBound(b,a) == GROOVY_OBJECT_TYPE
     }
 
     void testBuildCommonTypeFromTwoClassesInDifferentBranchesAndOneCommonInterface() {
         ClassNode a = make(ClassA1_Serializable)
         ClassNode b = make(ClassB1_Serializable)
-        assert lowestUpperBound(a,b).interfaces as Set == [make(Serializable), make(GroovyObject)] as Set
-        assert lowestUpperBound(b,a).interfaces as Set == [make(Serializable), make(GroovyObject)] as Set
+        assert lowestUpperBound(a,b).interfaces as Set == [make(Serializable), GROOVY_OBJECT_TYPE] as Set
+        assert lowestUpperBound(b,a).interfaces as Set == [make(Serializable), GROOVY_OBJECT_TYPE] as Set
     }
 
     void testBuildCommonTypeFromTwoClassesWithCommonSuperClassAndOneCommonInterface() {
@@ -178,22 +185,22 @@ final class WideningCategoriesTest extends GenericsTestCase {
     // GROOVY-8111
     void testBuildCommonTypeFromTwoClassesWithTwoCommonInterfacesOneIsSelfReferential() {
         ClassNode a = boolean_TYPE
-        ClassNode b = extractTypesFromCode("${getClass().getName()}.Pair<String,String> type").type
+        ClassNode b = extractTypesFromCode("${this.class.name}.Pair<String,String> type").type
         ClassNode lub = lowestUpperBound(a, b)
 
         assert lub.superClass == OBJECT_TYPE
-        assert lub.interfaces as Set == [make(Comparable), make(Serializable)] as Set
+        assert lub.interfaces as Set == [COMPARABLE_TYPE, make(Serializable)] as Set
 
         lub = lowestUpperBound(b, a)
         assert lub.superClass == OBJECT_TYPE
-        assert lub.interfaces as Set == [make(Comparable), make(Serializable)] as Set
+        assert lub.interfaces as Set == [COMPARABLE_TYPE, make(Serializable)] as Set
     }
 
     void testStringWithGString() {
         ClassNode a = make(String)
         ClassNode b = make(GString)
         ClassNode type = lowestUpperBound(a,b)
-        assert type.interfaces as Set == [make(CharSequence), make(Comparable), make(Serializable)] as Set
+        assert type.interfaces as Set == [make(CharSequence), COMPARABLE_TYPE, make(Serializable)] as Set
     }
 
     void testDistinctPrimitiveTypes() {
@@ -210,44 +217,44 @@ final class WideningCategoriesTest extends GenericsTestCase {
     }
 
     void testLUBWithTwoInterfacesAndSameGenericArg() {
-        ClassNode a = extractTypesFromCode("List<String> type").type
-        ClassNode b = extractTypesFromCode("List<String> type").type
+        ClassNode a = extractTypesFromCode('List<String> type').type
+        ClassNode b = extractTypesFromCode('List<String> type').type
         ClassNode lub = lowestUpperBound(a,b)
-        assert lub == make(List)
+        assert lub == LIST_TYPE
         assert lub.genericsTypes.length == 1
         assert lub.genericsTypes[0].type == STRING_TYPE
     }
 
     void testLUBWithTwoInterfacesAndCommonSuperClassGenericArg() {
-        ClassNode a = extractTypesFromCode("List<Integer> type").type
-        ClassNode b = extractTypesFromCode("List<Long> type").type
+        ClassNode a = extractTypesFromCode('List<Integer> type').type
+        ClassNode b = extractTypesFromCode('List<Long> type').type
         ClassNode lub = lowestUpperBound(a,b)
-        assert lub == make(List)
+        assert lub == LIST_TYPE
         assert lub.genericsTypes.length == 1
         assert lub.genericsTypes[0].wildcard
         assert lub.genericsTypes[0].upperBounds[0].superClass == Number_TYPE
-        assert make(Comparable) in lub.genericsTypes[0].upperBounds[0].interfaces
+        assert COMPARABLE_TYPE in lub.genericsTypes[0].upperBounds[0].interfaces
     }
 
     void testLUBWithTwoInterfacesAndSingleCommonInterface() {
-        ClassNode a = extractTypesFromCode("List<Set> type").type
-        ClassNode b = extractTypesFromCode("List<List> type").type
+        ClassNode a = extractTypesFromCode('List<Set> type').type
+        ClassNode b = extractTypesFromCode('List<List> type').type
         ClassNode lub = lowestUpperBound(a,b)
-        assert lub == make(List)
+        assert lub == LIST_TYPE
         assert lub.genericsTypes.length == 1
         assert lub.genericsTypes[0].wildcard
         assert lub.genericsTypes[0].upperBounds[0] == make(Collection)
     }
 
     void testLUBWithTwoInterfacesAndNestedSingleCommonInterface() {
-        ClassNode a = extractTypesFromCode("Collection<List<Set>> type").type
-        ClassNode b = extractTypesFromCode("Collection<List<SortedSet>> type").type
+        ClassNode a = extractTypesFromCode('Collection<List<Set>> type').type
+        ClassNode b = extractTypesFromCode('Collection<List<SortedSet>> type').type
         ClassNode lub = lowestUpperBound(a,b)
         assert lub == make(Collection)
         assert lub.genericsTypes.length == 1
         def nestedType = lub.genericsTypes[0].type
-        assert nestedType == make(List)
-        assert nestedType.genericsTypes.length==1
+        assert nestedType == LIST_TYPE
+        assert nestedType.genericsTypes.length == 1
         assert nestedType.genericsTypes[0].wildcard
         assert nestedType.genericsTypes[0].upperBounds[0] == make(Set)
     }
@@ -276,18 +283,20 @@ final class WideningCategoriesTest extends GenericsTestCase {
         ClassNode b = extractTypesFromCode('org.codehaus.groovy.ast.tools.WideningCategoriesTest.PTopLong type').type
         ClassNode lub = lowestUpperBound(a,b)
         assert lub instanceof LowestUpperBoundClassNode // a virtual class which extends PTop<? extends Number> and implements Serializable
+        assert lub.interfaces == [make(Serializable)]
         assert lub.unresolvedSuperClass == make(PTop)
         assert lub.unresolvedSuperClass.genericsTypes.length == 1
         assert lub.unresolvedSuperClass.genericsTypes[0].wildcard // ? extends Number
-        ClassNode genericType = lub.unresolvedSuperClass.genericsTypes[0].upperBounds[0]
-        assert genericType == Long_TYPE
+        ClassNode upperBound = lub.unresolvedSuperClass.genericsTypes[0].upperBounds[0]
+        assert upperBound.superClass == Number_TYPE
+        assert upperBound.interfaces.contains(COMPARABLE_TYPE)
     }
 
     void testCommonAssignableType() {
         def typeA = extractTypesFromCode('LinkedList type').type
         def typeB = extractTypesFromCode('List type').type
         def superType = lowestUpperBound(typeA, typeB)
-        assert superType == make(List)
+        assert superType == LIST_TYPE
     }
 
     void testCommonAssignableType2() {
@@ -336,9 +345,8 @@ final class WideningCategoriesTest extends GenericsTestCase {
         def type = superType.genericsTypes[0]
         assert type.wildcard
         assert type.upperBounds[0] instanceof LowestUpperBoundClassNode
-        [Comparable, Serializable].each {
-            assert make(it) in type.upperBounds[0].interfaces
-        }
+        assert type.upperBounds[0].interfaces.contains(COMPARABLE_TYPE)
+        assert type.upperBounds[0].interfaces.contains(make(Serializable))
     }
 
     void testLUBOfArrayTypes() {
@@ -346,8 +354,7 @@ final class WideningCategoriesTest extends GenericsTestCase {
         def typeB = extractTypesFromCode('Integer[] type').type
         def superType = lowestUpperBound(typeA, typeB)
         assert superType.isArray()
-        def component = superType.getComponentType()
-        assert component == make(Number)
+        assert superType.componentType == Number_TYPE
     }
 
     // ---------- Classes and Interfaces used in this unit test ----------------