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 TypeCheck {
029      inh TypeDecl LambdaParameters.unknownType();
030      inh TypeDecl InferredParameterDeclaration.unknownType();
031      inh TypeDecl LambdaBody.unknownType();
032      eq LambdaExpr.getLambdaParameters().unknownType() = unknownType();
033      eq LambdaExpr.getLambdaBody().unknownType() = unknownType();
034    
035      syn lazy TypeDecl IntersectionCastExpr.type() = unknownType();
036    
037      // Lambdas should propagate target return type to returns in lambda body.
038      eq BlockLambdaBody.getBlock().returnType() {
039        TypeDecl decl = enclosingLambda().targetType();
040        if (decl == null) {
041          return unknownType();
042        } else if (!(decl instanceof InterfaceDecl)) {
043          return unknownType();
044        } else {
045          InterfaceDecl iDecl = (InterfaceDecl)decl;
046          if (!iDecl.isFunctional()) {
047            return unknownType();
048          } else {
049            return iDecl.functionDescriptor().method.type();
050          }
051        }
052      }
053    
054    
055      syn lazy TypeDecl LambdaExpr.type() {
056        // 15.27.3
057        if (!assignmentContext() && !castContext() && !invocationContext()) {
058          return unknownType();
059        }
060        if (targetInterface() == null) {
061          return unknownType();
062        }
063    
064        InterfaceDecl iDecl = targetInterface();
065        if (!iDecl.isFunctional()) {
066          return unknownType();
067        }
068        if (congruentTo(iDecl.functionDescriptor())) {
069          return iDecl;
070        } else {
071          return unknownType();
072        }
073      }
074    
075      syn lazy TypeDecl MethodReference.type() {
076        // 15.13.1
077        if (!assignmentContext() && !castContext() && !invocationContext()) {
078          return unknownType();
079        }
080        if (targetInterface() == null) {
081          return unknownType();
082        }
083    
084        InterfaceDecl iDecl = targetInterface();
085        if (!iDecl.isFunctional()) {
086          return unknownType();
087        }
088    
089        if (congruentTo(iDecl.functionDescriptor())) {
090          return iDecl;
091        } else {
092          return unknownType();
093        }
094      }
095    
096      syn lazy TypeDecl ConstructorReference.type() {
097        // 15.13.1
098        if (!assignmentContext() && !castContext() && !invocationContext()) {
099          return unknownType();
100        }
101        if (targetInterface() == null) {
102          return unknownType();
103        }
104    
105        InterfaceDecl iDecl = targetInterface();
106        if (!iDecl.isFunctional()) {
107          return unknownType();
108        }
109    
110        return iDecl;
111      }
112    
113    
114      /* TODO: Type check that works for wildcard-parameterized interfaces */
115      public void LambdaExpr.typeCheck() {
116        if (!assignmentContext() && !castContext() && !invocationContext()) {
117          // 15.27
118          error("Lambda expressions must target a functional interface");
119          return;
120        }
121    
122        // This means there was an error in the overload resolution, will be reported elsewhere
123        if (invocationContext() && targetType() == unknownType()) {
124          return;
125        }
126    
127        if (!targetType().isFunctionalInterface()) {
128          // 15.27
129          error("Lambda expressions must target a functional interface");
130          return;
131        }
132    
133        InterfaceDecl iDecl = targetInterface();
134    
135        if (!iDecl.isFunctional()) {
136          // 15.27
137          errorf("Interface %s is not functional and can therefore not be targeted by a lambda expression",
138              iDecl.typeName());
139          return;
140        }
141    
142        FunctionDescriptor f = iDecl.functionDescriptor();
143    
144        if (f.isGeneric()) {
145          // 15.27
146          errorf("Illegal lambda expression: Method %s in interface %s is generic",
147              iDecl.functionDescriptor().method.name(), iDecl.typeName());
148          return;
149        }
150    
151        if (!getLambdaParameters().congruentTo(f)) {
152          errorf("Lambda expression parameters incompatible with"
153              + " parameters in method %s in interface %s",
154              f.method.name(), iDecl.typeName());
155        }
156    
157        if (getLambdaBody() instanceof ExprLambdaBody) {
158          ExprLambdaBody exprBody = (ExprLambdaBody)getLambdaBody();
159          if (f.method.type().isVoid()) {
160            if (!exprBody.getExpr().stmtCompatible()) {
161              errorf("Lambda expression body must be a statement expression,"
162                  + " because the method %s in interface %s has return type void",
163                  f.method.name(), iDecl.typeName());
164            }
165          } else {
166            if (!exprBody.getExpr().type().assignConversionTo(f.method.type(), exprBody.getExpr())) {
167              errorf("Lambda expression body is not compatible with"
168                  + " the return type %s in method %s in interface %s",
169                  f.method.type().typeName(), f.method.name(), iDecl.typeName());
170            }
171          }
172        } else {
173          BlockLambdaBody blockBody = (BlockLambdaBody)getLambdaBody();
174          if (f.method.type().isVoid()) {
175            if (!blockBody.voidCompatible()) {
176              errorf("Lambda expression body is not allowed to return a value,"
177                  + " because the method %s in interface %s has return type void",
178                  f.method.name(), iDecl.typeName());
179            }
180          } else if (!blockBody.valueCompatible()) {
181            errorf("Lambda expression body must not complete normally or contain empty return"
182                + " statments, because the method %s in interface"
183                + " %s has a return type which is non-void",
184                f.method.name(), iDecl.typeName());
185          }
186        }
187      }
188    
189      public void BlockLambdaBody.typeCheck() {
190        // 15.27.2
191        if (!voidCompatible() && !valueCompatible()) {
192          error("Block lambda bodies must be either void or value compatible");
193        }
194      }
195    
196      refine TypeCheck
197      public void ReturnStmt.typeCheck() {
198        if (enclosingLambda() == null || enclosingLambda().hostType() != hostType()) {
199          if (hasResult() && !returnType().isVoid()) {
200            if (!getResult().type().assignConversionTo(returnType(), getResult())) {
201              errorf("return value must be an instance of %s which %s is not",
202                  returnType().typeName(), getResult().type().typeName());
203            }
204          }
205          // 8.4.5 8.8.5
206          if (returnType().isVoid() && hasResult()) {
207            error("return stmt may not have an expression in void methods");
208          }
209          // 8.4.5
210          if (!returnType().isVoid() && !hasResult()) {
211            error("return stmt must have an expression in non void methods");
212          }
213          if (enclosingBodyDecl() instanceof InstanceInitializer
214              || enclosingBodyDecl() instanceof StaticInitializer) {
215            error("Initializers may not return");
216          }
217        } else {
218          if (hasResult() && !returnType().isVoid() && !(getResult() instanceof LambdaExpr)) {
219            if (!getResult().type().assignConversionTo(returnType(), getResult())) {
220              errorf("return value must be an instance of %s which %s is not",
221                  returnType().typeName(), getResult().type().typeName());
222            }
223          }
224        }
225      }
226    
227      public void MethodReference.typeCheck() {
228        // 15.13.1
229        if (!assignmentContext() && !castContext() && !invocationContext()) {
230          error("Method references must target a functional interface");
231          return;
232        }
233    
234        // This means there was an error in the overload resolution, will be reported elsewhere.
235        if (invocationContext() && targetType() == unknownType()) {
236          return;
237        }
238    
239        if (!targetType().isFunctionalInterface()) {
240          error("Method references must target a functional interface");
241          return;
242        }
243    
244        InterfaceDecl iDecl = targetInterface();
245    
246        if (!iDecl.isFunctional()) {
247          errorf("Interface %s is not functional and can therefore not be targeted by a method reference",
248              iDecl.typeName());
249          return;
250        }
251    
252        MethodDecl found = null;
253        FunctionDescriptor f = iDecl.functionDescriptor();
254        // Lookup method here and check that one most specific can be found
255        if (this instanceof ExprMethodReference) {
256          ExprMethodReference ref = (ExprMethodReference)this;
257          found = ref.targetMethod(f);
258          if (unknownMethod() == found) {
259            // 15.13.1
260            errorf("No method %s that is compatible with the method %s in the interface %s was found",
261                name(), iDecl.functionDescriptor().method.fullSignature(), iDecl.typeName());
262          } else if (found.isStatic()) {
263            errorf("The method %s in type %s must be accessed in a static way",
264                found.fullSignature(), found.hostType().typeName());
265          } else if (ref.getExpr() instanceof Access && ((Access)ref.getExpr()).lastAccess() instanceof SuperAccess) {
266            // 15.13.2
267            if (found.isAbstract()) {
268              errorf("Cannot directly invoke the abstract method %s in type %s",
269                  found.fullSignature(), found.hostType().typeName());
270            }
271    
272            SuperAccess superAccess = (SuperAccess)((Access)ref.getExpr()).lastAccess();
273            if (superAccess.isQualified() && superAccess.decl() instanceof InterfaceDecl) {
274              if (hostType().isClassDecl()) {
275                ClassDecl classDecl = (ClassDecl)hostType();
276                if (classDecl.hasOverridingMethodInSuper(found)) {
277                  errorf("Cannot make a super reference to method %s, there is a more specific override",
278                      found.fullSignature());
279                }
280              } else if (hostType().isInterfaceDecl()) {
281                InterfaceDecl interfaceDecl = (InterfaceDecl)hostType();
282                if (interfaceDecl.hasOverridingMethodInSuper(found)) {
283                  errorf("Cannot make a super reference to method %s, there is a more specific override",
284                      found.fullSignature());
285                }
286              }
287            }
288    
289          }
290        } else if (this instanceof TypeMethodReference) {
291          TypeMethodReference ref = (TypeMethodReference) this;
292          MethodDecl staticMethod = ref.targetStaticMethod(f);
293          MethodDecl instanceMethod = ref.targetInstanceMethod(f);
294          if (ref.validStaticMethod(f) && ref.validInstanceMethod(f)) {
295            errorf("Ambiguity error: two possible methods %s was found", staticMethod.name());
296            return;
297          } else if (unknownMethod() == staticMethod && unknownMethod() == instanceMethod) {
298            errorf("No method %s that is compatible with the method %s in the interface %s was found",
299                name(), iDecl.functionDescriptor().method.fullSignature(), iDecl.typeName());
300             return;
301          } else if (ref.validStaticMethod(f)) {
302            if (ref.getTypeAccess() instanceof ParTypeAccess) {
303              error("Parameterized qualifier is not allowed for static method references");
304            } else {
305              found = staticMethod;
306            }
307          } else if (ref.validInstanceMethod(f)) {
308            found = instanceMethod;
309          } else if (unknownMethod() != staticMethod && !staticMethod.isStatic()) {
310            errorf("Cannot make a static reference to the non-static method %s in type %s",
311                staticMethod.fullSignature(), staticMethod.hostType().typeName());
312            return;
313          } else if (instanceMethod.isStatic()) {
314            errorf("The method %s in type %s must be accessed in a static way",
315                instanceMethod.fullSignature(), instanceMethod.hostType().typeName());
316            return;
317          }
318        }
319    
320        if (found != null && unknownMethod() != found) {
321          // Check that found is compatible with the function descriptor
322          if (!iDecl.functionDescriptor().method.type().isVoid()) {
323            // 15.13.1
324            if (found.type().isVoid()
325                || !found.type().assignConversionTo(iDecl.functionDescriptor().method.type(), null)) {
326              errorf("Return type of referenced method %s is not compatible with method %s in interface %s",
327                  found.fullSignature(), iDecl.functionDescriptor().method.fullSignature(),
328                  iDecl.typeName());
329            }
330          }
331    
332          for (int i = 0; i < found.getNumException(); i++) {
333            TypeDecl exception = found.getException(i).type();
334            if (exception.isUncheckedException()) {
335              continue;
336            }
337    
338            boolean legalException = false;
339            for (TypeDecl descriptorThrows : iDecl.functionDescriptor().throwsList) {
340              if (exception.strictSubtype(descriptorThrows)) {
341                legalException = true;
342                break;
343              }
344            }
345            if (!legalException) {
346              // 15.13.1
347              errorf("Referenced method %s throws unhandled exception type %s",
348                  found.name(), exception.typeName());
349            }
350          }
351        }
352      }
353    
354      public void ConstructorReference.typeCheck() {
355        // 15.13.1
356        if (!assignmentContext() && !castContext() && !invocationContext()) {
357          error("Constructor references must target a functional interface");
358          return;
359        }
360    
361        // This means there was an error in the overload resolution, will be reported elsewhere
362        if (invocationContext() && targetType() == unknownType()) {
363          return;
364        }
365    
366        if (!targetType().isFunctionalInterface()) {
367          error("Constructor references must target a functional interface");
368          return;
369        }
370        InterfaceDecl iDecl = targetInterface();
371    
372        if (!iDecl.isFunctional()) {
373          errorf("Interface %s is not functional and can therefore not be targeted by a constructor reference",
374              iDecl.typeName());
375          return;
376        }
377    
378        FunctionDescriptor f = iDecl.functionDescriptor();
379    
380        if (this instanceof ClassReference) {
381          ClassReference ref = (ClassReference)this;
382          ConstructorDecl decl = ref.targetConstructor(f);
383          if (unknownConstructor() == decl) {
384            errorf("No constructor for the type %s that is compatible with the method %s in the interface %s was found",
385                getTypeAccess().type().typeName(), f.method.fullSignature(), iDecl.typeName());
386          }
387          if (!f.method.type().isVoid()) {
388            // 15.13.1
389            TypeDecl returnType = ref.syntheticInstanceExpr(f).type();
390            if (!returnType.assignConversionTo(f.method.type(), null)) {
391              errorf("Return type of method %s in interface %s is not compatible with"
392                  + " referenced constructor which has return type: %s",
393                  f.method.fullSignature(), iDecl.typeName(), returnType.typeName());
394            }
395          }
396          for (int i = 0; i < decl.getNumException(); i++) {
397            TypeDecl exception = decl.getException(i).type();
398            if (exception.isUncheckedException()) {
399              continue;
400            }
401    
402            boolean legalException = false;
403            for (TypeDecl descriptorThrows : iDecl.functionDescriptor().throwsList) {
404              if (exception.strictSubtype(descriptorThrows)) {
405                legalException = true;
406                break;
407              }
408            }
409            if (!legalException) {
410              // 15.13.1
411              errorf("Referenced constructor %s throws unhandled exception type %s",
412                  decl.name(), exception.typeName());
413            }
414          }
415          ref.syntheticInstanceExpr(f).typeCheck();
416        } else {
417          ArrayReference ref = (ArrayReference)this;
418          if (f.method.getNumParameter() != 1) {
419            errorf("Array reference not compatible with method %s in interface %s,"
420                + " should have a single parameter of type int",
421                f.method.fullSignature(), iDecl.typeName());
422            return;
423          }
424          if (!f.method.getParameter(0).type().assignConversionTo(iDecl.typeInt(), null)) {
425            errorf("Array reference not compatible with method %s in interface %s,"
426                + " should have a single parameter of type int",
427                f.method.fullSignature(), iDecl.typeName());
428            return;
429          }
430          if (!f.method.type().isVoid()) {
431            if (!getTypeAccess().type().assignConversionTo(f.method.type(), null)) {
432              errorf("Return type %s of method %s in interface %s is not compatible with"
433                  + " the array reference type %s",
434                  f.method.type().typeName(), f.method.fullSignature(), iDecl.typeName(),
435                  getTypeAccess().type().typeName());
436            }
437          }
438        }
439      }
440    
441      refine TypeCheck
442      eq MethodDecl.mayOverride(MethodDecl m) {
443        // 9.4.3
444        if (isDefault() && m.hostType() == type().typeObject() && !m.isPrivate()) {
445          return false;
446        } else {
447          return returnTypeSubstitutableFor(m);
448        }
449      }
450    }
451    
452    aspect LambdaParametersInference {
453      inh lazy TypeDecl InferredParameterDeclaration.inferredType();
454      eq InferredLambdaParameters.getParameter(int i).inferredType() {
455        if (enclosingLambda().targetInterface() == null) {
456          return unknownType();
457        }
458        InterfaceDecl iDecl = (InterfaceDecl)enclosingLambda().targetInterface();
459        if (!iDecl.isFunctional()) {
460          return unknownType();
461        } else if (iDecl.functionDescriptor().method.getNumParameter() < i + 1) {
462          return unknownType();
463        } else {
464          return iDecl.functionDescriptor().method.getParameter(i).type();
465        }
466      }
467    }