Tue, 16 Jun 2009 10:46:37 +0100
6638712: Inference with wildcard types causes selection of inapplicable method
Summary: Added global sanity check in order to make sure that return type inference does not violate bounds constraints
Reviewed-by: jjg
1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.javac.code;
28 import java.util.Locale;
30 import com.sun.tools.javac.api.Messages;
31 import com.sun.tools.javac.code.Type.*;
32 import com.sun.tools.javac.code.Symbol.*;
33 import com.sun.tools.javac.util.List;
34 import com.sun.tools.javac.util.ListBuffer;
36 import static com.sun.tools.javac.code.TypeTags.*;
37 import static com.sun.tools.javac.code.BoundKind.*;
38 import static com.sun.tools.javac.code.Flags.*;
40 /**
41 * A combined type/symbol visitor for generating non-trivial localized string
42 * representation of types and symbols.
43 */
44 public abstract class Printer implements Type.Visitor<String, Locale>, Symbol.Visitor<String, Locale> {
46 List<Type> seenCaptured = List.nil();
47 static final int PRIME = 997; // largest prime less than 1000
49 /**
50 * This method should be overriden in order to provide proper i18n support.
51 *
52 * @param locale the locale in which the string is to be rendered
53 * @param key the key corresponding to the message to be displayed
54 * @param args a list of optional arguments
55 * @return localized string representation
56 */
57 protected abstract String localize(Locale locale, String key, Object... args);
59 /**
60 * Maps a captured type into an unique identifier.
61 *
62 * @param t the captured type for which an id is to be retrieved
63 * @param locale locale settings
64 * @return unique id representing this captured type
65 */
66 protected abstract String capturedVarId(CapturedType t, Locale locale);
68 /**
69 * Create a printer with default i18n support provided by Messages. By default,
70 * captured types ids are generated using hashcode.
71 *
72 * @param messages Messages class to be used for i18n
73 * @return printer visitor instance
74 */
75 public static Printer createStandardPrinter(final Messages messages) {
76 return new Printer() {
77 @Override
78 protected String localize(Locale locale, String key, Object... args) {
79 return messages.getLocalizedString(locale, key, args);
80 }
82 @Override
83 protected String capturedVarId(CapturedType t, Locale locale) {
84 return (t.hashCode() & 0xFFFFFFFFL) % PRIME + "";
85 }};
86 }
88 /**
89 * Get a localized string representation for all the types in the input list.
90 *
91 * @param ts types to be displayed
92 * @param locale the locale in which the string is to be rendered
93 * @return localized string representation
94 */
95 public String visitTypes(List<Type> ts, Locale locale) {
96 ListBuffer<String> sbuf = ListBuffer.lb();
97 for (Type t : ts) {
98 sbuf.append(visit(t, locale));
99 }
100 return sbuf.toList().toString();
101 }
103 /**
104 * * Get a localized string represenation for all the symbols in the input list.
105 *
106 * @param ts symbols to be displayed
107 * @param locale the locale in which the string is to be rendered
108 * @return localized string representation
109 */
110 public String visitSymbols(List<Symbol> ts, Locale locale) {
111 ListBuffer<String> sbuf = ListBuffer.lb();
112 for (Symbol t : ts) {
113 sbuf.append(visit(t, locale));
114 }
115 return sbuf.toList().toString();
116 }
118 /**
119 * Get a localized string represenation for a given type.
120 *
121 * @param ts type to be displayed
122 * @param locale the locale in which the string is to be rendered
123 * @return localized string representation
124 */
125 public String visit(Type t, Locale locale) {
126 return t.accept(this, locale);
127 }
129 /**
130 * Get a localized string represenation for a given symbol.
131 *
132 * @param ts symbol to be displayed
133 * @param locale the locale in which the string is to be rendered
134 * @return localized string representation
135 */
136 public String visit(Symbol s, Locale locale) {
137 return s.accept(this, locale);
138 }
140 @Override
141 public String visitCapturedType(CapturedType t, Locale locale) {
142 if (seenCaptured.contains(t))
143 return localize(locale, "compiler.misc.type.captureof.1",
144 capturedVarId(t, locale));
145 else {
146 try {
147 seenCaptured = seenCaptured.prepend(t);
148 return localize(locale, "compiler.misc.type.captureof",
149 capturedVarId(t, locale),
150 visit(t.wildcard, locale));
151 }
152 finally {
153 seenCaptured = seenCaptured.tail;
154 }
155 }
156 }
158 @Override
159 public String visitForAll(ForAll t, Locale locale) {
160 return "<" + visitTypes(t.tvars, locale) + ">" + visit(t.qtype, locale);
161 }
163 @Override
164 public String visitUndetVar(UndetVar t, Locale locale) {
165 if (t.inst != null) {
166 return visit(t.inst, locale);
167 } else {
168 return visit(t.qtype, locale) + "?";
169 }
170 }
172 @Override
173 public String visitArrayType(ArrayType t, Locale locale) {
174 return visit(t.elemtype, locale) + "[]";
175 }
177 @Override
178 public String visitClassType(ClassType t, Locale locale) {
179 StringBuffer buf = new StringBuffer();
180 if (t.getEnclosingType().tag == CLASS && t.tsym.owner.kind == Kinds.TYP) {
181 buf.append(visit(t.getEnclosingType(), locale));
182 buf.append(".");
183 buf.append(className(t, false, locale));
184 } else {
185 buf.append(className(t, true, locale));
186 }
187 if (t.getTypeArguments().nonEmpty()) {
188 buf.append('<');
189 buf.append(visitTypes(t.getTypeArguments(), locale));
190 buf.append(">");
191 }
192 return buf.toString();
193 }
195 @Override
196 public String visitMethodType(MethodType t, Locale locale) {
197 return "(" + printMethodArgs(t.argtypes, false, locale) + ")" + visit(t.restype, locale);
198 }
200 @Override
201 public String visitPackageType(PackageType t, Locale locale) {
202 return t.tsym.getQualifiedName().toString();
203 }
205 @Override
206 public String visitWildcardType(WildcardType t, Locale locale) {
207 StringBuffer s = new StringBuffer();
208 s.append(t.kind);
209 if (t.kind != UNBOUND) {
210 s.append(visit(t.type, locale));
211 }
212 return s.toString();
213 }
215 @Override
216 public String visitErrorType(ErrorType t, Locale locale) {
217 return visitType(t, locale);
218 }
220 @Override
221 public String visitTypeVar(TypeVar t, Locale locale) {
222 return visitType(t, locale);
223 }
225 public String visitType(Type t, Locale locale) {
226 String s = (t.tsym == null || t.tsym.name == null)
227 ? localize(locale, "compiler.misc.type.none")
228 : t.tsym.name.toString();
229 return s;
230 }
232 /**
233 * Converts a class name into a (possibly localized) string. Anonymous
234 * inner classes gets converted into a localized string.
235 *
236 * @param t the type of the class whose name is to be rendered
237 * @param longform if set, the class' fullname is displayed - if unset the
238 * short name is chosen (w/o package)
239 * @param locale the locale in which the string is to be rendered
240 * @return localized string representation
241 */
242 protected String className(ClassType t, boolean longform, Locale locale) {
243 Symbol sym = t.tsym;
244 if (sym.name.length() == 0 && (sym.flags() & COMPOUND) != 0) {
245 StringBuffer s = new StringBuffer(visit(t.supertype_field, locale));
246 for (List<Type> is = t.interfaces_field; is.nonEmpty(); is = is.tail) {
247 s.append("&");
248 s.append(visit(is.head, locale));
249 }
250 return s.toString();
251 } else if (sym.name.length() == 0) {
252 String s;
253 ClassType norm = (ClassType) t.tsym.type;
254 if (norm == null) {
255 s = localize(locale, "compiler.misc.anonymous.class", (Object) null);
256 } else if (norm.interfaces_field.nonEmpty()) {
257 s = localize(locale, "compiler.misc.anonymous.class",
258 visit(norm.interfaces_field.head, locale));
259 } else {
260 s = localize(locale, "compiler.misc.anonymous.class",
261 visit(norm.supertype_field, locale));
262 }
263 return s;
264 } else if (longform) {
265 return sym.getQualifiedName().toString();
266 } else {
267 return sym.name.toString();
268 }
269 }
271 /**
272 * Converts a set of method argument types into their corresponding
273 * localized string representation.
274 *
275 * @param args arguments to be rendered
276 * @param varArgs if true, the last method argument is regarded as a vararg
277 * @param locale the locale in which the string is to be rendered
278 * @return localized string representation
279 */
280 protected String printMethodArgs(List<Type> args, boolean varArgs, Locale locale) {
281 if (!varArgs) {
282 return visitTypes(args, locale);
283 } else {
284 StringBuffer buf = new StringBuffer();
285 while (args.tail.nonEmpty()) {
286 buf.append(visit(args.head, locale));
287 args = args.tail;
288 buf.append(',');
289 }
290 if (args.head.tag == ARRAY) {
291 buf.append(visit(((ArrayType) args.head).elemtype, locale));
292 buf.append("...");
293 } else {
294 buf.append(visit(args.head, locale));
295 }
296 return buf.toString();
297 }
298 }
300 @Override
301 public String visitClassSymbol(ClassSymbol sym, Locale locale) {
302 return sym.name.isEmpty()
303 ? localize(locale, "compiler.misc.anonymous.class", sym.flatname)
304 : sym.fullname.toString();
305 }
307 @Override
308 public String visitMethodSymbol(MethodSymbol s, Locale locale) {
309 if ((s.flags() & BLOCK) != 0) {
310 return s.owner.name.toString();
311 } else {
312 String ms = (s.name == s.name.table.names.init)
313 ? s.owner.name.toString()
314 : s.name.toString();
315 if (s.type != null) {
316 if (s.type.tag == FORALL) {
317 ms = "<" + visitTypes(s.type.getTypeArguments(), locale) + ">" + ms;
318 }
319 ms += "(" + printMethodArgs(
320 s.type.getParameterTypes(),
321 (s.flags() & VARARGS) != 0,
322 locale) + ")";
323 }
324 return ms;
325 }
326 }
328 @Override
329 public String visitOperatorSymbol(OperatorSymbol s, Locale locale) {
330 return visitMethodSymbol(s, locale);
331 }
333 @Override
334 public String visitPackageSymbol(PackageSymbol s, Locale locale) {
335 return s.isUnnamed()
336 ? localize(locale, "compiler.misc.unnamed.package")
337 : s.fullname.toString();
338 }
340 @Override
341 public String visitTypeSymbol(TypeSymbol s, Locale locale) {
342 return visitSymbol(s, locale);
343 }
345 @Override
346 public String visitVarSymbol(VarSymbol s, Locale locale) {
347 return visitSymbol(s, locale);
348 }
350 @Override
351 public String visitSymbol(Symbol s, Locale locale) {
352 return s.name.toString();
353 }
354 }