diff options
author | Juan M Gómez <info@jmgomez.me> | 2023-09-09 09:34:20 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-09 10:34:20 +0200 |
commit | e6ca13ec857dc065ebf3297129cc1538d4698f87 (patch) | |
tree | 1558f221071299fae8082ba23d069719c0c9aead | |
parent | 5f13e15e0a6f90c462a71cd30addc677f688c4dc (diff) | |
download | Nim-e6ca13ec857dc065ebf3297129cc1538d4698f87.tar.gz |
Instantiates generics in the module that uses it (#22513)
Attempts to move the generic instantiation to the module that uses it. This should decrease re-compilation times as the source module where the generic lives doesnt need to be recompiled --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/commands.nim | 5 | ||||
-rw-r--r-- | compiler/ic/ic.nim | 2 | ||||
-rw-r--r-- | compiler/ic/packed_ast.nim | 1 | ||||
-rw-r--r-- | compiler/options.nim | 3 | ||||
-rw-r--r-- | compiler/pragmas.nim | 1 | ||||
-rw-r--r-- | compiler/sem.nim | 3 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/seminst.nim | 23 | ||||
-rw-r--r-- | compiler/semmagic.nim | 2 | ||||
-rw-r--r-- | compiler/vm.nim | 2 | ||||
-rw-r--r-- | compiler/vmgen.nim | 2 | ||||
-rw-r--r-- | tests/ic/tgenericinst.nim | 11 |
13 files changed, 52 insertions, 8 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index e82a9a293..8658251e5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -936,6 +936,7 @@ type # it won't cause problems # for skModule the string literal to output for # deprecated modules. + instantiatedFrom*: PSym # for instances, the generic symbol where it came from. when defined(nimsuggest): allUsages*: seq[TLineInfo] @@ -1936,7 +1937,7 @@ proc skipGenericOwner*(s: PSym): PSym = ## Generic instantiations are owned by their originating generic ## symbol. This proc skips such owners and goes straight to the owner ## of the generic itself (the module or the enclosing proc). - result = if s.kind in skProcKinds and sfFromGeneric in s.flags: + result = if s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule: s.owner.owner else: s.owner diff --git a/compiler/commands.nim b/compiler/commands.nim index ba996f77e..ac5366f10 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -783,7 +783,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if conf.backend == backendJs or conf.cmd == cmdNimscript: discard else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info) #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe) - of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + of "tlsemulation": + processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + if optTlsEmulation in conf.globalOptions: + conf.legacyFeatures.incl emitGenerics of "implicitstatic": processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) of "patterns", "trmacros": diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 845e3d1fa..8b0a31054 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -413,6 +413,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId p.annex = toPackedLib(s.annex, c, m) when hasFFI: p.cname = toLitId(s.cname, m) + p.instantiatedFrom = s.instantiatedFrom.safeItemId(c, m) # fill the reserved slot, nothing else: m.syms[s.itemId.item] = p @@ -876,6 +877,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; if externalName != "": result.loc.r = rope externalName result.loc.flags = s.locFlags + result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom) proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym = if s == nilItemId: diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 8eafa5e96..b87348c5a 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -71,6 +71,7 @@ type when hasFFI: cname*: LitId constraint*: NodeId + instantiatedFrom*: PackedItemId PackedType* = object kind*: TTypeKind diff --git a/compiler/options.nim b/compiler/options.nim index bda86a598..c18ca92dc 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -235,6 +235,9 @@ type laxEffects ## Lax effects system prior to Nim 2.0. verboseTypeMismatch + emitGenerics + ## generics are emitted in the module that contains them. + ## Useful for libraries that rely on local passC SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 49b3b819e..01df9bbcd 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1129,6 +1129,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wLocalPassc: assert sym != nil and sym.kind == skModule let s = expectStrLit(c, it) + appendToModule(sym, n) extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex)) recordPragma(c, it, "localpassl", s) of wPush: diff --git a/compiler/sem.nim b/compiler/sem.nim index 74045e5cf..55c6a427f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -19,7 +19,8 @@ import intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, strtabs, int128, - isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs + isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs, + extccomp when not defined(leanCompiler): diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 001ce958a..d62e25610 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2543,7 +2543,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags: # may have been resolved to `@`[empty] at some point, # reset to `@` to deal with this - n[0] = newSymNode(n[0].sym.owner, n[0].info) + n[0] = newSymNode(n[0].sym.instantiatedFrom, n[0].info) n[1] = semExpr(c, n[1], flags, arrayType) result = semDirectOp(c, n, flags, expectedType) else: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 61480494b..1dba1ebdc 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -313,6 +313,17 @@ proc fillMixinScope(c: PContext) = addSym(c.currentScope, n.sym) p = p.next +proc getLocalPassC(c: PContext, s: PSym): string = + if s.ast == nil or s.ast.len == 0: return "" + result = "" + template extractPassc(p: PNode) = + if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc": + return p[0][1].strVal + extractPassc(s.ast[0]) #it is set via appendToModule in pragmas (fast access) + for n in s.ast: + for p in n: + extractPassc(p) + proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. @@ -328,14 +339,22 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module # we set the friend module: - c.friendModules.add(getModule(fn)) + let producer = getModule(fn) + c.friendModules.add(producer) let oldMatchedConcept = c.matchedConcept c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, c.idgen) incl(result.flags, sfFromGeneric) - result.owner = fn + result.instantiatedFrom = fn + if sfGlobal in result.flags and c.config.symbolFiles != disabledSf: + let passc = getLocalPassC(c, producer) + if passc != "": #pass the local compiler options to the consumer module too + extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex)) + result.owner = c.module + else: + result.owner = fn result.ast = n pushOwner(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 2b57d1873..cb5e76e8c 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -502,6 +502,8 @@ proc semNewFinalize(c: PContext; n: PNode): PNode = getAttachedOp(c.graph, t, attachedDestructor).owner == fin: discard "already turned this one into a finalizer" else: + if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move + fin.owner = fin.instantiatedFrom let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info) let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen)) selfSymNode.typ = fin.typ[1] diff --git a/compiler/vm.nim b/compiler/vm.nim index 18b264865..579bab600 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1313,7 +1313,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if a.kind == nkSym and a.sym.kind in skProcKinds and b.kind == nkSym and b.sym.kind in skProcKinds: regs[ra].intVal = - if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1 + if sfFromGeneric in a.sym.flags and a.sym.instantiatedFrom == b.sym: 1 else: 0 else: stackTrace(c, tos, pc, "node is not a proc symbol") diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 33e1d8463..16911e640 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -2098,7 +2098,7 @@ proc toKey(s: PSym): string = result.add s.name.s if s.owner != nil: if sfFromGeneric in s.flags: - s = s.owner.owner + s = s.instantiatedFrom.owner else: s = s.owner result.add "." diff --git a/tests/ic/tgenericinst.nim b/tests/ic/tgenericinst.nim new file mode 100644 index 000000000..3346764f5 --- /dev/null +++ b/tests/ic/tgenericinst.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim cpp --incremental:on $file" +""" + +{.emit:"""/*TYPESECTION*/ +#include <iostream> + struct Foo { }; +""".} + +type Foo {.importcpp.} = object +echo $Foo() #Notice the generic is instantiate in the this module if not, it wouldnt find Foo \ No newline at end of file |