E08266: an injected component is always set, so checking that it is set is always true - use the injected value directly; for a fallback, use a new variable ('chosen <- supplied' then 'chosen :=? injected')

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

What went wrong

You are checking whether an INJECTED component is set. An injected variable ('name as T!') is always set: the compiler proves that every injection a program can reach is registered in its application (E08210, E08220), so the application always supplies the component. The check is always true - it is dead code that reads as though the injection might not arrive, and any 'else' it guards never runs.

Every form of the check is rejected: 'logger?' as an if/while condition, in 'require' or 'assert', as a returned value or as a ternary's control; the coalescing operator '?:' with the injected variable on the left; a guard on the injected variable itself ('if log <- logger'); and a re-point checked in the head ('if log := logger then log?').

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 669 (Why can't I reassign an injected component variable?)

How to fix it

  1. Remove the check and use the injected value directly - it is always there.
  2. If you wanted a fallback, the fallback runs the other way round: keep the injected component as the default and assign it to a NEW variable only when another value is not set (good_fallback.ek9 in the [EXAMPLE] section below). The injected variable itself is never assigned (E08170).
  3. If the component may genuinely be absent, it should not be injected: pass it as a parameter, or hold it in an ordinary variable or property that can be unset.

Common causes

  • Defensive null-checking habit from Spring/Guice, where an optional injection can be null
  • 'require injected?' written to "touch" the variable - use it instead
  • The idiom 'x <- injected? <- injected else Default()', which looks like a fallback but never takes it
  • Checking a component property declared 'as T!' before calling it

See also

  • E08266: checking that an injected component is set (this error)
  • E08170: an injected variable is never assigned, ':=?' included
  • E08210: an injection reachable from a program is not registered in its application
  • E08220: a program reaches an injection but has no application
  • E08088: an isSet check that data flow proves is always true

Examples

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

Fails with E08266: an injected component is always set, so checking that it is set is always true - use the injected value directly; for a fallback, use a new variable ('chosen <- supplied' then 'chosen :=? injected')

#!ek9
defines module error.e08266.bad

  defines function

    report()
      logger as Logger!
      if logger?
        logger.log("report")

  defines component

    Logger as abstract
      log() as abstract
        -> message as String

Fixed: Use the injected component directly - it is always set

#!ek9
defines module error.e08266.good

  defines function

    report()
      logger as Logger!
      logger.log("report")

  defines component

    Logger as abstract
      log() as abstract
        -> message as String

Fixed: For a fallback, start from the other value and fill in with :=?

#!ek9
defines module error.e08266.good.fallback

  defines component

    Logger as abstract
      log() as abstract
        -> message as String

  defines function

    choose()
      -> supplied as Logger
      logger as Logger!
      chosen <- supplied
      chosen :=? logger
      chosen.log("choose")

Questions and answers

These Q&A entries show E08266 and its fix: