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

Accept nnkTypeSection from typedef macro pragmas #19168

Merged
merged 1 commit into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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