How does EK9 track variable initialization across control flow branches?
← Data Flow Safety · Ref: Q633
EK9 tracks the initialization state of every variable through all control flow paths. If a variable might be uninitialized on any reachable path where it is used, the compiler raises E08020.
BRANCH INITIALIZATION
When a variable is assigned in both the if and else branches, the compiler knows it is initialized after the if-else regardless of which branch executes. If only one branch initializes the variable, the compiler considers it potentially uninitialized after the conditional.
RETURN VARIABLE DEFAULTS
Return variables declared with a default value (e.g., '<- rtn as String: unknown') are always initialized. The default ensures the variable has a value even if no branch explicitly sets it.
SAFE PATTERN
Either provide a default value at declaration time, or ensure every branch of every conditional initializes the variable before it is used.
See Q632 for definition order. See Q634 for guard-based safe access. See Q251 for debugging unset variable errors.
Example
defines module qa.dataflow.branchinit defines constant EXCELLENT_THRESHOLD <- 90 GOOD_THRESHOLD <- 70 POOR_THRESHOLD <- 50 defines function <?- Correct: the return variable has a default value, so it is always initialized regardless of branch taken. -?> classifyScore() as pure -> score as Integer <- category as String: "average" if score >= EXCELLENT_THRESHOLD category: "excellent" else if score >= GOOD_THRESHOLD category: "good" else if score < POOR_THRESHOLD category: "poor" <?- Correct: both branches explicitly set the return variable. The compiler can verify both paths initialize it. -?> describeSign() as pure -> number as Integer <- description as String: "zero" if number > 0 description: "positive" else if number < 0 description: "negative" <?- Correct: intermediate variables are initialized at declaration and used after initialization in both branches. -?> formatAmount() as pure -> amount as Float showDecimals as Boolean <- formatted as String: `${amount}` rounded <- amount + 0.5 label <- "Amount" if showDecimals formatted: `${label}: ${amount}` else formatted: `${label}: ${rounded}` defines program BranchInitDemo() stdout <- Stdout() cat1 <- classifyScore(95) stdout.println(`Score 95: ${cat1}`) cat2 <- classifyScore(42) stdout.println(`Score 42: ${cat2}`) sign1 <- describeSign(5) stdout.println(`5 is ${sign1}`) sign2 <- describeSign(-3) stdout.println(`-3 is ${sign2}`) fmt1 <- formatAmount(19.99, true) stdout.println(fmt1) fmt2 <- formatAmount(19.99, false) stdout.println(fmt2)
Common mistakes
E50001 — If category is only initialized in one branch, it may be uninitialized when used later. Provide a default value at declaration or initialize on all paths. See ek9 -h E50001 for details.
Incorrect:
category as String: String() if score >= EXCELLENT_THRESHOLD category: "excellent" stdout.println(category)
Correct:
category as String: "average"
Other ways to ask this
- What happens if a variable is only initialized in one branch?
- How does EK9 check initialization in if-else?
- What is E08020 used before initialized?
Coming from another language?
Java: definite assignment analysis requires variables to be assigned before use, similar concept. Rust: borrow checker ensures initialization on all paths. Go: zero-value initialization means variables are always initialized (but may have wrong value). Python: runtime NameError if variable not assigned on taken path. C: undefined behavior for uninitialized variables. EK9: compile error E08020 for potentially uninitialized variables on any reachable path.
Keywords: safety, else, initialize, uninitialized, variable, flow, if, path, E08020, control, data-flow, initialization, branch