From 0ce79c14845dcff13d8bc8b4712e953daa5e7bb0 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 27 Jul 2021 15:16:44 +0200 Subject: [PATCH 1/2] fixes #18543 --- lib/core/macros.nim | 274 +++++++++++-------------------- testament/important_packages.nim | 1 + 2 files changed, 99 insertions(+), 176 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c09fae6b3ac0e..d3eaf62988b40 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1068,11 +1068,11 @@ proc newEmptyNode*(): NimNode {.noSideEffect.} = ## Create a new empty node. result = newNimNode(nnkEmpty) -proc newStmtList*(stmts: varargs[NimNode]): NimNode= +proc newStmtList*(stmts: varargs[NimNode]): NimNode = ## Create a new statement list. result = newNimNode(nnkStmtList).add(stmts) -proc newPar*(exprs: varargs[NimNode]): NimNode= +proc newPar*(exprs: varargs[NimNode]): NimNode = ## Create a new parentheses-enclosed expression. newNimNode(nnkPar).add(exprs) @@ -1494,148 +1494,80 @@ macro expandMacros*(body: typed): untyped = echo body.toStrLit result = body -proc findPragmaNodeInRecList(arg, fieldSym: NimNode): NimNode = - case arg.kind - of nnkRecList, nnkRecCase: - for it in arg.children: - result = findPragmaNodeInRecList(it, fieldSym) - if result != nil: return - of nnkOfBranch: - return findPragmaNodeInRecList(arg[1], fieldSym) - of nnkElse: - return findPragmaNodeInRecList(arg[0], fieldSym) - of nnkIdentDefs: - for i in 0.. 0: - impl.expectKind nnkTypeDef - let pragmaExpr = impl[0] - if pragmaExpr.kind == nnkPragmaExpr: - result = pragmaExpr[1] - -proc getPragmaNodeFromType(typ: NimNode): NimNode = - case typ.kind - of nnkSym: - result = getPragmaNodeFromTypeSym(typ) - of nnkProcTy: - result = typ[1] - else: error("illegal typ kind for argument: " & $typ.kind, typ) - -proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode = - sym.expectKind nnkSym - if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym) - let impl = sym.getImpl - impl.expectKind nnkIdentDefs - impl.expectLen 3 - let pragmaExpr = impl[0] - if pragmaExpr.kind == nnkPragmaExpr: - result = pragmaExpr[1] - -proc getPragmasByName(pragmaExpr: NimNode, name: string): seq[NimNode] = - if pragmaExpr.kind == nnkPragma: - for it in pragmaExpr: - if it.kind in nnkPragmaCallKinds: - if eqIdent(it[0], name): - result.add it - elif it.kind == nnkSym: - if eqIdent(it, name): - result.add it - -proc getCustomPragmaNodes(sym: NimNode, name: string): seq[NimNode] = - sym.expectKind nnkSym - case sym.symKind - of nskField: - result = getPragmaNodeFromObjFieldSym(sym).getPragmasByName(name) - of nskProc: - result = getPragmaNodeFromProcSym(sym).getPragmasByName(name) - of nskType: - result = getPragmaNodeFromTypeSym(sym).getPragmasByName(name) - of nskParam: - # When a typedesc parameter is passed to the macro, it will be of nskParam. - let typeInst = getTypeInst(sym) - if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"): - result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmasByName(name) +proc customPragmaNode(n: NimNode): NimNode = + expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) + let + typ = n.getTypeInst() + + if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: + return typ[1][1] + elif typ.typeKind == ntyTypeDesc: + let impl = typ[1].getImpl() + if impl[0].kind == nnkPragmaExpr: + return impl[0][1] else: - error("illegal sym kind for argument: " & $sym.symKind, sym) - of nskVar, nskLet: - # This checks the type of the sym too, this is consistent with how - # field expressions are handled too. If this is changed, make sure to - # change it for fields expressions too. - result = getPragmaNodeFromType(sym.getTypeInst).getPragmasByName(name) - result.add getPragmaNodeFromVarLetSym(sym).getPragmasByName(name) - else: - error("illegal sym kind for argument: " & $sym.symKind, sym) - -since (1, 5): - export getCustomPragmaNodes - -proc hasCustomPragma*(n: NimNode, name: string): bool = - n.expectKind nnkSym - result = getCustomPragmaNodes(n, name).len > 0 - -proc getCustomPragmaNodesSmart(n: NimNode, name: string): seq[NimNode] = - case n.kind - of nnkDotExpr: - result = getCustomPragmaNodes(n[1], name) - of nnkCheckedFieldExpr: - expectKind n[0], nnkDotExpr - result = getCustomPragmaNodes(n[0][1], name) - of nnkSym: - result = getCustomPragmaNodes(n, name) - of nnkTypeOfExpr: - var typ = n.getTypeInst - while typ.kind == nnkBracketExpr and typ[0].eqIdent "typeDesc": - typ = typ[1] - result = getPragmaNodeFromType(typ).getPragmasByName(name) - of nnkBracketExpr: - discard - else: - n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr}) + return impl[0] # handle types which don't have macro at all + + if n.kind == nnkSym: # either an variable or a proc + let impl = n.getImpl() + if impl.kind in RoutineNodes: + return impl.pragma + elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr: + return impl[0][1] + else: + let timpl = typ.getImpl() + if timpl.len>0 and timpl[0].len>1: + return timpl[0][1] + else: + return timpl + + if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: + let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) + let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) + var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst) + while typDef != nil: + typDef.expectKind(nnkTypeDef) + let typ = typDef[2] + typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) + let isRef = typ.kind in {nnkRefTy, nnkPtrTy} + if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) + typDef = getImpl(typ[0]) + else: # object definition, maybe an object directly defined as a ref type + let + obj = (if isRef: typ[0] else: typ) + var identDefsStack = newSeq[NimNode](obj[2].len) + for i in 0.. 0: + var identDefs = identDefsStack.pop() + if identDefs.kind == nnkRecCase: + identDefsStack.add(identDefs[0]) + for i in 1.. 0) - -iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] = - f.expectKind nnkFormalParams - for i in 1.. 0 and p[0].kind == nnkSym and p[0] == cp): + return newLit(true) + return newLit(false) + +macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## Expands to value of custom pragma `cp` of expression `n` which is expected ## to be `nnkDotExpr`, a proc or a type. ## @@ -1678,28 +1606,22 @@ macro getCustomPragmaVal*(n: typed, cp: typed): untyped = ## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf") ## assert(o.getCustomPragmaVal(serializationKey) == "mo") ## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo") - n.expectKind {nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr} - cp.expectKind {nnkSym, nnkOpenSymChoice, nnkClosedSymChoice} - let pragmaNodes = getCustomPragmaNodesSmart(n, $cp) - if pragmaNodes.len == 0: - error(n.repr & " doesn't have any custom pragmas") - let pragmaNode = pragmaNodes[^1] - - case pragmaNode.kind - of nnkPragmaCallKinds: - if pragmaNode.len == 2: - result = pragmaNode[1] - else: - # create a named tuple expression for pragmas with multiple arguments - result = newTree(nnkPar) - var i = 1 - for (key, t, v) in iterOverFormalArgs(pragmaNode[0].getImpl[3]): - result.add nnkExprColonExpr.newTree(key, pragmaNode[i]) - inc i - of nnkSym: - error("The named pragma " & cp.repr & " in " & n.repr & " has no arguments and therefore no value.") - else: - error(n.repr & " doesn't have a pragma named " & cp.repr, n) + result = nil + let pragmaNode = customPragmaNode(n) + for p in pragmaNode: + if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp: + if p.len == 2: + result = p[1] + else: + let def = p[0].getImpl[3] + result = newTree(nnkPar) + for i in 1 ..< def.len: + let key = def[i][0] + let val = p[i] + result.add newTree(nnkExprColonExpr, key, val) + break + if result.kind == nnkEmpty: + error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = ## Calls `callee` with `args` unpacked as individual arguments. diff --git a/testament/important_packages.nim b/testament/important_packages.nim index afceffb2ec992..ab29dcd0d9a26 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -114,6 +114,7 @@ pkg "nimsvg" pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true +pkg "nimYAML", "nim c -r test/tserialization.nim" pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "nim c -r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" From b472bc095d01c92cdf76b3b1d61f9f8d9e5a429c Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 27 Jul 2021 16:45:05 +0200 Subject: [PATCH 2/2] make tests green again --- changelog.md | 1 - tests/pragmas/tcustom_pragma.nim | 25 +++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/changelog.md b/changelog.md index d33e24b654235..89e013cffdb0f 100644 --- a/changelog.md +++ b/changelog.md @@ -51,7 +51,6 @@ implementations. Old behavior can be obtained with `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same underlying code is also updated the same way. -- Custom pragma values have now an API for use in macros. - On POSIX systems, the default signal handlers used for Nim programs (it's used for printing the stacktrace on fatal signals) will now re-raise the diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 3ef5bdcb65832..b197a7c55180f 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -384,7 +384,7 @@ block: discard Hello(a: 1.0, b: 12) # issue #11511 -block: +when false: template myAttr {.pragma.} type TObj = object @@ -395,23 +395,24 @@ block: let recList = objTy[2] let sym = recList[0] assert sym.kind == nnkSym and sym.eqIdent("a") - let hasAttr = sym.hasCustomPragma("myAttr") + let hasAttr = sym.hasCustomPragma(myAttr) newLit(hasAttr) doAssert hasMyAttr(TObj) -# misc -{.pragma: haha.} -{.pragma: hoho.} -template hehe(key, val: string, haha) {.pragma.} +when false: + # misc + {.pragma: haha.} + {.pragma: hoho.} + template hehe(key, val: string, haha) {.pragma.} -type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int + type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int -assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") -template hehe(key, val: int) {.pragma.} + template hehe(key, val: int) {.pragma.} -var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 + var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 -# left-to-right priority/override order for getCustomPragmaVal -assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + # left-to-right priority/override order for getCustomPragmaVal + assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")