Tue, 29 Mar 2011 16:40:51 +0100
7030150: Type inference for generic instance creation failed for formal type parameter
Summary: Problem when explicit generic constructor type-arguments are used in conjunction with diamond
Reviewed-by: jjg
mcimadamore@950 | 1 | /* |
mcimadamore@950 | 2 | * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. |
mcimadamore@950 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
mcimadamore@950 | 4 | * |
mcimadamore@950 | 5 | * This code is free software; you can redistribute it and/or modify it |
mcimadamore@950 | 6 | * under the terms of the GNU General Public License version 2 only, as |
mcimadamore@950 | 7 | * published by the Free Software Foundation. |
mcimadamore@950 | 8 | * |
mcimadamore@950 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
mcimadamore@950 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
mcimadamore@950 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
mcimadamore@950 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
mcimadamore@950 | 13 | * accompanied this code). |
mcimadamore@950 | 14 | * |
mcimadamore@950 | 15 | * You should have received a copy of the GNU General Public License version |
mcimadamore@950 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
mcimadamore@950 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
mcimadamore@950 | 18 | * |
mcimadamore@950 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
mcimadamore@950 | 20 | * or visit www.oracle.com if you need additional information or have any |
mcimadamore@950 | 21 | * questions. |
mcimadamore@950 | 22 | */ |
mcimadamore@950 | 23 | |
mcimadamore@950 | 24 | /* |
mcimadamore@950 | 25 | * @test |
mcimadamore@950 | 26 | * @bug 7030150 |
mcimadamore@950 | 27 | * @summary Type inference for generic instance creation failed for formal type parameter |
mcimadamore@950 | 28 | */ |
mcimadamore@950 | 29 | |
mcimadamore@950 | 30 | import com.sun.source.util.JavacTask; |
mcimadamore@950 | 31 | import java.net.URI; |
mcimadamore@950 | 32 | import java.util.Arrays; |
mcimadamore@950 | 33 | import javax.tools.Diagnostic; |
mcimadamore@950 | 34 | import javax.tools.JavaCompiler; |
mcimadamore@950 | 35 | import javax.tools.JavaFileObject; |
mcimadamore@950 | 36 | import javax.tools.SimpleJavaFileObject; |
mcimadamore@950 | 37 | import javax.tools.StandardJavaFileManager; |
mcimadamore@950 | 38 | import javax.tools.ToolProvider; |
mcimadamore@950 | 39 | |
mcimadamore@950 | 40 | public class GenericConstructorAndDiamondTest { |
mcimadamore@950 | 41 | |
mcimadamore@950 | 42 | enum BoundKind { |
mcimadamore@950 | 43 | NO_BOUND(""), |
mcimadamore@950 | 44 | STRING_BOUND("extends String"), |
mcimadamore@950 | 45 | INTEGER_BOUND("extends Integer"); |
mcimadamore@950 | 46 | |
mcimadamore@950 | 47 | String boundStr; |
mcimadamore@950 | 48 | |
mcimadamore@950 | 49 | private BoundKind(String boundStr) { |
mcimadamore@950 | 50 | this.boundStr = boundStr; |
mcimadamore@950 | 51 | } |
mcimadamore@950 | 52 | |
mcimadamore@950 | 53 | boolean matches(TypeArgumentKind tak) { |
mcimadamore@950 | 54 | switch (tak) { |
mcimadamore@950 | 55 | case NONE: return true; |
mcimadamore@950 | 56 | case STRING: return this != INTEGER_BOUND; |
mcimadamore@950 | 57 | case INTEGER: return this != STRING_BOUND; |
mcimadamore@950 | 58 | default: return false; |
mcimadamore@950 | 59 | } |
mcimadamore@950 | 60 | } |
mcimadamore@950 | 61 | } |
mcimadamore@950 | 62 | |
mcimadamore@950 | 63 | enum ConstructorKind { |
mcimadamore@950 | 64 | NON_GENERIC("Foo(Object o) {}"), |
mcimadamore@950 | 65 | GENERIC_NO_BOUND("<T> Foo(T t) {}"), |
mcimadamore@950 | 66 | GENERIC_STRING_BOUND("<T extends String> Foo(T t) {}"), |
mcimadamore@950 | 67 | GENERIC_INTEGER_BOUND("<T extends Integer> Foo(T t) {}"); |
mcimadamore@950 | 68 | |
mcimadamore@950 | 69 | String constrStr; |
mcimadamore@950 | 70 | |
mcimadamore@950 | 71 | private ConstructorKind(String constrStr) { |
mcimadamore@950 | 72 | this.constrStr = constrStr; |
mcimadamore@950 | 73 | } |
mcimadamore@950 | 74 | |
mcimadamore@950 | 75 | boolean matches(ArgumentKind ak) { |
mcimadamore@950 | 76 | switch (ak) { |
mcimadamore@950 | 77 | case STRING: return this != GENERIC_INTEGER_BOUND; |
mcimadamore@950 | 78 | case INTEGER: return this != GENERIC_STRING_BOUND; |
mcimadamore@950 | 79 | default: return false; |
mcimadamore@950 | 80 | } |
mcimadamore@950 | 81 | } |
mcimadamore@950 | 82 | } |
mcimadamore@950 | 83 | |
mcimadamore@950 | 84 | enum TypeArgArity { |
mcimadamore@950 | 85 | ONE(1), |
mcimadamore@950 | 86 | TWO(2), |
mcimadamore@950 | 87 | THREE(3); |
mcimadamore@950 | 88 | |
mcimadamore@950 | 89 | int n; |
mcimadamore@950 | 90 | |
mcimadamore@950 | 91 | private TypeArgArity(int n) { |
mcimadamore@950 | 92 | this.n = n; |
mcimadamore@950 | 93 | } |
mcimadamore@950 | 94 | } |
mcimadamore@950 | 95 | |
mcimadamore@950 | 96 | enum TypeArgumentKind { |
mcimadamore@950 | 97 | NONE(""), |
mcimadamore@950 | 98 | STRING("String"), |
mcimadamore@950 | 99 | INTEGER("Integer"); |
mcimadamore@950 | 100 | |
mcimadamore@950 | 101 | String typeargStr; |
mcimadamore@950 | 102 | |
mcimadamore@950 | 103 | private TypeArgumentKind(String typeargStr) { |
mcimadamore@950 | 104 | this.typeargStr = typeargStr; |
mcimadamore@950 | 105 | } |
mcimadamore@950 | 106 | |
mcimadamore@950 | 107 | String getArgs(TypeArgArity arity) { |
mcimadamore@950 | 108 | if (this == NONE) return ""; |
mcimadamore@950 | 109 | else { |
mcimadamore@950 | 110 | StringBuilder buf = new StringBuilder(); |
mcimadamore@950 | 111 | String sep = ""; |
mcimadamore@950 | 112 | for (int i = 0 ; i < arity.n ; i++) { |
mcimadamore@950 | 113 | buf.append(sep); |
mcimadamore@950 | 114 | buf.append(typeargStr); |
mcimadamore@950 | 115 | sep = ","; |
mcimadamore@950 | 116 | } |
mcimadamore@950 | 117 | return "<" + buf.toString() + ">"; |
mcimadamore@950 | 118 | } |
mcimadamore@950 | 119 | } |
mcimadamore@950 | 120 | |
mcimadamore@950 | 121 | boolean matches(ArgumentKind ak) { |
mcimadamore@950 | 122 | switch (ak) { |
mcimadamore@950 | 123 | case STRING: return this != INTEGER; |
mcimadamore@950 | 124 | case INTEGER: return this != STRING; |
mcimadamore@950 | 125 | default: return false; |
mcimadamore@950 | 126 | } |
mcimadamore@950 | 127 | } |
mcimadamore@950 | 128 | } |
mcimadamore@950 | 129 | |
mcimadamore@950 | 130 | enum ArgumentKind { |
mcimadamore@950 | 131 | STRING("\"\""), |
mcimadamore@950 | 132 | INTEGER("1"); |
mcimadamore@950 | 133 | |
mcimadamore@950 | 134 | String argStr; |
mcimadamore@950 | 135 | |
mcimadamore@950 | 136 | private ArgumentKind(String argStr) { |
mcimadamore@950 | 137 | this.argStr = argStr; |
mcimadamore@950 | 138 | } |
mcimadamore@950 | 139 | } |
mcimadamore@950 | 140 | |
mcimadamore@950 | 141 | public static void main(String... args) throws Exception { |
mcimadamore@950 | 142 | |
mcimadamore@950 | 143 | //create default shared JavaCompiler - reused across multiple compilations |
mcimadamore@950 | 144 | JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); |
mcimadamore@950 | 145 | StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); |
mcimadamore@950 | 146 | |
mcimadamore@950 | 147 | for (BoundKind boundKind : BoundKind.values()) { |
mcimadamore@950 | 148 | for (ConstructorKind constructorKind : ConstructorKind.values()) { |
mcimadamore@950 | 149 | for (TypeArgumentKind declArgKind : TypeArgumentKind.values()) { |
mcimadamore@950 | 150 | for (TypeArgArity arity : TypeArgArity.values()) { |
mcimadamore@950 | 151 | for (TypeArgumentKind useArgKind : TypeArgumentKind.values()) { |
mcimadamore@950 | 152 | for (ArgumentKind argKind : ArgumentKind.values()) { |
mcimadamore@950 | 153 | new GenericConstructorAndDiamondTest(boundKind, constructorKind, |
mcimadamore@950 | 154 | declArgKind, arity, useArgKind, argKind).run(comp, fm); |
mcimadamore@950 | 155 | } |
mcimadamore@950 | 156 | } |
mcimadamore@950 | 157 | } |
mcimadamore@950 | 158 | } |
mcimadamore@950 | 159 | } |
mcimadamore@950 | 160 | } |
mcimadamore@950 | 161 | } |
mcimadamore@950 | 162 | |
mcimadamore@950 | 163 | BoundKind boundKind; |
mcimadamore@950 | 164 | ConstructorKind constructorKind; |
mcimadamore@950 | 165 | TypeArgumentKind declTypeArgumentKind; |
mcimadamore@950 | 166 | TypeArgArity useTypeArgArity; |
mcimadamore@950 | 167 | TypeArgumentKind useTypeArgumentKind; |
mcimadamore@950 | 168 | ArgumentKind argumentKind; |
mcimadamore@950 | 169 | JavaSource source; |
mcimadamore@950 | 170 | DiagnosticChecker diagChecker; |
mcimadamore@950 | 171 | |
mcimadamore@950 | 172 | GenericConstructorAndDiamondTest(BoundKind boundKind, ConstructorKind constructorKind, |
mcimadamore@950 | 173 | TypeArgumentKind declTypeArgumentKind, TypeArgArity useTypeArgArity, |
mcimadamore@950 | 174 | TypeArgumentKind useTypeArgumentKind, ArgumentKind argumentKind) { |
mcimadamore@950 | 175 | this.boundKind = boundKind; |
mcimadamore@950 | 176 | this.constructorKind = constructorKind; |
mcimadamore@950 | 177 | this.declTypeArgumentKind = declTypeArgumentKind; |
mcimadamore@950 | 178 | this.useTypeArgArity = useTypeArgArity; |
mcimadamore@950 | 179 | this.useTypeArgumentKind = useTypeArgumentKind; |
mcimadamore@950 | 180 | this.argumentKind = argumentKind; |
mcimadamore@950 | 181 | this.source = new JavaSource(); |
mcimadamore@950 | 182 | this.diagChecker = new DiagnosticChecker(); |
mcimadamore@950 | 183 | } |
mcimadamore@950 | 184 | |
mcimadamore@950 | 185 | class JavaSource extends SimpleJavaFileObject { |
mcimadamore@950 | 186 | |
mcimadamore@950 | 187 | String template = "class Foo<X #BK> {\n" + |
mcimadamore@950 | 188 | "#CK\n" + |
mcimadamore@950 | 189 | "}\n" + |
mcimadamore@950 | 190 | "class Test {\n" + |
mcimadamore@950 | 191 | "void test() {\n" + |
mcimadamore@950 | 192 | "Foo#TA1 f = new #TA2 Foo<>(#A);\n" + |
mcimadamore@950 | 193 | "}\n" + |
mcimadamore@950 | 194 | "}\n"; |
mcimadamore@950 | 195 | |
mcimadamore@950 | 196 | String source; |
mcimadamore@950 | 197 | |
mcimadamore@950 | 198 | public JavaSource() { |
mcimadamore@950 | 199 | super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); |
mcimadamore@950 | 200 | source = template.replace("#BK", boundKind.boundStr). |
mcimadamore@950 | 201 | replace("#CK", constructorKind.constrStr) |
mcimadamore@950 | 202 | .replace("#TA1", declTypeArgumentKind.getArgs(TypeArgArity.ONE)) |
mcimadamore@950 | 203 | .replace("#TA2", useTypeArgumentKind.getArgs(useTypeArgArity)) |
mcimadamore@950 | 204 | .replace("#A", argumentKind.argStr); |
mcimadamore@950 | 205 | } |
mcimadamore@950 | 206 | |
mcimadamore@950 | 207 | @Override |
mcimadamore@950 | 208 | public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
mcimadamore@950 | 209 | return source; |
mcimadamore@950 | 210 | } |
mcimadamore@950 | 211 | } |
mcimadamore@950 | 212 | |
mcimadamore@950 | 213 | void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { |
mcimadamore@950 | 214 | JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, |
mcimadamore@950 | 215 | null, null, Arrays.asList(source)); |
mcimadamore@950 | 216 | ct.analyze(); |
mcimadamore@950 | 217 | check(); |
mcimadamore@950 | 218 | } |
mcimadamore@950 | 219 | |
mcimadamore@950 | 220 | void check() { |
mcimadamore@950 | 221 | boolean badActual = !constructorKind.matches(argumentKind); |
mcimadamore@950 | 222 | |
mcimadamore@950 | 223 | boolean badArity = constructorKind != ConstructorKind.NON_GENERIC && |
mcimadamore@950 | 224 | useTypeArgumentKind != TypeArgumentKind.NONE && |
mcimadamore@950 | 225 | useTypeArgArity != TypeArgArity.ONE; |
mcimadamore@950 | 226 | |
mcimadamore@950 | 227 | boolean badMethodTypeArg = constructorKind != ConstructorKind.NON_GENERIC && |
mcimadamore@950 | 228 | !useTypeArgumentKind.matches(argumentKind); |
mcimadamore@950 | 229 | |
mcimadamore@950 | 230 | boolean badGenericType = !boundKind.matches(declTypeArgumentKind); |
mcimadamore@950 | 231 | |
mcimadamore@950 | 232 | boolean shouldFail = badActual || badArity || badMethodTypeArg || badGenericType; |
mcimadamore@950 | 233 | |
mcimadamore@950 | 234 | if (shouldFail != diagChecker.errorFound) { |
mcimadamore@950 | 235 | throw new Error("invalid diagnostics for source:\n" + |
mcimadamore@950 | 236 | source.getCharContent(true) + |
mcimadamore@950 | 237 | "\nFound error: " + diagChecker.errorFound + |
mcimadamore@950 | 238 | "\nExpected error: " + shouldFail); |
mcimadamore@950 | 239 | } |
mcimadamore@950 | 240 | } |
mcimadamore@950 | 241 | |
mcimadamore@950 | 242 | static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { |
mcimadamore@950 | 243 | |
mcimadamore@950 | 244 | boolean errorFound; |
mcimadamore@950 | 245 | |
mcimadamore@950 | 246 | public void report(Diagnostic<? extends JavaFileObject> diagnostic) { |
mcimadamore@950 | 247 | if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { |
mcimadamore@950 | 248 | errorFound = true; |
mcimadamore@950 | 249 | } |
mcimadamore@950 | 250 | } |
mcimadamore@950 | 251 | } |
mcimadamore@950 | 252 | } |