E08089: isSet check is always false based on data flow analysis

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: PRE_IR_CHECKS · Classification: NEVER_SET_ISSET_CHECK · All error codes

What went wrong

This isSet check (?) is always false based on data flow analysis. The variable is provably UNSET at this point — typically because it was declared with ? (unset) and never assigned a value before this check.

The same applies to set-checks you do not write as '?' (D7). A guard whose stored value is provably unset never enters: 'if v <- Integer()' (a literal type's no-argument constructor is present but unset), or 'if late :=? String()' on an unset 'late'. 'then not v?' after a guard on v is always false, because the condition only runs once the gate has found v set. An 'x?' that is one operand of an 'and' condition is judged like a whole condition.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 2256 (Why does the compiler say my 'if x?' can never be true...)
  • ek9 -q 559 (Why does EK9 reject my isSet check as redundant?)
  • ek9 -q 2255 (How does the EK9 compiler decide a variable is definitely...)
  • ek9 -q 736 (How does EK9 detect redundant empty and isSet checks?)
  • ek9 -q 640 (When is an isSet check on a collection genuinely needed...)
  • ek9 -q 311 (What code quality checks does the EK9 compiler perform?)

How to fix it

  1. Assign a value to the variable before checking it with ?
  2. Remove the unreachable code block
  3. Check if a required assignment is missing
  4. Consider whether the variable should have been declared with an initial value

Common causes

  • Variable declared with ? (unset) but no assignment before the isSet check
  • Intended assignment was accidentally deleted or commented out
  • Refactoring moved the check above the assignment
  • Misunderstanding of EK9's unset declaration semantics
  • Optional created empty (no value) and then checked with ? — always UNSET
  • A guard over 'Integer()', 'String()', 'Date()' ... — the no-argument constructor makes an unset value, so the guard never enters; use the value you meant to store
  • 'then not v?' after 'if v <- f()' — the guard only enters when v is set

See also

  • E08088: isSet check always true (variable provably SET)
  • E08087: condition always false (comparison-based tautology)
  • E08086: condition always true (comparison-based)

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E08089: isSet check is always false based on data flow analysis

#!ek9
defines module error.e08089.bad

  defines function

    computeName()
      <- rtn as String: "Alice"

    computeCount()
      <- rtn as Integer: 42

    //Else of isSet narrows to UNSET, re-checking is always false
    checkNeverSetInElse()
      <- result as String: "no-name-found"

      name <- computeName()
      if name?
        result: name
      else
        if name?
          result: "unreachable-name-branch"

    //Same pattern with Integer variable
    checkNeverSetIntegerElse()
      <- result as String: "no-count-found"

      count <- computeCount()
      if count?
        result: $count
      else
        if count?
          result: "unreachable-count-branch"

Fixed: Do not re-test what the else already decided

#!ek9
defines module error.e08089.good

  defines function

    computeName()
      <- rtn as String: "Alice"

    checkName()
      <- result as String: "no-name-found"
      name <- computeName()
      if name?
        result: name

Questions and answers

These Q&A entries show E08089 and its fix: