summary refs log tree commit diff stats
path: root/compiler/cgmeth.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/cgmeth.nim')
-rw-r--r--compiler/cgmeth.nim277
1 files changed, 146 insertions, 131 deletions
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 6f7d9f489..ca97d0494 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -7,13 +7,20 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements code generation for multi methods.
+## This module implements code generation for methods.
 
 import
-  intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils, modulegraphs
+  options, ast, msgs, idents, renderer, types, magicsys,
+  sempass2, modulegraphs, lineinfos, astalgo
 
-proc genConv(n: PNode, d: PType, downcast: bool): PNode =
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std/[tables]
+
+proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
   var dest = skipTypes(d, abstractPtrs)
   var source = skipTypes(n.typ, abstractPtrs)
   if (source.kind == tyObject) and (dest.kind == tyObject):
@@ -23,13 +30,13 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
       result = n
     elif diff < 0:
       result = newNodeIT(nkObjUpConv, n.info, d)
-      addSon(result, n)
-      if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
+      result.add n
+      if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
     elif diff > 0:
       result = newNodeIT(nkObjDownConv, n.info, d)
-      addSon(result, n)
+      result.add n
       if not downcast:
-        internalError(n.info, "cgmeth.genConv: no downcast allowed")
+        internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
     else:
       result = n
   else:
@@ -37,53 +44,58 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
 
 proc getDispatcher*(s: PSym): PSym =
   ## can return nil if is has no dispatcher.
-  let dispn = lastSon(s.ast)
-  if dispn.kind == nkSym:
-    let disp = dispn.sym
-    if sfDispatcher in disp.flags: result = disp
+  if dispatcherPos < s.ast.len:
+    result = s.ast[dispatcherPos].sym
+    doAssert sfDispatcher in result.flags
+  else:
+    result = nil
 
-proc methodCall*(n: PNode): PNode =
+proc methodCall*(n: PNode; conf: ConfigRef): PNode =
   result = n
   # replace ordinary method by dispatcher method:
-  let disp = getDispatcher(result.sons[0].sym)
+  let disp = getDispatcher(result[0].sym)
   if disp != nil:
-    result.sons[0].sym = disp
+    result[0].typ = disp.typ
+    result[0].sym = disp
     # change the arguments to up/downcasts to fit the dispatcher's parameters:
-    for i in countup(1, sonsLen(result)-1):
-      result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
+    for i in 1..<result.len:
+      result[i] = genConv(result[i], disp.typ[i], true, conf)
   else:
-    localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
+    localError(conf, n.info, "'" & $result[0] & "' lacks a dispatcher")
 
 type
   MethodResult = enum No, Invalid, Yes
 
-proc sameMethodBucket(a, b: PSym): MethodResult =
+proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
+  result = No
   if a.name.id != b.name.id: return
-  if sonsLen(a.typ) != sonsLen(b.typ):
+  if a.typ.signatureLen != b.typ.signatureLen:
     return
 
-  for i in countup(1, sonsLen(a.typ) - 1):
-    var aa = a.typ.sons[i]
-    var bb = b.typ.sons[i]
+  var i = 0
+  for x, y in paramTypePairs(a.typ, b.typ):
+    inc i
+    var aa = x
+    var bb = y
     while true:
       aa = skipTypes(aa, {tyGenericInst, tyAlias})
       bb = skipTypes(bb, {tyGenericInst, tyAlias})
-      if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef}:
-        aa = aa.lastSon
-        bb = bb.lastSon
+      if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
+        aa = aa.elementType
+        bb = bb.elementType
       else:
         break
-    if sameType(aa, bb):
+    if sameType(x, y):
       if aa.kind == tyObject and result != Invalid:
         result = Yes
-    elif aa.kind == tyObject and bb.kind == tyObject:
+    elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
       let diff = inheritanceDiff(bb, aa)
       if diff < 0:
         if result != Invalid:
           result = Yes
         else:
           return No
-      elif diff != high(int):
+      elif diff != high(int) and sfFromGeneric notin (a.flags+b.flags):
         result = Invalid
       else:
         return No
@@ -91,131 +103,131 @@ proc sameMethodBucket(a, b: PSym): MethodResult =
       return No
   if result == Yes:
     # check for return type:
-    if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]):
-      if b.typ.sons[0] != nil and b.typ.sons[0].kind == tyExpr:
+    # ignore flags of return types; # bug #22673
+    if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
+      if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
         # infer 'auto' from the base to make it consistent:
-        b.typ.sons[0] = a.typ.sons[0]
+        b.typ.setReturnType a.typ.returnType
       else:
         return No
 
 proc attachDispatcher(s: PSym, dispatcher: PNode) =
-  var L = s.ast.len-1
-  var x = s.ast.sons[L]
-  if x.kind == nkSym and sfDispatcher in x.sym.flags:
+  if dispatcherPos < s.ast.len:
     # we've added a dispatcher already, so overwrite it
-    s.ast.sons[L] = dispatcher
+    s.ast[dispatcherPos] = dispatcher
   else:
-    s.ast.add(dispatcher)
+    setLen(s.ast.sons, dispatcherPos+1)
+    if s.ast[resultPos] == nil:
+      s.ast[resultPos] = newNodeI(nkEmpty, s.info)
+    s.ast[dispatcherPos] = dispatcher
 
-proc createDispatcher(s: PSym): PSym =
-  var disp = copySym(s)
+proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
+  var disp = copySym(s, idgen)
   incl(disp.flags, sfDispatcher)
   excl(disp.flags, sfExported)
-  disp.typ = copyType(disp.typ, disp.typ.owner, false)
+  let old = disp.typ
+  disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
+  copyTypeProps(g, idgen.module, disp.typ, old)
+
   # we can't inline the dispatcher itself (for now):
-  if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
+  if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
   disp.ast = copyTree(s.ast)
-  disp.ast.sons[bodyPos] = ast.emptyNode
-  disp.loc.r = nil
-  if s.typ.sons[0] != nil:
-    if disp.ast.sonsLen > resultPos:
-      disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
+  disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
+  disp.loc.snippet = ""
+  if s.typ.returnType != nil:
+    if disp.ast.len > resultPos:
+      disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
     else:
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
       # be updated in fixupDispatcher().
-      disp.ast.addSon(ast.emptyNode)
+      disp.ast.add newNodeI(nkEmpty, s.info)
   attachDispatcher(s, newSymNode(disp))
   # attach to itself to prevent bugs:
   attachDispatcher(disp, newSymNode(disp))
   return disp
 
-proc fixupDispatcher(meth, disp: PSym) =
+proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
   # We may have constructed the dispatcher from a method prototype
   # and need to augment the incomplete dispatcher with information
   # from later definitions, particularly the resultPos slot. Also,
   # the lock level of the dispatcher needs to be updated/checked
   # against that of the method.
-  if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
-     disp.ast.sons[resultPos] == ast.emptyNode:
-    disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
+  if disp.ast.len > resultPos and meth.ast.len > resultPos and
+     disp.ast[resultPos].kind == nkEmpty:
+    disp.ast[resultPos] = copyTree(meth.ast[resultPos])
 
