Why does the compiler say my 'if x?' can never be true (E08089)?

← Data Flow Safety · Ref: Q2256

'if x?' asks whether x has a value. When the compiler is SURE x is unset at that point, the answer is always no, so the block can never run - it is dead code, and EK9 reports it (E08089). EK9 has no warnings, so the check fires only when the compiler is certain.

THE USUAL CAUSES

- the value was made by a built-in type's no-argument constructor, which makes it present but unset:

  retries <- Integer()
  if retries?                 //E08089: retries is always unset here
    Stdout().println($retries)

- the check repeats one the code has already answered: in the 'else' of 'if x?', or inside 'if not x?', x is unset, so a second 'x?' there can never be true.

THE FIX

Take the value from where it may be set - a function, a parameter, a lookup - and the check genuinely decides. If it is always unset at that point, the block is dead: delete it, or give the value a value first. To give a default where it is unset, use 'if not x?' with ':=', or ':=?' on a value that MAY be unset.

The mirror is E08088: 'if x?' on a value the compiler is sure is SET is always true (Q559). How the compiler decides what is sure, and when it gives up, is Q2255.

Example

#!ek9
defines module qa.dataflowsafety.issetnevertrue

  defines function

    //The compiler does not look inside a function for what it returns, so the checks below decide at run time.
    configuredRetries()
      <- rtn as Integer: 5

    savedName()
      -> known as Boolean
      <- rtn as String: String()
      if known
        rtn := "Steve"

    greet()
      -> known as Boolean
      stdout <- Stdout()
      name <- savedName(known)
      if name?
        stdout.println(`name: ${name}`)
      else
        stdout.println("no name saved")

  defines program

    IsSetNeverTrueDemo()
      stdout <- Stdout()

      retries <- configuredRetries()
      if retries?
        stdout.println(`retries: ${retries}`)
      if not retries?
        retries := 3
      stdout.println(`retries now: ${retries}`)

      greet(true)
      greet(false)

Common mistakes

E08089 — Integer() makes an unset Integer, so 'if retries?' can never be true. Take the value from where it may be set. See ek9 -h E08089 for details.

Incorrect:

      retries <- Integer()

Correct:

      retries <- configuredRetries()

E08089 — In the else of 'if name?' the name is unset, so a second 'name?' there can never be true. Use a plain else. See ek9 -h E08089 for details.

Incorrect:

      else if name?
        stdout.println("no name saved")

Correct:

      else
        stdout.println("no name saved")
Other ways to ask this
  • What is E08089 isSet check is always false?
  • Why is my isSet check always false?
  • Why can my '?' check never succeed?
  • Why does 'if count?' after 'count <- Integer()' fail to compile?

Coming from another language?

Java: 'if (x != null)' straight after 'x = null' compiles; an IDE may warn that the condition is always false. Kotlin reports some such checks as warnings. EK9 has no null and no warnings: 'Integer()' is a present but UNSET Integer, and an 'x?' that can never be true is reported as E08089 - only when the compiler is sure.

Keywords: ?, E08089, Integer(), String(), always false, data flow, dead code, isSet, never true, unset