Skip to content

Commit 6c1bd4b

Browse files
authored
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
1 parent 8540065 commit 6c1bd4b

File tree

5 files changed

+107
-20
lines changed

5 files changed

+107
-20
lines changed

compiler/semstmts.nim

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -453,31 +453,36 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
453453
return nil
454454
let nodePragma = b[1][0]
455455
# see: `singlePragma`
456-
if nodePragma.kind notin {nkIdent, nkAccQuoted}:
457-
return nil
458-
let ident = considerQuotedIdent(c, nodePragma)
459-
var userPragma = strTableGet(c.userPragmas, ident)
460-
if userPragma != nil: return nil
461-
462-
let w = nodePragma.whichPragma
463-
if n.kind == nkVarSection and w in varPragmas or
464-
n.kind == nkLetSection and w in letPragmas or
465-
n.kind == nkConstSection and w in constPragmas:
466-
return nil
467456

468457
var amb = false
469-
let sym = searchInScopes(c, ident, amb)
470-
# XXX what if amb is true?
471-
if sym == nil or sfCustomPragma in sym.flags: return nil
458+
var sym: PSym = nil
459+
case nodePragma.kind
460+
of nkIdent, nkAccQuoted:
461+
let ident = considerQuotedIdent(c, nodePragma)
462+
var userPragma = strTableGet(c.userPragmas, ident)
463+
if userPragma != nil: return nil
464+
let w = nodePragma.whichPragma
465+
if n.kind == nkVarSection and w in varPragmas or
466+
n.kind == nkLetSection and w in letPragmas or
467+
n.kind == nkConstSection and w in constPragmas:
468+
return nil
469+
sym = searchInScopes(c, ident, amb)
470+
# XXX what if amb is true?
471+
# CHECKME: should that test also apply to `nkSym` case?
472+
if sym == nil or sfCustomPragma in sym.flags: return nil
473+
of nkSym:
474+
sym = nodePragma.sym
475+
else:
476+
return nil
472477
# skip if not in scope; skip `template myAttr() {.pragma.}`
478+
473479
let lhs = b[0]
474480
let clash = strTableGet(c.currentScope.symbols, lhs.ident)
475481
if clash != nil:
476482
# refs https://github.com/nim-lang/Nim/issues/8275
477483
wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info)
478484

479485
result = newTree(nkCall)
480-
doAssert nodePragma.kind in {nkIdent, nkAccQuoted}, $nodePragma.kind
481486
result.add nodePragma
482487
result.add lhs
483488
if a[1].kind != nkEmpty:

compiler/semtempl.nim

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,18 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
208208
if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
209209
let pragmaNode = n[1]
210210
for i in 0..<pragmaNode.len:
211-
openScope(c)
212-
pragmaNode[i] = semTemplBody(c, pragmaNode[i])
213-
closeScope(c)
211+
let ni = pragmaNode[i]
212+
# see D20210801T100514
213+
var found = false
214+
if ni.kind == nkIdent:
215+
for a in templatePragmas:
216+
if ni.ident == getIdent(c.c.cache, $a):
217+
found = true
218+
break
219+
if not found:
220+
openScope(c)
221+
pragmaNode[i] = semTemplBody(c, pragmaNode[i])
222+
closeScope(c)
214223
let ident = getIdentNode(c, n)
215224
if not isTemplParam(c, ident):
216225
if n.kind != nkSym:

tests/pragmas/tpragmas_misc.nim

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,55 @@ block:
1010
static: doAssert defined(tpragmas_misc_def)
1111
{.undef(tpragmas_misc_def).}
1212
static: doAssert not defined(tpragmas_misc_def)
13+
14+
block: # (partial fix) bug #15920
15+
block: # var template pragmas don't work in templates
16+
template foo(lhs, typ, expr) =
17+
let lhs = expr
18+
proc fun1()=
19+
let a {.foo.} = 1
20+
template fun2()=
21+
let a {.foo.} = 1
22+
fun1() # ok
23+
fun2() # WAS bug
24+
25+
template foo2() = discard # distractor (template or other symbol kind)
26+
block:
27+
template foo2(lhs, typ, expr) =
28+
let lhs = expr
29+
proc fun1()=
30+
let a {.foo2.} = 1
31+
template fun2()=
32+
let a {.foo2.} = 1
33+
fun1() # ok
34+
when false: # bug: Error: invalid pragma: foo2
35+
fun2()
36+
37+
block: # proc template pragmas don't work in templates
38+
# adapted from $nim/lib/std/private/since.nim
39+
# case without overload
40+
template since3(version: (int, int), body: untyped) {.dirty.} =
41+
when (NimMajor, NimMinor) >= version:
42+
body
43+
when false: # bug
44+
template fun3(): int {.since3: (1, 3).} = 12
45+
46+
block: # ditto, w
47+
# case with overload
48+
template since2(version: (int, int), body: untyped) {.dirty.} =
49+
when (NimMajor, NimMinor) >= version:
50+
body
51+
template since2(version: (int, int, int), body: untyped) {.dirty.} =
52+
when (NimMajor, NimMinor, NimPatch) >= version:
53+
body
54+
when false: # bug
55+
template fun3(): int {.since2: (1, 3).} = 12
56+
57+
when true: # D20210801T100514:here
58+
from macros import genSym
59+
block:
60+
template fn() =
61+
var ret {.gensym.}: int # must special case template pragmas so it doesn't get confused
62+
discard ret
63+
fn()
64+
static: discard genSym()

tests/stdlib/tdecls.nim

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
discard """
2+
targets: "c cpp js"
3+
"""
4+
15
import std/decls
26

3-
block:
7+
template fun() =
48
var s = @[10,11,12]
59
var a {.byaddr.} = s[0]
610
a+=100
@@ -34,6 +38,13 @@ block:
3438
doAssert compiles(block:
3539
var b2 {.byaddr.}: int = s[2])
3640

41+
proc fun2() = fun()
42+
fun()
43+
fun2()
44+
static: fun2()
45+
when false: # pending bug #13887
46+
static: fun()
47+
3748
## We can define custom pragmas in user code
3849
template byUnsafeAddr(lhs, typ, expr) =
3950
when typ is type(nil):
@@ -68,3 +79,13 @@ block: # nkAccQuoted
6879
let a {.`cast`.} = s[0]
6980
doAssert a == "foo"
7081
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr
82+
83+
block: # bug #15920
84+
template foo(lhs, typ, expr) =
85+
let lhs = expr
86+
proc fun1()=
87+
let a {.foo.} = 1
88+
template fun2()=
89+
let a {.foo.} = 1
90+
fun1() # ok
91+
fun2() # BUG

tests/stdlib/tsince.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ since (99, 3):
2727
doAssert false
2828

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

0 commit comments

Comments
 (0)