⚗️

Compilers & Programming Language Theory MCQ

Test your Compilers & PLT knowledge with 100 multiple choice questions covering fundamentals to advanced concepts, with instant feedback and explanations.

100 Questions 40 Beginner 40 Intermediate 20 Advanced

How This Practice Test Works

Every question below expands right on this page — click a question to reveal its four options, pick the one you think is correct, and you'll get instant feedback along with the correct answer and a short explanation of the reasoning. Questions are grouped by difficulty, so start with the 40 beginner questions to confirm your fundamentals, work through the 40 intermediate ones, and finish with the 20 advanced questions that mirror what exams and technical screenings actually ask. There's no sign-up, no timer, and no limit — retake the test as often as you like.

Curated by Tech Baithak Editorial Team  ·  Last updated: June 2026

1

What are the main phases of a compiler?

A

Correct Answer

Lexical analysis, syntax analysis, semantic analysis, intermediate code generation, optimization, code generation

Explanation

Compiler phases: Lexer (tokens) → Parser (AST) → Semantic Analysis (type checking) → IR generation → Optimization → Code generation. A linker is a separate tool.

2

What is lexical analysis?

B

Correct Answer

The first compiler phase scanning source text and grouping characters into meaningful tokens (identifiers, keywords, literals)

Explanation

The lexer (scanner) uses regular expressions/DFAs to recognize tokens: IF, id("count"), INT_LIT(42), PLUS(+). Whitespace and comments are discarded. Produces a token stream for the parser.

3

What is a token in compiler design?

B

Correct Answer

A classified unit of source code — a (token-type, value) pair produced by the lexer

Explanation

