GenericOverrideTest.java revision 1519:5c956be64b9e
1/*
2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 7062745 8006694
27 * @summary  Regression: difference in overload resolution when two methods
28 *  are maximally specific
29 *  temporarily workaround combo tests are causing time out in several platforms
30 * @library ../../../lib
31 * @build JavacTestingAbstractThreadedTest
32 * @run main/othervm GenericOverrideTest
33 */
34
35// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
36// see JDK-8006746
37
38import java.net.URI;
39import java.util.Arrays;
40import javax.tools.Diagnostic;
41import javax.tools.JavaFileObject;
42import javax.tools.SimpleJavaFileObject;
43import com.sun.source.util.JavacTask;
44
45public class GenericOverrideTest
46    extends JavacTestingAbstractThreadedTest
47    implements Runnable {
48
49    enum SignatureKind {
50        NON_GENERIC(""),
51        GENERIC("<X>");
52
53        String paramStr;
54
55        private SignatureKind(String paramStr) {
56            this.paramStr = paramStr;
57        }
58    }
59
60    enum ReturnTypeKind {
61        LIST("List"),
62        ARRAYLIST("ArrayList");
63
64        String retStr;
65
66        private ReturnTypeKind(String retStr) {
67            this.retStr = retStr;
68        }
69
70        boolean moreSpecificThan(ReturnTypeKind that) {
71            switch (this) {
72                case LIST:
73                    return that == this;
74                case ARRAYLIST:
75                    return that == LIST || that == ARRAYLIST;
76                default: throw new AssertionError("Unexpected ret kind: " + this);
77            }
78        }
79    }
80
81    enum TypeArgumentKind {
82        NONE(""),
83        UNBOUND("<?>"),
84        INTEGER("<Number>"),
85        NUMBER("<Integer>"),
86        TYPEVAR("<X>");
87
88        String typeargStr;
89
90        private TypeArgumentKind(String typeargStr) {
91            this.typeargStr = typeargStr;
92        }
93
94        boolean compatibleWith(SignatureKind sig) {
95            switch (this) {
96                case TYPEVAR: return sig != SignatureKind.NON_GENERIC;
97                default: return true;
98            }
99        }
100
101        boolean moreSpecificThan(TypeArgumentKind that, boolean strict) {
102            switch (this) {
103                case NONE:
104                    return that == this || !strict;
105                case UNBOUND:
106                    return that == this || that == NONE;
107                case INTEGER:
108                case NUMBER:
109                case TYPEVAR:
110                    return that == this || that == NONE || that == UNBOUND;
111                default: throw new AssertionError("Unexpected typearg kind: " + this);
112            }
113        }
114
115        boolean assignableTo(TypeArgumentKind that, SignatureKind sig) {
116            switch (this) {
117                case NONE:
118                    //this case needs to workaround to javac's impl of 15.12.2.8 being too strict
119                    //ideally should be just 'return true' (see 7067746)
120                    return sig == SignatureKind.NON_GENERIC || that == NONE;
121                case UNBOUND:
122                    return that == this || that == NONE;
123                case INTEGER:
124                case NUMBER:
125                    return that == this || that == NONE || that == UNBOUND;
126                case TYPEVAR:
127                    return true;
128                default: throw new AssertionError("Unexpected typearg kind: " + this);
129            }
130        }
131    }
132
133    public static void main(String... args) throws Exception {
134        for (SignatureKind sig1 : SignatureKind.values()) {
135            for (ReturnTypeKind rt1 : ReturnTypeKind.values()) {
136                for (TypeArgumentKind ta1 : TypeArgumentKind.values()) {
137                    if (!ta1.compatibleWith(sig1)) continue;
138                    for (SignatureKind sig2 : SignatureKind.values()) {
139                        for (ReturnTypeKind rt2 : ReturnTypeKind.values()) {
140                            for (TypeArgumentKind ta2 : TypeArgumentKind.values()) {
141                                if (!ta2.compatibleWith(sig2)) continue;
142                                for (ReturnTypeKind rt3 : ReturnTypeKind.values()) {
143                                    for (TypeArgumentKind ta3 : TypeArgumentKind.values()) {
144                                        if (!ta3.compatibleWith(SignatureKind.NON_GENERIC))
145                                            continue;
146                                        pool.execute(
147                                                new GenericOverrideTest(sig1,
148                                                rt1, ta1, sig2, rt2,
149                                                ta2, rt3, ta3));
150                                    }
151                                }
152                            }
153                        }
154                    }
155                }
156            }
157        }
158
159        checkAfterExec();
160    }
161
162    SignatureKind sig1, sig2;
163    ReturnTypeKind rt1, rt2, rt3;
164    TypeArgumentKind ta1, ta2, ta3;
165    JavaSource source;
166    DiagnosticChecker diagChecker;
167
168    GenericOverrideTest(SignatureKind sig1, ReturnTypeKind rt1, TypeArgumentKind ta1,
169            SignatureKind sig2, ReturnTypeKind rt2, TypeArgumentKind ta2,
170            ReturnTypeKind rt3, TypeArgumentKind ta3) {
171        this.sig1 = sig1;
172        this.sig2 = sig2;
173        this.rt1 = rt1;
174        this.rt2 = rt2;
175        this.rt3 = rt3;
176        this.ta1 = ta1;
177        this.ta2 = ta2;
178        this.ta3 = ta3;
179        this.source = new JavaSource();
180        this.diagChecker = new DiagnosticChecker();
181    }
182
183    class JavaSource extends SimpleJavaFileObject {
184
185        String template = "import java.util.*;\n" +
186                          "interface A { #S1 #R1#TA1 m(); }\n" +
187                          "interface B { #S2 #R2#TA2 m(); }\n" +
188                          "interface AB extends A, B {}\n" +
189                          "class Test {\n" +
190                          "  void test(AB ab) { #R3#TA3 n = ab.m(); }\n" +
191                          "}";
192
193        String source;
194
195        public JavaSource() {
196            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
197            source = template.replace("#S1", sig1.paramStr).
198                    replace("#S2", sig2.paramStr).
199                    replace("#R1", rt1.retStr).
200                    replace("#R2", rt2.retStr).
201                    replace("#R3", rt3.retStr).
202                    replace("#TA1", ta1.typeargStr).
203                    replace("#TA2", ta2.typeargStr).
204                    replace("#TA3", ta3.typeargStr);
205        }
206
207        @Override
208        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
209            return source;
210        }
211    }
212
213    @Override
214    public void run() {
215        JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), diagChecker,
216                null, null, Arrays.asList(source));
217        try {
218            ct.analyze();
219        } catch (Throwable ex) {
220            throw new AssertionError("Error thrown when compiling the following code:\n" +
221                    source.getCharContent(true));
222        }
223        check();
224    }
225
226    void check() {
227        checkCount.incrementAndGet();
228
229        boolean errorExpected = false;
230        int mostSpecific = 0;
231
232        //first check that either |R1| <: |R2| or |R2| <: |R1|
233        if (rt1 != rt2) {
234            if (!rt1.moreSpecificThan(rt2) &&
235                    !rt2.moreSpecificThan(rt1)) {
236                errorExpected = true;
237            } else {
238                mostSpecific = rt1.moreSpecificThan(rt2) ? 1 : 2;
239            }
240        }
241
242        //check that either TA1 <= TA2 or TA2 <= TA1 (unless most specific return found above is raw)
243        if (!errorExpected) {
244            if (ta1 != ta2) {
245                boolean useStrictCheck = ta1.moreSpecificThan(ta2, true) ||
246                        ta2.moreSpecificThan(ta1, true);
247                if (!ta1.moreSpecificThan(ta2, useStrictCheck) &&
248                        !ta2.moreSpecificThan(ta1, useStrictCheck)) {
249                    errorExpected = true;
250                } else {
251                    int mostSpecific2 = ta1.moreSpecificThan(ta2, useStrictCheck) ? 1 : 2;
252                    if (mostSpecific != 0 && mostSpecific2 != mostSpecific) {
253                        errorExpected = mostSpecific == 1 ?
254                                ta1 != TypeArgumentKind.NONE :
255                                ta2 != TypeArgumentKind.NONE;
256                    } else {
257                        mostSpecific = mostSpecific2;
258                    }
259                }
260            } else if (mostSpecific == 0) {
261                //when no signature is better than the other, an arbitrary choice
262                //must be made - javac always picks the second signature
263                mostSpecific = 2;
264            }
265        }
266
267        //finally, check that most specific return type is compatible with expected type
268        if (!errorExpected) {
269            ReturnTypeKind msrt = mostSpecific == 1 ? rt1 : rt2;
270            TypeArgumentKind msta = mostSpecific == 1 ? ta1 : ta2;
271            SignatureKind mssig = mostSpecific == 1 ? sig1 : sig2;
272
273            if (!msrt.moreSpecificThan(rt3) ||
274                    !msta.assignableTo(ta3, mssig)) {
275                errorExpected = true;
276            }
277        }
278
279        if (errorExpected != diagChecker.errorFound) {
280            throw new Error("invalid diagnostics for source:\n" +
281                source.getCharContent(true) +
282                "\nFound error: " + diagChecker.errorFound +
283                "\nExpected error: " + errorExpected);
284        }
285    }
286
287    static class DiagnosticChecker
288        implements javax.tools.DiagnosticListener<JavaFileObject> {
289
290        boolean errorFound;
291
292        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
293            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
294                errorFound = true;
295            }
296        }
297    }
298
299}
300