5b6b514441
Reviewed-by: jlaskey, darcy
205 lines
9.8 KiB
Java
205 lines
9.8 KiB
Java
/*
|
|
* Copyright (c) 2022, 2024, 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 Verify Infer.instantiatePatternType provides correct results
|
|
* @library /tools/lib/types
|
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
|
* jdk.compiler/com.sun.tools.javac.code
|
|
* jdk.compiler/com.sun.tools.javac.comp
|
|
* jdk.compiler/com.sun.tools.javac.model
|
|
* jdk.compiler/com.sun.tools.javac.parser
|
|
* jdk.compiler/com.sun.tools.javac.tree
|
|
* jdk.compiler/com.sun.tools.javac.util
|
|
* @run main InferenceUnitTest
|
|
*/
|
|
|
|
import com.sun.tools.javac.api.JavacTaskImpl;
|
|
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
|
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
|
|
|
import com.sun.tools.javac.code.Type;
|
|
import com.sun.tools.javac.code.Types;
|
|
import com.sun.tools.javac.comp.Attr;
|
|
import com.sun.tools.javac.comp.Infer;
|
|
import com.sun.tools.javac.model.JavacElements;
|
|
import com.sun.tools.javac.parser.ParserFactory;
|
|
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
|
import com.sun.tools.javac.util.Context;
|
|
import com.sun.tools.javac.util.List;
|
|
import java.net.URI;
|
|
import java.util.Objects;
|
|
import javax.tools.JavaCompiler;
|
|
import javax.tools.SimpleJavaFileObject;
|
|
import javax.tools.ToolProvider;
|
|
|
|
public class InferenceUnitTest {
|
|
|
|
Context context;
|
|
Infer infer;
|
|
Types types;
|
|
|
|
public static void main(String... args) throws Exception {
|
|
new InferenceUnitTest().runAll();
|
|
}
|
|
|
|
void runAll() {
|
|
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
|
String source = """
|
|
interface A<T> {}
|
|
interface B<T> extends A<T> {}
|
|
interface C<X,Y> extends A<X> {}
|
|
interface D<X,Y> extends A<Y> {}
|
|
interface E<T> extends C<T,T> {}
|
|
interface F<T> extends A<B<T>> {}
|
|
interface G<T extends Number> extends A<T> {}
|
|
interface H extends A<String> {}
|
|
interface I<T> extends H {}
|
|
class Test<T1 extends CharSequence&Runnable, T2 extends Number> {
|
|
}
|
|
interface RecursiveTest1Interface<IB extends RecursiveTest1Interface<IB>> { }
|
|
interface RecursiveTest1Use<BB extends RecursiveTest1Use<BB>> extends RecursiveTest1Interface<BB> { }
|
|
interface RecursiveTest2Interface<X> { }
|
|
interface RecursiveTest2Use<X extends RecursiveTest2Use<X, Y>, Y> extends RecursiveTest2Interface<Y> { }
|
|
""";
|
|
|
|
JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, null, null, List.of(SimpleJavaFileObject.forSource(URI.create("mem://Test.java"), source)));
|
|
task.enter();
|
|
context = task.getContext();
|
|
infer = Infer.instance(context);
|
|
types = Types.instance(context);
|
|
|
|
checkInferedType("A<String>", "B", "B<java.lang.String>");
|
|
checkInferedType("A<String>", "C", "C<java.lang.String,?>");
|
|
checkInferedType("A<String>", "D", "D<?,java.lang.String>");
|
|
checkInferedType("A<String>", "E", "E<java.lang.String>");
|
|
checkInferedType("A<String>", "F", null);
|
|
checkInferedType("A<String>", "G", null); // doesn't check bounds
|
|
checkInferedType("A<String>", "H", "H");
|
|
checkInferedType("A<String>", "I", "I<?>");
|
|
|
|
checkInferedType("A<B<String>>", "B", "B<B<java.lang.String>>");
|
|
checkInferedType("A<B<String>>", "C", "C<B<java.lang.String>,?>");
|
|
checkInferedType("A<B<String>>", "F", "F<java.lang.String>");
|
|
checkInferedType("A<B<String>>", "H", null);
|
|
checkInferedType("A<B<String>>", "I", null);
|
|
|
|
checkInferedType("C<String, String>", "E", "E<java.lang.String>");
|
|
checkInferedType("C<String, Integer>", "E", null);
|
|
checkInferedType("C<A<?>, A<?>>", "E", "E<A<?>>");
|
|
checkInferedType("C<A<? extends Object>, A<?>>", "E", "E<A<? extends java.lang.Object>>");
|
|
|
|
if (false) {
|
|
checkInferedType("A", "B", "B");
|
|
checkInferedType("A", "C", "C");
|
|
checkInferedType("A", "D", "D");
|
|
checkInferedType("A", "E", "E");
|
|
checkInferedType("A", "F", "F");
|
|
checkInferedType("A", "G", "G");
|
|
checkInferedType("A", "H", "H");
|
|
}
|
|
|
|
checkInferedType("A", "I", "I<?>"); // always erases if input is raw
|
|
|
|
checkInferedType("A<?>", "B", "B<?>");
|
|
checkInferedType("A<?>", "C", "C<?,?>");
|
|
checkInferedType("A<?>", "D", "D<?,?>");
|
|
checkInferedType("A<?>", "E", "E<?>");
|
|
checkInferedType("A<?>", "F", "F<?>");
|
|
checkInferedType("A<?>", "G", "G<?>");
|
|
checkInferedType("A<?>", "H", "H");
|
|
checkInferedType("A<?>", "I", "I<?>");
|
|
|
|
checkInferedType("A<? extends Runnable>", "B", "B<? extends java.lang.Runnable>");
|
|
checkInferedType("A<? extends Runnable>", "C", "C<? extends java.lang.Runnable,?>");
|
|
checkInferedType("A<? extends Runnable>", "D", "D<?,? extends java.lang.Runnable>");
|
|
checkInferedType("A<? extends Runnable>", "E", "E<? extends java.lang.Runnable>");
|
|
checkInferedType("A<? extends Runnable>", "F", null);
|
|
checkInferedType("A<? extends Runnable>", "G", "G<? extends java.lang.Number&java.lang.Runnable>"); // should infer an intersection bound
|
|
checkInferedType("A<? extends Runnable>", "H", null);
|
|
checkInferedType("A<? extends Runnable>", "I", null);
|
|
|
|
checkInferedType("A<? extends B<String>>", "F", "F<java.lang.String>"); // inference doesn't recur on bounds checks
|
|
checkInferedType("A<? extends A<String>>", "F", "F<java.lang.String>"); // inference doesn't recur on bounds checks
|
|
|
|
checkInferedType("C<? extends Number, Integer>", "E", "E<java.lang.Integer>"); // doesn't know how to mix types and wildcards
|
|
checkInferedType("C<Integer, ? extends Number>", "E", "E<java.lang.Integer>"); // doesn't know how to mix types and wildcards
|
|
checkInferedType("C<?, ? extends Number>", "E", "E<? extends java.lang.Number>");
|
|
checkInferedType("C<? extends Number, ?>", "E", "E<? extends java.lang.Number>");
|
|
|
|
checkInferedType("C<? extends Number, ? extends Integer>", "E", "E<? extends java.lang.Integer>");
|
|
checkInferedType("C<? extends Integer, ? extends Number>", "E", "E<? extends java.lang.Integer>");
|
|
checkInferedType("C<? extends Runnable, ? extends Cloneable>", "E", "E<? extends java.lang.Object&java.lang.Cloneable&java.lang.Runnable>"); // should infer an intersection bound
|
|
checkInferedType("C<? extends Number, ? super Integer>", "E", "E<? extends java.lang.Number>"); // doesn't know how to mix lower/upper
|
|
checkInferedType("C<? super Integer, ? super Number>", "E", "E<? super java.lang.Number>");
|
|
checkInferedType("C<? super B<String>, ? super C<String,String>>", "E", "E<? super A<java.lang.String>>"); // doesn't do lub
|
|
|
|
checkInferedType("H", "I", "I<?>");
|
|
|
|
checkInferedType("B<String>", "C", null); // no sideways casts
|
|
|
|
checkInferedType("A<T1>", "B", "B<T1>");
|
|
checkInferedType("RecursiveTest1Interface<?>", "RecursiveTest1Use", "RecursiveTest1Use<? extends java.lang.Object&RecursiveTest1Use<?>&RecursiveTest1Interface<? extends RecursiveTest1Use<?>>>");
|
|
checkInferedType("RecursiveTest2Interface<?>", "RecursiveTest2Use", "RecursiveTest2Use<? extends RecursiveTest2Use<?,?>,?>");
|
|
}
|
|
|
|
private void checkInferedType(String base, String test, String expected) {
|
|
Type baseType = parseType(base);
|
|
TypeSymbol testType = parseType(test).tsym;
|
|
Type actualType = infer.instantiatePatternType(baseType, testType);
|
|
String actualTypeString = actualType != null ? actualType.toString() : null;
|
|
if (!Objects.equals(expected, actualTypeString)) {
|
|
error("Unexpected type, expected: " + expected + ", got: " + actualTypeString);
|
|
}
|
|
}
|
|
Type parseType(String spec) {
|
|
ParserFactory fact = ParserFactory.instance(context);
|
|
JCExpression specTypeTree = fact.newParser(spec, false, false, false).parseType();
|
|
Attr attr = Attr.instance(context);
|
|
JavacElements elementUtils = JavacElements.instance(context);
|
|
ClassSymbol testClass = elementUtils.getTypeElement("Test");
|
|
return attr.attribType(specTypeTree, testClass);
|
|
}
|
|
|
|
/** assert that 's' is the same type as 't' */
|
|
public void assertSameType(Type s, Type t) {
|
|
assertSameType(s, t, true);
|
|
}
|
|
|
|
/** assert that 's' is/is not the same type as 't' */
|
|
public void assertSameType(Type s, Type t, boolean expected) {
|
|
if (types.isSameType(s, t) != expected) {
|
|
String msg = expected ?
|
|
" is not the same type as " :
|
|
" is the same type as ";
|
|
error(s + msg + t);
|
|
}
|
|
}
|
|
|
|
private void error(String msg) {
|
|
throw new AssertionError("Unexpected result: " + msg);
|
|
}
|
|
|
|
}
|