Static (Lexical) Scoping: How Variable References Get Their Meaning
Under static scoping (also called lexical scoping), the meaning of a variable reference is determined primarily by the lexical program structure—i.e., by which declaration is visible according to the surrounding blocks/modules/functions in the program text.
This is in contrast to dynamic scoping, where the runtime call sequence controls which binding a reference resolves to.
In terms of the answer choices:
- (i) Runtime call sequence → not primary under static scoping.
- (ii) CPU register contents → irrelevant to the language rule of binding (registers hold runtime values, not lexical name-resolution rules).
- (iii) Heap location → not primary; heap addresses are consequences of execution/implementation details, while static scoping is a semantic name-resolution property.
- (iv) Lexical program structure → correct: it determines which binding a variable reference denotes.
Static vs Dynamic Scoping (Concept Overview)
Key concepts you need
- name binding: assigning an identifier to a specific declaration.
- scope: where a binding applies.
- environment: how implementations represent bindings during execution.
Static scoping is fundamentally a compile-time / semantic rule: when you write x, the language determines (from the program’s text structure) which x declaration you mean, based on nesting and visibility. Runtime then follows those decisions to fetch the appropriate value.
Memory vs meaning
CPU registers, the heap, and activation records hold values/locations at runtime. Static scoping determines which declaration a variable reference refers to, based on lexical structure.
How a variable reference is resolved under static (lexical) scoping
- 1Step 1
Identify the declarations and the nesting structure (e.g., blocks, functions, modules).
- 2Step 2
Starting from the reference’s position, walk outward through the lexical nesting to find the nearest visible declaration.
- 3Step 3
The reference’s meaning becomes “the variable declared here” (not “whatever value happens to be on the call stack”).
- 4Step 4
The runtime uses the binding decision to access the corresponding environment/activation record; this may involve registers, stack frames, or heap locations depending on the implementation.
Why (i) runtime call sequence is not primary
Under static scoping, the same reference resolves the same way regardless of how the program arrived there via calls. The call stack affects which functions are currently active, but static scoping’s core rule says the identifier’s declaration is determined by where it appears in the source text.
A useful mental model:
- Static scoping answers: “Which declaration does this
xmean?” using lexical nesting. - Dynamic scoping answers: “Which caller context is active?” using runtime call sequence.
CPU registers and heap locations are not the scoping rule
Choices (ii) and (iii) are tempting because they appear during execution, but the language semantics of static scoping does not say “use register contents” or “use heap location” to resolve identifiers.
Instead:
- Lexical scoping decides the binding (identifier → declaration).
- Implementation decides how to represent that binding at runtime (e.g., registers for short-lived values, stack frames for activations, heap for closures).
This explains why you can observe different runtime behaviors (different addresses/locations) while still having the same static binding meaning.
Resolution depends on nesting in the source code.
The reference binds to the nearest visible declaration in the lexical structure.
Static scoping with a name-shadowing example (conceptual)
Consider a nested structure where an inner block declares a new x. Under static scoping, any reference to x inside the inner block refers to the inner declaration, even if the program was invoked through different call sequences.
This illustrates lexical priority: inner declarations shadow outer ones based on structure, not runtime history.
Common misconception
Static scoping does NOT mean bindings are decided at CPU-level (registers/heap). It means name resolution follows the program’s lexical nesting/visibility rules.
Where static scoping decisions show up
Name resolution rule applied
Lexical/semantic phaseChoose the declaration a reference denotes using lexical structure."
References may be compiled to offsets/links
Compilation phaseCompiler can map the binding to a specific runtime access path (e.g., frame slot)."
Values are fetched/stored
Execution phaseRuntime uses environment/activation representation; registers/heap manage data movement, not scoping semantics."
Check your understanding
Knowledge Check
Under static (lexical) scoping, the meaning of a variable reference is determined primarily by which factor?
Explore Related Topics
Type Checking in Compiler Phases (Choose the Correct Answer)
Register-Reference vs Memory-Reference Instructions: Detailed Study with Examples
Compare Between Compile-Time, Load-Time, and Execution-Time Address Binding
Address binding maps a program’s symbolic or relocatable addresses to actual physical memory, and it can be performed at compile‑time, load‑time, or execution‑time.
- Compile‑time binding: the compiler produces absolute code; final addresses are fixed before loading and require recompilation if the start location changes.
- Load‑time binding: the loader relocates code using a base register (e.g., ); addresses are fixed after loading but no recompilation is needed.
- Execution‑time binding: the CPU generates logical addresses that the MMU translates on each reference, allowing the process to move while running and supporting paging, segmentation, and virtual memory.