cgen: no [] on a PNode outside tree construction

Completes the vocabulary migration started with `BNode`: every child READ in
the cgen files now goes through an accessor that a `.bif` `Cursor` can also
serve, so flipping `newIcBackend` is a matter of implementing the vocabulary
rather than rewriting call sites.

  * constant indices -> `firstSon` / `secondSon` / `lastSon` / `son(n, k)`,
    including the `namePos`/`paramsPos`/`bodyPos`/... slot reads;
  * indexed loops -> `sons` / `sonsFrom` / `sonsButLast` and the index-yielding
    `isons` / `isonsButLast`. `for i in 0..<n.len: n[i]` is quadratic once
    `BNode` is a `Cursor`, because reaching child `i` costs one `skip` per
    preceding SUBTREE;
  * `n.len == 0` / `> 0` on a node -> `hasSons`, which does not count.

`astdef` gains `sonsButLast(n, count)` and `isonsButLast` — the
`nkOfBranch`/`nkExceptBranch` shape, whose last child is the branch body, and
with `count = 2` the `nkVarTuple`/`nkIdentDefs` shape.

Three places needed more than a rename:

  * the C++/goto/setjmp try generators re-subscripted `t[i]` up to ten times
    per iteration of their `while i < t.len` walk; the branch node is now
    hoisted once per step;
  * `genParams` scans the arguments BACKWARDS to decide which need a temporary,
    which a `Cursor` cannot do at all. It materializes them in one forward pass
    and indexes that — the same order of work, since `needTmp` already
    allocates per call;
  * loops that stop at a computed position (`casePos`, `until`, `splitPoint`)
    walk forward and break instead of counting up to the bound.

What is left is exactly what a `Cursor` backend will not do: writes that build
a fresh `nkProcDef`, and subscripts of a `PType`, `string`, `seq` or `Table`.
`bnode.nim` records the invariant and the `PType` trap — `ast.sons(t: PType)`
is a proc returning `var TTypeSeq`, not the iterator of the same name — which
the type checker enforces, since the `firstSon`/`secondSon`/`lastSon`/`son`
family exists for `PNode` only.

Pure refactor, verified as one: all 216 generated `.c` files byte-identical to
the parent commit; metamorphic IC 16/16; icSuite 19/19 fragments; categories
gc 78, arc 140, destructor 97, closure 23, iter 71, trmacros 6, cpp 50,
exception 47, casestmt 16 with one pre-existing environmental failure
(tests/cpp/tasync_cpp.nim: `cannot open file: jester`).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE
This commit is contained in:
araq
2026-08-29 06:57:55 +02:00
parent 8d4ddd5516
commit 9e076d74c0
10 changed files with 527 additions and 484 deletions

View File

@@ -977,6 +977,23 @@ iterator sonsFrom*(n: PNode; start: int): PNode =
## over a case/try statement's selector or a call's callee.
for i in start..<n.safeLen: yield n[i]
iterator sonsButLast*(n: PNode; count = 1): PNode =
## `sons` without the last `count` children. Replaces `for i in 0..<n.len-1:
## ... n[i] ...`, which is what an `nkOfBranch`/`nkExceptBranch` walk looks
## like: the last child is the branch BODY, the ones before it are the labels
## it matches. `count = 2` is the `nkVarTuple`/`nkIdentDefs` shape, whose last
## two children are the type and the value. A `Cursor` can serve this with a
## single pass and `count` nodes of lookahead; the indexed form has to re-walk
## the children for every label.
##
## Use `isonsButLast` instead when the index is still needed.
for i in 0 ..< n.safeLen - count: yield n[i]
iterator isonsButLast*(n: PNode; count = 1): tuple[i: int, n: PNode] =
## Like `sonsButLast` but also yields the child index — for a tuple field
## position, a parallel index into the tuple's `PType`, and so on.
for i in 0 ..< n.safeLen - count: yield (i, n[i])
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 2322968
var gNodeId: int

View File

@@ -20,11 +20,11 @@
## below one area at a time and the compiler keeps building throughout, because
## on the `PNode` side the vocabulary is what `ast`/`astdef` already provide —
## `kind`, `len`, `safeLen`, `sym`, `typ`, `info`, `firstSon`, `secondSon`,
## `lastSon` and the `sons`/`isons`/`sonsFrom` iterators all exist. This module
## deliberately does
## NOT redefine them for `PNode`: an identical second overload would make every
## call site ambiguous. It adds only what the AST lacks (`son`, `hasSons`), and
## supplies the whole vocabulary on the `Cursor` side.
## `lastSon` and the `sons`/`isons`/`sonsFrom`/`sonsButLast`/`isonsButLast`
## iterators all exist. This module deliberately does NOT redefine them for
## `PNode`: an identical second overload would make every call site ambiguous.
## It adds only what the AST lacks (`son`, `hasSons`), and supplies the whole
## vocabulary on the `Cursor` side.
##
## THE COST MODEL DIFFERS, and that is what the vocabulary is shaped around. A
## `Cursor` is a copyable position in a token buffer, so a child is reached by
@@ -32,14 +32,27 @@
## whole subtree. Reading child `i` is therefore O(size of children 0..<i):
##
## * `firstSon` / `secondSon` / `son(n, k)` with small constant `k` — cheap, and
## already how most structural access reads (344 of 777 indexed accesses in
## the cgen files use a constant or a `*Pos` index).
## * `for x in sons(n)` / `sonsFrom(n, k)` — one linear pass. ALWAYS migrate an
## indexed loop to these: `for i in 0..<n.len: n[i]` is O(n^2) once `BNode` is
## a `Cursor`, and ~196 such accesses remain.
## * `lastSon(n)` — O(len). Fine once, a trap inside a loop; 28 `n[^1]` uses.
## how nearly all structural access in the cgen files now reads.
## * `for x in sons(n)` / `sonsFrom(n, k)` / `sonsButLast(n, k)`, and the
## index-yielding `isons` / `isonsButLast` — one linear pass. ALWAYS use these
## for a loop: `for i in 0..<n.len: n[i]` is O(n^2) once `BNode` is a `Cursor`.
## A loop that stops at a computed position walks forward and breaks
## (`for i, it in isons(n): if i >= casePos: break`) rather than counting up to
## the bound.
## * `lastSon(n)` — O(len). Fine once, a trap inside a loop.
## * `len(n)` — O(len) on a `Cursor`, which has to count. Do not put it in a loop
## condition; use `sons`/`sonsFrom`, or `hasSons` for an emptiness test.
##
## The cgen files hold to one invariant, which is what makes the eventual flip
## mechanical: NO `[]` ON A `PNode` OUTSIDE OF TREE CONSTRUCTION. Every read is
## `firstSon`/`secondSon`/`lastSon`/`son(n, k)` or one of the iterators; the
## remaining subscripts are writes that build a fresh `nkProcDef`
## (`theProc[namePos] = ...`), which a `Cursor` backend will not do at all, and
## accesses to a `PType`, a `string`, a `seq` or a `Table`, none of which are
## `BNode`s. `PType` is the trap to watch for: `ast.sons(t: PType)` is a `proc`
## returning `var TTypeSeq`, NOT the iterator of the same name, so `t[i]` there
## means something else entirely. The `firstSon`/`secondSon`/`lastSon`/`son`
## family is defined for `PNode` only, so a mistaken base does not compile.
import ast, lineinfos
@@ -70,6 +83,9 @@ when defined(newIcBackend):
proc info*(n: BNode): TLineInfo {.error: "BNode.info: not implemented for Cursor yet".} = discard
iterator sons*(n: BNode): BNode {.error: "BNode.sons: not implemented for Cursor yet".} = discard
iterator sonsFrom*(n: BNode; start: int): BNode {.error: "BNode.sonsFrom: not implemented for Cursor yet".} = discard
iterator sonsButLast*(n: BNode; count = 1): BNode {.error: "BNode.sonsButLast: not implemented for Cursor yet (one pass with `count` nodes of lookahead)".} = discard
iterator isons*(n: BNode; start = 0): tuple[i: int, n: BNode] {.error: "BNode.isons: not implemented for Cursor yet".} = discard
iterator isonsButLast*(n: BNode; count = 1): tuple[i: int, n: BNode] {.error: "BNode.isonsButLast: not implemented for Cursor yet".} = discard
else:
type BNode* = PNode

View File

@@ -42,7 +42,7 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
nkCheckedFieldExpr:
n = n.firstSon
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
n = n[1]
n = n.secondSon
else:
# cannot analyse the location; assume the worst
return true
@@ -184,7 +184,7 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} =
of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
x = x.firstSon
of nkHiddenStdConv:
x = x[1]
x = x.secondSon
else:
break
if x.kind == nkSym and x.sym.kind == skParam:
@@ -193,9 +193,9 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} =
result = true
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
var a = initLocExpr(p, q[1])
var b = initLocExpr(p, q[2])
var c = initLocExpr(p, q[3])
var a = initLocExpr(p, q.secondSon)
var b = initLocExpr(p, son(q, 2))
var c = initLocExpr(p, son(q, 3))
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
# are mapped into ctPtrToArray, the dereference of which is skipped
# in the `genDeref`. We need to skip these ptrs here
@@ -221,7 +221,7 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
let lit = cIntLiteral(first)
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, cOp(Sub, NimInt, rb, lit))), lengthExpr)
of tyOpenArray, tyVarargs:
let data = if reifiedOpenArray(q[1]): dotField(ra, "Field0") else: ra
let data = if reifiedOpenArray(q.secondSon): dotField(ra, "Field0") else: ra
result = (cCast(ptrType(dest), cOp(Add, NimInt, data, rb)), lengthExpr)
of tyUncheckedArray, tyCstring:
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
@@ -258,23 +258,23 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
var q = skipConv(n)
var skipped = false
while q.kind == nkStmtListExpr and q.len > 0:
while q.kind == nkStmtListExpr and q.hasSons:
skipped = true
q = q.lastSon
if getMagic(q) == mSlice:
# magic: pass slice to openArray:
if skipped:
q = skipConv(n)
while q.kind == nkStmtListExpr and q.len > 0:
for i in 0..<q.len-1:
genStmts(p, q[i])
while q.kind == nkStmtListExpr and q.hasSons:
for it in sonsButLast(q):
genStmts(p, it)
q = q.lastSon
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
result.add(x)
result.addArgumentSeparator()
result.add(y)
else:
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n.secondSon else: n)
case skipTypes(a.t, abstractVar+{tyStatic}).kind
of tyOpenArray, tyVarargs:
let ra = rdLoc(a)
@@ -439,7 +439,7 @@ proc skipTrivialIndirections(n: PNode): PNode =
of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv:
result = result.firstSon
of nkHiddenStdConv, nkHiddenSubConv:
result = result[1]
result = result.secondSon
else: break
proc getPotentialReads(n: PNode; result: var seq[PNode]) =
@@ -453,29 +453,35 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
# We must generate temporaries in cases like #14396
# to keep the strict Left-To-Right evaluation
var needTmp = newSeq[bool](ri.len - 1)
# The arguments are walked BACKWARDS below, which a `Cursor` cannot do and
# which costs a re-walk per step even on a `PNode`. Materialize them in one
# forward pass and index that; `needTmp` already allocates per call, so this
# is the same order of work.
var args: seq[PNode] = @[]
for it in sonsFrom(ri, 1): args.add it
var needTmp = newSeq[bool](args.len)
var potentialWrites: seq[PNode] = @[]
for i in countdown(ri.len - 1, 1):
if ri[i].skipTrivialIndirections.kind == nkSym:
needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
for i in countdown(args.high, 0):
if args[i].skipTrivialIndirections.kind == nkSym:
needTmp[i] = potentialAlias(args[i], potentialWrites)
else:
#if not ri[i].typ.isCompileTimeOnly:
#if not args[i].typ.isCompileTimeOnly:
var potentialReads: seq[PNode] = @[]
getPotentialReads(ri[i], potentialReads)
getPotentialReads(args[i], potentialReads)
for n in potentialReads:
if not needTmp[i - 1]:
needTmp[i - 1] = potentialAlias(n, potentialWrites)
getPotentialWrites(ri[i], false, potentialWrites)
if not needTmp[i]:
needTmp[i] = potentialAlias(n, potentialWrites)
getPotentialWrites(args[i], false, potentialWrites)
when false:
# this optimization is wrong, see bug #23748
if ri[i].kind in {nkHiddenAddr, nkAddr}:
if args[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
needTmp[i] = false
for i, it in isons(ri, 1):
if i < typ.n.len:
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
assert(son(typ.n, i).kind == nkSym)
let paramType = son(typ.n, i)
if not paramType.typ.isCompileTimeOnly:
var arg = newBuilder("")
genArg(p, it, paramType.sym, ri, arg, needTmp[i-1])
@@ -611,22 +617,22 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
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]
let paramType = son(typ.n, i)
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
discard
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
elif paramType.typ.kind in {tyVar} and son(ri, i).kind == nkHiddenAddr:
result.addArgument(argBuilder):
genArgNoParam(p, ri[i].firstSon, result)
genArgNoParam(p, son(ri, i).firstSon, result)
else:
result.addArgument(argBuilder):
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
genArgNoParam(p, son(ri, i), result) #, son(typ.n, i).sym)
else:
if tfVarargs notin typ.flags:
localError(p.config, ri.info, "wrong argument count")
else:
result.addArgument(argBuilder):
genArgNoParam(p, ri[i], result)
genArgNoParam(p, son(ri, i), result)
discard """
Dot call syntax in C++
@@ -688,10 +694,10 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert p.config, i < typ.n.len
assert(typ.n[i].kind == nkSym)
assert(son(typ.n, i).kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
var ri = ri[i]
var ri = son(ri, i)
while ri.kind == nkObjDownConv: ri = ri.firstSon
let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink})
if t.kind in {tyVar}:
@@ -715,7 +721,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
else:
ri = skipAddrDeref(ri)
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri.firstSon
genArgNoParam(p, ri, result) #, typ.n[i].sym)
genArgNoParam(p, ri, result) #, son(typ.n, i).sym)
result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) =
@@ -730,7 +736,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
inc i
of '#':
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
let ri = ri[j]
let ri = son(ri, j)
if ri.kind in nkCallKinds:
let typ = skipTypes(ri.firstSon.typ, abstractInst)
if pat[i+1] == '+': genArgNoParam(p, ri.firstSon, result)
@@ -738,7 +744,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
if 1 < ri.len:
var callBuilder: CallBuilder = default(CallBuilder)
genOtherArg(p, ri, 1, typ, result, callBuilder)
for k in j+1..<ri.len:
for k, _ in isons(ri, j+1):
var callBuilder: CallBuilder = default(CallBuilder)
genOtherArg(p, ri, k, typ, result, callBuilder)
result.add(")")
@@ -749,7 +755,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
genThisArg(p, ri, j, typ, result)
inc i
elif i+1 < pat.len and pat[i+1] == '[':
var arg = ri[j].skipAddrDeref
var arg = son(ri, j).skipAddrDeref
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg.firstSon
genArgNoParam(p, arg, result)
#result.add debugTree(arg, 0, 10)
@@ -830,21 +836,21 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
pl.add(op.snippet)
if ri.len > 1:
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
genArg(p, ri.secondSon, typ.n.secondSon.sym, ri, pl)
start = 2
else:
if ri.len > 1:
genArg(p, ri[1], typ.n[1].sym, ri, pl)
genArg(p, ri.secondSon, typ.n.secondSon.sym, ri, pl)
pl.add(" ")
pl.add(op.snippet)
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
genArg(p, son(ri, 2), son(typ.n, 2).sym, ri, pl)
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)
var param = typ.n[i].sym
assert(son(typ.n, i).kind == nkSym)
var param = son(typ.n, i).sym
pl.add(" ")
pl.add(param.name.s)
pl.add(": ")
@@ -902,7 +908,7 @@ proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
the 'let args = ...' statement. We exploit this to generate better
code for 'return'. ]#
result = e.len == 2 and e.firstSon.kind == nkSym and
e.firstSon.sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr)
e.firstSon.sym.name.s == "=destroy" and notYetAlive(e.secondSon.skipAddr)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri):

File diff suppressed because it is too large Load Diff

View File

@@ -22,8 +22,8 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
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
if (n.firstSon.kind != nkSym): internalError(p.config, n.info, "specializeResetN")
let disc = n.firstSon.sym
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
if disc.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")

View File

@@ -98,8 +98,8 @@ proc genVarTuple(p: BProc, n: PNode) =
if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple")
# if we have a something that's been captured, use the lowering instead:
for i in 0..<n.len-2:
if n[i].kind != nkSym:
for it in sonsButLast(n, 2):
if it.kind != nkSym:
genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.module.idgen, p.prc))
return
@@ -120,10 +120,9 @@ proc genVarTuple(p: BProc, n: PNode) =
initElifBranch(p.s(cpsStmts), hcrIf, hcrCond)
genLineDir(p, n)
var tup = initLocExpr(p, n[^1])
var tup = initLocExpr(p, n.lastSon)
var t = tup.t.skipTypes(abstractInst)
for i in 0..<n.len-2:
let vn = n[i]
for i, vn in isonsButLast(n, 2):
let v = vn.sym
if sfCompileTime in v.flags: continue
backendEnsureMutable v
@@ -133,7 +132,7 @@ proc genVarTuple(p: BProc, n: PNode) =
registerTraverseProc(p, v)
else:
assignLocalVar(p, vn)
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n.lastSon))
var field = initLoc(locExpr, vn, tup.storage)
let rtup = rdLoc(tup)
let fieldName =
@@ -278,9 +277,9 @@ proc genGotoState(p: BProc, n: PNode) =
howManyExcepts = p.inExceptBlockLen)
p.s(cpsStmts).addGoto("BeforeRet_")
var statesCounter = lastOrd(p.config, n.firstSon.typ)
if n.len >= 2 and n[1].kind == nkIntLit:
statesCounter = getInt(n[1])
let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
if n.len >= 2 and n.secondSon.kind == nkIntLit:
statesCounter = getInt(n.secondSon)
let prefix = if n.len == 3 and son(n, 2).kind == nkStrLit: son(n, 2).strVal.rope
else: rope"STATE"
for i in 0i64..toInt64(statesCounter):
p.s(cpsStmts).addSingleSwitchCase(cIntValue(i)):
@@ -291,7 +290,7 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
d = initLoc(locExpr, n, OnUnknown)
if n.firstSon.kind == nkClosure:
a = initLocExpr(p, n.firstSon[1])
a = initLocExpr(p, n.firstSon.secondSon)
let ra = a.rdLoc
d.snippet = cOp(LessThan,
subscript(
@@ -478,15 +477,15 @@ proc genSingleVar(p: BProc, a: PNode) =
discard
else:
return
genSingleVar(p, v, a.firstSon, a[2])
genSingleVar(p, v, a.firstSon, son(a, 2))
proc genClosureVar(p: BProc, a: PNode) =
var immediateAsgn = a[2].kind != nkEmpty
var immediateAsgn = son(a, 2).kind != nkEmpty
var v: TLoc = initLocExpr(p, a.firstSon)
genLineDir(p, a)
if immediateAsgn:
loadInto(p, a.firstSon, a[2], v)
elif sfNoInit notin a.firstSon[1].sym.flags:
loadInto(p, a.firstSon, son(a, 2), v)
elif sfNoInit notin a.firstSon.secondSon.sym.flags:
constructLoc(p, v)
proc genVarStmt(p: BProc, n: PNode) =
@@ -538,9 +537,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
if p.module.compileToCpp:
# avoid "jump to label crosses initialization" error:
p.s(cpsStmts).addScope():
expr(p, it[1], d)
expr(p, it.secondSon, d)
else:
expr(p, it[1], d)
expr(p, it.secondSon, d)
endSimpleBlock(p, scope)
if n.len > 1:
p.s(cpsStmts).addGoto(lend)
@@ -578,11 +577,11 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) =
var scope: ScopeBuilder
startSimpleBlock(p, scope)
let it = child
for j in 0..<it.len-1:
if it[j].kind == nkRange:
for label in sonsButLast(it):
if label.kind == nkRange:
localError(p.config, it.info, "range notation not available for computed goto")
return
let val = getOrdValue(it[j])
let val = getOrdValue(label)
p.s(cpsStmts).addLabel("NIMSTATE_" & $val)
genStmts(p, it.lastSon)
endSimpleBlock(p, scope)
@@ -592,9 +591,8 @@ iterator fieldValuePairs(n: PNode): tuple[memberSym, valueSym: PNode] =
assert(n.kind in {nkLetSection, nkVarSection})
for identDefs in n:
if identDefs.kind == nkIdentDefs:
let valueSym = identDefs[^1]
for i in 0..<identDefs.len-2:
let memberSym = identDefs[i]
let valueSym = identDefs.lastSon
for memberSym in sonsButLast(identDefs, 2):
yield((memberSym: memberSym, valueSym: valueSym))
proc genComputedGoto(p: BProc; n: PNode) =
@@ -637,10 +635,11 @@ proc genComputedGoto(p: BProc; n: PNode) =
p.s(cpsStmts).addField(labelsInit, ""):
p.s(cpsStmts).add(cLabelAddr("TMP" & $(id+i) & "_"))
for j in 0..<casePos:
genStmts(p, n[j])
for j, it in isons(n):
if j >= casePos: break
genStmts(p, it)
let caseStmt = n[casePos]
let caseStmt = son(n, casePos)
var a: TLoc = initLocExpr(p, caseStmt.firstSon)
let ra = a.rdLoc
# first goto:
@@ -650,43 +649,43 @@ proc genComputedGoto(p: BProc; n: PNode) =
var scope: ScopeBuilder
startSimpleBlock(p, scope)
let it = child
for j in 0..<it.len-1:
if it[j].kind == nkRange:
for label in sonsButLast(it):
if label.kind == nkRange:
localError(p.config, it.info, "range notation not available for computed goto")
return
let val = getOrdValue(it[j])
let val = getOrdValue(label)
let lit = cIntLiteral(toInt64(val)+id+1)
p.s(cpsStmts).addLabel("TMP" & lit & "_")
genStmts(p, it.lastSon)
for j in casePos+1..<n.len:
genStmts(p, n[j])
for after in sonsFrom(n, casePos+1):
genStmts(p, after)
for j in 0..<casePos:
for j, before in isons(n):
if j >= casePos: break
# prevent new local declarations
# compile declarations as assignments
let it = n[j]
if it.kind in {nkLetSection, nkVarSection}:
let asgn = copyNode(it)
if before.kind in {nkLetSection, nkVarSection}:
let asgn = copyNode(before)
asgn.transitionSonsKind(nkAsgn)
asgn.sons.setLen 2
for sym, value in it.fieldValuePairs:
for sym, value in before.fieldValuePairs:
if value.kind != nkEmpty:
asgn[0] = sym
asgn[1] = value
asgn.secondSon = value
genStmts(p, asgn)
else:
genStmts(p, it)
genStmts(p, before)
var a: TLoc = initLocExpr(p, caseStmt.firstSon)
let ra = a.rdLoc
p.s(cpsStmts).addComputedGoto(subscript(tmp, ra))
endSimpleBlock(p, scope)
for j in casePos+1..<n.len:
genStmts(p, n[j])
for it in sonsFrom(n, casePos+1):
genStmts(p, it)
proc genWhileStmt(p: BProc, t: PNode) =
@@ -699,12 +698,12 @@ proc genWhileStmt(p: BProc, t: PNode) =
genLineDir(p, t)
preserveBreakIdx:
var loopBody = t[1]
var loopBody = t.secondSon
if loopBody.stmtsContainPragma(wComputedGoto) and
hasComputedGoto in CC[p.config.cCompiler].props:
# for closure support weird loop bodies are generated:
if loopBody.len == 2 and loopBody.firstSon.kind == nkEmpty:
loopBody = loopBody[1]
loopBody = loopBody.secondSon
genComputedGoto(p, loopBody)
else:
var stmt: WhileBuilder
@@ -746,7 +745,7 @@ proc genBlock(p: BProc, n: PNode, d: var TLoc) =
sym.locImpl.k = locOther
sym.positionImpl = p.breakIdx+1
# ^ IC: review this
expr(p, n[1], d)
expr(p, n.secondSon, d)
endSimpleBlock(p, scope)
proc genParForStmt(p: BProc, t: PNode) =
@@ -759,21 +758,21 @@ proc genParForStmt(p: BProc, t: PNode) =
assignLocalVar(p, t.firstSon)
#initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
#discard mangleName(forLoopVar)
let call = t[1]
let call = t.secondSon
assert(call.len == 4 or call.len == 5)
var rangeA = initLocExpr(p, call[1])
var rangeB = initLocExpr(p, call[2])
var rangeA = initLocExpr(p, call.secondSon)
var rangeB = initLocExpr(p, son(call, 2))
var stepNode: PNode = nil
# $n at the beginning because of #9710
if call.len == 4: # procName(a, b, annotation)
if call.firstSon.sym.name.s == "||": # `||`(a, b, annotation)
p.s(cpsStmts).addCPragma("omp " & call[3].getStr)
p.s(cpsStmts).addCPragma("omp " & son(call, 3).getStr)
else:
p.s(cpsStmts).addCPragma(call[3].getStr)
p.s(cpsStmts).addCPragma(son(call, 3).getStr)
else: # `||`(a, b, step, annotation)
stepNode = call[3]
p.s(cpsStmts).addCPragma("omp " & call[4].getStr)
stepNode = son(call, 3)
p.s(cpsStmts).addCPragma("omp " & son(call, 4).getStr)
p.breakIdx = startBlockWith(p):
if stepNode == nil:
@@ -782,7 +781,7 @@ proc genParForStmt(p: BProc, t: PNode) =
var step: TLoc = initLocExpr(p, stepNode)
initForStep(p.s(cpsStmts), forLoopVar.loc.rdLoc, rangeA.rdLoc, rangeB.rdLoc, step.rdLoc, true)
p.blocks[p.breakIdx].isLoop = true
genStmts(p, t[2])
genStmts(p, son(t, 2))
endBlockWith(p):
finishFor(p.s(cpsStmts))
@@ -909,17 +908,17 @@ proc genRaiseStmt(p: BProc, t: PNode) =
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
rangeFormat, eqFormat: untyped) =
var x, y: TLoc
for i in 0..<b.len - 1:
for it in sonsButLast(b):
let rlabel {.inject.} = labl
if b[i].kind == nkRange:
x = initLocExpr(p, b[i].firstSon)
y = initLocExpr(p, b[i][1])
if it.kind == nkRange:
x = initLocExpr(p, it.firstSon)
y = initLocExpr(p, it.secondSon)
let ra {.inject.} = rdCharLoc(e)
let rb {.inject.} = rdCharLoc(x)
let rc {.inject.} = rdCharLoc(y)
rangeFormat
else:
x = initLocExpr(p, b[i])
x = initLocExpr(p, it)
let ra {.inject.} = rdCharLoc(e)
let rb {.inject.} = rdCharLoc(x)
eqFormat
@@ -927,15 +926,16 @@ template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
labId, until: int): TLabel =
var lend = getLabel(p)
for i in 1..until:
for i, branch in isons(t, 1):
if i > until: break
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
p.s(cpsStmts).addLabel("LA" & $(labId + i) & "_")
if t[i].kind == nkOfBranch:
exprBlock(p, t[i][^1], d)
if branch.kind == nkOfBranch:
exprBlock(p, branch.lastSon, d)
p.s(cpsStmts).addGoto(lend)
else:
exprBlock(p, t[i].firstSon, d)
exprBlock(p, branch.firstSon, d)
result = lend
template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
@@ -944,11 +944,12 @@ template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
# generate a C-if statement for a Nim case statement
var res: TLabel
var labId = p.labels
for i in 1..until:
for i, branch in isons(t, 1):
if i > until: break
inc(p.labels)
let lab = "LA" & $p.labels & "_"
if t[i].kind == nkOfBranch: # else statement
genCaseGenericBranch(p, t[i], a, lab, rangeFormat, eqFormat)
if branch.kind == nkOfBranch: # else statement
genCaseGenericBranch(p, branch, a, lab, rangeFormat, eqFormat)
else:
p.s(cpsStmts).addGoto(lab)
if until < t.len-1:
@@ -971,13 +972,13 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
stringKind: TTypeKind,
branches: var openArray[Builder]) =
var x: TLoc
for i in 0..<b.len - 1:
assert(b[i].kind != nkRange)
x = initLocExpr(p, b[i])
for it in sonsButLast(b):
assert(it.kind != nkRange)
x = initLocExpr(p, it)
var j: int = 0
case b[i].kind
case it.kind
of nkStrLit..nkTripleStrLit:
j = int(hashString(p.config, b[i].strVal) and high(branches))
j = int(hashString(p.config, it.strVal) and high(branches))
of nkNilLit: j = 0
else:
assert false, "invalid string case branch node kind"
@@ -1022,7 +1023,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
p.s(cpsStmts).add(extract(branches[j]))
p.s(cpsStmts).addBreak()
# else statement:
if t[^1].kind != nkOfBranch:
if t.lastSon.kind != nkOfBranch:
p.s(cpsStmts).addGoto("LA" & rope(p.labels) & "_")
# third pass: generate statements
var lend = genCaseSecondPass(p, t, d, labId, t.len-1)
@@ -1043,7 +1044,7 @@ proc branchHasTooBigRange(b: PNode): bool =
for it in b:
# last son is block
if (it.kind == nkRange) and
it[1].intVal - it.firstSon.intVal > RangeExpandLimit:
it.secondSon.intVal - it.firstSon.intVal > RangeExpandLimit:
return true
proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
@@ -1057,24 +1058,24 @@ proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
result = i
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder) =
for j in 0..<branch.len-1:
if branch[j].kind == nkRange:
for it in sonsButLast(branch):
if it.kind == nkRange:
if hasSwitchRange in CC[p.config.cCompiler].props:
var litA = newBuilder("")
var litB = newBuilder("")
genLiteral(p, branch[j].firstSon, litA)
genLiteral(p, branch[j][1], litB)
genLiteral(p, it.firstSon, litA)
genLiteral(p, it.secondSon, litB)
p.s(cpsStmts).addCaseRange(info, extract(litA), extract(litB))
else:
var v = copyNode(branch[j].firstSon)
while v.intVal <= branch[j][1].intVal:
var v = copyNode(it.firstSon)
while v.intVal <= it.secondSon.intVal:
var litA = newBuilder("")
genLiteral(p, v, litA)
p.s(cpsStmts).addCase(info, extract(litA))
inc(v.intVal)
else:
var litA = newBuilder("")
genLiteral(p, branch[j], litA)
genLiteral(p, it, litA)
p.s(cpsStmts).addCase(info, extract(litA))
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
@@ -1100,10 +1101,9 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
let rca = rdCharLoc(a)
p.s(cpsStmts).addSwitchStmt(rca):
var hasDefault = false
for i in splitPoint+1..<n.len:
for branch in sonsFrom(n, splitPoint+1):
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
var branch = n[i]
var caseBuilder: SwitchCaseBuilder
p.s(cpsStmts).addSwitchCase(caseBuilder):
if branch.kind == nkOfBranch:
@@ -1196,7 +1196,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
#init on locals, fixes #23306
lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
let fin = if t.lastSon.kind == nkFinally: t.lastSon else: nil
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
if t.kind == nkHiddenTryStmt:
@@ -1221,10 +1221,11 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
var ifStmt = default(IfBuilder)
var hasIf = false
var hasElse = false
while (i < t.len) and (t[i].kind == nkExceptBranch):
while i < t.len and son(t, i).kind == nkExceptBranch:
let exceptBranch = son(t, i)
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
if t[i].len == 1:
if exceptBranch.len == 1:
hasImportedCppExceptions = true
hasElse = true
# general except section:
@@ -1236,7 +1237,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
scope = initScope(p.s(cpsStmts))
# we handled the error:
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
expr(p, t[i].firstSon, d)
expr(p, exceptBranch.firstSon, d)
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
endBlockWith(p):
if hasIf:
@@ -1246,11 +1247,11 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
else:
var orExpr = newRopeAppender()
var exvar = PNode(nil)
for j in 0..<t[i].len - 1:
var typeNode = t[i][j]
if t[i][j].isInfixAs():
typeNode = t[i][j][1]
exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
for label in sonsButLast(exceptBranch):
var typeNode = label
if label.isInfixAs():
typeNode = label.secondSon
exvar = son(label, 2) # ex1 in `except ExceptType as ex1:`
assert(typeNode.kind == nkType)
if isImportedException(typeNode.typ, p.config):
hasImportedCppExceptions = true
@@ -1278,7 +1279,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
rdLoc(exvar.sym.loc), rope(etmp+1)])
# we handled the error:
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
expr(p, t[i][^1], d)
expr(p, exceptBranch.lastSon, d)
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
endBlockWith(p):
finishBranch(p.s(cpsStmts), ifStmt)
@@ -1314,31 +1315,31 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
p.s(cpsStmts).add("}\n")
catchAllPresent = true
else:
for j in 0..<it.len-1:
var typeNode = it[j]
if it[j].isInfixAs():
typeNode = it[j][1]
for label in sonsButLast(it):
var typeNode = label
if label.isInfixAs():
typeNode = label.secondSon
if isImportedException(typeNode.typ, p.config):
let exvar = it[j][2] # ex1 in `except ExceptType as ex1:`
let exvar = son(label, 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(it[^1]) # exception handler body will duplicated for every type
genExceptBranchBody(it.lastSon) # 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, it[j].typ)])
genExceptBranchBody(it[^1]) # exception handler body will duplicated for every type
lineCg(p, cpsStmts, "catch ($1&) {$n", [getTypeDesc(p.module, label.typ)])
genExceptBranchBody(it.lastSon) # exception handler body will duplicated for every type
endBlockWith(p):
p.s(cpsStmts).add("}\n")
excl p.flags, noSafePoints
discard pop(p.nestedTryStmts)
# general finally block:
if t.len > 0 and t[^1].kind == nkFinally:
if t.hasSons and t.lastSon.kind == nkFinally:
if not catchAllPresent:
startBlockWith(p):
p.s(cpsStmts).add("catch (...) {\n")
@@ -1349,7 +1350,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
var scope: ScopeBuilder
startSimpleBlock(p, scope)
genStmts(p, t[^1].firstSon)
genStmts(p, t.lastSon.firstSon)
linefmt(p, cpsStmts, "if (T$1_) std::rethrow_exception(T$1_);$n", [etmp])
endSimpleBlock(p, scope)
@@ -1372,10 +1373,10 @@ proc bodyCanRaise(p: BProc; n: PNode): bool =
result = false
proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
let fin = if t.lastSon.kind == nkFinally: t.lastSon else: nil
inc p.labels
let lab = p.labels
let hasExcept = t[1].kind == nkExceptBranch
let hasExcept = t.secondSon.kind == nkExceptBranch
if hasExcept: inc p.withinTryWithExcept
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, Natural lab))
@@ -1389,7 +1390,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
var ifStmt = default(IfBuilder)
var scope = default(ScopeBuilder)
var isIf = false
if 1 < t.len and t[1].kind == nkExceptBranch:
if 1 < t.len and t.secondSon.kind == nkExceptBranch:
startBlockWith(p):
isIf = true
ifStmt = initIfStmt(p.s(cpsStmts))
@@ -1404,7 +1405,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
var innerIfStmt = default(IfBuilder)
var innerScope = default(ScopeBuilder)
var innerIsIf = false
while (i < t.len) and (t[i].kind == nkExceptBranch):
while i < t.len and son(t, i).kind == nkExceptBranch:
let exceptBranch = son(t, i)
inc p.labels
let nextExcept = p.labels
@@ -1413,7 +1415,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
var isScope = false
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
if t[i].len == 1:
if exceptBranch.len == 1:
# general except section:
startBlockWith(p):
if innerIsIf:
@@ -1423,14 +1425,14 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
innerScope = initScope(p.s(cpsStmts))
# we handled the exception, remember this:
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), NimFalse)
expr(p, t[i].firstSon, d)
expr(p, exceptBranch.firstSon, d)
else:
if not innerIsIf:
innerIsIf = true
innerIfStmt = initIfStmt(p.s(cpsStmts))
var orExpr: Snippet = ""
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
for label in sonsButLast(exceptBranch):
assert(label.kind == nkType)
var excVal = cCall(cgsymValue(p.module, "nimBorrowCurrentException"))
let member =
if p.module.compileToCpp:
@@ -1439,13 +1441,13 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
dotField(derefField(excVal, "Sup"), "m_type")
var branch: Snippet = ""
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
let checkFor = $getObjDepth(label.typ)
branch = cCall(cgsymValue(p.module, "isObjDisplayCheck"),
member,
checkFor,
$genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config))))
$genDisplayElem(MD5Digest(hashType(label.typ, p.config))))
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
let checkFor = genTypeInfoV1(p.module, label.typ, label.info)
branch = cCall(cgsymValue(p.module, "isObj"),
member,
checkFor)
@@ -1458,7 +1460,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
initElifBranch(p.s(cpsStmts), innerIfStmt, orExpr)
# we handled the exception, remember this:
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), NimFalse)
expr(p, t[i][^1], d)
expr(p, exceptBranch.lastSon, d)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
p.s(cpsStmts).addLabel("LA" & $nextExcept & "_")
@@ -1479,19 +1481,20 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
else:
finishScope(p.s(cpsStmts), scope)
if i < t.len and t[i].kind == nkFinally:
if i < t.len and son(t, i).kind == nkFinally:
let finallyBranch = son(t, i)
var finallyScope: ScopeBuilder
startSimpleBlock(p, finallyScope)
if not bodyCanRaise(p, t[i].firstSon):
if not bodyCanRaise(p, finallyBranch.firstSon):
# this is an important optimization; most destroy blocks are detected not to raise an
# exception and so we help the C optimizer by not mutating nimErr_ pointlessly:
genStmts(p, t[i].firstSon)
genStmts(p, finallyBranch.firstSon)
else:
# pretend we did handle the error for the safe execution of the 'finally' section:
p.procSec(cpsLocals).addVar(kind = Local, name = "oldNimErrFin" & $lab & "_", typ = NimBool)
p.s(cpsStmts).addAssignment("oldNimErrFin" & $lab & "_", cDeref("nimErr_"))
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), NimFalse)
genStmts(p, t[i].firstSon)
genStmts(p, finallyBranch.firstSon)
# this is correct for all these cases:
# 1. finally is run during ordinary control flow
# 2. finally is run after 'except' block handling: these however set the
@@ -1582,7 +1585,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
nonQuirkyIf = initIfStmt(p.s(cpsStmts))
initElifBranch(p.s(cpsStmts), nonQuirkyIf, removeSinglePar(
cOp(Equal, dotField(safePoint, "status"), cIntValue(0))))
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
let fin = if t.lastSon.kind == nkFinally: t.lastSon else: nil
p.nestedTryStmts.add((fin, quirkyExceptions, t.kind == nkHiddenTryStmt, 0.Natural))
expr(p, t.firstSon, d)
var quirkyIf = default(IfBuilder)
@@ -1595,7 +1598,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
initElseBranch(p.s(cpsStmts), nonQuirkyIf)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popSafePoint"))
genRestoreFrameAfterException(p)
elif 1 < t.len and t[1].kind == nkExceptBranch:
elif 1 < t.len and t.secondSon.kind == nkExceptBranch:
startBlockWith(p):
quirkyIf = initIfStmt(p.s(cpsStmts))
initElifBranch(p.s(cpsStmts), quirkyIf,
@@ -1608,10 +1611,11 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
var i = 1
var exceptIf = default(IfBuilder)
var exceptIfInited = false
while (i < t.len) and (t[i].kind == nkExceptBranch):
while i < t.len and son(t, i).kind == nkExceptBranch:
let exceptBranch = son(t, i)
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
if t[i].len == 1:
if exceptBranch.len == 1:
# general except section:
var scope = default(ScopeBuilder)
startBlockWith(p):
@@ -1621,7 +1625,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
scope = initScope(p.s(cpsStmts))
if not quirkyExceptions:
p.s(cpsStmts).addFieldAssignment(safePoint, "status", cIntValue(0))
expr(p, t[i].firstSon, d)
expr(p, exceptBranch.firstSon, d)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
endBlockWith(p):
if exceptIfInited:
@@ -1630,8 +1634,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
finishScope(p.s(cpsStmts), scope)
else:
var orExpr: Snippet = ""
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
for label in sonsButLast(exceptBranch):
assert(label.kind == nkType)
var excVal = cCall(cgsymValue(p.module, "nimBorrowCurrentException"))
let member =
if p.module.compileToCpp:
@@ -1640,13 +1644,13 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
dotField(derefField(excVal, "Sup"), "m_type")
var branch: Snippet = ""
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
let checkFor = $getObjDepth(label.typ)
branch = cCall(cgsymValue(p.module, "isObjDisplayCheck"),
member,
checkFor,
$genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config))))
$genDisplayElem(MD5Digest(hashType(label.typ, p.config))))
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
let checkFor = genTypeInfoV1(p.module, label.typ, label.info)
branch = cCall(cgsymValue(p.module, "isObj"),
member,
checkFor)
@@ -1662,7 +1666,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
initElifBranch(p.s(cpsStmts), exceptIf, orExpr)
if not quirkyExceptions:
p.s(cpsStmts).addFieldAssignment(safePoint, "status", cIntValue(0))
expr(p, t[i][^1], d)
expr(p, exceptBranch.lastSon, d)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
endBlockWith(p):
finishBranch(p.s(cpsStmts), exceptIf)
@@ -1680,11 +1684,12 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
else:
finishBranch(p.s(cpsStmts), quirkyIf)
finishIfStmt(p.s(cpsStmts), quirkyIf)
if i < t.len and t[i].kind == nkFinally:
if i < t.len and son(t, i).kind == nkFinally:
let finallyBranch = son(t, i)
p.finallySafePoints.add(safePoint)
var finallyScope: ScopeBuilder
startSimpleBlock(p, finallyScope)
genStmts(p, t[i].firstSon)
genStmts(p, finallyBranch.firstSon)
# pretend we handled the exception in a 'finally' so that we don't
# re-raise the unhandled one but instead keep the old one (it was
# not popped either):
@@ -1758,7 +1763,7 @@ proc genAsmStmt(p: BProc, t: PNode) =
if (let p = t.firstSon; p.kind == nkPragma):
for i in p:
if whichPragma(i) == wAsmSyntax:
asmSyntax = i[1].strVal
asmSyntax = i.secondSon.strVal
if asmSyntax != "" and
not (
@@ -1789,10 +1794,10 @@ proc determineSection(n: PNode): TCFileSection =
proc genEmit(p: BProc, t: PNode) =
var s = newRopeAppender()
genAsmOrEmitStmt(p, t[1], false, s)
genAsmOrEmitStmt(p, t.secondSon, false, s)
if p.prc == nil:
# top level emit pragma?
let section = determineSection(t[1])
let section = determineSection(t.secondSon)
genCLineDir(p.module.s[section], t.info, p.config)
p.module.s[section].add(s)
else:
@@ -1835,9 +1840,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.firstSon
var a = initLocExpr(p, e.firstSon)
var tmp: TLoc = getTemp(p, a.t)
expr(p, e[1], tmp)
expr(p, e.secondSon, tmp)
if p.inUncheckedAssignSection == 0:
let field = dotExpr[1].sym
let field = dotExpr.secondSon.sym
genDiscriminantCheck(p, a, tmp, dotExpr.firstSon.typ, field)
message(p.config, e.info, warnCaseTransition)
genAssignment(p, a, tmp, {})
@@ -1845,7 +1850,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
if e.firstSon.kind == nkSym and sfGoto in e.firstSon.sym.flags:
genLineDir(p, e)
genGotoVar(p, e[1])
genGotoVar(p, e.secondSon)
elif optFieldCheck in p.options and isDiscriminantField(e.firstSon):
genLineDir(p, e)
asgnFieldDiscriminant(p, e)
@@ -1854,13 +1859,13 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
# nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally)
genLineDir(p, e)
var base = initLocExpr(p, e.firstSon.firstSon)
var idx = initLocExpr(p, e.firstSon[1])
var rhs = initLocExpr(p, e[1])
var idx = initLocExpr(p, e.firstSon.secondSon)
var rhs = initLocExpr(p, e.secondSon)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimStrPutV3"),
byRefLoc(p, base), rdLoc(idx), rdCharLoc(rhs))
else:
let le = e.firstSon
let ri = e[1]
let ri = e.secondSon
var a: TLoc = initLoc(locNone, le, OnUnknown)
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar)
a.flags.incl(lfEnforceDeref)

View File

@@ -34,9 +34,9 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
for it in sons(n):
genTraverseProc(c, accessor, it, typ)
of nkRecCase:
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
if (n.firstSon.kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
let disc = n[0].sym
let disc = n.firstSon.sym
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
if disc.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")

View File

@@ -598,10 +598,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t.returnType, check, dkResult)])
var types, names, args: seq[string] = @[]
if not isCtor:
var this = t.n[1].sym
var this = t.n.secondSon.sym
backendEnsureMutable this
fillParamName(m, this)
fillLoc(this.locImpl, locParam, t.n[1],
fillLoc(this.locImpl, locParam, t.n.secondSon,
this.paramStorageLoc)
if this.typ.kind == tyPtr:
this.locImpl.snippet = "this"
@@ -1267,7 +1267,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Builder; asPtr: bool
var check = initIntSet()
fillBackendName(m, prc)
backendEnsureMutable prc
fillLoc(prc.locImpl, locProc, prc.ast[namePos], OnUnknown)
fillLoc(prc.locImpl, locProc, son(prc.ast, namePos), OnUnknown)
var memberOp = "#." #only virtual
var typ: PType
if isCtor:
@@ -1321,7 +1321,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Builder; visibility: var D
var check = initIntSet()
fillBackendName(m, prc)
backendEnsureMutable prc
fillLoc(prc.locImpl, locProc, prc.ast[namePos], OnUnknown)
fillLoc(prc.locImpl, locProc, son(prc.ast, namePos), OnUnknown)
var rettype: Snippet = ""
var desc = newBuilder("")
genProcParams(m, prc.typ, rettype, desc, check, true, false)
@@ -1559,15 +1559,15 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
of nkOfBranch:
if b.len < 2:
internalError(m.config, b.info, "genObjectFields; nkOfBranch broken")
for j in 0..<b.len - 1:
if b[j].kind == nkRange:
var x = toInt(getOrdValue(b[j].firstSon))
var y = toInt(getOrdValue(b[j][1]))
for label in sonsButLast(b):
if label.kind == nkRange:
var x = toInt(getOrdValue(label.firstSon))
var y = toInt(getOrdValue(label.secondSon))
while x <= y:
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(x), cAddr(tmp2))
inc(x)
else:
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(getOrdValue(b[j])), cAddr(tmp2))
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(getOrdValue(label)), cAddr(tmp2))
of nkElse:
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(L), cAddr(tmp2))
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
@@ -2289,7 +2289,7 @@ proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rop
proc retrieveSym(n: PNode): PSym =
case n.kind
of nkPostfix: result = retrieveSym(n[1])
of nkPostfix: result = retrieveSym(n.secondSon)
of nkPragmaExpr, nkTypeDef: result = retrieveSym(n.firstSon)
of nkSym: result = n.sym
else: result = nil

View File

@@ -172,12 +172,12 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
raiseAssert "unreachable"
of tyRange:
var val = "range_"
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
val.addFloat t.n[0].floatVal
if t.n.firstSon.typ.kind in {tyFloat..tyFloat128}:
val.addFloat t.n.firstSon.floatVal
val.add "_"
val.addFloat t.n[1].floatVal
val.addFloat t.n.secondSon.floatVal
else:
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
val.add $t.n.firstSon.intVal & "_" & $t.n.secondSon.intVal
result = encodeName(val)
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)

View File

@@ -187,7 +187,7 @@ proc ownsRuntimeRoutine*(s: PSym; modPos: int): bool =
{sfForward, sfImportc, sfCompileTime, sfError} * s.flags == {} and
s.typ != nil and not signatureHasMetaType(s.typ) and
s.ast != nil and s.ast.safeLen > bodyPos and
s.ast[genericParamsPos].kind == nkEmpty
son(s.ast, genericParamsPos).kind == nkEmpty
# NOTE: an `nkEmpty` body is NOT a disqualifier. A concrete, owned, non-
# forward/-importc/-magic routine whose body folds to nothing is still a real
# definition the owner must emit (`void f(void){}`), exactly as whole-program
@@ -1207,7 +1207,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
cCast(getTypeDesc(m, sym.typ, dkVar),
cCall(callee, params)))
var last = lastSon(n)
if last.kind == nkHiddenStdConv: last = last[1]
if last.kind == nkHiddenStdConv: last = last.secondSon
internalAssert(m.config, last.kind == nkStrLit)
let idx = last.strVal
if idx.len == 0:
@@ -1312,14 +1312,14 @@ proc closeNamespaceNim(result: var Builder) =
proc closureSetup(p: BProc, prc: PSym) =
if tfCapturesEnv notin prc.typ.flags: return
# prc.ast[paramsPos].last contains the type we're after — BUT a closure loaded
# The `paramsPos` child of `prc.ast` has the type we're after — BUT a closure loaded
# from a `.t.bif` (a lambda-lifted nested proc / generic instance the `lower`
# stage transformed) can arrive with an EMPTY AST param node: the lifted hidden
# `:env` param lives in `typ.n`, the authoritative signature (`genProc` already
# reads `typ.n`, not the AST). The two param nodes diverge across the NIF
# boundary; fall back to `typ.n` so the env param resolves instead of indexing
# an empty container.
var params = prc.ast[paramsPos]
var params = son(prc.ast, paramsPos)
if params.safeLen == 0 and prc.typ.n != nil and prc.typ.n.kind == nkFormalParams:
params = prc.typ.n
var ls = lastSon(params)
@@ -1352,7 +1352,7 @@ proc containsResult(n: BNode): bool =
of nkReturnStmt:
for ni in n.sons:
if containsResult(ni): return true
result = n.len > 0 and n.firstSon.kind == nkEmpty
result = n.hasSons and n.firstSon.kind == nkEmpty
of nkSym:
if n.sym.kind == skResult:
result = true
@@ -1368,11 +1368,11 @@ proc easyResultAsgn(n: PNode): PNode =
while i < n.len and n[i].kind in harmless: inc i
if i < n.len: result = easyResultAsgn(n[i])
of nkAsgn, nkFastAsgn, nkSinkAsgn:
if n.firstSon.kind == nkSym and n.firstSon.sym.kind == skResult and not containsResult(n[1]):
if n.firstSon.kind == nkSym and n.firstSon.sym.kind == skResult and not containsResult(n.secondSon):
incl n.flags, nfPreventCg
return n[1]
return n.secondSon
of nkReturnStmt:
if n.len > 0:
if n.hasSons:
result = easyResultAsgn(n.firstSon)
if result != nil: incl n.flags, nfPreventCg
else: discard
@@ -1414,8 +1414,8 @@ proc allPathsAsgnResult(p: BProc; n: BNode): InitResultEnum =
if result != Unknown: return result
of nkAsgn, nkFastAsgn, nkSinkAsgn:
if n.firstSon.kind == nkSym and n.firstSon.sym.kind == skResult:
if not containsResult(n[1]):
if allPathsAsgnResult(p, n[1]) == InitRequired:
if not containsResult(n.secondSon):
if allPathsAsgnResult(p, n.secondSon) == InitRequired:
result = InitRequired
else:
result = InitSkippable
@@ -1423,9 +1423,9 @@ proc allPathsAsgnResult(p: BProc; n: BNode): InitResultEnum =
elif containsResult(n):
result = InitRequired
else:
result = allPathsAsgnResult(p, n[1])
result = allPathsAsgnResult(p, n.secondSon)
of nkReturnStmt:
if n.len > 0:
if n.hasSons:
if n.firstSon.kind == nkEmpty and result != InitSkippable:
# This is a bare `return` statement, if `result` was not initialized
# anywhere else (or if we're not sure about this) let's require it to be
@@ -1459,7 +1459,7 @@ proc allPathsAsgnResult(p: BProc; n: BNode): InitResultEnum =
# condition and that would be fine. Everything else isn't:
result = allPathsAsgnResult(p, n.firstSon)
if result == Unknown:
result = allPathsAsgnResult(p, n[1])
result = allPathsAsgnResult(p, n.secondSon)
# we cannot assume that the 'while' loop is really executed at least once:
if result == InitSkippable: result = Unknown
of harmless:
@@ -1592,7 +1592,7 @@ proc genProcLvl3*(m: BModule, prc: PSym) =
if sfPure notin prc.flags and prc.typ.returnType != nil:
if resultPos >= prc.ast.len:
internalError(m.config, prc.info, "proc has no result symbol")
let resNode = prc.ast[resultPos]
let resNode = son(prc.ast, resultPos)
let res = resNode.sym # get result symbol
if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags:
if sfNoInit in prc.flags: incl(res, sfNoInit)
@@ -1832,17 +1832,17 @@ include inliner
proc genProcLvl2(m: BModule, prc: PSym) =
if lfImportCompilerProc in prc.loc.flags:
fillProcLoc(m, prc.ast[namePos])
fillProcLoc(m, son(prc.ast, namePos))
useHeader(m, prc)
# dependency to a compilerproc:
cgsym(m, prc.name.s)
return
if lfNoDecl in prc.loc.flags:
fillProcLoc(m, prc.ast[namePos])
fillProcLoc(m, son(prc.ast, namePos))
genProcPrototype(m, prc)
elif lfDynamicLib in prc.loc.flags:
var q = findPendingModule(m, prc)
fillProcLoc(q, prc.ast[namePos])
fillProcLoc(q, son(prc.ast, namePos))
genProcPrototype(m, prc)
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
symInDynamicLib(q, prc)
@@ -1869,13 +1869,13 @@ proc genProcLvl2(m: BModule, prc: PSym) =
# not on the first module that uses it
if m.module.itemId.module != prc.itemId.module and optCompress in m.config.globalOptions:
let prcCopy = prc # copyInlineProc(prc, m.idgen)
fillProcLoc(m, prcCopy.ast[namePos])
fillProcLoc(m, son(prcCopy.ast, namePos))
genProcPrototype(m, prcCopy)
genProcLvl3(m, prcCopy)
else:
let m2 = if m.config.symbolFiles != disabledSf: m
else: findPendingModule(m, prc)
fillProcLoc(m2, prc.ast[namePos])
fillProcLoc(m2, son(prc.ast, namePos))
#elif {sfExportc, sfImportc} * prc.flags == {}:
# # reset name to restore consistency in case of hashing collisions:
# #echo "resetting ", prc.id, " by ", m.module.name.s
@@ -1885,7 +1885,7 @@ proc genProcLvl2(m: BModule, prc: PSym) =
genProcLvl3(m, prc)
elif sfImportc notin prc.flags:
var q = findPendingModule(m, prc)
fillProcLoc(q, prc.ast[namePos])
fillProcLoc(q, son(prc.ast, namePos))
# generate a getProc call to initialize the pointer for this
# externally-to-the-current-module defined proc, also important
# to do the declaredProtos check before the call to genProcPrototype
@@ -1906,7 +1906,7 @@ proc genProcLvl2(m: BModule, prc: PSym) =
if emitsBodyInThisModule(m, prc):
genProcLvl3(q, prc)
else:
fillProcLoc(m, prc.ast[namePos])
fillProcLoc(m, son(prc.ast, namePos))
useHeader(m, prc)
if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
@@ -1928,7 +1928,7 @@ proc genProc(m: BModule, prc: PSym) =
if sfBorrow in prc.flags or not isActivated(prc): return
if sfForward in prc.flags:
addForwardedProc(m, prc)
fillProcLoc(m, prc.ast[namePos])
fillProcLoc(m, son(prc.ast, namePos))
else:
genProcLvl2(m, prc)
if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and
@@ -2468,7 +2468,7 @@ proc genDatInitCode(m: BModule) =
proc hcrGetProcLoadCode(builder: var Builder, m: BModule, sym, prefix, handle, getProcFunc: string) =
let prc = magicsys.getCompilerProc(m.g.graph, sym)
assert prc != nil
fillProcLoc(m, prc.ast[namePos])
fillProcLoc(m, son(prc.ast, namePos))
var tmp = mangleDynLibProc(prc)
backendEnsureMutable prc