changeset 937:bb800adbe578

Update compile-time checks for extension methods. Bring the compiler in sync with the latest version of the formal extension method document at: http://hg.openjdk.java.net/lambda/defender-prototype/raw-file/18a7771b6c1b/doc/featherweight-defenders.pdf
author mcimadamore
date Tue, 29 Mar 2011 09:50:09 +0100
parents 8d77997dfc50
children f81cf9e0675f
files src/share/classes/com/sun/tools/javac/code/Types.java src/share/classes/com/sun/tools/javac/comp/Attr.java src/share/classes/com/sun/tools/javac/comp/Check.java src/share/classes/com/sun/tools/javac/resources/compiler.properties test/tools/javac/defender/Neg01.out test/tools/javac/defender/Neg02.out test/tools/javac/defender/Neg03.java test/tools/javac/defender/Neg03.out test/tools/javac/defender/Neg04.java test/tools/javac/defender/Neg04.out test/tools/javac/defender/Neg05.java test/tools/javac/defender/Neg05.out test/tools/javac/defender/Pos06.java test/tools/javac/defender/Pos08.java test/tools/javac/defender/Pos09.java test/tools/javac/diags/examples.not-yet.txt
diffstat 16 files changed, 232 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Types.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Types.java	Tue Mar 29 09:50:09 2011 +0100
@@ -39,7 +39,6 @@
 import com.sun.tools.javac.comp.Resolve;
 import com.sun.tools.javac.comp.AttrContext;
 
-import static com.sun.tools.javac.code.Scope.*;
 import static com.sun.tools.javac.code.Type.*;
 import static com.sun.tools.javac.code.TypeTags.*;
 import static com.sun.tools.javac.code.Symbol.*;
@@ -2662,10 +2661,10 @@
                 MethodSymbol impl = implementationInternal(ms, origin, checkResult, implFilter);
                 if ((impl == null || (impl.flags() & ABSTRACT) != 0) &&
                         source.allowDefenderMethods()) {
-                    MethodSymbol newImpl =
-                            findCompatibleDefendersInSite(origin.type, ms).head;
-                    if (newImpl != null)
-                        impl = newImpl; //we found a suitable defender impl
+                    MethodSymbol prov = provenence(origin.type, ms).head;
+                    if (prov != null && prov.overrides(ms, origin, Types.this, checkResult)) {
+                        impl = prov;
+                    }
                 }
                 cache.put(origin, new Entry(impl, implFilter, checkResult));
                 return impl;
@@ -2745,15 +2744,41 @@
 
 
     //where
