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.nim287
1 files changed, 178 insertions, 109 deletions
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 1a7bd9b6b..1bc6d31a2 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -28,76 +28,66 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) =
       # it is not an error to shadow fields via parameters
   else: discard
 
-proc rawPushProcCon(c: PContext, owner: PSym) =
-  var x: PProcCon
-  new(x)
-  x.owner = owner
-  x.next = c.p
-  c.p = x
-
-proc rawHandleSelf(c: PContext; owner: PSym) =
-  const callableSymbols = {skProc, skFunc, skMethod, skConverter, skIterator, skMacro}
-  if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
-    let params = owner.typ.n
-    if params.len > 1:
-      let arg = params[1].sym
-      if arg.name.id == c.selfName.id:
-        c.p.selfSym = arg
-        arg.flags.incl sfIsSelf
-        var t = c.p.selfSym.typ.skipTypes(abstractPtrs)
-        while t.kind == tyObject:
-          addObjFieldsToLocalScope(c, t.n)
-          if t[0] == nil: break
-          t = t[0].skipTypes(skipPtrs)
-
 proc pushProcCon*(c: PContext; owner: PSym) =
-  rawPushProcCon(c, owner)
-  rawHandleSelf(c, owner)
+  c.p = PProcCon(owner: owner, next: c.p)
 
 const
   errCannotInstantiateX = "cannot instantiate: '$1'"
 
-iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
   internalAssert c.config, n.kind == nkGenericParams
-  for i, a in n.pairs:
+  for a in n.items:
     internalAssert c.config, a.kind == nkSym
     var q = a.sym
-    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
-      continue
-    let symKind = if q.typ.kind == tyStatic: skConst else: skType
-    var s = newSym(symKind, q.name, getCurrOwner(c), 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(c.config, 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 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 == tyGenericParam:
-      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
+      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 = {ExactTypeDescValues,
-                                   ExactGcSafety}): return
+                                   ExactGcSafety,
+                                   PickyCAliases}): return
     result = true
+  else:
+    result = false
 
-proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
+proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
                      id: CompilesId): PSym =
-  for inst in genericSym.procInstCache:
+  result = nil
+  for inst in procInstCacheItems(g, genericSym):
     if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
       return inst.sym
 
@@ -105,7 +95,7 @@ when false:
   proc `$`(x: PSym): string =
     result = x.name.s & " " & " id " & $x.id
 
-proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
+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:
   #  if n.sym.owner != orig:
@@ -113,17 +103,17 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
   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 = PSym(idTableGet(symMap, s))
+    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)
+      x = copySym(s, c.idgen)
       x.owner = owner
       idTablePut(symMap, s, x)
       n.sym = x
   else:
-    for i in 0..<n.safeLen: freshGenSyms(n[i], owner, orig, symMap)
+    for i in 0..<n.safeLen: freshGenSyms(c, n[i], owner, orig, symMap)
 
 proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
 
@@ -137,18 +127,28 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
     inc c.inGenericInst
     # add it here, so that recursive generic procs are possible:
     var b = n[bodyPos]
-    var symMap: TIdTable
-    initIdTable symMap
+    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(b, result, orig, symMap)
-    b = semProcBody(c, b)
+    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)
-    trackProc(c, result, result.ast[bodyPos])
     excl(result.flags, sfForward)
+    trackProc(c, result, result.ast[bodyPos])
     dec c.inGenericInst
 
 proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
@@ -160,7 +160,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       pushInfoContext(c.config, oldPrc.info)
       openScope(c)
       var n = oldPrc.ast
-      n[bodyPos] = copyTree(s.getBody)
+      n[bodyPos] = copyTree(getBody(c.graph, s))
       instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
       closeScope(c)
       popInfoContext(c.config)
@@ -177,17 +177,12 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                           allowMetaTypes = false): PType =
   internalAssert c.config, header.kind == tyGenericInvocation
 
-  var
-    cl: TReplTypeVars
-
-  initIdTable(cl.symMap)
-  initIdTable(cl.localCache)
-  cl.typeMap = LayeredIdTable()
-  initIdTable(cl.typeMap.topLayer)
+  var cl: TReplTypeVars = TReplTypeVars(symMap: initSymMapping(),
+        localCache: initTypeMapping(), typeMap: LayeredIdTable(),
+        info: info, c: c, allowMetaTypes: allowMetaTypes
+      )
 
-  cl.info = info
-  cl.c = c
-  cl.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.
@@ -195,12 +190,11 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
   # perhaps the code can be extracted in a shared function.
   openScope(c)
   let genericTyp = header.base
