New/better macro pragmas, mark some as experimental (#19406)

* New/better macro pragmas, make some experimental

fix #15920, close #18212, close #14781, close #6696,
close https://github.com/nim-lang/RFCs/issues/220

Variable macro pragmas have been changed to
only take a unary section node.
They can now also be applied in sections with multiple variables,
as well as `const` sections. They also accept arguments.

Templates now support macro pragmas, mirroring other routine types.

Type and variable macro pragmas have been made experimental.
Symbols without parentheses instatiating nullary macros or templates
has also been documented in the experimental manual.

A check for a redefinition error based on the left hand side of variable
definitions when using variable macro pragmas was disabled.
This nerfs `byaddr` specifically, however this has been documented as
a consequence of the experimental features `byaddr` uses.

Given how simple these changes are I'm worried if I'm missing something.

* accomodate compiler boot

* allow weird pragmas

* add test for #10994

* remove some control flow, try remove some logic
This commit is contained in:
metagn
2022-01-20 22:57:50 +03:00
committed by GitHub
parent 1563cb2f6e
commit 2bd1aa186e
8 changed files with 435 additions and 205 deletions

View File

@@ -341,8 +341,12 @@ proc checkNilable(c: PContext; v: PSym) =
#include liftdestructors
proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
let value = identDefs[^1]
proc addToVarSection(c: PContext; result: var PNode; n: PNode) =
if result.kind != nkStmtList:
result = makeStmtList(result)
result.add n
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
if result.kind == nkStmtList:
let o = copyNode(orig)
o.add identDefs
@@ -445,55 +449,115 @@ proc setVarType(c: PContext; v: PSym, typ: PType) =
"; new type is: " & typeToString(typ, preferDesc))
v.typ = typ
proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
var b = a[0]
if b.kind == nkPragmaExpr:
if b[1].len != 1:
# we could in future support pragmas w args e.g.: `var foo {.bar:"goo".} = expr`
return nil
let nodePragma = b[1][0]
# see: `singlePragma`
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
# make sure it's not a normal pragma, and calls an identifier
# considerQuotedIdent below will fail on non-identifiers
result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds
if result:
# make sure it's not a user pragma
let ident = considerQuotedIdent(c, key)
result = strTableGet(c.userPragmas, ident) == nil
if result:
# make sure it's not a custom pragma
var amb = false
let sym = searchInScopes(c, ident, amb)
result = sym == nil or sfCustomPragma notin sym.flags
var amb = false
var sym: PSym = nil
case nodePragma.kind
of nkIdent, nkAccQuoted:
let ident = considerQuotedIdent(c, nodePragma)
var userPragma = strTableGet(c.userPragmas, ident)
if userPragma != nil: return nil
let w = nodePragma.whichPragma
if n.kind == nkVarSection and w in varPragmas or
n.kind == nkLetSection and w in letPragmas or
n.kind == nkConstSection and w in constPragmas:
return nil
sym = searchInScopes(c, ident, amb)
# XXX what if amb is true?
# CHECKME: should that test also apply to `nkSym` case?
if sym == nil or sfCustomPragma in sym.flags: return nil
of nkSym:
sym = nodePragma.sym
else:
return nil
# skip if not in scope; skip `template myAttr() {.pragma.}`
proc copyExcept(n: PNode, i: int): PNode =
result = copyNode(n)
for j in 0..<n.len:
if j != i: result.add(n[j])
let lhs = b[0]
let clash = strTableGet(c.currentScope.symbols, lhs.ident)
if clash != nil:
# refs https://github.com/nim-lang/Nim/issues/8275
wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info)
proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
# Mirrored with semProcAnnotation
result = nil
# a, b {.prag.}: int = 3 not allowed
const lhsPos = 0
if a.len == 3 and a[lhsPos].kind == nkPragmaExpr:
var b = a[lhsPos]
const
namePos = 0
pragmaPos = 1
let pragmas = b[pragmaPos]
for i in 0 ..< pragmas.len:
let it = pragmas[i]
let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it
when false:
let lhs = b[0]
let clash = strTableGet(c.currentScope.symbols, lhs.ident)
if clash != nil:
# refs https://github.com/nim-lang/Nim/issues/8275
wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info)
result = newTree(nkCall)
result.add nodePragma
result.add lhs
if a[1].kind != nkEmpty:
result.add a[1]
else:
result.add newNodeIT(nkNilLit, a.info, c.graph.sysTypes[tyNil])
result.add a[2]
result.info = a.info
let ret = newNodeI(nkStmtList, a.info)
ret.add result
result = semExprNoType(c, ret)
if isPossibleMacroPragma(c, it, key):
# we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and
# let the semantic checker deal with it:
var x = newNodeI(nkCall, key.info)
x.add(key)
if it.kind in nkPragmaCallKinds and it.len > 1:
# pass pragma arguments to the macro too:
for i in 1..<it.len:
x.add(it[i])
# Drop the pragma from the list, this prevents getting caught in endless
# recursion when the nkCall is semanticized
let oldExpr = a[lhsPos]
let newPragmas = copyExcept(pragmas, i)
if newPragmas.kind != nkEmpty and newPragmas.len == 0:
a[lhsPos] = oldExpr[namePos]
else:
a[lhsPos] = copyNode(oldExpr)
a[lhsPos].add(oldExpr[namePos])
a[lhsPos].add(newPragmas)
var unarySection = newNodeI(n.kind, a.info)
unarySection.add(a)
x.add(unarySection)
# recursion assures that this works for multiple macro annotations too:
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
if r == nil:
# Restore the old list of pragmas since we couldn't process this
a[lhsPos] = oldExpr
# No matching macro was found but there's always the possibility this may
# be a .pragma. template instead
continue
doAssert r[0].kind == nkSym
let m = r[0].sym
case m.kind
of skMacro: result = semMacroExpr(c, r, r, m, {})
of skTemplate: result = semTemplateExpr(c, r, m, {})
else:
a[lhsPos] = oldExpr
continue
doAssert result != nil
# since a macro pragma can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas.
if false and result.kind in {nkVarSection, nkLetSection, nkConstSection}:
var validPragmas: TSpecialWords
case result.kind
of nkVarSection:
validPragmas = varPragmas
of nkLetSection:
validPragmas = letPragmas
of nkConstSection:
validPragmas = constPragmas
else:
# unreachable
discard
for defs in result:
for i in 0 ..< defs.len - 2:
let ex = defs[i]
if ex.kind == nkPragmaExpr and
ex[namePos].kind == nkSym and
ex[pragmaPos].kind != nkEmpty:
pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas)
return result
proc errorSymChoiceUseQualifier(c: PContext; n: PNode) =
assert n.kind in nkSymChoices
@@ -508,10 +572,6 @@ proc errorSymChoiceUseQualifier(c: PContext; n: PNode) =
localError(c.config, n.info, errGenerated, err)
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if n.len == 1:
result = semLowerLetVarCustomPragma(c, n[0], n)
if result != nil: return result
var b: PNode
result = copyNode(n)
for i in 0..<n.len:
@@ -521,6 +581,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if a.kind notin {nkIdentDefs, nkVarTuple}: illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
b = semVarMacroPragma(c, a, n)
if b != nil:
addToVarSection(c, result, b)
continue
var typ: PType = nil
if a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
@@ -663,6 +728,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n)
inc c.inStaticContext
var b: PNode
for i in 0..<n.len:
var a = n[i]
if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
@@ -670,6 +736,11 @@ proc semConst(c: PContext, n: PNode): PNode =
if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
b = semVarMacroPragma(c, a, n)
if b != nil:
addToVarSection(c, result, b)
continue
var typ: PType = nil
if a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
@@ -704,7 +775,6 @@ proc semConst(c: PContext, n: PNode): PNode =
typFlags.incl taConcept
typeAllowedCheck(c, a.info, typ, skConst, typFlags)
var b: PNode
if a.kind == nkVarTuple:
if typ.kind != tyTuple:
localError(c.config, a.info, errXExpected, "tuple")
@@ -735,7 +805,7 @@ proc semConst(c: PContext, n: PNode): PNode =
v.ast = if def[j].kind != nkExprColonExpr: def[j]
else: def[j][1]
b[j] = newSymNode(v)
result.add b
addToVarSection(c, result, n, b)
dec c.inStaticContext
include semfields
@@ -1519,77 +1589,61 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
n.add newSymNode(c.p.resultSym)
addParamOrResult(c, c.p.resultSym, owner)
proc copyExcept(n: PNode, i: int): PNode =
result = copyNode(n)
for j in 0..<n.len:
if j != i: result.add(n[j])
proc semProcAnnotation(c: PContext, prc: PNode;
validPragmas: TSpecialWords): PNode =
# Mirrored with semVarMacroPragma
var n = prc[pragmasPos]
if n == nil or n.kind == nkEmpty: return
for i in 0..<n.len:
let it = n[i]
let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it
if whichPragma(it) != wInvalid:
# Not a custom pragma
continue
else:
let ident = considerQuotedIdent(c, key)
if strTableGet(c.userPragmas, ident) != nil:
continue # User defined pragma
if isPossibleMacroPragma(c, it, key):
# we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
# let the semantic checker deal with it:
var x = newNodeI(nkCall, key.info)
x.add(key)
if it.kind in nkPragmaCallKinds and it.len > 1:
# pass pragma arguments to the macro too:
for i in 1..<it.len:
x.add(it[i])
# Drop the pragma from the list, this prevents getting caught in endless
# recursion when the nkCall is semanticized
prc[pragmasPos] = copyExcept(n, i)
if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
prc[pragmasPos] = c.graph.emptyNode
x.add(prc)
# recursion assures that this works for multiple macro annotations too:
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
if r == nil:
# Restore the old list of pragmas since we couldn't process this
prc[pragmasPos] = n
# No matching macro was found but there's always the possibility this may
# be a .pragma. template instead
continue
doAssert r[0].kind == nkSym
let m = r[0].sym
case m.kind
of skMacro: result = semMacroExpr(c, r, r, m, {})
of skTemplate: result = semTemplateExpr(c, r, m, {})
else:
var amb = false
let sym = searchInScopes(c, ident, amb)
if sym != nil and sfCustomPragma in sym.flags:
continue # User custom pragma
prc[pragmasPos] = n
continue
# we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
# let the semantic checker deal with it:
var x = newNodeI(nkCall, key.info)
x.add(key)
doAssert result != nil
if it.kind in nkPragmaCallKinds and it.len > 1:
# pass pragma arguments to the macro too:
for i in 1..<it.len:
x.add(it[i])
# since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas.
if false and result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
# Drop the pragma from the list, this prevents getting caught in endless
# recursion when the nkCall is semanticized
prc[pragmasPos] = copyExcept(n, i)
if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
prc[pragmasPos] = c.graph.emptyNode
x.add(prc)
# recursion assures that this works for multiple macro annotations too:
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
if r == nil:
# Restore the old list of pragmas since we couldn't process this
prc[pragmasPos] = n
# No matching macro was found but there's always the possibility this may
# be a .pragma. template instead
continue
doAssert r[0].kind == nkSym
let m = r[0].sym
case m.kind
of skMacro: result = semMacroExpr(c, r, r, m, {})
of skTemplate: result = semTemplateExpr(c, r, m, {})
else:
prc[pragmasPos] = n
continue
doAssert result != nil
# since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas.
if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return
return result
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
## used for resolving 'auto' in lambdas based on their callsite

