7062745: Regression: difference in overload resolution when two methods are maximally specific

Wed, 27 Jul 2011 19:00:53 +0100

author
mcimadamore
date
Wed, 27 Jul 2011 19:00:53 +0100
changeset 1059
0b5beb9562c6
parent 1058
36f31b87b0ab
child 1060
d5f33267a06d

7062745: Regression: difference in overload resolution when two methods are maximally specific
Summary: Fix most specific when two methods are maximally specific and only one has non-raw return type
Reviewed-by: jjg, dlsmith

src/share/classes/com/sun/tools/javac/comp/Resolve.java file | annotate | diff | comparison | revisions
test/tools/javac/generics/rawOverride/7062745/GenericOverrideTest.java file | annotate | diff | comparison | revisions
test/tools/javac/generics/rawOverride/7062745/T7062745neg.java file | annotate | diff | comparison | revisions
test/tools/javac/generics/rawOverride/7062745/T7062745neg.out file | annotate | diff | comparison | revisions
test/tools/javac/generics/rawOverride/7062745/T7062745pos.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Fri Jul 22 21:31:14 2011 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Wed Jul 27 19:00:53 2011 +0100
     1.3 @@ -767,16 +767,13 @@
     1.4                                         m2.erasure(types).getParameterTypes()))
     1.5                      return ambiguityError(m1, m2);
     1.6                  // both abstract, neither overridden; merge throws clause and result type
     1.7 -                Symbol mostSpecific;
     1.8 -                if (types.returnTypeSubstitutable(mt1, mt2))
     1.9 -                    mostSpecific = m1;
    1.10 -                else if (types.returnTypeSubstitutable(mt2, mt1))
    1.11 -                    mostSpecific = m2;
    1.12 -                else {
    1.13 +                Type mst = mostSpecificReturnType(mt1, mt2);
    1.14 +                if (mst == null) {
    1.15                      // Theoretically, this can't happen, but it is possible
    1.16                      // due to error recovery or mixing incompatible class files
    1.17                      return ambiguityError(m1, m2);
    1.18                  }
    1.19 +                Symbol mostSpecific = mst == mt1 ? m1 : m2;
    1.20                  List<Type> allThrown = chk.intersect(mt1.getThrownTypes(), mt2.getThrownTypes());
    1.21                  Type newSig = types.createMethodTypeWithThrown(mostSpecific.type, allThrown);
    1.22                  MethodSymbol result = new MethodSymbol(
    1.23 @@ -859,6 +856,28 @@
    1.24          }
    1.25      }
    1.26      //where
    1.27 +    Type mostSpecificReturnType(Type mt1, Type mt2) {
    1.28 +        Type rt1 = mt1.getReturnType();
    1.29 +        Type rt2 = mt2.getReturnType();
    1.30 +
    1.31 +        if (mt1.tag == FORALL && mt2.tag == FORALL) {
    1.32 +            //if both are generic methods, adjust return type ahead of subtyping check
    1.33 +            rt1 = types.subst(rt1, mt1.getTypeArguments(), mt2.getTypeArguments());
    1.34 +        }
    1.35 +        //first use subtyping, then return type substitutability
    1.36 +        if (types.isSubtype(rt1, rt2)) {
    1.37 +            return mt1;
    1.38 +        } else if (types.isSubtype(rt2, rt1)) {
    1.39 +            return mt2;
    1.40 +        } else if (types.returnTypeSubstitutable(mt1, mt2)) {
    1.41 +            return mt1;
    1.42 +        } else if (types.returnTypeSubstitutable(mt2, mt1)) {
    1.43 +            return mt2;
    1.44 +        } else {
    1.45 +            return null;
    1.46 +        }
    1.47 +    }
    1.48 +    //where
    1.49      Symbol ambiguityError(Symbol m1, Symbol m2) {
    1.50          if (((m1.flags() | m2.flags()) & CLASH) != 0) {
    1.51              return (m1.flags() & CLASH) == 0 ? m1 : m2;
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/test/tools/javac/generics/rawOverride/7062745/GenericOverrideTest.java	Wed Jul 27 19:00:53 2011 +0100
     2.3 @@ -0,0 +1,286 @@
     2.4 +/*
     2.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
     2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.7 + *
     2.8 + * This code is free software; you can redistribute it and/or modify it
     2.9 + * under the terms of the GNU General Public License version 2 only, as
    2.10 + * published by the Free Software Foundation.
    2.11 + *
    2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    2.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    2.15 + * version 2 for more details (a copy is included in the LICENSE file that
    2.16 + * accompanied this code).
    2.17 + *
    2.18 + * You should have received a copy of the GNU General Public License version
    2.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    2.21 + *
    2.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    2.23 + * or visit www.oracle.com if you need additional information or have any
    2.24 + * questions.
    2.25 + */
    2.26 +
    2.27 +/*
    2.28 + * @test
    2.29 + * @bug 7062745
    2.30 + * @summary  Regression: difference in overload resolution when two methods are maximally specific
    2.31 + */
    2.32 +
    2.33 +import com.sun.source.util.JavacTask;
    2.34 +import java.net.URI;
    2.35 +import java.util.Arrays;
    2.36 +import javax.tools.Diagnostic;
    2.37 +import javax.tools.JavaCompiler;
    2.38 +import javax.tools.JavaFileObject;
    2.39 +import javax.tools.SimpleJavaFileObject;
    2.40 +import javax.tools.StandardJavaFileManager;
    2.41 +import javax.tools.ToolProvider;
    2.42 +
    2.43 +public class GenericOverrideTest {
    2.44 +
    2.45 +    static int checkCount = 0;
    2.46 +
    2.47 +    enum SignatureKind {
    2.48 +        NON_GENERIC(""),
    2.49 +        GENERIC("<X>");
    2.50 +
    2.51 +        String paramStr;
    2.52 +
    2.53 +        private SignatureKind(String paramStr) {
    2.54 +            this.paramStr = paramStr;
    2.55 +        }
    2.56 +    }
    2.57 +
    2.58 +    enum ReturnTypeKind {
    2.59 +        LIST("List"),
    2.60 +        ARRAYLIST("ArrayList");
    2.61 +
    2.62 +        String retStr;
    2.63 +
    2.64 +        private ReturnTypeKind(String retStr) {
    2.65 +            this.retStr = retStr;
    2.66 +        }
    2.67 +
    2.68 +        boolean moreSpecificThan(ReturnTypeKind that) {
    2.69 +            switch (this) {
    2.70 +                case LIST:
    2.71 +                    return that == this;
    2.72 +                case ARRAYLIST:
    2.73 +                    return that == LIST || that == ARRAYLIST;
    2.74 +                default: throw new AssertionError("Unexpected ret kind: " + this);
    2.75 +            }
    2.76 +        }
    2.77 +    }
    2.78 +
    2.79 +    enum TypeArgumentKind {
    2.80 +        NONE(""),
    2.81 +        UNBOUND("<?>"),
    2.82 +        INTEGER("<Number>"),
    2.83 +        NUMBER("<Integer>"),
    2.84 +        TYPEVAR("<X>");
    2.85 +
    2.86 +        String typeargStr;
    2.87 +
    2.88 +        private TypeArgumentKind(String typeargStr) {
    2.89 +            this.typeargStr = typeargStr;
    2.90 +        }
    2.91 +
    2.92 +        boolean compatibleWith(SignatureKind sig) {
    2.93 +            switch (this) {
    2.94 +                case TYPEVAR: return sig != SignatureKind.NON_GENERIC;
    2.95 +                default: return true;
    2.96 +            }
    2.97 +        }
    2.98 +
    2.99 +        boolean moreSpecificThan(TypeArgumentKind that, boolean strict) {
   2.100 +            switch (this) {
   2.101 +                case NONE:
   2.102 +                    return that == this || !strict;
   2.103 +                case UNBOUND:
   2.104 +                    return that == this || that == NONE;
   2.105 +                case INTEGER:
   2.106 +                case NUMBER:
   2.107 +                case TYPEVAR:
   2.108 +                    return that == this || that == NONE || that == UNBOUND;
   2.109 +                default: throw new AssertionError("Unexpected typearg kind: " + this);
   2.110 +            }
   2.111 +        }
   2.112 +
   2.113 +        boolean assignableTo(TypeArgumentKind that, SignatureKind sig) {
   2.114 +            switch (this) {
   2.115 +                case NONE:
   2.116 +                    //this case needs to workaround to javac's impl of 15.12.2.8 being too strict
   2.117 +                    //ideally should be just 'return true' (see 7067746)
   2.118 +                    return sig == SignatureKind.NON_GENERIC || that == NONE;
   2.119 +                case UNBOUND:
   2.120 +                    return that == this || that == NONE;
   2.121 +                case INTEGER:
   2.122 +                case NUMBER:
   2.123 +                    return that == this || that == NONE || that == UNBOUND;
   2.124 +                case TYPEVAR:
   2.125 +                    return true;
   2.126 +                default: throw new AssertionError("Unexpected typearg kind: " + this);
   2.127 +            }
   2.128 +        }
   2.129 +    }
   2.130 +
   2.131 +    public static void main(String... args) throws Exception {
   2.132 +
   2.133 +        //create default shared JavaCompiler - reused across multiple compilations
   2.134 +        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
   2.135 +        StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
   2.136 +
   2.137 +        for (SignatureKind sig1 : SignatureKind.values()) {
   2.138 +            for (ReturnTypeKind rt1 : ReturnTypeKind.values()) {
   2.139 +                for (TypeArgumentKind ta1 : TypeArgumentKind.values()) {
   2.140 +                    if (!ta1.compatibleWith(sig1)) continue;
   2.141 +                    for (SignatureKind sig2 : SignatureKind.values()) {
   2.142 +                        for (ReturnTypeKind rt2 : ReturnTypeKind.values()) {
   2.143 +                            for (TypeArgumentKind ta2 : TypeArgumentKind.values()) {
   2.144 +                                if (!ta2.compatibleWith(sig2)) continue;
   2.145 +                                for (ReturnTypeKind rt3 : ReturnTypeKind.values()) {
   2.146 +                                    for (TypeArgumentKind ta3 : TypeArgumentKind.values()) {
   2.147 +                                        if (!ta3.compatibleWith(SignatureKind.NON_GENERIC)) continue;
   2.148 +                                        new GenericOverrideTest(sig1, rt1, ta1, sig2, rt2, ta2, rt3, ta3).run(comp, fm);
   2.149 +                                    }
   2.150 +                                }
   2.151 +                            }
   2.152 +                        }
   2.153 +                    }
   2.154 +                }
   2.155 +            }
   2.156 +        }
   2.157 +        System.out.println("Total check executed: " + checkCount);
   2.158 +    }
   2.159 +
   2.160 +    SignatureKind sig1, sig2;
   2.161 +    ReturnTypeKind rt1, rt2, rt3;
   2.162 +    TypeArgumentKind ta1, ta2, ta3;
   2.163 +    JavaSource source;
   2.164 +    DiagnosticChecker diagChecker;
   2.165 +
   2.166 +    GenericOverrideTest(SignatureKind sig1, ReturnTypeKind rt1, TypeArgumentKind ta1,
   2.167 +            SignatureKind sig2, ReturnTypeKind rt2, TypeArgumentKind ta2, ReturnTypeKind rt3, TypeArgumentKind ta3) {
   2.168 +        this.sig1 = sig1;
   2.169 +        this.sig2 = sig2;
   2.170 +        this.rt1 = rt1;
   2.171 +        this.rt2 = rt2;
   2.172 +        this.rt3 = rt3;
   2.173 +        this.ta1 = ta1;
   2.174 +        this.ta2 = ta2;
   2.175 +        this.ta3 = ta3;
   2.176 +        this.source = new JavaSource();
   2.177 +        this.diagChecker = new DiagnosticChecker();
   2.178 +    }
   2.179 +
   2.180 +    class JavaSource extends SimpleJavaFileObject {
   2.181 +
   2.182 +        String template = "import java.util.*;\n" +
   2.183 +                          "interface A { #S1 #R1#TA1 m(); }\n" +
   2.184 +                          "interface B { #S2 #R2#TA2 m(); }\n" +
   2.185 +                          "interface AB extends A, B {}\n" +
   2.186 +                          "class Test {\n" +
   2.187 +                          "  void test(AB ab) { #R3#TA3 n = ab.m(); }\n" +
   2.188 +                          "}";
   2.189 +
   2.190 +        String source;
   2.191 +
   2.192 +        public JavaSource() {
   2.193 +            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   2.194 +            source = template.replace("#S1", sig1.paramStr).
   2.195 +                    replace("#S2", sig2.paramStr).
   2.196 +                    replace("#R1", rt1.retStr).
   2.197 +                    replace("#R2", rt2.retStr).
   2.198 +                    replace("#R3", rt3.retStr).
   2.199 +                    replace("#TA1", ta1.typeargStr).
   2.200 +                    replace("#TA2", ta2.typeargStr).
   2.201 +                    replace("#TA3", ta3.typeargStr);
   2.202 +        }
   2.203 +
   2.204 +        @Override
   2.205 +        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   2.206 +            return source;
   2.207 +        }
   2.208 +    }
   2.209 +
   2.210 +    void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
   2.211 +        JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
   2.212 +                null, null, Arrays.asList(source));
   2.213 +        try {
   2.214 +            ct.analyze();
   2.215 +        } catch (Throwable ex) {
   2.216 +            throw new AssertionError("Error thron when compiling the following code:\n" + source.getCharContent(true));
   2.217 +        }
   2.218 +        check();
   2.219 +    }
   2.220 +
   2.221 +    void check() {
   2.222 +        checkCount++;
   2.223 +
   2.224 +        boolean errorExpected = false;
   2.225 +        int mostSpecific = 0;
   2.226 +
   2.227 +        //first check that either |R1| <: |R2| or |R2| <: |R1|
   2.228 +        if (rt1 != rt2) {
   2.229 +            if (!rt1.moreSpecificThan(rt2) &&
   2.230 +                    !rt2.moreSpecificThan(rt1)) {
   2.231 +                errorExpected = true;
   2.232 +            } else {
   2.233 +                mostSpecific = rt1.moreSpecificThan(rt2) ? 1 : 2;
   2.234 +            }
   2.235 +        }
   2.236 +
   2.237 +        //check that either TA1 <= TA2 or TA2 <= TA1 (unless most specific return found above is raw)
   2.238 +        if (!errorExpected) {
   2.239 +            if (ta1 != ta2) {
   2.240 +                boolean useStrictCheck = ta1.moreSpecificThan(ta2, true) || ta2.moreSpecificThan(ta1, true);
   2.241 +                if (!ta1.moreSpecificThan(ta2, useStrictCheck) &&
   2.242 +                        !ta2.moreSpecificThan(ta1, useStrictCheck)) {
   2.243 +                    errorExpected = true;
   2.244 +                } else {
   2.245 +                    int mostSpecific2 = ta1.moreSpecificThan(ta2, useStrictCheck) ? 1 : 2;
   2.246 +                    if (mostSpecific != 0 && mostSpecific2 != mostSpecific) {
   2.247 +                        errorExpected = mostSpecific == 1 ? ta1 != TypeArgumentKind.NONE : ta2 != TypeArgumentKind.NONE;
   2.248 +                    } else {
   2.249 +                        mostSpecific = mostSpecific2;
   2.250 +                    }
   2.251 +                }
   2.252 +            } else if (mostSpecific == 0) {
   2.253 +                //when no signature is better than the other, an arbitrary choice
   2.254 +                //must be made - javac always picks the second signature
   2.255 +                mostSpecific = 2;
   2.256 +            }
   2.257 +        }
   2.258 +
   2.259 +        //finally, check that most specific return type is compatible with expected type
   2.260 +        if (!errorExpected) {
   2.261 +            ReturnTypeKind msrt = mostSpecific == 1 ? rt1 : rt2;
   2.262 +            TypeArgumentKind msta = mostSpecific == 1 ? ta1 : ta2;
   2.263 +            SignatureKind mssig = mostSpecific == 1 ? sig1 : sig2;
   2.264 +
   2.265 +            if (!msrt.moreSpecificThan(rt3) ||
   2.266 +                    !msta.assignableTo(ta3, mssig)) {
   2.267 +                errorExpected = true;
   2.268 +            }
   2.269 +        }
   2.270 +
   2.271 +        if (errorExpected != diagChecker.errorFound) {
   2.272 +            throw new Error("invalid diagnostics for source:\n" +
   2.273 +                source.getCharContent(true) +
   2.274 +                "\nFound error: " + diagChecker.errorFound +
   2.275 +                "\nExpected error: " + errorExpected);
   2.276 +        }
   2.277 +    }
   2.278 +
   2.279 +    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
   2.280 +
   2.281 +        boolean errorFound;
   2.282 +
   2.283 +        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   2.284 +            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
   2.285 +                errorFound = true;
   2.286 +            }
   2.287 +        }
   2.288 +    }
   2.289 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/tools/javac/generics/rawOverride/7062745/T7062745neg.java	Wed Jul 27 19:00:53 2011 +0100
     3.3 @@ -0,0 +1,18 @@
     3.4 +/*
     3.5 + * @test /nodynamiccopyright/
     3.6 + * @bug     7062745
     3.7 + * @summary  Regression: difference in overload resolution when two methods are maximally specific
     3.8 + * @compile/fail/ref=T7062745neg.out -XDrawDiagnostics T7062745neg.java
     3.9 + */
    3.10 +
    3.11 +import java.util.*;
    3.12 +
    3.13 +class T7062745neg {
    3.14 +    interface A { List<Number> getList(); }
    3.15 +    interface B { ArrayList getList(); }
    3.16 +    interface AB extends A, B {}
    3.17 +
    3.18 +    void test(AB ab) {
    3.19 +        Number n = ab.getList().get(1);
    3.20 +    }
    3.21 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/test/tools/javac/generics/rawOverride/7062745/T7062745neg.out	Wed Jul 27 19:00:53 2011 +0100
     4.3 @@ -0,0 +1,2 @@
     4.4 +T7062745neg.java:16:36: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.lang.Object, java.lang.Number
     4.5 +1 error
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/tools/javac/generics/rawOverride/7062745/T7062745pos.java	Wed Jul 27 19:00:53 2011 +0100
     5.3 @@ -0,0 +1,42 @@
     5.4 +/*
     5.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
     5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.7 + *
     5.8 + * This code is free software; you can redistribute it and/or modify it
     5.9 + * under the terms of the GNU General Public License version 2 only, as
    5.10 + * published by the Free Software Foundation.
    5.11 + *
    5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    5.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    5.15 + * version 2 for more details (a copy is included in the LICENSE file that
    5.16 + * accompanied this code).
    5.17 + *
    5.18 + * You should have received a copy of the GNU General Public License version
    5.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    5.21 + *
    5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    5.23 + * or visit www.oracle.com if you need additional information or have any
    5.24 + * questions.
    5.25 + */
    5.26 +
    5.27 +/*
    5.28 + * @test
    5.29 + * @bug 7062745
    5.30 + * @summary  Regression: difference in overload resolution when two methods are maximally specific
    5.31 + *
    5.32 + * @compile T7062745pos.java
    5.33 + */
    5.34 +
    5.35 +import java.util.*;
    5.36 +
    5.37 +class T7062745pos {
    5.38 +    interface A { List<Number> getList(); }
    5.39 +    interface B { List getList(); }
    5.40 +    interface AB extends A, B {}
    5.41 +
    5.42 +    void test(AB ab) {
    5.43 +        Number n = ab.getList().get(1);
    5.44 +    }
    5.45 +}

mercurial