Accept nnkTypeSection from typedef macro pragmas (#19168)

This commit is contained in:
hlaaftana 2021-11-22 12:41:35 +03:00 • committed by GitHub
commit 1b143f5e79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 5 deletions

View file

@ -13,7 +13,36 @@
## Language changes ## 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 ## Compiler changes

View file

@ -1112,7 +1112,12 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
if name.kind == nkPragmaExpr: if name.kind == nkPragmaExpr:
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef) let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
if rewritten != nil: if rewritten != nil:
case rewritten.kind
of nkTypeDef:
typeSection[i] = rewritten typeSection[i] = rewritten
of nkTypeSection:
typeSection.sons[i .. i] = rewritten.sons
else: illFormedAst(rewritten, c.config)
typeDefLeftSidePass(c, typeSection, i) typeDefLeftSidePass(c, typeSection, i)
return return
pragma(c, s, name[1], typePragmas) pragma(c, s, name[1], typePragmas)
@ -1143,16 +1148,19 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
proc typeSectionLeftSidePass(c: PContext, n: PNode) = proc typeSectionLeftSidePass(c: PContext, n: PNode) =
# process the symbols on the left side for the whole type section, before # process the symbols on the left side for the whole type section, before
# we even look at the type definitions on the right # 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] var a = n[i]
when defined(nimsuggest): when defined(nimsuggest):
if c.config.cmd == cmdIdeTools: if c.config.cmd == cmdIdeTools:
inc c.inTypeContext inc c.inTypeContext
suggestStmt(c, a) suggestStmt(c, a)
dec c.inTypeContext dec c.inTypeContext
if a.kind == nkCommentStmt: continue case a.kind
if a.kind != nkTypeDef: illFormedAst(a, c.config) of nkCommentStmt: discard
typeDefLeftSidePass(c, n, i) of nkTypeDef: typeDefLeftSidePass(c, n, i)
else: illFormedAst(a, c.config)
inc i
proc checkCovariantParamsUsages(c: PContext; genericType: PType) = proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
var body = genericType[^1] var body = genericType[^1]

View file

@ -7755,6 +7755,7 @@ This is translated to:
This is translated to a call to the `schema` macro with a `nnkTypeDef` 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 AST node capturing both the left-hand side and right-hand side of the
definition. The macro can return a potentially modified `nnkTypeDef` tree 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. which will replace the original row in the type section.
When multiple macro pragmas are applied to the same definition, the When multiple macro pragmas are applied to the same definition, the

View file

@ -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