summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-12-14 16:25:34 +0100
committerGitHub <noreply@github.com>2023-12-14 16:25:34 +0100
commit6ed33b6d61518c8de94b07c7ffb68fcc6f839897 (patch)
tree037796db0c0cb1efee88ec582442b719b422699d
parent1b7b0d69db41b3c5a27cca643d66c0acabbe41df (diff)
downloadNim-6ed33b6d61518c8de94b07c7ffb68fcc6f839897.tar.gz
type graph refactor; part 3 (#23064)
-rw-r--r--compiler/ast.nim82
-rw-r--r--compiler/astalgo.nim69
-rw-r--r--compiler/docgen.nim3
-rw-r--r--compiler/expanddefaults.nim2
-rw-r--r--compiler/ic/ic.nim2
-rw-r--r--compiler/isolation_check.nim16
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/sempass2.nim2
-rw-r--r--compiler/semstmts.nim12
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/sigmatch.nim30
-rw-r--r--compiler/sizealignoffsetimpl.nim11
-rw-r--r--compiler/typeallowed.nim27
-rw-r--r--compiler/types.nim246
-rw-r--r--compiler/varpartitions.nim3
-rw-r--r--compiler/vmdeps.nim2
-rw-r--r--tests/typerel/tregionptrs.nim16
17 files changed, 257 insertions, 270 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index ab46a02d6..c880cb651 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1199,9 +1199,6 @@ proc discardSons*(father: PNode)
 proc len*(n: PNode): int {.inline.} =
   result = n.sons.len
 
-proc len*(n: PType): int {.inline.} =
-  result = n.sons.len
-
 proc safeLen*(n: PNode): int {.inline.} =
   ## works even for leaves.
   if n.kind in {nkNone..nkNilLit}: result = 0
@@ -1576,11 +1573,74 @@ proc `$`*(s: PSym): string =
   else:
     result = "<nil>"
 
-iterator items*(t: PType): PType =
+when false:
+  iterator items*(t: PType): PType =
+    for i in 0..<t.sons.len: yield t.sons[i]
+
+  iterator pairs*(n: PType): tuple[i: int, n: PType] =
+    for i in 0..<n.sons.len: yield (i, n.sons[i])
+
+when true:
+  proc len*(n: PType): int {.inline.} =
+    result = n.sons.len
+
+proc sameTupleLengths*(a, b: PType): bool {.inline.} =
+  result = a.sons.len == b.sons.len
+
+iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
+  for i in 0 ..< a.sons.len:
+    yield (i, a.sons[i], b.sons[i])
+
+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:
+    yield (a.sons[i], b.sons[i])
+
+proc signatureLen*(t: PType): int {.inline.} =
+  result = t.sons.len
+
+proc kidsLen*(t: PType): int {.inline.} =
+  result = t.sons.len
+
+proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0
+
+proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0
+proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0
+proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1
+
+iterator genericInstParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len-1:
+    yield (i!=1, t.sons[i])
+
+iterator genericInvocationParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len:
+    yield (i!=1, t.sons[i])
+
+iterator genericBodyParams*(t: PType): (bool, PType) =
+  for i in 0..<t.sons.len-1:
+    yield (i!=0, t.sons[i])
+
+iterator userTypeClassInstParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len-1:
+    yield (i!=1, t.sons[i])
+
+iterator ikids*(t: PType): (int, PType) =
+  for i in 0..<t.sons.len: yield (i, t.sons[i])
+
+const
+  FirstParamAt* = 1
+
+iterator paramTypes*(t: PType): (int, PType) =
+  for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
+
+template paramTypeToNodeIndex*(x: int): int = x
+
+iterator kids*(t: PType): PType =
   for i in 0..<t.sons.len: yield t.sons[i]
 
-iterator pairs*(n: PType): tuple[i: int, n: PType] =
-  for i in 0..<n.sons.len: yield (i, n.sons[i])
+iterator signature*(t: PType): PType =
+  # yields return type + parameter types
+  for i in 0..<t.sons.len: yield t.sons[i]
 
 proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
   let id = nextTypeId idgen
@@ -1624,8 +1684,8 @@ proc assignType*(dest, src: PType) =
       mergeLoc(dest.sym.loc, src.sym.loc)
     else:
       dest.sym = src.sym
-  newSons(dest, src.len)
-  for i in 0..<src.len: dest[i] = src[i]
+  newSons(dest, src.sons.len)
+  for i in 0..<src.sons.len: dest[i] = src[i]
 
 proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
   result = newType(t.kind, idgen, owner)
@@ -1705,7 +1765,7 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
   ## same as skipTypes but handles 'nil'
   result = t
   while result != nil and result.kind in kinds:
-    if result.len == 0: return nil
+    if result.sons.len == 0: return nil
     result = last(result)
 
 proc isGCedMem*(t: PType): bool {.inline.} =
@@ -2084,7 +2144,7 @@ proc findUnresolvedStatic*(n: PNode): PNode =
     return n
   if n.typ != nil and n.typ.kind == tyTypeDesc:
     let t = skipTypes(n.typ, {tyTypeDesc})
-    if t.kind == tyGenericParam and t.len == 0:
+    if t.kind == tyGenericParam and not t.genericParamHasConstraints:
       return n
   for son in n:
     let n = son.findUnresolvedStatic
@@ -2145,7 +2205,7 @@ proc newProcType*(info: TLineInfo; idgen: IdGenerator; owner: PSym): PType =
   result.n.add newNodeI(nkEffectList, info)
 
 proc addParam*(procType: PType; param: PSym) =
-  param.position = procType.len-1
+  param.position = procType.sons.len-1
   procType.n.add newSymNode(param)
   rawAddSon(procType, param.typ)
 
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 9b3a42ebe..238aa6776 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -37,15 +37,6 @@ proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
 proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
 proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
 
-proc typekinds*(t: PType) {.deprecated.} =
-  var t = t
-  var s = ""
-  while t != nil and t.len > 0:
-    s.add $t.kind
-    s.add " "
-    t = t.last
-  echo s
-
 template debug*(x: PSym|PType|PNode) {.deprecated.} =
   when compiles(c.config):
     debug(c.config, x)
@@ -337,21 +328,18 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
     sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
         strutils.toHex(cast[int](n), sizeof(n) * 2))]
   else:
-    if n.len > 0:
-      sonsRope = rope("[")
-      for i in 0..<n.len:
-        if i > 0: sonsRope.add(",")
-        sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i],
-            marker, indent + 4, maxRecDepth - 1)])
-      sonsRope.addf("$N$1]", [rspaces(indent + 2)])
-    else:
-      sonsRope = rope("null")
+    sonsRope = rope("[")
+    for i, a in n.ikids:
+      if i > 0: sonsRope.add(",")
+      sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, a,
+          marker, indent + 4, maxRecDepth - 1)])
+    sonsRope.addf("$N$1]", [rspaces(indent + 2)])
 
     let istr = rspaces(indent + 2)
     result = rope("{")
     result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
     result.addf("$N$1\"sym\": $2",  [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
-    result.addf("$N$1\"n\": $2",     [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
+    result.addf("$N$1\"n\": $2",    [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
     if card(n.flags) > 0:
       result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
     result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)])
@@ -573,14 +561,12 @@ proc value(this: var DebugPrinter; value: PType) =
     this.key "n"
     this.value value.n
 
-  if value.len > 0:
-    this.key "sons"
-    this.openBracket
-    for i in 0..<value.len:
-      this.value value[i]
-      if i != value.len - 1:
-        this.comma
-    this.closeBracket
+  this.key "sons"
+  this.openBracket
+  for i, a in value.ikids:
+    if i > 0: this.comma
+    this.value a
+  this.closeBracket
 
   if value.n != nil:
     this.key "n"
@@ -649,30 +635,33 @@ proc value(this: var DebugPrinter; value: PNode) =
 
 
 proc debug(n: PSym; conf: ConfigRef) =
-  var this: DebugPrinter
-  this.visited = initTable[pointer, int]()
-  this.renderSymType = true
-  this.useColor = not defined(windows)
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: true,
+    useColor: not defined(windows)
+  )
   this.value(n)
   echo($this.res)
 
 proc debug(n: PType; conf: ConfigRef) =
-  var this: DebugPrinter
-  this.visited = initTable[pointer, int]()
-  this.renderSymType = true
-  this.useColor = not defined(windows)
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: true,
+    useColor: not defined(windows)
+  )
   this.value(n)
   echo($this.res)
 
 proc debug(n: PNode; conf: ConfigRef) =
-  var this: DebugPrinter
-  this.visited = initTable[pointer, int]()
-  #this.renderSymType = true
-  this.useColor = not defined(windows)
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: false,
+    useColor: not defined(windows)
+  )
   this.value(n)
   echo($this.res)
 
-proc nextTry(h, maxHash: Hash): Hash =
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = ((5 * h) + 1) and maxHash
   # For any initial h in range(maxHash), repeating that maxHash times
   # generates each int in range(maxHash) exactly once (see any text on
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 29eeced9b..b53307ee0 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -1199,8 +1199,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
         var param = %{"name": %($genericParam)}
         if genericParam.sym.typ.len > 0:
           param["types"] = newJArray()
-        for kind in genericParam.sym.typ:
-          param["types"].add %($kind)
+        param["types"].add %($genericParam.sym.typ.elementType)
         result.json["signature"]["genericParams"].add param
   if optGenIndex in d.conf.globalOptions:
     genItem(d, n, nameNode, k, kForceExport)
diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim
index 395d31cc8..86f87cd84 100644
--- a/compiler/expanddefaults.nim
+++ b/compiler/expanddefaults.nim
@@ -118,7 +118,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
     expandDefaultObj(t, info, result)
   of tyTuple:
     result = newZero(t, info, nkTupleConstr)
-    for it in t:
+    for it in t.kids:
       result.add expandDefault(it, info)
   of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
     result = newZero(t, info, nkBracket)
diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim
index 0085ea748..a7d3ed81c 100644
--- a/compiler/ic/ic.nim
+++ b/compiler/ic/ic.nim
@@ -370,7 +370,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
       paddingAtEnd: t.paddingAtEnd)
     storeNode(p, t, n)
     p.typeInst = t.typeInst.storeType(c, m)
-    for kid in items t:
+    for kid in kids t:
       p.types.add kid.storeType(c, m)
     c.addMissing t.sym
     p.sym = t.sym.safeItemId(c, m)
diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim
index 08a2cc604..17fbde29e 100644
--- a/compiler/isolation_check.nim
+++ b/compiler/isolation_check.nim
@@ -54,14 +54,14 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool =
   of tyObject:
     if isFinal(ret):
       result = canAliasN(arg, ret.n, marker)
-      if not result and ret.len > 0 and ret[0] != nil:
-        result = canAlias(arg, ret[0], marker)
+      if not result and ret.baseClass != nil:
+        result = canAlias(arg, ret.baseClass, marker)
     else:
       result = true
   of tyTuple:
     result = false
-    for i in 0..<ret.len:
-      result = canAlias(arg, ret[i], marker)
+    for r in ret.kids:
+      result = canAlias(arg, r, marker)
       if result: break
   of tyArray, tySequence, tyDistinct, tyGenericInst,
      tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
@@ -124,9 +124,11 @@ proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
     if result == NotFound: result = containsDangerousRefAux(t.n, marker)
   of tyGenericInst, tyDistinct, tyAlias, tySink:
     result = containsDangerousRefAux(skipModifier(t), marker)
-  of tyArray, tySet, tyTuple, tySequence:
-    for i in 0..<t.len:
-      result = containsDangerousRefAux(t[i], marker)
+  of tyArray, tySet, tySequence:
+    result = containsDangerousRefAux(t.elementType, marker)
+  of tyTuple:
+    for a in t.kids:
+      result = containsDangerousRefAux(a, marker)
       if result == Found: return result
   else:
     discard
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index c066e3a7b..f3d661ff1 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -468,7 +468,7 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
     result = newTypeS(tyOr, c)
     template addOr(t1) =
       if t1.kind == tyOr:
-        for x in t1: result.rawAddSon x
+        for x in t1.kids: result.rawAddSon x
       else:
         result.rawAddSon t1
     addOr(t1)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 8fc218955..36611fd27 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -1421,7 +1421,7 @@ proc track(tracked: PEffects, n: PNode) =
 proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
   if spec.typ.kind == tyOr:
     result = false
-    for t in spec.typ:
+    for t in spec.typ.kids:
       if safeInheritanceDiff(g.excType(real), t) <= 0:
         return true
   else:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 22e863c5c..eacda7f9b 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -45,7 +45,7 @@ proc hasEmpty(typ: PType): bool =
     result = typ.elementType.kind == tyEmpty
   elif typ.kind == tyTuple:
     result = false
-    for s in typ:
+    for s in typ.kids:
       result = result or hasEmpty(s)
   else:
     result = false
@@ -1362,7 +1362,7 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
     of tyArray:
       return traverseSubTypes(c, t.elementType)
     of tyProc:
-      for subType in t:
+      for subType in t.signature:
         if subType != nil:
           subresult traverseSubTypes(c, subType)
       if result:
@@ -1395,11 +1395,11 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
     of tyUserTypeClass, tyUserTypeClassInst:
       error("non-invariant type parameters are not supported in concepts")
     of tyTuple:
-      for fieldType in t:
+      for fieldType in t.kids:
         subresult traverseSubTypes(c, fieldType)
     of tyPtr, tyRef, tyVar, tyLent:
-      if t.base.kind == tyGenericParam: return true
-      return traverseSubTypes(c, t.base)
+      if t.elementType.kind == tyGenericParam: return true
+      return traverseSubTypes(c, t.elementType)
     of tyDistinct, tyAlias, tySink, tyOwned:
       return traverseSubTypes(c, t.skipModifier)
     of tyGenericInst:
@@ -2086,7 +2086,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
     if c.config.backend == backendCpp:
       if s.typ.len < 2 and not isCtor:
         localError(c.config, n.info, pragmaName & " must have at least one parameter")
-      for son in s.typ:
+      for son in s.typ.signature:
         if son!=nil and son.isMetaType:
           localError(c.config, n.info, pragmaName & " unsupported for generic routine")
       var typ: PType
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 337714142..fe87c2973 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1036,7 +1036,7 @@ proc findEnforcedStaticType(t: PType): PType =
   if t == nil: return nil
   if t.kind == tyStatic: return t
   if t.kind == tyAnd:
-    for s in t:
+    for s in t.kids:
       let t = findEnforcedStaticType(s)
       if t != nil: return t
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 38cc66637..9d26b71a3 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -227,7 +227,7 @@ proc sumGeneric(t: PType): int =
       inc result
     of tyOr:
       var maxBranch = 0
-      for branch in t:
+      for branch in t.kids:
         let branchSum = sumGeneric(branch)
         if branchSum > maxBranch: maxBranch = branchSum
       inc result, maxBranch
@@ -240,11 +240,16 @@ proc sumGeneric(t: PType): int =
       t = t.elementType
       if t.kind == tyEmpty: break
       inc result
-    of tyGenericInvocation, tyTuple, tyProc, tyAnd:
+    of tyGenericInvocation, tyTuple, tyAnd:
       result += ord(t.kind in {tyGenericInvocation, tyAnd})
-      for i in 0..<t.len:
-        if t[i] != nil:
-          result += sumGeneric(t[i])
+      for a in t.kids:
+        if a != nil:
+          result += sumGeneric(a)
+      break
+    of tyProc:
+      result += sumGeneric(t.returnType)
+      for _, a in t.paramTypes:
+        result += sumGeneric(a)
       break
     of tyStatic:
       return sumGeneric(t.skipModifier) + 1
@@ -1146,7 +1151,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     # both int and string must match against number
     # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
     result = isGeneric
-    for branch in a:
+    for branch in a.kids:
       let x = typeRel(c, f, branch, flags + {trDontBind})
       if x == isNone: return isNone
       if x < result: result = x
@@ -1156,7 +1161,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     c.typedescMatched = true
     # seq[Sortable and Iterable] vs seq[Sortable]
     # only one match is enough
-    for branch in a:
+    for branch in a.kids:
       let x = typeRel(c, f, branch, flags + {trDontBind})
       if x != isNone:
         return if x >= isGeneric: isGeneric else: x
@@ -1660,7 +1665,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
   of tyAnd:
     considerPreviousT:
       result = isEqual
-      for branch in f:
+      for branch in f.kids:
         let x = typeRel(c, branch, aOrig, flags)
         if x < isSubtype: return isNone
         # 'and' implies minimum matching result:
@@ -1672,7 +1677,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       result = isNone
       let oldInheritancePenalty = c.inheritancePenalty
       var maxInheritance = 0
-      for branch in f:
+      for branch in f.kids:
         c.inheritancePenalty = 0
         let x = typeRel(c, branch, aOrig, flags)
         maxInheritance = max(maxInheritance, c.inheritancePenalty)
@@ -1686,9 +1691,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       c.inheritancePenalty = oldInheritancePenalty + maxInheritance
   of tyNot:
     considerPreviousT:
-      for branch in f:
-        if typeRel(c, branch, aOrig, flags) != isNone:
-          return isNone
+      if typeRel(c, f.elementType, aOrig, flags) != isNone:
+        return isNone
 
       bindingRet isGeneric
   of tyAnything:
@@ -1699,7 +1703,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       return isGeneric
   of tyBuiltInTypeClass:
     considerPreviousT:
-      let target = f[0]
+      let target = f.genericHead
       let targetKind = target.kind
       var effectiveArgType = a.getObjectTypeOrNil()
       if effectiveArgType == nil: return isNone
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index 7cc11f55f..d114f59da 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -332,8 +332,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
   of tyTuple:
     try:
       var accum = OffsetAccum(maxAlign: 1)
-      for i in 0..<typ.len:
-        let child = typ[i]
+      for i, child in typ.ikids:
         computeSizeAlign(conf, child)
         accum.align(child.align)
         if typ.n != nil: # is named tuple (has field symbols)?
@@ -403,16 +402,16 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.align = szIllegalRecursion
       typ.paddingAtEnd = szIllegalRecursion
   of tyInferred:
-    if typ.len > 0:
+    if typ.hasElementType:
       computeSizeAlign(conf, typ.last)
       typ.size = typ.last.size
       typ.align = typ.last.align
       typ.paddingAtEnd = typ.last.paddingAtEnd
 
   of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
-    computeSizeAlign(conf, typ.last)
-    typ.size = typ.last.size
-    typ.align = typ.last.align
+    computeSizeAlign(conf, typ.skipModifier)
+    typ.size = typ.skipModifier.size
+    typ.align = typ.skipModifier.align
     typ.paddingAtEnd = typ.last.paddingAtEnd
 
   of tyTypeClasses:
diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim
index 04dbc69c5..d226b2e06 100644
--- a/compiler/typeallowed.nim
+++ b/compiler/typeallowed.nim
@@ -96,9 +96,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
         # only closure iterators may be assigned to anything.
         result = t
       let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
-      for i in 1..<t.len:
+      for _, a in t.paramTypes:
         if result != nil: break
-        result = typeAllowedAux(marker, t[i], skParam, c, f-{taIsOpenArray})
+        result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray})
       if result.isNil and t.returnType != nil:
         result = typeAllowedAux(marker, t.returnType, skResult, c, flags)
   of tyTypeDesc:
@@ -179,17 +179,22 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
     result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
   of tySet:
     result = typeAllowedAux(marker, t.elementType, kind, c, flags)
-  of tyObject, tyTuple:
+  of tyObject:
     if kind in {skProc, skFunc, skConst} and
-        t.kind == tyObject and t.baseClass != nil and taIsDefaultField notin flags:
+        t.baseClass != nil and taIsDefaultField notin flags:
       result = t
     else:
       let flags = flags+{taField}
-      for i in 0..<t.len:
-        result = typeAllowedAux(marker, t[i], kind, c, flags)
-        if result != nil: break
+      result = typeAllowedAux(marker, t.baseClass, kind, c, flags)
       if result.isNil and t.n != nil:
         result = typeAllowedNode(marker, t.n, kind, c, flags)
+  of tyTuple:
+    let flags = flags+{taField}
+    for a in t.kids:
+      result = typeAllowedAux(marker, a, kind, c, flags)
+      if result != nil: break
+    if result.isNil and t.n != nil:
+      result = typeAllowedNode(marker, t.n, kind, c, flags)
   of tyEmpty:
     if kind in {skVar, skLet}: result = t
   of tyProxy:
@@ -197,7 +202,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
     # prevent cascading errors:
     result = nil
   of tyOwned:
-    if t.len == 1 and t.skipModifier.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
+    if t.hasElementType and t.skipModifier.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
       result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
     else:
       result = t
@@ -247,14 +252,14 @@ proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind =
      tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic:
     result = classifyViewTypeAux(marker, skipModifier(t))
   of tyFromExpr:
-    if t.len > 0:
+    if t.hasElementType:
       result = classifyViewTypeAux(marker, skipModifier(t))
     else:
       result = noView
   of tyTuple:
     result = noView
-    for i in 0..<t.len:
-      result.combine classifyViewTypeAux(marker, t[i])
+    for a in t.kids:
+      result.combine classifyViewTypeAux(marker, a)
       if result == mutableView: break
   of tyObject:
     result = noView
diff --git a/compiler/types.nim b/compiler/types.nim
index eef85f36a..7d7d3349d 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -68,13 +68,10 @@ template `$`*(typ: PType): string = typeToString(typ)
 # ------------------- type iterator: ----------------------------------------
 type
   TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop
-  TTypeMutator* = proc (t: PType, closure: RootRef): PType {.nimcall.} # copy t and mutate it
   TTypePredicate* = proc (t: PType): bool {.nimcall.}
 
 proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool
   # Returns result of `iter`.
-proc mutateType*(t: PType, iter: TTypeMutator, closure: RootRef): PType
-  # Returns result of `iter`.
 
 type
   TParamsEquality* = enum     # they are equal, but their
@@ -228,8 +225,8 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
     of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred:
       result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
     else:
-      for i in 0..<t.len:
-        result = iterOverTypeAux(marker, t[i], iter, closure)
+      for a in t.kids:
+        result = iterOverTypeAux(marker, a, iter, closure)
         if result: return
       if t.n != nil and t.kind != tyProc: result = iterOverNode(marker, t.n, iter, closure)
 
@@ -278,8 +275,8 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate,
   of tyGenericInst, tyDistinct, tyAlias, tySink:
     result = searchTypeForAux(skipModifier(t), predicate, marker)
   of tyArray, tySet, tyTuple:
-    for i in 0..<t.len:
-      result = searchTypeForAux(t[i], predicate, marker)
+    for a in t.kids:
+      result = searchTypeForAux(a, predicate, marker)
       if result: return
   else:
     discard
@@ -307,7 +304,6 @@ type
 
 proc analyseObjectWithTypeFieldAux(t: PType,
                                    marker: var IntSet): TTypeFieldResult =
-  var res: TTypeFieldResult
   result = frNone
   if t == nil: return
   case t.kind
@@ -315,20 +311,19 @@ proc analyseObjectWithTypeFieldAux(t: PType,
     if t.n != nil:
       if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
         return frEmbedded
-    for i in 0..<t.len:
-      var x = t[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      res = analyseObjectWithTypeFieldAux(x, marker)
-      if res == frEmbedded:
-        return frEmbedded
-      if res == frHeader: result = frHeader
+    var x = t.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    let res = analyseObjectWithTypeFieldAux(x, marker)
+    if res == frEmbedded:
+      return frEmbedded
+    if res == frHeader: result = frHeader
     if result == frNone:
       if isObjectWithTypeFieldPredicate(t): result = frHeader
   of tyGenericInst, tyDistinct, tyAlias, tySink:
     result = analyseObjectWithTypeFieldAux(skipModifier(t), marker)
   of tyArray, tyTuple:
-    for i in 0..<t.len:
-      res = analyseObjectWithTypeFieldAux(t[i], marker)
+    for a in t.kids:
+      let res = analyseObjectWithTypeFieldAux(a, marker)
       if res != frNone:
         return frEmbedded
   else:
@@ -405,9 +400,7 @@ proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PTyp
       if withRef and sameBackendType(t, orig):
         result = true
       elif not containsOrIncl(marker, t.id):
-        for i in 0..<t.len:
-          result = canFormAcycleAux(g, marker, t[i], orig, withRef or t.kind != tyUncheckedArray, hasTrace)
-          if result: return
+        result = canFormAcycleAux(g, marker, t.elementType, orig, withRef or t.kind != tyUncheckedArray, hasTrace)
   of tyObject:
     if withRef and sameBackendType(t, orig):
       result = true
@@ -416,8 +409,8 @@ proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PTyp
       let op = getAttachedOp(g, t.skipTypes({tyRef}), attachedTrace)
       if op != nil and sfOverridden in op.flags:
         hasTrace = true
-      for i in 0..<t.len:
-        result = canFormAcycleAux(g, marker, t[i], orig, withRef, hasTrace)
+      if t.baseClass != nil:
+        result = canFormAcycleAux(g, marker, t.baseClass, orig, withRef, hasTrace)
         if result: return
       if t.n != nil: result = canFormAcycleNode(g, marker, t.n, orig, withRef, hasTrace)
     # Inheritance can introduce cyclic types, however this is not relevant
@@ -426,13 +419,18 @@ proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PTyp
     if tfFinal notin t.flags:
       # damn inheritance may introduce cycles:
       result = true
-  of tyTuple, tySequence, tyArray, tyOpenArray, tyVarargs:
+  of tyTuple:
     if withRef and sameBackendType(t, orig):
       result = true
     elif not containsOrIncl(marker, t.id):
-      for i in 0..<t.len:
-        result = canFormAcycleAux(g, marker, t[i], orig, withRef, hasTrace)
+      for a in t.kids:
+        result = canFormAcycleAux(g, marker, a, orig, withRef, hasTrace)
         if result: return
+  of tySequence, tyArray, tyOpenArray, tyVarargs:
+    if withRef and sameBackendType(t, orig):
+      result = true
+    elif not containsOrIncl(marker, t.id):
+      result = canFormAcycleAux(g, marker, t.elementType, orig, withRef, hasTrace)
   of tyProc: result = typ.callConv == ccClosure
   else: discard
 
@@ -445,37 +443,6 @@ proc canFormAcycle*(g: ModuleGraph, typ: PType): bool =
   let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
   result = canFormAcycleAux(g, marker, t, t, false, false)
 
-proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator,
-                   closure: RootRef): PType
-proc mutateNode(marker: var IntSet, n: PNode, iter: TTypeMutator,
-                closure: RootRef): PNode =
-  result = nil
-  if n != nil:
-    result = copyNode(n)
-    result.typ = mutateTypeAux(marker, n.typ, iter, closure)
-    case n.kind
-    of nkNone..nkNilLit:
-      # a leaf
-      discard
-    else:
-      for i in 0..<n.len:
-        result.add mutateNode(marker, n[i], iter, closure)
-
-proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator,
-                   closure: RootRef): PType =
-  result = nil
-  if t == nil: return
-  result = iter(t, closure)
-  if not containsOrIncl(marker, t.id):
-    for i in 0..<t.len:
-      result[i] = mutateTypeAux(marker, result[i], iter, closure)
-    if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
-  assert(result != nil)
-
-proc mutateType(t: PType, iter: TTypeMutator, closure: RootRef): PType =
-  var marker = initIntSet()
-  result = mutateTypeAux(marker, t, iter, closure)
-
 proc valueToString(a: PNode): string =
   case a.kind
   of nkCharLit, nkUIntLit..nkUInt64Lit:
@@ -566,13 +533,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       elif prefer in {preferName, preferTypeName, preferInlayHint} or t.sym.owner.isNil:
         # note: should probably be: {preferName, preferTypeName, preferGenericArg}
         result = t.sym.name.s
-        if t.kind == tyGenericParam and t.len > 0:
+        if t.kind == tyGenericParam and t.genericParamHasConstraints:
           result.add ": "
-          var first = true
-          for son in t:
-            if not first: result.add " or "
-            result.add son.typeToString
-            first = false
+          result.add t.elementType.typeToString
       else:
         result = t.sym.owner.name.s & '.' & t.sym.name.s
       result.addTypeFlags(t)
@@ -589,17 +552,23 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
           result = "int"
         else:
           result = "int literal(" & $t.n.intVal & ")"
-    of tyGenericInst, tyGenericInvocation:
+    of tyGenericInst:
       result = typeToString(t.genericHead) & '['
-      for i in 1..<t.len-ord(t.kind != tyGenericInvocation):
-        if i > 1: result.add(", ")
-        result.add(typeToString(t[i], preferGenericArg))
+      for needsComma, a in t.genericInstParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferGenericArg))
+      result.add(']')
+    of tyGenericInvocation:
+      result = typeToString(t.genericHead) & '['
+      for needsComma, a in t.genericInvocationParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferGenericArg))
       result.add(']')
     of tyGenericBody:
       result = typeToString(t.typeBodyImpl) & '['
-      for i in 0..<t.len-1:
-        if i > 0: result.add(", ")
-        result.add(typeToString(t[i], preferTypeName))
+      for needsComma, a in t.genericBodyParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferTypeName))
       result.add(']')
     of tyTypeDesc:
       if t.elementType.kind == tyNone: result = "typedesc"
@@ -608,7 +577,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       if prefer == preferGenericArg and t.n != nil:
         result = t.n.renderTree
       else:
-        result = "static[" & (if t.len > 0: typeToString(t.skipModifier) else: "") & "]"
+        result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]"
         if t.n != nil: result.add "(" & renderTree(t.n) & ")"
     of tyUserTypeClass:
       if t.sym != nil and t.sym.owner != nil:
@@ -639,20 +608,18 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     of tyUserTypeClassInst:
       let body = t.base
       result = body.sym.name.s & "["
-      for i in 1..<t.len - 1:
-        if i > 1: result.add(", ")
-        result.add(typeToString(t[i]))
+      for needsComma, a in t.userTypeClassInstParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a))
       result.add "]"
     of tyAnd:
-      for i, son in t:
+      for i, son in t.ikids:
+        if i > 0: result.add(" and ")
         result.add(typeToString(son))
-        if i < t.len - 1:
-          result.add(" and ")
     of tyOr:
-      for i, son in t:
+      for i, son in t.ikids:
+        if i > 0: result.add(" or ")
         result.add(typeToString(son))
-        if i < t.len - 1:
-          result.add(" or ")
     of tyNot:
       result = "not " & typeToString(t.elementType)
     of tyUntyped:
@@ -665,7 +632,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
         result = "typeof(" & renderTree(t.n) & ")"
     of tyArray:
       result = "array"
-      if t.len > 0:
+      if t.hasElementType:
         if t.indexType.kind == tyRange:
           result &= "[" & rangeToStr(t.indexType.n) & ", " &
               typeToString(t.elementType) & ']'
@@ -674,26 +641,26 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
               typeToString(t.elementType) & ']'
     of tyUncheckedArray:
       result = "UncheckedArray"
-      if t.len > 0:
+      if t.hasElementType:
         result &= "[" & typeToString(t.elementType) & ']'
     of tySequence:
       if t.sym != nil and prefer != preferResolved:
         result = t.sym.name.s
       else:
         result = "seq"
-        if t.len > 0:
+        if t.hasElementType:
           result &= "[" & typeToString(t.elementType) & ']'
     of tyOrdinal:
       result = "ordinal"
-      if t.len > 0:
+      if t.hasElementType:
         result &= "[" & typeToString(t.skipModifier) & ']'
     of tySet:
       result = "set"
-      if t.len > 0:
+      if t.hasElementType:
         result &= "[" & typeToString(t.elementType) & ']'
     of tyOpenArray:
       result = "openArray"
-      if t.len > 0:
+      if t.hasElementType:
         result &= "[" & typeToString(t.elementType) & ']'
     of tyDistinct:
       result = "distinct " & typeToString(t.elementType,
@@ -701,38 +668,33 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     of tyIterable:
       # xxx factor this pattern
       result = "iterable"
-      if t.len > 0:
+      if t.hasElementType:
         result &= "[" & typeToString(t.skipModifier) & ']'
     of tyTuple:
       # we iterate over t.sons here, because t.n may be nil
       if t.n != nil:
         result = "tuple["
-        assert(t.n.len == t.len)
         for i in 0..<t.n.len:
           assert(t.n[i].kind == nkSym)
-          result.add(t.n[i].sym.name.s & ": " & typeToString(t[i]))
+          result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ))
           if i < t.n.len - 1: result.add(", ")
         result.add(']')
-      elif t.len == 0:
+      elif t.isEmptyTupleType:
         result = "tuple[]"
+      elif t.isSingletonTupleType:
+        result = "("
+        for son in t.kids:
+          result.add(typeToString(son))
+        result.add(",)")
       else:
         result = "("
-        for i in 0..<t.len:
-          result.add(typeToString(t[i]))
-          if i < t.len - 1: result.add(", ")
-          elif t.len == 1: result.add(",")
+        for i, son in t.ikids:
+          if i > 0: result.add ", "
+          result.add(typeToString(son))
         result.add(')')
     of tyPtr, tyRef, tyVar, tyLent:
       result = if isOutParam(t): "out " else: typeToStr[t.kind]
-      if t.len >= 2:
-        setLen(result, result.len-1)
-        result.add '['
-        for i in 0..<t.len:
-          result.add(typeToString(t[i]))
-          if i < t.len - 1: result.add(", ")
-        result.add ']'
-      else:
-        result.add typeToString(t.elementType)
+      result.add typeToString(t.elementType)
     of tyRange:
       result = "range "
       if t.n != nil and t.n.kind == nkRange:
@@ -751,14 +713,15 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
                 "proc "
       if tfUnresolved in t.flags: result.add "[*missing parameters*]"
       result.add "("
-      for i in 1..<t.len:
-        if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
-          result.add(t.n[i].sym.name.s)
+      for i, a in t.paramTypes:
+        if i > FirstParamAt: result.add(", ")
+        let j = paramTypeToNodeIndex(i)
+        if t.n != nil and j < t.n.len and t.n[j].kind == nkSym:
+          result.add(t.n[j].sym.name.s)
           result.add(": ")
-        result.add(typeToString(t[i]))
-        if i < t.len - 1: result.add(", ")
+        result.add(typeToString(a))
       result.add(')')
-      if t.len > 0 and t.returnType != nil: result.add(": " & typeToString(t.returnType))
+      if t.returnType != nil: result.add(": " & typeToString(t.returnType))
       var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
       if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
         let pragmasNode = t.owner.ast[pragmasPos]
@@ -813,7 +776,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
   of tyUInt..tyUInt64: result = Zero
   of tyEnum:
     # if basetype <> nil then return firstOrd of basetype
-    if t.len > 0 and t.baseClass != nil:
+    if t.baseClass != nil:
       result = firstOrd(conf, t.baseClass)
     else:
       if t.n.len > 0:
@@ -827,7 +790,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
   of tyUserTypeClasses:
     result = firstOrd(conf, last(t))
   of tyOrdinal:
-    if t.len > 0: result = firstOrd(conf, skipModifier(t))
+    if t.hasElementType: result = firstOrd(conf, skipModifier(t))
     else:
       result = Zero
       internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
@@ -923,7 +886,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
     result = lastOrd(conf, last(t))
   of tyProxy: result = Zero
   of tyOrdinal:
-    if t.len > 0: result = lastOrd(conf, skipModifier(t))
+    if t.hasElementType: result = lastOrd(conf, skipModifier(t))
     else:
       result = Zero
       internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
@@ -1093,11 +1056,11 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
   # two tuples are equivalent iff the names, types and positions are the same;
   # however, both types may not have any field names (t.n may be nil) which
   # complicates the matter a bit.
-  if a.len == b.len:
+  if sameTupleLengths(a, b):
     result = true
-    for i in 0..<a.len:
-      var x = a[i]
-      var y = b[i]
+    for i, aa, bb in tupleTypePairs(a, b):
+      var x = aa
+      var y = bb
       if IgnoreTupleFields in c.flags:
         x = skipTypes(x, {tyRange, tyGenericInst, tyAlias})
         y = skipTypes(y, {tyRange, tyGenericInst, tyAlias})
@@ -1184,18 +1147,16 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
     result = false
 
 proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool =
-  # check base types:
-  if a.len != b.len: return
-  for i in 0..<a.len:
-    if not sameTypeOrNilAux(a[i], b[i], c): return
-  if not sameObjectTree(a.n, b.n, c): return
+  if not sameTypeOrNilAux(a.baseClass, b.baseClass, c): return false
+  if not sameObjectTree(a.n, b.n, c): return false
   result = true
 
 proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
-  if a.len != b.len: return false
+  if not sameTupleLengths(a, b): return false
+  # XXX This is not tuple specific.
   result = true
-  for i in 0..<a.len:
-    result = sameTypeOrNilAux(a[i], b[i], c)
+  for _, x, y in tupleTypePairs(a, b):
+    result = sameTypeOrNilAux(x, y, c)
     if not result: return
 
 proc isGenericAlias*(t: PType): bool =
@@ -1253,15 +1214,13 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     objects ie `type A[T] = SomeObject`
   ]#
   # this is required by tunique_type but makes no sense really:
-  if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags:
+  if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
     let
       lhs = x.skipGenericAlias
       rhs = y.skipGenericAlias
     if rhs.kind != tyGenericInst or lhs.base != rhs.base:
       return false
-    for i in 1..<lhs.len - 1:
-      let ff = rhs[i]
-      let aa = lhs[i]
+    for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
       if not sameTypeAux(ff, aa, c): return false
     return true
 
@@ -1279,7 +1238,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
 
   of tyStatic, tyFromExpr:
     result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
-    if result and a.len == b.len and a.len == 1:
+    if result and sameTupleLengths(a, b) and a.hasElementType:
       cycleCheck()
       result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyObject:
@@ -1314,15 +1273,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
       result = a.sym.position == b.sym.position
   of tyBuiltInTypeClass:
-    assert a.len == 1
-    assert a[0].len == 0
-    assert b.len == 1
-    assert b[0].len == 0
-    result = a[0].kind == b[0].kind and sameFlags(a[0], b[0])
-    if result and a[0].kind == tyProc and IgnoreCC notin c.flags:
-      let ecc = a[0].flags * {tfExplicitCallConv}
-      result = ecc == b[0].flags * {tfExplicitCallConv} and
-               (ecc == {} or a[0].callConv == b[0].callConv)
+    result = a.elementType.kind == b.elementType.kind and sameFlags(a.elementType, b.elementType)
+    if result and a.elementType.kind == tyProc and IgnoreCC notin c.flags:
+      let ecc = a.elementType.flags * {tfExplicitCallConv}
+      result = ecc == b.elementType.flags * {tfExplicitCallConv} and
+               (ecc == {} or a.elementType.callConv == b.elementType.callConv)
   of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef,
      tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs,
      tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst,
@@ -1418,15 +1373,6 @@ proc commonSuperclass*(a, b: PType): PType =
       return t
     y = y.baseClass
 
-proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
-                last: TTypeKind): bool =
-  var a = a
-  for k, i in pattern.items:
-    if a.kind != k: return false
-    if i >= a.len or a[i] == nil: return false
-    a = a[i]
-  result = a.kind == last
-
 
 include sizealignoffsetimpl
 
@@ -1578,8 +1524,8 @@ proc isCompileTimeOnly*(t: PType): bool {.inline.} =
 
 proc containsCompileTimeOnly*(t: PType): bool =
   if isCompileTimeOnly(t): return true
-  for i in 0..<t.len:
-    if t[i] != nil and isCompileTimeOnly(t[i]):
+  for a in t.kids:
+    if a != nil and isCompileTimeOnly(a):
       return true
   return false
 
@@ -1787,9 +1733,9 @@ proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =
   of tyTuple:
     result = false
     var cycleDetectorCopy: IntSet
-    for i in 0..<t.len:
+    for a in t.kids:
       cycleDetectorCopy = cycleDetector
-      if isTupleRecursive(t[i], cycleDetectorCopy):
+      if isTupleRecursive(a, cycleDetectorCopy):
         return true
   of tyRef, tyPtr, tyVar, tyLent, tySink,
       tyArray, tyUncheckedArray, tySequence, tyDistinct:
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
index 44d38ebff..2ba644658 100644
--- a/compiler/varpartitions.nim
+++ b/compiler/varpartitions.nim
@@ -400,11 +400,10 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) =
         if typ != nil:
           typ = skipTypes(typ, abstractInst)
           if typ.kind != tyProc: typ = nil
-          else: assert(typ.len == typ.n.len)
 
         for i in 1 ..< n.len:
           let it = n[i]
-          if typ != nil and i < typ.len:
+          if typ != nil and i < typ.n.len:
             assert(typ.n[i].kind == nkSym)
             let paramType = typ.n[i].typ
             if not paramType.isCompileTimeOnly and not typ.returnType.isEmptyType and
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index b58ae109f..6d0e03813 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -205,7 +205,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       # only named tuples have a node, unnamed tuples don't
       if t.n.isNil:
         result = newNodeX(nkTupleConstr)
-        for subType in t:
+        for subType in t.kids:
           result.add mapTypeToAst(subType, info)
       else:
         result = newNodeX(nkTupleTy)
diff --git a/tests/typerel/tregionptrs.nim b/tests/typerel/tregionptrs.nim
deleted file mode 100644
index 504ec1011..000000000
--- a/tests/typerel/tregionptrs.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  errormsg: "type mismatch: got <BPtr> but expected 'APtr = ptr[RegionA, int]'"
-  line: 16
-"""
-
-type
-  RegionA = object
-  APtr = RegionA ptr int
-  RegionB = object
-  BPtr = RegionB ptr int
-
-var x,xx: APtr
-var y: BPtr
-x = nil
-x = xx
-x = y