summary refs log tree commit diff stats
path: root/compiler/seminst.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/seminst.nim')
-rw-r--r--compiler/seminst.nim555
1 files changed, 417 insertions, 138 deletions
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 9dc99d173..1bc6d31a2 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -1,6 +1,6 @@
 #
 #
-#           The Nimrod Compiler
+#           The Nim Compiler
 #        (c) Copyright 2012 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
@@ -10,184 +10,463 @@
 # This module implements the instantiation of generic procs.
 # included from sem.nim
 
-proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
-                                 entry: var TInstantiation) = 
-  if n.kind != nkGenericParams: 
-    InternalError(n.info, "instantiateGenericParamList; no generic params")
-  newSeq(entry.concreteTypes, n.len)
-  for i in countup(0, n.len - 1):
-    var a = n.sons[i]
-    if a.kind != nkSym: 
-      InternalError(a.info, "instantiateGenericParamList; no symbol")
+proc addObjFieldsToLocalScope(c: PContext; n: PNode) =
+  template rec(n) = addObjFieldsToLocalScope(c, n)
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      rec n[i]
+  of nkRecCase:
+    if n.len > 0: rec n[0]
+    for i in 1..<n.len:
+      if n[i].kind in {nkOfBranch, nkElse}: rec lastSon(n[i])
+  of nkSym:
+    let f = n.sym
+    if f.kind == skField and fieldVisible(c, f):
+      c.currentScope.symbols.strTableIncl(f, onConflictKeepOld=true)
+      incl(f.flags, sfUsed)
+      # it is not an error to shadow fields via parameters
+  else: discard
+
+proc pushProcCon*(c: PContext; owner: PSym) =
+  c.p = PProcCon(owner: owner, next: c.p)
+
+const
+  errCannotInstantiateX = "cannot instantiate: '$1'"
+
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
+  internalAssert c.config, n.kind == nkGenericParams
+  for a in n.items:
+    internalAssert c.config, a.kind == nkSym
     var q = a.sym
-    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyTypeClass, tyExpr}: continue
-    var s = newSym(skType, q.name, getCurrOwner(), q.info)
-    s.flags = s.flags + {sfUsed, sfFromGeneric}
-    var t = PType(IdTableGet(pt, q.typ))
-    if t == nil:
-      if tfRetType in q.typ.flags:
-        # keep the generic type and allow the return type to be bound 
-        # later by semAsgn in return type inference scenario
-        t = q.typ
-      else:
-        LocalError(a.info, errCannotInstantiateX, s.name.s)
+    if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses:
+      let symKind = if q.typ.kind == tyStatic: skConst else: skType
+      var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
+      s.flags.incl {sfUsed, sfFromGeneric}
+      var t = idTableGet(pt, q.typ)
+      if t == nil:
+        if tfRetType in q.typ.flags:
+          # keep the generic type and allow the return type to be bound
+          # later by semAsgn in return type inference scenario
+          t = q.typ
+        else:
+          if q.typ.kind != tyCompositeTypeClass:
+            localError(c.config, a.info, errCannotInstantiateX % s.name.s)
+          t = errorType(c)
+      elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}:
+        localError(c.config, a.info, errCannotInstantiateX % q.name.s)
         t = errorType(c)
-    elif t.kind == tyGenericParam: 
-      InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
-    elif t.kind == tyGenericInvokation:
-      #t = instGenericContainer(c, a, t)
-      t = generateTypeInstance(c, pt, a, t)
-      #t = ReplaceTypeVarsT(cl, t)
-    t.flags.incl tfInstantiated
-    s.typ = t
-    addDecl(c, s)
-    entry.concreteTypes[i] = t
+      elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
+            (q.typ.kind == tyGenericParam and
+              q.typ.genericParamHasConstraints and
+              q.typ.genericConstraint.kind == tyStatic)) and
+          c.inGenericContext == 0 and c.matchedConcept == nil:
+        # generic/concept type bodies will try to instantiate static values but
+        # won't actually use them
+        localError(c.config, a.info, errCannotInstantiateX % q.name.s)
+        t = errorType(c)
+      elif t.kind == tyGenericInvocation:
+        #t = instGenericContainer(c, a, t)
+        t = generateTypeInstance(c, pt, a, t)
+        #t = ReplaceTypeVarsT(cl, t)
+      s.typ = t
+      if t.kind == tyStatic: s.ast = t.n
+      yield s
 
 proc sameInstantiation(a, b: TInstantiation): bool =
   if a.concreteTypes.len == b.concreteTypes.len:
     for i in 0..a.concreteTypes.high:
       if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