-  # The following code works only with lock levels, so we disable
-  # it when they're not available.
-  when declared(TLockLevel):
-    proc `<`(a, b: TLockLevel): bool {.borrow.}
-    proc `==`(a, b: TLockLevel): bool {.borrow.}
-    if disp.typ.lockLevel == UnspecifiedLockLevel:
-      disp.typ.lockLevel = meth.typ.lockLevel
-    elif meth.typ.lockLevel != UnspecifiedLockLevel and
-         meth.typ.lockLevel != disp.typ.lockLevel:
-      message(meth.info, warnLockLevel,
-        "method has lock level $1, but another method has $2" %
-        [$meth.typ.lockLevel, $disp.typ.lockLevel])
-      # XXX The following code silences a duplicate warning in
-      # checkMethodeffects() in sempass2.nim for now.
-      if disp.typ.lockLevel < meth.typ.lockLevel:
-        disp.typ.lockLevel = meth.typ.lockLevel
+proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
+  var witness: PSym = nil
+  if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
+      g.config.isDefined("nimInternalNonVtablesTesting"):
+    localError(g.config, s.info, errGenerated, "method `" & s.name.s &
+          "` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
+  if sfImportc in s.flags:
+    localError(g.config, s.info, errGenerated, "method `" & s.name.s &
+          "` is not allowed to have 'importc' pragmas")
 
-proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
-  let L = len(g.methods)
-  var witness: PSym
-  for i in countup(0, L - 1):
+  for i in 0..<g.methods.len:
     let disp = g.methods[i].dispatcher
-    case sameMethodBucket(disp, s)
+    case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
     of Yes:
-      add(g.methods[i].methods, s)
-      attachDispatcher(s, lastSon(disp.ast))
-      fixupDispatcher(s, disp)
+      g.methods[i].methods.add(s)
+      attachDispatcher(s, disp.ast[dispatcherPos])
+      fixupDispatcher(s, disp, g.config)
       #echo "fixup ", disp.name.s, " ", disp.id
-      when useEffectSystem: checkMethodEffects(disp, s)
+      when useEffectSystem: checkMethodEffects(g, disp, s)
       if {sfBase, sfFromGeneric} * s.flags == {sfBase} and
            g.methods[i].methods[0] != s:
         # already exists due to forwarding definition?
-        localError(s.info, "method is not a base")
+        localError(g.config, s.info, "method is not a base")
       return
     of No: discard
     of Invalid:
       if witness.isNil: witness = g.methods[i].methods[0]
   # create a new dispatcher:
-  add(g.methods, (methods: @[s], dispatcher: createDispatcher(s)))
+  # stores the id and the position
+  if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
+    g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
+  else:
+    g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
+  g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
   #echo "adding ", s.info
-  #if fromCache:
-  #  internalError(s.info, "no method dispatcher found")
   if witness != nil:
-    localError(s.info, "invalid declaration order; cannot attach '" & s.name.s &
-                       "' to method defined here: " & $witness.info)
+    localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
+                       "' to method defined here: " & g.config$witness.info)
   elif sfBase notin s.flags:
-    message(s.info, warnUseBase)
+    message(g.config, s.info, warnUseBase)
 
-proc relevantCol(methods: TSymSeq, col: int): bool =
+proc relevantCol*(methods: seq[PSym], col: int): bool =
   # returns true iff the position is relevant
-  var t = methods[0].typ.sons[col].skipTypes(skipPtrs)
+  result = false
+  var t = methods[0].typ[col].skipTypes(skipPtrs)
   if t.kind == tyObject:
-    for i in countup(1, high(methods)):
-      let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
+    for i in 1..high(methods):
+      let t2 = skipTypes(methods[i].typ[col], skipPtrs)
       if not sameType(t2, t):
         return true
 
 proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
-  for col in countup(1, sonsLen(a.typ) - 1):
+  result = 0
+  for col in FirstParamAt..<a.typ.signatureLen:
     if contains(relevantCols, col):
-      var aa = skipTypes(a.typ.sons[col], skipPtrs)
-      var bb = skipTypes(b.typ.sons[col], skipPtrs)
+      var aa = skipTypes(a.typ[col], skipPtrs)
+      var bb = skipTypes(b.typ[col], skipPtrs)
       var d = inheritanceDiff(aa, bb)
       if (d != high(int)) and d != 0:
         return d
 
-proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
+proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
   # we use shellsort here; fast and simple
-  var n = len(a)
+  var n = a.len
   var h = 1
   while true:
     h = 3 * h + 1
     if h > n: break
   while true:
     h = h div 3
-    for i in countup(h, n - 1):
+    for i in h..<n:
       var v = a[i]
       var j = i
       while cmpSignatures(a[j - h], v, relevantCols) >= 0:
