Skip to content

[RFC] Working with types in macro is difficult. #44

Open
@mratsim

Description

@mratsim

Context

I find working with types in macro quite difficult and I think Nim would gain a lot if it was easier.
The 2 main issues I've noted are:

  • getTypeInst only works in a typed macro, but it doesn't work in a compile-time proc. In an untyped context, this forces us to use a helper macro with getAST/quote do as we can't call a macro from a macro.
  • It is very easy to get Cannot instantiate or type mismatch because of forgetting to convert the NimSym given by getTypeInst to NimIdent if they are not used raw.

Example:

import macros

type Conv2DLayer[T] = object
  data: T
type Context[T] = ref object
  data: T

proc replaceNodes*(ast: NimNode): NimNode =
  # Replace NimIdent and NimSym by a fresh ident node
  proc inspect(node: NimNode): NimNode =
    case node.kind:
    of {nnkIdent, nnkSym}:
      return ident($node)
    of nnkEmpty:
      return node
    of nnkLiterals:
      return node
    else:
      var rTree = node.kind.newTree()
      for child in node:
        rTree.add inspect(child)
      return rTree
  result = inspect(ast)

####################################

####################################

macro ctxSubtype(context: Context): untyped =
  ## Extract the subtype from a Context
  echo context.getTypeInst.treerepr
  result = context.getTypeInst[1] # This gives a cryptic error got <Foo> expected <bar>
  # result = replaceNodes(context.getTypeInst[1]) # If we replace the symbol by ident it works!

macro genModelFromCtx(modelType: untyped, context: Context): untyped =

  var records = nnkRecList.newTree

  let subtype = getAST(ctxSubtype(context))

  let field_type = nnkBracketExpr.newTree(
      ident("Conv2DLayer"), subtype
    )

  records.add nnkIdentDefs.newTree(
      newIdentNode("field_name"),
      field_type,
      newEmptyNode()
    )

  result = nnkStmtList.newTree(
    nnkTypeSection.newTree(
      nnkTypeDef.newTree(
        newIdentNode($modelType),
        newEmptyNode(),
        nnkObjectTy.newTree(
          newEmptyNode(),
          newEmptyNode(),
          records
        )
      )
    )
  )

var ctx: Context[seq[int]]

expandMacros:
  genModelFromCtx(FooType2, ctx)

## Output - before replaceNodes:
# got: <seq[int]>
# but expected: <T>

## Output fixed:
# type
#   FooType2 = object
#     field_name: Conv2DLayer[seq[int]]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions