001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     *               2011-2015, Jesper Öqvist <jesper.oqvist@cs.lth.se>
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions are met:
007     *
008     * 1. Redistributions of source code must retain the above copyright notice,
009     * this list of conditions and the following disclaimer.
010     *
011     * 2. Redistributions in binary form must reproduce the above copyright notice,
012     * this list of conditions and the following disclaimer in the documentation
013     * and/or other materials provided with the distribution.
014     *
015     * 3. Neither the name of the copyright holder nor the names of its
016     * contributors may be used to endorse or promote products derived from this
017     * software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
029     * POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    /**
033     * Type inference for generic instance creation.
034     *
035     * According to the JLSv7 $15.9.1, type inference for generic
036     * instance creation uses the type inference for generic methods.
037     *
038     * Empty type argument lists (&lt;&gt;) are parsed as DiamondAccess
039     * nodes. The type of the DiamondAccess is computed by creating placeholder
040     * methods and using generic method type inference from Java1.5Frontend
041     * to find the inferred type arguments for the DiamondAccess.
042     */
043    aspect Diamond {
044    
045      public void StandInMethodDecl.nameCheck() { }
046      public void StandInMethodDecl.typeCheck() { }
047      public void StandInMethodDecl.exceptionHandling() { }
048      public void StandInMethodDecl.checkUnreachableStmt() { }
049      public void StandInMethodDecl.definiteAssignment() { }
050      public void StandInMethodDecl.checkModifiers() { }
051    
052      /**
053       * If this DiamondAccess node constitutes a legal use of
054       * the diamond operator, the inferred generic type for the
055       * enclosing class instance expression is returned.
056       */
057      eq DiamondAccess.type() {
058        TypeDecl accessType = getTypeAccess().type();
059    
060        if (isAnonymousDecl()) {
061          return accessType;
062        }
063    
064        if (getClassInstanceExpr() == null) {
065          // It is an error if the DiamondAccess does not occurr
066          // within a class instance creation expression, but this
067          // error is handled in typeCheck.
068          return accessType;
069        }
070    
071        if (!(accessType instanceof ParClassDecl)) {
072          // It is an error if the TypeDecl of a DiamondAccess is not
073          // a generic type, but this error is handled in typeCheck.
074          return accessType;
075        }
076    
077        SimpleSet maxSpecific = chooseConstructor();
078    
079        if (maxSpecific.isEmpty()) {
080          return getTypeAccess().type();
081        }
082    
083        MethodDecl constructor = (MethodDecl) maxSpecific.iterator().next();
084        return constructor.type();
085      }
086    
087      syn boolean Access.isDiamond() = false;
088      eq DiamondAccess.isDiamond() = true;
089    
090      inh ClassInstanceExpr DiamondAccess.getClassInstanceExpr();
091      eq ClassInstanceExpr.getAccess().getClassInstanceExpr() = this;
092      eq Program.getChild(int i).getClassInstanceExpr() = null; // TODO
093    
094      protected static SimpleSet DiamondAccess.mostSpecific(SimpleSet maxSpecific, MethodDecl decl) {
095        if (maxSpecific.isEmpty()) {
096          maxSpecific = maxSpecific.add(decl);
097        } else {
098          MethodDecl other = (MethodDecl) maxSpecific.iterator().next();
099          if (decl.moreSpecificThan(other)) {
100            maxSpecific = decl;
101          } else if (!other.moreSpecificThan(decl)) {
102            maxSpecific = maxSpecific.add(decl);
103          }
104        }
105        return maxSpecific;
106      }
107    
108      /**
109       * Choose a constructor for the diamond operator using placeholder
110       * methods.
111       */
112      protected SimpleSet DiamondAccess.chooseConstructor() {
113        ClassInstanceExpr instanceExpr = getClassInstanceExpr();
114        TypeDecl type = getTypeAccess().type();
115    
116        assert instanceExpr != null;
117        assert type instanceof ParClassDecl;
118    
119        GenericClassDecl genericType = (GenericClassDecl) ((ParClassDecl) type).genericDecl();
120    
121        List<StandInMethodDecl> placeholderMethods = genericType.getStandInMethodList();
122    
123        SimpleSet maxSpecific = SimpleSet.emptySet;
124        Collection<MethodDecl> potentiallyApplicable = potentiallyApplicable(placeholderMethods);
125        for (MethodDecl candidate : potentiallyApplicable) {
126          if (applicableBySubtyping(instanceExpr, candidate)
127              || applicableByMethodInvocationConversion(instanceExpr, candidate)
128              || applicableByVariableArity(instanceExpr, candidate)) {
129            maxSpecific = mostSpecific(maxSpecific, candidate);
130          }
131        }
132        return maxSpecific;
133      }
134    
135      /**
136       * The placeholder method list for the constructors of this generic
137       * class.
138       *
139       * @return list of placeholder methods
140       */
141      syn nta List<StandInMethodDecl> GenericClassDecl.getStandInMethodList() {
142        List<StandInMethodDecl> placeholderMethods = new List<StandInMethodDecl>();
143        List<TypeVariable> typeParams = getTypeParameterList();
144        List<TypeVariable> classTypeVars = new List<TypeVariable>();
145        List<Access> typeArgs = new List<Access>();
146    
147        // Copy the list of type parameters.
148        int arg = 0;
149        for (Iterator iter = typeParams.iterator(); iter.hasNext(); ++arg) {
150          String substName = "#"+arg;
151          typeArgs.add(new TypeAccess(substName));
152    
153          TypeVariable typeVar = (TypeVariable) iter.next();
154          List<Access> typeBounds = new List<Access>();
155          for (Access typeBound : typeVar.getTypeBoundList()) {
156            typeBounds.add((Access) typeBound.treeCopyNoTransform());
157          }
158          classTypeVars.add(new TypeVariable(
159              new Modifiers(),
160              substName,
161              new List<BodyDecl>(),
162              typeBounds));
163        }
164    
165        ParTypeAccess returnType = new ParTypeAccess(
166            createQualifiedAccess(),
167            typeArgs);
168    
169        for (Iterator iter = constructors().iterator(); iter.hasNext(); ) {
170          ConstructorDecl decl = (ConstructorDecl) iter.next();
171          if (decl instanceof ConstructorDeclSubstituted) {
172            decl = ((ConstructorDeclSubstituted) decl).getOriginal();
173          }
174    
175          // Filter accessible constructors.
176          if (!decl.accessibleFrom(hostType())) {
177            continue;
178          }
179    
180          Collection<TypeVariable> originalTypeVars = new LinkedList<TypeVariable>();
181          List<TypeVariable> typeVars = new List<TypeVariable>();
182          for (TypeVariable typeVar : typeParams) {
183            originalTypeVars.add(typeVar);
184          }
185          for (TypeVariable typeVar : classTypeVars) {
186            typeVars.add((TypeVariable) typeVar.treeCopyNoTransform());
187          }
188    
189          if (decl.isGeneric()) {
190            GenericConstructorDecl genericDecl = decl.genericDecl();
191            List<TypeVariable> typeVariables = new List<TypeVariable>();
192            for (int i = 0; i < genericDecl.getNumTypeParameter(); ++i) {
193              String substName = "#" + (arg+i);
194    
195              TypeVariable typeVar = genericDecl.getTypeParameter(i);
196              originalTypeVars.add(typeVar);
197              List<Access> typeBounds = new List<Access>();
198              for (Access typeBound : typeVar.getTypeBoundList()) {
199                typeBounds.add((Access) typeBound.treeCopyNoTransform());
200              }
201              typeVars.add(new TypeVariable(
202                    new Modifiers(),
203                    substName,
204                    new List<BodyDecl>(),
205                    typeBounds));
206            }
207          }
208    
209          List<ParameterDeclaration> substParameters = new List<ParameterDeclaration>();
210          for (ParameterDeclaration param : decl.getParameterList()) {
211            substParameters.add(param.substituted(originalTypeVars, typeVars));
212          }
213    
214          List<Access> substExceptions = new List<Access>();
215          for (Access exception : decl.getExceptionList()) {
216            substExceptions.add(exception.substituted(originalTypeVars, typeVars));
217          }
218    
219          StandInMethodDecl placeholderMethod = new StandInMethodDecl(
220              (Modifiers) decl.getModifiers().treeCopyNoTransform(),
221              (Access) returnType.treeCopyNoTransform(),
222              "#" + getID(),
223              substParameters,
224              substExceptions,
225              new Opt(new Block()),
226              typeVars);
227    
228          placeholderMethods.add(placeholderMethod);
229        }
230        return placeholderMethods;
231      }
232    
233      /**
234       * Select potentially applicable method declarations
235       * from a set of candidates.
236       * Type inference is applied to the (potentially) applicable candidates.
237       */
238      protected Collection<MethodDecl> DiamondAccess.potentiallyApplicable(
239          List<StandInMethodDecl> candidates) {
240        Collection<MethodDecl> potentiallyApplicable = new LinkedList<MethodDecl>();
241        for (GenericMethodDecl candidate : candidates) {
242          if (potentiallyApplicable(candidate)) {
243            MethodDecl decl = candidate.lookupParMethodDecl(
244                inferTypeArguments(
245                    candidate.type(),
246                    candidate.getParameterList(),
247                    getClassInstanceExpr().getArgList(),
248                    candidate.getTypeParameterList()));
249            potentiallyApplicable.add(decl);
250          }
251        }
252        return potentiallyApplicable;
253      }
254    
255      /**
256       * Test if a method is applicable for this diamond access.
257       * @param candidate candidate method
258       * @return false if the candidate method is not applicable.
259       */
260      protected boolean DiamondAccess.potentiallyApplicable(
261          GenericMethodDecl candidate) {
262        if (candidate.isVariableArity() && !(getClassInstanceExpr().arity() >= candidate.arity()-1)) {
263          return false;
264        }
265        if (!candidate.isVariableArity() && !(getClassInstanceExpr().arity() == candidate.arity())) {
266          return false;
267        }
268    
269        java.util.List<TypeDecl> typeArgs = inferTypeArguments(
270            candidate.type(),
271            candidate.getParameterList(),
272            getClassInstanceExpr().getArgList(),
273            candidate.getTypeParameterList());
274        Parameterization par = new SimpleParameterization(candidate.getTypeParameterList(), typeArgs);
275        if (typeArgs.size() != 0) {
276          if (candidate.getNumTypeParameter() != typeArgs.size()) {
277            return false;
278          }
279          for (int i = 0; i < candidate.getNumTypeParameter(); i++) {
280            if (!typeArgs.get(i).withinBounds(candidate.original().getTypeParameter(i), par)) {
281              return false;
282            }
283          }
284        }
285        return true;
286      }
287    
288      /**
289       * @return true if the method is applicable by subtyping
290       */
291      protected boolean DiamondAccess.applicableBySubtyping(ClassInstanceExpr expr, MethodDecl method) {
292        if (method.getNumParameter() != expr.getNumArg()) {
293          return false;
294        }
295        for (int i = 0; i < method.getNumParameter(); i++) {
296          if (!expr.getArg(i).type().instanceOf(method.getParameter(i).type())) {
297            return false;
298          }
299        }
300        return true;
301      }
302    
303      /**
304       * @return true if the method is applicable by method invocation conversion
305       */
306      protected boolean DiamondAccess.applicableByMethodInvocationConversion(
307          ClassInstanceExpr expr, MethodDecl method) {
308        if (method.getNumParameter() != expr.getNumArg()) {
309          return false;
310        }
311        for (int i = 0; i < method.getNumParameter(); i++) {
312          if (!expr.getArg(i).type().methodInvocationConversionTo(
313              method.getParameter(i).type())) {
314            return false;
315          }
316        }
317        return true;
318      }
319    
320      /**
321       * @return true if the method is applicable by variable arity
322       */
323      protected boolean DiamondAccess.applicableByVariableArity(
324          ClassInstanceExpr expr, MethodDecl method) {
325        for (int i = 0; i < method.getNumParameter() - 1; i++) {
326          if (!expr.getArg(i).type().methodInvocationConversionTo(
327              method.getParameter(i).type())) {
328            return false;
329          }
330        }
331        for (int i = method.getNumParameter() - 1; i < expr.getNumArg(); i++) {
332          if (!expr.getArg(i).type().methodInvocationConversionTo(
333              method.lastParameter().type().componentType())) {
334            return false;
335          }
336        }
337        return true;
338      }
339    
340      /**
341       * Builds a copy of this ParameterDeclaration node where all occurrences
342       * of type variables in the original type parameter list have been replaced
343       * by the substitution type parameters.
344       *
345       * @return the substituted ParameterDeclaration node
346       */
347      syn ParameterDeclaration ParameterDeclaration.substituted(Collection<TypeVariable> original,
348          List<TypeVariable> substitution) =
349        new ParameterDeclaration(
350            (Modifiers) getModifiers().treeCopyNoTransform(),
351            getTypeAccess().substituted(original, substitution),
352            getID());
353    
354      /**
355       * Builds a copy of this Access node where all occurrences
356       * of type variables in the original type parameter list have been replaced
357       * by the substitution type parameters.
358       *
359       * @return the substituted Access node
360       */
361      syn Access Access.substituted(Collection<TypeVariable> original,
362          List<TypeVariable> substitution) = (Access) treeCopyNoTransform();
363    
364      /**
365       * Builds a copy of this TypeAccess node where all occurrences
366       * of type variables in the original type parameter list have been replaced
367       * by the substitution type parameters.
368       *
369       * @return the substituted TypeAccess node
370       */
371      eq TypeAccess.substituted(Collection<TypeVariable> original, List<TypeVariable> substitution) {
372        TypeDecl decl = decl();
373        int i = 0;
374        for (TypeVariable typeVar : original) {
375          if (typeVar == decl) {
376            return new TypeAccess(substitution.getChild(i).getID());
377          }
378          i += 1;
379        }
380        return super.substituted(original, substitution);
381      }
382    
383      /**
384       * Builds a copy of this ParTypeAccess node where all occurrences
385       * of type variables in the original type parameter list have been replaced
386       * by the substitution type parameters.
387       *
388       * @return the substituted ParTypeAccess node
389       */
390      eq ParTypeAccess.substituted(Collection<TypeVariable> original, List<TypeVariable> substitution) {
391        List<Access> substArgs = new List<Access>();
392        for (Access arg : getTypeArgumentList()) {
393          substArgs.add(arg.substituted(original, substitution));
394        }
395        return new ParTypeAccess(getTypeAccess().substituted(original, substitution), substArgs);
396      }
397    
398      /**
399       * @return true if this access is part of an anonymous class declaration
400       */
401      inh boolean DiamondAccess.isAnonymousDecl();
402    
403      /**
404       * @return true if this ClassInstanceExpr is an anonymous class declaration
405       */
406      eq ClassInstanceExpr.getAccess().isAnonymousDecl() = hasTypeDecl();
407    
408      /**
409       * @return false
410       */
411      eq Program.getChild().isAnonymousDecl() = false;
412    
413      /**
414       * @return true if the Access is part of a generic constructor invocation
415       * with explicit type arguments
416       */
417      inh boolean DiamondAccess.isExplicitGenericConstructorAccess();
418    
419      /**
420       * @return true
421       */
422      eq ParClassInstanceExpr.getAccess().isExplicitGenericConstructorAccess() = true;
423    
424      /**
425       * @return false
426       */
427      eq Program.getChild(int i).isExplicitGenericConstructorAccess() = false;
428    
429      /**
430       * Checks if this diamond access is legal.
431       * The diamond access is not legal if it either is part of an inner class
432       * declaration, if it is used to access a non-generic type, or if it is
433       * part of a call to a generic constructor with explicit type arguments.
434       */
435      public void DiamondAccess.typeCheck() {
436        if (isAnonymousDecl()) {
437          error("the diamond operator can not be used with anonymous classes");
438        }
439        if (isExplicitGenericConstructorAccess()) {
440          error("the diamond operator may not be used with generic "
441              + "constructors with explicit type parameters");
442        }
443        if (getClassInstanceExpr() == null) {
444          error("the diamond operator can only be used in class instance expressions");
445        }
446        if (!(getTypeAccess().type() instanceof ParClassDecl)) {
447          error("the diamond operator can only be used to instantiate generic classes");
448        }
449      }
450    
451    }