Skip to content

Commit

Permalink
fix: var a{.foo.} = expr inside templates (refs nim-lang#15920) (ex…
Browse files Browse the repository at this point in the history
…cept when `foo` is overloaded) (nim-lang#13869)

* fix: `var a{.foo.} = expr` inside templates
* add test
* improve tdecls test
* improve tests
* add failing test
* PRTEMP
* fixup
  • Loading branch information
timotheecour authored and PMunch committed Mar 28, 2022
1 parent 3bf0409 commit 7e69a16
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 20 deletions.
35 changes: 20 additions & 15 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -453,31 +453,36 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
return nil
let nodePragma = b[1][0]
# see: `singlePragma`
if nodePragma.kind notin {nkIdent, nkAccQuoted}:
return nil
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

var amb = false
let sym = searchInScopes(c, ident, amb)
# XXX what if amb is true?
if sym == nil or sfCustomPragma in sym.flags: return nil
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.}`

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)
doAssert nodePragma.kind in {nkIdent, nkAccQuoted}, $nodePragma.kind
result.add nodePragma
result.add lhs
if a[1].kind != nkEmpty:
Expand Down
15 changes: 12 additions & 3 deletions compiler/semtempl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,18 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
let pragmaNode = n[1]
for i in 0..<pragmaNode.len:
openScope(c)
pragmaNode[i] = semTemplBody(c, pragmaNode[i])
closeScope(c)
let ni = pragmaNode[i]
# see D20210801T100514
var found = false
if ni.kind == nkIdent:
for a in templatePragmas:
if ni.ident == getIdent(c.c.cache, $a):
found = true
break
if not found:
openScope(c)
pragmaNode[i] = semTemplBody(c, pragmaNode[i])
closeScope(c)
let ident = getIdentNode(c, n)
if not isTemplParam(c, ident):
if n.kind != nkSym:
Expand Down
52 changes: 52 additions & 0 deletions tests/pragmas/tpragmas_misc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,55 @@ block:
static: doAssert defined(tpragmas_misc_def)
{.undef(tpragmas_misc_def).}
static: doAssert not defined(tpragmas_misc_def)

block: # (partial fix) bug #15920
block: # var template pragmas don't work in templates
template foo(lhs, typ, expr) =
let lhs = expr
proc fun1()=
let a {.foo.} = 1
template fun2()=
let a {.foo.} = 1
fun1() # ok
fun2() # WAS bug

template foo2() = discard # distractor (template or other symbol kind)
block:
template foo2(lhs, typ, expr) =
let lhs = expr
proc fun1()=
let a {.foo2.} = 1
template fun2()=
let a {.foo2.} = 1
fun1() # ok
when false: # bug: Error: invalid pragma: foo2
fun2()

block: # proc template pragmas don't work in templates
# 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
template fun3(): int {.since3: (1, 3).} = 12

block: # ditto, w
# case with overload
template since2(version: (int, int), body: untyped) {.dirty.} =
when (NimMajor, NimMinor) >= version:
body
template since2(version: (int, int, int), body: untyped) {.dirty.} =
when (NimMajor, NimMinor, NimPatch) >= version:
body
when false: # bug
template fun3(): int {.since2: (1, 3).} = 12

when true: # D20210801T100514:here
from macros import genSym
block:
template fn() =
var ret {.gensym.}: int # must special case template pragmas so it doesn't get confused
discard ret
fn()
static: discard genSym()
23 changes: 22 additions & 1 deletion tests/stdlib/tdecls.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
discard """
targets: "c cpp js"
"""

import std/decls

block:
template fun() =
var s = @[10,11,12]
var a {.byaddr.} = s[0]
a+=100
Expand Down Expand Up @@ -34,6 +38,13 @@ block:
doAssert compiles(block:
var b2 {.byaddr.}: int = s[2])

proc fun2() = fun()
fun()
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):
Expand Down Expand Up @@ -68,3 +79,13 @@ block: # nkAccQuoted
let a {.`cast`.} = s[0]
doAssert a == "foo"
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr

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
2 changes: 1 addition & 1 deletion tests/stdlib/tsince.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ since (99, 3):
doAssert false

when false:
# pending https://github.com/timotheecour/Nim/issues/129
# pending bug #15920
# Error: cannot attach a custom pragma to 'fun3'
template fun3(): int {.since: (1, 3).} = 12

0 comments on commit 7e69a16

Please sign in to comment.