Coursify

Compiler Design (CD)

Semantic Analysis and Type Checking

55 mins

The final phase of the frontend compiler pipeline. Learn how the compiler ensures type safety and semantic correctness before generating intermediate code.

Learning Goals

  • Explain the role of semantic analysis as the phase that checks meaning beyond syntax.
  • Describe how the compiler cross-references attributes stored in the symbol table to ensure variables and functions are used correctly.
  • Trace type checking step-by-step on an expression involving mixed types and coercion.
  • Differentiate between type coercion (implicit) and type casting (explicit).
  • Explain scope rules and how the semantic analyzer enforces declaration-before-use.
  • Compare static scope (lexical) vs dynamic scope with code examples.
  • Differentiate between static and dynamic type checking.
  • Distinguish strong vs weak typing and name vs structural type equivalence.
  • Identify common semantic errors and understand why static type checking is preferred in compiled languages.

The Role of Semantic Analysis

Semantic analysis is the compiler phase that checks the meaning of a program beyond its grammatical structure. While syntax analysis verifies that tokens follow the grammar rules (e.g., an assignment has the form identifier = expression), semantic analysis verifies that the assignment is semantically valid — the identifier has been declared, the expression's type is compatible with the identifier's type, and no rules are violated.

Key responsibilities of the semantic analyzer:

ResponsibilityDescription
Type CheckingVerifies that operands of each operator are type-compatible.
Scope ResolutionDetermines which declaration a variable reference refers to.
Declaration Before UseEnsures every identifier is declared before it is referenced.
Function MatchingChecks that function calls match declared parameter types and count.
Coercion InsertionAdds implicit type conversion nodes where widening is needed.

Type Checking & Symbol Table Integration

Type checking is entirely dependent on the Symbol Table. The compiler cannot check types just by looking at the syntax tree; it must cross-reference historical data stored during earlier declarations.

When the parser produces a parse tree, the semantic analyzer walks it and annotates each node with type information drawn from the symbol table. The result is an annotated AST — a tree where every expression node carries its computed type and every coercion is explicitly marked.

Trace: Type Checking on `a = b + c`

  1. 1
    Step 1

    The analyzer retrieves types for a, b, and c. Current state:

    • a: float
    • b: int
    • c: float
  2. 2
    Step 2

    For b + c, the analyzer sees int + float. Since int can be widened to float safely, it decides to perform implicit coercion.

  3. 3
    Step 3

    The analyzer inserts a node: int_to_float(b). The expression becomes int_to_float(b) + c. The result type of this addition is now float.

  4. 4
    Step 4

    The final assignment a = float_result is checked. Since a is a float, the operation is semantically valid.

Annotated AST Visualization

The tree below illustrates how the semantic analyzer transforms a raw parse tree by inserting explicit coercion nodes:

Coercion vs. Casting

Coercion is implicit and automatic (e.g., intfloat). It is safe widening.

Casting is explicit and programmer-driven (e.g., (int)3.9). It can be narrowing and may result in data loss.

Scope Rules and Declaration Checking

Scope is the region of program text where a particular declaration is visible. The semantic analyzer enforces two critical rules:

  1. Declaration before use: Every identifier must be declared before it is referenced.
  2. Uniqueness: No duplicate declarations of the same name are allowed within the same scope level.

Also called Lexical Scope. Scope is determined by the source code structure. A reference resolves to the declaration in the closest enclosing block in the text.

Used by: C, Java, Python, Rust.

Frequently Asked Questions

Knowledge Check

Question 1 of 4
Q1Single choice

If a compiler implicitly converts an int to a float during type checking, this is known as:

Semantic Analysis and Type Checking | Compiler Design (CD) | Coursify