Tue, 11 Aug 2009 01:13:14 +0100
6521805: Regression: JDK5/JDK6 javac allows write access to outer class reference
Summary: javac should warn/complain about identifiers with the same name as synthetic symbol
Reviewed-by: jjg
1 /*
2 * Copyright 1999-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.comp;
28 import com.sun.tools.javac.util.*;
29 import com.sun.tools.javac.util.List;
30 import com.sun.tools.javac.code.*;
31 import com.sun.tools.javac.code.Type.*;
32 import com.sun.tools.javac.code.Symbol.*;
33 import com.sun.tools.javac.util.JCDiagnostic;
35 import static com.sun.tools.javac.code.TypeTags.*;
37 /** Helper class for type parameter inference, used by the attribution phase.
38 *
39 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
40 * you write code that depends on this, you do so at your own risk.
41 * This code and its internal interfaces are subject to change or
42 * deletion without notice.</b>
43 */
44 public class Infer {
45 protected static final Context.Key<Infer> inferKey =
46 new Context.Key<Infer>();
48 /** A value for prototypes that admit any type, including polymorphic ones. */
49 public static final Type anyPoly = new Type(NONE, null);
51 Symtab syms;
52 Types types;
53 Resolve rs;
54 JCDiagnostic.Factory diags;
56 public static Infer instance(Context context) {
57 Infer instance = context.get(inferKey);
58 if (instance == null)
59 instance = new Infer(context);
60 return instance;
61 }
63 protected Infer(Context context) {
64 context.put(inferKey, this);
65 syms = Symtab.instance(context);
66 types = Types.instance(context);
67 rs = Resolve.instance(context);
68 diags = JCDiagnostic.Factory.instance(context);
69 ambiguousNoInstanceException =
70 new NoInstanceException(true, diags);
71 unambiguousNoInstanceException =
72 new NoInstanceException(false, diags);
73 invalidInstanceException =
74 new InvalidInstanceException(diags);
76 }
78 public static class InferenceException extends RuntimeException {
79 private static final long serialVersionUID = 0;
81 JCDiagnostic diagnostic;
82 JCDiagnostic.Factory diags;
84 InferenceException(JCDiagnostic.Factory diags) {
85 this.diagnostic = null;
86 this.diags = diags;
87 }
89 InferenceException setMessage(String key, Object... args) {
90 this.diagnostic = diags.fragment(key, args);
91 return this;
92 }
94 public JCDiagnostic getDiagnostic() {
95 return diagnostic;
96 }
97 }
99 public static class NoInstanceException extends InferenceException {
100 private static final long serialVersionUID = 1;
102 boolean isAmbiguous; // exist several incomparable best instances?
104 NoInstanceException(boolean isAmbiguous, JCDiagnostic.Factory diags) {
105 super(diags);
106 this.isAmbiguous = isAmbiguous;
107 }
108 }
110 public static class InvalidInstanceException extends InferenceException {
111 private static final long serialVersionUID = 2;
113 InvalidInstanceException(JCDiagnostic.Factory diags) {
114 super(diags);
115 }
116 }
118 private final NoInstanceException ambiguousNoInstanceException;
119 private final NoInstanceException unambiguousNoInstanceException;
120 private final InvalidInstanceException invalidInstanceException;
122 /***************************************************************************
123 * Auxiliary type values and classes
124 ***************************************************************************/
126 /** A mapping that turns type variables into undetermined type variables.
127 */
128 Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
129 public Type apply(Type t) {
130 if (t.tag == TYPEVAR) return new UndetVar(t);
131 else return t.map(this);
132 }
133 };
135 /** A mapping that returns its type argument with every UndetVar replaced
136 * by its `inst' field. Throws a NoInstanceException
137 * if this not possible because an `inst' field is null.
138 */
139 Mapping getInstFun = new Mapping("getInstFun") {
140 public Type apply(Type t) {
141 switch (t.tag) {
142 case UNKNOWN:
143 throw ambiguousNoInstanceException
144 .setMessage("undetermined.type");
145 case UNDETVAR:
146 UndetVar that = (UndetVar) t;
147 if (that.inst == null)
148 throw ambiguousNoInstanceException
149 .setMessage("type.variable.has.undetermined.type",
150 that.qtype);
151 return apply(that.inst);
152 default:
153 return t.map(this);
154 }
155 }
156 };
158 /***************************************************************************
159 * Mini/Maximization of UndetVars
160 ***************************************************************************/
162 /** Instantiate undetermined type variable to its minimal upper bound.
163 * Throw a NoInstanceException if this not possible.
164 */
165 void maximizeInst(UndetVar that, Warner warn) throws NoInstanceException {
166 if (that.inst == null) {
167 if (that.hibounds.isEmpty())
168 that.inst = syms.objectType;
169 else if (that.hibounds.tail.isEmpty())
170 that.inst = that.hibounds.head;
171 else
172 that.inst = types.glb(that.hibounds);
173 }
174 if (that.inst == null ||
175 that.inst.isErroneous())
176 throw ambiguousNoInstanceException
177 .setMessage("no.unique.maximal.instance.exists",
178 that.qtype, that.hibounds);
179 }
180 //where
181 private boolean isSubClass(Type t, final List<Type> ts) {
182 t = t.baseType();
183 if (t.tag == TYPEVAR) {
184 List<Type> bounds = types.getBounds((TypeVar)t);
185 for (Type s : ts) {
186 if (!types.isSameType(t, s.baseType())) {
187 for (Type bound : bounds) {
188 if (!isSubClass(bound, List.of(s.baseType())))
189 return false;
190 }
191 }
192 }
193 } else {
194 for (Type s : ts) {
195 if (!t.tsym.isSubClass(s.baseType().tsym, types))
196 return false;
197 }
198 }
199 return true;
200 }
202 /** Instantiate undetermined type variable to the lub of all its lower bounds.
203 * Throw a NoInstanceException if this not possible.
204 */
205 void minimizeInst(UndetVar that, Warner warn) throws NoInstanceException {
206 if (that.inst == null) {
207 if (that.lobounds.isEmpty())
208 that.inst = syms.botType;
209 else if (that.lobounds.tail.isEmpty())
210 that.inst = that.lobounds.head.isPrimitive() ? syms.errType : that.lobounds.head;
211 else {
212 that.inst = types.lub(that.lobounds);
213 }
214 if (that.inst == null || that.inst.tag == ERROR)
215 throw ambiguousNoInstanceException
216 .setMessage("no.unique.minimal.instance.exists",
217 that.qtype, that.lobounds);
218 // VGJ: sort of inlined maximizeInst() below. Adding
219 // bounds can cause lobounds that are above hibounds.
220 if (that.hibounds.isEmpty())
221 return;
222 Type hb = null;
223 if (that.hibounds.tail.isEmpty())
224 hb = that.hibounds.head;
225 else for (List<Type> bs = that.hibounds;
226 bs.nonEmpty() && hb == null;
227 bs = bs.tail) {
228 if (isSubClass(bs.head, that.hibounds))
229 hb = types.fromUnknownFun.apply(bs.head);
230 }
231 if (hb == null ||
232 !types.isSubtypeUnchecked(hb, that.hibounds, warn) ||
233 !types.isSubtypeUnchecked(that.inst, hb, warn))
234 throw ambiguousNoInstanceException;
235 }
236 }
238 /***************************************************************************
239 * Exported Methods
240 ***************************************************************************/
242 /** Try to instantiate expression type `that' to given type `to'.
243 * If a maximal instantiation exists which makes this type
244 * a subtype of type `to', return the instantiated type.
245 * If no instantiation exists, or if several incomparable
246 * best instantiations exist throw a NoInstanceException.
247 */
248 public Type instantiateExpr(ForAll that,
249 Type to,
250 Warner warn) throws InferenceException {
251 List<Type> undetvars = Type.map(that.tvars, fromTypeVarFun);
252 for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) {
253 UndetVar v = (UndetVar) l.head;
254 ListBuffer<Type> hibounds = new ListBuffer<Type>();
255 for (List<Type> l1 = types.getBounds((TypeVar) v.qtype); l1.nonEmpty(); l1 = l1.tail) {
256 if (!l1.head.containsSome(that.tvars)) {
257 hibounds.append(l1.head);
258 }
259 }
260 v.hibounds = hibounds.toList();
261 }
262 Type qtype1 = types.subst(that.qtype, that.tvars, undetvars);
263 if (!types.isSubtype(qtype1, to)) {
264 throw unambiguousNoInstanceException
265 .setMessage("no.conforming.instance.exists",
266 that.tvars, that.qtype, to);
267 }
268 for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail)
269 maximizeInst((UndetVar) l.head, warn);
270 // System.out.println(" = " + qtype1.map(getInstFun));//DEBUG
272 // check bounds
273 List<Type> targs = Type.map(undetvars, getInstFun);
274 targs = types.subst(targs, that.tvars, targs);
275 checkWithinBounds(that.tvars, targs, warn);
276 return that.inst(targs, types);
277 }
279 /** Instantiate method type `mt' by finding instantiations of
280 * `tvars' so that method can be applied to `argtypes'.
281 */
282 public Type instantiateMethod(List<Type> tvars,
283 MethodType mt,
284 final List<Type> argtypes,
285 final boolean allowBoxing,
286 final boolean useVarargs,
287 final Warner warn) throws InferenceException {
288 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
289 List<Type> undetvars = Type.map(tvars, fromTypeVarFun);
290 List<Type> formals = mt.argtypes;
291 //need to capture exactly once - otherwise subsequent
292 //applicability checks might fail
293 final List<Type> capturedArgs = types.capture(argtypes);
294 List<Type> actuals = capturedArgs;
295 List<Type> actualsNoCapture = argtypes;
296 // instantiate all polymorphic argument types and
297 // set up lower bounds constraints for undetvars
298 Type varargsFormal = useVarargs ? formals.last() : null;
299 while (actuals.nonEmpty() && formals.head != varargsFormal) {
300 Type formal = formals.head;
301 Type actual = actuals.head.baseType();
302 Type actualNoCapture = actualsNoCapture.head.baseType();
303 if (actual.tag == FORALL)
304 actual = instantiateArg((ForAll)actual, formal, tvars, warn);
305 Type undetFormal = types.subst(formal, tvars, undetvars);
306 boolean works = allowBoxing
307 ? types.isConvertible(actual, undetFormal, warn)
308 : types.isSubtypeUnchecked(actual, undetFormal, warn);
309 if (!works) {
310 throw unambiguousNoInstanceException
311 .setMessage("no.conforming.assignment.exists",
312 tvars, actualNoCapture, formal);
313 }
314 formals = formals.tail;
315 actuals = actuals.tail;
316 actualsNoCapture = actualsNoCapture.tail;
317 }
318 if (formals.head != varargsFormal || // not enough args
319 !useVarargs && actuals.nonEmpty()) { // too many args
320 // argument lists differ in length
321 throw unambiguousNoInstanceException
322 .setMessage("arg.length.mismatch");
323 }
325 // for varargs arguments as well
326 if (useVarargs) {
327 Type elemType = types.elemtype(varargsFormal);
328 Type elemUndet = types.subst(elemType, tvars, undetvars);
329 while (actuals.nonEmpty()) {
330 Type actual = actuals.head.baseType();
331 Type actualNoCapture = actualsNoCapture.head.baseType();
332 if (actual.tag == FORALL)
333 actual = instantiateArg((ForAll)actual, elemType, tvars, warn);
334 boolean works = types.isConvertible(actual, elemUndet, warn);
335 if (!works) {
336 throw unambiguousNoInstanceException
337 .setMessage("no.conforming.assignment.exists",
338 tvars, actualNoCapture, elemType);
339 }
340 actuals = actuals.tail;
341 actualsNoCapture = actualsNoCapture.tail;
342 }
343 }
345 // minimize as yet undetermined type variables
346 for (Type t : undetvars)
347 minimizeInst((UndetVar) t, warn);
349 /** Type variables instantiated to bottom */
350 ListBuffer<Type> restvars = new ListBuffer<Type>();
352 /** Instantiated types or TypeVars if under-constrained */
353 ListBuffer<Type> insttypes = new ListBuffer<Type>();
355 /** Instantiated types or UndetVars if under-constrained */
356 ListBuffer<Type> undettypes = new ListBuffer<Type>();
358 for (Type t : undetvars) {
359 UndetVar uv = (UndetVar)t;
360 if (uv.inst.tag == BOT) {
361 restvars.append(uv.qtype);
362 insttypes.append(uv.qtype);
363 undettypes.append(uv);
364 uv.inst = null;
365 } else {
366 insttypes.append(uv.inst);
367 undettypes.append(uv.inst);
368 }
369 }
370 checkWithinBounds(tvars, undettypes.toList(), warn);
372 mt = (MethodType)types.subst(mt, tvars, insttypes.toList());
374 if (!restvars.isEmpty()) {
375 // if there are uninstantiated variables,
376 // quantify result type with them
377 final List<Type> inferredTypes = insttypes.toList();
378 final List<Type> all_tvars = tvars; //this is the wrong tvars
379 final MethodType mt2 = new MethodType(mt.argtypes, null, mt.thrown, syms.methodClass);
380 mt2.restype = new ForAll(restvars.toList(), mt.restype) {
381 @Override
382 public Type inst(List<Type> inferred, Types types) throws NoInstanceException {
383 List<Type> formals = types.subst(mt2.argtypes, tvars, inferred);
384 if (!rs.argumentsAcceptable(capturedArgs, formals,
385 allowBoxing, useVarargs, warn)) {
386 // inferred method is not applicable
387 throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", formals, argtypes);
388 }
389 // check that inferred bounds conform to their bounds
390 checkWithinBounds(all_tvars,
391 types.subst(inferredTypes, tvars, inferred), warn);
392 return super.inst(inferred, types);
393 }};
394 return mt2;
395 }
396 else if (!rs.argumentsAcceptable(capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn)) {
397 // inferred method is not applicable
398 throw invalidInstanceException.setMessage("inferred.do.not.conform.to.params", mt.getParameterTypes(), argtypes);
399 }
400 else {
401 // return instantiated version of method type
402 return mt;
403 }
404 }
405 //where
407 /** Try to instantiate argument type `that' to given type `to'.
408 * If this fails, try to insantiate `that' to `to' where
409 * every occurrence of a type variable in `tvars' is replaced
410 * by an unknown type.
411 */
412 private Type instantiateArg(ForAll that,
413 Type to,
414 List<Type> tvars,
415 Warner warn) throws InferenceException {
416 List<Type> targs;
417 try {
418 return instantiateExpr(that, to, warn);
419 } catch (NoInstanceException ex) {
420 Type to1 = to;
421 for (List<Type> l = tvars; l.nonEmpty(); l = l.tail)
422 to1 = types.subst(to1, List.of(l.head), List.of(syms.unknownType));
423 return instantiateExpr(that, to1, warn);
424 }
425 }
427 /** check that type parameters are within their bounds.
428 */
429 private void checkWithinBounds(List<Type> tvars,
430 List<Type> arguments,
431 Warner warn)
432 throws InvalidInstanceException {
433 for (List<Type> tvs = tvars, args = arguments;
434 tvs.nonEmpty();
435 tvs = tvs.tail, args = args.tail) {
436 if (args.head instanceof UndetVar) continue;
437 List<Type> bounds = types.subst(types.getBounds((TypeVar)tvs.head), tvars, arguments);
438 if (!types.isSubtypeUnchecked(args.head, bounds, warn))
439 throw invalidInstanceException
440 .setMessage("inferred.do.not.conform.to.bounds",
441 args.head, bounds);
442 }
443 }
444 }