mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-06 23:39:08 +00:00
progress
This commit is contained in:
@@ -248,17 +248,22 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
localError(c.config, n.info, "cannot use symbol of kind '$1' as a '$2'" %
|
||||
[result.kind.toHumanStr, kind.toHumanStr])
|
||||
# bug #25693: a local declared inside a template/macro operand (recorded in
|
||||
# `shadowDiscardedDefs`) may be realized in a real scope at most once. A
|
||||
# second realization - e.g. a `typed` argument captured by a `{.dirty.}`
|
||||
# template and emitted by two `t()` calls - produces two definitions of the
|
||||
# same symbol, which is a redefinition just like `var x = 0; var x = 1`.
|
||||
# `shadowDiscardedDefs`) can be captured by a `{.dirty.}` template and
|
||||
# re-emitted as a definition more than once. The first emission keeps the
|
||||
# original symbol (so a leaked dirty-template name still resolves); every
|
||||
# later emission gets a fresh copy, so distinct emissions don't share one
|
||||
# symbol - which the destructor/liveness analysis would otherwise miscompile.
|
||||
# Unlike a plain redefinition check this is control-flow agnostic, so the
|
||||
# common "emit a `typed` body in several mutually-exclusive branches" pattern
|
||||
# keeps working.
|
||||
if kind in {skVar, skLet, skForVar} and sfGenSym notin result.flags and
|
||||
result.id in c.shadowDiscardedDefs:
|
||||
if containsOrIncl(c.realizedDefs, result.id) and
|
||||
injectedSymbolRedefinition notin c.config.legacyFeatures:
|
||||
localError(c.config, n.info,
|
||||
"redefinition of '$1'; it is injected by a template more than once" %
|
||||
result.name.s)
|
||||
if containsOrIncl(c.realizedDefs, result.id):
|
||||
let fresh = copySym(result, c.idgen)
|
||||
fresh.ast = result.ast
|
||||
put(c.p, result, fresh)
|
||||
c.hasSymRedefs = true
|
||||
result = fresh
|
||||
when false:
|
||||
if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
|
||||
# declarative context, so produce a fresh gensym:
|
||||
|
||||
@@ -191,11 +191,16 @@ type
|
||||
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
|
||||
shadowDiscardedDefs*: IntSet
|
||||
# ids of local symbols that were declared inside a template/macro operand's
|
||||
# shadow scope and then discarded; such a symbol may be realized in a real
|
||||
# scope at most once. See bug #25693 and `rememberShadowDefs`.
|
||||
# shadow scope and then discarded; re-emitting such a symbol as a
|
||||
# definition gives a fresh copy so distinct emissions don't share a symbol.
|
||||
# See bug #25693 and `rememberShadowDefs`.
|
||||
realizedDefs*: IntSet
|
||||
# ids from `shadowDiscardedDefs` that have already been realized as a
|
||||
# definition; a second realization is a redefinition.
|
||||
# ids from `shadowDiscardedDefs` already realized once; the first emission
|
||||
# keeps the original symbol (so leaked dirty-template names still resolve),
|
||||
# later emissions get a fresh copy.
|
||||
hasSymRedefs*: bool
|
||||
# set once a redefinition mapping has been installed; makes `getGenSym`
|
||||
# consult the proc-con mapping for non-gensym symbols too.
|
||||
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
@@ -288,7 +293,10 @@ proc get*(p: PProcCon; key: PSym): PSym =
|
||||
result = p.mapping.getOrDefault(key.itemId)
|
||||
|
||||
proc getGenSym*(c: PContext; s: PSym): PSym =
|
||||
if sfGenSym notin s.flags: return s
|
||||
# `c.hasSymRedefs` additionally routes ordinary (non-gensym) symbols through
|
||||
# the mapping so a re-emitted definition can redirect them to its fresh copy,
|
||||
# see bug #25693 and `newSymG`.
|
||||
if sfGenSym notin s.flags and not c.hasSymRedefs: return s
|
||||
var it = c.p
|
||||
while it != nil:
|
||||
result = get(it, s)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
discard """
|
||||
output: '''ok
|
||||
ok
|
||||
ok'''
|
||||
"""
|
||||
|
||||
@@ -33,6 +34,27 @@ block: # scalar `untyped` parameter
|
||||
block: t()
|
||||
echo "ok"
|
||||
|
||||
block: # `typed` parameter captured and re-emitted: each emission gets its own
|
||||
# symbols, otherwise the destructor/liveness pass miscompiles the shared
|
||||
# local `a` and the program crashes at runtime
|
||||
template g(b: typed) {.dirty.} =
|
||||
template t: untyped = b
|
||||
|
||||
let q = "a"
|
||||
g:
|
||||
var a: seq[int]
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
block: t()
|
||||
block: t()
|
||||
echo "ok"
|
||||
|
||||
block: # `varargs[untyped]` parameter takes the same pristine-AST path
|
||||
template g(b: varargs[untyped]) {.dirty.} =
|
||||
template t: untyped = b
|
||||
|
||||
Reference in New Issue
Block a user