@@ -225,69 +237,72 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
       a[j] = v
     if h == 1: break
 
-proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
-  var base = lastSon(methods[0].ast).sym
+proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
+  var base = methods[0].ast[dispatcherPos].sym
   result = base
-  var paramLen = sonsLen(base.typ)
+  var paramLen = base.typ.signatureLen
   var nilchecks = newNodeI(nkStmtList, base.info)
   var disp = newNodeI(nkIfStmt, base.info)
-  var ands = getSysSym("and")
-  var iss = getSysSym("of")
-  for col in countup(1, paramLen - 1):
+  var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
+  var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
+  let boolType = getSysType(g, unknownLineInfo, tyBool)
+  for col in FirstParamAt..<paramLen:
     if contains(relevantCols, col):
-      let param = base.typ.n.sons[col].sym
+      let param = base.typ.n[col].sym
       if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
-        addSon(nilchecks, newTree(nkCall,
-            newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
-  for meth in countup(0, high(methods)):
+        nilchecks.add newTree(nkCall,
+            newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param))
+  for meth in 0..high(methods):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
-    for col in countup(1, paramLen - 1):
+    for col in FirstParamAt..<paramLen:
       if contains(relevantCols, col):
-        var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
-        addSon(isn, newSymNode(iss))
-        let param = base.typ.n.sons[col].sym
-        addSon(isn, newSymNode(param))
-        addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
+        var isn = newNodeIT(nkCall, base.info, boolType)
+        isn.add newSymNode(iss)
+        let param = base.typ.n[col].sym
+        isn.add newSymNode(param)
+        isn.add newNodeIT(nkType, base.info, curr.typ[col])
         if cond != nil:
-          var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
-          addSon(a, newSymNode(ands))
-          addSon(a, cond)
-          addSon(a, isn)
+          var a = newNodeIT(nkCall, base.info, boolType)
+          a.add newSymNode(ands)
+          a.add cond
+          a.add isn
           cond = a
         else:
           cond = isn
-    let retTyp = base.typ.sons[0]
+    let retTyp = base.typ.returnType
     let call = newNodeIT(nkCall, base.info, retTyp)
-    addSon(call, newSymNode(curr))
-    for col in countup(1, paramLen - 1):
-      addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
-                           curr.typ.sons[col], false))
+    call.add newSymNode(curr)
+    for col in 1..<paramLen:
+      call.add genConv(newSymNode(base.typ.n[col].sym),
+                           curr.typ[col], false, g.config)
     var ret: PNode
     if retTyp != nil:
       var a = newNodeI(nkFastAsgn, base.info)
-      addSon(a, newSymNode(base.ast.sons[resultPos].sym))
-      addSon(a, call)
+      a.add newSymNode(base.ast[resultPos].sym)
+      a.add call
       ret = newNodeI(nkReturnStmt, base.info)
-      addSon(ret, a)
+      ret.add a
     else:
       ret = call
     if cond != nil:
       var a = newNodeI(nkElifBranch, base.info)
-      addSon(a, cond)
-      addSon(a, ret)
-      addSon(disp, a)
+      a.add cond
+      a.add ret
+      disp.add a
     else:
       disp = ret
   nilchecks.add disp
-  result.ast.sons[bodyPos] = nilchecks
+  nilchecks.flags.incl nfTransf # should not be further transformed
+  result.ast[bodyPos] = nilchecks
 
-proc generateMethodDispatchers*(g: ModuleGraph): PNode =
-  result = newNode(nkStmtList)
-  for bucket in countup(0, len(g.methods) - 1):
+proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
+  for bucket in 0..<g.methods.len:
     var relevantCols = initIntSet()
-    for col in countup(1, sonsLen(g.methods[bucket].methods[0].typ) - 1):
+    for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
       if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
+      if optMultiMethods notin g.config.globalOptions:
+        # if multi-methods are not enabled, we are interested only in the first field
+        break
     sortBucket(g.methods[bucket].methods, relevantCols)
-    addSon(result,
-           newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols)))
+    g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)