E08269: a coalescing operator ('?:', '<?', '>?') mixed with a different binary operator must be parenthesised - it binds tighter than arithmetic, comparison and logic, so write '(a + b) ?: c' or 'a + (b ?: c)' to say which is meant

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

What went wrong

A coalescing operator - coalesce (Elvis) '?:' or one of the comparison coalescing operators '<?', '>?' - is mixed with a DIFFERENT binary operator without parentheses. EK9 does not let precedence decide which is applied first, because the two readings give different answers on an unset value. 'first + second ?: fallback' reads as 'first + (second ?: fallback)', because '?:' binds tighter than '+'; when 'first' is unset the sum is unset - the fallback never applied to the sum.

Other languages disagree about this precedence (Kotlin puts '?:' below '+', C# and JavaScript put their '??' below '=='), so whichever reading you expected, someone reading your code expects the other.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 2060 (Why must I put parentheses around '?:' when I mix it with '+' or '=='?)

Each of these is E08269:

  • sum <- first + second ?: fallback //'?:' inside '+'
  • fallenBack <- first ?: second + fallback //also '?:' inside '+': (first ?: second) + fallback
  • same <- first == second ?: fallback
  • both <- ready ?: false and other
  • smallest <- first <? second + limit //'+' takes '<?' as an operand
  • bounded <- first <? second >? floor //two DIFFERENT coalescing operators
  • chosen <- first ?: second <? fallback //'?:' and '<?' are different operators
  • power <- first ?: second ^ 2 //'^' binds tighter than '?:'

Each of these is fine:

  • //The sum, or the fallback when the sum is unset
  • wholeSum <- (first + second) ?: fallback
  • //First plus (second, or the fallback)
  • oneOperand <- first + (second ?: fallback)
  • //The SAME operator chained reads one way only
  • chosen <- first ?: second ?: fallback
  • smallest <- first <? second <? third
  • bounded <- (first <? second) >? floor

How to fix it

  1. Decide which operator is meant to apply first and parenthesise it: '(first + second) ?: fallback' falls back when the whole sum is unset; 'first + (second ?: fallback)' falls back for one operand only.
  2. Chains of the same coalescing operator ('a ?: b ?: c', 'a <? b <? c') need no parentheses.

Common causes

  • Expecting Kotlin's precedence, where '?:' binds below '+'
  • A long one-line expression combining '<~>', '?:' and '<'
  • Writing 'flag ?: false and other' to default a Boolean before combining it

See also

  • E08269: a coalescing operator mixed with another operator (this error)
  • E08094: a coalescing expression with a predetermined result
  • E50030: the operands of a coalescing operator must share a type
  • E08273: word 'not' as an operand of an operator that binds tighter than it

Examples

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

Fails with E08269: a coalescing operator ('?:', '<?', '>?') mixed with a different binary operator must be parenthesised - it binds tighter than arithmetic, comparison and logic, so write '(a + b) ?: c' or 'a + (b ?: c)' to say which is meant

#!ek9
defines module error.e08269.bad

  defines function

    total()
      ->
        first as Integer
        second as Integer
        fallback as Integer
      <- rtn as Integer?

      rtn: first + second ?: fallback

    mixedForms()
      ->
        first as Integer
        second as Integer
        fallback as Integer
        limit as Integer
        floor as Integer
        ready as Boolean
        other as Boolean

      sum <- first + second ?: fallback //'?:' inside '+'
      fallenBack <- first ?: second + fallback //also '?:' inside '+': (first ?: second) + fallback
      same <- first == second ?: fallback
      both <- ready ?: false and other
      smallest <- first <? second + limit //'+' takes '<?' as an operand
      bounded <- first <? second >? floor //two DIFFERENT coalescing operators
      chosen <- first ?: second <? fallback //'?:' and '<?' are different operators
      power <- first ?: second ^ 2 //'^' binds tighter than '?:'

Fixed: Parenthesise the operand you mean

#!ek9
defines module error.e08269.good

  defines function

    total()
      ->
        first as Integer
        second as Integer
        fallback as Integer
      <- rtn as Integer?

      rtn: (first + second) ?: fallback

Fixed: Parenthesise to say which applies first; a chain of the same operator reads one way only

#!ek9
defines module error.e08269.good.forms

  defines function

    readings()
      ->
        first as Integer
        second as Integer
        third as Integer
        fallback as Integer
        floor as Integer
      <- rtn as String?
      //The sum, or the fallback when the sum is unset
      wholeSum <- (first + second) ?: fallback
      //First plus (second, or the fallback)
      oneOperand <- first + (second ?: fallback)
      //The SAME operator chained reads one way only
      chosen <- first ?: second ?: fallback
      smallest <- first <? second <? third
      bounded <- (first <? second) >? floor
      rtn: `${wholeSum} ${oneOperand} ${chosen} ${smallest} ${bounded}`

Questions and answers

These Q&A entries show E08269 and its fix: