make routine implicitly gensym when other gensym symbol exists again (#23842)

fixes #23813, partially reverts #23392

Before #23392, if a `gensym` symbol was defined before a proc with the
same name in a template even with an `inject` annotation, the proc would
be `gensym`. After #23392 the proc was instead changed to be `inject` as
long as no `gensym` annotation was given. Now, to keep compatibility
with the old behavior, the behavior is changed back to infer the proc as
`gensym` when no `inject` annotation is given, however an explicit
`inject` annotation will still inject the proc. This is also documented
in the manual as the old behavior was undocumented and the new behavior
is slightly different.

(cherry picked from commit cd946084ab)
This commit is contained in:
metagn
2024-07-16 00:47:06 -06:00
committed by narimiran
parent 76e6130f64
commit 1ce954a0cf
4 changed files with 26 additions and 9 deletions

View File

@@ -255,25 +255,27 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
if not isField:
styleCheckUse(c, n.info, s)
proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
result = n
if n.kind == nkIdent:
let s = qualifiedLookUp(c.c, n, {})
if s != nil:
if s.owner == c.owner and s.kind == skParam:
if s.owner == c.owner and (s.kind == skParam or
(sfGenSym in s.flags and not explicitInject)):
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
onUse(n.info, s)
else:
for i in 0..<n.safeLen:
result[i] = semRoutineInTemplName(c, n[i])
result[i] = semRoutineInTemplName(c, n[i], explicitInject)
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
result = n
checkSonsLen(n, bodyPos + 1, c.c.config)
if n.kind notin nkLambdaKinds:
# routines default to 'inject':
if symBinding(n[pragmasPos]) == spGenSym:
let binding = symBinding(n[pragmasPos])
if binding == spGenSym:
let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
if not hasParam:
var s = newGenSym(k, ident, c)
@@ -285,7 +287,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
else:
n[namePos] = ident
else:
n[namePos] = semRoutineInTemplName(c, n[namePos])
n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
# open scope for parameters
openScope(c)
for i in patternPos..paramsPos-1:

View File

@@ -6154,9 +6154,12 @@ scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas:
`gensym`'ed symbols are not exposed but `inject`'ed symbols are.
The default for symbols of entity `type`, `var`, `let` and `const`
is `gensym` and for `proc`, `iterator`, `converter`, `template`,
`macro` is `inject`. However, if the name of the entity is passed as a
template parameter, it is an `inject`'ed symbol:
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
is defined in the same syntax-level scope, it will be `gensym` by default.
This can be overriden by marking the routine as `inject`.
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
```nim
template withFile(f, fn, mode: untyped, actions: untyped): untyped =

View File

@@ -7,7 +7,7 @@ block:
when false:
let x = 123
else:
template x: untyped = 456
template x: untyped {.inject.} = 456
echo x #[tt.Error
^ undeclared identifier: 'x`gensym0'; if declared in a template, this identifier may be inconsistently marked inject or gensym]#
foo()

View File

@@ -6,3 +6,15 @@ block: # #20002
discard 3.bar # evaluates to 10 but only check if it compiles for now
block:
foo()
block: # issue #23813
template r(body: untyped) =
proc x() {.gensym.} =
body
template g() =
r:
let y = 0
r:
proc y() = discard
y()
g()