protected void checkInstanceOf(Expr.InstanceOf e) throws ClassNotFoundException { checkExpression(e.lhs()); Type lhs_t = e.lhs().attribute(Type.class); final Type rhs_t = e.rhs().attribute(Type.class); if (lhs_t instanceof Type.Primitive) { ErrorHandler.handleTypeMismatch( new TypeMismatchException( e.lhs(), new Type.Wildcard(JAVA_LANG_OBJECT, null), loader, types), e.lhs().attribute(SourceLocation.class)); } else if (!(rhs_t instanceof Type.Reference)) { // This is an unusual case - the error is in the source code, so we can't // make a suggestion by reflection. We just make a generic suggestion syntax_error( "Syntax Error: Can only check if object is instance of an array or class type", e.rhs()); } else if ((lhs_t instanceof Type.Array || rhs_t instanceof Type.Array) && !(types.subtype(lhs_t, rhs_t, loader))) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(e.lhs(), rhs_t, loader, types), e.lhs().attribute(SourceLocation.class)); } }
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 checkUnOp(Expr.UnOp uop) { checkExpression(uop.expr()); Type e_t = uop.expr().attribute(Type.class); switch (uop.op()) { case UnOp.NEG: if (!(e_t instanceof Type.Byte || e_t instanceof Type.Char || e_t instanceof Type.Short || e_t instanceof Type.Int || e_t instanceof Type.Long || e_t instanceof Type.Float || e_t instanceof Type.Double)) { ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( uop.expr(), T_INT, loader, types, uop.operator(), AllowedType.PRIMITIVE), uop.expr().attribute(SourceLocation.class)); } break; case UnOp.NOT: if (!(e_t instanceof Type.Bool)) { ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( uop.expr(), T_BOOL, loader, types, uop.operator(), AllowedType.BOOL), uop.expr().attribute(SourceLocation.class)); } break; case UnOp.INV: if (!(e_t instanceof Type.Byte || e_t instanceof Type.Char || e_t instanceof Type.Short || e_t instanceof Type.Int || e_t instanceof Type.Long)) { ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( uop.expr(), T_INT, loader, types, uop.operator(), AllowedType.NUMBER), uop.expr().attribute(SourceLocation.class)); } break; } }
protected void checkArrayIndex(Expr.ArrayIndex e) { checkExpression(e.index()); checkExpression(e.target()); Type i_t = e.index().attribute(Type.class); if (!(i_t instanceof Type.Int)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(e.index(), T_INT, loader, types), e.index().attribute(SourceLocation.class)); } Type t_t = e.target().attribute(Type.class); if (!(t_t instanceof Type.Array)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException( e.target(), new Type.Array(new Type.Wildcard(JAVA_LANG_OBJECT, null)), loader, types), e.attribute(SourceLocation.class)); } }
protected void checkTernOp(Expr.TernOp e) { checkExpression(e.condition()); checkExpression(e.trueBranch()); checkExpression(e.falseBranch()); Type c_t = e.condition().attribute(Type.class); if (!(c_t instanceof Type.Bool)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(e.condition(), T_BOOL, loader, types), e.condition().attribute(SourceLocation.class)); } }
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 checkDoWhile(Stmt.DoWhile stmt) { checkExpression(stmt.condition()); checkStatement(stmt.body()); if (stmt.condition() != null) { Type c_t = stmt.condition().attribute(Type.class); if (!(c_t instanceof Type.Bool)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(stmt.condition(), T_BOOL, loader, types), stmt.condition().attribute(SourceLocation.class)); } } }
protected void checkSynchronisedBlock(Stmt.SynchronisedBlock block) { checkExpression(block.expr()); checkBlock(block); Type e_t = block.expr().attribute(Type.class); if (!(e_t instanceof Type.Reference)) { // The most generic reference type possible - use Object as lower bound to help get the point // across ErrorHandler.handleTypeMismatch( new TypeMismatchException( block.expr(), new Type.Wildcard(JAVA_LANG_OBJECT, null), loader, types), block.expr().attribute(SourceLocation.class)); } }
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 checkSwitch(Stmt.Switch sw) { checkExpression(sw.condition()); Type condT = sw.condition().attribute(Type.class); if (!(condT instanceof Type.Int)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(sw.condition(), T_INT, loader, types), sw.condition().attribute(SourceLocation.class)); } for (Case c : sw.cases()) { checkExpression(c.condition()); for (Stmt s : c.statements()) { checkStatement(s); } } }
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); } }
protected void checkBinOp(Expr.BinOp e) { checkExpression(e.lhs()); checkExpression(e.rhs()); Type lhs_t = e.lhs().attribute(Type.class); Type rhs_t = e.rhs().attribute(Type.class); Type e_t = e.attribute(Type.class); if ((lhs_t instanceof Type.Primitive || rhs_t instanceof Type.Primitive) && !lhs_t.equals(rhs_t)) { if ((lhs_t instanceof Type.Long || lhs_t instanceof Type.Int || lhs_t instanceof Type.Short || lhs_t instanceof Type.Char || lhs_t instanceof Type.Byte) && rhs_t instanceof Type.Int && (e.op() == BinOp.SHL || e.op() == BinOp.SHR || e.op() == BinOp.USHR)) { return; // Ok! } else if ((isJavaLangString(lhs_t) || isJavaLangString(rhs_t)) && e.op() == BinOp.CONCAT) { return; // OK } } else if ((lhs_t instanceof Type.Char || lhs_t instanceof Type.Byte || lhs_t instanceof Type.Int || lhs_t instanceof Type.Long || lhs_t instanceof Type.Short || lhs_t instanceof Type.Float || lhs_t instanceof Type.Double) && (rhs_t instanceof Type.Char || rhs_t instanceof Type.Byte || rhs_t instanceof Type.Int || rhs_t instanceof Type.Long || rhs_t instanceof Type.Short || rhs_t instanceof Type.Float || rhs_t instanceof Type.Double)) { switch (e.op()) { // easy cases first case BinOp.EQ: case BinOp.NEQ: case BinOp.LT: case BinOp.LTEQ: case BinOp.GT: case BinOp.GTEQ: // need more checks here if (!(e_t instanceof Type.Bool)) { ErrorHandler.handleTypeMismatch( new TypeMismatchException(e, T_BOOL, loader, types), e.attribute(SourceLocation.class)); } return; case BinOp.ADD: case BinOp.SUB: case BinOp.MUL: case BinOp.DIV: case BinOp.MOD: { // hmmmm ? return; } case BinOp.SHL: case BinOp.SHR: case BinOp.USHR: { // bit-shift operations always take an int as their rhs, so // make sure we have an int type if (lhs_t instanceof Type.Float || lhs_t instanceof Type.Double) { ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( e.lhs(), T_INT, loader, types, e.operator(), AllowedType.INTEGER), e.lhs().attribute(SourceLocation.class)); } else if (!(rhs_t instanceof Type.Int)) { ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( e.rhs(), T_INT, loader, types, e.operator(), AllowedType.INT), e.rhs().attribute(SourceLocation.class)); } return; } case BinOp.AND: case BinOp.OR: case BinOp.XOR: { if (rhs_t instanceof Type.Float || rhs_t instanceof Type.Double) { ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( e.rhs(), T_INT, loader, types, e.operator(), AllowedType.INTEGER), e.rhs().attribute(SourceLocation.class)); } return; } } } else if (lhs_t instanceof Type.Bool && rhs_t instanceof Type.Bool) { switch (e.op()) { case BinOp.LOR: case BinOp.LAND: case BinOp.AND: case BinOp.OR: case BinOp.XOR: case BinOp.EQ: case BinOp.NEQ: return; // OK } } else if ((isJavaLangString(lhs_t) || isJavaLangString(rhs_t)) && e.op() == Expr.BinOp.CONCAT) { return; // OK } else if (lhs_t instanceof Type.Reference && rhs_t instanceof Type.Reference && (e.op() == Expr.BinOp.EQ || e.op() == Expr.BinOp.NEQ)) { return; } if (checkTypeAllowed(lhs_t, e.getAllowed(true))) ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( e.rhs(), (checkTypeAllowed(T_INT, e.getAllowed(false))) ? T_INT : T_BOOL, loader, types, e.operator(), e.getAllowed(false)), e.rhs().attribute(SourceLocation.class)); ErrorHandler.handleOperatorTypeMismatch( new OperatorTypeMismatchException( e.lhs(), (checkTypeAllowed(T_INT, e.getAllowed(true))) ? T_INT : T_BOOL, loader, types, e.operator(), e.getAllowed(true)), e.lhs().attribute(SourceLocation.class)); }