E08096: an operator that keeps an unset value unset can never give it a value

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

What went wrong

An in-place operator is applied to a value that is always unset at this point, and the operator is one that keeps an unset value unset - so the operation can never give it a value. Data flow analysis proves both: the value is unset here (it was made by a no-argument constructor such as 'Integer()' or 'String()', or this code is inside 'if not x?'), and '+=', '-=', '*=', '/=', '++' and '--' on the built-in types never set an unset value, whatever the argument. The program would run and quietly leave the value unset.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 2253 (Why is 'count <- Integer()' then 'count++' reported as an ...)
  • ek9 -q 2251 (How do I build up a String with '+=' in EK9?)
  • ek9 -q 2252 (What should a counter or a running total start from in EK9?)

Rationale

EK9 values are tri-state: absent, present but unset, or set. 'Integer()' and 'String()' make a value that is PRESENT but UNSET - it has no value yet, not even 0 or "". An operator on an unset value gives an unset result rather than guessing, and '+=' is no exception: there is no starting value to add to, and the operator never invents one. So a total started from 'Integer()' stays unset however many amounts are added, and a sentence started from 'String()' loses every word.

This is reported only when the compiler is SURE. EK9 has no warnings, so an error must be certain: a value that came from a function call may be set, and the compiler stays silent; inside a loop the value must stay unset on every pass (a loop that also assigns it is left alone); an operator of your own type defines its own meaning and is never reported. A copy ':=:' does set an unset value, and a stream into a variable ('cat items > total') works differently again - neither is one of these operators.

How to fix it

  1. Start the value from a literal, which is set: a String from "", a count or a sum from 0, a product from 1, a Float from 0.0, a Duration from PT0S, a Millisecond from 0ms.
  2. Inside 'if not x?' give it a value with ':=' instead; the condition itself makes the value unset there, so adding to it can do nothing (assign-if-unset there always applies, E08097).
  3. If the value is meant to stay unset until something real arrives, assign that value when it does instead of adding to the unset one.

Common causes

  • 'text <- String()' then 'text += word' to build up a String - start from ""
  • 'total <- Integer()' then 'total += amount' in a loop - start from 0
  • 'count <- Integer()' then 'count++' - start from 0
  • A returning variable declared '<- rtn as String: String()' and then appended to
  • Adding to a value inside 'if not x?', the one place it is known to be unset

See also

  • E08089: isSet check always false (the same data flow proof, applied to '?')
  • E08095: assign-if-unset ':=?' can never apply (the target is always set)
  • E08097: assign-if-unset ':=?' always applies (the target is always unset)
  • E08020: a variable might be used before being initialised

Examples

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

Fails with E08096: an operator that keeps an unset value unset can never give it a value

#!ek9
defines module error.e08096.bad

  defines function

    joinWords()
      -> words as List of String
      <- sentence as String: String()
      for word in words
        sentence += word

Fixed: Start from a value, or give the value one first

#!ek9
defines module error.e08096.good

  defines function

    //Start from a literal: '""' and '0' are set, so '+=' and '++' build on them.
    describe()
      -> words as List of String
      <- summary as String: ""
      count <- 0
      for word in words
        summary += word
        count++
      summary += ` (${count} words)`

    //An unset value is given one first - inside 'if not total?' it is unset, so ':=' is the form.
    topUp()
      -> amount as Integer
      <- total as Integer: amount
      if not total?
        total := 0
      total += 1

    //A value from a function call may be set: the compiler cannot be sure, so it stays silent.
    supplied()
      <- rtn as Integer: 1

    addToSupplied()
      <- total as Integer: supplied()
      total += 1

Fixed: Start the String from "" so '+=' has a value to add to

#!ek9
defines module error.e08096.good.fix

  defines function

    joinWords()
      -> words as List of String
      <- sentence as String: ""
      for word in words
        sentence += word

Questions and answers

These Q&A entries show E08096 and its fix: