IC: migrate expr and the emitters — cgen generates from a cursor

`genProcBody` is handed `BNode(bodyBuf.rootCursor)` under `-d:newIcBackend`, so
`expr` and the ~160 procs under it read the routine body through a cursor rather
than a tree. This had to land as one change: `expr` dispatches to all of them, so
they move together or the dispatch converts at every node.

The evidence that it works is not that it compiles. Cursor-driven and
`PNode`-driven builds emit BYTE-IDENTICAL `.c` (50/50 on an 89k-line target,
12/12 on the grind target), the built program runs and prints the right thing,
and — the part that makes the first number mean something — sabotaging
`bnode.intVal` changes all 12 files. The generator is genuinely reading through
the cursor, not quietly falling back.

Four kinds of site could not simply take `AnyNode`, and each is marked where it
sits rather than left for the next person to rediscover:

* THE GENERATOR REWRITES. `mAppendSeqElem`, `mNewSeq`, `genSetLengthSeq`,
  `genWasMoved` and `genArrToSeq` replace a child or a type IN PLACE, and
  `genEnumToStr`/`mAsgn`/`spawn` build fresh trees. Those run on `origin(n)` —
  the very node the buffer was encoded from — so the mutation lands exactly
  where it always did. Where the mutation is then READ (`genArrToSeq` retypes a
  bracket, `genArg` replaces a `var` param's type), generation continues on the
  origin too, because the buffer does not see the write and a cursor would keep
  reading the slot as encoded.
* NILABLE NODES stay `PNode`: a cursor has no standalone nil. That is the
  assignment DESTINATION throughout the call family (`genCall` passes nil), the
  `check` of an object-constructor field, `exvar`, `stepNode`, the `fin` of a
  try statement.
* `PNode`-KEYED TABLES AND ANALYSES take `origin`: `dataCache`, `isPartOf`,
  `lhsDoesAlias`, `potentialAlias`, the type-record walkers.
* SHARED PREDICATES in `ast.nim` cannot see `BNode`, so `skipHiddenAddr`,
  `isInfixAs` and `getStr` join `canRaise`/`getInt` as templates instantiated
  for both. `skipPragmaExpr` is a deliberate exception: it sits above the point
  in `ast.nim` where `firstSon` for a `PNode` exists, so `bnode` carries a
  one-line spelling with a pointer back.

Two Nim details worth recording. Repeated occurrences of a type class in one
signature share ONE implicit generic, so any proc whose two node parameters can
differ in representation needs explicit params — `genSingleVar`,
`genFieldObjConstr`, `callGlobalVarCppCtor`. And a `{.dirty.}` template inside a
generic resolves its identifiers at instantiation, so `genClosureCall`'s local
`rawProc` had to be bound before the template that uses it or it lost to the
module-level proc of the same name.

Verified: grind clean (1431 bodies, 260_431 nodes, 0 disagreements, origins
exact); the default path is byte-identical to HEAD; all four build
configurations compile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR
This commit is contained in:
Araq
2026-08-30 15:43:14 +02:00
parent e5bafa48c5
commit 709fd00861
12 changed files with 422 additions and 328 deletions

View File

@@ -714,6 +714,10 @@ proc extractPragma*(s: PSym): PNode =
proc skipPragmaExpr*(n: PNode): PNode =
## if pragma expr, give the node the pragmas are applied to,
## otherwise give node itself
##
## `bnode` carries the `BNode` spelling. It is a separate one-liner rather
## than a shared template because this sits above the point in this module
## where `firstSon` for a `PNode` exists.
if n.kind == nkPragmaExpr:
result = n[0]
else:
@@ -1505,14 +1509,21 @@ proc getFloat*(a: PNode): BiggestFloat =
#internalError(a.info, "getFloat")
#result = 0.0
proc getStr*(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkNilLit:
# let's hope this fixes more problems than it creates:
result = ""
else:
raiseRecoverableError("cannot extract string from invalid AST node")
template getStrImpl*(aArg: typed): string =
## Body shared with `bnode`'s `BNode` spelling — see `canRaiseImpl`.
block:
let gs = aArg
var res = ""
case gs.kind
of nkStrLit..nkTripleStrLit: res = gs.strVal
of nkNilLit:
# let's hope this fixes more problems than it creates:
res = ""
else:
raiseRecoverableError("cannot extract string from invalid AST node")
res
proc getStr*(a: PNode): string = getStrImpl(a)
#doAssert false, "getStr"
#internalError(a.info, "getStr")
#result = ""
@@ -1655,8 +1666,14 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
result = base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}
proc isInfixAs*(n: PNode): bool =
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
template isInfixAsImpl*(nArg: typed): bool =
## Body shared with `bnode`'s `BNode` spelling — see `canRaiseImpl`.
block:
let ia = nArg
ia.kind == nkInfix and ia.firstSon.kind == nkIdent and
ia.firstSon.ident.id == ord(wAs)
proc isInfixAs*(n: PNode): bool = isInfixAsImpl(n)
proc skipColon*(n: PNode): PNode =
result = n
@@ -1851,8 +1868,13 @@ proc toHumanStr*(kind: TTypeKind): string =
## strips leading `tk`
result = toHumanStrImpl(kind, 2)
proc skipHiddenAddr*(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n[0] else: n)
template skipHiddenAddrImpl*(nArg: typed): untyped =
## Body shared with `bnode`'s `BNode` spelling — see `canRaiseImpl`.
block:
let sha = nArg
(if sha.kind == nkHiddenAddr: sha.firstSon else: sha)
proc skipHiddenAddr*(n: PNode): PNode {.inline.} = skipHiddenAddrImpl(n)
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
assert n.kind == nkTypeClassTy

View File

@@ -740,7 +740,7 @@ proc listSymbolNames*(symbols: openArray[PSym]): string =
result.add ", "
result.add sym.name.s
proc isDiscriminantField*(n: PNode): bool =
if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags
elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags
proc isDiscriminantField*(n: AnyNode): bool =
if n.kind == nkCheckedFieldExpr: sfDiscriminant in n.firstSon.secondSon.sym.flags
elif n.kind == nkDotExpr: sfDiscriminant in n.secondSon.sym.flags
else: false

View File

@@ -699,6 +699,16 @@ when defined(newIcBackend):
proc getInt*(n: BNode): Int128 = getIntImpl(n)
proc skipHiddenAddr*(n: BNode): BNode {.inline.} = skipHiddenAddrImpl(n)
proc isInfixAs*(n: BNode): bool = isInfixAsImpl(n)
proc getStr*(n: BNode): string = getStrImpl(n)
proc skipPragmaExpr*(n: BNode): BNode {.inline.} =
## The `BNode` spelling of `ast.skipPragmaExpr`.
(if n.kind == nkPragmaExpr: n.firstSon else: n)
proc canRaiseConservative*(fn: BNode): bool = canRaiseConservativeImpl(fn)
proc canRaise*(fn: BNode): bool = canRaiseImpl(fn)

View File

@@ -37,14 +37,12 @@ proc canRaiseDisp(p: BProc; n: AnyNode): bool =
if n.kind == nkSym:
logCanRaise(n.sym, result)
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
## STAYS on `PNode`, and the reason is a capability the seam does not have
## rather than an accessor it is missing: the `warnObservableStores` message
## interpolates `$le`, i.e. it RENDERS the node. Rendering is `renderer.nim`
## reconstructing source text, which is a different job from reading a node's
## kind/sym/type, and nothing needs it until a diagnostic does. The alias
## analysis this calls (`isPartOf`) is already `AnyNode`, so only the message
## is in the way.
proc preventNrvo(p: BProc; dest, le: PNode; ri: AnyNode): bool =
## `dest` and `le` stay `PNode`s: they are DESTINATIONS, which the whole call
## family keeps as `PNode`s so they can be nil and so they can be handed to
## the alias analysis, and it is also what keeps the `warnObservableStores`
## message able to RENDER `le` — rendering being a capability the cursor seam
## does not have at all. `ri`, the call being generated, is a cursor.
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
result = false
var n = le
@@ -70,18 +68,20 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
return true
result = false
if le != nil:
if not le.isNilNode:
for r in sonsFrom(ri, 1):
if isPartOf(le, r, {pfStructural}) != arNo: return true
# `isPartOf` compares field symbols by identity and so has not moved to
# the seam; `origin` hands it the same nodes it always compared.
if isPartOf(le, origin(r), {pfStructural}) != arNo: return true
# we use the weaker 'canRaise' here in order to prevent too many
# annoying warnings, see #14514
if canRaise(ri.firstSon) and
locationEscapes(p, le, p.nestedTryStmts.len > 0):
message(p.config, le.info, warnObservableStores, $le)
# bug #19613 prevent dangerous aliasing too:
if dest != nil and dest != le:
if not dest.isNilNode and dest != le:
for r in sonsFrom(ri, 1):
if isPartOf(dest, r, {pfStructural}) != arNo: return true
if isPartOf(dest, origin(r), {pfStructural}) != arNo: return true
proc hasNoInit(call: AnyNode): bool {.inline.} =
result = call.firstSon.kind == nkSym and sfNoInit in call.firstSon.sym.flags
@@ -115,7 +115,11 @@ proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
else:
result = false
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
# `le` — the assignment DESTINATION — stays a `PNode` throughout this family.
# It is nilable (`genCall` passes nil, and a cursor has no standalone nil), and
# it is what `preventNrvo` and `isPartOf` are handed, both of which are still
# `PNode`-typed. `ri`, the expression being generated, is the part that moves.
proc fixupCall(p: BProc, le: PNode, ri: AnyNode, d: var TLoc,
result: var Builder, call: var CallBuilder) =
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri.firstSon)
genLineDir(p, ri)
@@ -214,7 +218,7 @@ proc reifiedOpenArray(n: AnyNode): bool {.inline.} =
else:
result = true
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
proc genOpenArraySlice(p: BProc; q: AnyNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
var a = initLocExpr(p, q.secondSon)
var b = initLocExpr(p, son(q, 2))
var c = initLocExpr(p, son(q, 3))
@@ -277,7 +281,7 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
result = ("", "")
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
proc openArrayLoc(p: BProc, formalType: PType, n: AnyNode; result: var Builder) =
var q = skipConv(n)
var skipped = false
while q.kind == nkStmtListExpr and q.hasSons:
@@ -387,13 +391,13 @@ proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
result = getTemp(p, a.lode.typ, needsInit=false)
genAssignment(p, result, a, {})
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
proc genArgStringToCString(p: BProc, n: AnyNode; result: var Builder; needsTmp: bool) {.inline.} =
var a = initLocExpr(p, n.firstSon)
let tmp = withTmpIfNeeded(p, a, needsTmp)
let ra = if p.config.usesSso(): byRefLoc(p, tmp) else: tmp.rdLoc
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
proc genArg(p: BProc, n: AnyNode, param: PSym; call: AnyNode; result: var Builder; needsTmp = false) =
var a: TLoc
if n.kind == nkStringToCString:
genArgStringToCString(p, n, result, needsTmp)
@@ -413,10 +417,16 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; n
# will be a reference in C++ and we cannot create a temporary reference
# variable. Thus, we create a temporary pointer variable instead.
let needsIndirect = mapType(p.config, n.firstSon.typ, mapTypeChooser(n.firstSon) == skParam) != ctArray
# A REWRITE, and one that has to be followed. The node's type is replaced in
# place, and a cursor would keep reading the type slot as it was ENCODED —
# the buffer does not see the mutation. So from here this site works on the
# origin, which is the node being mutated and therefore the one that has the
# new type.
let nn = origin(n)
if needsIndirect:
n.typ = copyType(n.typ, p.module.idgen, n.typ.owner)
n.typ.incl tfVarIsPtr
a = initLocExprSingleUse(p, n)
nn.typ = copyType(nn.typ, p.module.idgen, nn.typ.owner)
nn.typ.incl tfVarIsPtr
a = initLocExprSingleUse(p, nn)
a = withTmpIfNeeded(p, a, needsTmp)
if needsIndirect: a.flags.incl lfIndirect
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
@@ -438,7 +448,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; n
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
proc genArgNoParam(p: BProc, n: PNode; result: var Builder; needsTmp = false) =
proc genArgNoParam(p: BProc, n: AnyNode; result: var Builder; needsTmp = false) =
var a: TLoc
if n.kind == nkStringToCString:
genArgStringToCString(p, n, result, needsTmp)
@@ -448,7 +458,7 @@ proc genArgNoParam(p: BProc, n: PNode; result: var Builder; needsTmp = false) =
import aliasanalysis
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
proc potentialAlias(n: AnyNode, potentialWrites: seq[PNode]): bool =
result = false
for p in potentialWrites:
if p.aliases(n) != no or n.aliases(p) != no:
@@ -467,23 +477,30 @@ proc skipTrivialIndirections[T: AnyNode](n: T): T =
result = result.secondSon
else: break
proc getPotentialReads(n: PNode; result: var seq[PNode]) =
proc getPotentialReads(n: AnyNode; result: var seq[PNode]) =
case n.kind:
of nkLiterals, nkIdent, nkFormalParams: discard
of nkSym: result.add n
else:
for s in n:
for s in sons(n):
getPotentialReads(s, result)
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
proc genParams(p: BProc, ri: AnyNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
# We must generate temporaries in cases like #14396
# to keep the strict Left-To-Right evaluation
# 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.
#
# The arguments are materialized as `PNode`s, not cursors, because the alias
# analysis below (`potentialAlias`, `getPotentialReads`) carries a
# `seq[PNode]` beside the node and has not moved to the seam — see the
# mixed-representation blocker in `bnode`'s module doc. `origin` gives the
# same objects the tree-driven build used, so this is the argument list it
# always was; when that analysis moves, this becomes `seq[AnyNode]`.
var args: seq[PNode] = @[]
for it in sonsFrom(ri, 1): args.add it
for it in sonsFrom(ri, 1): args.add origin(it)
var needTmp = newSeq[bool](args.len)
var potentialWrites: seq[PNode] = @[]
for i in countdown(args.high, 0):
@@ -525,7 +542,7 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
(sym.typ.callConv == ccInline or sym.owner.id == module.id):
res = res & "_actual".rope
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genPrefixCall(p: BProc, le: PNode, ri: AnyNode, d: var TLoc) =
# this is a hotspot in the compiler
var op = initLocExpr(p, ri.firstSon)
# getUniqueType() is too expensive here:
@@ -541,7 +558,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
genParams(p, ri, typ, res, call)
fixupCall(p, le, ri, d, res, call)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genClosureCall(p: BProc, le: PNode, ri: AnyNode, d: var TLoc) =
template callProc(rp, params, pTyp: Snippet): Snippet =
let e = dotField(rp, "ClE_0")
@@ -576,6 +593,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
var argBuilder = default(CallBuilder) # not initCallBuilder, we just want the params
genParams(p, ri, typ, params, argBuilder)
# `rawProc` is bound BEFORE the `{.dirty.}` template that uses it. Inside a
# generic proc a dirty template's identifiers resolve at instantiation, and a
# local declared after the template loses to the module-level `rawProc` proc
# — which type-checks as a completely different thing.
let rawProc = getClosureType(p.module, typ, clHalf)
template genCallPattern {.dirty.} =
let rp = rdLoc(op)
let pars = extract(params)
@@ -584,8 +607,6 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
p.s(cpsStmts).add(callIter(rp, pars))
else:
p.s(cpsStmts).add(callProc(rp, pars, rawProc))
let rawProc = getClosureType(p.module, typ, clHalf)
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri.firstSon)
if typ.returnType != nil:
if isInvalidReturnType(p.config, typ):
@@ -637,7 +658,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
genCallPattern()
if canRaise: raiseExit(p)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
proc genOtherArg(p: BProc; ri: AnyNode; i: int; typ: PType; result: var Builder;
argBuilder: var CallBuilder) =
if i < typ.n.len:
# 'var T' is 'T&' in C++. This means we ignore the request of
@@ -714,7 +735,7 @@ proc skipAddrDeref[T: AnyNode](node: T): T =
else:
result = node
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
proc genThisArg(p: BProc; ri: AnyNode; i: int; typ: PType; result: var Builder) =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
@@ -749,7 +770,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
genArgNoParam(p, ri, result) #, son(typ.n, i).sym)
result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) =
proc genPatternCall(p: BProc; ri: AnyNode; pat: string; typ: PType; result: var Builder) =
var i = 0
var j = 1
while i < pat.len:
@@ -803,7 +824,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
if i - 1 >= start:
result.add(substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genInfixCall(p: BProc, le: PNode, ri: AnyNode, d: var TLoc) =
var op = initLocExpr(p, ri.firstSon)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri.firstSon.typ, abstractInst)
@@ -844,7 +865,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
genOtherArg(p, ri, i, typ, res, call)
fixupCall(p, le, ri, d, res, call)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
proc genNamedParamCall(p: BProc, ri: AnyNode, d: var TLoc) =
# generates a crappy ObjC call
var op = initLocExpr(p, ri.firstSon)
var pl = newBuilder("[")
@@ -935,7 +956,7 @@ proc isInactiveDestructorCall(p: BProc, e: AnyNode): bool =
result = e.safeLen == 2 and e.firstSon.kind == nkSym and
e.firstSon.sym.name.s == "=destroy" and notYetAlive(e.secondSon.skipAddr)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genAsgnCall(p: BProc, le: PNode, ri: AnyNode, d: var TLoc) =
if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri):
return
when defined(icDbgHash):
@@ -957,4 +978,4 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
else:
genPrefixCall(p, le, ri, d)
proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d)
proc genCall(p: BProc, e: AnyNode, d: var TLoc) = genAsgnCall(p, nil, e, d)

File diff suppressed because it is too large Load Diff

View File

@@ -53,11 +53,11 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
proc genStringLiteralV1(m: BModule; n: PNode; result: var Builder) =
proc genStringLiteralV1(m: BModule; n: AnyNode; result: var Builder) =
if s.isNil:
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
else:
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
let id = nodeTableTestOrSet(m.dataCache, origin(n), m.labels)
var name: string = ""
if id == m.labels:
# string literal not found in the cache:
@@ -85,8 +85,8 @@ proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bo
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
proc genStringLiteralV2(m: BModule; n: AnyNode; isConst: bool; result: var Builder) =
let id = nodeTableTestOrSet(m.dataCache, origin(n), m.labels)
var litName: string
if id == m.labels:
cgsym(m, "NimStrPayload")
@@ -111,8 +111,8 @@ proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
m.s[cfsStrData].add(extract(res))
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
proc genStringLiteralV2Const(m: BModule; n: AnyNode; isConst: bool; result: var Builder) =
let id = nodeTableTestOrSet(m.dataCache, origin(n), m.labels)
var pureLit: Rope
if id == m.labels:
pureLit = getTempName(m)
@@ -164,7 +164,7 @@ proc ssoMoreLit(m: BModule; s: string): string =
val = val or (ch shl (uint(ptrSize - 1 - i) * 8))
result = cCast(ptrType("LongString"), "(uintptr_t)" & $val)
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
proc genStringLiteralV3Const(m: BModule; n: AnyNode; isConst: bool; result: var Builder) =
# Inline SmallString struct initializer for use inside const aggregate types.
# Layout: {bytes: NimUint, more: ptr LongString}
# bytes = slen (low byte) | char[0]<<8 | char[1]<<16 | ... | char[6]<<56
@@ -220,7 +220,7 @@ proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Bu
# ------ Version 3: SmallString (SSO) strings --------------------------------
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder) =
proc genStringLiteralV3(m: BModule; n: AnyNode; isConst: bool; result: var Builder) =
# SmallString literal. Always generate a fresh SmallString variable (like v2
# always generates a fresh outer NimStringV2). For long strings, cache the
# LongString payload to avoid duplicates within a module.
@@ -259,7 +259,7 @@ proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder
else:
# Long: cache the LongString block to emit it only once per module per string.
# Always generate a fresh SmallString pointing at the (possibly cached) block.
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
let id = nodeTableTestOrSet(m.dataCache, origin(n), m.labels)
var dataName: string
if id == m.labels:
dataName = getTempName(m)
@@ -301,7 +301,7 @@ proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Builder) =
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
proc genStringLiteral(m: BModule; n: AnyNode; result: var Builder) =
case detectStrVersion(m)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)

View File

@@ -94,13 +94,13 @@ template endBlockWith(p: BProc, body: typed) =
body
endBlockOutside(p, label)
proc genVarTuple(p: BProc, n: PNode) =
proc genVarTuple(p: BProc, n: AnyNode) =
if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple")
# if we have a something that's been captured, use the lowering instead:
for it in sonsButLast(n, 2):
if it.kind != nkSym:
genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.module.idgen, p.prc))
genStmts(p, lowerTupleUnpacking(p.module.g.graph, origin(n), p.module.idgen, p.prc))
return
# check only the first son
@@ -172,7 +172,9 @@ proc genVarTuple(p: BProc, n: PNode) =
cCast(ptrType(CPointer), cAddr(curr.loc.snippet))))
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
proc loadInto(p: BProc, le: PNode, ri: AnyNode, a: var TLoc) {.inline.} =
## `le` is the DESTINATION and stays a `PNode` — it only ever reaches
## `genAsgnCall`, which keeps it a `PNode` for the alias analysis.
if ri.kind in nkCallKinds and (ri.firstSon.kind != nkSym or
ri.firstSon.sym.magic == mNone):
genAsgnCall(p, le, ri, a)
@@ -199,13 +201,13 @@ proc endSimpleBlock(p: BProc, scope: var ScopeBuilder) {.inline.} =
endBlockWith(p):
finishScope(p.s(cpsStmts), scope)
proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
proc genSimpleBlock(p: BProc, stmts: AnyNode) {.inline.} =
var scope: ScopeBuilder
startSimpleBlock(p, scope)
genStmts(p, stmts)
endSimpleBlock(p, scope)
proc exprBlock(p: BProc, n: PNode, d: var TLoc) =
proc exprBlock(p: BProc, n: AnyNode, d: var TLoc) =
var scope: ScopeBuilder
startSimpleBlock(p, scope)
expr(p, n, d)
@@ -216,7 +218,7 @@ template preserveBreakIdx(body: untyped): untyped =
body
p.breakIdx = oldBreakIdx
proc genState(p: BProc, n: PNode) =
proc genState(p: BProc, n: AnyNode) =
internalAssert p.config, n.len == 1
let n0 = n.firstSon
if n0.kind == nkIntLit:
@@ -261,7 +263,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt
for i in countdown(howManyExcepts-1, 0):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
proc genGotoState(p: BProc, n: PNode) =
proc genGotoState(p: BProc, n: AnyNode) =
# we resist the temptation to translate it into duff's device as it later
# will be translated into computed gotos anyway for GCC at least:
# switch (x.state) {
@@ -285,7 +287,7 @@ proc genGotoState(p: BProc, n: PNode) =
p.s(cpsStmts).addSingleSwitchCase(cIntValue(i)):
p.s(cpsStmts).addGoto(prefix & $i)
proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
proc genBreakState(p: BProc, n: AnyNode, d: var TLoc) =
var a: TLoc
d = initLoc(locExpr, n, OnUnknown)
@@ -307,23 +309,23 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
cIntValue(1)),
cIntValue(0))
proc genGotoVar(p: BProc; value: PNode) =
proc genGotoVar(p: BProc; value: AnyNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
localError(p.config, value.info, "'goto' target must be a literal value")
else:
p.s(cpsStmts).addGoto("NIMSTATE_" & $value.intVal)
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder)
proc genBracedInit(p: BProc, n: AnyNode; isConst: bool; optionalType: PType; result: var Builder)
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Builder) =
proc potentialValueInit(p: BProc; v: PSym; value: AnyNode; result: var Builder) =
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
discard "nothing to do"
elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
elif sfGlobal in v.flags and not value.isNilNode and isDeepConstExpr(value, p.module.compileToCpp) and
p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
genBracedInit(p, value, isConst = false, v.typ, result)
proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet =
proc genCppParamsForCtor(p: BProc; call: AnyNode; didGenTemp: var bool): Snippet =
var res = newBuilder("")
var argBuilder = default(CallBuilder) # not init, only building params
let typ = skipTypes(call.firstSon.typ, abstractInst)
@@ -348,7 +350,11 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet =
genOtherArg(p, call, i, typ, res, argBuilder)
result = extract(res)
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
proc genSingleVar[V: AnyNode; W: AnyNode](p: BProc, v: PSym; vn: V; value: W) =
## `vn` and `value` are SEPARATE type parameters, not one shared: the
## definition site is a body node while the value can come from the symbol's
## own AST (`astdef`), so the two are not necessarily the same
## representation.
if sfGoto in v.flags:
# translate 'var state {.goto.} = X' into 'goto LX':
genGotoVar(p, value)
@@ -462,13 +468,13 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
genLineDir(targetProc, vn)
if not isCppCtorCall:
backendEnsureMutable v
loadInto(targetProc, vn, value, v.locImpl)
loadInto(targetProc, origin(vn), value, v.locImpl)
if forHcr:
endBlockWith(targetProc):
finishBranch(p.s(cpsStmts), hcrInit)
finishIfStmt(p.s(cpsStmts), hcrInit)
proc genSingleVar(p: BProc, a: PNode) =
proc genSingleVar(p: BProc, a: AnyNode) =
let v = a.firstSon.sym
if sfCompileTime in v.flags:
# fix issue #12640
@@ -479,17 +485,17 @@ proc genSingleVar(p: BProc, a: PNode) =
return
genSingleVar(p, v, a.firstSon, son(a, 2))
proc genClosureVar(p: BProc, a: PNode) =
proc genClosureVar(p: BProc, a: AnyNode) =
var immediateAsgn = son(a, 2).kind != nkEmpty
var v: TLoc = initLocExpr(p, a.firstSon)
genLineDir(p, a)
if immediateAsgn:
loadInto(p, a.firstSon, son(a, 2), v)
loadInto(p, origin(a.firstSon), son(a, 2), v)
elif sfNoInit notin a.firstSon.secondSon.sym.flags:
constructLoc(p, v)
proc genVarStmt(p: BProc, n: PNode) =
for it in n:
proc genVarStmt(p: BProc, n: AnyNode) =
for it in sons(n):
case it.kind
of nkCommentStmt: discard
of nkIdentDefs:
@@ -503,7 +509,7 @@ proc genVarStmt(p: BProc, n: PNode) =
else:
genVarTuple(p, it)
proc genIf(p: BProc, n: PNode, d: var TLoc) =
proc genIf(p: BProc, n: AnyNode, d: var TLoc) =
#
# { if (!expr1) goto L1;
# thenPart }
@@ -552,7 +558,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
else: internalError(p.config, n.info, "genIf()")
if n.len > 1: fixLabel(p, lend)
proc genReturnStmt(p: BProc, t: PNode) =
proc genReturnStmt(p: BProc, t: AnyNode) =
if nfPreventCg in t.flags: return
p.flags.incl beforeRetNeeded
genLineDir(p, t)
@@ -572,7 +578,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
p.s(cpsStmts).addGoto("BeforeRet_")
proc genGotoForCase(p: BProc; caseStmt: PNode) =
proc genGotoForCase(p: BProc; caseStmt: AnyNode) =
for child in sonsFrom(caseStmt, 1):
var scope: ScopeBuilder
startSimpleBlock(p, scope)
@@ -589,18 +595,20 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) =
iterator fieldValuePairs(n: PNode): tuple[memberSym, valueSym: PNode] =
assert(n.kind in {nkLetSection, nkVarSection})
for identDefs in n:
for identDefs in sons(n):
if identDefs.kind == nkIdentDefs:
let valueSym = identDefs.lastSon
for memberSym in sonsButLast(identDefs, 2):
yield((memberSym: memberSym, valueSym: valueSym))
proc genComputedGoto(p: BProc; n: PNode) =
proc genComputedGoto(p: BProc; n: AnyNode) =
# first pass: Generate array of computed labels:
# flatten the loop body because otherwise let and var sections
# wrapped inside stmt lists by inject destructors won't be recognised
let n = n.flattenStmts()
# REBUILDS the statement list, so from here this proc works on
# a fresh `PNode` tree — there is nothing in the buffer corresponding to it.
let n = origin(n).flattenStmts()
var casePos = -1
var arraySize: int = 0
for i, it in isons(n):
@@ -688,7 +696,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
genStmts(p, it)
proc genWhileStmt(p: BProc, t: PNode) =
proc genWhileStmt(p: BProc, t: AnyNode) =
# we don't generate labels here as for example GCC would produce
# significantly worse code
var
@@ -727,7 +735,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
dec(p.withinLoop)
proc genBlock(p: BProc, n: PNode, d: var TLoc) =
proc genBlock(p: BProc, n: AnyNode, d: var TLoc) =
if not isEmptyType(n.typ):
# bug #4505: allocate the temp in the outer scope
# so that it can escape the generated {}:
@@ -748,7 +756,7 @@ proc genBlock(p: BProc, n: PNode, d: var TLoc) =
expr(p, n.secondSon, d)
endSimpleBlock(p, scope)
proc genParForStmt(p: BProc, t: PNode) =
proc genParForStmt(p: BProc, t: AnyNode) =
assert(t.len == 3)
inc(p.withinLoop)
genLineDir(p, t)
@@ -765,13 +773,13 @@ proc genParForStmt(p: BProc, t: PNode) =
var stepNode: PNode = nil
# $n at the beginning because of #9710
if call.len == 4: # procName(a, b, annotation)
if call.safeLen == 4: # procName(a, b, annotation)
if call.firstSon.sym.name.s == "||": # `||`(a, b, annotation)
p.s(cpsStmts).addCPragma("omp " & son(call, 3).getStr)
else:
p.s(cpsStmts).addCPragma(son(call, 3).getStr)
else: # `||`(a, b, step, annotation)
stepNode = son(call, 3)
stepNode = origin(son(call, 3))
p.s(cpsStmts).addCPragma("omp " & son(call, 4).getStr)
p.breakIdx = startBlockWith(p):
@@ -787,7 +795,7 @@ proc genParForStmt(p: BProc, t: PNode) =
dec(p.withinLoop)
proc genBreakStmt(p: BProc, t: PNode) =
proc genBreakStmt(p: BProc, t: AnyNode) =
var idx = p.breakIdx
if t.firstSon.kind != nkEmpty:
# named break?
@@ -868,7 +876,7 @@ proc raiseInstr(p: BProc; result: var Builder) =
result.addGoto("LA" & $p.nestedTryStmts[L-1].label & "_")
# + ord(p.nestedTryStmts[L-1].inExcept)])
proc genRaiseStmt(p: BProc, t: PNode) =
proc genRaiseStmt(p: BProc, t: AnyNode) =
if t.firstSon.kind != nkEmpty:
var a: TLoc = initLocExprSingleUse(p, t.firstSon)
finallyActions(p)
@@ -905,7 +913,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "reraiseException"))
raiseInstr(p, p.s(cpsStmts))
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
template genCaseGenericBranch(p: BProc, b: AnyNode, e: TLoc, labl: TLabel,
rangeFormat, eqFormat: untyped) =
var x, y: TLoc
for it in sonsButLast(b):
@@ -923,7 +931,7 @@ template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
let rb {.inject.} = rdCharLoc(x)
eqFormat
proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
proc genCaseSecondPass(p: BProc, t: AnyNode, d: var TLoc,
labId, until: int): TLabel =
var lend = getLabel(p)
for i, branch in isons(t, 1):
@@ -938,7 +946,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
exprBlock(p, branch.firstSon, d)
result = lend
template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
template genIfForCaseUntil(p: BProc, t: AnyNode, d: var TLoc,
until: int, a: TLoc,
rangeFormat, eqFormat: untyped): TLabel =
# generate a C-if statement for a Nim case statement
@@ -962,13 +970,13 @@ template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
res = genCaseSecondPass(p, t, d, labId, until)
res
template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
template genCaseGeneric(p: BProc, t: AnyNode, d: var TLoc,
rangeFormat, eqFormat: untyped) =
var a: TLoc = initLocExpr(p, t.firstSon)
var lend = genIfForCaseUntil(p, t, d, t.len-1, a, rangeFormat, eqFormat)
var lend = genIfForCaseUntil(p, t, d, t.safeLen-1, a, rangeFormat, eqFormat)
fixLabel(p, lend)
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
proc genCaseStringBranch(p: BProc, b: AnyNode, e: TLoc, labl: TLabel,
stringKind: TTypeKind,
branches: var openArray[Builder]) =
var x: TLoc
@@ -990,7 +998,7 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
do:
branches[j].addGoto(labl)
proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
proc genStringCase(p: BProc, t: AnyNode, stringKind: TTypeKind, d: var TLoc) =
# count how many constant strings there are in the case:
var strings = 0
for it in sonsFrom(t, 1):
@@ -1057,7 +1065,7 @@ proc ifSwitchSplitPoint(p: BProc, n: AnyNode): int =
if branch.kind == nkOfBranch and branchHasTooBigRange(branch):
result = i
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder) =
proc genCaseRange(p: BProc, branch: AnyNode, info: var SwitchCaseBuilder) =
for it in sonsButLast(branch):
if it.kind == nkRange:
if hasSwitchRange in CC[p.config.cCompiler].props:
@@ -1067,7 +1075,9 @@ proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder) =
genLiteral(p, it.secondSon, litB)
p.s(cpsStmts).addCaseRange(info, extract(litA), extract(litB))
else:
var v = copyNode(it.firstSon)
# A working COPY is mutated in the loop below, so it is a `PNode`
# built from the origin — there is nothing to mutate on a cursor.
var v = copyNode(origin(it.firstSon))
while v.intVal <= it.secondSon.intVal:
var litA = newBuilder("")
genLiteral(p, v, litA)
@@ -1078,7 +1088,7 @@ proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder) =
genLiteral(p, it, litA)
p.s(cpsStmts).addCase(info, extract(litA))
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
proc genOrdinalCase(p: BProc, n: AnyNode, d: var TLoc) =
# analyse 'case' statement:
var splitPoint = ifSwitchSplitPoint(p, n)
@@ -1124,7 +1134,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
p.s(cpsStmts).addCallStmt("__assume", cIntValue(0))
if lend != "": fixLabel(p, lend)
proc genCase(p: BProc, t: PNode, d: var TLoc) =
proc genCase(p: BProc, t: AnyNode, d: var TLoc) =
genLineDir(p, t)
if not isEmptyType(t.typ) and d.k == locNone:
d = getTemp(p, t.typ)
@@ -1160,7 +1170,7 @@ proc genRestoreFrameAfterException(p: BProc) =
p.procSec(cpsInit).addCall(cgsymValue(p.module, "getFrame"))
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "setFrame"), "_nimCurFrame")
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
proc genTryCpp(p: BProc, t: AnyNode, d: var TLoc) =
#[ code to generate:
std::exception_ptr error;
@@ -1196,7 +1206,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.lastSon.kind == nkFinally: t.lastSon else: nil
let fin = if t.lastSon.kind == nkFinally: origin(t.lastSon) else: nil
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
if t.kind == nkHiddenTryStmt:
@@ -1251,7 +1261,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
var typeNode = label
if label.isInfixAs():
typeNode = label.secondSon
exvar = son(label, 2) # ex1 in `except ExceptType as ex1:`
exvar = origin(son(label, 2)) # ex1 in `except ExceptType as ex1:`
assert(typeNode.kind == nkType)
if isImportedException(typeNode.typ, p.config):
hasImportedCppExceptions = true
@@ -1292,7 +1302,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "}$n", [])
# Second pass: handle C++ based exceptions:
template genExceptBranchBody(body: PNode) {.dirty.} =
template genExceptBranchBody(body: AnyNode) {.dirty.} =
genRestoreFrameAfterException(p)
#linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
expr(p, body, d)
@@ -1320,7 +1330,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
if label.isInfixAs():
typeNode = label.secondSon
if isImportedException(typeNode.typ, p.config):
let exvar = son(label, 2) # ex1 in `except ExceptType as ex1:`
let exvar = origin(son(label, 2)) # ex1 in `except ExceptType as ex1:`
fillLocalName(p, exvar.sym)
backendEnsureMutable exvar.sym
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack)
@@ -1372,8 +1382,8 @@ proc bodyCanRaise(p: BProc; n: AnyNode): bool =
for it in sons(n):
if bodyCanRaise(p, it): return true
proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
let fin = if t.lastSon.kind == nkFinally: t.lastSon else: nil
proc genTryGoto(p: BProc; t: AnyNode; d: var TLoc) =
let fin = if t.lastSon.kind == nkFinally: origin(t.lastSon) else: nil
inc p.labels
let lab = p.labels
let hasExcept = t.secondSon.kind == nkExceptBranch
@@ -1506,7 +1516,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
raiseExit(p)
if hasExcept: inc p.withinTryWithExcept
proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
proc genTrySetjmp(p: BProc, t: AnyNode, d: var TLoc) =
# code to generate:
#
# XXX: There should be a standard dispatch algorithm
@@ -1585,7 +1595,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.lastSon.kind == nkFinally: t.lastSon else: nil
let fin = if t.lastSon.kind == nkFinally: origin(t.lastSon) else: nil
p.nestedTryStmts.add((fin, quirkyExceptions, t.kind == nkHiddenTryStmt, 0.Natural))
expr(p, t.firstSon, d)
var quirkyIf = default(IfBuilder)
@@ -1708,7 +1718,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
cIntValue(0))):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "reraiseException"))
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
proc genAsmOrEmitStmt(p: BProc, t: AnyNode, isAsmStmt=false; result: var Rope) =
var res = ""
let offset =
if isAsmStmt: 1 # first son is pragmas
@@ -1754,14 +1764,14 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
res.add("\L")
result.add res.rope
proc genAsmStmt(p: BProc, t: PNode) =
proc genAsmStmt(p: BProc, t: AnyNode) =
assert(t.kind == nkAsmStmt)
genLineDir(p, t)
var s = newRopeAppender()
var asmSyntax = ""
if (let p = t.firstSon; p.kind == nkPragma):
for i in p:
for i in sons(p):
if whichPragma(i) == wAsmSyntax:
asmSyntax = i.secondSon.strVal
@@ -1784,7 +1794,7 @@ proc genAsmStmt(p: BProc, t: PNode) =
addIndent p, p.s(cpsStmts)
p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
proc determineSection(n: PNode): TCFileSection =
proc determineSection(n: AnyNode): TCFileSection =
result = cfsProcHeaders
if n.len >= 1 and n.firstSon.kind in {nkStrLit..nkTripleStrLit}:
let sec = n.firstSon.strVal
@@ -1792,7 +1802,7 @@ proc determineSection(n: PNode): TCFileSection =
elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
proc genEmit(p: BProc, t: PNode) =
proc genEmit(p: BProc, t: AnyNode) =
var s = newRopeAppender()
genAsmOrEmitStmt(p, t.secondSon, false, s)
if p.prc == nil:
@@ -1804,12 +1814,12 @@ proc genEmit(p: BProc, t: PNode) =
genLineDir(p, t)
line(p, cpsStmts, s)
proc genPragma(p: BProc, n: PNode) =
proc genPragma(p: BProc, n: AnyNode) =
for i, it in isons(n):
case whichPragma(it)
of wEmit: genEmit(p, it)
of wPush:
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
processPushBackendOption(p.config, p.optionsStack, p.options, origin(n), i+1)
of wPop:
processPopBackendOption(p.config, p.optionsStack, p.options)
else: discard
@@ -1835,7 +1845,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
if p.config.exc == excGoto:
raiseExit(p)
proc asgnFieldDiscriminant(p: BProc, e: PNode) =
proc asgnFieldDiscriminant(p: BProc, e: AnyNode) =
var dotExpr = e.firstSon
if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.firstSon
var a = initLocExpr(p, e.firstSon)
@@ -1847,7 +1857,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
message(p.config, e.info, warnCaseTransition)
genAssignment(p, a, tmp, {})
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
proc genAsgn(p: BProc, e: AnyNode, fastAsgn: bool) =
if e.firstSon.kind == nkSym and sfGoto in e.firstSon.sym.flags:
genLineDir(p, e)
genGotoVar(p, e.secondSon)
@@ -1876,9 +1886,9 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
if fastAsgn: incl(a.flags, lfNoDeepCopy)
assert(a.t != nil)
genLineDir(p, ri)
loadInto(p, le, ri, a)
loadInto(p, origin(le), ri, a)
proc genStmts(p: BProc, t: PNode) =
proc genStmts(p: BProc, t: AnyNode) =
var a: TLoc = default(TLoc)
let isPush = p.config.hasHint(hintExtendedContext)

View File

@@ -18,7 +18,7 @@ type
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder)
proc genCaseRange(p: BProc, branch: AnyNode, info: var SwitchCaseBuilder)
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
proc visit(p: BProc, data, visitor: Snippet) =

View File

@@ -747,7 +747,7 @@ proc hasCppCtor(m: BModule; typ: PType): bool =
if sfConstructor in prc.flags:
return true
proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string
proc genCppParamsForCtor(p: BProc; call: AnyNode; didGenTemp: var bool): string
proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string =
#To avoid creating a BProc per test when called inside a struct nil BProc is allowed

View File

@@ -135,11 +135,11 @@ proc signatureHasMetaType*(t: PType; depth: int = 0): bool =
# `HashList[T, N]`, …) is carried as a `tyStatic` node inside the otherwise
# fully-concrete `tyGenericInst`, but it is NOT meta: the routine is a normal
# runtime routine the owner must emit. Only an UNRESOLVED `static T` parameter
# (no bound value, `t.n == nil`) is meta. Without this, every routine whose
# (no bound value, `t.n.isNilNode`) is meta. Without this, every routine whose
# signature touches a `static`-parameterized generic instance (the bulk of
# the SSZ/`MDigest` API) is dropped from the owned-routine seeding and ends up
# an undefined reference at link (mirrors the tyGenericBody case above).
return t.n == nil
return t.n.isNilNode
if t.kind in {tyTyped, tyUntyped, tyTypeDesc, tyGenericParam,
tyAnything, tyFromExpr, tyError}:
return true
@@ -512,7 +512,7 @@ proc genCLineDir(r: var Builder, p: BProc, info: TLineInfo; conf: ConfigRef) =
if freshLineInfo(p, info):
genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
proc genLineDir(p: BProc, t: PNode) =
proc genLineDir(p: BProc; t: AnyNode) =
if p == p.module.preInitProc: return
let line = t.info.safeLineNm
@@ -595,7 +595,7 @@ include ccgtypes
# ------------------------------ Manager of temporaries ------------------
template mapTypeChooser(n: PNode): TSymKind =
template mapTypeChooser(n: AnyNode): TSymKind =
(if n.kind == nkSym: n.sym.kind else: skVar)
template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
@@ -632,8 +632,8 @@ type
needAssignCall
TAssignmentFlags = set[TAssignmentFlag]
proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
proc rawConstExpr(p: BProc, n: PNode; d: var TLoc)
proc genObjConstr(p: BProc; e: AnyNode, d: var TLoc)
proc rawConstExpr(p: BProc; n: AnyNode; d: var TLoc)
proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
type
@@ -852,7 +852,7 @@ proc getIntTemp(p: BProc): TLoc =
flags: {})
p.s(cpsLocals).addVar(kind = Local, name = result.snippet, typ = NimInt)
proc localVarDecl(res: var Builder, p: BProc; n: PNode,
proc localVarDecl(res: var Builder, p: BProc; n: AnyNode,
initializer: Snippet = "",
initializerKind: VarInitializerKind = Assignment) =
let s = n.sym
@@ -870,7 +870,7 @@ proc localVarDecl(res: var Builder, p: BProc; n: PNode,
initializer = initializer,
initializerKind = initializerKind)
proc assignLocalVar(p: BProc, n: PNode) =
proc assignLocalVar(p: BProc; n: AnyNode) =
#assert(s.loc.k == locNone) # not yet assigned
# this need not be fulfilled for inline procs; they are regenerated
# for each module that uses them!
@@ -894,7 +894,7 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
# and s.owner.kind == skModule # owner isn't always a module (global pragma on local var)
# and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used
proc genGlobalVarDecl(res: var Builder, p: BProc, n: PNode; td: Snippet;
proc genGlobalVarDecl(res: var Builder, p: BProc; n: AnyNode; td: Snippet;
initializer: Snippet = "",
initializerKind: VarInitializerKind = Assignment,
allowConst = true) =
@@ -935,7 +935,7 @@ proc genGlobalVarDecl(res: var Builder, p: BProc, n: PNode; td: Snippet;
initializer = initializer,
initializerKind = initializerKind)
proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
proc assignGlobalVar(p: BProc; n: AnyNode; value: Rope) =
let s = n.sym
if s.loc.k == locNone:
fillBackendName(p.module, s)
@@ -999,7 +999,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
backendEnsureMutable s
resetLoc(p, s.locImpl)
proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
proc callGlobalVarCppCtor[V: AnyNode; W: AnyNode](p: BProc; v: PSym; vn: V; value: W; didGenTemp: var bool) =
let s = vn.sym
fillBackendName(p.module, s)
backendEnsureMutable s
@@ -1018,7 +1018,7 @@ proc assignParam(p: BProc, s: PSym, retType: PType) =
assert(s.loc.snippet != "")
scopeMangledParam(p, s)
proc fillProcLoc(m: BModule; n: PNode) =
proc fillProcLoc(m: BModule; n: AnyNode) =
let sym = n.sym
if sym.loc.k == locNone:
fillBackendName(m, sym)
@@ -1032,22 +1032,22 @@ proc getLabel(p: BProc): TLabel =
proc fixLabel(p: BProc, labl: TLabel) =
p.s(cpsStmts).addLabel(labl)
proc genVarPrototype(m: BModule, n: PNode)
proc genVarPrototype(m: BModule, n: AnyNode)
proc requestConstImpl(p: BProc, sym: PSym)
proc genStmts(p: BProc, t: PNode)
proc expr(p: BProc, n: PNode, d: var TLoc)
proc genStmts(p: BProc, t: AnyNode)
proc expr(p: BProc, n: AnyNode, d: var TLoc)
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
proc genLiteral(p: BProc, n: PNode; result: var Builder)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder; argBuilder: var CallBuilder)
proc genLiteral(p: BProc; n: AnyNode; result: var Builder)
proc genOtherArg(p: BProc; ri: AnyNode; i: int; typ: PType; result: var Builder; argBuilder: var CallBuilder)
proc raiseExit(p: BProc)
proc raiseExitCleanup(p: BProc, destroy: string)
proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc =
proc initLocExpr(p: BProc; e: AnyNode, flags: TLocFlags = {}): TLoc =
result = initLoc(locNone, e, OnUnknown, flags)
expr(p, e, result)
proc initLocExprSingleUse(p: BProc, e: PNode): TLoc =
proc initLocExprSingleUse(p: BProc; e: AnyNode): TLoc =
result = initLoc(locNone, e, OnUnknown)
if e.kind in nkCallKinds and (e.firstSon.kind != nkSym or e.firstSon.sym.magic == mNone):
# We cannot check for tfNoSideEffect here because of mutable parameters.
@@ -1748,7 +1748,7 @@ when defined(newIcBackend):
# `(ht . <sym>)` type difference. Only a subtree that clean is handed to
# `grindPredicates` — see the descent below for why.
result = true
if a == nil:
if a.isNilNode:
if not c.isNilNode: bail("nil-ness", "not-nil", "nil")
return
if c.isNilNode: bail("nil-ness", "nil", "not-nil")
@@ -1891,7 +1891,7 @@ when defined(newIcBackend):
## disagreement with the original, so both directions are checked by the one
## oracle rather than by a hand-written comparator that could agree with the
## bug.
if bnodeGrind == 0 or body == nil: return
if bnodeGrind == 0 or body.isNilNode: return
let htBefore = tolHtNil
let fieldBefore = tolFieldSym
@@ -1908,7 +1908,7 @@ when defined(newIcBackend):
# Asserted rather than assumed, with `==` on the reference: an equal copy
# would not do.
proc grindOrigins(enc: var BridgeBuf; c: BNode; a: PNode; path: string) =
if a == nil: return
if a.isNilNode: return
# Through the AMBIENT accessor (`bnode.origin`, via `currentNav`), which
# is the one a migrated generator proc will call from inside `initLoc` —
# not the direct `originOf`, which would test a path nothing uses.
@@ -1988,7 +1988,7 @@ when defined(newIcBackend):
let ast = prc.ast
if ast == nil or ast.safeLen <= bodyPos: return
let body = son(ast, bodyPos)
if body == nil: return
if body.isNilNode: return
var scope = default(BodyScope)
var viaCursor = default(BNode)
if not lazyBodyBNode(body, scope, viaCursor): return
@@ -2028,7 +2028,7 @@ proc getProcTypeCast(m: BModule, prc: PSym): Rope =
let params = extract(desc)
result = procPtrTypeUnnamed(rettype = rettype, params = params)
proc genProcBody(p: BProc; procBody: PNode) =
proc genProcBody(p: BProc; procBody: AnyNode) =
genStmts(p, procBody) # modifies p.locals, p.init, etc.
if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}:
p.flags.incl nimErrorFlagDeclared
@@ -2194,7 +2194,14 @@ proc genProcLvl3*(m: BModule, prc: PSym) =
continue
assignParam(p, param, prc.typ.returnType)
closureSetup(p, prc)
genProcBody(p, procBody)
# THE FLIP: under `-d:newIcBackend` the generator is driven off the cursor
# into the handed-off buffer, not the tree. Both spellings must produce the
# same C, which is what the cursor-vs-`PNode` `.c` comparison checks.
when defined(newIcBackend):
withBridge(bodyBuf.tables):
genProcBody(p, BNode(bodyBuf.rootCursor))
else:
genProcBody(p, procBody)
# IC: spurious write, seems fine for now:
prc.infoImpl = tmpInfo
@@ -2482,7 +2489,7 @@ proc requestProcDef*(m: BModule, prc: PSym) =
## code had referenced it.
genProc(m, prc)
proc genVarPrototype(m: BModule, n: PNode) =
proc genVarPrototype(m: BModule, n: AnyNode) =
#assert(sfGlobal in sym.flags)
let sym = n.sym
useHeader(m, sym)
@@ -3373,9 +3380,9 @@ when false:
readMergeInfo(getCFile(m), m)
result = m
proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool, init: var IfBuilder) =
proc addHcrInitGuards(p: BProc; n: AnyNode, inInitGuard: var bool, init: var IfBuilder) =
if n.kind == nkStmtList:
for child in n:
for child in sons(n):
addHcrInitGuards(p, child, inInitGuard, init)
else:
let stmtShouldExecute = n.kind in {nkVarSection, nkLetSection} or
@@ -3412,7 +3419,7 @@ proc handleProcGlobals(m: BModule) =
handleProcGlobals(m)
m.preInitProc.s(cpsStmts).add stmts.extract()
proc genTopLevelStmt*(m: BModule; n: PNode) =
proc genTopLevelStmt*(m: BModule; n: AnyNode) =
## Also called from `ic/cbackend.nim`.
if pipelineutils.skipCodegen(m.config, n): return
m.initProc.options = initProcOptions(m)
@@ -3507,7 +3514,7 @@ proc writeModule(m: BModule) =
code = stripCnifMarks(code)
registerModuleCode(m, cf, code)
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: AnyNode; isDynlib: bool): PSym =
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
let procname = getIdent(graph.cache, prefixedName)
result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
@@ -3561,7 +3568,7 @@ proc genIcModuleDestroyGlobals*(graph: ModuleGraph; m: BModule): string =
dtor.ast = theProc
genProcLvl3(m, dtor)
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: AnyNode) =
## Also called from IC.
if sfMainModule in m.module.flags:
# phase ordering problem here: We need to announce this
@@ -3584,7 +3591,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
# if the module is cached, we don't regenerate the main proc
# nor the dispatchers? But if the dispatchers changed?
# XXX emit the dispatchers into its own .c file?
if n != nil:
if not n.isNilNode:
m.initProc.options = initProcOptions(m)
genProcBody(m.initProc, n)

View File

@@ -93,7 +93,7 @@ proc getMagic*(op: AnyNode): TMagic =
else: result = mNone
else: result = mNone
proc isConstExpr*(n: PNode): bool =
proc isConstExpr*(n: AnyNode): bool =
const atomKinds = {nkCharLit..nkNilLit} # Char, Int, UInt, Str, Float and Nil literals
n.kind in atomKinds or nfAllConst in n.flags

View File

@@ -11,7 +11,7 @@
import
ast, astalgo, trees, msgs, platform, renderer, options,
lineinfos, int128, modulegraphs, astmsgs
lineinfos, int128, modulegraphs, astmsgs, bnode
import std/[intsets, strutils]
@@ -102,7 +102,7 @@ proc isPureObject*(typ: PType): bool =
proc isUnsigned*(t: PType): bool =
t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}
proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
proc getOrdValueAux*(n: AnyNode, err: var bool): Int128 =
var k = n.kind
if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}:
k = nkUIntLit
@@ -119,17 +119,17 @@ proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
of nkNilLit:
int128.Zero
of nkHiddenStdConv:
getOrdValueAux(n[1], err)
getOrdValueAux(n.secondSon, err)
else:
err = true
int128.Zero
proc getOrdValue*(n: PNode): Int128 =
proc getOrdValue*(n: AnyNode): Int128 =
var err: bool = false
result = getOrdValueAux(n, err)
#assert err == false
proc getOrdValue*(n: PNode, onError: Int128): Int128 =
proc getOrdValue*(n: AnyNode, onError: Int128): Int128 =
var err = false
result = getOrdValueAux(n, err)
if err:
@@ -1392,17 +1392,17 @@ proc classify*(t: PType): OrdinalType =
result = IntLike
else: result = NoneLike
proc skipConv*(n: PNode): PNode =
proc skipConv*[T: AnyNode](n: T): T =
result = n
case n.kind
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
# only skip the conversion if it doesn't lose too important information
# (see bug #1334)
if n[0].typ.classify == n.typ.classify:
result = n[0]
if n.firstSon.typ.classify == n.typ.classify:
result = n.firstSon
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if n[1].typ.classify == n.typ.classify:
result = n[1]
if n.secondSon.typ.classify == n.typ.classify:
result = n.secondSon
else: discard
proc skipHidden*(n: PNode): PNode =