View File

@@ -603,7 +603,12 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
for i in 0..<n.len:
result[i] = semTemplBodyDirty(c, n[i])
# in semstmts.nim:
proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode
proc semTemplateDef(c: PContext, n: PNode): PNode =
result = semProcAnnotation(c, n, templatePragmas)
if result != nil: return result
result = n
var s: PSym
if isTopLevel(c):

View File

@@ -7781,9 +7781,10 @@ More examples with custom pragmas:
Macro pragmas
-------------
All macros and templates can also be used as pragmas. They can be attached
to routines (procs, iterators, etc), type names, or type expressions. The
compiler will perform the following simple syntactic transformations:
Macros and templates can sometimes be called with the pragma syntax. Cases
where this is possible include when attached to routine (procs, iterators, etc)
declarations or routine type expressions. The compiler will perform the
following simple syntactic transformations:
.. code-block:: nim
template command(name: string, def: untyped) = discard
@@ -7810,20 +7811,15 @@ This is translated to:
------
.. code-block:: nim
type
MyObject {.schema: "schema.protobuf".} = object
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
compiler will apply them consequently from left to right. Each macro
will receive as input the output of the previous one.
When multiple macro pragmas are applied to the same definition, the first one
from left to right will be evaluated. This macro can then choose to keep
the remaining macro pragmas in its output, and those will be evaluated in
the same way.
There are a few more applications of macro pragmas, such as in type,
variable and constant declarations, but this behavior is considered to be
experimental and is documented in the `experimental manual
<manual_experimental.html#extended-macro-pragmas>` instead.
Foreign function interface

View File

@@ -404,6 +404,76 @@ to use this operator.
doAssert (a.b)(c) == `()`(a.b, c)
Extended macro pragmas
======================
Macro pragmas as described in `the manual <manual.html#userminusdefined-pragmas-macro-pragmas>`_
can also be applied to type, variable and constant declarations.
For types:
.. code-block:: nim
type
MyObject {.schema: "schema.protobuf".} = object
This is translated to a call to the `schema` macro with a `nnkTypeDef`
AST node capturing the left-hand side, remaining pragmas and the right-hand
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
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
the parent scope of the type section.
------
For variables and constants, it is largely the same, except a unary node with
the same kind as the section containing a single definition is passed to macros,
and macros can return any expression.
.. code-block:: nim
var
a = ...
b {.importc, foo, nodecl.} = ...
c = ...
Assuming `foo` is a macro or a template, this is roughly equivalent to:
.. code-block:: nim
var a = ...
foo:
var b {.importc, nodecl.} = ...
var c = ...
Symbols as template/macro calls
===============================
Templates and macros that take no arguments can be called as lone symbols,
i.e. without parentheses. This is useful for repeated uses of complex
expressions that cannot conveniently be represented as runtime values.
.. code-block:: nim
type Foo = object
bar: int
var foo = Foo(bar: 10)
template bar: untyped = foo.bar
assert bar == 10
bar = 15
assert bar == 15
In the future, this may require more specific information on template or macro
signatures to be used. Specializations for some applications of this may also
be introduced to guarantee consistency and circumvent bugs.
Not nil annotation
==================
@@ -613,7 +683,7 @@ has `source` as the owner. A path expression `e` is defined recursively:
If a view type is used as a return type, the location must borrow from a location
that is derived from the first parameter that is passed to the proc.
See `the manual <https://nim-lang.org/docs/manual.html#procedures-var-return-type>`_
See `the manual <manual.html#procedures-var-return-type>`_
for details about how this is done for `var T`.
A mutable view can borrow from a mutable location, an immutable view can borrow

