001    /* Copyright (c) 2014, Erik Hogeman <Erik.Hogemn@gmail.com>
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     *     * Redistributions of source code must retain the above copyright notice,
008     *       this list of conditions and the following disclaimer.
009     *     * Redistributions in binary form must reproduce the above copyright
010     *       notice, this list of conditions and the following disclaimer in the
011     *       documentation and/or other materials provided with the distribution.
012     *     * Neither the name of the Lund University nor the names of its
013     *       contributors may be used to endorse or promote products derived from
014     *       this software without specific prior written permission.
015     *
016     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026     * POSSIBILITY OF SUCH DAMAGE.
027     */
028    aspect PolyExpressions {
029      syn lazy boolean Expr.isBooleanExpression() =
030          !isPolyExpression() && type().isBoolean();
031    
032      eq ParExpr.isBooleanExpression() =
033          getExpr().isBooleanExpression();
034    
035      eq ClassInstanceExpr.isBooleanExpression() {
036        if (getAccess() instanceof TypeAccess) {
037          TypeAccess typeAccess = (TypeAccess) getAccess();
038          return typeAccess.name().equals("Boolean");
039        }
040        return false;
041      }
042    
043      eq MethodAccess.isBooleanExpression() {
044        MethodDecl decl = decl();
045        if (decl instanceof ParMethodDecl) {
046          return ((ParMethodDecl) decl).genericMethodDecl().type().isBoolean();
047        } else {
048          return decl.type().isBoolean();
049        }
050      }
051    
052      eq ConditionalExpr.isBooleanExpression() =
053          isBooleanConditional();
054    
055      syn lazy boolean ConditionalExpr.isBooleanConditional() =
056          getTrueExpr().isBooleanExpression() && getFalseExpr().isBooleanExpression();
057    
058    
059      syn lazy boolean Expr.isNumericExpression() =
060          !isPolyExpression() && type().isNumericType();
061    
062      eq MethodAccess.isNumericExpression() {
063        MethodDecl decl = decl();
064        if (decl instanceof ParMethodDecl) {
065          return ((ParMethodDecl) decl).genericMethodDecl().type().isNumericType();
066        } else {
067          return decl.type().isNumericType();
068        }
069      }
070    
071      eq ConditionalExpr.isNumericExpression() =
072          isNumericConditional();
073    
074      syn lazy boolean ConditionalExpr.isNumericConditional() =
075          getTrueExpr().isNumericExpression() && getFalseExpr().isNumericExpression();
076    
077      syn lazy boolean ConditionalExpr.isReferenceConditional() =
078          !isBooleanConditional() && !isNumericConditional();
079    
080      syn lazy boolean Expr.isPolyExpression() = false;
081    
082      eq ConditionalExpr.isPolyExpression() =
083          isReferenceConditional() && (assignmentContext() || invocationContext());
084    
085      // 15.9
086      eq ClassInstanceExpr.isPolyExpression() =
087          (getAccess() instanceof DiamondAccess) && (assignmentContext() || invocationContext());
088    
089      // 15.8.5
090      eq ParExpr.isPolyExpression() =
091          getExpr().isPolyExpression();
092    
093      eq LambdaExpr.isPolyExpression() = true;
094      eq MethodReference.isPolyExpression() = true;
095      eq ConstructorReference.isPolyExpression() = true;
096    
097      // 15.12
098      eq ParMethodAccess.isPolyExpression() = false;
099      eq MethodAccess.isPolyExpression() {
100        if (!assignmentContext() && !invocationContext()) {
101          return false;
102        }
103        if (!decl().isGeneric()) {
104          return false;
105        }
106        GenericMethodDecl genericDecl = decl().genericDecl();
107        return genericDecl.typeVariableInReturn();
108      }
109    
110      syn lazy boolean GenericMethodDecl.typeVariableInReturn() {
111        if (!getTypeAccess().usesTypeVariable()) {
112          return false;
113        }
114        ASTNode current = getTypeAccess();
115        LinkedList<ASTNode> list = new LinkedList<ASTNode>();
116        list.add(current);
117        boolean foundUse = false;
118        while (!list.isEmpty()) {
119          current = list.poll();
120          for (int i = 0; i < current.getNumChild(); i++) {
121            list.add(current.getChild(i));
122          }
123          if (current instanceof TypeAccess) {
124            TypeAccess typeAccess = (TypeAccess)current;
125            if (typeAccess.type().isTypeVariable()) {
126              for (int i = 0; i < getNumTypeParameter(); i++) {
127                if (typeAccess.type() == getTypeParameter(i)) {
128                  foundUse = true;
129                  break;
130                }
131              }
132              if (foundUse) {
133                break;
134              }
135            }
136          }
137        }
138        return foundUse;
139      }
140    
141      // This can be used to check assignment compatibility without worrying about poly expressions type
142      syn lazy boolean Expr.assignConversionTo(TypeDecl type) = type().assignConversionTo(type, this);
143    
144      eq LambdaExpr.assignConversionTo(TypeDecl type) {
145        if (!type.isFunctionalInterface()) {
146          return false;
147        }
148        FunctionDescriptor f = ((InterfaceDecl) type).functionDescriptor();
149        return congruentTo(f);
150      }
151    
152      eq MethodReference.assignConversionTo(TypeDecl type) {
153        if (!type.isFunctionalInterface()) {
154          return false;
155        }
156        FunctionDescriptor f = ((InterfaceDecl) type).functionDescriptor();
157        return congruentTo(f);
158      }
159    
160      eq ConstructorReference.assignConversionTo(TypeDecl type) {
161        if (!type.isFunctionalInterface()) {
162          return false;
163        }
164        FunctionDescriptor f = ((InterfaceDecl) type).functionDescriptor();
165        return congruentTo(f);
166      }
167    
168      eq ParExpr.assignConversionTo(TypeDecl type) =
169          getExpr().assignConversionTo(type);
170    
171      eq ConditionalExpr.assignConversionTo(TypeDecl type) {
172        if (!isPolyExpression()) {
173          return type().assignConversionTo(type, this);
174        } else {
175          return getTrueExpr().assignConversionTo(type) && getFalseExpr().assignConversionTo(type);
176        }
177      }
178    
179      eq ClassInstanceExpr.assignConversionTo(TypeDecl type) {
180        if (!isPolyExpression()) {
181          return super.assignConversionTo(type);
182        } else {
183          return ((DiamondAccess) getAccess()).getTypeAccess().type().assignConversionTo(
184              type, ((DiamondAccess)getAccess()).getTypeAccess());
185        }
186      }
187    
188    }