protected void checkReturn(Stmt.Return ret) { JavaMethod method = (JavaMethod) getEnclosingScope(JavaMethod.class); if (method instanceof JavaConstructor) { return; // could do better than this. } Type retType = method.returnType().attribute(Type.class); if (ret.expr() != null) { checkExpression(ret.expr()); Type ret_t = ret.expr().attribute(Type.class); try { if (ret_t.equals(new Type.Void())) { syntax_error("cannot return a value from method whose result type is void", ret); } else if (!types.subtype(retType, ret_t, loader)) { syntax_error("required return type " + retType + ", found type " + ret_t, ret); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), ret); } } else if (!(retType instanceof Type.Void)) { syntax_error("missing return value", ret); } }
protected void checkVarDef(Stmt.VarDef def) { // Observe that we cannot use the declared type here, rather we have to // use the resolved type! Type t = def.type().attribute(Type.class); for (Triple<String, Integer, Expr> d : def.definitions()) { if (d.third() != null) { checkExpression(d.third()); Type nt = t; for (int i = 0; i != d.second(); ++i) { nt = new Type.Array(nt); } Type i_t = d.third().attribute(Type.class); try { if (!types.subtype(nt, i_t, loader)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(d.third(), nt, loader, types), d.third().attribute(SourceLocation.class)); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), def); } } } }
protected void checkConvert(Expr.Convert e) { Type rhs_t = e.expr().attribute(Type.class); Type c_t = (Type) e.type().attribute(Type.class); try { if (!types.subtype(c_t, rhs_t, loader)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(e.expr(), c_t, loader, types), e.expr().attribute(SourceLocation.class)); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), e); } }
protected void checkAssignment(Stmt.Assignment def) { checkExpression(def.lhs()); checkExpression(def.rhs()); Type lhs_t = def.lhs().attribute(Type.class); Type rhs_t = def.rhs().attribute(Type.class); try { if (!types.subtype(lhs_t, rhs_t, loader)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(def.rhs(), lhs_t, loader, types), def.rhs().attribute(SourceLocation.class)); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), def); } }
protected void checkTryCatchBlock(Stmt.TryCatchBlock block) { checkBlock(block); checkBlock(block.finaly()); for (final Stmt.CatchBlock cb : block.handlers()) { checkBlock(cb); try { if (!types.subtype(JAVA_LANG_THROWABLE, cb.type().attribute(Type.class), loader)) { // This is a unique case - no substitution can be found by reflection // (as the error is within the actual source code text), so we make a generic // suggestion syntax_error("Syntax Error: Exception type must extend java.lang.Throwable", cb.type()); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), block); } } }
protected void checkField(JavaField d) { checkExpression(d.initialiser()); Type lhs_t = d.type().attribute(Type.class); if (d.initialiser() != null) { Type rhs_t = d.initialiser().attribute(Type.class); try { if (!types.subtype(lhs_t, rhs_t, loader)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(d.initialiser(), lhs_t, loader, types), d.initialiser().attribute(SourceLocation.class)); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), d); } } }
protected void checkCast(Expr.Cast e) { Type e_t = e.expr().attribute(Type.class); Type c_t = e.type().attribute(Type.class); try { if (e_t instanceof Type.Clazz && c_t instanceof Type.Clazz) { Clazz c_c = loader.loadClass((Type.Clazz) c_t); Clazz e_c = loader.loadClass((Type.Clazz) e_t); // the trick here, is that javac will never reject a cast // between an interface and a class or interface. However, if we // have a cast from one class to another class, then it will // reject this if neither is a subclass of the other. if (c_c.isInterface() || e_c.isInterface()) { // cast cannot fail here. return; } } else if ((e_t instanceof Type.Variable || e_t instanceof Type.Wildcard) && c_t instanceof Type.Reference) { // javac always lets this pass, no matter what return; } if (types.boxSubtype(c_t, e_t, loader) || types.boxSubtype(e_t, c_t, loader)) { // this is OK return; } else if (c_t instanceof Type.Primitive && e_t instanceof Type.Primitive) { if (e_t instanceof Type.Char && (c_t instanceof Type.Byte || c_t instanceof Type.Short)) { return; } else if (c_t instanceof Type.Char && (e_t instanceof Type.Byte || e_t instanceof Type.Short)) { return; } } ErrorHandler.handleTypeMismatch( new TypeMismatchException(e.expr(), c_t, loader, types), e.attribute(SourceLocation.class)); } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), e); } }
protected void checkForEach(Stmt.ForEach stmt) { checkExpression(stmt.source()); checkStatement(stmt.body()); // need to check that the static type of the source expression // implements java.lang.iterable Type s_t = stmt.source().attribute(Type.class); try { if (!(s_t instanceof Type.Array) && !types.subtype(new Type.Clazz("java.lang", "Iterable"), s_t, loader)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException( stmt.source(), new Type.Wildcard(new Type.Clazz("java.lang", "Iterable"), null), loader, types), stmt.source().attribute(SourceLocation.class)); } } catch (ClassNotFoundException ex) { syntax_error(ex.getMessage(), stmt); } }