View File

@@ -1,19 +1,32 @@
# see `semLowerLetVarCustomPragma` for compiler support that enables these
# lowerings
template byaddr*(lhs, typ, ex) =
## Allows a syntax for lvalue reference, exact analog to
## `auto& a = ex;` in C++
import macros
macro byaddr*(sect) =
## Allows a syntax for l-value references, being an exact analog to
## `auto& a = ex;` in C++.
##
## Warning: This makes use of 2 experimental features, namely nullary
## templates instantiated as symbols and variable macro pragmas.
## For this reason, its behavior is not stable. The current implementation
## allows redefinition, but this is not an intended consequence.
runnableExamples:
var s = @[10,11,12]
var s = @[10, 11, 12]
var a {.byaddr.} = s[0]
a+=100
doAssert s == @[110,11,12]
doAssert a is int
a += 100
assert s == @[110, 11, 12]
assert a is int
var b {.byaddr.}: int = s[0]
doAssert a.addr == b.addr
when typ is typeof(nil):
let tmp = addr(ex)
else:
let tmp: ptr typ = addr(ex)
template lhs: untyped = tmp[]
assert a.addr == b.addr
expectLen sect, 1
let def = sect[0]
let
lhs = def[0]
typ = def[1]
ex = def[2]
addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ)
result = quote do:
let tmp: `addrTyp` = addr(`ex`)
template `lhs`: untyped = tmp[]
result.copyLineInfo(def)

