390 likes | 565 Views
Compiler Construction Semantic Analysis II. Ran Shaham and Ohad Shacham School of Computer Science Tel-Aviv University. PAs. PA2 Deadline extension Dec 29 Fix PA1 grader’s notes in your IC.lex PA1 Escape characters should stay as they are <STRING> <br> { str.append(“<br>”) }.
E N D
Compiler ConstructionSemantic Analysis II Ran Shaham and Ohad Shacham School of Computer Science Tel-Aviv University
PAs • PA2 • Deadline extension Dec 29 • Fix PA1 grader’s notes in your IC.lex • PA1 • Escape characters should stay as they are • <STRING> \\n { str.append(“\n”) }
x86 executable exe ICProgram ic IC compiler Compiler We saw: • Scope • Symbol tables LexicalAnalysis Syntax Analysis Parsing AST SymbolTableetc. Inter.Rep.(IR) CodeGeneration Today: • Type checking • Recap
assigned type doesn’t match declared type relational operator applied to non-int type a is not a subtype of b argument list doesn’t match formal parameters Examples of type errors 1 < true int a; a = true; class A {…}class B extends A { void foo() { A a; B b; b = a; }} void foo(int x) { int x; foo(5,7);}
Types • Type • Set of values computed during program execution • boolean = {true,false} • int = {-232,232} • void = {} • Type safety • Types usage adheres formally defined typing rules
Type judgments • e : T • Formal notation for type judgments • e is a well-typed expression of type T • 2 : int • 2 * (3 + 4) : int • true : bool • “Hello” : string
Type judgments • E e : T • Formal notation for type judgments • In the context E,e is a well-typed expression of T • b:bool, x:int b:bool • x:int 1 + x < 4:bool • foo:int->string, x:int foo(x) : string • Type context • set of type bindings id : T (symbol table)
Typing rules Premise [Name] Conclusion Axioms [Name] Conclusion
Typing rules for expressions E e1 : int E e2 : int [+] E e1+e2 : int AST leaves E true : bool E false : bool E int-literal : int E string-literal : string E null : null E new T() : T
Some IC expression rules 1 E true : bool E false : bool E int-literal : int E string-literal : string E e1 : int E e2 : int op { +, -, /, *, %} E e1 op e2 : int E e1 : int E e2 : int rop { <=,<, >, >=} E e1 rop e2 : bool E e1 : T E e2 : T rop { ==,!=} E e1 rop e2 : bool
Some IC expression rules 2 E e1 : bool E e2 : bool lop { &&,|| } E e1 lop e2 : bool E e1 : int E e1 : bool E - e1 : int E ! e1 : bool E e1 : T[] E e1 : T[] E e2 : int E e1 : int E e1[e2] : T E e1.length : int E new T[e1] : T[] id : T E E id : T E new T() : T
Type-checking algorithm • Construct types • Add basic types to type table • Traverse AST looking for user-defined types (classes,methods,arrays) and store in table • Bind all symbols to types • Traverse AST bottom-up (using visitor) • For each AST node find corresponding rule(there is only one for each kind of node) • Check if rule holds • Yes: assign type to node according to consequent • No: report error
E e1 : bool E e2 : bool E e1 lop e2 : bool lop { &&,|| } E e1 : bool E e1 : int E e2 : int E !e1 : bool E e1 rop e2 : bool rop { <=,<, >, >=} E false : bool intLiteral intLiteral boolLiteral E int-literal : int val=false val=45 val=32 Algorithm example … BinopExpr : bool op=AND : bool : bool BinopExpr UnopExpr op=GT op=NEG : int : int : bool 45 > 32 && !false
Type-checking visitor class TypeChecker implements PropagatingVisitor<Type,SymbolTable> { public Type visit(ArithBinopExp e, SymbolTable symtab) throws Exception { Type lType = e.left.accept(this, symtab); Type rType = e.right.accept(this, symtab); if (lType != TypeTable.intType()) throw new TypeError(“Expecting int type, found “ + lType.toString(), e.getLine); if (rType != TypeTable.intType) throw new TypeError(“Expecting int type, found “ + rType.toString(), e.getLine); // we only get here if no exceptions were thrown e.type = TypeTable.intType; } ...}
E e:bool E S1 E S2 E e:bool E e:bool E S E S E if (e) S1 else S2 E while (e) S E if (e) S E break E continue Statement rules • Statements have type void • Judgments of the form E S • In environment E, S is well-typed
T subtype of T’ Checking return statements • Special entry {ret:Tr} represents return value • Add to symbol table when entering method • Lookup entry when hit return statement E e:T ret:T’E T≤T’ ret:void E E return e; E return;
Subtyping • Inheritance induces subtyping relation • Type hierarchy is a tree • Subtyping rules: • Subtyping does not extend to array types • A subtype of B then A[] is not a subtype of B[]
E e : S S ≤ T E e : T Type checking with subtyping • S ≤ T • S may be used whenever T is expected • An Expression E from type S also has type T
IC rules with subtyping E e1 : T1 E e2 : T2 T1 ≤ T2 or T2 ≤ T1op {==,!=} E e1 op e2 : bool
Semantic analysis flow • Parsing and AST construction • Combine library AST with IC program AST • Construct and initialize global type table • Phase 1: Symbol table construction • Construct class hierarchy and check that hierarchy is a tree • Construct remaining symbol table hierarchy • Assign enclosing-scope for each AST node • Phase 2: Scope checking • Resolve names • Check scope rules using symbol table • Phase 3: Type checking • Assign type for each AST node • Phase 4: Remaining semantic checks
Class hierarchy for types abstract class Type {...}class IntType extends Type {...}class BoolType extends Type {...}class ArrayType extends Type { Type elemType;}class MethodType extends Type { Type[] paramTypes; Type returnType; ... }class ClassType extends Type { ICClass classAST; ...}...
Type comparison • Option 1: use a unique object for each distinct type • Resolve each type expression to same object • Use reference equality for comparison (==) • Option 2: implement a method t1.equals(t2) • Perform deep (structural) test • For object-oriented languages also need sub-typing: t1.subtypeof(t2)
Type table implementation class TypeTable { // Maps element types to array types private Map<Type,ArrayType> uniqueArrayTypes; private Map<String,ClassType> uniqueClassTypes; public static Type boolType = new BoolType(); public static Type intType = new IntType(); ... // Returns unique array type object public static ArrayType arrayType(Type elemType) { if (uniqueArrayTypes.containsKey(elemType)) { // array type object already created – return it return uniqueArrayTypes.get(elemType); } else { // object doesn’t exist – create and return it ArrayType arrt = new ArrayType(elemType); uniqueArrayTypes.put(elemType,ArrayType); return arrt; } } ... }
Semantic analysis flow example class A { int x; int f(int x) { boolean y; ... }}class B extends A { boolean y; int t;}class C { A o; int z;}
Parsing and AST construction AST Programfile = … class A { int x; int f(int x) { boolean y; ... }}class B extends A { boolean y; int t;}class C { A o; int z;} parser.parse() classes[2] classes[0] classes[1] … ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType TypeTable IntTypeBoolTypeABCf : int->int… Table populated with user-defined types during parsing(or special AST pass)
Defined types and type table abstract class Type { String name; boolean subtypeof(Type t) {...}}class IntType extends Type {...}class BoolType extends Type {...}class ArrayType extends Type { Type elemType;}class MethodType extends Type { Type[] paramTypes; Type returnType;}class ClassType extends Type { ICClass classAST;} class A { int x; int f(int x) { boolean y; ... }}class B extends A { boolean y; int t;}class C { A o; int z;} class TypeTable { public static Type boolType = new BoolType(); public static Type intType = new IntType(); ... public static ArrayType arrayType(Type elemType) {…} public static ClassType classType(String name, String super, ICClass ast) {…} public static MethodType methodType(String name,Type retType, Type[] paramTypes) {…}} TypeTable IntTypeBoolTypeABCf : int->int…
Assigning types by declarations AST All type bindings available during parsing time Programfile = … classes[2] classes[0] classes[1] … ICClassname = Bsuper = A ICClassname = C ICClassname = A TypeTable type IntTypeBoolType ... fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … ClassTypename = C type body parameters[0] ClassTypename = B DeclarationvarName = yinitExpr = nulltype = BoolType type Paramname = xtype = IntType super ClassTypename = A type MethodTypename = fretTypeparamTypes
Symbol tables Global symtab AST Programfile = … classes[2] classes[0] classes[1] … A symtab C symtab ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … f symtab B symtab body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType abstract class SymbolTable { private SymbolTable parent;}class ClassSymbolTable extends SymbolTable { Map<String,Symbol> methodEntries; Map<String,Symbol> fieldEntries; }class MethodSymbolTable extends SymbolTable { Map<String,Symbol> variableEntries;} abstract class Symbol { String name;}class VarSymbol extends Symbol {…} class LocalVarSymbol extends Symbol {…}class ParamSymbol extends Symbol {…}...
Scope nesting in IC class GlobalSymbolTable extends SymbolTable {}class ClassSymbolTable extends SymbolTable {}class MethodSymbolTable extends SymbolTable {}class BlockSymbolTable extends SymbolTable {} Global names of all classes Class fields and methods Method formals + locals Block variables defined in block
Symbol tables Global symtab AST Programfile = … classes[2] classes[0] classes[1] … A symtab C symtab ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … f symtab B symtab body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType this belongs to method scope … Locationname = xtype = ? ret can be used later for type-checking return statements
Sym. tables phase 1 : construction Global symtab AST enclosingScope Programfile = … classes[2] classes[0] classes[1] … A symtab C symtab ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … f symtab B symtab body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType Build tables,Link each AST node to enclosing table … ? Locationname = xtype = ? symbol abstract class ASTNode { SymbolTable enclosingScope;} class TableBuildingVisitor implements Visitor { ...}
Sym. tables phase 1 : construct Global symtab AST Programfile = … classes[2] classes[0] classes[1] … A symtab C symtab ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … f symtab B symtab body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType During this phase, add symbols from definitions, not uses, e.g., assignment to variable x … ? Locationname = xtype = ? symbol class TableBuildingVisitor implements Visitor { ...}
Sym. tables phase 2 : resolve Global symtab AST Programfile = … classes[2] classes[0] classes[1] … A symtab C symtab ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … f symtab B symtab body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType Resolve each id to a symbol,e.g., in x=5 in foo, x is the formal parameter of f … Locationname = xtype=? symbol check scope rules:illegal symbol re-definitions,illegal shadowing,illegal use of undefined symbols... enclosingScope class SymResolvingVisitor implements Visitor { ...}
Type-check AST AST Programfile = … TypeTable classes[2] IntTypeBoolType ... classes[0] classes[1] … ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType Use type-rules to infer types for all AST expression nodes Check type rules for statements … Locationname = xtype = IntType class TypeCheckingVisitor implements Visitor { ...}
Miscellaneous semantic checks AST Programfile = … classes[2] classes[0] classes[1] … ICClassname = Bsuper = A ICClassname = C ICClassname = A fields[0] methods[0] … Fieldname = xtype = IntType Methodname = f … body parameters[0] DeclarationvarName = yinitExpr = nulltype = BoolType Paramname = xtype = IntType Check remaining semantic checks: single main method, break/continue inside loops etc. … Locationname = xtype = IntType class SemanticChecker { ...}
public Type visit(While whileStatement) { Type conditionType = whileStatement.getCondition().accept(this); whileStatement.setType(PrimitiveTypes.VOID); if (conditionType != PrimitiveTypes.BOOLEAN) Error ++loopDepth; whileStatement.getOperation().accept(this); --loopDepth; returnnull; }
public Type visit(Break breakStatement) { if (loopDepth == 0) error; setType(PrimitiveTypes.VOID); returnnull; }