-                          flags = {TypeDescExactMatch}): return
+                          flags = {ExactTypeDescValues,
+                                   ExactGcSafety,
+                                   PickyCAliases}): return
     result = true
+  else:
+    result = false
 
-proc GenericCacheGet(genericSym: Psym, entry: TInstantiation): PSym =
-  if genericSym.procInstCache != nil:
-    for inst in genericSym.procInstCache:
-      if sameInstantiation(entry, inst[]):
-        return inst.sym
+proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
+                     id: CompilesId): PSym =
+  result = nil
+  for inst in procInstCacheItems(g, genericSym):
+    if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
+      return inst.sym
 
-proc removeDefaultParamValues(n: PNode) = 
-  # we remove default params, because they cannot be instantiated properly
-  # and they are not needed anyway for instantiation (each param is already
-  # provided).
-  when false:
-    for i in countup(1, sonsLen(n)-1): 
-      var a = n.sons[i]
-      if a.kind != nkIdentDefs: IllFormedAst(a)
-      var L = a.len
-      if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty:
-        # ``param: typ = defaultVal``. 
-        # We don't need defaultVal for semantic checking and it's wrong for
-        # ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
-        # not possible... XXX We don't solve this issue here.
-        a.sons[L-1] = ast.emptyNode
-
-proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
+when false:
+  proc `$`(x: PSym): string =
+    result = x.name.s & " " & " id " & $x.id
+
+proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMapping) =
   # we need to create a fresh set of gensym'ed symbols:
-  if n.kind == nkSym and sfGenSym in n.sym.flags:
-    var x = PSym(IdTableGet(symMap, n.sym))
-    if x == nil:
-      x = copySym(n.sym, false)
+  #if n.kind == nkSym and sfGenSym in n.sym.flags:
+  #  if n.sym.owner != orig:
+  #    echo "symbol ", n.sym.name.s, " orig ", orig, " owner ", n.sym.owner
+  if n.kind == nkSym and sfGenSym in n.sym.flags: # and
+    #  (n.sym.owner == orig or n.sym.owner.kind in {skPackage}):
+    let s = n.sym
+    var x = idTableGet(symMap, s)
+    if x != nil:
+      n.sym = x
+    elif s.owner == nil or s.owner.kind == skPackage:
+      #echo "copied this ", s.name.s
+      x = copySym(s, c.idgen)
       x.owner = owner
-      IdTablePut(symMap, n.sym, x)
-    n.sym = x
+      idTablePut(symMap, s, x)
+      n.sym = x
   else:
-    for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)
+    for i in 0..<n.safeLen: freshGenSyms(c, n[i], owner, orig, symMap)
 
-proc instantiateBody(c: PContext, n: PNode, result: PSym) =
-  if n.sons[bodyPos].kind != nkEmpty:
+proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
+
+proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
+  if n[bodyPos].kind != nkEmpty:
+    let procParams = result.typ.n
+    for i in 1..<procParams.len:
+      addDecl(c, procParams[i].sym)
+    maybeAddResult(c, result, result.ast)
+
+    inc c.inGenericInst
     # add it here, so that recursive generic procs are possible:
-    addDecl(c, result)
-    pushProcCon(c, result)
-    maybeAddResult(c, result, n)
-    var b = n.sons[bodyPos]
-    var symMap: TIdTable
-    InitIdTable symMap
-    freshGenSyms(b, result, symMap)
-    b = semProcBody(c, b)
-    b = hloBody(c, b)
-    n.sons[bodyPos] = transformBody(c.module, b, result)
-    #echo "code instantiated ", result.name.s
+    var b = n[bodyPos]
+    var symMap = initSymMapping()
+    if params != nil:
+      for i in 1..<params.len:
+        let param = params[i].sym
+        if sfGenSym in param.flags:
+          idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
+    freshGenSyms(c, b, result, orig, symMap)
+
+    if sfBorrow notin orig.flags:
+      # We do not want to generate a body for generic borrowed procs.
+      # As body is a sym to the borrowed proc.
+      let resultType = # todo probably refactor it into a function
+        if result.kind == skMacro:
+          sysTypeFromName(c.graph, n.info, "NimNode")
+        elif not isInlineIterator(result.typ):
+          result.typ.returnType
+        else:
+          nil
+      b = semProcBody(c, b, resultType)
+    result.ast[bodyPos] = hloBody(c, b)
     excl(result.flags, sfForward)
-    popProcCon(c)
+    trackProc(c, result, result.ast[bodyPos])
+    dec c.inGenericInst
 
 proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
-  for i in countup(0, c.generics.len - 1):
+  for i in 0..<c.generics.len:
     if c.generics[i].genericSym.id == s.id:
       var oldPrc = c.generics[i].inst.sym
-      pushInfoContext(oldPrc.info)
+      pushProcCon(c, oldPrc)
+      pushOwner(c, oldPrc)
+      pushInfoContext(c.config, oldPrc.info)
       openScope(c)
       var n = oldPrc.ast
-      n.sons[bodyPos] = copyTree(s.getBody)
-      if n.sons[paramsPos].kind != nkEmpty: 
-        addParams(c, oldPrc.typ.n, oldPrc.kind)
-      instantiateBody(c, n, oldPrc)
+      n[bodyPos] = copyTree(getBody(c.graph, s))
+      instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
       closeScope(c)
-      popInfoContext()
+      popInfoContext(c.config)
+      popOwner(c)
+      popProcCon(c)
+
+proc sideEffectsCheck(c: PContext, s: PSym) =
+  when false:
+    if {sfNoSideEffect, sfSideEffect} * s.flags ==
+        {sfNoSideEffect, sfSideEffect}:
+      localError(s.info, errXhasSideEffects, s.name.s)
+
+proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
+                          allowMetaTypes = false): PType =
+  internalAssert c.config, header.kind == tyGenericInvocation
+
+  var cl: TReplTypeVars = TReplTypeVars(symMap: initSymMapping(),
+        localCache: initTypeMapping(), typeMap: LayeredIdTable(),
+        info: info, c: c, allowMetaTypes: allowMetaTypes
+      )
+
+  cl.typeMap.topLayer = initTypeMapping()
+
+  # We must add all generic params in scope, because the generic body
+  # may include tyFromExpr nodes depending on these generic params.
+  # XXX: This looks quite similar to the code in matchUserTypeClass,
+  # perhaps the code can be extracted in a shared function.
+  openScope(c)
+  let genericTyp = header.base
+  for i, genParam in genericBodyParams(genericTyp):
+    var param: PSym
+
+    template paramSym(kind): untyped =
+      newSym(kind, genParam.sym.name, c.idgen, genericTyp.sym, genParam.sym.info)
+
+    if genParam.kind == tyStatic:
+      param = paramSym skConst
+      param.ast = header[i+1].n
+      param.typ = header[i+1]
+    else:
+      param = paramSym skType
+      param.typ = makeTypeDesc(c, header[i+1])
+
+    # this scope was not created by the user,
+    # unused params shouldn't be reported.
+    param.flags.incl sfUsed
+    addDecl(c, param)
+
+  result = replaceTypeVarsT(cl, header)
+  closeScope(c)
+
+proc referencesAnotherParam(n: PNode, p: PSym): bool =
+  if n.kind == nkSym:
+    return n.sym.kind == skParam and n.sym.owner == p
+  else:
+    for i in 0..<n.safeLen:
+      if referencesAnotherParam(n[i], p): return true
+    return false
+
+proc instantiateProcType(c: PContext, pt: TypeMapping,
+                         prc: PSym, info: TLineInfo) =
+  # XXX: Instantiates a generic proc signature, while at the same
+  # time adding the instantiated proc params into the current scope.
+  # This is necessary, because the instantiation process may refer to
+  # these params in situations like this:
+  # proc foo[Container](a: Container, b: a.type.Item): typeof(b.x)
+  #
+  # Alas, doing this here is probably not enough, because another
+  # proc signature could appear in the params:
+  # proc foo[T](a: proc (x: T, b: typeof(x.y))
+  #
+  # The solution would be to move this logic into semtypinst, but
+  # at this point semtypinst have to become part of sem, because it
+  # will need to use openScope, addDecl, etc.
+  #addDecl(c, prc)
+  pushInfoContext(c.config, info)
+  var typeMap = initLayeredTypeMap(pt)
+  var cl = initTypeVars(c, typeMap, info, nil)
+  var result = instCopyType(cl, prc.typ)
+  let originalParams = result.n
+  result.n = originalParams.shallowCopy
+  for i, resulti in paramTypes(result):
+    # twrong_field_caching requires these 'resetIdTable' calls:
+    if i > FirstParamAt:
+      resetIdTable(cl.symMap)
+      resetIdTable(cl.localCache)
+
+    # take a note of the original type. If't a free type or static parameter
+    # we'll need to keep it unbound for the `fitNode` operation below...
+    var typeToFit = resulti
+
+    let needsStaticSkipping = resulti.kind == tyFromExpr
+    let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
+    if resulti.kind == tyFromExpr:
+      resulti.flags.incl tfNonConstExpr
+    result[i] = replaceTypeVarsT(cl, resulti)
+    if needsStaticSkipping:
+      result[i] = result[i].skipTypes({tyStatic})
+    if needsTypeDescSkipping:
+      result[i] = result[i].skipTypes({tyTypeDesc})
+      typeToFit = result[i]
+
+    # ...otherwise, we use the instantiated type in `fitNode`
+    if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
+       (typeToFit.kind != tyStatic):
+      typeToFit = result[i]
+
+    internalAssert c.config, originalParams[i].kind == nkSym
+    let oldParam = originalParams[i].sym
+    let param = copySym(oldParam, c.idgen)
+    param.owner = prc
+    param.typ = result[i]
+
+    # The default value is instantiated and fitted against the final
+    # concrete param type. We avoid calling `replaceTypeVarsN` on the
+    # call head symbol, because this leads to infinite recursion.
+    if oldParam.ast != nil:
+      var def = oldParam.ast.copyTree
+      if def.typ.kind == tyFromExpr:
+        def.typ.flags.incl tfNonConstExpr
+      if not isIntLit(def.typ):
+        def = prepareNode(cl, def)
+
+      # allow symchoice since node will be fit later
+      # although expectedType should cover it
+      def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit)
+      if def.referencesAnotherParam(getCurrOwner(c)):
+        def.flags.incl nfDefaultRefsParam
+
+      var converted = indexTypesMatch(c, typeToFit, def.typ, def)
+      if converted == nil:
+        # The default value doesn't match the final instantiated type.
+        # As an example of this, see:
+        # https://github.com/nim-lang/Nim/issues/1201
+        # We are replacing the default value with an error node in case
+        # the user calls an explicit instantiation of the proc (this is
+        # the only way the default value might be inserted).
+        param.ast = errorNode(c, def)
+        # we know the node is empty, we need the actual type for error message
+        param.ast.typ = def.typ
+      else:
+        param.ast = fitNodePostMatch(c, typeToFit, converted)
+      param.typ = result[i]
+
+    result.n[i] = newSymNode(param)
+    propagateToOwner(result, result[i])
+    addDecl(c, param)
+
+  resetIdTable(cl.symMap)
+  resetIdTable(cl.localCache)
+  cl.isReturnType = true
+  result.setReturnType replaceTypeVarsT(cl, result.returnType)
+  cl.isReturnType = false
+  result.n[0] = originalParams[0].copyTree
+  if result[0] != nil:
+    propagateToOwner(result, result[0])
 