View File

@@ -13,8 +13,8 @@ block:
block: # (partial fix) bug #15920
block: # var template pragmas don't work in templates
template foo(lhs, typ, expr) =
let lhs = expr
template foo(expr) =
expr
proc fun1()=
let a {.foo.} = 1
template fun2()=
@@ -24,23 +24,22 @@ block: # (partial fix) bug #15920
template foo2() = discard # distractor (template or other symbol kind)
block:
template foo2(lhs, typ, expr) =
let lhs = expr
template foo2(expr) =
expr
proc fun1()=
let a {.foo2.} = 1
template fun2()=
let a {.foo2.} = 1
fun1() # ok
when false: # bug: Error: invalid pragma: foo2
fun2()
fun2() # bug: Error: invalid pragma: foo2
block: # proc template pragmas don't work in templates
block: # template pragmas don't work for templates, #18212
# adapted from $nim/lib/std/private/since.nim
# case without overload
template since3(version: (int, int), body: untyped) {.dirty.} =
when (NimMajor, NimMinor) >= version:
body
when false: # bug
when true: # bug
template fun3(): int {.since3: (1, 3).} = 12
block: # ditto, w
@@ -51,7 +50,7 @@ block: # (partial fix) bug #15920
template since2(version: (int, int, int), body: untyped) {.dirty.} =
when (NimMajor, NimMinor, NimPatch) >= version:
body
when false: # bug
when true: # bug
template fun3(): int {.since2: (1, 3).} = 12
when true: # D20210801T100514:here
@@ -62,3 +61,10 @@ when true: # D20210801T100514:here
discard ret
fn()
static: discard genSym()
block: # issue #10994
macro foo(x): untyped = x
template bar {.pragma.}
proc a {.bar.} = discard # works
proc b {.bar, foo.} = discard # doesn't

View File

