E08030: has not been checked before access

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

What went wrong

You're accessing a method on Optional, Result, or Iterator that requires a set-check BEFORE access, but the compiler cannot verify safety. Check and access must be in the SAME syntactic scope.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 993 (The compiler reports E08030 on my discount lookup. What ...)
  • ek9 -q 163 (How do I safely unwrap an Optional value?)
  • ek9 -q 86 (How do Result guard patterns and ternary access work?)
  • ek9 -q 47 (How does the Optional type work in EK9?)
  • ek9 -q 84 (What operations does Optional support in EK9?)
  • ek9 -q 48 (How does the Result type work in EK9?)
  • ek9 -q 1379 (Why can't I call get() in a do-while body when the traili...)

Safe patterns

Each of these compiles:

//Inside the block the check protects
if o?
  rtn += o.get()
//isOk() guards ok()
if r.isOk()
  rtn += r.ok()
//A guard checks implicitly
if found <- maybeCount()
  rtn += found.get()
//hasNext() guards next()
if iter <- o.iterator() then iter.hasNext()
  rtn += iter.next()
//A top-level 'and' conjunct proves it
if checked := maybeCount() then checked? and (rtn < limit)
  rtn += checked.get()
//An earlier conjunct protects a later one
if o? and o.get() > limit
  rtn += o.get()

Unsafe patterns

Each of these is this error:

//A copy of the check is not a guard
hasValue <- o?
if hasValue
  rtn += o.get()
//No protection at all
rtn += o.get()
//A '?' under 'or', 'not' or a call argument proves nothing
if (o? and first) or (o? and second)
  rtn += o.get()
//The access runs BEFORE its check
if o.get() > limit and o?
  rtn += 1

Rationale

Storing the check result in a variable breaks the guarantee because the original variable could be reassigned between check and access.

Safe alternatives

For Optional:

  • o.getOrDefault("fallback")
  • o.whenPresent(consumer)

For Result:

  • r.okOrDefault("fallback")
  • r.whenOk(consumer)

For Iterator:

  • while iter <- x.iterator() then iter.hasNext()

How to fix it

  1. Use if/guard pattern that keeps check and access together
  2. Or use getOrDefault/whenPresent methods

See also

  • E08030: unprotected access (this error)
  • E08040: reassignment in protected block

Examples

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

Fails with E08030: has not been checked before access

#!ek9
defines module error.e08030.bad

  defines function

    nameOf()
      -> maybe as Optional of String
      <- rtn as String: "nobody"
      rtn: maybe.get()

    unsafeForms()
      ->
        o as Optional of Integer
        first as Boolean
        second as Boolean
        limit as Integer
      <- rtn as Integer: 0

      //A copy of the check is not a guard
      hasValue <- o?
      if hasValue
        rtn += o.get()
      //No protection at all
      rtn += o.get()
      //A '?' under 'or', 'not' or a call argument proves nothing
      if (o? and first) or (o? and second)
        rtn += o.get()
      //The access runs BEFORE its check
      if o.get() > limit and o?
        rtn += 1

Fixed: Check the Optional with ? on the path to each get()

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

  defines function

    nameOf()
      -> maybe as Optional of String
      <- rtn as String: "nobody"
      if maybe?
        rtn: maybe.get()

    unsafeForms()
      ->
        o as Optional of Integer
        first as Boolean
        second as Boolean
        limit as Integer
      <- rtn as Integer: 0

      //A copy of the check is not a guard
      if o?
        rtn += o.get()
      //No protection at all
      if o?
        rtn += o.get()
      //A '?' under 'or', 'not' or a call argument proves nothing
      if o? and (first or second)
        rtn += o.get()
      //The access runs BEFORE its check
      if o? and o.get() > limit
        rtn += 1

Fixed: Check that it is set first

#!ek9
defines module error.e08030.good

  defines function

    nameOf()
      -> maybe as Optional of String
      <- rtn as String: "nobody"
      if maybe?
        rtn: maybe.get()

Fixed: Every form that checks before it reads, in the same construct

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

  defines function

    maybeCount()
      <- rtn as Optional of Integer: Optional(2)

    safeForms()
      ->
        o as Optional of Integer
        r as Result of (Integer, String)
        limit as Integer
      <- rtn as Integer: 0
      checked <- Optional() of Integer
      //Inside the block the check protects
      if o?
        rtn += o.get()
      //isOk() guards ok()
      if r.isOk()
        rtn += r.ok()
      //A guard checks implicitly
      if found <- maybeCount()
        rtn += found.get()
      //hasNext() guards next()
      if iter <- o.iterator() then iter.hasNext()
        rtn += iter.next()
      //A top-level 'and' conjunct proves it
      if checked := maybeCount() then checked? and (rtn < limit)
        rtn += checked.get()
      //An earlier conjunct protects a later one
      if o? and o.get() > limit
        rtn += o.get()

Questions and answers

These Q&A entries show E08030 and its fix: