Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot instantiate T when generating AST from macro #10902

Open
bluenote10 opened this issue Mar 24, 2019 · 4 comments
Open

cannot instantiate T when generating AST from macro #10902

bluenote10 opened this issue Mar 24, 2019 · 4 comments

Comments

@bluenote10
Copy link
Contributor

I'm repeatedly running into the same problem here, maybe this is the actual problem behind #10858. The macro also produces an AST involving generics that is valid when written manually, but somehow the AST returned from the macro doesn't compile -- this time with a different error though.

Would be great if a more experience Nim dev could quickly comment if this looks like a bug, or if I'm trying to do something that is not possible? (I'm trying to base a design decision on whether this is possible or not.)

Example

import macros

type
  Base[T] = ref object

dumpTree: # reference tree to be produced by macro below
  proc clone[T](self: Base[T]): Base[T] =
    discard

macro genCloneProc(typeWithGenArg: untyped): untyped =
  echo typeWithGenArg.treeRepr
  # =>
  #   BracketExpr
  #     Ident "Base"
  #     Ident "T"
  result = newProc(
    ident "clone", [
      typeWithGenArg, # return type: Base[T]
      newIdentDefs(
        ident "self",
        typeWithGenArg,
      )
    ],
    newStmtList(
      newNimNode(nnkDiscardStmt).add(newEmptyNode())
    )
  )
  # set the generic param 
  let genericParamIdent = typeWithGenArg[1] # the `T`
  result[2] = newNimNode(nnkGenericParams)
  result[2].add(newIdentDefs(genericParamIdent, newEmptyNode()))
  echo result.repr
  echo result.treeRepr

genCloneProc(Base[T])

Current Output

report_2019_03_24_macro_cannot_instantiate.nim(35, 18) Error: cannot instantiate Base
got: <T>
but expected: <T>

Additional Information

$ nim -v
Nim Compiler Version 0.19.9 [Linux: amd64]
Compiled at 2019-03-24
Copyright (c) 2006-2019 by Andreas Rumpf

git hash: 1332f649b2e32a84fb643b580750e2f554eb8a4f
active boot switches: -d:release

Comparing the refernce AST to the macro AST:

image

@mratsim
Copy link
Collaborator

mratsim commented Mar 24, 2019

This seems like another case of nim-lang/RFCs#44 (Working with types in macro is difficult).

Unfortunately the naive regen idents that worked wonder in #8531 and nim-lang/RFCs#44 (comment) doesn't work here:

import macros

type
  Base[T] = ref object

dumpTree: # reference tree to be produced by macro below
  proc clone[T](self: Base[T]): Base[T] =
    discard

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 genCloneProc(typeWithGenArg: untyped): untyped =
  echo typeWithGenArg.treeRepr
  # =>
  #   BracketExpr
  #     Ident "Base"
  #     Ident "T"
  result = newProc(
    ident "clone", [
      typeWithGenArg, # return type: Base[T]
      newIdentDefs(
        ident "self",
        typeWithGenArg,
      )
    ],
    newStmtList(
      newNimNode(nnkDiscardStmt).add(newEmptyNode())
    )
  )
  # set the generic param 
  let genericParamIdent = newIdentNode($typeWithGenArg[1]) # the `T`
  result[2] = newNimNode(nnkGenericParams)
  result[2].add(newIdentDefs(genericParamIdent, newEmptyNode()))
  echo result.repr
  echo result.treeRepr

genCloneProc(Base[T])

and even building the AST from scratch doesn't:

import macros

type
  Base[T] = ref object

dumpASTGen: # reference tree to be produced by macro below
  proc clone[T](self: Base[T]): Base[T] =
    discard

macro genCloneProc(typeWithGenArg: untyped): untyped =
  echo typeWithGenArg.treeRepr
  # let typeWithGenArg = typeWithGenArg.replaceNodes

  # =>
  #   BracketExpr
  #     Ident "Base"
  #     Ident "T"
  result = newStmtList()
  result.add nnkProcDef.newTree(
    newIdentNode"clone",
    newEmptyNode(),
    nnkGenericParams.newTree(
      newIdentDefs(
        typeWithGenArg[1],
        newEmptyNode()
      )
    ),
    nnkFormalParams.newTree(
      typeWithGenArg,
      newIdentDefs(
        newIdentNode"self",
        typeWithGenArg
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      nnkDiscardStmt.newTree(
        newEmptyNode()
      )
    )
  )

  echo result.repr
  echo result.treeRepr

expandMacros:
  genCloneProc(Base[T])

@Araq
Copy link
Member

Araq commented Mar 25, 2019

I can confirm it's a compiler bug. And one that I don't understand (yet).

@jxy
Copy link
Contributor

jxy commented Mar 26, 2019

It's simply a problem of some parts of the AST points to the same NimNode. The solution is just copy the result once. The following compiles.

import macros

type
  Base[T] = ref object

dumpTree: # reference tree to be produced by macro below
  proc clone[T](self: Base[T]): Base[T] =
    discard

macro genCloneProc(typeWithGenArg: untyped): untyped =
  echo typeWithGenArg.treeRepr
  # =>
  #   BracketExpr
  #     Ident "Base"
  #     Ident "T"
  result = newProc(
    ident "clone", [
      typeWithGenArg, # return type: Base[T]  ::: This is used multiple times in the AST
      newIdentDefs(
        ident "self",
        typeWithGenArg,
      )
    ],
    newStmtList(
      newNimNode(nnkDiscardStmt).add(newEmptyNode())
    )
  )
  # set the generic param 
  let genericParamIdent = typeWithGenArg[1] # the `T`
  result[2] = newNimNode(nnkGenericParams)
  result[2].add(newIdentDefs(genericParamIdent, newEmptyNode()))
  result = result.copy   # Notice the copy here.
  echo result.repr
  echo result.treeRepr

genCloneProc(Base[T])

@bluenote10
Copy link
Contributor Author

Thanks for figuring out a work-around! I can confirm this also works in my actual use case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants