001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     *                    2014, 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    aspect GenericMethods {
033      public void ParMethodAccess.typeCheck() {
034        super.typeCheck();
035        if (!decl().hostType().isUnknown()) {
036          // JLS 15.12.2.1 specifically permits type arguments on non-generic method access
037          if (decl() instanceof ParMethodDecl) {
038            ParMethodDecl m = (ParMethodDecl) decl();
039            if (!(m instanceof RawMethodDecl) && m.numTypeParameter() != getNumTypeArgument()) {
040              errorf("generic method %s requires %s type arguments",
041                  m.signature(), m.numTypeParameter());
042            } else {
043            }
044          }
045        }
046      }
047    
048      syn lazy final MethodDecl GenericMethodDecl.rawMethodDecl() =
049          lookupParMethodDecl(Collections.<TypeDecl>emptyList());
050    
051      syn lazy GenericMethodDecl ParMethodDecl.genericMethodDecl() = getGenericMethodDecl();
052    
053      syn lazy GenericConstructorDecl ParConstructorDecl.genericConstructorDecl() =
054          getGenericConstructorDecl();
055    
056      syn nta MethodDecl GenericMethodDecl.lookupParMethodDecl(Collection<TypeDecl> typeArguments) =
057          newParMethodDecl(typeArguments);
058    
059      public ParMethodDecl GenericMethodDecl.newParMethodDecl(Collection<TypeDecl> typeArguments) {
060        ParMethodDecl methodDecl = typeArguments.isEmpty() ? new RawMethodDecl() : new ParMethodDecl();
061    
062        // Adding a link to GenericMethodDecl to be used during substitution
063        // instead of the not yet existing parent link.
064        methodDecl.setGenericMethodDecl(this);
065    
066        List<Access> list = new List<Access>();
067        if (typeArguments.isEmpty()) {
068          GenericMethodDecl original = original();
069          for (int i = 0; i < original.getNumTypeParameter(); i++) {
070            list.add(original.getTypeParameter(i).erasure().createBoundAccess());
071          }
072        } else {
073          for (TypeDecl arg : typeArguments) {
074            list.add(arg.createBoundAccess());
075          }
076        }
077        methodDecl.setTypeArgumentList(list);
078        methodDecl.setModifiers((Modifiers) getModifiers().treeCopyNoTransform());
079        methodDecl.setTypeAccess(getTypeAccess().type().substituteReturnType(methodDecl));
080        methodDecl.setID(getID());
081        methodDecl.setParameterList(getParameterList().substitute(methodDecl));
082        methodDecl.setExceptionList(getExceptionList().substitute(methodDecl));
083        return methodDecl;
084      }
085    
086      syn nta ParConstructorDecl GenericConstructorDecl.lookupParConstructorDecl(Collection<TypeDecl> typeArguments) =
087          newParConstructorDecl(typeArguments);
088    
089      public ParConstructorDecl GenericConstructorDecl.newParConstructorDecl(Collection<TypeDecl> typeArguments) {
090        ParConstructorDecl constructorDecl = typeArguments.isEmpty()
091            ? new RawConstructorDecl()
092            : new ParConstructorDecl();
093    
094        // Adding a link to GenericConstructorDecl to be used during substitution
095        // instead of the not yet existing parent link.
096        constructorDecl.setGenericConstructorDecl(this);
097    
098        List<Access> list = new List<Access>();
099        if (typeArguments.isEmpty()) {
100          GenericConstructorDecl original = original();
101          for (int i = 0; i < original.getNumTypeParameter(); i++) {
102            list.add(original.getTypeParameter(i).erasure().createBoundAccess());
103          }
104        } else {
105          for (TypeDecl arg : typeArguments) {
106            list.add(arg.createBoundAccess());
107          }
108        }
109        constructorDecl.setTypeArgumentList(list);
110        constructorDecl.setModifiers((Modifiers) getModifiers().treeCopyNoTransform());
111        constructorDecl.setID(getID());
112        constructorDecl.setParameterList(getParameterList().substitute(constructorDecl));
113        constructorDecl.setExceptionList(getExceptionList().substitute(constructorDecl));
114        return constructorDecl;
115      }
116    
117    }
118    
119    aspect TypeCheck {
120      // Disable error checking in instantiated generic methods
121      public void ParMethodDecl.collectErrors() {
122      }
123    
124      /**
125       * Allows covariant return types.
126       * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.5">JLSv7 8.4.5</a>
127       * @param other
128       * @return {@code true} if this method is return-type-substitutable for the
129       * other declaration
130       */
131      refine TypeHierarchyCheck eq MethodDecl.mayOverride(MethodDecl m) {
132        TypeDecl typeA = type();
133        TypeDecl typeB = m.type();
134        if (typeA == typeB) {
135          return true;
136        }
137        if (typeA.isPrimitive() && typeB.isPrimitive()) {
138          return false;
139        }
140        return typeA.subtype(typeB);
141      }
142    
143      /**
144       * @param other
145       * @return {@code true} if the signature of this method is same as the
146       * the signature of the argument method, taking generics into consideration.
147       */
148      refine LookupMethod eq MethodDecl.sameSignature(MethodDecl other) {
149        if (!refined(other)) {
150          return false;
151        }
152        for (int i = 0; i < getNumParameter(); ++i) {
153          TypeDecl p1 = getParameter(i).type();
154          TypeDecl p2 = other.getParameter(i).type();
155          // JLSv7 $8.4.8.1 exception: if one parameter type is raw, then don't check type bounds
156          if (p1 != p2 && !p1.isRawType() && !p2.isRawType()) {
157            return false;
158          }
159        }
160        return true;
161      }
162    
163      refine TypeHierarchyCheck protected void TypeDecl.checkAbstractMethodDecls(MethodDecl m1, MethodDecl m2) {
164    
165        if (!m1.sameSignature(m2)) {
166          errorf("method declarations %s and %s in interface %s are incompatible",
167              m1.fullSignature(), m2.fullSignature(), fullName());
168        } else {
169          refined(m1, m2);
170        }
171      }
172    }
173    
174    aspect GenericMethodsNameAnalysis {
175    
176      eq ParMethodAccess.getTypeArgument().nameType() = NameType.TYPE_NAME;
177      eq ParMethodAccess.getTypeArgument().lookupType(String name) = unqualifiedScope().lookupType(name);
178      eq GenericMethodDecl.getTypeParameter().nameType() = NameType.TYPE_NAME;
179    
180      inh SimpleSet GenericMethodDecl.lookupType(String name);
181      syn SimpleSet GenericMethodDecl.localLookupType(String name) {
182        for (int i = 0; i < getNumTypeParameter(); i++) {
183          if (original().getTypeParameter(i).name().equals(name)) {
184            return SimpleSet.emptySet.add(original().getTypeParameter(i));
185          }
186        }
187        return SimpleSet.emptySet;
188      }
189      eq GenericMethodDecl.getChild().lookupType(String name) = localLookupType(name).isEmpty() ? lookupType(name) : localLookupType(name);
190    
191      eq ParConstructorAccess.getTypeArgument().nameType() = NameType.TYPE_NAME;
192      eq ParConstructorAccess.getTypeArgument().lookupType(String name) = unqualifiedScope().lookupType(name);
193      eq ParSuperConstructorAccess.getTypeArgument().nameType() = NameType.TYPE_NAME;
194      eq ParSuperConstructorAccess.getTypeArgument().lookupType(String name) = unqualifiedScope().lookupType(name);
195      eq GenericConstructorDecl.getTypeParameter().nameType() = NameType.TYPE_NAME;
196    
197      inh SimpleSet GenericConstructorDecl.lookupType(String name);
198      syn SimpleSet GenericConstructorDecl.localLookupType(String name) {
199        for (int i = 0; i < getNumTypeParameter(); i++) {
200          if (original().getTypeParameter(i).name().equals(name)) {
201            return SimpleSet.emptySet.add(original().getTypeParameter(i));
202          }
203        }
204        return SimpleSet.emptySet;
205      }
206      eq GenericConstructorDecl.getChild().lookupType(String name) = localLookupType(name).isEmpty() ? lookupType(name) : localLookupType(name);
207    
208      eq ParClassInstanceExpr.getTypeArgument().nameType() = NameType.TYPE_NAME;
209      eq ParClassInstanceExpr.getTypeArgument().lookupType(String name) = unqualifiedScope().lookupType(name);
210    
211    }
212    
213    aspect GenericMethodsPrettyPrint {
214    
215      public void ParMethodAccess.prettyPrint(PrettyPrinter out) {
216        out.print("<");
217        for (int i = 0; i < getNumTypeArgument(); i++) {
218          if (i != 0) {
219            out.print(", ");
220          }
221          out.print(getTypeArgument(i));
222        }
223        out.print(">");
224        super.prettyPrint(out);
225      }
226    
227      public void ParConstructorAccess.prettyPrint(PrettyPrinter out) {
228        out.print("<");
229        for (int i = 0; i < getNumTypeArgument(); i++) {
230          if (i != 0) {
231            out.print(", ");
232          }
233          out.print(getTypeArgument(i));
234        }
235        out.print(">");
236        super.prettyPrint(out);
237      }
238    
239      public void ParSuperConstructorAccess.prettyPrint(PrettyPrinter out) {
240        out.print("<");
241        for (int i = 0; i < getNumTypeArgument(); i++) {
242          if (i != 0) {
243            out.print(", ");
244          }
245          out.print(getTypeArgument(i));
246        }
247        out.print(">");
248        super.prettyPrint(out);
249      }
250    
251      public void ParClassInstanceExpr.prettyPrint(PrettyPrinter out) {
252        out.print("<");
253        for (int i = 0; i < getNumTypeArgument(); i++) {
254          if (i != 0) {
255            out.print(", ");
256          }
257          out.print(getTypeArgument(i));
258        }
259        out.print(">");
260        super.prettyPrint(out);
261      }
262    
263    }