mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-03 22:18:40 +00:00
do not forget about early expansions of templates and macros
This commit is contained in:
@@ -894,6 +894,7 @@ var reexpModTag = registerTag("reexpmod")
|
||||
var offerTag = registerTag("offer")
|
||||
var typeOfferTag = registerTag("toffer")
|
||||
var modulesrcTag = registerTag("modulesrc")
|
||||
var expansionTag = registerTag("expansion")
|
||||
# `(unusedid <int>)` — the module's first FREE itemId after the frontend
|
||||
# (`.s.bif`) or the lower stage (`.t.bif`). The backend seeds its per-module
|
||||
# sym/type counters here so freshly-minted backend ids (closure envs, RTTI
|
||||
@@ -934,6 +935,7 @@ proc registerNifAstTags*() =
|
||||
offerTag = registerTag("offer")
|
||||
typeOfferTag = registerTag("toffer")
|
||||
modulesrcTag = registerTag("modulesrc")
|
||||
expansionTag = registerTag("expansion")
|
||||
|
||||
proc writeNode(w: var Writer; dest: var IcBuilder; n: PNode; forAst = false) =
|
||||
if n == nil:
|
||||
@@ -1559,7 +1561,8 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode;
|
||||
genericParamsCount: int]] = @[];
|
||||
typeOffers: seq[tuple[generic: PSym; inst: PType]] = @[];
|
||||
resolvedImportDeps: seq[FileIndex] = @[];
|
||||
firstUnusedId: int32 = 0) =
|
||||
firstUnusedId: int32 = 0;
|
||||
expansions: seq[(PSym, TLineInfo)] = @[]) =
|
||||
var w = Writer(infos: newLineInfoWriter(config), currentModule: thisModule)
|
||||
w.deps = newIcBuilder(64)
|
||||
var content = newIcBuilder(300)
|
||||
@@ -1637,6 +1640,17 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode;
|
||||
w.deps.addStrLit toFullPath(config, FileIndex(thisModule))
|
||||
w.deps.addParRi
|
||||
|
||||
# Template/macro expansions leave no trace in the sem'checked AST, so record
|
||||
# each as `(expansion <symUse @call-site>)`: a `Symbol` use of the expanded
|
||||
# routine carrying the ORIGINAL call-site line info. The loader skips the tag
|
||||
# (processTopLevel), but `idetools` scans every `Symbol` token in the buffer,
|
||||
# so this restores "find usages / goto-def" for templates and macros.
|
||||
for (sym, info) in expansions:
|
||||
if sym == nil: continue
|
||||
w.deps.addParLe expansionTag, NoLineInfo
|
||||
w.deps.addSymUse pool.syms.getOrIncl(w.toNifSymName(sym)), trLineInfo(w, info)
|
||||
w.deps.addParRi
|
||||
|
||||
# Generic TYPE-instance OFFERS: the `tyGenericInst` types this module created
|
||||
# (e.g. `HashArray[8192, Gwei]`). Non-IC keeps ONE such instance in the global
|
||||
# `typeInstCache`, so a structural bound computed at the first instantiation
|
||||
@@ -3262,6 +3276,10 @@ proc processTopLevel(c: var DecodeContext; cur: var Cursor; flags: set[LoadFlag]
|
||||
# self-identification record for the standalone include-graph scanner;
|
||||
# not needed by the loader, just skip past it.
|
||||
skip cur
|
||||
elif tagIs(cur, "expansion"):
|
||||
# template/macro expansion usage record for tooling (`idetools` scans it
|
||||
# as a `Symbol` use); the loader itself needs nothing from it.
|
||||
skip cur
|
||||
elif tagIs(cur, "implementation"):
|
||||
cont = false
|
||||
elif LoadFullAst in flags or tagIs(cur, toNifTag(nkLetSection)) or
|
||||
|
||||
@@ -177,6 +177,11 @@ type
|
||||
|
||||
procGlobals*: seq[PNode]
|
||||
nifReplayActions*: Table[int32, seq[PNode]] # module position -> replay actions for NIF
|
||||
nifExpansions*: Table[int32, seq[(PSym, TLineInfo)]]
|
||||
# module position -> (template/macro sym, call-site info) for every expansion
|
||||
# in that module. Templates/macros leave no trace in the sem'checked AST, so
|
||||
# this side-channel (written into the `.bif`, see ast2nif) is what lets
|
||||
# `nim track --usages`/`--def` find them. Populated by `rememberExpansion`.
|
||||
cachedMods: IntSet
|
||||
hookClosure: IntSet # modules whose serialized hooks were already registered
|
||||
|
||||
|
||||
@@ -317,9 +317,12 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
# the backend seeds its id minting ABOVE this so closure envs / RTTI hooks
|
||||
# never share a `toId` with a frontend sym/type. See ast2nif `(unusedid)`.
|
||||
let firstUnusedId = max(idgen.symId, idgen.typeId)
|
||||
var expansions: seq[(PSym, TLineInfo)] = @[]
|
||||
discard graph.nifExpansions.take(module.position.int32, expansions)
|
||||
writeNifModule(graph.config, module.position.int32, topLevelStmts, graph.opsLog,
|
||||
replayActions, implDeps, reexportedModuleSyms(graph, module),
|
||||
genericOffers, typeOffers, resolvedImportDeps, firstUnusedId)
|
||||
genericOffers, typeOffers, resolvedImportDeps, firstUnusedId,
|
||||
expansions)
|
||||
# The module's REAL direct imports (incl. macro-generated) for `nim ic`'s
|
||||
# graph re-derivation; see ast2nif.writeSemDeps / semdata.addImportFileDep.
|
||||
var semDepPaths: seq[string] = @[]
|
||||
|
||||
@@ -576,10 +576,12 @@ const
|
||||
|
||||
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
|
||||
rememberExpansion(c, nOrig.info, sym)
|
||||
let info = getCallLineInfo(n)
|
||||
# the callee identifier's position is the usage site tooling expects (matches
|
||||
# `markUsed` below), not the whole-call `nOrig.info`.
|
||||
rememberExpansion(c, info, sym)
|
||||
pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
|
||||
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, sym)
|
||||
onUse(info, sym)
|
||||
if sym == c.p.owner:
|
||||
|
||||
@@ -668,7 +668,13 @@ proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
|
||||
## ("find all usages of this template" would not work). We need special
|
||||
## logic to remember macro/template expansions. This is done here and
|
||||
## delegated to the "NIF" file mechanism.
|
||||
discard "XXX To implement"
|
||||
##
|
||||
## We only bother when a NIF file is actually going to be written (IC / `nim m`,
|
||||
## `--compress`, or a running suggestion engine); a plain `nim c` throws the
|
||||
## record away, so recording it would be pure overhead.
|
||||
if info.fileIndex == InvalidFileIdx: return
|
||||
if c.config.cmd == cmdM or optCompress in c.config.globalOptions or c.config.ideActive:
|
||||
c.graph.nifExpansions.mgetOrPut(c.module.position.int32, @[]).add (expandedSym, info)
|
||||
|
||||
const
|
||||
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
|
||||
|
||||
@@ -26,13 +26,15 @@ const
|
||||
|
||||
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
|
||||
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
|
||||
rememberExpansion(c, n.info, s)
|
||||
let info = getCallLineInfo(n)
|
||||
# `info` (the callee identifier's position, not the whole call node) is what
|
||||
# tooling wants to see as the usage site — matches `markUsed` below.
|
||||
rememberExpansion(c, info, s)
|
||||
# IC: this expands `s`'s body into the current module's sem, so the module
|
||||
# depends on that body — record a NeedsImpl (strong) edge to `s`'s module.
|
||||
# The iface cookie hashes only signatures now, so a template body edit moves
|
||||
# only the impl cookie, and just the modules that expanded it re-sem.
|
||||
recordIcImplDep(c.graph, s)
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
# Note: This is n.info on purpose. It prevents template from creating an info
|
||||
|
||||
Reference in New Issue
Block a user