fix: var a{.foo.} = expr inside templates (refs #15920) (except when foo is overloaded) (#13869)

* fix: `var a{.foo.} = expr` inside templates
* add test
* improve tdecls test
* improve tests
* add failing test
* PRTEMP
* fixup
This commit is contained in:
Timothee Cour
2021-08-11 03:17:17 -07:00
committed by GitHub
parent 8540065754
commit 6c1bd4bb1c
5 changed files with 107 additions and 20 deletions

View File

@@ -453,23 +453,29 @@ 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:
@@ -477,7 +483,6 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
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:

View File

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

View File

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

View File

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

View File

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