status-im/nim-stew

`results` `valueOr` access to `error` template injected symbol fails with certain generic callers

Closed this issue · 2 comments

import stew/results

proc makeBeaconBlockForHeadAndSlot[T](): Result[int, string] =
  var gps: Result[int, string]

  discard gps.valueOr:
    return err(error)

  return err("")

proc getBlindedBlockParts() =
  discard makeBeaconBlockForHeadAndSlot[int]()

For example, results in an error of:

stew/results.nim(405, 16) Error: type mismatch: got 'proc (self: Result[error.T, error.E]): E{.noSideEffect.}' for 'error' but expected 'string'

but if one makes makeBeaconBlockForHeadAndSlot non-generic, it compiles fine, even though it doesn't use its dummy parameter regardless.

It's the result of conflict between the error func and injected symbol, which is a known Nim issue. Reproduced in

type
  Result[T, E] = object
   case o: bool
   of false:
     e: E
   of true:
     v: T

template err[T, E](R: type Result[T, E], x: untyped): R =
  R(o: false, e: x)

template err(v: auto): auto = err(typeof(result), v)

func error[T, E](self: Result[T, E]): E =
  if self.o:
    raiseAssert("Trying to access error when value is set")
  when E isnot void:
    self.e

template valueOr[T: not void, E](self: Result[T, E], def: untyped): T =
  let s = (self) # TODO avoid copy
  if s.o: s.v
  else:
    when E isnot void:
      template error: E {.used, inject.} = s.e
    def

#proc makeBeaconBlockForHeadAndSlot[T](): Result[int, string] =
proc makeBeaconBlockForHeadAndSlot(): Result[int, string] =
  var gps: Result[int, string]

  discard gps.valueOr:
    return err(error)

  return err("")

proc getBlindedBlockParts() =
  #discard makeBeaconBlockForHeadAndSlot[int]()
  discard makeBeaconBlockForHeadAndSlot()

in a self-contained way.

Workaround: qualify the error access inside valueOr:

proc makeBeaconBlockForHeadAndSlot[T](): Result[int, string] =
  var gps: Result[int, string]

  discard gps.valueOr:
    return err(gps.error)

  return err("")