Skip to content

Commit

Permalink
Accept nnkTypeSection from typedef macro pragmas (#19168)
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn authored Nov 22, 2021
1 parent eb5358d commit 1b143f5
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 6 deletions.
31 changes: 30 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,36 @@

## Language changes


- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
allowing multiple type definitions to be injected in place of the original type definition.

```nim
import macros
macro multiply(amount: static int, s: untyped): untyped =
let name = $s[0].basename
result = newNimNode(nnkTypeSection)
for i in 1 .. amount:
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
type
Foo = object
Bar {.multiply: 3.} = object
x, y, z: int
Baz = object
# becomes
type
Foo = object
Bar1 = object
x, y, z: int
Bar2 = object
x, y, z: int
Bar3 = object
x, y, z: int
Baz = object
```

## Compiler changes

Expand Down
18 changes: 13 additions & 5 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,12 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
if name.kind == nkPragmaExpr:
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
if rewritten != nil:
typeSection[i] = rewritten
case rewritten.kind
of nkTypeDef:
typeSection[i] = rewritten
of nkTypeSection:
typeSection.sons[i .. i] = rewritten.sons
else: illFormedAst(rewritten, c.config)
typeDefLeftSidePass(c, typeSection, i)
return
pragma(c, s, name[1], typePragmas)
Expand Down Expand Up @@ -1143,16 +1148,19 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
# process the symbols on the left side for the whole type section, before
# we even look at the type definitions on the right
for i in 0..<n.len:
var i = 0
while i < n.len: # n may grow due to type pragma macros
var a = n[i]
when defined(nimsuggest):
if c.config.cmd == cmdIdeTools:
inc c.inTypeContext
suggestStmt(c, a)
dec c.inTypeContext
if a.kind == nkCommentStmt: continue
if a.kind != nkTypeDef: illFormedAst(a, c.config)
typeDefLeftSidePass(c, n, i)
case a.kind
of nkCommentStmt: discard
of nkTypeDef: typeDefLeftSidePass(c, n, i)
else: illFormedAst(a, c.config)
inc i

proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
var body = genericType[^1]
Expand Down
1 change: 1 addition & 0 deletions doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7755,6 +7755,7 @@ This is translated to:
This is translated to a call to the `schema` macro with a `nnkTypeDef`
AST node capturing both the left-hand side and right-hand side of the
definition. The macro can return a potentially modified `nnkTypeDef` tree
or multiple `nnkTypeDef` trees contained in a `nnkTypeSection` node
which will replace the original row in the type section.

When multiple macro pragmas are applied to the same definition, the
Expand Down
66 changes: 66 additions & 0 deletions tests/pragmas/ttypedef_macro.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import macros

macro makeref(s): untyped =
expectKind s, nnkTypeDef
result = newTree(nnkTypeDef, s[0], s[1], newTree(nnkRefTy, s[2]))

type
Obj {.makeref.} = object
a: int

doAssert Obj is ref
doAssert Obj(a: 3)[].a == 3

macro multiply(amount: static int, s): untyped =
let name = $s[0].basename
result = newNimNode(nnkTypeSection)
for i in 1 .. amount:
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))

type
Foo = object
Bar {.multiply: 2.} = object
x, y, z: int
Baz = object

let bar1 = Bar1(x: 1, y: 2, z: 3)
let bar2 = Bar2(x: bar1.x, y: bar1.y, z: bar1.z)
doAssert Bar1 isnot Bar2
doAssert not declared(Bar)
doAssert not declared(Bar3)

# https://github.com/nim-lang/RFCs/issues/219

macro inferKind(td): untyped =
let name = $td[0].basename
var rhs = td[2]
while rhs.kind in {nnkPtrTy, nnkRefTy}: rhs = rhs[0]
if rhs.kind != nnkObjectTy:
result = td
else:
for n in rhs[^1]:
if n.kind == nnkRecCase and n[0][^2].eqIdent"_":
let kindTypeName = ident(name & "Kind")
let en = newTree(nnkEnumTy, newEmptyNode())
for i in 1 ..< n.len:
let branch = n[i]
if branch.kind == nnkOfBranch:
for j in 0 ..< branch.len - 1:
en.add(branch[j])
n[0][^2] = kindTypeName
return newTree(nnkTypeSection,
newTree(nnkTypeDef, kindTypeName, newEmptyNode(), en),
td)

type Node {.inferKind.} = ref object
case kind: _
of opValue: value: int
of opAdd, opSub, opMul, opCall: kids: seq[Node]

doAssert opValue is NodeKind
let node = Node(kind: opMul, kids: @[
Node(kind: opValue, value: 3),
Node(kind: opValue, value: 5)
])
doAssert node.kind == opMul
doAssert node.kids[0].value * node.kids[1].value == 15

0 comments on commit 1b143f5

Please sign in to comment.