src/littlesugar/withAliases

Search:
Group by:

Types

WithAliasesFlag = enum
  waSideEffect, waCompileTime

Macros

macro withAliases(flags: static[set[WithAliasesFlag]]; args: varargs[untyped]): untyped

Creates new scope with aliases so that you can use them safely.

When you use an alias of an element of seq or other collection type, that alias can become invalid pointer when you add or delete an element to it or resize it. These operation might move some elements in it or reallocate internal heap. Aliases are actually pointer and it might become pointer to wrong object or freed memory after that. This macro prevends modification of the collection after creating aliases by creating a new scope that cannot access local variables declared in outside of the scope. You can access only aliases specified in arguments.

You can still declare both an alias of a seq and an alias of the element of that seq at same time and modify them without generating compile errors. But you can find such error easily by checking arguments. When Nim implements "Aliasing restrictions in parameter passing", that should become compile error. See: https://nim-lang.org/docs/manual_experimental.html#aliasing-restrictions-in-parameter-passing

You can declare aliases in 3 ways.

  • Specify identifier as is It creates immutable alias with same name.
  • aliasname = expression It creates immutable alias with name specified in left side of = operator.
  • aliasname from expression It creates mutable alias with specified name

You can also declare small function that can be used as alias of function/procedure.

When flags is empty, making side effects in statements under withAliases is compile error like func. When waSideEffect is set to flags, making side effects is not compile error. When waCompileTime is set to flags, statements under withAliases are executed at compile time.

Restrictions:

  • You cannot use break or continue for a loop/block outside of withAliases
  • Return statements within withAliases block does not exit from a outside proc. It exits from withAliases block and returned value become value of withAliases.

For example:

var
  a = 1
  b = @[1, 2]
let r = withAliases({}, a, x from b[0], y = b[1], da(x) = doAssert(x), f(x, y) = x + y):
  x = a + y
  da(x == 3)
  da(f(a, y) == 3)
  x + a + y

doAssert r == 6

transforms to the code like this:

var
  a = 1
  b = @[1, 2]
func genSym1234(a: auto; x: var auto; y: auto): auto {.inline.} =
  func da(x {.inject.}: auto): auto {.inline.} =
    doAssert(x)
  
  func f(x {.inject.}: auto; y {.inject.}: auto): auto {.inline.} =
    x + y
  
  x = a + y
  da(x == 3)
  da(f(a, y) == 3)
  x + a + y

let r = genSym1234(a, b[0], b[1])
doAssert r == 6

Example:

let
  foo = 1
  bar = 2
let  a = withAliases({}, x = foo, bar):
  x + bar
doAssert a == 3

var b = [(n: 1, s: "aaa"), (n: 2, s: "b")]
withAliases({waSideEffect}, x from b[0].n, y = b[0].s, z from b[1]):
  x += y.len
  z.s &= $z.n
doAssert b == [(n: 4, s: "aaa"), (n: 2, s: "b2")]

Templates

template doWithAliases(args: varargs[untyped]): untyped
Same to withAliases({waSideEffect}, ...)