Tokens: keyword (if, while), identifier (x, myVar), literal (42, "hello"), operator (+, ==), delimiter (;, {). The token stream abstracts away whitespace and enables grammar-based parsing.

4

What is a parse tree (concrete syntax tree)?

B

Correct Answer

A tree representing the syntactic structure of a program according to every production rule of the grammar, including all grammar symbols

Explanation

Parse trees show every grammar production used. The AST (abstract syntax tree) simplifies this by removing non-informative nodes and keeping only the essential structure.

5

What is an Abstract Syntax Tree (AST)?

B

Correct Answer

A simplified tree representing the essential structure of a program, omitting syntactic sugar and grammar artifacts

Explanation

ASTs remove noise from parse trees: no redundant parentheses, semicolons, or intermediate grammar symbols. Compilers, interpreters, linters, and refactoring tools all work on the AST.

6

What is a context-free grammar used for in compilers?

B

Correct Answer

Specifying the syntactic structure of programming languages, enabling automated parser generation

Explanation

CFGs define programming language syntax: statement → if ( expr ) statement | for ( expr ; expr ; expr ) statement | ... Parser generators (yacc, ANTLR, Bison) automatically build parsers from CFG specifications.

7

What is the difference between a compiler and an interpreter?

B

Correct Answer

A compiler translates the entire program to machine code before execution; an interpreter executes source code directly, typically line-by-line

Explanation

Compiler (C, Rust): translate to native code, no runtime translator needed. Interpreter (Python, Ruby): translate+execute per statement. JIT (Java, JS V8): compile hot paths at runtime for speed with interpreter flexibility.

8

What is semantic analysis in compilers?

B

Correct Answer

Checking that a syntactically valid program is also semantically correct: type checking, scope resolution, and identifier declarations

Explanation

Semantic analysis: type checking (can't add int + string), scope checking (variable declared before use), function signature checking. Builds and queries the symbol table. Reports errors before code generation.

9

What is a symbol table in compilers?

B

Correct Answer

A data structure mapping identifiers to their attributes: type, scope, memory location, and other properties

Explanation

Symbol tables support declaration and lookup of identifiers. Scoped symbol tables handle nested scopes. Attributes stored: type, size, offset, parameter list for functions. Central to type checking and code generation.

10

What is intermediate representation (IR)?

B

Correct Answer

A language-neutral, machine-neutral representation between source and target code, enabling language-independent optimizations

Explanation

IR (three-address code, SSA, LLVM IR, JVM bytecode) decouples the front-end (language-specific) from the back-end (machine-specific). Optimizations on IR benefit all languages and targets.

11

What is register allocation in code generation?

B

Correct Answer

Assigning program variables to a limited set of CPU registers, spilling to memory when registers are exhausted

Explanation

Register allocation: choose which variables live in registers (fast) vs. memory (slow). Graph coloring-based allocation: interference graph colors = registers. Spilling: variable that doesn't fit goes to stack.

12

What is instruction selection in code generation?

B

Correct Answer

Mapping IR operations to target architecture instruction sequences, choosing the best machine instructions for each operation

Explanation

Instruction selection: IR multiplication might map to MUL, or shift+add if multiplying by power of 2. Tree-pattern matching (IBURG, SDISel) finds optimal instruction sequences.

13

What is a recursive descent parser?

B

Correct Answer

A top-down parser with one mutually recursive function per non-terminal — simple to write by hand from LL(1) grammars

Explanation

Recursive descent: parseExpr() calls parseTerm() calls parseFactor(). Handles LL(k) grammars. Easy to implement manually, extends to error recovery. Used in GCC's front-end, LLVM Clang.

14

What is the difference between top-down and bottom-up parsing?

B

Correct Answer

Top-down (recursive descent, LL) starts from the start symbol and derives; bottom-up (LR, shift-reduce) starts from tokens and reduces to the start symbol

Explanation

LL parsers: predictive, use lookahead to predict which production to apply. LR parsers: build rightmost derivation in reverse, handle larger grammar classes. GCC uses recursive descent; LR is used by Yacc/Bison.

15

What is a shift-reduce conflict?

B

Correct Answer

An ambiguity in an LR parser where it's unclear whether to shift (read next token) or reduce (apply a production) at a state

Explanation

Shift-reduce conflict: in "if (cond) if (cond) stmt else stmt" — the else might belong to either if (dangling else). Resolved by convention (else matches nearest if) or grammar disambiguation.

16

What is dead code elimination?

B

Correct Answer

A compiler optimization removing code that never executes (unreachable code) or whose result is never used (dead stores)

Explanation

DCE: unreachable code (after return, always-false conditions) and dead stores (x=5; x=10; — first assignment unused). DCE interacts with other optimizations; IR in SSA form makes DCE trivial.

17

What is constant folding?

B

Correct Answer

A compiler optimization evaluating constant expressions at compile time rather than runtime: 3*4 → 12

Explanation

Constant folding: 3+4 → 7 at compile time. Combined with constant propagation (replace uses of constant variables with their values). Eliminates unnecessary computation.

18

What is function inlining?

B

Correct Answer

A compiler optimization replacing a function call with the function body, eliminating call overhead and enabling further optimizations

Explanation

Inlining eliminates call overhead (save/restore registers, jump, return). More importantly, it exposes the callee's code to surrounding optimizations. The compiler decides when inlining is beneficial.

19

What is loop unrolling?

B

Correct Answer

A compiler optimization replicating loop body multiple times to reduce loop overhead and enable instruction-level parallelism

Explanation

Loop unrolling: for(i=0;i<8;i++) body → body body body body body body body body (8 copies, no loop). Reduces branch overhead, increases ILP. Downside: larger code, more register pressure.

20

What is SSA (Static Single Assignment) form?

B

Correct Answer

An IR form where every variable is defined (assigned) exactly once, with φ-functions merging values at control flow joins

Explanation

SSA (Rosen et al., 1988): x₁=...; x₂=...; y=x₁+x₂. φ-functions at join points: x₃=φ(x₁,x₂) depending on control flow. Makes data flow obvious, simplifies optimizations (DCE, constant propagation, LICM).

21

What is type inference?

B

Correct Answer

Automatically deducing variable and expression types from context without explicit annotations — used in Haskell, ML, Rust, and partially in Java/C#

Explanation

Hindley-Milner type inference (ML, Haskell): unification-based algorithm deriving principal types. The most general type is inferred without annotations. C++ template deduction and Java generics use related techniques.

22

What is a grammar conflict?

B

Correct Answer

When a parser cannot determine which production to use due to ambiguity or grammar structure (shift/reduce or reduce/reduce conflicts)

Explanation

Reduce/reduce conflict: two different productions can be applied to the same input. More serious than shift/reduce conflicts. Indicates grammar ambiguity or LR(1) grammar issues requiring grammar restructuring.

23

What is a language runtime?

B

Correct Answer

The system providing services to programs during execution: memory management, exception handling, type checking, and standard library

Explanation

Runtime systems provide: garbage collection (Java, Python, Go), dynamic dispatch, stack unwinding (exceptions), reflection, bounds checking. C has minimal runtime; Java's JVM is a full runtime environment.

24

What is the difference between compiled and interpreted languages?

B

Correct Answer

Compiled languages translate to machine code ahead of time; interpreted languages are translated+executed at runtime — but modern JITs blur this distinction

Explanation

C/Rust: AOT compiled → fast native code. Python: interpreted at runtime → flexible but slower. Java/JS: bytecode/IR + JIT compilation at runtime → combines portability with near-native speed after warmup.

25

What is a linker and what does it do?

B

Correct Answer

A tool combining compiled object files and resolving external symbol references to produce an executable

Explanation

Linker: takes .o files, resolves symbols (function calls, globals), combines into executable or library. Static linking embeds libraries; dynamic linking uses shared libraries (.so/.dll) loaded at runtime.

26

What is name mangling in C++ compilers?

B

Correct Answer

Encoding function names with type signature information to support function overloading and templates in compiled symbol tables

Explanation

C++ allows overloaded functions (same name, different parameters). Linkers work on unique names, so compilers encode: foo(int,float) → _Z3fooif. extern "C" disables mangling for C interop.

27

What is a virtual machine (VM) in the context of PLT?

B

Correct Answer

An abstract execution engine providing a portable runtime for bytecode — JVM for Java, CLR for .NET, CPython VM for Python

Explanation

Language VMs execute bytecode, providing: portability (write once, run anywhere), managed memory, security sandboxing, and dynamic optimization (JIT). Different from system VMs (VMware) which virtualize hardware.

28

What is bytecode?

B

Correct Answer

A compact, portable intermediate representation executed by a virtual machine, between source code and machine code

Explanation

Java bytecode (.class files), Python bytecode (.pyc), .NET CIL are examples. Platform-independent: the VM interprets/JIT-compiles bytecode for each architecture. More portable than native code.

29

What is garbage collection?

B

Correct Answer

Automatic memory management reclaiming heap memory occupied by objects no longer reachable by the program

Explanation

GC algorithms: reference counting (Python — cycles need cycle collector), mark-and-sweep (traverse from roots, free unreachable), copying (compact live objects), generational (most objects die young). Trade: simpler programming vs. GC pauses.

30

What is tail call optimization (TCO)?

B

Correct Answer

Replacing a tail-recursive call with a jump, reusing the current stack frame and enabling O(1) space recursion

Explanation

Tail call: the last action of a function is a call with no remaining work. TCO replaces call+return with a jump, reusing the stack frame. Enables recursion in place of loops without stack overflow. Mandated in Scheme, supported by Kotlin, optional in others.

31

What is a type system?

B

Correct Answer

A formal system assigning types to program constructs to prevent type errors, catch bugs at compile time, and document interfaces

Explanation

Type systems: static (checked at compile time: Java, Rust, Haskell), dynamic (checked at runtime: Python, JavaScript), gradual (both: TypeScript, mypy). Strong typing prevents implicit conversions; weak allows them.

32

What is polymorphism in type systems?

B

Correct Answer

The ability of code to work with values of multiple types — parametric (generics), ad-hoc (overloading), and subtype (inheritance) polymorphism

Explanation

Parametric (List<T> works for any T), ad-hoc (+ works for int, float, string via overloading), subtype (Cat behaves as Animal). Strachey's 1967 classification. Related: type classes (Haskell), traits (Rust).

33

What is a closure in programming languages?

B

Correct Answer

A function together with its captured environment — variables from the enclosing scope it can access after that scope has exited

Explanation

Closures capture variables from enclosing scope. In Python: def make_adder(x): return lambda y: x+y. The returned lambda closes over x. Used for callbacks, higher-order functions, and state encapsulation.

34

What is currying?

B

Correct Answer

Transforming a function of n arguments into a chain of n single-argument functions: f(a,b) becomes g(a)(b)

Explanation

Currying (Haskell Curry): add(3)(4) instead of add(3,4). In Haskell, all functions are curried by default. Enables partial application and function composition. Lambda calculus theoretically needs only single-argument functions.

35

What is pattern matching in programming languages?

B

Correct Answer

A language feature testing a value against patterns (shapes, constructors, ranges) and binding variables to matched parts, enabling elegant deconstruction of data types

Explanation

Pattern matching (Haskell, Rust, OCaml, Scala, Python 3.10): match list { [] -> 0 | [x] -> 1 | x::xs -> 1+length(xs) }. More expressive than if/else for algebraic data types.

36

What is a monad in functional programming?

B

Correct Answer

A design pattern and type class abstracting sequential computation with context (option, list, IO, state), supporting flatMap/bind for chaining

Explanation

Monads: Option (maybe null), List (non-determinism), IO (side effects), State (threading state). Operations: return (wrap value), bind (>>=, chain computations). "A monad is just a monoid in the category of endofunctors."

37

What is the lambda calculus?

B

Correct Answer

A formal system for computation based on anonymous function abstraction (λx.e) and application, equivalent in power to Turing machines

Explanation

Lambda calculus (Church 1932): λx.x is the identity function. Application: (λx.x+1) 5 = 6. β-reduction: (λx.e₁) e₂ → e₁[x:=e₂]. Church-Turing thesis: LC and TMs compute the same functions. Foundation of functional programming.

38

What is an interpreter vs a JIT compiler?

B

Correct Answer

An interpreter executes code directly; a JIT (Just-in-Time) compiler detects hot code paths and compiles them to native code at runtime for speed

Explanation

JIT (Java HotSpot, V8 TurboFan, PyPy, LuaJIT): detect frequently executed code (hot spots), compile to optimized native code at runtime. Provides interpreter flexibility + near-compiled performance after warmup.

39

What is memoization and how is it used in PL implementations?

B

Correct Answer

Caching function results for previously computed inputs — used to optimize recursive functions and implement lazy evaluation in languages

Explanation

Memoization makes pure functions fast: fib(40) goes from exponential to O(n) with memo. Languages use it for lazy evaluation (Haskell thunks cache results on first evaluation) and dynamic programming.

40

What is the difference between syntax and semantics in a programming language?

A

Correct Answer

Syntax is the set of rules defining how valid programs are written (grammar/structure); semantics defines what those programs mean (their behavior or effect)

Explanation

A program can be syntactically valid (well-formed according to the grammar) yet semantically wrong (e.g., adding a string to a boolean). Compilers check syntax via parsing and semantics via type checking and other static analyses.

1

What is the Hindley-Milner type inference algorithm?

B

Correct Answer

A unification-based algorithm inferring the most general (principal) type for every expression in a polymorphic type system without annotations

Explanation

HM (Algorithm W): unification variables, constraint generation, unification to solve constraints. Infers ∀a. [a] → Int for length :: [a] → Int. Decidable for Hindley-Milner; undecidable for System F (full second-order).

2

What is continuation-passing style (CPS)?

B

Correct Answer

A program transformation representing all control flow (including return, exception, loop exit) as explicit continuation functions rather than using the call stack

Explanation

CPS: factorial(n, k) where k is the continuation (what to do with the result). Every call becomes a tail call. CPS enables: trampolining (space-efficient recursion), call/cc, and is the target of CPS transforms in compilers.

3

What is abstract interpretation?

B

Correct Answer

A static analysis framework executing programs on abstract domains (intervals, signs, polyhedra) to prove properties without running the actual program

Explanation

Abstract interpretation (Cousot 1977): instead of executing on concrete values, run on abstract values (signs: +/-/0, intervals [l,u]). Sound overapproximations: if abstract execution is safe, concrete execution is safe. Used in Astrée (Airbus), Frama-C.

4

What is the difference between static and dynamic scoping?

B

Correct Answer

Static scoping: variable lookup uses the lexical nesting at definition; dynamic scoping: lookup uses the call stack at runtime

Explanation

Static (lexical) scoping (most languages): name resolution at compile time from definition site. Dynamic scoping (older Lisps, Perl with local): name resolution at runtime from caller chain. Static is predictable; dynamic enables implicit parameter passing.

5

What is a dependent type system?

B

Correct Answer

A type system where types can depend on values, enabling types like Vec<T,n> (length-indexed) and compile-time verification of program properties

Explanation

Dependent types (Coq, Agda, Lean, Idris): types are first-class values. "length-2 list" is a type. Addition with dependent types: add : Nat → Nat → Nat with proof of commutativity. Programs-as-proofs (Curry-Howard).

6

What is the Curry-Howard correspondence?

B

Correct Answer

A deep connection between type systems and logic: types correspond to propositions, programs to proofs, function types to implication, product types to conjunction

Explanation

Curry-Howard: A→B in logic corresponds to function type A→B in types. ∧ corresponds to product types. ∨ to sum types. A proof of a proposition is a program of the corresponding type. Foundation of proof assistants.

7

What is effect type systems?

B

Correct Answer

Type systems tracking and controlling side effects (IO, exceptions, state, non-termination) as part of the type, enabling safe effect isolation and composition

Explanation

Effect systems: Haskell's IO monad, algebraic effects (Koka, Eff), capability systems. Rust's Send/Sync track thread safety. Effect handlers (Multicore OCaml) provide modular non-local control flow with type tracking.

8

What is dataflow analysis?

B

Correct Answer

A compiler technique computing information about possible values of variables at each program point using a lattice of abstract states

Explanation

Dataflow analysis: live variable analysis (which variables are needed after each point), reaching definitions (which assignments may reach a use), available expressions. Uses fixed-point iteration over the control-flow graph.

9

What is alias analysis in compilers?

B

Correct Answer

Determining whether two pointers/references may point to the same memory location, enabling optimizations that require independence

Explanation

Alias analysis: if p and q may alias, the compiler cannot reorder or optimize p→ writes and q→ reads independently. Must-alias (always), may-alias (possibly), no-alias (never). Key to enabling vectorization and load/store reordering.

10

What is loop-invariant code motion (LICM)?

B

Correct Answer

Moving computations that produce the same result on every iteration out of the loop, reducing redundant computation

Explanation

LICM: for(i=0;i<n;i++) x=a*b+c; → x=a*b+c; for(i=0;i<n;i++). The a*b+c doesn't change each iteration. Requires proving the computation is loop-invariant and loop always executes. Reduces N iterations to 1.

11

What is strength reduction?

B

Correct Answer

Replacing expensive operations with cheaper equivalents: x*2 → x+x, x*4 → x<<2, or converting multiplications in loops to additions

Explanation

Strength reduction: mul by power of 2 → shift. In loops: for(i=0;i<n;i++) sum += a[i*stride] → track running offset. Induction variable strength reduction converts multiplications to additions.

12

What is devirtualization?

B

Correct Answer

A compiler optimization resolving virtual (dynamic dispatch) method calls to direct calls when the concrete type is known, enabling inlining

Explanation

Devirtualization: if the compiler proves obj is always a Foo, it can replace obj->method() (vtable lookup + indirect call) with Foo::method() (direct call). Enables inlining and eliminates vtable overhead.

13

What is escape analysis?

B

Correct Answer

Determining whether an object's lifetime is bounded by the current function, enabling stack allocation and lock elision for non-escaping objects

Explanation

If the compiler proves obj never escapes a method (no external references), it can: allocate on stack (no GC), eliminate synchronization (no sharing). JVM HotSpot, GraalVM perform escape analysis for lock and allocation elision.

14

What is partial evaluation?

B

Correct Answer

Specializing a program for known inputs by pre-computing expressions involving those inputs, producing a residual program optimized for remaining unknown inputs

Explanation

Partial evaluation (Futamura projections): specialize an interpreter for a specific program → compiled code. The Futamura projections show compilers can be derived from interpreters by partial evaluation — deep connection between the two.

15

What is the difference between call-by-value, call-by-reference, and call-by-need?

B

Correct Answer

Call-by-value: evaluate argument before call; by-reference: pass the location; by-need (lazy): evaluate argument only when needed, at most once

Explanation

CBV (C, Java, Rust): argument evaluated before function call. CBR (C++ &, pass address): modifications affect caller. CBN/CBNeed (Haskell): evaluate when needed, memoize result. Lazy evaluation enables infinite lists but makes space analysis harder.

16

What is monomorphization in generics/templates?

B

Correct Answer

Generating separate concrete code copies for each distinct type a generic function/class is used with, ensuring zero runtime overhead at the cost of code size

Explanation

Monomorphization (C++ templates, Rust generics): vector<int> and vector<string> generate separate compiled code. Zero overhead since no type erasure. Tradeoff: binary size. Java/Go use type erasure instead.

17

What is deforestation in functional programming compilation?

B

Correct Answer

An optimization eliminating intermediate list/tree structures produced by function composition, fusing list transformers like map and filter into a single pass

Explanation

Deforestation (Wadler 1988): map f . filter p . map g → single traversal without intermediate lists. Stream fusion in GHC: build/foldr or stream/unstream pairs enable the GHC optimizer to fuse pipelines automatically.

18

What is type erasure in generic programming?

B

Correct Answer

Discarding generic type parameters at runtime (Java/Kotlin generics), replacing them with Object and using casts, so no runtime type info is preserved for generics

Explanation

Java generics: List<String> and List<Integer> are the same type at runtime (List). Type casts are inserted at call sites. Cannot do instanceof List<String>. C++ templates and C# generics preserve type info (reification).

19

What is a trampoline in programming languages?

B

Correct Answer

A runtime technique implementing mutual tail recursion by returning thunks (suspended computations) that a driver loop executes iteratively, avoiding stack overflow

Explanation

Trampoline: each tail call returns a thunk instead of calling directly. A loop executes thunks iteratively. Enables TCO in languages without native TCO (JVM, .NET). Space: O(1) per mutual tail call.

20

How does an LL(1) parser decide which production to apply?

C

Correct Answer

It consults a parsing table indexed by the current non-terminal and the next single lookahead token, using FIRST and FOLLOW sets built from the grammar

Explanation

LL(1) parsing tables are constructed from FIRST sets (tokens that can start a production) and FOLLOW sets (tokens that can follow a non-terminal). With one token of lookahead, the table gives a unique production to apply, avoiding backtracking.

21

Why can left recursion break a recursive descent parser?

A

Correct Answer

Because the parsing function for the non-terminal would call itself again before consuming any input, causing infinite recursion and stack overflow

Explanation

A rule like expr -> expr + term makes parseExpr() immediately call parseExpr() again with no token consumed, looping forever. The standard fix is to rewrite the grammar to be right-recursive or to use an iterative loop with left-factoring.

22

What distinguishes an LR(1) parser from an LR(0) parser?

A

Correct Answer

LR(1) uses one token of lookahead when deciding to reduce, allowing it to handle a strictly larger class of grammars than LR(0), which decides based on state alone

Explanation

LR(0) items carry no lookahead, so many practical grammars produce conflicts. LR(1) attaches a lookahead symbol to each item, letting the parser decide whether to reduce based on what token follows, resolving many of those conflicts at the cost of larger tables.

23

What is the role of a lattice in dataflow analysis?

A

Correct Answer

It provides a partially ordered set of abstract values with a meet/join operation and a finite height, guaranteeing that iterative dataflow algorithms converge to a fixed point

Explanation

Dataflow frameworks model facts (e.g., sets of live variables) as lattice elements ordered by precision. The transfer functions are monotonic and the lattice has finite height, so repeatedly applying them from an initial approximation must reach a fixed point.

24

What problem does common subexpression elimination (CSE) solve?

C

Correct Answer

It detects expressions that compute the same value more than once and replaces the redundant computations with a reference to the first result

Explanation

CSE finds expressions like a*b appearing in multiple places whose operands have not changed between occurrences, computes the value once, and reuses it — reducing redundant arithmetic, memory loads, or function calls.

25

In a control-flow graph, what is a dominator of a basic block?

A

Correct Answer

A block that appears in every possible execution path from the entry node to the block in question, used to compute loop structure and placement of phi-functions

Explanation

Block A dominates block B if every path from the entry to B passes through A. Dominator trees identify natural loops and determine where SSA phi-functions must be inserted (at dominance-frontier nodes).

26

What is the purpose of the FOLLOW set when constructing predictive parsing tables?

C

Correct Answer

It lists the tokens that can immediately follow a non-terminal in some derivation, which is needed to decide when to apply epsilon-productions

Explanation

FOLLOW(A) contains every terminal that can appear immediately after A in some sentential form (plus end-of-input where applicable). It is essential for filling table entries for productions that derive the empty string.

27

What is the main idea behind a two-pass assembler or compiler design?

C

Correct Answer

Separating the work into a first pass that gathers information such as label/symbol definitions and a second pass that uses that information to resolve forward references and emit final code

Explanation

A one-pass design struggles with forward references (using a label or symbol before its definition appears). A first pass builds a complete symbol table; the second pass then resolves all references and generates correct code or addresses.

28

What does it mean for a programming language to have first-class functions?

A

Correct Answer

Functions can be passed as arguments, returned from other functions, assigned to variables, and stored in data structures, just like any other value

Explanation

Languages with first-class functions (JavaScript, Python, Haskell, Rust closures) treat functions as ordinary values, enabling higher-order functions such as map, filter, and reduce, and patterns like callbacks and combinators.

29

What is the difference between eager (strict) evaluation and lazy evaluation?

D

Correct Answer

Eager evaluation computes expressions as soon as they are bound; lazy evaluation defers computation until the value is actually needed, potentially never evaluating unused expressions

Explanation

Most imperative languages are eager: let x = expensiveCall() runs immediately. Haskell is lazy by default: a binding creates a thunk that is only forced when its value is demanded, which allows infinite data structures but complicates reasoning about memory usage.

30

What is the purpose of three-address code as an intermediate representation?

C

Correct Answer

To express computations as a sequence of simple instructions with at most one operator and three operands (e.g., t1 = b + c), simplifying optimization and code generation

Explanation

Three-address code (e.g., t1 = a * b; t2 = t1 + c) breaks complex expressions into uniform, simple steps. Its regular structure makes it easy to apply dataflow analyses, perform optimizations, and translate to target machine instructions.

31

What is a basic block in compiler analysis?

A

Correct Answer

A maximal straight-line sequence of instructions with one entry point (the first instruction) and one exit point (the last instruction), and no internal jumps or jump targets

Explanation

Basic blocks are the nodes of a control-flow graph: control enters only at the first instruction and leaves only at the last (a branch or jump). This property lets compilers analyze and optimize execution paths systematically.

32

What problem does operator precedence parsing address?

D

Correct Answer

It resolves ambiguity in expression grammars by assigning relative precedence and associativity to operators so the parser can decide how to group operands without full LR table construction

Explanation

Without precedence rules, "2 + 3 * 4" could parse multiple ways. Operator precedence parsing uses precedence and associativity tables (or precedence climbing) to build the correctly grouped expression tree efficiently.

33

What is the key idea behind generational garbage collection?

A

Correct Answer

Heap memory is divided into generations based on object age, because most objects die young; young generations are collected frequently and cheaply while older, surviving objects are collected less often

Explanation

The "generational hypothesis" observes that most allocated objects become garbage shortly after creation. By focusing collection effort on the young generation (which is small and has high garbage density), collectors like those in the JVM and .NET reduce overall pause times significantly.

34

What is the difference between a mark-and-sweep collector and a copying collector?

C

Correct Answer

Mark-and-sweep traverses from roots to mark live objects and then frees unmarked memory in place; a copying collector instead copies live objects into a new region, naturally compacting memory and leaving fragmentation behind

Explanation

Mark-and-sweep can leave the heap fragmented since dead objects are freed in place. Copying collectors (e.g., semispace) relocate live objects to a contiguous region, which compacts memory and makes allocation a simple pointer bump, at the cost of needing extra space and updating references.

35

What does "duck typing" mean in dynamically typed languages?

D

Correct Answer

A style where an object's suitability is determined by the presence of certain methods or properties at runtime, rather than by its declared type — "if it walks like a duck and quacks like a duck, it is a duck"

Explanation

In Python or Ruby, a function that calls obj.read() will work with any object providing a compatible read method, regardless of its class hierarchy. The check happens dynamically at the call site rather than through static type declarations.

36

What is the purpose of a calling convention in compiled code generation?

C

Correct Answer

It specifies the agreed-upon rules for how arguments are passed, return values are returned, and registers are saved/restored across function calls, enabling code compiled separately to interoperate correctly

Explanation

Calling conventions (e.g., System V AMD64 ABI, cdecl, stdcall) define which registers hold arguments, who cleans up the stack, and which registers a callee must preserve. Without an agreed convention, code from different compilers or languages could not call each other reliably.

37

What is the difference between a virtual method table (vtable) and inline caching?

D

Correct Answer

A vtable is a fixed per-class table of function pointers for dynamic dispatch; inline caching remembers a call site's last lookup result to speed up future dispatches

Explanation

Statically typed OOP languages resolve dynamic dispatch through per-class vtables built at compile time. Dynamic-language runtimes (V8, Smalltalk VMs) instead use inline caches that remember which method was found for a given receiver shape, avoiding repeated lookups for monomorphic call sites.

38

What is a phi (φ) function used for in SSA-based intermediate representations?

D

Correct Answer

It selects, at a control-flow merge point, which of several incoming SSA-renamed definitions of a variable should be used, depending on which predecessor block control came from

Explanation

When two branches each assign a different SSA version of a variable (x1 in one branch, x2 in the other), a phi-function x3 = φ(x1, x2) at the join point picks the correct version based on the predecessor block, preserving the single-assignment property while merging control flow.

39

What is the purpose of a thunk in lazy or call-by-name evaluation?

C

Correct Answer

A suspended, unevaluated computation paired with its environment, which is forced (evaluated) only when its value is actually required, and whose result may then be cached for reuse

Explanation

In lazy languages such as Haskell, binding a name to an expression does not immediately compute it; instead a thunk wraps the expression. The first time the value is demanded, the thunk is evaluated and, in call-by-need semantics, the result replaces the thunk so subsequent uses are free.

40

Why do many real-world type checkers use bidirectional type checking instead of pure inference or pure checking?

D

Correct Answer

Because it combines a synthesis mode (compute a type from an expression) with a checking mode (verify against an expected type), handling polymorphism and literals with fewer annotations

Explanation

Bidirectional type checking (used in TypeScript, Swift, and many academic systems) propagates expected types downward (checking) and computes types upward from the leaves (synthesis). This combination handles higher-rank polymorphism and ambiguous literals more gracefully than algorithms relying purely on one direction.

1

What is the Hindley-Milner let-polymorphism and its restriction?

A

Correct Answer

Polymorphism allowing let-bindings to be polymorphic while lambdas are monomorphic

Explanation

HM let-polymorphism: let f = λx.x in (f 1, f "a") type-checks (f is polymorphic). But (λf. (f 1, f "a")) (λx.x) doesn't (lambda parameters can't be polymorphic in HM — rank-2 types needed).

2

What is intersection types in programming languages?

B

Correct Answer

Types allowing a value to simultaneously inhabit multiple types: x : A ∩ B means x can be used where both A and B are expected, enabling more precise typing than union

Explanation

Intersection types (A ∩ B): the term has both behaviors. f : (Int→Int) ∩ (String→String) can specialize its behavior. More expressive than union. Used in TypeScript (A&B), Scala, and type theory for overloading.

3

What is the difference between nominal and structural typing?

B

Correct Answer

Nominal typing: types are compatible only if they have the same name/declaration. Structural typing: types are compatible if they have the same structure (duck typing formalized)

Explanation

Nominal (Java, C#): Dog and Cat are different types even if they have the same fields. Structural (Go interfaces, TypeScript): any type with matching methods satisfies the interface. Structural is more flexible; nominal catches more accidental coincidences.

4

What is gradual typing?

B

Correct Answer

A type system allowing any and statically typed parts to coexist, with runtime checks at the boundary (casts) ensuring consistency

Explanation

Gradual typing (Siek & Taha 2006): dynamic type is a supertype of all types. Boundaries between dynamic/static code insert runtime type checks (casts). TypeScript, mypy, gradual Racket. Blame tracking assigns casts to parties.

5

What is the semantic analysis phase and attribute grammars?

B

Correct Answer

Attribute grammars annotate grammar productions with equations defining synthesized (bottom-up) and inherited (top-down) attributes, enabling context-sensitive analysis

Explanation

Attribute grammars (Knuth 1968): synthesized attributes (computed from children: type, value), inherited attributes (from parent/siblings: expected type, environment). Specify type checking and code generation as grammar attribute equations.

6

What is operational semantics?

B

Correct Answer

A formal semantics defining programming language meaning by specifying how programs execute step-by-step as transitions between configurations

Explanation

Operational semantics (Structural, Natural/Big-step, Small-step): inference rules defining transition relation ⟨e,σ⟩→⟨e',σ'⟩ or ⟨e,σ⟩⟹⟨v,σ'⟩. Mechanically verifiable. Used in language specifications (ECMA, JVM) and proof assistants.

7

What is denotational semantics?

B

Correct Answer

A formal semantics assigning mathematical objects (functions, domains) as meanings to programs — a function [[e]] mapping syntactic expression e to its mathematical meaning

Explanation

Denotational semantics (Scott-Strachey): [[if b then e1 else e2]] = if [[b]] then [[e1]] else [[e2]]. Compositional: meaning of compound expression from meanings of parts. Requires domain theory (Scott domains) for recursive definitions.

8

What is the difference between early and late binding in object-oriented languages?

B

Correct Answer

Early binding (static dispatch): method resolved at compile time by static type; late binding (dynamic dispatch): resolved at runtime by actual object type via vtable/dispatch

Explanation

C++: non-virtual methods are early bound (direct call). Virtual methods are late bound (vtable lookup). Java: all non-final methods are late-bound. Late binding enables polymorphism; early binding enables inlining.

9

What is the Boehm-Berarducci encoding?

B

Correct Answer

A Church encoding of algebraic data types in System F (polymorphic lambda calculus), representing ADTs as higher-order functions without primitive data types

Explanation

B-B encoding (1985): List<A> = ∀R. R → (A → R → R) → R. Represents nil/cons as functions. Enables ADTs in pure System F. Related to Church numerals, continuation-passing. Scott encoding is an alternative.

10

What is the difference between System F and System Fω?

B

Correct Answer

System F adds universal quantification over types (∀T.T→T). System Fω further adds type-level lambda abstraction (type constructors as functions of kinds), enabling higher-kinded types

Explanation

System F (Girard/Reynolds): polymorphic lambda calculus with ∀. System Fω adds kind system: ⋆ (types), ⋆→⋆ (type constructors), (⋆→⋆)→⋆ (higher-kinded). Haskell uses a simplified System Fω-like core.

11

What is the linear type system and its application to resource management?

B

Correct Answer

A type system ensuring every linear value is used exactly once, enabling memory management without GC and compile-time prevention of use-after-free and double-free

Explanation

Linear types (Girard's linear logic, Rust's ownership): a linear resource must be used exactly once. Prevents: use-after-free (can't use moved value), double-free (only one owner), aliasing violations. Rust implements linear types for memory safety.

12

What is the Y combinator in lambda calculus?

B

Correct Answer

A fixed-point combinator enabling recursion in lambda calculus without explicit self-reference: Y = λf.(λx.f(x x))(λx.f(x x))

Explanation

Y combinator: Y f = f (Y f). Enables defining recursive functions without named recursion. In call-by-value languages, use Z combinator (strict version). Y demonstrates lambda calculus has fixed points, enabling computation of all recursive functions.

13

What is the Landin correspondence?

B

Correct Answer

Landin's J-operator and the SECD machine show that control operators (call/cc, setjmp) correspond to first-class continuations and can be expressed in enriched lambda calculi

Explanation

Landin (1964): SECD machine gave operational semantics to Algol-like languages. His J-operator (predecessor to call/cc) enabled first-class jumps. Foundation for understanding how control operators fit into denotational semantics.

14

What is algebraic effects and handlers?

B

Correct Answer

A programming model providing modular, type-safe side effects via effect declarations (like interfaces) and effect handlers (interpreters) that can be composed and overridden

Explanation

Algebraic effects (Bauer & Pretnar 2015, Koka, Multicore OCaml, Effekt): effects are declared as operations (raise, yield, state.get). Handlers provide implementation. Allows replacing one handler with another for testing — more modular than monads.

15

What is certified compilation (CompCert)?

B

Correct Answer

A formally verified compiler (CompCert for C) with machine-checked proofs that the compiled code preserves the source program's semantics

Explanation

CompCert (Leroy 2006): verified in Coq. Proof: for any C source S and compiled code C, if S terminates with value v, then C terminates with value v. Used in aviation and automotive safety-critical software (DO-178C).

16

What is WebAssembly's type safety and its formal semantics?

B

Correct Answer

WebAssembly has a sound type system with a formal specification (published in PLDI 2017) proving type safety: well-typed programs don't get stuck — they either terminate or continue

Explanation

Wasm formal spec (Haas et al., PLDI 2017): structural typing for operand stacks, explicit type annotations, structured control flow. Theorem: well-typed Wasm programs are safe (no undefined behavior, type errors, or stack overflows).

17

What is the principal types property in type inference?

B

Correct Answer

A type system has the principal types property if every typeable expression has a most general type that is an instance of every other type the expression can be given

Explanation

Hindley-Milner has principal types: the inferred type is the most general — any other valid type is a substitution instance of it. System F lacks principal types (type inference is undecidable). Principal types ensure type inference is well-defined.

18

What is the relationship between CPS transformation and continuation monad?

B

Correct Answer

The continuation monad (Cont r a = (a → r) → r) captures CPS computationally: flatMap composes continuations exactly as CPS transformation chains function calls

Explanation

CPS transform: f(x) becomes λk.k(f_cps(x)). The continuation monad Cont r a = (a→r)→r has bind that composes these exactly. callCC in the monad corresponds to grabbing the current continuation. Equational connection between operational and denotational semantics.

19

How does an LR parser generator construct its canonical collection of LR(0) item sets, and why does this determine the parser's states?

D

Correct Answer

By repeatedly applying the closure and goto operations starting from the augmented grammar's initial item set, where each resulting item set becomes a distinct parser state encoding exactly how much of which productions has been recognized so far

Explanation

The canonical LR(0) construction starts from the item S' -> .S, applies closure (adding items reachable via epsilon-like derivation) and goto (advancing the dot over a grammar symbol) until no new item sets appear. Each item set becomes a parser state, and transitions between them form the LR automaton that drives shift/reduce decisions.

20

In Hindley-Milner type inference, what does the occurs check in unification prevent, and what happens if it is omitted?

D

Correct Answer

It prevents a type variable from being unified with a type expression that contains that same variable, which would otherwise build an infinite (cyclic) type and cause the algorithm to loop forever or produce an inconsistent substitution

Explanation

Unifying a with a -> a would require a to equal a function whose argument is itself, an infinite type with no finite representation. The occurs check rejects such unifications; skipping it (as some implementations do for performance) can cause non-termination or unsound "infinite type" results.