-proc sideEffectsCheck(c: PContext, s: PSym) = 
-  if {sfNoSideEffect, sfSideEffect} * s.flags ==
-      {sfNoSideEffect, sfSideEffect}: 
-    LocalError(s.info, errXhasSideEffects, s.name.s)
-  elif sfThread in s.flags and semthreads.needsGlobalAnalysis() and 
-      s.ast.sons[genericParamsPos].kind == nkEmpty:
-    c.threadEntries.add(s)
+  eraseVoidParams(result)
+  skipIntLiteralParams(result, c.idgen)
 
-proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, 
+  prc.typ = result
+  popInfoContext(c.config)
+
+proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType =
+  # instantiates only the type of a given proc symbol
+  # used by sigmatch for explicit generics
+  # wouldn't be needed if sigmatch could handle complex cases,
+  # examples are in texplicitgenerics
+  # might be buggy, see rest of generateInstance if problems occur
+  let fakeSym = copySym(prc, c.idgen)
+  incl(fakeSym.flags, sfFromGeneric)
+  fakeSym.instantiatedFrom = prc
+  openScope(c)
+  for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
+    addDecl(c, s)
+  instantiateProcType(c, pt, fakeSym, info)
+  closeScope(c)
+  result = fakeSym.typ
+
+proc fillMixinScope(c: PContext) =
+  var p = c.p
+  while p != nil:
+    for bnd in p.localBindStmts:
+      for n in bnd:
+        addSym(c.currentScope, n.sym)
+    p = p.next
+
+proc getLocalPassC(c: PContext, s: PSym): string =
+  when defined(nimsuggest): return ""
+  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: TypeMapping,
                       info: TLineInfo): PSym =
+  ## Generates a new instance of a generic procedure.
+  ## The `pt` parameter is a type-unsafe mapping table used to link generic
+  ## parameters to their concrete types within the generic instance.
   # no need to instantiate generic templates/macros:
-  if fn.kind in {skTemplate, skMacro}: return fn
-  
+  internalAssert c.config, fn.kind notin {skMacro, skTemplate}
   # generates an instantiated proc
-  if c.InstCounter > 1000: InternalError(fn.ast.info, "nesting too deep")
-  inc(c.InstCounter)
+  if c.instCounter > 50:
+    globalError(c.config, info, "generic instantiation too nested")
+  inc c.instCounter
+  defer: dec c.instCounter
   # careful! we copy the whole AST including the possibly nil body!
   var n = copyTree(fn.ast)
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
-  var oldFriend = c.friendModule
-  c.friendModule = getModule(fn)
-  result = copySym(fn, false)
+  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)
-  # keep the owner if it's an inner proc (for proper closure transformations):
-  if fn.owner.kind == skModule:
-    result.owner = getCurrOwner().owner
+  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(result)
+  pushOwner(c, result)
+
+  # mixin scope:
+  openScope(c)
+  fillMixinScope(c)
+
   openScope(c)
-  if n.sons[genericParamsPos].kind == nkEmpty: 
-    InternalError(n.info, "generateInstance")
-  n.sons[namePos] = newSymNode(result)
-  pushInfoContext(info)
+  let gp = n[genericParamsPos]
+  if gp.kind != nkGenericParams:
+    # bug #22137
+    globalError(c.config, info, "generic instantiation too nested")
+  n[namePos] = newSymNode(result)
+  pushInfoContext(c.config, info, fn.detailedInfo)
   var entry = TInstantiation.new
   entry.sym = result