-    public List<MethodSymbol> findCompatibleDefendersInSite(Type site, MethodSymbol ms) {
-        ListBuffer<MethodSymbol> methods = ListBuffer.lb();
-        findCompatibleDefendersInSite(site, ms, site.tsym, methods);
+    public List<MethodSymbol> provenence(Type site, MethodSymbol ms) {
+        Filter<Symbol> filter = new MethodFilter(ms, site);
+        List<MethodSymbol> abstracts = List.nil();
+        for (Symbol s : membersClosure(site).getElements(filter)) {
+            if (!site.tsym.isInterface() && !s.owner.isInterface()) {
+                return List.of((MethodSymbol)s);
+            } else if (!abstracts.contains(s) &&
+                    (s.flags() & DEFENDER) != 0) {
+                abstracts = abstracts.prepend((MethodSymbol)s);
+            }
+        }
+        return prune(abstracts, ownerComparator);
+    }
+
+    public MethodSymbol mostSpecificMethod(Type site, MethodSymbol ms) {
+        Filter<Symbol> filter = new MethodFilter(ms, site);
+        List<MethodSymbol> methods = List.nil();
+        for (Symbol s : membersClosure(site).getElements(filter)) {
+            if (!methods.contains(s)) {
+                methods = methods.prepend((MethodSymbol)s);
+            }
+        }
+        List<MethodSymbol> mostSpecificReturn =
+               prune(methods, new ReturnTypeExtractor(site));
+        return mostSpecificReturn.size() == 1 ?
+            mostSpecificReturn.head : null;
+    }
+
+    public List<MethodSymbol> prune(List<MethodSymbol> methods, Comparator<MethodSymbol> cmp) {
         ListBuffer<MethodSymbol> methodsMin = ListBuffer.lb();
         for (MethodSymbol m1 : methods) {
             boolean isMin_m1 = true;
             for (MethodSymbol m2 : methods) {
                 if (m1 == m2) continue;
-                if (asSuper(m2.owner.type, m1.owner) != null) {
+                if (cmp.compare(m2, m1) < 0) {
                     isMin_m1 = false;
                     break;
                 }
@@ -2764,42 +2789,42 @@
         return methodsMin.toList();
     }
 
-    private void findCompatibleDefendersInSite(Type site, MethodSymbol ms, TypeSymbol origin, ListBuffer<MethodSymbol> methods) {
-        for (Type t = origin.type; t.tag == CLASS || t.tag == TYPEVAR; t = supertype(t)) {
-            while (t.tag == TYPEVAR)
-                t = t.getUpperBound();
-            TypeSymbol c = t.tsym;
-            Filter<Symbol> filter = new DefenderOrAbstractFilter(ms, site);
-            for (Scope.Entry e = c.members().lookup(ms.name, filter);
-                 e.scope != null;
-                 e = e.next(filter)) {
-                if (e.sym != null) {
-                    if ((e.sym.flags() & DEFENDER) != 0 &&
-                            !methods.contains(e.sym))
-                        methods.append((MethodSymbol)e.sym);
-                    return;
-                }
-            }
-            for (Type t2 : interfaces(t)) {
-                findCompatibleDefendersInSite(site, ms, t2.tsym, methods);
-            }
+    Comparator<MethodSymbol> ownerComparator = new Comparator<MethodSymbol>() {
+        public int compare(MethodSymbol s1, MethodSymbol s2) {
+            return s1.owner.isSubClass(s2.owner, Types.this) ? -1 : 1;
+        }
+    };
+
+    class ReturnTypeExtractor implements Comparator<MethodSymbol> {
+
+        Type site;
+
+        public ReturnTypeExtractor(Type site) {
+            this.site = site;
+        }
+
+        public int compare(MethodSymbol m1, MethodSymbol m2) {
+            Type mt1 = memberType(site, m1);
+            Type mt2 = memberType(site, m2);
+            return returnTypeSubstitutable(mt1, mt2) ? -1 : 1;
         }
     }
     // where
-            private class DefenderOrAbstractFilter implements Filter<Symbol> {
+            private class MethodFilter implements Filter<Symbol> {
 
                 Symbol msym;
                 Type site;
 
-                DefenderOrAbstractFilter(Symbol msym, Type site) {
+                MethodFilter(Symbol msym, Type site) {
                     this.msym = msym;
                     this.site = site;
                 }
 
                 public boolean accepts(Symbol s) {
                     return s.kind == Kinds.MTH &&
-                            overrideEquivalent(memberType(site, s), memberType(site, msym)) &&
-                            (s.flags() & DEFENDER | ABSTRACT) != 0;
+                            s.name == msym.name &&
+                            s.isInheritedIn(site.tsym, Types.this) &&
+                            overrideEquivalent(memberType(site, s), memberType(site, msym));
                 }
             };
     // </editor-fold>
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Mar 29 09:50:09 2011 +0100
@@ -757,6 +757,7 @@
                 chk.checkHideClashes(tree.pos(), env.enclClass.type, m);
             } else {
                 chk.checkOverrideClashes(tree.pos(), env.enclClass.type, m);
+                chk.checkDefenderPassthrough(tree.pos(), env.enclClass.type, m);
             }
             chk.checkOverride(tree, m);
 
@@ -3831,6 +3832,7 @@
             // are compatible (i.e. no two define methods with same arguments
             // yet different return types).  (JLS 8.4.6.3)
             chk.checkCompatibleSupertypes(tree.pos(), c.type);
+            chk.checkDefenderClashes(tree.pos(), c.type);
         }
 
         // Check that class does not import the same parameterized interface
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Mar 29 09:50:09 2011 +0100
@@ -1864,31 +1864,10 @@
                         (types.covariantReturnType(rt1, rt2, Warner.noWarnings) ||
                          types.covariantReturnType(rt2, rt1, Warner.noWarnings)) ||
                          checkCommonOverriderIn(s1,s2,site);
-                    String errKey = "types.incompatible.diff.ret";
-                    Type diagType1 = t1;
-                    Type diagType2 = t2;
-                    if (compat && allowDefenderMethods) {
-                        Symbol defaultImpl = null;
-                        for (MethodSymbol defender :
-                                types.findCompatibleDefendersInSite(site, (MethodSymbol)s1)) {
-                            if (defaultImpl == null) {
-                                defaultImpl = defender.getDefaultImpl();
-                                diagType1 = defender.owner.type;
-                            } else {
-                                compat = compat &&
-                                        defaultImpl == defender.getDefaultImpl();
-                                if (!compat) {                                    
-                                    diagType2 = defender.owner.type;
-                                    break;
-                                }
-                            }
-                        }
-                        errKey = "types.incompatible.diff.default";
-                    }
                     if (!compat) {
-                        log.error(pos, errKey,
-                            diagType1, diagType2, s2.name +
-                            "(" + types.memberType(diagType2, s2).getParameterTypes() + ")");
+                        log.error(pos, "types.incompatible.diff.ret",
+                            t1, t2, s2.name +
+                            "(" + types.memberType(t2, s2).getParameterTypes() + ")");
                         return s2;
                     }
                 } else if (checkNameClash((ClassSymbol)site.tsym, s1, s2)) {
@@ -2392,6 +2371,66 @@
          }
      }
 
+    void checkDefenderClashes(DiagnosticPosition pos, Type site) {
+        DefenderClashFilter dcf = new DefenderClashFilter(site);
+        for (Symbol m : types.membersClosure(site).getElements(dcf)) {
+            List<MethodSymbol> prov = types.provenence(site, (MethodSymbol)m);
+            if (prov.size() > 1) {
+                Symbol tsym1 = prov.head.owner;
+                Symbol tsym2 = prov.tail.head.owner;
+                log.error(pos, "types.incompatible.unrelated.defaults",
+                        Kinds.kindName(site.tsym), site,
+                        m.name, types.memberType(site, m).getParameterTypes(),
+                        tsym1, tsym2);
+            } else if (prov.size() == 1) {
+                if ((prov.head.flags() & DEFENDER) == 0) continue;
+                Symbol msm = types.mostSpecificMethod(site, (MethodSymbol)m);
+                if (msm == null) continue;
+                Type mt1 = types.memberType(site, prov.head);
+                Type mt2 = types.memberType(site, msm);
+                if (!types.isSameType(mt1.getReturnType(), mt2.getReturnType())) {
+                    log.error(pos, "types.incompatible.bad.default.override",
+                            Kinds.kindName(site.tsym), site,
+                            m.name, mt1.getParameterTypes(),
+                            mt1.getReturnType(), mt2.getReturnType());
+                }
+            }
+        }
+    }
+
+    void checkDefenderPassthrough(DiagnosticPosition pos, Type site, Symbol msym) {
+        if (!msym.owner.isInterface() ||
+                (msym.flags() & ABSTRACT) == 0 ||
+                (msym.flags() & DEFENDER) != 0) return;
+        List<MethodSymbol> prov = types.provenence(site, (MethodSymbol)msym);
+        if (prov.isEmpty() || (prov.head.flags() & DEFENDER) == 0) return;
+        Type def = types.memberType(site, prov.head);
+        Type mt = types.memberType(site, msym);
+        if (!types.isSameType(def.getReturnType(), mt.getReturnType())) {
+            log.error(pos, "types.incompatible.bad.defender.passthrough",
+                    Kinds.kindName(site.tsym), site,
+                    msym.name, mt.getParameterTypes(),
+                    prov.head.owner);
+        }
+    }
+
+    //where
+     private class DefenderClashFilter implements Filter<Symbol> {
+
+         Type site;
+
+         DefenderClashFilter(Type site) {
+             this.site = site;
+         }
+
+         public boolean accepts(Symbol s) {
+             return s.kind == MTH &&
+                     (s.flags() & SYNTHETIC | ABSTRACT | DEFENDER) == (ABSTRACT | DEFENDER) &&
+                     s.isInheritedIn(site.tsym, types) &&
+                     !s.isConstructor();
+         }
+     }
+     
     /** Report a conflict between a user symbol and a synthetic symbol.
      */
     private void syntheticError(DiagnosticPosition pos, Symbol sym) {
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Mar 25 15:51:00 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Mar 29 09:50:09 2011 +0100
@@ -818,8 +818,19 @@
 compiler.err.types.incompatible.diff.ret=\
     types {0} and {1} are incompatible; both define {2}, but with unrelated return types
 
-compiler.err.types.incompatible.diff.default=\
-    types {0} and {1} are incompatible; both define {2}, but with unrelated default implementations
+# 0: kind, 1: type, 2: name, 3: list of type, 4: symbol, 5:symbol
+compiler.err.types.incompatible.unrelated.defaults=\
+    {0} {1} inherits unrelated defaults for {2}({3}) from types {4} and {5}
+
+# 0: kind, 1: type, 2: name, 3: list of type, 4: type, 5:type
+compiler.err.types.incompatible.bad.default.override=\
+    {0} {1} inherits an incompatible default for {2}({3})\n\
+    return type {4} is not compatible with most specific return type {5}
+
+# 0: kind, 1: type, 2: name, 3: list of type, 4: symbol
+compiler.err.types.incompatible.bad.defender.passthrough=\
+    {0} {1} defines an incompatible abstract method {2}({3})\n\
+    Attempt to override a defender method in {4} with incompatible return type
 
 compiler.err.unclosed.char.lit=\
     unclosed character literal
--- a/test/tools/javac/defender/Neg01.out	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Neg01.out	Tue Mar 29 09:50:09 2011 +0100
@@ -1,2 +1,2 @@
-Neg01.java:38:12: compiler.err.types.incompatible.diff.default: Neg01.IA, Neg01.IB, m()
+Neg01.java:38:12: compiler.err.types.incompatible.unrelated.defaults: kindname.class, Neg01.AB, m, , Neg01.IA, Neg01.IB
 1 error
--- a/test/tools/javac/defender/Neg02.out	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Neg02.out	Tue Mar 29 09:50:09 2011 +0100
@@ -1,2 +1,2 @@
-Neg02.java:40:13: compiler.err.types.incompatible.diff.default: Neg02.A, Neg02.B, m()
+Neg02.java:40:13: compiler.err.types.incompatible.unrelated.defaults: kindname.class, Neg02.X, m, , Neg02.A, Neg02.B
 1 error
--- a/test/tools/javac/defender/Neg03.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Neg03.java	Tue Mar 29 09:50:09 2011 +0100
@@ -45,10 +45,10 @@
     static class X implements C, A { } //ok - ignore extraneous remix of A
 
     interface D extends A, B {
-      void m();  // reabstraction of m()
+      void m();  // invalid - m() is not reabstracted!
     }
 
-    static class Y implements D, A { } // ok - m() is abstract in D but A provides the defender
+    static class Y implements D, A { } // invalid - both A.m() and B.m() still available
 
     interface E extends A {
         void m();  // reabstraction of m()
--- a/test/tools/javac/defender/Neg03.out	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Neg03.out	Tue Mar 29 09:50:09 2011 +0100
@@ -1,3 +1,5 @@
-Neg03.java:57:12: compiler.err.does.not.override.abstract: Neg03.W, m(), Neg03.D
-Neg03.java:59:12: compiler.err.types.incompatible.diff.default: Neg03.A, Neg03.B, m()
-2 errors
+Neg03.java:47:5: compiler.err.types.incompatible.unrelated.defaults: kindname.interface, Neg03.D, m, , Neg03.A, Neg03.B
+Neg03.java:51:12: compiler.err.types.incompatible.unrelated.defaults: kindname.class, Neg03.Y, m, , Neg03.B, Neg03.A
+Neg03.java:57:12: compiler.err.types.incompatible.unrelated.defaults: kindname.class, Neg03.W, m, , Neg03.B, Neg03.A
+Neg03.java:59:12: compiler.err.types.incompatible.unrelated.defaults: kindname.class, Neg03.Z, m, , Neg03.A, Neg03.B
+4 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Neg04.java	Tue Mar 29 09:50:09 2011 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary check that defender method must have most specific return type
+ * @author  Maurizio Cimadamore
+ * @compile/fail/ref=Neg04.out -XDrawDiagnostics Neg04.java
+ */
+
+class Neg04 {
+    interface IA1 { Number m(); }
+    interface IA2 { Number m() default Neg04.m; } //error
+    interface IA3 { Integer m(); }
+
+    abstract class C implements IA1, IA2, IA3{}
+
+    static int m(IA2 a) { return 0; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Neg04.out	Tue Mar 29 09:50:09 2011 +0100
@@ -0,0 +1,2 @@
+Neg04.java:36:14: compiler.err.types.incompatible.bad.default.override: kindname.class, Neg04.C, m, , java.lang.Number, java.lang.Integer
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Neg05.java	Tue Mar 29 09:50:09 2011 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary check that abstract method are compatible with inherithed defenders
+ * @author  Maurizio Cimadamore
+ * @compile/fail/ref=Neg05.out -XDrawDiagnostics Neg05.java
+ */
+
+class Neg05 {
+    interface IA1 { Number m() default Neg05.m1; }
+    interface IA2 extends IA1 { Integer m() default Neg05.m2; }
+    interface IA3 extends IA2 { Number m(); } //error
+
+    static class C implements IA3{}
+
+    static int m1(IA1 a) { return 0; }
+    static int m2(IA2 b) { return 0; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Neg05.out	Tue Mar 29 09:50:09 2011 +0100
@@ -0,0 +1,2 @@
+Neg05.java:34:40: compiler.err.types.incompatible.bad.defender.passthrough: kindname.interface, Neg05.IA3, m, , Neg05.IA2
+1 error
--- a/test/tools/javac/defender/Pos06.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Pos06.java	Tue Mar 29 09:50:09 2011 +0100
@@ -33,7 +33,7 @@
          extension void m() default Pos06.impl;
      }
 
-     interface B {
+     interface B extends A {
          extension void m() default Pos06.impl;
      }
 
--- a/test/tools/javac/defender/Pos08.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Pos08.java	Tue Mar 29 09:50:09 2011 +0100
@@ -41,10 +41,6 @@
         extension void m() default Pos08.b;
     }
 
-    interface D extends A, B {
-        void m();
-    }
-
     static void a(A o) { }
     static void b(B o) { }
 }
--- a/test/tools/javac/defender/Pos09.java	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/defender/Pos09.java	Tue Mar 29 09:50:09 2011 +0100
@@ -32,7 +32,7 @@
 import pkg1.A;
 
 class Pos09 {
-    interface B {
+    interface B extends A.I {
         extension void m() default A.m;
     }
 
--- a/test/tools/javac/diags/examples.not-yet.txt	Fri Mar 25 15:51:00 2011 +0000
+++ b/test/tools/javac/diags/examples.not-yet.txt	Tue Mar 29 09:50:09 2011 +0100
@@ -128,9 +128,11 @@
 compiler.err.method.references.not.supported.in.source                           #LAMBDA
 compiler.err.throw.typarams.not.supported.in.source                              #LAMBDA
 compiler.err.throws.typaram.not.allowed.here                                     #LAMBDA
+compiler.err.types.incompatible.unrelated.defaults                               #LAMBDA
+compiler.err.types.incompatible.bad.default.override                             #LAMBDA
+compiler.err.types.incompatible.bad.defender.passthrough                         #LAMBDA
 compiler.err.unexpected.lambda                                                   #LAMBDA
 compiler.err.unexpected.meth.reference                                           #LAMBDA
-compiler.err.types.incompatible.diff.default                                     #LAMBDA
 compiler.misc.bad.defender.method                                                #LAMBDA
 compiler.misc.disjunctive.type                                                   #LAMBDA
 compiler.misc.encl.instance.for.lambda.conv.target.must.be.in.scope              #LAMBDA