Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left for future work, I want to get this fix in first (keeping this comment as unresolved)

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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hack IMO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see test case labeled D20210801T100514

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