Compiler DesignUnit 711 min read
Type Checking: Rules, Errors & Semantic Analysis
Unit 7 of Compiler Design explores type checking—how compilers enforce data type consistency in source code, detect errors, and ensure semantic correctness before code execution. This note covers type systems (static vs. dynamic), type rules, type compatibility, and real-world applications in compilers like Java and C+
TAKEAWAYS:
- Type checking ensures type safety by validating operations (e.g.,
int + stringfails) before runtime. - Static typing (compile-time) catches errors early (e.g., Java), while dynamic typing (runtime) allows flexibility (e.g., Python).
- Type compatibility rules determine if two types can interact (e.g.,
int→floatis allowed, but not vice versa). - Type errors (e.g., mismatched declarations, invalid conversions) must be reported with descriptive messages (e.g., "Cannot convert
stringtoint"). - Type inference (e.g., in ML or Rust) deduces types automatically from usage.
- Overloading and inheritance complicate type checking but enable polymorphism.
1. Introduction to Type Checking
Type checking is a semantic analysis phase in compilers that verifies:
- Type declarations match usage (e.g.,
int x = 5;). - Operands in expressions are compatible (e.g.,
x + ywherexisintandyisfloat). - Function arguments match parameter types.
- Return types align with function declarations.
Why Type Checking Matters
- Prevents runtime crashes (e.g., dividing a string by 2).
- Improves code reliability (e.g., catching
nullpointer dereferences early). - Enables optimizations (e.g., knowing a variable is
intallows faster arithmetic).
2. Static vs. Dynamic Type Checking
| Feature | Static Typing (Compile-Time) | Dynamic Typing (Runtime) |
|---|---|---|
| Examples | Java, C++, Rust, Go | Python, JavaScript, Ruby |
| Error Detection | Early (compile-time) | Late (runtime) |
| Performance | Faster execution (types known ahead) | Slower (type checks at runtime) |
| Flexibility | Rigid (types fixed at declaration) | Flexible (types inferred dynamically) |
| Use Case | Large-scale systems (e.g., Android apps) | Scripting, rapid prototyping |
Example in Nepalese Context:
- eSewa (static typing in backend services like Java/Spring) ensures secure financial transactions by validating types before processing payments.
- Python-based tools (dynamic typing) in NTC’s network monitoring allow quick scripting but require runtime checks.
3. Type Rules and Compatibility
A. Basic Type Rules
Assignment Compatibility:
- A variable’s type must match its declaration.
int x = 10; // Valid int x = "hello"; // Error: Cannot convert string to int
- A variable’s type must match its declaration.
Expression Compatibility:
- Operands in an expression must be compatible types.
int + float→float(widening conversion allowed).int + string→ Error (incompatible types).
- Operands in an expression must be compatible types.
Function Call Compatibility:
- Argument types must match parameter types.
void greet(String name) { ... } greet(123); // Error: int cannot be converted to String
- Argument types must match parameter types.
B. Type Conversion Rules
| Conversion Type | Example | Valid? |
|---|---|---|
| Implicit (Widening) | int → float |
Yes |
| Explicit (Narrowing) | float → int |
Yes (with cast) |
| Incompatible | string → int |
No |
Example: Type Casting in C
float f = 3.14;
int i = (int)f; // Explicit narrowing: f = 3 (truncated)
Trace Table:
| Step | f Value |
i Value |
Action |
|---|---|---|---|
| Declaration | 3.14 | - | float f = 3.14; |
| Casting | 3.14 | 3 | int i = (int)f; (truncation) |
4. Type Errors and Recovery
flowchart TD
A["Parse: `int x = 'hello';`"]
B{"Type Check: `string` → `int`?"}
B -->|"No"| C["Error: Incompatible Types"]
C --> D["Recovery: Skip Token"]
D --> E["Continue Parsing"]
E --> F["Output: `int x = 5;` (if corrected)"]Compiler recovery for invalid type assignment (string → int).
Common Type Errors
- Type Mismatch:
x = "10" + 5 # Error: str + int unsupported - Invalid Conversion:
int y = (int)"hello"; // Error: Cannot cast String to int - Undefined Types:
typedef int myInt; myInt x = 5.5; // Error: float cannot be assigned to myInt
Error Recovery Strategies
- Insert Implicit Conversions (e.g.,
int x = "10";→int x = 10;). - Skip the Invalid Token (e.g., ignore
+in"hello" + 5). - Report and Halt (for critical errors like missing semicolons).
Example: Error Recovery in a Compiler
flowchart TD
A["Parse Expression"] --> B{"Types Compatible?"}
B -->|"Yes"| C["Proceed"]
B -->|"No"| D["Attempt Implicit Conversion"]
D --> E{"Success?"}
E -->|"Yes"| C
E -->|"No"| F["Report Error<br/>Skip Token"]
F --> C5. Type Checking in Real-World Compilers
A. Java (Static Typing)
- Strict type checking catches errors at compile-time.
- Example:
ArrayIndexOutOfBoundsExceptionis prevented by bounds checking.int[] arr = {1, 2, 3}; int x = arr[3]; // Compile-time error: Array index out of bounds
B. Python (Dynamic Typing)
- Flexible but runtime-checked.
- Example:
TypeErrorif operations are invalid.x = "10" y = x + 5 # Runtime Error: can only concatenate str (not "int") to str
C. Rust (Advanced Static Typing)
- Ownership and borrowing enforce memory safety.
- Example: Compile-time borrow checker prevents dangling pointers.
let x = 5; let y = &x; // Valid: y borrows x let z = &x; // Valid: multiple immutable borrows // let w = &mut x; // Error: cannot borrow x as mutable while y and z exist
In the Real World
eSewa (Nepal):
- Uses static typing (Java/Spring Boot) for financial transactions.
- Type checking ensures
amountisBigDecimal(notString) before processing payments, preventing fraud.
Khalti API (Nepal):
- Dynamic typing (Node.js/Python) allows quick integration but requires runtime type validation.
- Example: A merchant’s
order_idmust be aUUID(not aStringlike"123").
NTC’s Network Monitoring (Python Scripts):
- Dynamic typing helps parse logs quickly, but type errors (e.g.,
intvs.strin bandwidth data) crash scripts. - Solution: Use
try-exceptblocks for graceful error handling.
- Dynamic typing helps parse logs quickly, but type errors (e.g.,
NEPSE Stock Trading Platform:
- Static typing (C#/.NET) ensures
share_priceisdecimal(notstring) to avoid calculation errors.
- Static typing (C#/.NET) ensures
6. Worked Example: Type Checking in a Simple Language
Problem: Check the following C-like code for type errors:
int x = 5;
float y = x + 3.14;
char z = y + 'A';
printf("%c", z);
Solution:
- Step 1:
int x = 5;→ Valid. - Step 2:
float y = x + 3.14;x(int) +3.14(float) →float(widening allowed).- State After Step 2:
flowchart TD A["x: int = 5"] --> B["y: float = 8.14"]
- Step 3:
char z = y + 'A';y(float) +'A'(char/int) → Error: Cannot implicitly convertfloattochar.- Fix: Cast
ytoint:char z = (int)y + 'A'; // Valid: z = 8 + 65 = 73 ('I')
Trace Table:
| Step | x Type/Value |
y Type/Value |
z Type/Value |
Action |
|---|---|---|---|---|
| 1 | int = 5 |
- | - | int x = 5; |
| 2 | int = 5 |
float = 8.14 |
- | y = x + 3.14; (widening) |
| 3 (Error) | int = 5 |
float = 8.14 |
- | z = y + 'A' (invalid) |
| 3 (Fixed) | int = 5 |
float = 8.14 |
char = 'I' |
z = (int)y + 'A' |
7. Advanced Topics
A. Type Inference
- Example: ML (Functional Language)
let x = 5; (* Inferred as int *) let y = 3.14; (* Inferred as float *) let z = x + y; (* Error: int + float *) - Solution: Explicit types or widening:
let z = float x + y; (* z inferred as float *)
B. Overloading and Polymorphism
- Method Overloading (same name, different parameters):
void print(int x) { ... } void print(String s) { ... } // Overloaded - Type Checking: Ensures correct overload is called.
print(5); // Calls print(int) print("hi"); // Calls print(String)
C. Generic Programming (Templates in C++)
- Type Parameters allow reusable code:
template<typename T> T max(T a, T b) { return (a > b) ? a : b; } - Type Checking: Ensures
aandbare the same type.
8. Exam Tip
What to Expect in TU/PU Exams
Short Questions (2-5 marks):
- Define static vs. dynamic typing.
- List 3 type errors and their fixes.
- Explain type compatibility rules with examples.
Long Questions (10-15 marks):
- Trace type checking for a given code snippet (like the worked example above).
- Design a type checker for a subset of a language (e.g., arithmetic expressions).
- Compare static and dynamic typing with pros/cons (use the table above).
Programming (5-10 marks):
- Write a type checker for assignments/expressions (pseudocode or Python).
- Example:
def check_type_assign(lhs_type, rhs_type): if lhs_type == rhs_type: return True elif (lhs_type == "int" and rhs_type == "float"): return True # Widening allowed else: return False
Application-Based Questions:
- How does eSewa use type checking to prevent fraud?
- Why does Python use dynamic typing for scripting?
Key Formulas/Concepts to Remember
| Concept | Formula/Rule |
|---|---|
| Widening Conversion | int → float, char → int |
| Narrowing Conversion | float → int (requires cast) |
| Type Error | incompatible_types = False |
| Overloading | Same name, different parameter types |
9. Summary Checklist
Before the exam, ensure you can: ✅ Differentiate static vs. dynamic typing with examples. ✅ List 3 type errors and their fixes. ✅ Trace type checking for a given code snippet. ✅ Explain type compatibility rules (widening/narrowing). ✅ Describe real-world applications (eSewa, Khalti, NTC). ✅ Write a simple type checker in pseudocode.
In the real world
- eSewa (Nepal) uses static typing (Java/Spring Boot) to validate
PaymentRequestobjects before processing. Theamountfield must be aBigDecimal(notString), preventing fraudulent transactions likeamount = "₹1000". - Khalti API (Node.js/Python) dynamically checks types at runtime (e.g.,
order_idmust be aUUIDobject, not aStringlike"123"). - NTC’s Python scripts for network monitoring fail if
bandwidthdata is miscast (e.g.,int(512)vs.str("512")), requiringtry-exceptblocks to handleValueErrorgracefully.
Based on the PU BE Computer (PU) syllabus for Compiler Design (CMP360), unit 7.
Discussion
Loading…