cgen: replace indexed child loops with the sons/isons/sonsFrom iterators

`for i in k..<n.len: ... n[i] ...` is the dominant shape for walking a `PNode`'s
children in the code generator: 41 such loops across the cgen files, and 777
indexed node accesses in total. It reads worse than iterating, it bounds-checks
every subscript, and it is quadratic the moment the backend reads children off a
NIF `Cursor` rather than a materialised tree (a child is `firstSon` plus one
`skip` per preceding sibling, and `skip` steps over a whole subtree).

31 of the 41 are converted:

* 20 to `sons`/`sonsFrom` — the index only ever subscripted `n`.
* 9 to `isons`, which now takes a `start` index (defaulting to 0, so its eight
  existing call sites are unchanged). These genuinely need `i`: a parallel index
  into the routine's `PType` (`typ.n[i]`, `typ[i]`), a `needTmp[i-1]` lookup, an
  `i == field.position` test, `$i` in a generated struct name, or the index
  passed straight to `genOtherArg`.
* 2 to `sonsFrom` with a variable start (`firstParam`, `offset`).

`sonsFrom` is new, next to `sons`/`isons` in astdef.

The remaining 10 are deliberate. Eight are not `PNode` at all — `varargs[Snippet]`,
`seq[PSym]`, `string`, and `PType`, where `sons` is a `proc ...: var TTypeSeq`
rather than an iterator, so a blind rewrite would compile into something quite
different. Two iterate `0..<it.len-1`, excluding the last child, which no
iterator expresses cleanly.

Pure refactor, and verified as one: all 219 generated `.c` files of a 219-module
program and the linked binary are byte-identical to the parent commit. That is
the bar that matters here, because the index arithmetic (`i-1`, `i == position`,
`$i`) is the easy thing to get wrong. It also caught a real slip on the way:
`genFieldCheck` reassigns its loop variable, which a `for` binding cannot do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
araq
2026-08-29 05:44:14 +02:00
parent e0c0724b62
commit f84f53bff4
9 changed files with 110 additions and 105 deletions

View File

@@ -960,10 +960,22 @@ iterator sons*(n: PNode): PNode =
## as it does not rely on random indexed access (see doc/ic_backend_nif_native.md).
for i in 0..<n.safeLen: yield n[i]
iterator isons*(n: PNode): tuple[i: int, n: PNode] =
## Like `sons` but also yields the child index. Replaces
## `for i in 0..<n.len: ... n[i] ...` when `i` itself is still needed.
for i in 0..<n.safeLen: yield (i, n[i])
iterator isons*(n: PNode; start = 0): tuple[i: int, n: PNode] =
## Like `sons` but also yields the child index, and optionally skips the first
## `start` children. Replaces `for i in start..<n.len: ... n[i] ...` when `i`
## itself is still needed — for a parameter position, a `needTmp[i-1]` lookup,
## a parallel index into the routine's `PType`, and so on. `start` is almost
## always 1, to step over a call's callee or a case statement's selector.
##
## Use `sonsFrom` instead when the index is only ever used to subscript `n`.
for i in start..<n.safeLen: yield (i, n[i])
iterator sonsFrom*(n: PNode; start: int): PNode =
## `sons` skipping the first `start` children. Replaces
## `for i in start..<n.len: ... n[i] ...`, which is by far the commonest
## indexed shape in the code generator — `start` is almost always 1, to step
## over a case/try statement's selector or a call's callee.
for i in start..<n.safeLen: yield n[i]
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 2322968

View File

@@ -49,8 +49,7 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
result = false
if le != nil:
for i in 1..<ri.len:
let r = ri[i]
for r in sonsFrom(ri, 1):
if isPartOf(le, r, {pfStructural}) != arNo: return true
# we use the weaker 'canRaise' here in order to prevent too many
# annoying warnings, see #14514
@@ -59,8 +58,7 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
message(p.config, le.info, warnObservableStores, $le)
# bug #19613 prevent dangerous aliasing too:
if dest != nil and dest != le:
for i in 1..<ri.len:
let r = ri[i]
for r in sonsFrom(ri, 1):
if isPartOf(dest, r, {pfStructural}) != arNo: return true
proc hasNoInit(call: PNode): bool {.inline.} =
@@ -474,19 +472,19 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
for i in 1..<ri.len:
for i, it in isons(ri, 1):
if i < typ.n.len:
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
if not paramType.typ.isCompileTimeOnly:
var arg = newBuilder("")
genArg(p, ri[i], paramType.sym, ri, arg, needTmp[i-1])
genArg(p, it, paramType.sym, ri, arg, needTmp[i-1])
if arg.buf.len != 0:
result.addArgument(argBuilder):
result.add(extract(arg))
else:
var arg = newBuilder("")
genArgNoParam(p, ri[i], arg, needTmp[i-1])
genArgNoParam(p, it, arg, needTmp[i-1])
if arg.buf.len != 0:
result.addArgument(argBuilder):
result.add(extract(arg))
@@ -727,7 +725,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
case pat[i]
of '@':
var callBuilder = default(CallBuilder) # not init call builder
for k in j..<ri.len:
for k, _ in isons(ri, j):
genOtherArg(p, ri, k, typ, result, callBuilder)
inc i
of '#':
@@ -811,7 +809,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
pl.add(op.snippet)
var res = newBuilder("")
var call = initCallBuilder(res, extract(pl))
for i in 2..<ri.len:
for i, _ in isons(ri, 2):
genOtherArg(p, ri, i, typ, res, call)
fixupCall(p, le, ri, d, res, call)
@@ -842,7 +840,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
for i in start..<ri.len:
for i, it in isons(ri, start):
if i >= typ.n.len:
internalError(p.config, ri.info, "varargs for objective C method?")
assert(typ.n[i].kind == nkSym)
@@ -850,7 +848,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
pl.add(" ")
pl.add(param.name.s)
pl.add(": ")
genArg(p, ri[i], param, ri, pl)
genArg(p, it, param, ri, pl)
if typ.returnType != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(" ")

View File

@@ -1073,8 +1073,8 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym, ty: PType) =
var test, u, v: TLoc
for i in 1..<e.len:
var it = e[i]
for child in sonsFrom(e, 1):
var it = child
assert(it.kind in nkCallKinds)
assert(it.firstSon.kind == nkSym)
let op = it.firstSon.sym
@@ -1932,15 +1932,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
r = rdLoc(d)
discard getTypeDesc(p.module, t)
let ty = getUniqueType(t)
for i in 1..<e.len:
if nfPreventCg in e[i].flags:
for it in sonsFrom(e, 1):
if nfPreventCg in it.flags:
# this is an object constructor node generated by the VM and
# this field is in an inactive case branch, don't generate assignment
continue
var check: PNode = nil
if e[i].len == 3 and optFieldCheck in p.options:
check = e[i][2]
genFieldObjConstr(p, ty, useTemp, isRef, e[i].firstSon, e[i][1], check, d, r, e.info)
if it.len == 3 and optFieldCheck in p.options:
check = it[2]
genFieldObjConstr(p, ty, useTemp, isRef, it.firstSon, it[1], check, d, r, e.info)
if useTemp:
if d.k == locNone:
@@ -2447,8 +2447,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
b = initLoc(locExpr, e, OnUnknown)
if e[1].len > 0:
var val: Snippet = ""
for i in 0..<e[1].len:
let it = e[1][i]
for it in sons(e[1]):
var currentExpr: Snippet
if it.kind == nkRange:
x = initLocExpr(p, it.firstSon)
@@ -3861,8 +3860,7 @@ proc containsOpaqueImportcFieldAux(t: PType; n: PNode): bool =
of nkRecCase:
if containsOpaqueImportcFieldAux(t, n.firstSon):
return true
for i in 1..<n.len:
let branch = n[i]
for branch in sonsFrom(n, 1):
if branch.kind == nkOfBranch or branch.kind == nkElse:
if containsOpaqueImportcFieldAux(t, branch.lastSon):
return true
@@ -4003,13 +4001,13 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
var branch = Zero
if constOrNil != nil:
## find kind value, default is zero if not specified
for i in 1..<constOrNil.len:
if constOrNil[i].kind == nkExprColonExpr:
if constOrNil[i].firstSon.sym.name.id == obj.firstSon.sym.name.id:
branch = getOrdValue(constOrNil[i][1])
for i, it in isons(constOrNil, 1):
if it.kind == nkExprColonExpr:
if it.firstSon.sym.name.id == obj.firstSon.sym.name.id:
branch = getOrdValue(it[1])
break
elif i == obj.firstSon.sym.position:
branch = getOrdValue(constOrNil[i])
branch = getOrdValue(it)
break
let selectedBranch = caseObjDefaultBranch(obj, branch)
@@ -4050,14 +4048,14 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
result.addField(init, name = sname):
block fieldInit:
if constOrNil != nil:
for i in 1..<constOrNil.len:
if constOrNil[i].kind == nkExprColonExpr:
assert constOrNil[i].firstSon.kind == nkSym, "illformed object constr; the field is not a sym"
if constOrNil[i].firstSon.sym.name.id == field.name.id:
genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
for i, it in isons(constOrNil, 1):
if it.kind == nkExprColonExpr:
assert it.firstSon.kind == nkSym, "illformed object constr; the field is not a sym"
if it.firstSon.sym.name.id == field.name.id:
genBracedInit(p, it[1], isConst, field.typ, result)
break fieldInit
elif i == field.position:
genBracedInit(p, constOrNil[i], isConst, field.typ, result)
genBracedInit(p, it, isConst, field.typ, result)
break fieldInit
# not found, produce default value:
getDefaultValue(p, field.typ, info, result)

View File

@@ -19,8 +19,8 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
if n == nil: return
case n.kind
of nkRecList:
for i in 0..<n.len:
specializeResetN(p, accessor, n[i], typ)
for it in sons(n):
specializeResetN(p, accessor, it, typ)
of nkRecCase:
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
let disc = n[0].sym
@@ -29,8 +29,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
internalError(p.config, n.info, "specializeResetN()")
let discField = dotField(accessor, disc.loc.snippet)
p.s(cpsStmts).addSwitchStmt(discField):
for i in 1..<n.len:
let branch = n[i]
for branch in sonsFrom(n, 1):
assert branch.kind in {nkOfBranch, nkElse}
var caseBuilder: SwitchCaseBuilder
p.s(cpsStmts).addSwitchCase(caseBuilder):

View File

@@ -329,18 +329,18 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet =
var argBuilder = default(CallBuilder) # not init, only building params
let typ = skipTypes(call.firstSon.typ, abstractInst)
assert(typ.kind == tyProc)
for i in 1..<call.len:
for i, child in isons(call, 1):
#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].firstSon.kind == nkSym and call[i].firstSon.sym.kind == skType:
if child.kind == nkCall and child.firstSon.kind == nkSym and child.firstSon.sym.kind == skType:
res.addArgument(argBuilder):
res.add genCppInitializer(p.module, p, call[i].firstSon.sym.typ, didGenTemp)
res.add genCppInitializer(p.module, p, child.firstSon.sym.typ, didGenTemp)
else:
#We need to test for temp in globals, see: #23657
let param =
if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
call[i].firstSon
if typ[i].kind in {tyVar} and child.kind == nkHiddenAddr:
child.firstSon
else:
call[i]
child
if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in
{tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
tyVarargs, tySequence, tyString, tyCstring, tyTuple}):
@@ -574,10 +574,10 @@ proc genReturnStmt(p: BProc, t: PNode) =
p.s(cpsStmts).addGoto("BeforeRet_")
proc genGotoForCase(p: BProc; caseStmt: PNode) =
for i in 1..<caseStmt.len:
for child in sonsFrom(caseStmt, 1):
var scope: ScopeBuilder
startSimpleBlock(p, scope)
let it = caseStmt[i]
let it = child
for j in 0..<it.len-1:
if it[j].kind == nkRange:
localError(p.config, it.info, "range notation not available for computed goto")
@@ -646,10 +646,10 @@ proc genComputedGoto(p: BProc; n: PNode) =
# first goto:
p.s(cpsStmts).addComputedGoto(subscript(tmp, ra))
for i in 1..<caseStmt.len:
for child in sonsFrom(caseStmt, 1):
var scope: ScopeBuilder
startSimpleBlock(p, scope)
let it = caseStmt[i]
let it = child
for j in 0..<it.len-1:
if it[j].kind == nkRange:
localError(p.config, it.info, "range notation not available for computed goto")
@@ -992,18 +992,18 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
# count how many constant strings there are in the case:
var strings = 0
for i in 1..<t.len:
if t[i].kind == nkOfBranch: inc(strings, t[i].len - 1)
for it in sonsFrom(t, 1):
if it.kind == nkOfBranch: inc(strings, it.len - 1)
if strings > stringCaseThreshold:
var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[Builder]
newSeq(branches, bitMask + 1)
var a: TLoc = initLocExpr(p, t.firstSon) # first pass: generate ifs+goto:
var labId = p.labels
for i in 1..<t.len:
for it in sonsFrom(t, 1):
inc(p.labels)
if t[i].kind == nkOfBranch:
genCaseStringBranch(p, t[i], a, "LA" & rope(p.labels) & "_",
if it.kind == nkOfBranch:
genCaseStringBranch(p, it, a, "LA" & rope(p.labels) & "_",
stringKind, branches)
else:
# else statement: nothing to do yet
@@ -1048,8 +1048,7 @@ proc branchHasTooBigRange(b: PNode): bool =
proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
result = 0
for i in 1..<n.len:
var branch = n[i]
for i, branch in isons(n, 1):
var stmtBlock = lastSon(branch)
if stmtBlock.stmtsContainPragma(wLinearScanEnd):
result = i
@@ -1300,39 +1299,39 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
var catchAllPresent = false
incl p.flags, noSafePoints # mark as not needing 'popCurrentException'
if hasImportedCppExceptions:
for i in 1..<t.len:
if t[i].kind != nkExceptBranch: break
for it in sonsFrom(t, 1):
if it.kind != nkExceptBranch: break
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
if t[i].len == 1:
if it.len == 1:
# general except section:
startBlockWith(p):
p.s(cpsStmts).add("catch (...) {\n")
genExceptBranchBody(t[i].firstSon)
genExceptBranchBody(it.firstSon)
endBlockWith(p):
p.s(cpsStmts).add("}\n")
catchAllPresent = true
else:
for j in 0..<t[i].len-1:
var typeNode = t[i][j]
if t[i][j].isInfixAs():
typeNode = t[i][j][1]
for j in 0..<it.len-1:
var typeNode = it[j]
if it[j].isInfixAs():
typeNode = it[j][1]
if isImportedException(typeNode.typ, p.config):
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
let exvar = it[j][2] # ex1 in `except ExceptType as ex1:`
fillLocalName(p, exvar.sym)
backendEnsureMutable exvar.sym
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack)
startBlockWith(p):
lineCg(p, cpsStmts, "catch ($1& $2) {$n", [getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc)])
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
genExceptBranchBody(it[^1]) # exception handler body will duplicated for every type
endBlockWith(p):
p.s(cpsStmts).add("}\n")
elif isImportedException(typeNode.typ, p.config):
startBlockWith(p):
lineCg(p, cpsStmts, "catch ($1&) {$n", [getTypeDesc(p.module, t[i][j].typ)])
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
lineCg(p, cpsStmts, "catch ($1&) {$n", [getTypeDesc(p.module, it[j].typ)])
genExceptBranchBody(it[^1]) # exception handler body will duplicated for every type
endBlockWith(p):
p.s(cpsStmts).add("}\n")
@@ -1360,8 +1359,8 @@ proc bodyCanRaise(p: BProc; n: PNode): bool =
result = canRaiseDisp(p, n.firstSon)
if not result:
# also check the arguments:
for i in 1 ..< n.len:
if bodyCanRaise(p, n[i]): return true
for it in sonsFrom(n, 1):
if bodyCanRaise(p, it): return true
of nkRaiseStmt:
result = true
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
@@ -1710,8 +1709,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
if isAsmStmt: 1 # first son is pragmas
else: 0
for i in offset..<t.len:
let it = t[i]
for it in sonsFrom(t, offset):
case it.kind
of nkStrLit..nkTripleStrLit:
res.add(it.strVal)

View File

@@ -31,8 +31,8 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
if n == nil: return
case n.kind
of nkRecList:
for i in 0..<n.len:
genTraverseProc(c, accessor, n[i], typ)
for it in sons(n):
genTraverseProc(c, accessor, it, typ)
of nkRecCase:
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
@@ -42,8 +42,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
internalError(c.p.config, n.info, "genTraverseProc()")
let discField = dotField(accessor, disc.loc.snippet)
p.s(cpsStmts).addSwitchStmt(discField):
for i in 1..<n.len:
let branch = n[i]
for branch in sonsFrom(n, 1):
assert branch.kind in {nkOfBranch, nkElse}
var caseBuilder: SwitchCaseBuilder
p.s(cpsStmts).addSwitchCase(caseBuilder):

View File

@@ -611,9 +611,9 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
types.add getTypeDescWeak(m, this.typ, check, dkParam)
let firstParam = if isCtor: 1 else: 2
for i in firstParam..<t.n.len:
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
var param = t.n[i].sym
for it in sonsFrom(t.n, firstParam):
if it.kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
var param = it.sym
var descKind = dkParam
if optByRef in param.options:
if param.typ.kind == tyGenericInst:
@@ -623,7 +623,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
var typ, name: string
backendEnsureMutable param
fillParamName(m, param)
fillLoc(param.locImpl, locParam, t.n[i],
fillLoc(param.locImpl, locParam, it,
param.paramStorageLoc)
if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
typ = getTypeDescWeak(m, param.typ, check, descKind) & "*"
@@ -668,9 +668,9 @@ proc genProcParams(m: BModule; t: PType, rettype: var Rope, params: var Builder,
rettype = getTypeDescWeak(m, t.returnType, check, dkResult)
var paramBuilder: ProcParamBuilder
params.addProcParams(paramBuilder):
for i in 1..<t.n.len:
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = t.n[i].sym
for child in sonsFrom(t.n, 1):
if child.kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = child.sym
# The hidden closure environment param (`:envP`) is not a real C parameter:
# the environment is passed via the trailing `ClE_0` (added below) and
# `closureSetup` materialises `:envP` as a local cast of it. In a from-source
@@ -692,7 +692,7 @@ proc genProcParams(m: BModule; t: PType, rettype: var Rope, params: var Builder,
if isCompileTimeOnly(param.typ): continue
backendEnsureMutable param
fillParamName(m, param)
fillLoc(param.locImpl, locParam, t.n[i],
fillLoc(param.locImpl, locParam, child,
param.paramStorageLoc)
if isClosureEnv: continue # name/loc filled, but not part of the C signature
var typ: Rope
@@ -775,10 +775,10 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
# prefix mangled name with "_U" to avoid clashes with other field names,
# since identifiers are not allowed to start with '_'
var unionBody = newBuilder("")
for i in 1..<n.len:
case n[i].kind
for i, it in isons(n, 1):
case it.kind
of nkOfBranch, nkElse:
let k = lastSon(n[i])
let k = lastSon(it)
if k.kind != nkSym:
let structName = "_" & mangleRecFieldName(m, n.firstSon.sym) & "_" & $i
var a = newBuilder("")
@@ -1552,8 +1552,7 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
else:
m.s[cfsData].addArrayVar(kind = Local, name = tmp,
elementType = ptrType("TNimNode"), len = toInt(L)+1)
for i in 1..<n.len:
var b = n[i] # branch
for b in sonsFrom(n, 1):
var tmp2 = getNimNode(m)
genObjectFields(m, typ, origType, lastSon(b), tmp2, info)
case b.kind

View File

@@ -22,13 +22,13 @@ proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
result = nil
for i in 0..<n.len:
result = getPragmaStmt(n[i], w)
for it in sons(n):
result = getPragmaStmt(it, w)
if result != nil: break
of nkPragma:
result = nil
for i in 0..<n.len:
if whichPragma(n[i]) == w: return n[i]
for it in sons(n):
if whichPragma(it) == w: return it
else:
result = nil

View File

@@ -1194,8 +1194,11 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
var a: TLoc = initLocExpr(m.initProc, n.firstSon)
let callee = rdLoc(a)
var params: seq[Snippet] = @[]
for i in 1..<n.len-1:
a = initLocExpr(m.initProc, n[i])
var remaining = n.len - 2 # children 1 ..< len-1
for it in sonsFrom(n, 1):
if remaining <= 0: break
dec remaining
a = initLocExpr(m.initProc, it)
params.add(rdLoc(a))
params.add(makeCString($extname))
template load(builder: var Builder) =
@@ -1444,8 +1447,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
result = InitSkippable
var exhaustive = skipTypes(n.firstSon.typ,
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
for i in 1..<n.len:
let it = n[i]
for it in sonsFrom(n, 1):
allPathsInBranch(it.lastSon)
if it.kind == nkElse: exhaustive = true
if not exhaustive: result = Unknown
@@ -1477,11 +1479,11 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
# is 'finally: result = x'
result = InitSkippable
allPathsInBranch(n.firstSon)
for i in 1..<n.len:
if n[i].kind == nkFinally:
result = allPathsAsgnResult(p, n[i].lastSon)
for it in sonsFrom(n, 1):
if it.kind == nkFinally:
result = allPathsAsgnResult(p, it.lastSon)
else:
allPathsInBranch(n[i].lastSon)
allPathsInBranch(it.lastSon)
of nkCallKinds:
if canRaiseDisp(p, n.firstSon) or
(n.firstSon.kind == nkSym and sfNoReturn in n.firstSon.sym.flags):
@@ -1635,8 +1637,8 @@ proc genProcLvl3*(m: BModule, prc: PSym) =
backendEnsureMutable res
res.locImpl.storage = OnUnknown
for i in 1..<prc.typ.n.len:
let param = prc.typ.n[i].sym
for paramNode in sonsFrom(prc.typ.n, 1):
let param = paramNode.sym
if param.typ.isCompileTimeOnly: continue
if prc.typ.callConv == ccClosure and param.name.s == ":envP":
# The hidden closure-env param is materialised by `closureSetup`, never a