8159970: javac, JLS8 18.2.4 is not completely implemented by the compiler
Reviewed-by: mcimadamore
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java Tue Jun 28 15:29:20 2016 -0700
@@ -1126,10 +1126,13 @@
@Override
public Boolean visitWildcardType(WildcardType t, Type s) {
- if (s.isPartial())
- return visit(s, t);
- else
+ if (!s.hasTag(WILDCARD)) {
return false;
+ } else {
+ WildcardType t2 = (WildcardType)s;
+ return (t.kind == t2.kind || (t.isExtendsBound() && s.isExtendsBound())) &&
+ isSameType(t.type, t2.type, true);
+ }
}
@Override
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java Tue Jun 28 15:29:20 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -67,7 +67,7 @@
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
-class InferenceContext {
+public class InferenceContext {
/** list of inference vars as undet vars */
List<Type> undetvars;
@@ -112,6 +112,13 @@
}
/**
+ * returns the list of undetermined variables in this inference context
+ */
+ public List<Type> undetVars() {
+ return undetvars;
+ }
+
+ /**
* returns the list of uninstantiated variables (as type-variables) in this
* inference context
*/
@@ -208,7 +215,7 @@
* undet vars (used ahead of subtyping/compatibility checks to allow propagation
* of inference constraints).
*/
- final Type asUndetVar(Type t) {
+ public final Type asUndetVar(Type t) {
return types.subst(t, inferencevars, undetvars);
}
@@ -286,7 +293,7 @@
/**
* Save the state of this inference context
*/
- List<Type> save() {
+ public List<Type> save() {
ListBuffer<Type> buf = new ListBuffer<>();
for (Type t : undetvars) {
buf.add(((UndetVar)t).dup(infer.types));
@@ -298,7 +305,7 @@
* Consider that the number of saved undetermined variables can be different to the current
* amount. This is because new captured variables could have been added.
*/
- void rollback(List<Type> saved_undet) {
+ public void rollback(List<Type> saved_undet) {
Assert.check(saved_undet != null);
//restore bounds (note: we need to preserve the old instances)
ListBuffer<Type> newUndetVars = new ListBuffer<>();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T8159970/TypeEqualityInInferenceTest.java Tue Jun 28 15:29:20 2016 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, 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
+ * @bug 8159970
+ * @summary javac, JLS8 18.2.4 is not completely implemented by the compiler
+ * @library /tools/lib/types
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.code
+ * jdk.compiler/com.sun.tools.javac.comp
+ * jdk.compiler/com.sun.tools.javac.tree
+ * jdk.compiler/com.sun.tools.javac.util
+ * jdk.compiler/com.sun.tools.javac.file
+ * @build TypeHarness
+ * @run main TypeEqualityInInferenceTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.UndetVar;
+import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
+import com.sun.tools.javac.util.Assert;
+
+public class TypeEqualityInInferenceTest extends TypeHarness {
+ StrToTypeFactory strToTypeFactory;
+
+ public static void main(String... args) throws Exception {
+ new TypeEqualityInInferenceTest().runAll();
+ }
+
+ void runAll() {
+ List<String> imports = new ArrayList<>();
+ imports.add("java.util.*");
+ List<String> typeVars = new ArrayList<>();
+ typeVars.add("T");
+ strToTypeFactory = new StrToTypeFactory(null, imports, typeVars);
+
+ runTest("List<? extends T>", "List<? extends String>", predef.stringType);
+ runTest("List<? extends T>", "List<?>", predef.objectType);
+ runTest("List<? super T>", "List<? super String>", predef.stringType);
+ }
+
+ void runTest(String freeTypeStr, String typeStr, Type equalityBoundType) {
+ Type freeType = strToTypeFactory.getType(freeTypeStr);
+ Type aType = strToTypeFactory.getType(typeStr);
+
+ withInferenceContext(strToTypeFactory.getTypeVars(), inferenceContext -> {
+ assertSameType(inferenceContext.asUndetVar(freeType), aType);
+ UndetVar undetVarForT = (UndetVar)inferenceContext.undetVars().head;
+ checkEqualityBound(undetVarForT, equalityBoundType);
+ });
+
+ withInferenceContext(strToTypeFactory.getTypeVars(), inferenceContext -> {
+ assertSameType(aType, inferenceContext.asUndetVar(freeType));
+ UndetVar undetVarForT = (UndetVar)inferenceContext.undetVars().head;
+ checkEqualityBound(undetVarForT, equalityBoundType);
+ });
+ }
+
+ void checkEqualityBound(UndetVar uv, Type boundType) {
+ com.sun.tools.javac.util.List<Type> equalBounds = uv.getBounds(InferenceBound.EQ);
+ Assert.check(!equalBounds.isEmpty() && equalBounds.length() == 1,
+ "undetVar must have only one equality bound");
+ Type bound = equalBounds.head;
+ Assert.check(bound == boundType, "equal bound must be of type " + boundType);
+ }
+}
--- a/langtools/test/tools/javac/types/BoxingConversionTest.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/test/tools/javac/types/BoxingConversionTest.java Tue Jun 28 15:29:20 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -26,17 +26,18 @@
* @bug 7006109
* @summary Add test library to simplify the task of writing automated type-system tests
* @author mcimadamore
- * @library .
+ * @library /tools/lib/types
* @modules jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.util
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.tree
+ * @build TypeHarness
* @run main BoxingConversionTest
*/
import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.code.Symbol.*;
import java.lang.reflect.Array;
import java.util.EnumSet;
--- a/langtools/test/tools/javac/types/CastTest.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/test/tools/javac/types/CastTest.java Tue Jun 28 15:29:20 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -26,11 +26,14 @@
* @bug 7006109
* @summary Add test library to simplify the task of writing automated type-system tests
* @author mcimadamore
- * @library .
+ * @library /tools/lib/types
* @modules jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.util
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.tree
+ * @build TypeHarness
* @run main CastTest
*/
--- a/langtools/test/tools/javac/types/GenericTypeWellFormednessTest.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/test/tools/javac/types/GenericTypeWellFormednessTest.java Tue Jun 28 15:29:20 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -26,11 +26,14 @@
* @bug 7007432 7006109
* @summary Test generic types well-formedness
* @author mcimadamore
- * @library .
+ * @library /tools/lib/types
* @modules jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.util
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.tree
+ * @build TypeHarness
* @run main GenericTypeWellFormednessTest
*/
--- a/langtools/test/tools/javac/types/PrimitiveConversionTest.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/test/tools/javac/types/PrimitiveConversionTest.java Tue Jun 28 15:29:20 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -26,17 +26,19 @@
* @bug 7006109
* @summary Add test library to simplify the task of writing automated type-system tests
* @author mcimadamore
- * @library .
+ * @library /tools/lib/types
* @modules jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.comp
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.util
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.tree
+ * @build TypeHarness
* @run main PrimitiveConversionTest
*/
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.code.Symbol.*;
import java.lang.reflect.Array;
import java.util.EnumSet;
--- a/langtools/test/tools/javac/types/TestComparisons.java Tue Jun 28 15:46:52 2016 +0200
+++ b/langtools/test/tools/javac/types/TestComparisons.java Tue Jun 28 15:29:20 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -29,8 +29,6 @@
*/
import java.io.*;
-import java.lang.reflect.Array;
-import java.util.EnumSet;
public class TestComparisons {
--- a/langtools/test/tools/javac/types/TypeHarness.java Tue Jun 28 15:46:52 2016 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,333 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, 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.
- */
-
-import com.sun.tools.javac.code.BoundKind;
-import com.sun.tools.javac.code.Flags;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.code.Types;
-import com.sun.tools.javac.code.Symtab;
-import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.code.Symbol.*;
-import com.sun.tools.javac.comp.Check;
-import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.ListBuffer;
-import com.sun.tools.javac.util.Name;
-import com.sun.tools.javac.util.Names;
-import com.sun.tools.javac.file.JavacFileManager;
-
-/**
- * Test harness whose goal is to simplify the task of writing type-system
- * regression test. It provides functionalities to build custom types as well
- * as to access the underlying javac's symbol table in order to retrieve
- * predefined types. Among the features supported by the harness are: type
- * substitution, type containment, subtyping, cast-conversion, assigment
- * conversion.
- *
- * This class is meant to be a common super class for all concrete type test
- * classes. A subclass can access the type-factory and the test methods so as
- * to write compact tests. An example is reported below:
- *
- * <pre>
- * Type X = fac.TypeVariable();
- * Type Y = fac.TypeVariable();
- * Type A_X_Y = fac.Class(0, X, Y);
- * Type A_Obj_Obj = fac.Class(0,
- * predef.objectType,
- * predef.objectType);
- * checkSameType(A_Obj_Obj, subst(A_X_Y,
- * Mapping(X, predef.objectType),
- * Mapping(Y, predef.objectType)));
- * </pre>
- *
- * The above code is used to create two class types, namely {@code A<X,Y>} and
- * {@code A<Object,Object>} where both {@code X} and {@code Y} are type-variables.
- * The code then verifies that {@code [X:=Object,Y:=Object]A<X,Y> == A<Object,Object>}.
- *
- * @author mcimadamore
- */
-public class TypeHarness {
-
- protected Types types;
- protected Check chk;
- protected Symtab predef;
- protected Names names;
- protected Factory fac;
-
- protected TypeHarness() {
- Context ctx = new Context();
- JavacFileManager.preRegister(ctx);
- types = Types.instance(ctx);
- chk = Check.instance(ctx);
- predef = Symtab.instance(ctx);
- names = Names.instance(ctx);
- fac = new Factory();
- }
-
- // <editor-fold defaultstate="collapsed" desc="type assertions">
-
- /** assert that 's' is a subtype of 't' */
- public void assertSubtype(Type s, Type t) {
- assertSubtype(s, t, true);
- }
-
- /** assert that 's' is/is not a subtype of 't' */
- public void assertSubtype(Type s, Type t, boolean expected) {
- if (types.isSubtype(s, t) != expected) {
- String msg = expected ?
- " is not a subtype of " :
- " is a subtype of ";
- error(s + msg + t);
- }
- }
-
- /** 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);
- }
- }
-
- /** assert that 's' is castable to 't' */
- public void assertCastable(Type s, Type t) {
- assertCastable(s, t, true);
- }
-
- /** assert that 's' is/is not castable to 't' */
- public void assertCastable(Type s, Type t, boolean expected) {
- if (types.isCastable(s, t) != expected) {
- String msg = expected ?
- " is not castable to " :
- " is castable to ";
- error(s + msg + t);
- }
- }
-
- /** assert that 's' is convertible (method invocation conversion) to 't' */
- public void assertConvertible(Type s, Type t) {
- assertCastable(s, t, true);
- }
-
- /** assert that 's' is/is not convertible (method invocation conversion) to 't' */
- public void assertConvertible(Type s, Type t, boolean expected) {
- if (types.isConvertible(s, t) != expected) {
- String msg = expected ?
- " is not convertible to " :
- " is convertible to ";
- error(s + msg + t);
- }
- }
-
- /** assert that 's' is assignable to 't' */
- public void assertAssignable(Type s, Type t) {
- assertCastable(s, t, true);
- }
-
- /** assert that 's' is/is not assignable to 't' */
- public void assertAssignable(Type s, Type t, boolean expected) {
- if (types.isAssignable(s, t) != expected) {
- String msg = expected ?
- " is not assignable to " :
- " is assignable to ";
- error(s + msg + t);
- }
- }
-
- /** assert that generic type 't' is well-formed */
- public void assertValidGenericType(Type t) {
- assertValidGenericType(t, true);
- }
-
- /** assert that 's' is/is not assignable to 't' */
- public void assertValidGenericType(Type t, boolean expected) {
- if (chk.checkValidGenericType(t) != expected) {
- String msg = expected ?
- " is not a valid generic type" :
- " is a valid generic type";
- error(t + msg + " " + t.tsym.type);
- }
- }
- // </editor-fold>
-
- private void error(String msg) {
- throw new AssertionError("Unexpected result: " + msg);
- }
-
- // <editor-fold defaultstate="collapsed" desc="type functions">
-
- /** compute the erasure of a type 't' */
- public Type erasure(Type t) {
- return types.erasure(t);
- }
-
- /** compute the capture of a type 't' */
- public Type capture(Type t) {
- return types.capture(t);
- }
-
- /** compute the boxed type associated with 't' */
- public Type box(Type t) {
- if (!t.isPrimitive()) {
- throw new AssertionError("Cannot box non-primitive type: " + t);
- }
- return types.boxedClass(t).type;
- }
-
- /** compute the unboxed type associated with 't' */
- public Type unbox(Type t) {
- Type u = types.unboxedType(t);
- if (t == null) {
- throw new AssertionError("Cannot unbox reference type: " + t);
- } else {
- return u;
- }
- }
-
- /** compute a type substitution on 't' given a list of type mappings */
- public Type subst(Type t, Mapping... maps) {
- ListBuffer<Type> from = new ListBuffer<>();
- ListBuffer<Type> to = new ListBuffer<>();
- for (Mapping tm : maps) {
- from.append(tm.from);
- to.append(tm.to);
- }
- return types.subst(t, from.toList(), to.toList());
- }
-
- /** create a fresh type mapping from a type to another */
- public Mapping Mapping(Type from, Type to) {
- return new Mapping(from, to);
- }
-
- public static class Mapping {
- Type from;
- Type to;
- private Mapping(Type from, Type to) {
- this.from = from;
- this.to = to;
- }
- }
- // </editor-fold>
-
- // <editor-fold defaultstate="collapsed" desc="type factory">
-
- /**
- * This class is used to create Java types in a simple way. All main
- * kinds of type are supported: primitive, reference, non-denotable. The
- * factory also supports creation of constant types (used by the compiler
- * to represent the type of a literal).
- */
- public class Factory {
-
- private int synthNameCount = 0;
-
- private Name syntheticName() {
- return names.fromString("A$" + synthNameCount++);
- }
-
- public ClassType Class(long flags, Type... typeArgs) {
- ClassSymbol csym = new ClassSymbol(flags, syntheticName(), predef.noSymbol);
- csym.type = new ClassType(Type.noType, List.from(typeArgs), csym);
- ((ClassType)csym.type).supertype_field = predef.objectType;
- return (ClassType)csym.type;
- }
-
- public ClassType Class(Type... typeArgs) {
- return Class(0, typeArgs);
- }
-
- public ClassType Interface(Type... typeArgs) {
- return Class(Flags.INTERFACE, typeArgs);
- }
-
- public ClassType Interface(long flags, Type... typeArgs) {
- return Class(Flags.INTERFACE | flags, typeArgs);
- }
-
- public Type Constant(byte b) {
- return predef.byteType.constType(b);
- }
-
- public Type Constant(short s) {
- return predef.shortType.constType(s);
- }
-
- public Type Constant(int i) {
- return predef.intType.constType(i);
- }
-
- public Type Constant(long l) {
- return predef.longType.constType(l);
- }
-
- public Type Constant(float f) {
- return predef.floatType.constType(f);
- }
-
- public Type Constant(double d) {
- return predef.doubleType.constType(d);
- }
-
- public Type Constant(char c) {
- return predef.charType.constType(c + 0);
- }
-
- public ArrayType Array(Type elemType) {
- return new ArrayType(elemType, predef.arrayClass);
- }
-
- public TypeVar TypeVariable() {
- return TypeVariable(predef.objectType);
- }
-
- public TypeVar TypeVariable(Type bound) {
- TypeSymbol tvsym = new TypeVariableSymbol(0, syntheticName(), null, predef.noSymbol);
- tvsym.type = new TypeVar(tvsym, bound, null);
- return (TypeVar)tvsym.type;
- }
-
- public WildcardType Wildcard(BoundKind bk, Type bound) {
- return new WildcardType(bound, bk, predef.boundClass);
- }
-
- public CapturedType CapturedVariable(Type upper, Type lower) {
- return new CapturedType(syntheticName(), predef.noSymbol, upper, lower, null);
- }
-
- public ClassType Intersection(Type classBound, Type... intfBounds) {
- ClassType ct = Class(Flags.COMPOUND);
- ct.supertype_field = classBound;
- ct.interfaces_field = List.from(intfBounds);
- return ct;
- }
- }
- // </editor-fold>
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/lib/types/TypeHarness.java Tue Jun 28 15:29:20 2016 -0700
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2010, 2016, 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.
+ */
+
+import java.net.URI;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+
+import com.sun.tools.javac.code.BoundKind;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.*;
+import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.comp.Attr;
+import com.sun.tools.javac.comp.Check;
+import com.sun.tools.javac.comp.Infer;
+import com.sun.tools.javac.comp.InferenceContext;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.Abort;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+
+import static com.sun.tools.javac.util.List.*;
+
+/**
+ * Test harness whose goal is to simplify the task of writing type-system
+ * regression test. It provides functionalities to build custom types as well
+ * as to access the underlying javac's symbol table in order to retrieve
+ * predefined types. Among the features supported by the harness are: type
+ * substitution, type containment, subtyping, cast-conversion, assigment
+ * conversion.
+ *
+ * This class is meant to be a common super class for all concrete type test
+ * classes. A subclass can access the type-factory and the test methods so as
+ * to write compact tests. An example is reported below:
+ *
+ * <pre>
+ * Type X = fac.TypeVariable();
+ * Type Y = fac.TypeVariable();
+ * Type A_X_Y = fac.Class(0, X, Y);
+ * Type A_Obj_Obj = fac.Class(0,
+ * predef.objectType,
+ * predef.objectType);
+ * checkSameType(A_Obj_Obj, subst(A_X_Y,
+ * Mapping(X, predef.objectType),
+ * Mapping(Y, predef.objectType)));
+ * </pre>
+ *
+ * The above code is used to create two class types, namely {@code A<X,Y>} and
+ * {@code A<Object,Object>} where both {@code X} and {@code Y} are type-variables.
+ * The code then verifies that {@code [X:=Object,Y:=Object]A<X,Y> == A<Object,Object>}.
+ *
+ * @author mcimadamore
+ */
+public class TypeHarness {
+
+ protected Types types;
+ protected Check chk;
+ protected Symtab predef;
+ protected Names names;
+ protected ReusableJavaCompiler tool;
+ protected Infer infer;
+
+ protected Factory fac;
+
+ protected TypeHarness() {
+ Context ctx = new Context();
+ JavacFileManager.preRegister(ctx);
+ MyAttr.preRegister(ctx);
+ tool = new ReusableJavaCompiler(ctx);
+ types = Types.instance(ctx);
+ infer = Infer.instance(ctx);
+ chk = Check.instance(ctx);
+ predef = Symtab.instance(ctx);
+ names = Names.instance(ctx);
+ fac = new Factory();
+ }
+
+ // <editor-fold defaultstate="collapsed" desc="type assertions">
+
+ /** assert that 's' is a subtype of 't' */
+ public void assertSubtype(Type s, Type t) {
+ assertSubtype(s, t, true);
+ }
+
+ /** assert that 's' is/is not a subtype of 't' */
+ public void assertSubtype(Type s, Type t, boolean expected) {
+ if (types.isSubtype(s, t) != expected) {
+ String msg = expected ?
+ " is not a subtype of " :
+ " is a subtype of ";
+ error(s + msg + t);
+ }
+ }
+
+ /** 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);
+ }
+ }
+
+ /** assert that 's' is castable to 't' */
+ public void assertCastable(Type s, Type t) {
+ assertCastable(s, t, true);
+ }
+
+ /** assert that 's' is/is not castable to 't' */
+ public void assertCastable(Type s, Type t, boolean expected) {
+ if (types.isCastable(s, t) != expected) {
+ String msg = expected ?
+ " is not castable to " :
+ " is castable to ";
+ error(s + msg + t);
+ }
+ }
+
+ /** assert that 's' is convertible (method invocation conversion) to 't' */
+ public void assertConvertible(Type s, Type t) {
+ assertCastable(s, t, true);
+ }
+
+ /** assert that 's' is/is not convertible (method invocation conversion) to 't' */
+ public void assertConvertible(Type s, Type t, boolean expected) {
+ if (types.isConvertible(s, t) != expected) {
+ String msg = expected ?
+ " is not convertible to " :
+ " is convertible to ";
+ error(s + msg + t);
+ }
+ }
+
+ /** assert that 's' is assignable to 't' */
+ public void assertAssignable(Type s, Type t) {
+ assertCastable(s, t, true);
+ }
+
+ /** assert that 's' is/is not assignable to 't' */
+ public void assertAssignable(Type s, Type t, boolean expected) {
+ if (types.isAssignable(s, t) != expected) {
+ String msg = expected ?
+ " is not assignable to " :
+ " is assignable to ";
+ error(s + msg + t);
+ }
+ }
+
+ /** assert that generic type 't' is well-formed */
+ public void assertValidGenericType(Type t) {
+ assertValidGenericType(t, true);
+ }
+
+ /** assert that 's' is/is not assignable to 't' */
+ public void assertValidGenericType(Type t, boolean expected) {
+ if (chk.checkValidGenericType(t) != expected) {
+ String msg = expected ?
+ " is not a valid generic type" :
+ " is a valid generic type";
+ error(t + msg + " " + t.tsym.type);
+ }
+ }
+ // </editor-fold>
+
+ /** Creates an inference context given a list of type variables and performs the given action on it.
+ * The intention is to provide a way to do unit testing on inference contexts.
+ * @param typeVars a list of type variables to create the inference context from
+ * @param consumer the action to be performed on the inference context
+ */
+ protected void withInferenceContext(List<Type> typeVars, Consumer<InferenceContext> consumer) {
+ Assert.check(!typeVars.isEmpty(), "invalid parameter, empty type variables list");
+ ListBuffer undetVarsBuffer = new ListBuffer();
+ typeVars.stream().map((tv) -> new UndetVar((TypeVar)tv, null, types)).forEach((undetVar) -> {
+ undetVarsBuffer.add(undetVar);
+ });
+ List<Type> undetVarsList = undetVarsBuffer.toList();
+ InferenceContext inferenceContext = new InferenceContext(infer, nil(), undetVarsList);
+ inferenceContext.rollback(undetVarsList);
+ consumer.accept(inferenceContext);
+ }
+
+ private void error(String msg) {
+ throw new AssertionError("Unexpected result: " + msg);
+ }
+
+ // <editor-fold defaultstate="collapsed" desc="type functions">
+
+ /** compute the erasure of a type 't' */
+ public Type erasure(Type t) {
+ return types.erasure(t);
+ }
+
+ /** compute the capture of a type 't' */
+ public Type capture(Type t) {
+ return types.capture(t);
+ }
+
+ /** compute the boxed type associated with 't' */
+ public Type box(Type t) {
+ if (!t.isPrimitive()) {
+ throw new AssertionError("Cannot box non-primitive type: " + t);
+ }
+ return types.boxedClass(t).type;
+ }
+
+ /** compute the unboxed type associated with 't' */
+ public Type unbox(Type t) {
+ Type u = types.unboxedType(t);
+ if (t == null) {
+ throw new AssertionError("Cannot unbox reference type: " + t);
+ } else {
+ return u;
+ }
+ }
+
+ /** compute a type substitution on 't' given a list of type mappings */
+ public Type subst(Type t, Mapping... maps) {
+ ListBuffer<Type> from = new ListBuffer<>();
+ ListBuffer<Type> to = new ListBuffer<>();
+ for (Mapping tm : maps) {
+ from.append(tm.from);
+ to.append(tm.to);
+ }
+ return types.subst(t, from.toList(), to.toList());
+ }
+
+ /** create a fresh type mapping from a type to another */
+ public Mapping Mapping(Type from, Type to) {
+ return new Mapping(from, to);
+ }
+
+ public static class Mapping {
+ Type from;
+ Type to;
+ private Mapping(Type from, Type to) {
+ this.from = from;
+ this.to = to;
+ }
+ }
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="type factory">
+
+ /**
+ * This class is used to create Java types in a simple way. All main
+ * kinds of type are supported: primitive, reference, non-denotable. The
+ * factory also supports creation of constant types (used by the compiler
+ * to represent the type of a literal).
+ */
+ public class Factory {
+
+ private int synthNameCount = 0;
+
+ private Name syntheticName() {
+ return names.fromString("A$" + synthNameCount++);
+ }
+
+ public ClassType Class(long flags, Type... typeArgs) {
+ ClassSymbol csym = new ClassSymbol(flags, syntheticName(), predef.noSymbol);
+ csym.type = new ClassType(Type.noType, List.from(typeArgs), csym);
+ ((ClassType)csym.type).supertype_field = predef.objectType;
+ return (ClassType)csym.type;
+ }
+
+ public ClassType Class(Type... typeArgs) {
+ return Class(0, typeArgs);
+ }
+
+ public ClassType Interface(Type... typeArgs) {
+ return Class(Flags.INTERFACE, typeArgs);
+ }
+
+ public ClassType Interface(long flags, Type... typeArgs) {
+ return Class(Flags.INTERFACE | flags, typeArgs);
+ }
+
+ public Type Constant(byte b) {
+ return predef.byteType.constType(b);
+ }
+
+ public Type Constant(short s) {
+ return predef.shortType.constType(s);
+ }
+
+ public Type Constant(int i) {
+ return predef.intType.constType(i);
+ }
+
+ public Type Constant(long l) {
+ return predef.longType.constType(l);
+ }
+
+ public Type Constant(float f) {
+ return predef.floatType.constType(f);
+ }
+
+ public Type Constant(double d) {
+ return predef.doubleType.constType(d);
+ }
+
+ public Type Constant(char c) {
+ return predef.charType.constType(c + 0);
+ }
+
+ public ArrayType Array(Type elemType) {
+ return new ArrayType(elemType, predef.arrayClass);
+ }
+
+ public TypeVar TypeVariable() {
+ return TypeVariable(predef.objectType);
+ }
+
+ public TypeVar TypeVariable(Type bound) {
+ TypeSymbol tvsym = new TypeVariableSymbol(0, syntheticName(), null, predef.noSymbol);
+ tvsym.type = new TypeVar(tvsym, bound, null);
+ return (TypeVar)tvsym.type;
+ }
+
+ public WildcardType Wildcard(BoundKind bk, Type bound) {
+ return new WildcardType(bound, bk, predef.boundClass);
+ }
+
+ public CapturedType CapturedVariable(Type upper, Type lower) {
+ return new CapturedType(syntheticName(), predef.noSymbol, upper, lower, null);
+ }
+
+ public ClassType Intersection(Type classBound, Type... intfBounds) {
+ ClassType ct = Class(Flags.COMPOUND);
+ ct.supertype_field = classBound;
+ ct.interfaces_field = List.from(intfBounds);
+ return ct;
+ }
+ }
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="StrToTypeFactory">
+ /**
+ * StrToTypeFactory is a class provided to ease the creation of complex types from Strings.
+ * The client code can specify a package, a list of imports and a list of type variables when
+ * creating an instance of StrToTypeFactory. Later types including, or not, these type variables
+ * can be created by the factory. All occurrences of the same type variable in a type defined
+ * using a String are guaranteed to refer to the same type variable in the created type.
+ *
+ * An example is reported below:
+ *
+ * <pre>
+ * List<String> imports = new ArrayList<>();
+ * imports.add("java.util.*");
+ * List<String> typeVars = new ArrayList<>();
+ * typeVars.add("T");
+ * strToTypeFactory = new StrToTypeFactory(null, imports, typeVars);
+ *
+ * Type freeType = strToTypeFactory.getType("List<? extends T>");
+ * Type aType = strToTypeFactory.getType("List<? extends String>");
+ *
+ * // method withInferenceContext() belongs to TypeHarness
+ * withInferenceContext(strToTypeFactory.getTypeVars(), inferenceContext -> {
+ * assertSameType(inferenceContext.asUndetVar(freeType), aType);
+ * UndetVar undetVarForT = (UndetVar)inferenceContext.undetVars().head;
+ * com.sun.tools.javac.util.List<Type> equalBounds = undetVarForT.getBounds(InferenceBound.EQ);
+ * Assert.check(!equalBounds.isEmpty() && equalBounds.length() == 1,
+ * "undetVar must have only one equality bound");
+ * });
+ * </pre>
+ */
+ public class StrToTypeFactory {
+ int id = 0;
+ String pkg;
+ java.util.List<String> imports;
+ public java.util.List<String> typeVarDecls;
+ public List<Type> typeVariables;
+
+ public StrToTypeFactory(String pkg, java.util.List<String> imports, java.util.List<String> typeVarDecls) {
+ this.pkg = pkg;
+ this.imports = imports;
+ this.typeVarDecls = typeVarDecls;
+ this.typeVariables = from(typeVarDecls.stream()
+ .map(this::typeVarName)
+ .map(this::getType)
+ .collect(Collectors.toList())
+ );
+ }
+
+ TypeVar getTypeVarFromStr(String name) {
+ if (typeVarDecls == null) {
+ return null;
+ }
+ int index = typeVarDecls.indexOf(name);
+ if (index != -1) {
+ return (TypeVar)typeVariables.get(index);
+ }
+ return null;
+ }
+
+ List<Type> getTypeVars() {
+ return typeVariables;
+ }
+
+ String typeVarName(String typeVarDecl) {
+ String[] ss = typeVarDecl.split(" ");
+ return ss[0];
+ }
+
+ public final Type getType(String type) {
+ JavaSource source = new JavaSource(type);
+ MyAttr.theType = null;
+ MyAttr.typeParameters = List.nil();
+ tool.clear();
+ List<JavaFileObject> inputs = of(source);
+ try {
+ tool.compile(inputs);
+ } catch (Throwable ex) {
+ throw new Abort(ex);
+ }
+ if (typeVariables != null) {
+ return types.subst(MyAttr.theType, MyAttr.typeParameters, typeVariables);
+ }
+ return MyAttr.theType;
+ }
+
+ class JavaSource extends SimpleJavaFileObject {
+
+ String id;
+ String type;
+ String template = "#Package;\n" +
+ "#Imports\n" +
+ "class G#Id#TypeVars {\n" +
+ " #FieldType var;" +
+ "}";
+
+ JavaSource(String type) {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ this.id = String.valueOf(StrToTypeFactory.this.id++);
+ this.type = type;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ String impStmts = imports.size() > 0 ?
+ imports.stream().map(i -> "import " + i + ";").collect(Collectors.joining("\n")) : "";
+ String tvars = typeVarDecls.size() > 0 ?
+ typeVarDecls.stream().collect(Collectors.joining(",", "<", ">")) : "";
+ return template
+ .replace("#Package", (pkg == null) ? "" : "package " + pkg + ";")
+ .replace("#Imports", impStmts)
+ .replace("#Id", id)
+ .replace("#TypeVars", tvars)
+ .replace("#FieldType", type);
+ }
+ }
+ }
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="helper classes">
+ static class MyAttr extends Attr {
+
+ private static Type theType;
+ private static List<Type> typeParameters = List.nil();
+
+ static void preRegister(Context context) {
+ context.put(attrKey, (com.sun.tools.javac.util.Context.Factory<Attr>) c -> new MyAttr(c));
+ }
+
+ MyAttr(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void visitVarDef(JCVariableDecl tree) {
+ super.visitVarDef(tree);
+ theType = tree.type;
+ }
+
+ @Override
+ public void attribClass(DiagnosticPosition pos, ClassSymbol c) {
+ super.attribClass(pos, c);
+ ClassType ct = (ClassType)c.type;
+ typeParameters = ct.typarams_field;
+ }
+ }
+
+ static class ReusableJavaCompiler extends JavaCompiler {
+ ReusableJavaCompiler(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void checkReusable() {
+ // do nothing
+ }
+
+ @Override
+ public void close() {
+ //do nothing
+ }
+
+ void clear() {
+ newRound();
+ }
+ }
+ // </editor-fold>
+}