001    /*
002     * JastAddJ is covered by the modified BSD License. You should have received
003     * a copy of the modified BSD license with this compiler.
004     * 
005     * Copyright (c) 2011, Jesper Öqvist <jesper.oqvist@cs.lth.se>
006     * All rights reserved.
007     */
008    
009    /**
010     * Type inference for generic instance creation.
011     *
012     * According to the JLSv7 $15.9.1, type inference for generic
013     * instance creation uses the type inference for generic methods.
014     *
015     * Empty type argument lists (&lt;&gt;) are parsed as DiamondAccess
016     * nodes. The type of the DiamondAccess is computed by creating placeholder
017     * methods and using generic method type inference from Java1.5Frontend
018     * to find the inferred type arguments for the DiamondAccess.
019     */
020    aspect TypeInference {
021    
022        public void PlaceholderMethodDecl.nameCheck() { }
023        public void PlaceholderMethodDecl.typeCheck() { }
024        public void PlaceholderMethodDecl.exceptionHandling() { }
025        public void PlaceholderMethodDecl.checkUnreachableStmt() { }
026        public void PlaceholderMethodDecl.definiteAssignment() { }
027        public void PlaceholderMethodDecl.checkModifiers() { }
028    
029        /**
030         * Placeholder methods are not pretty printed.
031         */
032        public void PlaceholderMethodDecl.toString(StringBuffer s) { }
033    
034        /**
035         * If this DiamondAccess node constitutes a legal use of
036         * the diamond operator, the inferred generic type for the
037         * enclosing class instance expression is returned.
038         */
039        eq DiamondAccess.type() {
040                TypeDecl accessType = getTypeAccess().type();
041    
042                if (isAnonymousDecl())
043                        return accessType;
044    
045                if (getClassInstanceExpr() == null)
046                        // it is an error if the DiamondAccess does not occurr
047                        // within a class instance creation expression, but this
048                        // error is handled in typeCheck
049                        return accessType;
050    
051                if (!(accessType instanceof ParClassDecl))
052                        // it is an error if the TypeDecl of a DiamondAccess is not
053                        // a generic type, but this error is handled in typeCheck
054                        return accessType;
055    
056                SimpleSet maxSpecific = chooseConstructor();
057    
058                if (maxSpecific.isEmpty())
059                        return getTypeAccess().type();
060    
061                MethodDecl constructor = (MethodDecl) maxSpecific.iterator().next();
062                return constructor.type();
063        }
064    
065        syn boolean Access.isDiamond() = false;
066        eq DiamondAccess.isDiamond() = true;
067    
068        inh ClassInstanceExpr DiamondAccess.getClassInstanceExpr();
069        eq ClassInstanceExpr.getAccess().getClassInstanceExpr() = this;
070        eq Program.getChild(int i).getClassInstanceExpr() = null;// TODO
071    
072        protected static SimpleSet DiamondAccess.mostSpecific(
073                        SimpleSet maxSpecific, MethodDecl decl) {
074                if (maxSpecific.isEmpty()) {
075                        maxSpecific = maxSpecific.add(decl);
076                } else {
077                        if (decl.moreSpecificThan(
078                                                (MethodDecl)maxSpecific.iterator().next()))
079                                maxSpecific = SimpleSet.emptySet.add(decl);
080                        else if (!((MethodDecl)maxSpecific.iterator().next()).
081                                        moreSpecificThan(decl))
082                                maxSpecific = maxSpecific.add(decl);
083                }
084                return maxSpecific;
085        }
086    
087        /**
088         * Choose a constructor for the diamond operator using placeholder
089         * methods.
090         */
091        protected SimpleSet DiamondAccess.chooseConstructor() {
092                ClassInstanceExpr instanceExpr = getClassInstanceExpr();
093                TypeDecl type = getTypeAccess().type();
094    
095                assert instanceExpr != null;
096                assert type instanceof ParClassDecl;
097    
098                GenericClassDecl genericType =
099                        (GenericClassDecl) ((ParClassDecl)type).genericDecl();
100    
101               List<PlaceholderMethodDecl> placeholderMethods =
102                       genericType.getPlaceholderMethodList();
103    
104               SimpleSet maxSpecific = SimpleSet.emptySet;
105               Collection<MethodDecl> potentiallyApplicable =
106                       potentiallyApplicable(placeholderMethods);
107               for (MethodDecl candidate : potentiallyApplicable) {
108                       if (applicableBySubtyping(instanceExpr, candidate) ||
109                                       applicableByMethodInvocationConversion(
110                                               instanceExpr, candidate) ||
111                                       applicableByVariableArity(instanceExpr, candidate))
112                               maxSpecific = mostSpecific(maxSpecific, candidate);
113    
114               }
115               return maxSpecific;
116       }
117    
118       /**
119        * The placeholder method list for the constructors of this generic
120        * class.
121        *
122        * @return list of placeholder methods
123        */
124       syn nta List<PlaceholderMethodDecl> GenericClassDecl.getPlaceholderMethodList() {
125               List<PlaceholderMethodDecl> placeholderMethods =
126                       new List<PlaceholderMethodDecl>();
127               List<TypeVariable> typeParams = getTypeParameterList();
128               List<TypeVariable> classTypeVars = new List<TypeVariable>();
129               List<Access> typeArgs = new List<Access>();
130    
131               // copy the list of type parameters
132               int arg = 0;
133               for (Iterator iter = typeParams.iterator(); iter.hasNext(); ++arg) {
134                       String substName = "#"+arg;
135                       typeArgs.add(new TypeAccess(substName));
136    
137                       TypeVariable typeVar = (TypeVariable) iter.next();
138                       List<Access> typeBounds = new List<Access>();
139                       for (Access typeBound : typeVar.getTypeBoundList())
140                               typeBounds.add((Access) typeBound.cloneSubtree());
141                       classTypeVars.add(
142                                       new TypeVariable(
143                                               new Modifiers(),
144                                               substName,
145                                               new List<BodyDecl>(),
146                                               typeBounds));
147               }
148    
149               ParTypeAccess returnType = new ParTypeAccess(
150                               createQualifiedAccess(),
151                               typeArgs);
152    
153               for (Iterator iter = constructors().iterator(); iter.hasNext(); ) {
154                       ConstructorDecl decl = (ConstructorDecl)iter.next();
155                       if (decl instanceof ConstructorDeclSubstituted)
156                               decl = ((ConstructorDeclSubstituted) decl).getOriginal();
157    
158                       // filter accessible constructors
159                       if (!decl.accessibleFrom(hostType()))
160                               continue;
161    
162                       Collection<TypeVariable> originalTypeVars =
163                               new LinkedList<TypeVariable>();
164                       List<TypeVariable> typeVars = new List<TypeVariable>();
165                       for (TypeVariable typeVar : typeParams)
166                               originalTypeVars.add(typeVar);
167                       for (TypeVariable typeVar : classTypeVars)
168                               typeVars.add((TypeVariable) typeVar.cloneSubtree());
169    
170                       if (decl instanceof GenericConstructorDecl) {
171                               GenericConstructorDecl genericDecl =
172                                       (GenericConstructorDecl) decl;
173                               List<TypeVariable> typeVariables = new List<TypeVariable>();
174                               for (int i = 0; i < genericDecl.getNumTypeParameter(); ++i) {
175                                       String substName = "#" + (arg+i);
176    
177                                       TypeVariable typeVar = genericDecl.getTypeParameter(i);
178                                       originalTypeVars.add(typeVar);
179                                       List<Access> typeBounds = new List<Access>();
180                                       for (Access typeBound : typeVar.getTypeBoundList())
181                                               typeBounds.add((Access) typeBound.cloneSubtree());
182                                       typeVars.add(
183                                                       new TypeVariable(
184                                                               new Modifiers(),
185                                                               substName,
186                                                               new List<BodyDecl>(),
187                                                               typeBounds));
188                               }
189                       }
190    
191                       List<ParameterDeclaration> substParameters =
192                               new List<ParameterDeclaration>();
193                       for (ParameterDeclaration param : decl.getParameterList()) {
194                               substParameters.add(param.substituted(
195                                                       originalTypeVars, typeVars));
196                       }
197    
198                       List<Access> substExceptions = new List<Access>();
199                       for (Access exception : decl.getExceptionList()) {
200                               substExceptions.add(exception.substituted(
201                                                       originalTypeVars, typeVars));
202                       }
203    
204                       PlaceholderMethodDecl placeholderMethod =
205                               new PlaceholderMethodDecl(
206                                       (Modifiers) decl.getModifiers().cloneSubtree(),
207                                       (Access) returnType.cloneSubtree(),
208                                       "#"+getID(),
209                                       substParameters,
210                                       substExceptions,
211                                       new Opt(new Block()),
212                                       typeVars);
213    
214                       placeholderMethods.add(placeholderMethod);
215               }
216               return placeholderMethods;
217       }
218    
219       /**
220        * Select potentially applicable method declarations
221        * from a set of candidates.
222        * Type inference is applied to the (potentially) applicable candidates.
223        */
224       protected Collection<MethodDecl> DiamondAccess.potentiallyApplicable(
225                       List<PlaceholderMethodDecl> candidates) {
226               Collection<MethodDecl> potentiallyApplicable =
227                       new LinkedList<MethodDecl>();
228               for (GenericMethodDecl candidate : candidates) {
229                       if (potentiallyApplicable(candidate)) {
230                               MethodDecl decl = candidate.lookupParMethodDecl(
231                                               typeArguments(candidate));
232                               potentiallyApplicable.add(decl);
233                       }
234               }
235               return potentiallyApplicable;
236       }
237    
238       /**
239        * @return false if the candidate method is not applicable.
240        */
241       protected boolean DiamondAccess.potentiallyApplicable(
242                       GenericMethodDecl candidate) {
243               if (candidate.isVariableArity() &&
244                               !(getClassInstanceExpr().arity() >= candidate.arity()-1))
245                       return false;
246               if (!candidate.isVariableArity() &&
247                               !(getClassInstanceExpr().arity() == candidate.arity()))
248                       return false;
249    
250               java.util.List<TypeDecl> typeArgs = typeArguments(candidate);
251               if (typeArgs.size() != 0) {
252                       if (candidate.getNumTypeParameter() != typeArgs.size())
253                               return false;
254                       for (int i = 0; i < candidate.getNumTypeParameter(); i++)
255                               if (!typeArgs.get(i).subtype(
256                                                       candidate.original().getTypeParameter(i)))
257                                       return false;
258               }
259               return true;
260       }
261    
262       inh TypeDecl DiamondAccess.typeObject();
263    
264       /**
265        * Type inference for placeholder methods.
266        */
267       syn lazy java.util.List<TypeDecl> DiamondAccess.typeArguments(
268                       MethodDecl decl) {
269               java.util.List<TypeDecl> typeArguments = new LinkedList<TypeDecl>();
270               if (decl instanceof GenericMethodDecl) {
271                       GenericMethodDecl method = (GenericMethodDecl) decl;
272                       Collection<TypeDecl> arguments = computeConstraints(method);
273                       if (arguments.isEmpty())
274                               return typeArguments;
275                       int i = 0;
276                       for (TypeDecl argument : arguments) {
277                               if (argument == null) {
278                                       TypeVariable v = method.original().getTypeParameter(i);
279                                       if (v.getNumTypeBound() == 0)
280                                               argument = typeObject();
281                                       else if (v.getNumTypeBound() == 1)
282                                               argument = v.getTypeBound(0).type();
283                                       else
284                                               argument = v.lubType();
285                               }
286                               typeArguments.add(argument);
287    
288                               i += 1;
289                       }
290               }
291               return typeArguments;
292       }
293    
294       /**
295        * Diamond type inference.
296        */
297       public Collection<TypeDecl> DiamondAccess.computeConstraints(
298                       GenericMethodDecl decl) {
299               Constraints c = new Constraints();
300               // store type parameters
301               for (int i = 0; i < decl.original().getNumTypeParameter(); i++)
302                       c.addTypeVariable(decl.original().getTypeParameter(i));
303    
304               ClassInstanceExpr instanceExpr = getClassInstanceExpr();
305               for (int i = 0; i < instanceExpr.getNumArg(); i++) {
306                       TypeDecl A = instanceExpr.getArg(i).type();
307                       int index = i >= decl.getNumParameter() ?
308                               decl.getNumParameter() - 1 : i;
309                       TypeDecl F = decl.getParameter(index).type();
310                       if (decl.getParameter(index) instanceof
311                                       VariableArityParameterDeclaration &&
312                                       (instanceExpr.getNumArg() != decl.getNumParameter() ||
313                                       !A.isArrayDecl())) {
314                               F = F.componentType();
315                       }
316                       c.convertibleTo(A, F);
317               }
318               if (c.rawAccess)
319                       return new ArrayList();
320    
321               c.resolveEqualityConstraints();
322               c.resolveSupertypeConstraints();
323    
324               if (c.unresolvedTypeArguments()) {
325                       TypeDecl S = assignConvertedType();
326                       if (S.isUnboxedPrimitive())
327                               S = S.boxed();
328                       TypeDecl R = decl.type();
329                       if (R.isVoid())
330                               R = typeObject();
331    
332                       c.convertibleFrom(S, R);
333                       c.resolveEqualityConstraints();
334                       c.resolveSupertypeConstraints();
335                       c.resolveSubtypeConstraints();
336               }
337    
338               return c.typeArguments();
339       }
340    
341       /**
342        * @return true if the method is applicable by subtyping
343        */
344       protected boolean DiamondAccess.applicableBySubtyping(
345                       ClassInstanceExpr expr, MethodDecl method) {
346               if (method.getNumParameter() != expr.getNumArg())
347                       return false;
348               for (int i = 0; i < method.getNumParameter(); i++)
349                       if(!expr.getArg(i).type().instanceOf(method.getParameter(i).type()))
350                               return false;
351               return true;
352       }
353    
354       /**
355        * @return true if the method is applicable by method invocation conversion
356        */
357       protected boolean DiamondAccess.applicableByMethodInvocationConversion(
358                       ClassInstanceExpr expr, MethodDecl method) {
359               if (method.getNumParameter() != expr.getNumArg())
360                       return false;
361               for (int i = 0; i < method.getNumParameter(); i++)
362                       if (!expr.getArg(i).type().methodInvocationConversionTo(
363                                               method.getParameter(i).type()))
364                               return false;
365               return true;
366       }
367    
368       /**
369        * @return true if the method is applicable by variable arity
370        */
371       protected boolean DiamondAccess.applicableByVariableArity(
372                       ClassInstanceExpr expr, MethodDecl method) {
373               for (int i = 0; i < method.getNumParameter() - 1; i++)
374                       if(!expr.getArg(i).type().methodInvocationConversionTo(
375                                               method.getParameter(i).type()))
376                               return false;
377               for (int i = method.getNumParameter() - 1; i < expr.getNumArg(); i++)
378                       if (!expr.getArg(i).type().methodInvocationConversionTo(
379                                               method.lastParameter().type().componentType()))
380                               return false;
381               return true;
382       }
383    
384       /**
385        * Builds a copy of this ParameterDeclaration node where all occurrences
386        * of type variables in the original type parameter list have been replaced
387        * by the substitution type parameters.
388        *
389        * @return the substituted ParameterDeclaration node
390        */
391       syn ParameterDeclaration ParameterDeclaration.substituted(
392                       Collection<TypeVariable> original,
393                       List<TypeVariable> substitution) =
394               new ParameterDeclaration(
395                               (Modifiers) getModifiers().cloneSubtree(),
396                               getTypeAccess().substituted(original, substitution),
397                               getID());
398    
399       /**
400        * Builds a copy of this Access node where all occurrences
401        * of type variables in the original type parameter list have been replaced
402        * by the substitution type parameters.
403        *
404        * @return the substituted Access node
405        */
406       syn Access Access.substituted(
407                       Collection<TypeVariable> original,
408                       List<TypeVariable> substitution) =
409               (Access) cloneSubtree();
410    
411       /**
412        * Builds a copy of this TypeAccess node where all occurrences
413        * of type variables in the original type parameter list have been replaced
414        * by the substitution type parameters.
415        *
416        * @return the substituted TypeAccess node
417        */
418       eq TypeAccess.substituted(
419                       Collection<TypeVariable> original,
420                       List<TypeVariable> substitution) {
421               TypeDecl decl = decl();
422               int i = 0;
423               for (TypeVariable typeVar : original) {
424                       if (typeVar == decl)
425                               return new TypeAccess(substitution.getChild(i).getID());
426                       i += 1;
427               }
428               return super.substituted(original, substitution);
429       }
430    
431       /**
432        * Builds a copy of this ParTypeAccess node where all occurrences
433        * of type variables in the original type parameter list have been replaced
434        * by the substitution type parameters.
435        *
436        * @return the substituted ParTypeAccess node
437        */
438       eq ParTypeAccess.substituted(
439                       Collection<TypeVariable> original,
440                       List<TypeVariable> substitution) {
441               List<Access> substArgs = new List<Access>();
442               for (Access arg : getTypeArgumentList())
443                       substArgs.add(arg.substituted(original, substitution));
444               return new ParTypeAccess(
445                               getTypeAccess().substituted(original, substitution),
446                               substArgs);
447       }
448    
449       /**
450        * @return true if this access is part of an anonymous class declaration
451        */
452       inh boolean DiamondAccess.isAnonymousDecl();
453    
454       /**
455        * @return true if this ClassInstanceExpr is an anonymous class declaration
456        */
457       eq ClassInstanceExpr.getAccess().isAnonymousDecl() = hasTypeDecl();
458    
459       /**
460        * @return false
461        */
462       eq Program.getChild().isAnonymousDecl() = false;
463    
464       /**
465        * @return true if the Access is part of a generic constructor invocation
466        * with explicit type arguments
467        */
468       inh boolean DiamondAccess.isExplicitGenericConstructorAccess();
469    
470       /**
471        * @return true
472        */
473       eq ParClassInstanceExpr.getAccess().isExplicitGenericConstructorAccess() =
474               true;
475    
476       /**
477        * @return false
478        */
479       eq Program.getChild(int i).isExplicitGenericConstructorAccess() = false;
480    
481       /**
482        * Checks if this diamond access is legal.
483        * The diamond access is not legal if it either is part of an inner class
484        * declaration, if it is used to access a non-generic type, or if it is
485        * part of a call to a generic constructor with explicit type arguments.
486        */
487       public void DiamondAccess.typeCheck() {
488               if (isAnonymousDecl())
489                       error("the diamond operator can not be used with "+
490                                       "anonymous classes");
491               if (isExplicitGenericConstructorAccess())
492                       error("the diamond operator may not be used with generic "+
493                                       "constructors with explicit type parameters");
494               if (getClassInstanceExpr() == null)
495                       error("the diamond operator can only be used in "+
496                                       "class instance expressions");
497               if (!(getTypeAccess().type() instanceof ParClassDecl))
498                       error("the diamond operator can only be used to "+
499                                       "instantiate generic classes");
500       }
501    
502       /**
503        * Pretty printing of diamond access.
504        */
505       public void DiamondAccess.toString(StringBuffer sb) {
506               getTypeAccess().toString(sb);
507               sb.append("<>");
508       }
509    }