/* * 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 {} interface B extends A {} interface C extends A {} interface D extends A {} interface E extends C {} interface F extends A> {} interface G extends A {} interface H extends A {} interface I extends H {} class Test { } interface RecursiveTest1Interface> { } interface RecursiveTest1Use> extends RecursiveTest1Interface { } interface RecursiveTest2Interface { } interface RecursiveTest2Use, Y> extends RecursiveTest2Interface { } """; 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", "B", "B"); checkInferedType("A", "C", "C"); checkInferedType("A", "D", "D"); checkInferedType("A", "E", "E"); checkInferedType("A", "F", null); checkInferedType("A", "G", null); // doesn't check bounds checkInferedType("A", "H", "H"); checkInferedType("A", "I", "I"); checkInferedType("A>", "B", "B>"); checkInferedType("A>", "C", "C,?>"); checkInferedType("A>", "F", "F"); checkInferedType("A>", "H", null); checkInferedType("A>", "I", null); checkInferedType("C", "E", "E"); checkInferedType("C", "E", null); checkInferedType("C, A>", "E", "E>"); checkInferedType("C, A>", "E", "E>"); 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", "B", "B"); checkInferedType("A", "C", "C"); checkInferedType("A", "D", "D"); checkInferedType("A", "E", "E"); checkInferedType("A", "F", null); checkInferedType("A", "G", "G"); // should infer an intersection bound checkInferedType("A", "H", null); checkInferedType("A", "I", null); checkInferedType("A>", "F", "F"); // inference doesn't recur on bounds checks checkInferedType("A>", "F", "F"); // inference doesn't recur on bounds checks checkInferedType("C", "E", "E"); // doesn't know how to mix types and wildcards checkInferedType("C", "E", "E"); // doesn't know how to mix types and wildcards checkInferedType("C", "E", "E"); checkInferedType("C", "E", "E"); checkInferedType("C", "E", "E"); checkInferedType("C", "E", "E"); checkInferedType("C", "E", "E"); // should infer an intersection bound checkInferedType("C", "E", "E"); // doesn't know how to mix lower/upper checkInferedType("C", "E", "E"); checkInferedType("C, ? super C>", "E", "E>"); // doesn't do lub checkInferedType("H", "I", "I"); checkInferedType("B", "C", null); // no sideways casts checkInferedType("A", "B", "B"); checkInferedType("RecursiveTest1Interface", "RecursiveTest1Use", "RecursiveTest1Use&RecursiveTest1Interface>>"); checkInferedType("RecursiveTest2Interface", "RecursiveTest2Use", "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); } }