@@ -0,0 +1,128 @@
import macros
block: # test usage
macro modify(sec) =
result = copy sec
result[0][0] = ident(repr(result[0][0]) & "Modified")
block:
let foo {.modify.} = 3
doAssert fooModified == 3
block: # in section
let
a = 1
b {.modify.} = 2
c = 3
doAssert (a, bModified, c) == (1, 2, 3)
block: # with single argument
macro appendToName(name: static string, sec) =
result = sec
result[0][0] = ident(repr(result[0][0]) & name)
block:
let foo {.appendToName: "Bar".} = 3
doAssert fooBar == 3
block:
let
a = 1
b {.appendToName("").} = 2
c = 3
doAssert (a, b, c) == (1, 2, 3)
macro appendToNameAndAdd(name: static string, incr: static int, sec) =
result = sec
result[0][0] = ident(repr(result[0][0]) & name)
result[0][2] = infix(result[0][2], "+", newLit(incr))
block: # with multiple arguments
block:
let foo {.appendToNameAndAdd("Bar", 5).} = 3
doAssert fooBar == 8
block:
let
a = 1
b {.appendToNameAndAdd("", 15).} = 2
c = 3
doAssert (a, b, c) == (1, 17, 3)
block: # in other kinds of sections
block:
const
a = 1
b {.appendToNameAndAdd("", 15).} = 2
c = 3
doAssert (a, b, c) == (1, 17, 3)
doAssert static(b) == b
block:
var
a = 1
b {.appendToNameAndAdd("", 15).} = 2
c = 3
doAssert (a, b, c) == (1, 17, 3)
b += a
c += b
doAssert (a, b, c) == (1, 18, 21)
block: # with other pragmas
macro appendToNameAndAdd(name: static string, incr, sec) =
result = sec
result[0][0][0] = ident(repr(result[0][0][0]) & name)
result[0][0][1].add(ident"deprecated")
result[0][2] = infix(result[0][2], "+", incr)
var
a = 1
foo {.exportc: "exportedFooBar", appendToNameAndAdd("Bar", {'0'..'9'}), used.} = {'a'..'z', 'A'..'Z'}
b = 2
doAssert (a, b) == (1, 2)
let importedFooBar {.importc: "exportedFooBar", nodecl.}: set[char]
doAssert importedFooBar == fooBar #[tt.Warning
^ fooBar is deprecated
]#
block: # with stropping
macro `cast`(def) =
let def = def[0]
let
lhs = def[0]
typ = def[1]
ex = def[2]
addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ)
result = quote do:
let tmp: `addrTyp` = unsafeAddr(`ex`)
template `lhs`: untyped = tmp[]
macro assign(def) =
result = getAst(`cast`(def))
block:
let s = @["foo", "bar"]
let a {.`assign`.} = s[0]
doAssert a == "foo"
doAssert a[0].addr == s[0][0].addr
block:
let
s = @["foo", "bar"]
a {.`cast`.} = s[0]
doAssert a == "foo"
doAssert a[0].addr == s[0][0].addr
block: # bug #15920
macro foo(def) =
result = def
proc fun1()=
let a {.foo.} = 1
template fun2()=
let a {.foo.} = 1
fun1() # ok
fun2() # BUG

View File

@@ -13,15 +13,18 @@ template fun() =
var b {.byaddr.}: int = s[0]
doAssert a.addr == b.addr
doAssert not compiles(block:
# redeclaration not allowed
var foo = 0
var foo {.byaddr.} = s[0])
when false:
# template specific redeclaration issue
# see https://github.com/nim-lang/Nim/issues/8275
doAssert not compiles(block:
# redeclaration not allowed
var foo = 0
var foo {.byaddr.} = s[0])
doAssert not compiles(block:
# ditto
var foo {.byaddr.} = s[0]
var foo {.byaddr.} = s[0])
doAssert not compiles(block:
# ditto
var foo {.byaddr.} = s[0]
var foo {.byaddr.} = s[0])
block:
var b {.byaddr.} = s[1] # redeclaration ok in sub scope
@@ -44,48 +47,3 @@ fun2()
static: fun2()
when false: # pending bug #13887
static: fun()
## We can define custom pragmas in user code
template byUnsafeAddr(lhs, typ, expr) =
when typ is type(nil):
let tmp = addr(expr)
else:
let tmp: ptr typ = addr(expr)
template lhs: untyped = tmp[]
block:
let s = @["foo", "bar"]
let a {.byUnsafeAddr.} = s[0]
doAssert a == "foo"
doAssert a[0].addr == s[0][0].addr
block: # nkAccQuoted
# shows using a keyword, which requires nkAccQuoted
template `cast`(lhs, typ, expr) =
when typ is type(nil):
let tmp = addr(expr)
else:
let tmp: ptr typ = addr(expr)
template lhs: untyped = tmp[]
block:
let s = @["foo", "bar"]
let a {.`byUnsafeAddr`.} = s[0]
doAssert a == "foo"
doAssert a[0].addr == s[0][0].addr
block:
let s = @["foo", "bar"]
let a {.`cast`.} = s[0]
doAssert a == "foo"
doAssert a[0].addr == s[0][0].addr
block: # bug #15920
template foo(lhs, typ, expr) =
let lhs = expr
proc fun1()=
let a {.foo.} = 1
template fun2()=
let a {.foo.} = 1
fun1() # ok
fun2() # BUG