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

give type macro pragmas unary type section with experimental switch #24245

Open
wants to merge 4 commits into
base: devel
Choose a base branch
from
Open
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
26 changes: 26 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@

## Language changes

- With the experimental option `--experimental:typedTypeMacroPragma`,
macro pragmas in type definitions now receive a unary `nnkTypeSection` as
the argument instead of `nnkTypeDef`, which means `typed` arguments are now
possible for these macros.

```nim
{.experimental: "typedTypeMacroPragma".}

import macros

macro foo(def: typed) =
assert def.kind == nnkTypeSection # previously nnkTypeDef
assert def.len == 1
assert def[0].kind == nnkTypeDef
result = def

type Obj {.foo.} = object
x, y: int

let obj = Obj(x: 1, y: 2)
```

To keep compatibility, macros can be updated to accept either one of
`nnkTypeDef` or `nnkTypeSection` as input. Note that these macros can
still only return `nnkTypeDef` or `nnkTypeSection` nodes.


## Compiler changes

Expand Down
1 change: 1 addition & 0 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ type
# alternative to above:
genericsOpenSym
vtables
typedTypeMacroPragma

LegacyFeature* = enum
allowSemcheckedAstModification,
Expand Down
9 changes: 6 additions & 3 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,10 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
s.typ = newTypeS(tyForward, c)
s.typ.sym = s # process pragmas:
if name.kind == nkPragmaExpr:
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
var macroArg = typeDef
if typedTypeMacroPragma in c.features:
macroArg = newTreeI(nkTypeSection, typeDef.info, typeDef)
let rewritten = applyTypeSectionPragmas(c, name[1], macroArg)
if rewritten != nil:
case rewritten.kind
of nkTypeDef:
Expand Down Expand Up @@ -1712,8 +1715,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
obj.flags.incl sfPure
obj.typ = objTy
objTy.sym = obj
for sk in c.skipTypes:
discard semTypeNode(c, sk, nil)
for i in 0..<c.skipTypes.len:
discard semTypeNode(c, c.skipTypes[i], nil)
c.skipTypes = @[]

proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
Expand Down
11 changes: 7 additions & 4 deletions doc/manual_experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,13 @@ side of the definition. The macro can return either a type section or
another `nnkTypeDef` node, both of which will replace the original row
in the type section.

In the future, this `nnkTypeDef` argument may be replaced with a unary
type section node containing the type definition, or some other node that may
be more convenient to work with. The ability to return nodes other than type
definitions may also be supported, however currently this is not convenient
With the experimental option `--experimental:typedTypeMacroPragma`,
this `nnkTypeDef` argument is replaced with a unary type section node
containing the type definition, which allows macros to receive the node
as a `typed` argument.

In the future, the ability to return nodes other than type definitions may
also be supported, however currently this is not convenient
when dealing with mutual type recursion. For now, macros can return an unused
type definition where the right-hand node is of kind `nnkStmtListType`.
Declarations in this node will be attached to the same scope as
Expand Down
27 changes: 27 additions & 0 deletions tests/pragmas/tcustom_pragma.nim
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,33 @@ TypeDef

static: doAssert Baz.x is string

{.push experimental: "typedTypeMacroPragma".}

const typeAst2 = """
TypeSection
TypeDef
PragmaExpr
Ident "Baz2"
Pragma
Empty
ObjectTy
Empty
Empty
RecList
IdentDefs
Ident "x"
Ident "string"
Empty
"""

type
Baz2 {.expectedAst(typeAst2).} = object
x: string

static: doAssert Baz2.x is string

{.pop.}

const procAst = """
ProcDef
Ident "bar"
Expand Down
25 changes: 24 additions & 1 deletion tests/pragmas/ttypedef_macro.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
discard """
matrix: "; -d:typeSectionInput"
"""

const typeSectionInput = defined(typeSectionInput)

when typeSectionInput:
{.experimental: "typedTypeMacroPragma".}

import macros

macro makeref(s): untyped =
expectKind s, nnkTypeDef
when typeSectionInput:
expectKind s, nnkTypeSection
let s = s[0]
else:
expectKind s, nnkTypeDef
result = newTree(nnkTypeDef, s[0], s[1], newTree(nnkRefTy, s[2]))

type
Expand All @@ -12,6 +25,11 @@ doAssert Obj is ref
doAssert Obj(a: 3)[].a == 3

macro multiply(amount: static int, s): untyped =
when typeSectionInput:
expectKind s, nnkTypeSection
let s = s[0]
else:
expectKind s, nnkTypeDef
let name = $s[0].basename
result = newNimNode(nnkTypeSection)
for i in 1 .. amount:
Expand All @@ -32,6 +50,11 @@ doAssert not declared(Bar3)
# https://github.com/nim-lang/RFCs/issues/219

macro inferKind(td): untyped =
when typeSectionInput:
expectKind td, nnkTypeSection
let td = td[0]
else:
expectKind td, nnkTypeDef
let name = $td[0].basename
var rhs = td[2]
while rhs.kind in {nnkPtrTy, nnkRefTy}: rhs = rhs[0]
Expand Down
49 changes: 49 additions & 0 deletions tests/pragmas/ttypedef_macro_typed.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
discard """
nimout: '''
TypeSection
TypeDef
PragmaExpr
Sym "X"
Pragma
Empty
ObjectTy
Empty
Empty
Empty
'''
"""

import macros

{.experimental: "typedTypeMacroPragma".}

block: # changelog entry
macro foo(def: typed) =
doAssert def.kind == nnkTypeSection # previously nnkTypeDef
doAssert def.len == 1
doAssert def[0].kind == nnkTypeDef
result = def

type Obj {.foo.} = object
x, y: int

let obj = Obj(x: 1, y: 2)

block: # issue #18864
macro test(n: typed): untyped =
echo n.treeRepr
result = n

type
X {.test.} = object
var x = X()

block: # issue #15334
macro entity(entityType: typed) =
result = entityType

type
RootEntity = ref object of RootObj
Player {.entity.} = ref object of RootEntity
x, y: int
var foo = Player()
Loading