package cd.frontend.semantic; import cd.frontend.semantic.SemanticFailure.Cause; import cd.ir.Ast.ClassDecl; import cd.ir.Ast.MethodDecl; import cd.ir.AstVisitor; import cd.ir.Symbol.ClassSymbol; import cd.ir.Symbol.MethodSymbol; import cd.ir.Symbol.VariableSymbol; import cd.util.Pair; import java.util.HashSet; import java.util.Set; public class InheritanceChecker extends AstVisitor { ClassSymbol classSym; @Override public Void classDecl(ClassDecl ast, Void arg) { classSym = ast.sym; // check for cycles in the inheritance hierarchy: Set supers = new HashSet(); ClassSymbol sc = classSym.superClass; supers.add(classSym); while (sc != null) { if (supers.contains(sc)) throw new SemanticFailure( Cause.CIRCULAR_INHERITANCE, "Class %s has %s as a superclass twice", ast.name, sc.name); supers.add(sc); sc = sc.superClass; } this.visitChildren(ast, null); return null; } @Override public Void methodDecl(MethodDecl ast, Void arg) { // check that methods overridden from a parent class agree // on number/type of parameters MethodSymbol sym = ast.sym; MethodSymbol superSym = classSym.superClass.getMethod(ast.name); if (superSym != null) { if (superSym.parameters.size() != sym.parameters.size()) throw new SemanticFailure( Cause.INVALID_OVERRIDE, "Overridden method %s has %d parameters, " + "but original has %d", ast.name, sym.parameters.size(), superSym.parameters.size()); for (Pair pair : Pair.zip(sym.parameters, superSym.parameters)) if (pair.a.type != pair.b.type) throw new SemanticFailure( Cause.INVALID_OVERRIDE, "Method parameter %s has type %s, but " + "corresponding base class parameter %s has type %s", pair.a.name, pair.a.type, pair.b.name, pair.b.type); if (superSym.returnType != sym.returnType) throw new SemanticFailure( Cause.INVALID_OVERRIDE, "Overridden method %s has return type %s," + "but its superclass has %s", ast.name, sym.returnType, superSym.returnType); } return null; } }