-  instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
-  n.sons[genericParamsPos] = ast.emptyNode
-  # semantic checking for the parameters:
-  if n.sons[paramsPos].kind != nkEmpty:
-    removeDefaultParamValues(n.sons[ParamsPos])
-    semParamList(c, n.sons[ParamsPos], nil, result)
-  else:
-    result.typ = newTypeS(tyProc, c)
-    rawAddSon(result.typ, nil)
-  result.typ.callConv = fn.typ.callConv
-  if result.kind == skIterator: result.typ.flags.incl(tfIterator)
-  var oldPrc = GenericCacheGet(fn, entry[])
+  # we need to compare both the generic types and the concrete types:
+  # generic[void](), generic[int]()
+  # see ttypeor.nim test.
+  var i = 0
+  newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len)
+  # let param instantiation know we are in a concept for unresolved statics:
+  c.matchedConcept = oldMatchedConcept
+  for s in instantiateGenericParamList(c, gp, pt):
+    addDecl(c, s)
+    entry.concreteTypes[i] = s.typ
+    inc i
+  c.matchedConcept = nil
+  pushProcCon(c, result)
+  instantiateProcType(c, pt, result, info)
+  for _, param in paramTypes(result.typ):
+    entry.concreteTypes[i] = param
+    inc i
+  #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
+  if tfTriggersCompileTime in result.typ.flags:
+    incl(result.flags, sfCompileTime)
+  n[genericParamsPos] = c.graph.emptyNode
+  var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
   if oldPrc == nil:
-    fn.procInstCache.safeAdd(entry)
+    # we MUST not add potentially wrong instantiations to the caching mechanism.
+    # This means recursive instantiations behave differently when in
+    # a ``compiles`` context but this is the lesser evil. See
+    # bug #1055 (tevilcompiles).
+    #if c.compilesContextId == 0:
+    entry.compilesId = c.compilesContextId
+    addToGenericProcCache(c, fn, entry)
     c.generics.add(makeInstPair(fn, entry))
-    if n.sons[pragmasPos].kind != nkEmpty:
-      pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
-    if isNil(n.sons[bodyPos]):
-      n.sons[bodyPos] = copyTree(fn.getBody)
-    if fn.kind != skTemplate:
-      instantiateBody(c, n, result)
-      sideEffectsCheck(c, result)
-    ParamsTypeCheck(c, result.typ)
+    # bug #12985 bug #22913
+    # TODO: use the context of the declaration of generic functions instead
+    # TODO: consider fixing options as well
+    let otherPragmas = c.optionStack[^1].otherPragmas
+    c.optionStack[^1].otherPragmas = nil
+    if n[pragmasPos].kind != nkEmpty:
+      pragma(c, result, n[pragmasPos], allRoutinePragmas)
+    if isNil(n[bodyPos]):
+      n[bodyPos] = copyTree(getBody(c.graph, fn))
+    instantiateBody(c, n, fn.typ.n, result, fn)
+    c.optionStack[^1].otherPragmas = otherPragmas
+    sideEffectsCheck(c, result)
+    if result.magic notin {mSlice, mTypeOf}:
+      # 'toOpenArray' is special and it is allowed to return 'openArray':
+      paramsTypeCheck(c, result.typ)
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len
   else:
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len
     result = oldPrc
-  popInfoContext()
+  popProcCon(c)
+  popInfoContext(c.config)
   closeScope(c)           # close scope for parameters
-  popOwner()
-  c.friendModule = oldFriend
-  dec(c.InstCounter)
+  closeScope(c)           # close scope for 'mixin' declarations
+  popOwner(c)
+  c.currentScope = oldScope
+  discard c.friendModules.pop()
+  c.matchedConcept = oldMatchedConcept
   if result.kind == skMethod: finishMethod(c, result)
-  
-proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = 
-  var cl: TReplTypeVars
-  InitIdTable(cl.symMap)
-  InitIdTable(cl.typeMap)
-  cl.info = n.info
-  cl.c = c
-  result = ReplaceTypeVarsT(cl, header)
 
+  # inform IC of the generic
+  #addGeneric(c.ic, result, entry.concreteTypes)