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 LambdaExpr {
029    
030      eq LambdaAnonymousDecl.unqualifiedLookupMethod(String name) {
031        if (getParent().getParent().getParent() instanceof MethodReference) {
032          return ((MethodReference) getParent().getParent().getParent()).lookupMethod(name);
033        } else if (getParent().getParent().getParent() instanceof ConstructorReference) {
034          return ((ConstructorReference) getParent().getParent().getParent()).lookupMethod(name);
035        } else {
036          return enclosingLambda().lookupMethod(name);
037        }
038      }
039    
040      syn lazy int LambdaExpr.arity() = numParameters();
041    
042      syn lazy int LambdaParameters.numParameters();
043      syn lazy int LambdaExpr.numParameters() = getLambdaParameters().numParameters();
044    
045      eq InferredLambdaParameters.numParameters() = getNumParameter();
046      eq DeclaredLambdaParameters.numParameters() = getNumParameter();
047    
048      syn lazy boolean LambdaParameters.congruentTo(FunctionDescriptor f);
049    
050      // 15.27.3
051      eq InferredLambdaParameters.congruentTo(FunctionDescriptor f) =
052          numParameters() == f.method.getNumParameter();
053    
054      // 15.27.3
055      eq DeclaredLambdaParameters.congruentTo(FunctionDescriptor f) {
056        if (numParameters() != f.method.getNumParameter()) {
057          return false;
058        }
059        for (int i = 0; i < numParameters(); i++) {
060          //Lambdas can't declare type variables so that doesn't need to be taken into account
061          if (getParameter(i).type() != f.method.getParameter(i).type()) {
062            return false;
063          }
064        }
065        return true;
066      }
067    
068      syn lazy boolean LambdaExpr.isImplicit() = getLambdaParameters() instanceof InferredLambdaParameters;
069      syn lazy boolean LambdaExpr.isExplicit() = !isImplicit();
070    
071      syn lazy boolean LambdaBody.congruentTo(FunctionDescriptor f);
072    
073      // 15.27.3
074      eq ExprLambdaBody.congruentTo(FunctionDescriptor f) {
075        if (f.method.type().isVoid()) {
076          return getExpr().stmtCompatible();
077        } else {
078          return getExpr().assignConversionTo(f.method.type());
079        }
080      }
081    
082      // 15.27.3
083      eq BlockLambdaBody.congruentTo(FunctionDescriptor f) {
084        if (f.method.type().isVoid()) {
085          return voidCompatible();
086        } else {
087          if (!valueCompatible()) {
088            return false;
089          }
090          for (ReturnStmt returnStmt : lambdaReturns()) {
091            if (!returnStmt.getResult().assignConversionTo(f.method.type())) {
092              return false;
093            }
094          }
095          return true;
096        }
097      }
098    
099      /* BEWARE! READ THIS BEFORE USING THIS METHOD!
100         The congruency check will currently not infer different types for eventual inferred parameters,
101         but the target type function descriptor will always be used for inference. Thus this check will
102         NOT work for arbitrary function descriptors if there are inferred parameters in the lambda. Currently,
103         there is no use for this to work anyway because a lambda with inferred parameters will never be
104         pertinent to applicability and thus not need to be congruency checked, but in case there is need
105         for arbitary congruency checks that handle inferrence differently depending on the function descriptor
106         input to this method, then this check must be altered! */
107      // 15.27.3
108      syn lazy boolean LambdaExpr.congruentTo(FunctionDescriptor f) =
109          !f.isGeneric() && getLambdaParameters().congruentTo(f) && getLambdaBody().congruentTo(f);
110    
111      refine LookupParTypeDecl
112      public Access ParTypeDecl.substitute(Parameterization parTypeDecl) {
113        // TODO: include nesting as well....
114        if (parTypeDecl.isRawType()) {
115          return ((GenericTypeDecl) genericDecl()).rawType().createBoundAccess();
116        }
117    
118        /* These lines have been removed because they erase arguments from
119          parameter types when they are not using type variables, for example
120          C<String> is substituted to only C, which I don't think is correct?
121          And if the ParTypeDecl doesn't use any type variables, why is there
122          even any need for further substitution?
123    
124        if (!usesTypeVariable()) {
125          return super.substitute(parTypeDecl);
126        }
127        */
128        List<Access> list = new List<Access>();
129        for (Access argument : getArgumentList()) {
130          list.add(argument.type().substitute(parTypeDecl));
131        }
132        return new ParTypeAccess(genericDecl().createQualifiedAccess(), list);
133      }
134    }
135    
136    aspect StmtCompatible {
137      syn lazy boolean Expr.stmtCompatible() = false;
138      syn lazy boolean ConstructorAccess.stmtCompatible() = true;
139      syn lazy boolean ClassInstanceExpr.stmtCompatible() = true;
140      syn lazy boolean AssignExpr.stmtCompatible() = true;
141      syn lazy boolean PostIncExpr.stmtCompatible() = true;
142      syn lazy boolean PostDecExpr.stmtCompatible() = true;
143      syn lazy boolean PreIncExpr.stmtCompatible() = true;
144      syn lazy boolean PreDecExpr.stmtCompatible() = true;
145      syn lazy boolean MethodAccess.stmtCompatible() = true;
146      syn lazy boolean AbstractDot.stmtCompatible() = getRight().stmtCompatible();
147    }
148    
149    aspect ExceptionHandling {
150      eq LambdaExpr.getLambdaBody().handlesException(TypeDecl exceptionType) {
151        InterfaceDecl iDecl = targetInterface();
152        if (iDecl == null) {
153          return false;
154        } else if (!iDecl.isFunctional()) {
155          return false;
156        }
157        for (TypeDecl exception : iDecl.functionDescriptor().throwsList) {
158          if (exceptionType.strictSubtype(exception)) {
159            return true;
160          }
161        }
162        return false;
163      }
164    }