summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-12-15 10:20:57 +0100
committerGitHub <noreply@github.com>2023-12-15 10:20:57 +0100
commit91ad6a740bad84f76ecfba253d12f07d6abbd3eb (patch)
tree6fdc8aad065e19e51fad7f996016feaae9b6280e
parentcca5684a17e654a13ddac046f1e76873d8c19f55 (diff)
downloadNim-91ad6a740bad84f76ecfba253d12f07d6abbd3eb.tar.gz
type refactor: part 4 (#23077)
-rw-r--r--compiler/aliases.nim4
-rw-r--r--compiler/ast.nim9
-rw-r--r--compiler/ccgcalls.nim16
-rw-r--r--compiler/ccgexprs.nim11
-rw-r--r--compiler/ccgreset.nim11
-rw-r--r--compiler/ccgstmts.nim3
-rw-r--r--compiler/ccgtrav.nim15
-rw-r--r--compiler/ccgtypes.nim37
-rw-r--r--compiler/cgmeth.nim22
-rw-r--r--compiler/concepts.nim50
-rw-r--r--compiler/dfa.nim2
-rw-r--r--compiler/evaltempl.nim4
-rw-r--r--compiler/expanddefaults.nim2
-rw-r--r--compiler/injectdestructors.nim4
-rw-r--r--compiler/liftdestructors.nim18
-rw-r--r--compiler/semmacrosanity.nim2
-rw-r--r--compiler/sempass2.nim12
-rw-r--r--compiler/semtypinst.nim35
-rw-r--r--compiler/sighashes.nim32
-rw-r--r--compiler/sigmatch.nim44
-rw-r--r--compiler/varpartitions.nim4
-rw-r--r--compiler/vm.nim17
-rw-r--r--compiler/vmdeps.nim22
-rw-r--r--compiler/vmgen.nim8
-rw-r--r--compiler/vmmarshal.nim7
25 files changed, 197 insertions, 194 deletions
diff --git a/compiler/aliases.nim b/compiler/aliases.nim
index 3910ecb9d..fa1167753 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -59,8 +59,8 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
   of tySet, tyArray:
     result = isPartOfAux(a.elementType, b, marker)
   of tyTuple:
-    for i in 0..<a.len:
-      result = isPartOfAux(a[i], b, marker)
+    for aa in a.kids:
+      result = isPartOfAux(aa, b, marker)
       if result == arYes: return
   else: discard
 
diff --git a/compiler/ast.nim b/compiler/ast.nim
index aa12c6421..ce86d7369 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1593,12 +1593,15 @@ iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
 
 iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
   # XXX Figure out with what typekinds this is called.
-  for i in start ..< a.sons.len + without:
+  for i in start ..< min(a.sons.len, b.sons.len) + without:
     yield (a.sons[i], b.sons[i])
 
 proc signatureLen*(t: PType): int {.inline.} =
   result = t.sons.len
 
+proc paramsLen*(t: PType): int {.inline.} =
+  result = t.sons.len - 1
+
 proc kidsLen*(t: PType): int {.inline.} =
   result = t.sons.len
 
@@ -1629,10 +1632,14 @@ iterator ikids*(t: PType): (int, PType) =
 
 const
   FirstParamAt* = 1
+  FirstGenericParamAt* = 1
 
 iterator paramTypes*(t: PType): (int, PType) =
   for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
 
+iterator paramTypePairs*(a, b: PType): (PType, PType) =
+  for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i])
+
 template paramTypeToNodeIndex*(x: int): int = x
 
 iterator kids*(t: PType): PType =
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 53c147024..5dc6b39a6 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -394,7 +394,7 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
 
   var oldLen = result.len
   for i in 1..<ri.len:
-    if i < typ.len:
+    if i < typ.n.len:
       assert(typ.n[i].kind == nkSym)
       let paramType = typ.n[i]
       if not paramType.typ.isCompileTimeOnly:
@@ -419,7 +419,6 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
 
   var params = newRopeAppender()
   genParams(p, ri, typ, params)
@@ -442,7 +441,6 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
 
   var pl = newRopeAppender()
   genParams(p, ri, typ, pl)
@@ -502,14 +500,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
 
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
                  argsCounter: var int) =
-  if i < typ.len:
+  if i < typ.n.len:
     # 'var T' is 'T&' in C++. This means we ignore the request of
     # any nkHiddenAddr when it's a 'var T'.
     let paramType = typ.n[i]
     assert(paramType.kind == nkSym)
     if paramType.typ.isCompileTimeOnly:
       discard
-    elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
+    elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
       if argsCounter > 0: result.add ", "
       genArgNoParam(p, ri[i][0], result)
       inc argsCounter
@@ -584,7 +582,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
   # for better or worse c2nim translates the 'this' argument to a 'var T'.
   # However manual wrappers may also use 'ptr T'. In any case we support both
   # for convenience.
-  internalAssert p.config, i < typ.len
+  internalAssert p.config, i < typ.n.len
   assert(typ.n[i].kind == nkSym)
   # if the parameter is lying (tyVar) and thus we required an additional deref,
   # skip the deref:
@@ -674,7 +672,6 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
   # don't call '$' here for efficiency:
   let pat = $ri[0].sym.loc.r
   internalAssert p.config, pat.len > 0
@@ -708,7 +705,6 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
     pl.add(op.r)
     var params = newRopeAppender()
     for i in 2..<ri.len:
-      assert(typ.len == typ.n.len)
       genOtherArg(p, ri, i, typ, params, argsCounter)
     fixupCall(p, le, ri, d, pl, params)
 
@@ -719,7 +715,6 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
 
   # don't call '$' here for efficiency:
   let pat = $ri[0].sym.loc.r
@@ -741,8 +736,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
       pl.add(": ")
       genArg(p, ri[2], typ.n[2].sym, ri, pl)
   for i in start..<ri.len:
-    assert(typ.len == typ.n.len)
-    if i >= typ.len:
+    if i >= typ.n.len:
       internalError(p.config, ri.info, "varargs for objective C method?")
     assert(typ.n[i].kind == nkSym)
     var param = typ.n[i].sym
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 28f75f994..8cb9e208a 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -236,8 +236,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       flags
   let t = skipTypes(dest.t, abstractInst).getUniqueType()
-  for i in 0..<t.len:
-    let t = t[i]
+  for i, t in t.ikids:
     let field = "Field$1" % [i.rope]
     genAssignment(p, optAsgnLoc(dest, t, field),
                      optAsgnLoc(src, t, field), newflags)
@@ -362,7 +361,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   of tyTuple:
     if containsGarbageCollectedRef(dest.t):
-      if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags)
+      if dest.t.kidsLen <= 4: genOptAsgnTuple(p, dest, src, flags)
       else: genGenericAsgn(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
@@ -3193,11 +3192,11 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
     result.add "}"
   of tyTuple:
     result.add "{"
-    if p.vccAndC and t.len == 0:
+    if p.vccAndC and t.isEmptyTupleType:
       result.add "0"
-    for i in 0..<t.len:
+    for i, a in t.ikids:
       if i > 0: result.add ", "
-      getDefaultValue(p, t[i], info, result)
+      getDefaultValue(p, a, info, result)
     result.add "}"
   of tyArray:
     result.add "{"
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
index da7f37398..e4abcfc8c 100644
--- a/compiler/ccgreset.nim
+++ b/compiler/ccgreset.nim
@@ -63,15 +63,14 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
     specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
     lineF(p, cpsStmts, "}$n", [])
   of tyObject:
-    for i in 0..<typ.len:
-      var x = typ[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      specializeResetT(p, accessor.parentObj(p.module), x)
+    var x = typ.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    specializeResetT(p, accessor.parentObj(p.module), x)
     if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
   of tyTuple:
     let typ = getUniqueType(typ)
-    for i in 0..<typ.len:
-      specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
+    for i, a in typ.ikids:
+      specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
 
   of tyString, tyRef, tySequence:
     lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index c3d44c1d3..7bbc40890 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -289,13 +289,12 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
     #echo "New code produced for ", v.name.s, " ", p.config $ value.info
     genBracedInit(p, value, isConst = false, v.typ, result)
 
-proc genCppParamsForCtor(p: BProc; call: PNode): string = 
+proc genCppParamsForCtor(p: BProc; call: PNode): string =
   result = ""
   var argsCounter = 0
   let typ = skipTypes(call[0].typ, abstractInst)
   assert(typ.kind == tyProc)
   for i in 1..<call.len:
-    assert(typ.len == typ.n.len)
     #if it's a type we can just generate here another initializer as we are in an initializer context
     if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
       if argsCounter > 0: result.add ","
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index a7470c44f..3fd269bc6 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -87,15 +87,14 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
     else:
       lineF(p, cpsStmts, "}$n", [])
   of tyObject:
-    for i in 0..<typ.len:
-      var x = typ[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      genTraverseProc(c, accessor.parentObj(c.p.module), x)
+    var x = typ.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    genTraverseProc(c, accessor.parentObj(c.p.module), x)
     if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
   of tyTuple:
     let typ = getUniqueType(typ)
-    for i in 0..<typ.len:
-      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
+    for i, a in typ.ikids:
+      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
   of tyRef:
     lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
   of tySequence:
@@ -118,10 +117,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
 proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
   var p = c.p
   assert typ.kind == tySequence
-  var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
+  var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
   var oldCode = p.s(cpsStmts)
   freeze oldCode
-  var a: TLoc = TLoc(r: accessor)
+  var a = TLoc(r: accessor)
 
   lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
       [i.r, lenExpr(c.p, a)])
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 3d7aa3088..6a51fe3a0 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -826,7 +826,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope,
     if not hasField and typ.itemId notin m.g.graph.memberProcsPerType:
       if desc == "":
         result.add("\tchar dummy;\n")
-      elif typ.len == 1 and typ.n[0].kind == nkSym:
+      elif typ.n.len == 1 and typ.n[0].kind == nkSym:
         let field = typ.n[0].sym
         let fieldType = field.typ.skipTypes(abstractInst)
         if fieldType.kind == tyUncheckedArray:
@@ -845,9 +845,9 @@ proc getTupleDesc(m: BModule; typ: PType, name: Rope,
                   check: var IntSet): Rope =
   result = "$1 $2 {$n" % [structOrUnion(typ), name]
   var desc: Rope = ""
-  for i in 0..<typ.len:
+  for i, a in typ.ikids:
     desc.addf("$1 Field$2;$n",
-         [getTypeDescAux(m, typ[i], check, dkField), rope(i)])
+         [getTypeDescAux(m, a, check, dkField), rope(i)])
   if desc == "": result.add("char dummy;\L")
   else: result.add(desc)
   result.add("};\L")
@@ -872,13 +872,13 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
 proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
   # Make sure the index refers to one of the generic params of the type.
   # XXX: we should catch this earlier and report it as a semantic error.
-  if idx >= typ.len:
+  if idx >= typ.kidsLen:
     raiseAssert "invalid apostrophe type parameter index"
 
   result = typ[idx]
   for i in 1..stars:
-    if result != nil and result.len > 0:
-      result = if result.kind == tyGenericInst: result[1]
+    if result != nil and result.kidsLen > 0:
+      result = if result.kind == tyGenericInst: result[FirstGenericParamAt]
                else: result.elemType
 
 proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKind): Rope =
@@ -1079,9 +1079,9 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
         result.add cppName.substr(chunkStart)
       else:
         result = cppNameAsRope & "<"
-        for i in 1..<tt.len-1:
-          if i > 1: result.add(" COMMA ")
-          addResultType(tt[i])
+        for needsComma, a in tt.genericInstParams:
+          if needsComma: result.add(" COMMA ")
+          addResultType(a)
         result.add("> ")
       # always call for sideeffects:
       assert t.kind != tyTuple
@@ -1333,7 +1333,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
 proc genTypeInfoAux(m: BModule; typ, origType: PType, name: Rope;
                     info: TLineInfo) =
   var base: Rope
-  if typ.len > 0 and typ.last != nil:
+  if typ.hasElementType and typ.last != nil:
     var x = typ.last
     if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
     if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
@@ -1457,11 +1457,10 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo
 proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info)
   var expr = getNimNode(m)
-  if typ.len > 0:
-    var tmp = getTempName(m) & "_" & $typ.len
-    genTNimNodeArray(m, tmp, rope(typ.len))
-    for i in 0..<typ.len:
-      var a = typ[i]
+  if not typ.isEmptyTupleType:
+    var tmp = getTempName(m) & "_" & $typ.kidsLen
+    genTNimNodeArray(m, tmp, rope(typ.kidsLen))
+    for i, a in typ.ikids:
       var tmp2 = getNimNode(m)
       m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
       m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
@@ -1470,10 +1469,10 @@ proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo)
           "$1.name = \"Field$3\";$n",
            [tmp2, getTypeDesc(m, origType, dkVar), rope(i), genTypeInfoV1(m, a, info)])
     m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
-         [expr, rope(typ.len), tmp])
+         [expr, rope(typ.kidsLen), tmp])
   else:
     m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n",
-         [expr, rope(typ.len)])
+         [expr, rope(typ.kidsLen)])
   m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr])
 
 proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
@@ -1729,7 +1728,7 @@ proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLin
 
   m.s[cfsTypeInit3].add typeEntry
 
-  if t.kind == tyObject and t.len > 0 and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
+  if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
     discard genTypeInfoV1(m, t, info)
 
 proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
@@ -1779,7 +1778,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn
     addf(typeEntry, ", .flags = $1};$n", [rope(flags)])
     m.s[cfsVars].add typeEntry
 
-  if t.kind == tyObject and t.len > 0 and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
+  if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
     discard genTypeInfoV1(m, t, info)
 
 proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 6bfa6d789..6c3db7ad6 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -69,12 +69,14 @@ type
 proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
   result = No
   if a.name.id != b.name.id: return
-  if a.typ.len != b.typ.len:
+  if a.typ.signatureLen != b.typ.signatureLen:
     return
 
-  for i in 1..<a.typ.len:
-    var aa = a.typ[i]
-    var bb = b.typ[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})
@@ -83,7 +85,7 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
         bb = bb.elementType
       else:
         break
-    if sameType(a.typ[i], b.typ[i]):
+    if sameType(x, y):
       if aa.kind == tyObject and result != Invalid:
         result = Yes
     elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
@@ -204,7 +206,7 @@ proc relevantCol*(methods: seq[PSym], col: int): bool =
 
 proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
   result = 0
-  for col in 1..<a.typ.len:
+  for col in FirstParamAt..<a.typ.signatureLen:
     if contains(relevantCols, col):
       var aa = skipTypes(a.typ[col], skipPtrs)
       var bb = skipTypes(b.typ[col], skipPtrs)
@@ -234,13 +236,13 @@ proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
 proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
   var base = methods[0].ast[dispatcherPos].sym
   result = base
-  var paramLen = base.typ.len
+  var paramLen = base.typ.signatureLen
   var nilchecks = newNodeI(nkStmtList, base.info)
   var disp = newNodeI(nkIfStmt, base.info)
   var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
   var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
   let boolType = getSysType(g, unknownLineInfo, tyBool)
-  for col in 1..<paramLen:
+  for col in FirstParamAt..<paramLen:
     if contains(relevantCols, col):
       let param = base.typ.n[col].sym
       if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
@@ -249,7 +251,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
   for meth in 0..high(methods):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
-    for col in 1..<paramLen:
+    for col in FirstParamAt..<paramLen:
       if contains(relevantCols, col):
         var isn = newNodeIT(nkCall, base.info, boolType)
         isn.add newSymNode(iss)
@@ -293,7 +295,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
 proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
   for bucket in 0..<g.methods.len:
     var relevantCols = initIntSet()
-    for col in 1..<g.methods[bucket].methods[0].typ.len:
+    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
diff --git a/compiler/concepts.nim b/compiler/concepts.nim
index 01bfc542d..e44b0d1cd 100644
--- a/compiler/concepts.nim
+++ b/compiler/concepts.nim
@@ -105,18 +105,19 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
       #m.inferred.setLen oldLen
       #echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
     else:
-      if a.kind == tyTypeDesc and f.len == a.len:
-        for i in 0..<a.len:
-          if not matchType(c, f[i], a[i], m): return false
-        return true
+      if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
+        if f.hasElementType:
+          result = matchType(c, f.elementType, a.elementType, m)
+        else:
+          result = true # both lack it
       else:
         result = false
 
   of tyGenericInvocation:
     result = false
-    if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
-      if sameType(f[0], a[0]) and f.len == a.len-1:
-        for i in 1 ..< f.len:
+    if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
+      if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
+        for i in FirstGenericParamAt ..< f.kidsLen:
           if not matchType(c, f[i], a[i], m): return false
         return true
   of tyGenericParam:
@@ -126,10 +127,10 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
     else:
       let old = existingBinding(m, f)
       if old == nil:
-        if f.len > 0 and f[0].kind != tyNone:
+        if f.hasElementType and f.elementType.kind != tyNone:
           # also check the generic's constraints:
           let oldLen = m.inferred.len
-          result = matchType(c, f[0], a, m)
+          result = matchType(c, f.elementType, a, m)
           m.inferred.setLen oldLen
           if result:
             when logBindings: echo "A adding ", f, " ", ak
@@ -155,9 +156,9 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
     # modifiers in the concept must be there in the actual implementation
     # too but not vice versa.
     if a.kind == f.kind:
-      result = matchType(c, f[0], a[0], m)
+      result = matchType(c, f.elementType, a.elementType, m)
     elif m.magic == mArrPut:
-      result = matchType(c, f[0], a, m)
+      result = matchType(c, f.elementType, a, m)
     else:
       result = false
   of tyEnum, tyObject, tyDistinct:
@@ -167,7 +168,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
   of tyBool, tyChar, tyInt..tyUInt64:
     let ak = a.skipTypes(ignorableForArgType)
     result = ak.kind == f.kind or ak.kind == tyOrdinal or
-       (ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal)
+       (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
   of tyConcept:
     let oldLen = m.inferred.len
     let oldPotentialImplementation = m.potentialImplementation
@@ -178,10 +179,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
       m.inferred.setLen oldLen
   of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
      tyGenericInst:
+    # ^ XXX Rewrite this logic, it's more complex than it needs to be.
     result = false
     let ak = a.skipTypes(ignorableForArgType - {f.kind})
-    if ak.kind == f.kind and f.len == ak.len:
-      for i in 0..<ak.len:
+    if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
+      for i in 0..<ak.kidsLen:
         if not matchType(c, f[i], ak[i], m): return false
       return true
   of tyOr:
@@ -190,30 +192,30 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
       # say the concept requires 'int|float|string' if the potentialImplementation
       # says 'int|string' that is good enough.
       var covered = 0
-      for i in 0..<f.len:
-        for j in 0..<a.len:
+      for ff in f.kids:
+        for aa in a.kids:
           let oldLenB = m.inferred.len
-          let r = matchType(c, f[i], a[j], m)
+          let r = matchType(c, ff, aa, m)
           if r:
             inc covered
             break
           m.inferred.setLen oldLenB
 
-      result = covered >= a.len
+      result = covered >= a.kidsLen
       if not result:
         m.inferred.setLen oldLen
     else:
       result = false
-      for i in 0..<f.len:
-        result = matchType(c, f[i], a, m)
+      for ff in f.kids:
+        result = matchType(c, ff, a, m)
         if result: break # and remember the binding!
         m.inferred.setLen oldLen
   of tyNot:
     if a.kind == tyNot:
-      result = matchType(c, f[0], a[0], m)
+      result = matchType(c, f.elementType, a.elementType, m)
     else:
       let oldLen = m.inferred.len
-      result = not matchType(c, f[0], a, m)
+      result = not matchType(c, f.elementType, a, m)
       m.inferred.setLen oldLen
   of tyAnything:
     result = true
@@ -334,8 +336,8 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invo
     # we have a match, so bind 'arg' itself to 'concpt':
     bindings.idTablePut(concpt, arg)
     # invocation != nil means we have a non-atomic concept:
-    if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1:
+    if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
       # bind even more generic parameters
       assert invocation.kind == tyGenericInvocation
-      for i in 1 ..< invocation.len:
+      for i in FirstGenericParamAt ..< invocation.kidsLen:
         bindings.idTablePut(invocation[i], arg[i])
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 1511628dd..8ee527952 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -380,7 +380,7 @@ proc genCall(c: var Con; n: PNode) =
   if t != nil: t = t.skipTypes(abstractInst)
   for i in 1..<n.len:
     gen(c, n[i])
-    if t != nil and i < t.len and isOutParam(t[i]):
+    if t != nil and i < t.signatureLen and isOutParam(t[i]):
       # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
       genDef(c, n[i])
   # every call can potentially raise:
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index d87505102..98c6f4b82 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -44,7 +44,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
         handleParam actual[s.position]
       elif (s.owner != nil) and (s.kind == skGenericParam or
            s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
-        handleParam actual[s.owner.typ.len + s.position - 1]
+        handleParam actual[s.owner.typ.signatureLen + s.position - 1]
       else:
         internalAssert c.config, sfGenSym in s.flags or s.kind == skType
         var x = PSym(idTableGet(c.mapping, s))
@@ -116,7 +116,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
     # now that we have working untyped parameters.
     genericParams = if fromHlo: 0
                     else: s.ast[genericParamsPos].len
-    expectedRegularParams = s.typ.len-1
+    expectedRegularParams = s.typ.paramsLen
     givenRegularParams = totalParams - genericParams
   if givenRegularParams < 0: givenRegularParams = 0
 
diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim
index 86f87cd84..c121f5dea 100644
--- a/compiler/expanddefaults.nim
+++ b/compiler/expanddefaults.nim
@@ -87,7 +87,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
   of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
     result = expandDefault(t.skipModifier, info)
   of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
-    if t.len > 0:
+    if t.hasElementType:
       result = expandDefault(t.skipModifier, info)
     else:
       result = newZero(t, info, nkEmpty)
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index b8d6d5f63..a6620de2a 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -221,7 +221,7 @@ proc makePtrType(c: var Con, baseType: PType): PType =
 
 proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
   var addrExp: PNode
-  if op.typ != nil and op.typ.len > 1 and op.typ.firstParamType.kind != tyVar:
+  if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
     addrExp = dest
   else:
     addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
@@ -877,7 +877,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
         c.inSpawn.dec
 
       let parameters = n[0].typ
-      let L = if parameters != nil: parameters.len else: 0
+      let L = if parameters != nil: parameters.signatureLen else: 0
 
       when false:
         var isDangerous = false
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 2987a04a8..a3ca88dd5 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -56,10 +56,10 @@ proc destructorOverridden(g: ModuleGraph; t: PType): bool =
   op != nil and sfOverridden in op.flags
 
 proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  for i in 0..<t.len:
+  for i, a in t.ikids:
     let lit = lowerings.newIntLit(c.g, x.info, i)
-    let b = if c.kind == attachedTrace: y else: y.at(lit, t[i])
-    fillBody(c, t[i], body, x.at(lit, t[i]), b)
+    let b = if c.kind == attachedTrace: y else: y.at(lit, a)
+    fillBody(c, a, body, x.at(lit, a), b)
 
 proc dotField(x: PNode, f: PSym): PNode =
   result = newNodeI(nkDotExpr, x.info, 2)
@@ -221,15 +221,15 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
     illFormedAstLocal(n, c.g.config)
 
 proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
-  if t.len > 0 and t.baseClass != nil:
+  if t.baseClass != nil:
     fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, x, y)
   fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
 
 proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
   var hasCase = isCaseObj(t.n)
   var obj = t
-  while obj.len > 0 and obj[0] != nil:
-    obj = skipTypes(obj[0], abstractPtrs)
+  while obj.baseClass != nil:
+    obj = skipTypes(obj.baseClass, abstractPtrs)
     hasCase = hasCase or isCaseObj(obj.n)
 
   if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
@@ -288,7 +288,7 @@ proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
 
 proc getCycleParam(c: TLiftCtx): PNode =
   assert c.kind in {attachedAsgn, attachedDup}
-  if c.fn.typ.len == 4:
+  if c.fn.typ.signatureLen == 4:
     result = c.fn.typ.n.lastSon
     assert result.kind == nkSym
     assert result.sym.name.s == "cyclic"
@@ -308,9 +308,9 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
     result.add x
   if y != nil:
     result.add y
-  if op.typ.len == 4:
+  if op.typ.signatureLen == 4:
     assert y != nil
-    if c.fn.typ.len == 4:
+    if c.fn.typ.signatureLen == 4:
       result.add getCycleParam(c)
     else:
       # assume the worst: A cycle is created:
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 01b79c1bc..4aab216c7 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -63,7 +63,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
     if x.kind == tyTuple:
       n.typ = t
       for i in 0..<n.len:
-        if i >= x.len: globalError conf, n.info, "invalid field at index " & $i
+        if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
         else: annotateType(n[i], x[i], conf)
     elif x.kind == tyProc and x.callConv == ccClosure:
       n.typ = t
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 9b386cf5c..e2d45a388 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -660,7 +660,7 @@ proc isTrival(caller: PNode): bool {.inline.} =
 proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
   let a = skipConvCastAndClosure(n)
   let op = a.typ
-  let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil
+  let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil
   # assume indirect calls are taken here:
   if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
       not isTrival(caller) and
@@ -695,7 +695,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
         markGcUnsafe(tracked, a)
       elif tfNoSideEffect notin op.flags:
         markSideEffect(tracked, a, n.info)
-  let paramType = if formals != nil and argIndex < formals.len: formals[argIndex] else: nil
+  let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil
   if paramType != nil and paramType.kind in {tyVar}:
     invalidateFacts(tracked.guards, n)
     if n.kind == nkSym and isLocalSym(tracked, n.sym):
@@ -979,7 +979,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
       # may not look like an assignment, but it is:
       let arg = n[1]
       initVarViaNew(tracked, arg)
-      if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.elementType.flags != {}:
+      if arg.typ.hasElementType and {tfRequiresInit} * arg.typ.elementType.flags != {}:
         if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
             n[2].intVal == 0:
           # var s: seq[notnil];  newSeq(s, 0)  is a special case!
@@ -988,7 +988,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
           message(tracked.config, arg.info, warnProveInit, $arg)
 
       # check required for 'nim check':
-      if n[1].typ.len > 0:
+      if n[1].typ.hasElementType:
         createTypeBoundOps(tracked, n[1].typ.elementType, n.info)
         createTypeBoundOps(tracked, n[1].typ, n.info)
         # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
@@ -1012,11 +1012,11 @@ proc trackCall(tracked: PEffects; n: PNode) =
           n[0].sym = op
 
   if op != nil and op.kind == tyProc:
-    for i in 1..<min(n.safeLen, op.len):
+    for i in 1..<min(n.safeLen, op.signatureLen):
       let paramType = op[i]
       case paramType.kind
       of tySink:
-        createTypeBoundOps(tracked, paramType[0], n.info)
+        createTypeBoundOps(tracked, paramType.elementType, n.info)
         checkForSink(tracked, n[i])
       of tyVar:
         if isOutParam(paramType):
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 63941419c..1510a3f9c 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -37,7 +37,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
 
   for inst in typeInstCacheItems(g, genericTyp.sym):
     if inst.id == key.id: return inst
-    if inst.len < key.len:
+    if inst.kidsLen < key.kidsLen:
       # XXX: This happens for prematurely cached
       # types such as Channel[empty]. Why?
       # See the notes for PActor in handleGenericInvocation
@@ -47,7 +47,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
       continue
 
     block matchType:
-      for j in 1..<len(key):
+      for j in FirstGenericParamAt..<key.kidsLen:
         # XXX sameType is not really correct for nested generics?
         if not compareTypes(inst[j], key[j],
                             flags = {ExactGenericParams, PickyCAliases}):
@@ -366,7 +366,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     when defined(reportCacheHits):
       echo "Generic instantiation cached ", typeToString(result), " for ", typeToString(t)
     return
-  for i in 1..<t.len:
+  for i in FirstGenericParamAt..<t.kidsLen:
     var x = t[i]
     if x.kind in {tyGenericParam}:
       x = lookupTypeVar(cl, x)
@@ -404,7 +404,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
 
   cl.typeMap = newTypeMapLayer(cl)
 
-  for i in 1..<t.len:
+  for i in FirstGenericParamAt..<t.kidsLen:
     var x = replaceTypeVarsT(cl):
       if header[i].kind == tyGenericInst:
         t[i]
@@ -415,7 +415,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     propagateToOwner(header, x)
     cl.typeMap.put(body[i-1], x)
 
-  for i in 1..<t.len:
+  for i in FirstGenericParamAt..<t.kidsLen:
     # if one of the params is not concrete, we cannot do anything
     # but we already raised an error!
     rawAddSon(result, header[i], propagateHasAsgn = false)
@@ -481,11 +481,11 @@ proc eraseVoidParams*(t: PType) =
   if t.returnType != nil and t.returnType.kind == tyVoid:
     t.setReturnType nil
 
-  for i in 1..<t.len:
+  for i in FirstParamAt..<t.signatureLen:
     # don't touch any memory unless necessary
     if t[i].kind == tyVoid:
       var pos = i
-      for j in i+1..<t.len:
+      for j in i+1..<t.signatureLen:
         if t[j].kind != tyVoid:
           t[pos] = t[j]
           t.n[pos] = t.n[j]
@@ -495,8 +495,7 @@ proc eraseVoidParams*(t: PType) =
       break
 
 proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
-  for i in 0..<t.len:
-    let p = t[i]
+  for i, p in t.ikids:
     if p == nil: continue
     let skipped = p.skipIntLit(idgen)
     if skipped != p:
@@ -620,7 +619,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     bailout()
     result = instCopyType(cl, t)
     idTablePut(cl.localCache, t, result)
-    for i in 1..<result.len:
+    for i in FirstGenericParamAt..<result.kidsLen:
       result[i] = replaceTypeVarsT(cl, result[i])
     propagateToOwner(result, result.last)
 
@@ -633,15 +632,15 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       #if not cl.allowMetaTypes:
       idTablePut(cl.localCache, t, result)
 
-      for i in 0..<result.len:
-        if result[i] != nil:
-          if result[i].kind == tyGenericBody:
+      for i, resulti in result.ikids:
+        if resulti != nil:
+          if resulti.kind == tyGenericBody:
             localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
               "cannot instantiate '" &
               typeToString(result[i], preferDesc) &
               "' inside of type definition: '" &
               t.owner.name.s & "'; Maybe generic arguments are missing?")
-          var r = replaceTypeVarsT(cl, result[i])
+          var r = replaceTypeVarsT(cl, resulti)
           if result.kind == tyObject:
             # carefully coded to not skip the precious tyGenericInst:
             let r2 = r.skipTypes({tyAlias, tySink, tyOwned})
@@ -654,7 +653,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc))
       case result.kind
       of tyArray:
-        let idx = result[0]
+        let idx = result.indexType
         internalAssert cl.c.config, idx.kind != tyStatic
 
       of tyObject, tyTuple:
@@ -667,7 +666,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         skipIntLiteralParams(result, cl.c.idgen)
 
       of tyRange:
-        result[0] = result[0].skipTypes({tyStatic, tyDistinct})
+        result.setIndexType result.indexType.skipTypes({tyStatic, tyDistinct})
 
       else: discard
     else:
@@ -676,7 +675,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result = t
 
       # Slow path, we have some work to do
-      if t.kind == tyRef and t.len > 0 and t.elementType.kind == tyObject and t.elementType.n != nil:
+      if t.kind == tyRef and t.hasElementType and t.elementType.kind == tyObject and t.elementType.n != nil:
         discard replaceObjBranches(cl, t.elementType.n)
 
       elif result.n != nil and t.kind == tyObject:
@@ -712,7 +711,7 @@ when false:
     popInfoContext(p.config)
 
 proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
-  if t != nil and t.len > 0 and t.baseClass != nil:
+  if t != nil and t.baseClass != nil:
     let b = skipTypes(t.baseClass, skipPtrs)
     recomputeFieldPositions(b, b.n, currPosition)
   case obj.kind
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index ca1b74604..a058ffee9 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -104,8 +104,8 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
 
   case t.kind
   of tyGenericInvocation:
-    for i in 0..<t.len:
-      c.hashType t[i], flags, conf
+    for a in t.kids:
+      c.hashType a, flags, conf
   of tyDistinct:
     if CoDistinct in flags:
       if t.sym != nil: c.hashSym(t.sym)
@@ -121,8 +121,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
       # We cannot trust the `lastSon` to hold a properly populated and unique
       # value for each instantiation, so we hash the generic parameters here:
       let normalizedType = t.skipGenericAlias
-      for i in 0..<normalizedType.len - 1:
-        c.hashType t[i], flags, conf
+      c.hashType normalizedType.genericHead, flags, conf
+      for _, a in normalizedType.genericInstParams:
+        c.hashType a, flags, conf
     else:
       c.hashType t.skipModifier, flags, conf
   of tyAlias, tySink, tyUserTypeClasses, tyInferred:
@@ -143,8 +144,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
       let inst = t.typeInst
       t.typeInst = nil
       assert inst.kind == tyGenericInst
-      for i in 0..<inst.len - 1:
-        c.hashType inst[i], flags, conf
+      c.hashType inst.genericHead, flags, conf
+      for _, a in inst.genericInstParams:
+        c.hashType a, flags, conf
       t.typeInst = inst
       return
     c &= char(t.kind)
@@ -184,16 +186,16 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
           c &= ".empty"
     else:
       c &= t.id
-    if t.len > 0 and t.baseClass != nil:
+    if t.hasElementType and t.baseClass != nil:
       hashType c, t.baseClass, flags, conf
   of tyRef, tyPtr, tyVar:
     c &= char(t.kind)
-    if t.len > 0:
+    if t.hasElementType:
       c.hashType t.elementType, flags, conf
     if tfVarIsPtr in t.flags: c &= ".varisptr"
   of tyGenericBody:
     c &= char(t.kind)
-    if t.len > 0:
+    if t.hasElementType:
       c.hashType t.typeBodyImpl, flags, conf
   of tyFromExpr:
     c &= char(t.kind)
@@ -201,15 +203,14 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
   of tyTuple:
     c &= char(t.kind)
     if t.n != nil and CoType notin flags:
-      assert(t.n.len == t.len)
       for i in 0..<t.n.len:
         assert(t.n[i].kind == nkSym)
         c &= t.n[i].sym.name.s
         c &= ':'
-        c.hashType(t[i], flags+{CoIgnoreRange}, conf)
+        c.hashType(t.n[i].sym.typ, flags+{CoIgnoreRange}, conf)
         c &= ','
     else:
-      for i in 0..<t.len: c.hashType t[i], flags+{CoIgnoreRange}, conf
+      for a in t.kids: c.hashType a, flags+{CoIgnoreRange}, conf
   of tyRange:
     if CoIgnoreRange notin flags:
       c &= char(t.kind)
@@ -232,7 +233,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
         c &= ','
       c.hashType(t.returnType, flags, conf)
     else:
-      for i in 0..<t.len: c.hashType(t[i], flags, conf)
+      for a in t.signature: c.hashType(a, flags, conf)
     c &= char(t.callConv)
     # purity of functions doesn't have to affect the mangling (which is in fact
     # problematic for HCR - someone could have cached a pointer to another
@@ -244,10 +245,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
     if tfVarargs in t.flags: c &= ".varargs"
   of tyArray:
     c &= char(t.kind)
-    for i in 0..<t.len: c.hashType(t[i], flags-{CoIgnoreRange}, conf)
+    c.hashType(t.indexType, flags-{CoIgnoreRange}, conf)
+    c.hashType(t.elementType, flags-{CoIgnoreRange}, conf)
   else:
     c &= char(t.kind)
-    for i in 0..<t.len: c.hashType(t[i], flags, conf)
+    for a in t.kids: c.hashType(a, flags, conf)
   if tfNotNil in t.flags and CoType notin flags: c &= "not nil"
 
 when defined(debugSigHashes):
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 94d775e9a..127ed4a68 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -184,11 +184,11 @@ proc checkGeneric(a, b: TCandidate): int =
   let aa = a.callee
   let bb = b.callee
   var winner = 0
-  for i in 1..<min(aa.len, bb.len):
-    var ma = newCandidate(c, bb[i])
-    let tra = typeRel(ma, bb[i], aa[i], {trDontBind})
-    var mb = newCandidate(c, aa[i])
-    let trb = typeRel(mb, aa[i], bb[i], {trDontBind})
+  for aai, bbi in underspecifiedPairs(aa, bb, 1):
+    var ma = newCandidate(c, bbi)
+    let tra = typeRel(ma, bbi, aai, {trDontBind})
+    var mb = newCandidate(c, aai)
+    let trb = typeRel(mb, aai, bbi, {trDontBind})
     if tra == isGeneric and trb == isNone:
       if winner == -1: return 0
       winner = 1
@@ -259,9 +259,9 @@ proc sumGeneric(t: PType): int =
 proc complexDisambiguation(a, b: PType): int =
   # 'a' matches better if *every* argument matches better or equal than 'b'.
   var winner = 0
-  for i in 1..<min(a.len, b.len):
-    let x = a[i].sumGeneric
-    let y = b[i].sumGeneric
+  for ai, bi in underspecifiedPairs(a, b, 1):
+    let x = ai.sumGeneric
+    let y = bi.sumGeneric
     if x != y:
       if winner == 0:
         if x > y: winner = 1
@@ -276,8 +276,8 @@ proc complexDisambiguation(a, b: PType): int =
   result = winner
   when false:
     var x, y: int
-    for i in 1..<a.len: x += a[i].sumGeneric
-    for i in 1..<b.len: y += b[i].sumGeneric
+    for i in 1..<a.len: x += ai.sumGeneric
+    for i in 1..<b.len: y += bi.sumGeneric
     result = x - y
 
 proc writeMatches*(c: TCandidate) =
@@ -375,7 +375,7 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
   of tyOwned:
     # bug #11257: the comparison system.`==`[T: proc](x, y: T) works
     # better without the 'owned' type:
-    if f != nil and f.len > 0 and f[0].skipTypes({tyBuiltInTypeClass, tyOr}).kind == tyProc:
+    if f != nil and f.hasElementType and f.elementType.skipTypes({tyBuiltInTypeClass, tyOr}).kind == tyProc:
       result = t.skipModifier
     else:
       result = t
@@ -468,10 +468,10 @@ proc getObjectTypeOrNil(f: PType): PType =
   if f == nil: return nil
   case f.kind:
   of tyGenericInvocation, tyCompositeTypeClass, tyAlias:
-    if f.len <= 0 or f[0] == nil:
+    if not f.hasElementType or f.elementType == nil:
       result = nil
     else:
-      result = getObjectTypeOrNil(f[0])
+      result = getObjectTypeOrNil(f.elementType)
   of tyGenericInst:
     result = getObjectTypeOrNil(f.skipModifier)
   of tyGenericBody:
@@ -496,8 +496,8 @@ proc getObjectTypeOrNil(f: PType): PType =
 
 proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
   if fGenericOrigin != nil and last.kind == tyGenericInst and
-     last.len-1 == fGenericOrigin.len:
-    for i in 1..<fGenericOrigin.len:
+     last.kidsLen-1 == fGenericOrigin.kidsLen:
+    for i in FirstGenericParamAt..<fGenericOrigin.kidsLen:
       let x = PType(idTableGet(c.bindings, fGenericOrigin[i]))
       if x == nil:
         put(c, fGenericOrigin[i], last[i])
@@ -531,7 +531,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
   while r != nil:
     case r.kind
     of tyGenericInvocation:
-      r = r[0]
+      r = r.genericHead
     of tyRef:
       inc ptrs
       skipped = skippedRef
@@ -581,12 +581,12 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
   result = isNone
   if sameType(f, a):
     result = isEqual
-  elif a.len == f.len:
+  elif sameTupleLengths(a, f):
     result = isEqual
     let firstField = if f.kind == tyTuple: 0
                      else: 1
-    for i in firstField..<f.len:
-      var m = typeRel(c, f[i], a[i])
+    for _, ff, aa in tupleTypePairs(f, a):
+      var m = typeRel(c, ff, aa)
       if m < isSubtype: return isNone
       result = minRel(result, m)
     if f.n != nil and a.n != nil:
@@ -609,7 +609,7 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
     (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})) or
     isOutParam(f) != isOutParam(a)
 
-proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
+proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
   ## For example we have:
   ##   ```nim
   ##   proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ...
@@ -669,7 +669,7 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   case a.kind
   of tyProc:
-    if f.len != a.len: return
+    if f.signatureLen != a.signatureLen: return
     result = isEqual      # start with maximum; also correct for no
                           # params at all
 
@@ -1193,7 +1193,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       # being passed as parameters
       return isNone
   else: discard
-  
+
   case f.kind
   of tyEnum:
     if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
index 2ba644658..9b5ad009d 100644
--- a/compiler/varpartitions.nim
+++ b/compiler/varpartitions.nim
@@ -706,7 +706,7 @@ proc traverse(c: var Partitions; n: PNode) =
     for child in n: traverse(c, child)
 
     let parameters = n[0].typ
-    let L = if parameters != nil: parameters.len else: 0
+    let L = if parameters != nil: parameters.signatureLen else: 0
     let m = getMagic(n)
 
     if m == mEnsureMove and n[1].kind == nkSym:
@@ -857,7 +857,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
     for child in n: computeLiveRanges(c, child)
 
     let parameters = n[0].typ
-    let L = if parameters != nil: parameters.len else: 0
+    let L = if parameters != nil: parameters.signatureLen else: 0
 
     for i in 1..<n.len:
       let it = n[i]
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 6b00ff9d3..7063a7268 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -2264,7 +2264,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var typ = regs[rb].node.typ
       internalAssert c.config, typ != nil
-      while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.skipModifier
+      while typ.kind == tyTypeDesc and typ.hasElementType: typ = typ.skipModifier
       createStr regs[ra]
       regs[ra].node.strVal = typ.typeToString(preferExported)
 
@@ -2280,11 +2280,11 @@ proc execute(c: PCtx, start: int): PNode =
 proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
   c.loopIterations = c.config.maxLoopIterationsVM
   if sym.kind in routineKinds:
-    if sym.typ.len-1 != args.len:
+    if sym.typ.paramsLen != args.len:
       result = nil
       localError(c.config, sym.info,
         "NimScript: expected $# arguments, but got $#" % [
-        $(sym.typ.len-1), $args.len])
+        $(sym.typ.paramsLen), $args.len])
     else:
       let start = genProc(c, sym)
 
@@ -2296,8 +2296,8 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
       if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
         putIntoReg(tos.slots[0], getNullValue(sym.typ.returnType, sym.info, c.config))
       # XXX We could perform some type checking here.
-      for i in 1..<sym.typ.len:
-        putIntoReg(tos.slots[i], args[i-1])
+      for i in 0..<sym.typ.paramsLen:
+        putIntoReg(tos.slots[i+1], args[i])
 
       result = rawExecute(c, start, tos).regToNode
   else:
@@ -2435,7 +2435,7 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
   let gp = macroSym.ast[genericParamsPos]
   for i in 0..<gp.len:
     let genericParam = gp[i].sym
-    let posInCall = macroSym.typ.len + i
+    let posInCall = macroSym.typ.signatureLen + i
     if posInCall < call.len:
       yield (genericParam, call[posInCall])
 
@@ -2458,9 +2458,10 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
 
   # immediate macros can bypass any type and arity checking so we check the
   # arity here too:
-  if sym.typ.len > n.safeLen and sym.typ.len > 1:
+  let sl = sym.typ.signatureLen
+  if sl > n.safeLen and sl > 1:
     globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
-        n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
+        n.renderTree, $(n.safeLen-1), $(sym.typ.paramsLen)])
 
   setupGlobalCtx(module, g, idgen)
   var c = PCtx g.vm
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 6d0e03813..e293ab7b2 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -49,13 +49,13 @@ proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; inf
                        inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
   result.add atomicTypeX(cache, name, m, t, info, idgen)
-  for i in 0..<t.len:
-    if t[i] == nil:
+  for a in t.kids:
+    if a == nil:
       let voidt = atomicTypeX(cache, "void", mVoid, t, info, idgen)
       voidt.typ = newType(tyVoid, idgen, t.owner)
       result.add voidt
     else:
-      result.add mapTypeToAstX(cache, t[i], info, idgen, inst)
+      result.add mapTypeToAstX(cache, a, info, idgen, inst)
 
 proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
   if n.kind == nkSym:
@@ -74,9 +74,9 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   var allowRecursion = allowRecursionX
   template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen)
   template atomicType(s): untyped = atomicTypeX(s, info)
-  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst)
-  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true)
-  template mapTypeToAst(t,i,info): untyped =
+  template mapTypeToAst(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst)
+  template mapTypeToAstR(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true)
+  template mapTypeToAst(t, i, info): untyped =
     if i<t.len and t[i]!=nil: mapTypeToAstX(cache, t[i], info, idgen, inst)
     else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
@@ -129,8 +129,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       result = atomicType("typeDesc", mTypeDesc)
   of tyGenericInvocation:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    for i in 0..<t.len:
-      result.add mapTypeToAst(t[i], info)
+    for a in t.kids:
+      result.add mapTypeToAst(a, info)
   of tyGenericInst:
     if inst:
       if allowRecursion:
@@ -141,8 +141,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
         result = newNodeX(nkBracketExpr)
         #result.add mapTypeToAst(t.last, info)
         result.add mapTypeToAst(t.genericHead, info)
-        for i in 1..<t.len-1:
-          result.add mapTypeToAst(t[i], info)
+        for _, a in t.genericInstParams:
+          result.add mapTypeToAst(a, info)
     else:
       result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
       # keep original type info for getType calls on the output node:
@@ -243,7 +243,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
         fp.add newNodeI(nkEmpty, info)
       else:
         fp.add mapTypeToAst(t.returnType, t.n[0].info)
-      for i in 1..<t.len:
+      for i in FirstParamAt..<t.kidsLen:
         fp.add newIdentDefs(t.n[i], t[i])
       result.add fp
       result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 9667daa3d..9bbea2b08 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -620,7 +620,7 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
   for i in 0..<n.len:
     var r: TRegister = x+i
     c.gen(n[i], r, {gfIsParam})
-    if i >= fntyp.len:
+    if i >= fntyp.signatureLen:
       internalAssert c.config, tfVarargs in fntyp.flags
       c.gABx(n, opcSetType, r, c.genType(n[i].typ))
   if dest < 0:
@@ -1874,7 +1874,7 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
     genArrAccessOpcode(c, n, dest, opc, flags)
 
 proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
-  if t != nil and t.len > 0 and t.baseClass != nil:
+  if t != nil and t.baseClass != nil:
     let b = skipTypes(t.baseClass, skipPtrs)
     getNullValueAux(b, b.n, result, conf, currPosition)
   case obj.kind
@@ -1929,8 +1929,8 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
       result.add getNullValue(elemType(t), info, conf)
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
-    for i in 0..<t.len:
-      result.add getNullValue(t[i], info, conf)
+    for a in t.kids:
+      result.add getNullValue(a, info, conf)
   of tySet:
     result = newNodeIT(nkCurly, info, t)
   of tySequence, tyOpenArray:
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index d80010877..0391a5e7b 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -82,11 +82,11 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
       s.add("]")
   of tyTuple:
     s.add("{")
-    for i in 0..<t.len:
+    for i, ti in t.ikids:
       if i > 0: s.add(", ")
       s.add("\"Field" & $i)
       s.add("\": ")
-      storeAny(s, t[i], a[i].skipColon, stored, conf)
+      storeAny(s, ti, a[i].skipColon, stored, conf)
     s.add("}")
   of tyObject:
     s.add("{")
@@ -208,11 +208,12 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkTupleConstr)
     var i = 0
+    let tupleLen = t.kidsLen
     while p.kind != jsonObjectEnd and p.kind != jsonEof:
       if p.kind != jsonString:
         raiseParseErr(p, "string expected for a field name")
       next(p)
-      if i >= t.len:
+      if i >= tupleLen:
         raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
       result.add loadAny(p, t[i], tab, cache, conf, idgen)
       inc i