/* * Copyright (c) 2010, 2011, 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 7007432 7006109 * @summary Test generic types well-formedness * @author mcimadamore * @library . * @run main GenericTypeWellFormednessTest */ import com.sun.tools.javac.code.BoundKind; 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.code.Symbol.*; import java.lang.reflect.Array; /** * Check parameterized type well-formedness. This test executes a number of checks * in order to establish as to whether an instantiation of a generic type conforms * to the generic class' declared bounds. */ public class GenericTypeWellFormednessTest extends TypeHarness { static int executedCount = 0; static int ignoredCount = 0; InstantiableType[] rows; Type[] columns; static class InstantiableType { protected Type type; public InstantiableType(Type type) { this.type = type; } Type inst(Type clazz) { return type; } } enum Result { /* generic type is well-formed w.r.t. declared bounds */ OK(true), /* generic type is not well-formed w.r.t. declared bounds */ FAIL(false), /* generic type is not well-formed w.r.t. declared bounds according to the JLS 3rd, * but javac allows it (spec for generic type well-formedness is overly restrictive) * See regression test test/tools/generics/wildcards/T5097548.java */ IGNORE(false); boolean value; Result(boolean value) { this.value = value; } } static final Result T = Result.OK; static final Result F = Result.FAIL; static final Result I = Result.IGNORE; /*is a type in 'rows' a valid instantiation for the generic class in 'col' ? */ Result[][] isValidInstantiation = { //Foo, Foo, Foo, Foo>, Foo>, Foo>, Foo> /*Foo*/ { T , T , F , F , F , F , F }, /*Foo*/ { T , T , T , F , F , F , F }, /*Foo*/ { T , T , T , F , F , F , F }, /*Foo*/ { T , T , T , F , F , F , F }, /*Foo*/ { T , T , F , F , F , F , F }, /*Foo*/ { T , T , F , F , F , F , F }, /*Foo*/ { T , T , T , F , F , F , F }, /*Foo*/ { T , T , T , F , F , F , F }, /*Foo*/ { T , T , T , F , F , F , F }, /*Foo*/ { T , T , F , F , F , F , F }, /*Foo*/ { T , T , F , T , T , T , T }, /*Foo<+Object>*/ { T , T , I , I , I , I , I }, /*Foo<+Number>*/ { T , T , T , F , F , F , F }, /*Foo<+Integer>*/{ T , T , T , F , F , F , F }, /*Foo<+Double>*/ { T , T , T , F , F , F , F }, /*Foo<+String>*/ { T , T , F , F , F , F , F }, /*Foo<+X1>*/ { T , T , F , F , F , F , F }, /*Foo<+X2>*/ { T , T , T , F , F , F , F }, /*Foo<+X3>*/ { T , T , T , F , F , F , F }, /*Foo<+X4>*/ { T , T , T , F , F , F , F }, /*Foo<+X5>*/ { T , T , F , F , F , F , F }, /*Foo<+X6>*/ { T , T , F , T , T , I , T }, /*Foo<-Object>*/ { T , T , F , F , F , F , F }, /*Foo<-Number>*/ { T , T , T , F , F , F , F }, /*Foo<-Integer>*/{ T , T , T , F , F , F , F }, /*Foo<-Double>*/ { T , T , T , F , F , F , F }, /*Foo<-String>*/ { T , T , F , F , F , F , F }, /*Foo<-X1>*/ { T , T , I , I , I , I , I }, /*Foo<-X2>*/ { T , T , I , F , F , F , F }, /*Foo<-X3>*/ { T , T , I , F , F , F , F }, /*Foo<-X4>*/ { T , T , I , F , F , F , F }, /*Foo<-X5>*/ { T , T , I , F , F , F , F }, /*Foo<-X6>*/ { T , T , F , T , I , I , T }, /*Foo*/ { T , T , T , T , T , T , T }}; GenericTypeWellFormednessTest() { InstantiableType[] basicTypes = { new InstantiableType(predef.objectType), new InstantiableType(NumberType()), new InstantiableType(box(predef.intType)), new InstantiableType(box(predef.doubleType)), new InstantiableType(predef.stringType) }; InstantiableType[] typeVars = new InstantiableType[basicTypes.length + 1]; for (int i = 0 ; i < basicTypes.length ; i++) { typeVars[i] = new InstantiableType(fac.TypeVariable(basicTypes[i].type)); } typeVars[typeVars.length - 1] = new InstantiableType(null) { Type inst(Type clazz) { TypeVar tvar = fac.TypeVariable(); tvar.bound = subst(clazz, Mapping(clazz.getTypeArguments().head, tvar)); return tvar; } }; InstantiableType[] typeArgs = join(InstantiableType.class, basicTypes, typeVars); InstantiableType[] invariantTypes = new InstantiableType[typeArgs.length]; for (int i = 0 ; i < typeArgs.length ; i++) { final InstantiableType type1 = typeArgs[i]; invariantTypes[i] = new InstantiableType(typeArgs[i].type) { Type inst(Type clazz) { return subst(clazz, Mapping(clazz.getTypeArguments().head, type1.inst(clazz))); } }; } InstantiableType[] covariantTypes = new InstantiableType[typeArgs.length]; for (int i = 0 ; i < typeArgs.length ; i++) { final InstantiableType type1 = typeArgs[i]; covariantTypes[i] = new InstantiableType(null) { Type inst(Type clazz) { Type t = fac.Wildcard(BoundKind.EXTENDS, type1.inst(clazz)); return subst(clazz, Mapping(clazz.getTypeArguments().head, t)); } }; } InstantiableType[] contravariantTypes = new InstantiableType[typeArgs.length]; for (int i = 0 ; i < typeArgs.length ; i++) { final InstantiableType type1 = typeArgs[i]; contravariantTypes[i] = new InstantiableType(null) { Type inst(Type clazz) { Type t = fac.Wildcard(BoundKind.SUPER, type1.inst(clazz)); return subst(clazz, Mapping(clazz.getTypeArguments().head, t)); } }; } InstantiableType[] bivariantTypes = { new InstantiableType(fac.Wildcard(BoundKind.UNBOUND, predef.objectType)) { Type inst(Type clazz) { return subst(clazz, Mapping(clazz.getTypeArguments().head, type)); } } }; rows = join(InstantiableType.class, invariantTypes, covariantTypes, contravariantTypes, bivariantTypes); Type tv1 = fac.TypeVariable(); Type decl1 = fac.Class(tv1); Type tv2 = fac.TypeVariable(predef.objectType); Type decl2 = fac.Class(tv2); Type tv3 = fac.TypeVariable(NumberType()); Type decl3 = fac.Class(tv3); TypeVar tv4 = fac.TypeVariable(); Type decl4 = fac.Class(tv4); tv4.bound = decl4; tv4.tsym.name = predef.exceptionType.tsym.name; TypeVar tv5 = fac.TypeVariable(); Type decl5 = fac.Class(tv5); tv5.bound = subst(decl5, Mapping(tv5, fac.Wildcard(BoundKind.EXTENDS, tv5))); TypeVar tv6 = fac.TypeVariable(); Type decl6 = fac.Class(tv6); tv6.bound = subst(decl6, Mapping(tv6, fac.Wildcard(BoundKind.SUPER, tv6))); TypeVar tv7 = fac.TypeVariable(); Type decl7 = fac.Class(tv7); tv7.bound = subst(decl7, Mapping(tv7, fac.Wildcard(BoundKind.UNBOUND, predef.objectType))); columns = new Type[] { decl1, decl2, decl3, decl4, decl5, decl6, decl7 }; } void test() { for (int i = 0; i < rows.length ; i++) { for (int j = 0; j < columns.length ; j++) { Type decl = columns[j]; Type inst = rows[i].inst(decl); if (isValidInstantiation[i][j] != Result.IGNORE) { executedCount++; assertValidGenericType(inst, isValidInstantiation[i][j].value); } else { ignoredCount++; } } } } Type NumberType() { Symbol s = box(predef.intType).tsym; s.complete(); return ((ClassType)s.type).supertype_field; } @SuppressWarnings("unchecked") T[] join(Class type, T[]... args) { int totalLength = 0; for (T[] arr : args) { totalLength += arr.length; } T[] new_arr = (T[])Array.newInstance(type, totalLength); int idx = 0; for (T[] arr : args) { System.arraycopy(arr, 0, new_arr, idx, arr.length); idx += arr.length; } return new_arr; } public static void main(String[] args) { new GenericTypeWellFormednessTest().test(); System.out.println("Executed checks : " + executedCount); System.out.println("Ignored checks : " + ignoredCount); } }