summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJuan M Gómez <info@jmgomez.me>2023-09-09 09:34:20 +0100
committerGitHub <noreply@github.com>2023-09-09 10:34:20 +0200
commite6ca13ec857dc065ebf3297129cc1538d4698f87 (patch)
tree1558f221071299fae8082ba23d069719c0c9aead
parent5f13e15e0a6f90c462a71cd30addc677f688c4dc (diff)
downloadNim-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.nim3
-rw-r--r--compiler/commands.nim5
-rw-r--r--compiler/ic/ic.nim2
-rw-r--r--compiler/ic/packed_ast.nim1
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/pragmas.nim1
-rw-r--r--compiler/sem.nim3
-rw-r--r--compiler/semexprs.nim2
-rw-r--r--compiler/seminst.nim23
-rw-r--r--compiler/semmagic.nim2
-rw-r--r--compiler/vm.nim2
-rw-r--r--compiler/vmgen.nim2
-rw-r--r--tests/ic/tgenericinst.nim11
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