-  for i in 0..<genericTyp.len - 1:
-    let genParam = genericTyp[i]
+  for i, genParam in genericBodyParams(genericTyp):
     var param: PSym
 
     template paramSym(kind): untyped =
-      newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
+      newSym(kind, genParam.sym.name, c.idgen, genericTyp.sym, genParam.sym.info)
 
     if genParam.kind == tyStatic:
       param = paramSym skConst
@@ -226,17 +220,17 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool =
       if referencesAnotherParam(n[i], p): return true
     return false
 
-proc instantiateProcType(c: PContext, pt: TIdTable,
+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): type(b.x)
+  # 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: type(x.y))
+  # 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
@@ -248,20 +242,26 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   var result = instCopyType(cl, prc.typ)
   let originalParams = result.n
   result.n = originalParams.shallowCopy
-  for i in 1..<result.len:
+  for i, resulti in paramTypes(result):
     # twrong_field_caching requires these 'resetIdTable' calls:
-    if i > 1:
+    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 = result[i]
+    var typeToFit = resulti
 
-    let needsStaticSkipping = result[i].kind == tyFromExpr
-    result[i] = replaceTypeVarsT(cl, result[i])
+    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
@@ -270,7 +270,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
 
     internalAssert c.config, originalParams[i].kind == nkSym
     let oldParam = originalParams[i].sym
-    let param = copySym(oldParam)
+    let param = copySym(oldParam, c.idgen)
     param.owner = prc
     param.typ = result[i]
 
@@ -279,11 +279,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
     # call head symbol, because this leads to infinite recursion.
     if oldParam.ast != nil:
       var def = oldParam.ast.copyTree
-      if def.kind == nkCall:
-        for i in 1..<def.len:
-          def[i] = replaceTypeVarsN(cl, def[i])
-
-      def = semExprWithType(c, def)
+      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
 
@@ -296,6 +299,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
         # 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]
@@ -307,20 +312,56 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   resetIdTable(cl.symMap)
   resetIdTable(cl.localCache)
   cl.isReturnType = true
-  result[0] = replaceTypeVarsT(cl, result[0])
+  result.setReturnType replaceTypeVarsT(cl, result.returnType)
   cl.isReturnType = false
   result.n[0] = originalParams[0].copyTree
   if result[0] != nil:
     propagateToOwner(result, result[0])
 
   eraseVoidParams(result)
-  skipIntLiteralParams(result)
+  skipIntLiteralParams(result, c.idgen)
 
   prc.typ = result
   popInfoContext(c.config)
 
-proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
-                      info: TLineInfo): PSym {.nosinks.} =
+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.
@@ -329,25 +370,40 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # generates an instantiated proc
   if c.instCounter > 50:
     globalError(c.config, info, "generic instantiation too nested")
-  inc(c.instCounter)
+  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:
-  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)
+  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)
 
+  # mixin scope:
+  openScope(c)
+  fillMixinScope(c)
+
   openScope(c)
   let gp = n[genericParamsPos]
-  internalAssert c.config, gp.kind != nkEmpty
+  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
@@ -356,48 +412,61 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # generic[void](), generic[int]()
   # see ttypeor.nim test.
   var i = 0
-  newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
+  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
-  rawPushProcCon(c, result)
+  c.matchedConcept = nil
+  pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
-  for j in 1..<result.typ.len:
-    entry.concreteTypes[i] = result.typ[j]
+  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(fn, entry[], c.compilesContextId)
+  var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
   if oldPrc == nil:
     # 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:
-    rawHandleSelf(c, result)
     entry.compilesId = c.compilesContextId
-    fn.procInstCache.add(entry)
+    addToGenericProcCache(c, fn, entry)
     c.generics.add(makeInstPair(fn, entry))
+    # 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(fn.getBody)
-    if c.inGenericContext == 0:
-      instantiateBody(c, n, fn.typ.n, result, fn)
+      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
   popProcCon(c)
   popInfoContext(c.config)
   closeScope(c)           # close scope for parameters
+  closeScope(c)           # close scope for 'mixin' declarations
   popOwner(c)
   c.currentScope = oldScope
   discard c.friendModules.pop()
-  dec(c.instCounter)
   c.matchedConcept = oldMatchedConcept
   if result.kind == skMethod: finishMethod(c, result)
+
+  # inform IC of the generic
+  #addGeneric(c.ic, result, entry.concreteTypes)