From 709fd00861f89f611142591fe30500680e1e6e71 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 30 Aug 2026 15:43:14 +0200 Subject: [PATCH] =?UTF-8?q?IC:=20migrate=20`expr`=20and=20the=20emitters?= =?UTF-8?q?=20=E2=80=94=20cgen=20generates=20from=20a=20cursor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR --- compiler/ast.nim | 46 ++++-- compiler/astalgo.nim | 6 +- compiler/bnode.nim | 10 ++ compiler/ccgcalls.nim | 95 ++++++----- compiler/ccgexprs.nim | 338 +++++++++++++++++++++------------------ compiler/ccgliterals.nim | 20 +-- compiler/ccgstmts.nim | 138 ++++++++-------- compiler/ccgtrav.nim | 2 +- compiler/ccgtypes.nim | 2 +- compiler/cgen.nim | 71 ++++---- compiler/trees.nim | 2 +- compiler/types.nim | 20 +-- 12 files changed, 422 insertions(+), 328 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index d3c43dc638..8b5d62237c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 978c9e0a83..ad19af0a22 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -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 diff --git a/compiler/bnode.nim b/compiler/bnode.nim index 92a82fd751..edcbe5f096 100644 --- a/compiler/bnode.nim +++ b/compiler/bnode.nim @@ -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) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index a96de827d2..4dc6988a3d 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -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) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 4c49b96e64..bdbd826e45 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -12,7 +12,7 @@ when defined(nimCompilerStacktraceHints): import std/stackframes -proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, +proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: AnyNode, result: var Builder; init: var StructInitializer; isConst: bool, info: TLineInfo) @@ -20,7 +20,7 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope) -proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Builder) = +proc genLiteral(p: BProc, n: AnyNode, ty: PType; result: var Builder) = case n.kind of nkCharLit..nkUInt64Lit: var k: TTypeKind @@ -46,7 +46,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Builder) = of nkNilLit: let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure: - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + # `dataCache` is a `PNode`-keyed structural cache, so it needs the node + # itself. `origin` supplies the very node this cursor was encoded from, so + # the key is what it always was. + let id = nodeTableTestOrSet(p.module.dataCache, origin(n), p.module.labels) let tmpName = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: @@ -89,7 +92,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Builder) = else: internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') -proc genLiteral(p: BProc, n: PNode; result: var Builder) = +proc genLiteral(p: BProc, n: AnyNode; result: var Builder) = genLiteral(p, n, n.typ, result) proc genRawSetData(cs: TBitSet, size: int; result: var Builder) = @@ -106,11 +109,11 @@ proc genRawSetData(cs: TBitSet, size: int; result: var Builder) = else: result.addIntLiteral(cast[BiggestInt](bitSetToWord(cs, size))) -proc genSetNode(p: BProc, n: PNode; result: var Builder) = +proc genSetNode(p: BProc, n: AnyNode; result: var Builder) = var size = int(getSize(p.config, n.typ)) - let cs = toBitSet(p.config, n) + let cs = toBitSet(p.config, origin(n)) if size > 8: - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + let id = nodeTableTestOrSet(p.module.dataCache, origin(n), p.module.labels) let tmpName = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: @@ -122,7 +125,7 @@ proc genSetNode(p: BProc, n: PNode; result: var Builder) = else: genRawSetData(cs, size, result) -proc getStorageLoc(n: PNode): TStorageLoc = +proc getStorageLoc(n: AnyNode): TStorageLoc = ## deadcode case n.kind of nkSym: @@ -183,8 +186,8 @@ proc genRefAssign(p: BProc, dest, src: TLoc) = let rs = rdLoc(src) p.s(cpsStmts).addCallStmt(fnName, cCast(ptrType(CPointer), rad), rs) -proc asgnComplexity(n: PNode): int = - if n != nil: +proc asgnComplexity(n: AnyNode): int = + if not n.isNilNode: case n.kind of nkSym: result = 1 of nkRecCase: @@ -223,7 +226,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = optAsgnLoc(src, t, field), newflags) proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, - t: PNode, typ: PType) = + t: AnyNode, typ: PType) = if t == nil: return let newflags = if src.storage == OnStatic: @@ -567,7 +570,7 @@ proc putIntoDest(p: BProc, d: var TLoc, n: AnyNode, r: Rope; s=OnUnknown) = d.lode = origin(n) d.snippet = r -proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: TypedBinaryOp) = +proc binaryStmt(p: BProc, e: AnyNode, d: var TLoc, op: TypedBinaryOp) = if d.k != locNone: internalError(p.config, e.info, "binaryStmt") var a = initLocExpr(p, e.secondSon) var b = initLocExpr(p, son(e, 2)) @@ -575,7 +578,7 @@ proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: TypedBinaryOp) = let rb = rdLoc(b) p.s(cpsStmts).addInPlaceOp(op, getSimpleTypeDesc(p.module, e.secondSon.typ), ra, rb) -proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) = +proc binaryStmtAddr(p: BProc, e: AnyNode, d: var TLoc, cpname: string) = if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") var a = initLocExpr(p, e.secondSon) var b = initLocExpr(p, son(e, 2)) @@ -583,7 +586,7 @@ proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) = let rb = rdLoc(b) p.s(cpsStmts).addCallStmt(cgsymValue(p.module, cpname), bra, rb) -template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = +template binaryExpr(p: BProc, e: AnyNode, d: var TLoc, frmt: untyped) = assert(e.secondSon.typ != nil) assert(son(e, 2).typ != nil) block: @@ -593,7 +596,7 @@ template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = let rb {.inject.} = rdLoc(b) putIntoDest(p, d, e, frmt) -template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = +template binaryExprChar(p: BProc, e: AnyNode, d: var TLoc, frmt: untyped) = assert(e.secondSon.typ != nil) assert(son(e, 2).typ != nil) block: @@ -603,13 +606,13 @@ template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = let rb {.inject.} = rdCharLoc(b) putIntoDest(p, d, e, frmt) -template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = +template unaryExpr(p: BProc, e: AnyNode, d: var TLoc, frmt: untyped) = block: var a: TLoc = initLocExpr(p, e.secondSon) let ra {.inject.} = rdLoc(a) putIntoDest(p, d, e, frmt) -template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = +template unaryExprChar(p: BProc, e: AnyNode, d: var TLoc, frmt: untyped) = block: var a: TLoc = initLocExpr(p, e.secondSon) let ra {.inject.} = rdCharLoc(a) @@ -646,7 +649,7 @@ template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; result -proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc binaryArithOverflow(p: BProc, e: AnyNode, d: var TLoc, m: TMagic) = const prc: array[mAddI..mPred, string] = [ "nimAddInt", "nimSubInt", @@ -694,7 +697,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = let res = cCast(typ, cOp(opr[m], typ, wrapPar(rdLoc(a)), wrapPar(rdLoc(b)))) putIntoDest(p, d, e, res) -proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc unaryArithOverflow(p: BProc, e: AnyNode, d: var TLoc, m: TMagic) = var t: PType assert(e.secondSon.typ != nil) var a: TLoc = initLocExpr(p, e.secondSon) @@ -721,7 +724,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: assert(false, $m) -proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc binaryArith(p: BProc, e: AnyNode, d: var TLoc, op: TMagic) = var s, k: BiggestInt = 0 assert(e.secondSon.typ != nil) @@ -846,7 +849,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = assert(false, $op) putIntoDest(p, d, e, res) -proc genEqProc(p: BProc, e: PNode, d: var TLoc) = +proc genEqProc(p: BProc, e: AnyNode, d: var TLoc) = assert(e.secondSon.typ != nil) assert(son(e, 2).typ != nil) var a = initLocExpr(p, e.secondSon) @@ -860,7 +863,7 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) = else: putIntoDest(p, d, e, cOp(Equal, ra, rb)) -proc genIsNil(p: BProc, e: PNode, d: var TLoc) = +proc genIsNil(p: BProc, e: AnyNode, d: var TLoc) = let t = skipTypes(e.secondSon.typ, abstractRange) var a: TLoc = initLocExpr(p, e.secondSon) let ra = rdLoc(a) @@ -871,7 +874,7 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) = res = cOp(Equal, ra, cIntValue(0)) putIntoDest(p, d, e, res) -proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc unaryArith(p: BProc, e: AnyNode, d: var TLoc, op: TMagic) = var t: PType assert(e.secondSon.typ != nil) @@ -903,7 +906,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = skipTypes(typ, abstractInstOwned).kind in {tyVar} and tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags -proc genDeref(p: BProc, e: PNode, d: var TLoc) = +proc genDeref(p: BProc, e: AnyNode, d: var TLoc) = let mt = mapType(p.config, e.firstSon.typ, mapTypeChooser(e.firstSon) == skParam) if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should @@ -961,7 +964,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = else: putIntoDest(p, d, e, cDeref(rdLoc(a)), a.storage) -proc cowBracket(p: BProc; n: PNode) = +proc cowBracket(p: BProc; n: AnyNode) = if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and not p.config.usesSso(): let strCandidate = n.firstSon @@ -970,15 +973,15 @@ proc cowBracket(p: BProc; n: PNode) = let raa = byRefLoc(p, a) p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"), raa) -proc cow(p: BProc; n: PNode) {.inline.} = +proc cow(p: BProc; n: AnyNode) {.inline.} = if n.kind == nkHiddenAddr: cowBracket(p, n.firstSon) -template ignoreConv(e: PNode): bool = +template ignoreConv(e: AnyNode): bool = let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) let srcType = e.secondSon.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) sameBackendTypePickyAliases(destType, srcType) -proc genAddr(p: BProc, e: PNode, d: var TLoc) = +proc genAddr(p: BProc, e: AnyNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e.firstSon.typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: var a: TLoc = initLocExpr(p, e.firstSon) @@ -987,7 +990,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = elif mapType(p.config, e.firstSon.typ, mapTypeChooser(e.firstSon) == skParam) == ctArray or isCppRef(p, e.typ): expr(p, e.firstSon, d) # bug #19497 - d.lode = e + d.lode = origin(e) else: let ssoStrSub = p.config.usesSso() and e.firstSon.kind == nkBracketExpr and e.firstSon.firstSon.typ.skipTypes(abstractVar).kind == tyString @@ -1005,13 +1008,13 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = a.storage -proc genRecordFieldAux(p: BProc, e: PNode, d: var TLoc, a: var TLoc) = +proc genRecordFieldAux(p: BProc, e: AnyNode, d: var TLoc, a: var TLoc) = a = initLocExpr(p, e.firstSon) if e.secondSon.kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc -proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = +proc genTupleElem(p: BProc, e: AnyNode, d: var TLoc) = var i: int = 0 var a: TLoc = initLocExpr(p, e.firstSon) @@ -1043,7 +1046,7 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; ty = ty.baseClass if result == nil: internalError(p.config, field.info, "genCheckedRecordField") -proc genRecordField(p: BProc, e: PNode, d: var TLoc) = +proc genRecordField(p: BProc, e: AnyNode, d: var TLoc) = var a: TLoc = default(TLoc) if p.module.compileToCpp and e.kind == nkDotExpr and e.secondSon.kind == nkSym and e.secondSon.typ.kind == tyPtr: # special case for C++: we need to pull the type of the field as member and friends require the complete type. @@ -1069,9 +1072,9 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, r, a.storage) r.freeze -proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) +proc genInExprAux(p: BProc, e: AnyNode, a, b, d: var TLoc) -proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym, ty: PType) = +proc genFieldCheck(p: BProc, e: AnyNode, obj: Rope, field: PSym, ty: PType) = var test, u, v: TLoc for child in sonsFrom(e, 1): var it = child @@ -1153,7 +1156,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym, ty: PType) = raiseInstr(p, p.s(cpsStmts)) -proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = +proc genCheckedRecordField(p: BProc, e: AnyNode, d: var TLoc) = assert e.firstSon.kind == nkDotExpr if optFieldCheck in p.options: var a: TLoc = default(TLoc) @@ -1172,14 +1175,14 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = else: genRecordField(p, e.firstSon, d) -proc genUncheckedArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genUncheckedArrayElem(p: BProc, n, x, y: AnyNode, d: var TLoc) = var a = initLocExpr(p, x) var b = initLocExpr(p, y) d.inheritLocation(a) putIntoDest(p, d, n, subscript(rdLoc(a), rdCharLoc(b)), a.storage) -proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genArrayElem(p: BProc, n, x, y: AnyNode, d: var TLoc) = var a = initLocExpr(p, x) var b = initLocExpr(p, y) var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses) @@ -1218,7 +1221,7 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = let rcb = rdCharLoc(b) putIntoDest(p, d, n, subscript(ra, cOp(Sub, NimInt, rcb, first)), a.storage) -proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genCStringElem(p: BProc, n, x, y: AnyNode, d: var TLoc) = var a = initLocExpr(p, x) var b = initLocExpr(p, y) inheritLocation(d, a) @@ -1277,7 +1280,7 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) = else: discard -proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genOpenArrayElem(p: BProc, n, x, y: AnyNode, d: var TLoc) = var a = initLocExpr(p, x) var b = initLocExpr(p, y) let ra = rdLoc(a) @@ -1303,7 +1306,7 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = inheritLocation(d, a) putIntoDest(p, d, n, subscript(arrData, rcb), a.storage) -proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genSeqElem(p: BProc, n, x, y: AnyNode, d: var TLoc) = var a = initLocExpr(p, x) var b = initLocExpr(p, y) var ty = skipTypes(a.t, abstractVarRange) @@ -1344,7 +1347,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = let ra = rdLoc(a) putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage) -proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = +proc genBracketExpr(p: BProc; n: AnyNode; d: var TLoc) = var ty = skipTypes(n.firstSon.typ, abstractVarRange + tyUserTypeClasses) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.elementType, abstractVarRange) case ty.kind @@ -1373,7 +1376,7 @@ proc isSimpleExpr(n: AnyNode): bool = else: result = n.isAtom -proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc genAndOr(p: BProc, e: AnyNode, d: var TLoc, m: TMagic) = # how to generate code? # 'expr1 and expr2' becomes: # result = expr1 @@ -1428,7 +1431,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = genAssignment(p, d, tmp, {}) # no need for deep copying dec p.splitDecls -proc genEcho(p: BProc, n: PNode) = +proc genEcho(p: BProc, n: AnyNode) = # this unusual way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. internalAssert p.config, n.kind == nkBracket @@ -1476,8 +1479,8 @@ proc genEcho(p: BProc, n: PNode) = makeCString(repeat("%s", n.len) & "\L"), [args]) linefmt(p, cpsStmts, "fflush(stdout);$n", []) -proc gcUsage(conf: ConfigRef; n: PNode) = - if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree) +proc gcUsage(conf: ConfigRef; n: AnyNode) = + if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, origin(n).renderTree) proc strLoc(p: BProc; d: TLoc): Rope = if optSeqDestructors in p.config.globalOptions: @@ -1485,7 +1488,7 @@ proc strLoc(p: BProc; d: TLoc): Rope = else: result = rdLoc(d) -proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = +proc genStrConcat(p: BProc, e: AnyNode, d: var TLoc) = # # s = "Hello " & name & ", how do you feel?" & 'z' # @@ -1535,7 +1538,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, d, tmp, {}) # no need for deep copying gcUsage(p.config, e) -proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = +proc genStrAppend(p: BProc, e: AnyNode, d: var TLoc) = # # s &= "Hello " & name & ", how do you feel?" & 'z' # // BUG: what if s is on the left side too? @@ -1588,7 +1591,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = p.s(cpsStmts).addStmt(): p.s(cpsStmts).add(append) -proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = +proc genSeqElemAppend(p: BProc, e: AnyNode, d: var TLoc) = # seq &= x --> # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; @@ -1614,7 +1617,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) -proc genSeqElemAppendV2(p: BProc, e: PNode, d: var TLoc) = +proc genSeqElemAppendV2(p: BProc, e: AnyNode, d: var TLoc) = # s.add(x) with optSeqDestructors (arc/orc), inlined for direct slot construction: # NI oldLen = s.len; # if (s.p == NIM_NIL || (s.p->cap & ~NIM_STRLIT_FLAG) < oldLen + 1) @@ -1659,7 +1662,7 @@ proc genSeqElemAppendV2(p: BProc, e: PNode, d: var TLoc) = dest.snippet = subscript(dataField(p, ra), tmpL.snippet) genAssignment(p, dest, b, {}) -proc genDefault(p: BProc; n: PNode; d: var TLoc) = +proc genDefault(p: BProc; n: AnyNode; d: var TLoc) = if d.k == locNone: d = getTemp(p, n.typ, needsInit=true) else: resetLoc(p, d) @@ -1736,7 +1739,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = # set the object type: genObjectInit(p, cpsStmts, bt, a, constructRefObj) -proc genNew(p: BProc, e: PNode) = +proc genNew(p: BProc, e: AnyNode) = var a: TLoc = initLocExpr(p, e.secondSon) # 'genNew' also handles 'unsafeNew': if e.len == 3: @@ -1787,7 +1790,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = cgCall(p, "newSeq", typinfo, length)) genAssignment(p, dest, call, {}) -proc genNewSeq(p: BProc, e: PNode) = +proc genNewSeq(p: BProc, e: AnyNode) = var a = initLocExpr(p, e.secondSon) var b = initLocExpr(p, son(e, 2)) if optSeqDestructors in p.config.globalOptions: @@ -1808,7 +1811,7 @@ proc genNewSeq(p: BProc, e: PNode) = genNewSeqAux(p, a, b.rdLoc, lenIsZero) gcUsage(p.config, e) -proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = +proc genNewSeqOfCap(p: BProc; e: AnyNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) var a: TLoc = initLocExpr(p, e.secondSon) if optSeqDestructors in p.config.globalOptions: @@ -1834,10 +1837,10 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = putIntoDest(p, d, e, dres) gcUsage(p.config, e) -proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = +proc rawConstExpr(p: BProc, n: AnyNode; d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + let id = nodeTableTestOrSet(p.module.dataCache, origin(n), p.module.labels) fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic) if id == p.module.labels: # expression not found in the cache: @@ -1851,7 +1854,7 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = genBracedInit(p, n, isConst = true, t, data) p.module.s[cfsData].add(extract(data)) -proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = +proc handleConstExpr(p: BProc, n: AnyNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: rawConstExpr(p, n, d) result = true @@ -1859,12 +1862,12 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = result = false -proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) = +proc genFieldObjConstr[F: AnyNode; V: AnyNode](p: BProc; ty: PType; useTemp, isRef: bool; nField: F; val: V; check: PNode; d: var TLoc; r: Rope; info: TLineInfo) = var tmp2 = TLoc(snippet: r) let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet) if field.loc.snippet == "": fillObjectFields(p.module, ty) if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr") - if check != nil and optFieldCheck in p.options: + if not check.isNilNode and optFieldCheck in p.options: genFieldCheck(p, check, r, field, ty) tmp2.snippet = dotField(tmp2.snippet, field.loc.snippet) if useTemp: @@ -1873,7 +1876,7 @@ proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, c else: tmp2.k = d.k tmp2.storage = if isRef: OnHeap else: d.storage - tmp2.lode = val + tmp2.lode = origin(val) if nField.typ.skipTypes(abstractVar).kind in {tyOpenArray, tyVarargs}: var tmp3 = getTemp(p, val.typ) expr(p, val, tmp3) @@ -1881,7 +1884,7 @@ proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, c else: expr(p, val, tmp2) -proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = +proc genObjConstr(p: BProc, e: AnyNode, d: var TLoc) = # inheritance in C++ does not allow struct initialization so # we skip this step here: if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions: @@ -1907,7 +1910,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = isRef or d.k == locNone or (d.t != nil and not sameBackendType(t, d.t.skipTypes(abstractInstOwned))) or - (isPartOf(d.lode, e, {pfStructural, pfBidirectional}) != arNo) + (isPartOf(d.lode, origin(e), {pfStructural, pfBidirectional}) != arNo) var tmp: TLoc = default(TLoc) var r: Rope @@ -1937,9 +1940,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = # this is an object constructor node generated by the VM and # this field is in an inactive case branch, don't generate assignment continue + # Nilable, so a `PNode` — a cursor has no standalone nil. var check: PNode = nil - if it.len == 3 and optFieldCheck in p.options: - check = son(it, 2) + if it.safeLen == 3 and optFieldCheck in p.options: + check = origin(son(it, 2)) genFieldObjConstr(p, ty, useTemp, isRef, it.firstSon, it.secondSon, check, d, r, e.info) if useTemp: @@ -1949,15 +1953,17 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, d, tmp, {}) proc lhsDoesAlias(a, b: PNode): bool = + ## Stays `PNode`: it is `isPartOf` underneath, which compares field symbols by + ## identity and so has not moved to the seam (see `bnode.sym`). result = false for y in sons(b): if isPartOf(a, y) != arNo: return true -proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = +proc genSeqConstr(p: BProc, n: AnyNode, d: var TLoc) = var arr: TLoc var tmp: TLoc = default(TLoc) # bug #668 - let doesAlias = lhsDoesAlias(d.lode, n) + let doesAlias = lhsDoesAlias(d.lode, origin(n)) let dest = if doesAlias: addr(tmp) else: addr(d) if doesAlias: tmp = getTemp(p, n.typ) @@ -1994,11 +2000,15 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = else: genAssignment(p, d, tmp, {}) -proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = +proc genArrToSeq(p: BProc, n: AnyNode, d: var TLoc) = var elem, arr: TLoc if n.secondSon.kind == nkBracket: - n.secondSon.typ = n.typ - genSeqConstr(p, n.secondSon, d) + # Retypes the bracket in place and then generates it, so both the mutation + # and the generation run on the origin — a cursor would read the type slot + # as encoded and miss the assignment. + let bracket = origin(n.secondSon) + bracket.typ = n.typ + genSeqConstr(p, bracket, d) return if d.k == locNone: d = getTemp(p, n.typ) @@ -2042,7 +2052,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = genAssignment(p, elem, arr, {needToCopy}) -proc genNewFinalize(p: BProc, e: PNode) = +proc genNewFinalize(p: BProc, e: AnyNode) = var b: TLoc refType, bt: PType @@ -2089,7 +2099,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Bu ti, cache) -proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = +proc genOf(p: BProc, x: AnyNode, typ: PType, d: var TLoc) = var a: TLoc = initLocExpr(p, x) var dest = skipTypes(typ, typedescPtrs) var r = rdLoc(a) @@ -2117,10 +2127,10 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = putIntoDest(p, d, x, ofExpr, a.storage) -proc genOf(p: BProc, n: PNode, d: var TLoc) = +proc genOf(p: BProc, n: AnyNode, d: var TLoc) = genOf(p, n.secondSon, son(n, 2).typ, d) -proc genRepr(p: BProc, e: PNode, d: var TLoc) = +proc genRepr(p: BProc, e: AnyNode, d: var TLoc) = if optTinyRtti in p.config.globalOptions: localError(p.config, e.info, "'repr' is not available for --newruntime") var a: TLoc = initLocExpr(p, e.secondSon) @@ -2210,13 +2220,13 @@ proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Snippet; enforce if optTinyRtti in p.config.globalOptions and enforceV1: result = derefField(result, "typeInfoV1") -proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = +proc genGetTypeInfo(p: BProc, e: AnyNode, d: var TLoc) = cgsym(p.module, "TNimType") let t = e.secondSon.typ # ordinary static type information putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info)) -proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = +proc genGetTypeInfoV2(p: BProc, e: AnyNode, d: var TLoc) = let t = e.secondSon.typ if isFinal(t) or e.firstSon.sym.name.s != "getDynamicTypeInfo": # ordinary static type information @@ -2229,7 +2239,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = rdMType(p, a, nilCheck, rt) putIntoDest(p, d, e, rt) -proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = +proc genAccessTypeField(p: BProc; e: AnyNode; d: var TLoc) = var a: TLoc = initLocExpr(p, e.secondSon) var nilCheck = "" # use the dynamic type stored at offset 0: @@ -2237,7 +2247,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = rdMType(p, a, nilCheck, rt) putIntoDest(p, d, e, rt) -template genDollarIt(p: BProc, n: PNode, d: var TLoc, frmt: untyped) = +template genDollarIt(p: BProc, n: AnyNode, d: var TLoc, frmt: untyped) = block: var a: TLoc = initLocExpr(p, n.secondSon) let it {.inject.} = rdLoc(a) @@ -2247,7 +2257,7 @@ template genDollarIt(p: BProc, n: PNode, d: var TLoc, frmt: untyped) = genAssignment(p, d, a, {}) gcUsage(p.config, n) -proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc genArrayLen(p: BProc, e: AnyNode, d: var TLoc, op: TMagic) = var a = e.secondSon if a.kind == nkHiddenAddr: a = a.firstSon var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) @@ -2311,10 +2321,12 @@ proc isTrivialTypesToSnippet(t: PType): Snippet = else: result = NimTrue -proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc, noinit = false) = +proc genSetLengthSeq(p: BProc, e: AnyNode, d: var TLoc, noinit = false) = if optSeqDestructors in p.config.globalOptions: - e.secondSon = makeAddr(e.secondSon, p.module.idgen) - genCall(p, e, d) + # In-place rewrite; continue on the origin (see `mAppendSeqElem`). + let en = origin(e) + en.secondSon = makeAddr(en.secondSon, p.module.idgen) + genCall(p, en, d) return assert(d.k == locNone) var x = e.secondSon @@ -2341,7 +2353,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc, noinit = false) = genAssignment(p, a, call, {}) gcUsage(p.config, e) -proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = +proc genSetLengthStr(p: BProc, e: AnyNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: binaryStmtAddr(p, e, d, "setLengthStrV2") else: @@ -2354,7 +2366,7 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, a, call, {}) gcUsage(p.config, e) -proc genSwap(p: BProc, e: PNode, d: var TLoc) = +proc genSwap(p: BProc, e: AnyNode, d: var TLoc) = # swap(a, b) --> # temp = a # a = b @@ -2388,13 +2400,13 @@ proc fewCmps(conf: ConfigRef; s: AnyNode): bool = else: result = s.len <= 8 # 8 seems to be a good value -template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: untyped) = +template binaryExprIn(p: BProc, e: AnyNode, a, b, d: var TLoc, frmt: untyped) = var elem {.inject.}: Snippet = "" rdSetElemLoc(p.config, b, a.t, elem) let ra {.inject.} = rdLoc(a) putIntoDest(p, d, e, frmt) -proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = +proc genInExprAux(p: BProc, e: AnyNode, a, b, d: var TLoc) = let s = int(getSize(p.config, skipTypes(e.secondSon.typ, abstractVar))) case s of 1, 2, 4, 8: @@ -2423,7 +2435,7 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = cUintValue(7)))), cIntValue(0))) -template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = +template binaryStmtInExcl(p: BProc, e: AnyNode, d: var TLoc, frmt: untyped) = assert(d.k == locNone) var a = initLocExpr(p, e.secondSon) var b = initLocExpr(p, son(e, 2)) @@ -2432,7 +2444,7 @@ template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: untyped) = let ra {.inject.} = rdLoc(a) p.s(cpsStmts).add(frmt) -proc genInOp(p: BProc, e: PNode, d: var TLoc) = +proc genInOp(p: BProc, e: AnyNode, d: var TLoc) = var a, b, x, y: TLoc if (e.secondSon.kind == nkCurly) and fewCmps(p.config, e.secondSon): # a set constructor but not a constant set: @@ -2479,7 +2491,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = b = initLocExpr(p, son(e, 2)) genInExprAux(p, e, a, b, d) -proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc genSetOp(p: BProc, e: AnyNode, d: var TLoc, op: TMagic) = var a, b: TLoc var i: TLoc var setType = skipTypes(e.secondSon.typ, abstractVar) @@ -2595,10 +2607,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mInSet: genInOp(p, e, d) else: internalError(p.config, e.info, "genSetOp") -proc genOrd(p: BProc, e: PNode, d: var TLoc) = +proc genOrd(p: BProc, e: AnyNode, d: var TLoc) = unaryExprChar(p, e, d, ra) -proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = +proc genSomeCast(p: BProc, e: AnyNode, d: var TLoc) = const ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray} # we use whatever C gives us. Except if we have a value-type, we need to go @@ -2647,7 +2659,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = let val = rdCharLoc(a) putIntoDest(p, d, e, cCast(destTyp, wrapPar(val)), a.storage) -proc genCast(p: BProc, e: PNode, d: var TLoc) = +proc genCast(p: BProc, e: AnyNode, d: var TLoc) = const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray} let destt = skipTypes(e.typ, abstractRange) @@ -2687,7 +2699,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = # C code; plus it's the right thing to do for closures: genSomeCast(p, e, d) -proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = +proc genRangeChck(p: BProc, n: AnyNode, d: var TLoc) = var a: TLoc = initLocExpr(p, n.firstSon) var dest = skipTypes(n.typ, abstractVar) if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and @@ -2743,20 +2755,20 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = let val = rdCharLoc(a) putIntoDest(p, d, n, cCast(destType, wrapPar(val)), a.storage) -proc genConv(p: BProc, e: PNode, d: var TLoc) = +proc genConv(p: BProc, e: AnyNode, d: var TLoc) = if ignoreConv(e): expr(p, e.secondSon, d) else: genSomeCast(p, e, d) -proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = +proc convStrToCStr(p: BProc, n: AnyNode, d: var TLoc) = var a: TLoc = initLocExpr(p, n.firstSon) let arg = if p.config.usesSso(): byRefLoc(p, a) else: rdLoc(a) putIntoDest(p, d, n, cgCall(p, "nimToCStringConv", arg), a.storage) -proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = +proc convCStrToStr(p: BProc, n: AnyNode, d: var TLoc) = var a: TLoc = initLocExpr(p, n.firstSon) if p.module.compileToCpp: # fixes for const qualifier; bug #12703; bug #19588 @@ -2769,7 +2781,7 @@ proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = a.storage) gcUsage(p.config, n) -proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = +proc genStrEquals(p: BProc, e: AnyNode, d: var TLoc) = var x: TLoc var a = e.secondSon var b = son(e, 2) @@ -2784,7 +2796,7 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = else: binaryExpr(p, e, d, cgCall(p, "eqStrings", ra, rb)) -proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc binaryFloatArith(p: BProc, e: AnyNode, d: var TLoc, m: TMagic) = if {optNaNCheck, optInfCheck} * p.options != {}: const opr: array[mAddF64..mDivF64, TypedBinaryOp] = [Add, Sub, Mul, Div] assert(e.secondSon.typ != nil) @@ -2812,7 +2824,7 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: binaryArith(p, e, d, m) -proc genWasMoved(p: BProc; n: PNode) = +proc genWasMoved(p: BProc; n: AnyNode) = var a: TLoc let n1 = n.secondSon.skipAddr if p.withinBlockLeaveActions > 0 and notYetAlive(n1): @@ -2823,7 +2835,7 @@ proc genWasMoved(p: BProc; n: PNode) = #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) -proc genMove(p: BProc; n: PNode; d: var TLoc) = +proc genMove(p: BProc; n: AnyNode; d: var TLoc) = if n.len == 4: # generated by liftdestructors: var a: TLoc = initLocExpr(p, n.secondSon.skipAddr, {lfEnforceDeref, lfPrepareForMutation}) @@ -2852,14 +2864,16 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = genAssignment(p, d, a, {}) resetLoc(p, a) else: - n.secondSon = makeAddr(n.secondSon, p.module.idgen) - genCall(p, n, d) + # In-place rewrite; continue on the origin (see `mAppendSeqElem`). + let nn = origin(n) + nn.secondSon = makeAddr(nn.secondSon, p.module.idgen) + genCall(p, nn, d) else: var a: TLoc = initLocExpr(p, n.secondSon.skipAddr, {lfEnforceDeref, lfPrepareForMutation}) genAssignment(p, d, a, {}) resetLoc(p, a) -proc genDestroy(p: BProc; n: PNode) = +proc genDestroy(p: BProc; n: AnyNode) = if optSeqDestructors in p.config.globalOptions: let arg = n.secondSon.skipAddr let t = arg.typ.skipTypes(abstractInst) @@ -2900,7 +2914,7 @@ proc genDestroy(p: BProc; n: PNode) = internalError(p.config, n.info, "destructor turned out to be not trivial") discard "ignore calls to the default destructor" -proc genSlice(p: BProc; e: PNode; d: var TLoc) = +proc genSlice(p: BProc; e: AnyNode; d: var TLoc) = let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.elementType, prepareForMutation = e.secondSon.kind == nkHiddenDeref and e.secondSon.typ.skipTypes(abstractInst).kind == tyString and @@ -2913,20 +2927,23 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = localError(p.config, e.info, "invalid context for 'toOpenArray'; " & "'toOpenArray' is only valid within a call expression") -proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) = +proc genEnumToStr(p: BProc, e: AnyNode, d: var TLoc) = let t = e.secondSon.typ.skipTypes(abstractInst+{tyRange}) let toStrProc = getToStringProc(p.module.g.graph, t) # XXX need to modify this logic for IC. - var n = copyTree(e) + # The generator REWRITES here: it builds a fresh call and generates that. + # `origin` gives the `PNode` to copy, so a construction site is unaffected by + # the reader side having moved to a cursor. + var n = copyTree(origin(e)) n[0] = newSymNode(toStrProc) expr(p, n, d) -proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc genMagicExpr(p: BProc, e: AnyNode, d: var TLoc, op: TMagic) = case op of mAsgn: let kind = if e.firstSon.sym.name.s == "=sink": nkSinkAsgn else: nkAsgn - let lhs = e.secondSon.skipHiddenAddr - let n = newTreeI(kind, e.info, lhs, son(e, 2)) + let lhs = origin(e.secondSon.skipHiddenAddr) + let n = newTreeI(kind, e.info, lhs, origin(son(e, 2))) n.typ = e.typ cow(p, son(e, 2)) genAsgn(p, n, fastAsgn = kind != nkAsgn) @@ -2981,8 +2998,15 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # gcYrc is excluded because its add() acquires a striped reader lock. genSeqElemAppendV2(p, e, d) else: - e.secondSon = makeAddr(e.secondSon, p.module.idgen) - genCall(p, e, d) + # A REWRITE inside the generator: it replaces a child in place and + # then generates the mutated call. A cursor cannot be written to, so + # this works on the origin — which is the very node the buffer was + # encoded from, so the mutation lands exactly where it always did. + # The buffer is stale for this subtree afterwards; nothing reads it + # again, because generation for this expression continues on the tree. + let en = origin(e) + en.secondSon = makeAddr(en.secondSon, p.module.idgen) + genCall(p, en, d) else: genSeqElemAppend(p, e, d) of mEqStr: genStrEquals(p, e, d) @@ -3023,8 +3047,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = genNewFinalize(p, e) of mNewSeq: if optSeqDestructors in p.config.globalOptions: - e.secondSon = makeAddr(e.secondSon, p.module.idgen) - genCall(p, e, d) + # See `mAppendSeqElem` above: an in-place rewrite, so it runs on the + # origin and generation continues from the tree. + let en = origin(e) + en.secondSon = makeAddr(en.secondSon, p.module.idgen) + genCall(p, en, d) else: genNewSeq(p, e) of mNewSeqOfCap: genNewSeqOfCap(p, e, d) @@ -3035,11 +3062,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let t = e.secondSon.typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e, cCast(NimInt, cAlignof(getTypeDesc(p.module, t, dkVar)))) of mOffsetOf: + # `nil` is a possible value here, so this is a `PNode` — a cursor has no + # standalone nil — and `origin` supplies it. var dotExpr: PNode if e.secondSon.kind == nkDotExpr: - dotExpr = e.secondSon + dotExpr = origin(e.secondSon) elif e.secondSon.kind == nkCheckedFieldExpr: - dotExpr = e.secondSon.firstSon + dotExpr = origin(e.secondSon.firstSon) else: dotExpr = nil internalError(p.config, e.info, "unknown ast") @@ -3113,13 +3142,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = when defined(leanCompiler): p.config.quitOrRaise "compiler built without support for the 'spawn' statement" else: - let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil) + let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, origin(e), e.typ, nil, nil) expr(p, n, d) of mParallel: when defined(leanCompiler): p.config.quitOrRaise "compiler built without support for the 'parallel' statement" else: - let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e) + let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, origin(e)) expr(p, n, d) of mDeepCopy: if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc} and optEnableDeepCopy notin p.config.globalOptions: @@ -3153,7 +3182,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", son(p.prc.ast, genericParamsPos).kind internalError(p.config, e.info, "genMagicExpr: " & $op) -proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = +proc genSetConstr(p: BProc, e: AnyNode, d: var TLoc) = # example: { a..b, c, d, e, f..g } # we have to emit an expression of the form: # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); @@ -3226,7 +3255,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = cOp(Shl, ts, cCast(ts, cIntValue(1)), cOp(Mod, ts, aa, cOp(Mul, ts, cIntValue(size), cIntValue(8))))) -proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = +proc genTupleConstr(p: BProc, n: AnyNode, d: var TLoc) = var rec: TLoc if not handleConstExpr(p, n, d): let t = n.typ @@ -3234,7 +3263,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var tmp: TLoc = default(TLoc) # bug #16331 - let doesAlias = lhsDoesAlias(d.lode, n) + let doesAlias = lhsDoesAlias(d.lode, origin(n)) let dest = if doesAlias: addr(tmp) else: addr(d) if doesAlias: tmp = getTemp(p, n.typ) @@ -3261,7 +3290,7 @@ proc isConstClosure(n: AnyNode): bool {.inline.} = result = n.firstSon.kind == nkSym and isRoutine(n.firstSon.sym) and n.secondSon.kind == nkNilLit -proc genClosure(p: BProc, n: PNode, d: var TLoc) = +proc genClosure(p: BProc, n: AnyNode, d: var TLoc) = assert n.kind in {nkPar, nkTupleConstr, nkClosure} if isConstClosure(n): @@ -3292,7 +3321,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = p.s(cpsStmts).addFieldAssignment(dest, "ClE_0", b.rdLoc) putLocIntoDest(p, d, tmp) -proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = +proc genArrayConstr(p: BProc, n: AnyNode, d: var TLoc) = var arr: TLoc if not handleConstExpr(p, n, d): if d.k == locNone: d = getTemp(p, n.typ) @@ -3328,17 +3357,17 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} = if frameName != "": p.s(cpsStmts).add deinitFrameNoDebug(p, frameName) -proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = +proc genStmtListExpr(p: BProc, n: AnyNode, d: var TLoc) = genStmtListExprImpl: expr(p, n.lastSon, d) -proc genStmtList(p: BProc, n: PNode) = +proc genStmtList(p: BProc, n: AnyNode) = genStmtListExprImpl: genStmts(p, n.lastSon) from parampatterns import isLValue -proc upConv(p: BProc, n: PNode, d: var TLoc) = +proc upConv(p: BProc, n: AnyNode, d: var TLoc) = var a: TLoc = initLocExpr(p, n.firstSon) let dest = skipTypes(n.typ, abstractPtrs) if optObjCheck in p.options and not isObjLackingTypeField(dest): @@ -3368,7 +3397,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = elif n.firstSon.typ.kind != tyObject: let destTyp = getTypeDesc(p.module, n.typ) let val = rdLoc(a) - if n.isLValue: + if origin(n).isLValue: # (*((destType) (&(val))))" putIntoDest(p, d, n, cDeref( @@ -3388,7 +3417,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = wrapPar(val))), a.storage) -proc downConv(p: BProc, n: PNode, d: var TLoc) = +proc downConv(p: BProc, n: AnyNode, d: var TLoc) = var arg = n.firstSon while arg.kind == nkObjDownConv: arg = arg.firstSon @@ -3396,7 +3425,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = let src = skipTypes(arg.typ, abstractPtrs) discard getTypeDesc(p.module, src) let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent} - if isRef and d.k == locNone and n.typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr} and n.isLValue: + if isRef and d.k == locNone and n.typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr} and origin(n).isLValue: # it can happen that we end up generating '&&x->Sup' here, so we pack # the '&x->Sup' into a temporary and then those address is taken # (see bug #837). However sometimes using a temporary is not correct: @@ -3427,10 +3456,10 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = r = cAddr(r) putIntoDest(p, d, n, r, a.storage) -proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = +proc exprComplexConst(p: BProc, n: AnyNode, d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + let id = nodeTableTestOrSet(p.module.dataCache, origin(n), p.module.labels) let tmp = p.module.tmpBase & rope(id) if id == p.module.labels: @@ -3542,17 +3571,17 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = q.initProc.procSec(cpsLocals).addArgument(copyCall): q.initProc.procSec(cpsLocals).addSizeof(rdLoc(sym.loc)) -proc genConstStmt(p: BProc, n: PNode) = +proc genConstStmt(p: BProc, n: AnyNode) = # This code is only used in the new DCE implementation. assert delayedCodegen(p.module) let m = p.module - for it in n: + for it in sons(n): if it.firstSon.kind == nkSym: let sym = it.firstSon.sym if not isSimpleConst(sym.typ) and sym.itemId.item in m.alive and genConstSetup(p, sym): genConstDefinition(m, p, sym) -proc expr(p: BProc, n: PNode, d: var TLoc) = +proc expr(p: BProc, n: AnyNode, d: var TLoc) = when defined(nimCompilerStacktraceHints): setFrameMsg p.config$n.info & " " & $n.kind p.currLineInfo = n.info @@ -3749,7 +3778,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if delayedCodegen(p.module): genConstStmt(p, n) else: # enforce addressable consts for exportc - for it in n: + for it in sons(n): let symNode = skipPragmaExpr(it.firstSon) if symNode.kind == nkSym and sfExportc in symNode.sym.flags: requestConstImpl(p, symNode.sym) @@ -3787,7 +3816,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkTypeSection: # we have to emit the type information for object types here to support # separate compilation: - genTypeSection(p.module, n) + genTypeSection(p.module, origin(n)) of nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: @@ -3796,7 +3825,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkPragmaBlock: var inUncheckedAssignSection = 0 let pragmaList = n.firstSon - for pi in pragmaList: + for pi in sons(pragmaList): if whichPragma(pi) == wCast: case whichPragma(pi.secondSon) of wUncheckedAssign: @@ -3843,14 +3872,14 @@ proc isOpaqueImportcType(t: PType): bool = if tfCompleteStruct notin t.flags: if tfIncompleteStruct in t.flags: return true - if t.kind == tyObject and (t.n == nil or not t.n.hasSons): + if t.kind == tyObject and (t.n.isNilNode or not t.n.hasSons): return true return false proc containsOpaqueImportcField(typ: PType): bool -proc containsOpaqueImportcFieldAux(t: PType; n: PNode): bool = - if n == nil: return false +proc containsOpaqueImportcFieldAux(t: PType; n: AnyNode): bool = + if n.isNilNode: return false case n.kind of nkRecList: for child in sons(n): @@ -3979,12 +4008,12 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder) else: globalError(p.config, info, "cannot create null element for: " & $t.kind) -proc isEmptyCaseObjectBranch(n: PNode): bool = - for it in n: +proc isEmptyCaseObjectBranch(n: AnyNode): bool = + for it in sons(n): if it.kind == nkSym and not isEmptyType(it.sym.typ): return false return true -proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, +proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: AnyNode, result: var Builder; init: var StructInitializer; isConst: bool, info: TLineInfo) = case obj.kind @@ -4061,7 +4090,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, else: localError(p.config, info, "cannot create null element for: " & $obj) -proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, +proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: AnyNode, result: var Builder; init: var StructInitializer; isConst: bool, info: TLineInfo) = var base = t.baseClass @@ -4085,33 +4114,28 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, # do not emit '{}' as that is not valid C: if oldcount == count: result = oldRes -proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Builder) = +proc genConstObjConstr(p: BProc; n: AnyNode; isConst: bool; result: var Builder) = let t = n.typ.skipTypes(abstractInstOwned) # Use designated initializers when opaque importc fields present var objInit: StructInitializer let initKind = if t.kind == tyObject and containsOpaqueImportcField(t): siNamedStruct else: siOrderedStruct result.addStructInitializer(objInit, kind = initKind): if t.kind == tyObject: - getNullValueAuxT(p, t, t, t.n, n, result, objInit, isConst, n.info) + getNullValueAuxT(p, t, t, t.n, origin(n), result, objInit, isConst, n.info) -proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Builder) = +proc genConstSimpleList(p: BProc, n: AnyNode; isConst: bool; result: var Builder) = var arrInit: StructInitializer result.addStructInitializer(arrInit, kind = siArray): if p.vccAndC and not n.hasSons and n.typ.kind == tyArray: result.addField(arrInit, name = ""): getDefaultValue(p, n.typ.elementType, n.info, result) for it in sons(n): - var ind, val: PNode - if it.kind == nkExprColonExpr: - ind = it.firstSon - val = it.secondSon - else: - ind = it - val = it + let ind = if it.kind == nkExprColonExpr: it.firstSon else: it + let val = if it.kind == nkExprColonExpr: it.secondSon else: it result.addField(arrInit, name = ""): genBracedInit(p, val, isConst, ind.typ, result) -proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Builder) = +proc genConstTuple(p: BProc, n: AnyNode; isConst: bool; tup: PType; result: var Builder) = var tupleInit: StructInitializer result.addStructInitializer(tupleInit, kind = siOrderedStruct): if p.vccAndC and not n.hasSons: @@ -4126,7 +4150,7 @@ proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Bu result.addField(tupleInit, name = "Field" & $i): genBracedInit(p, it, isConst, tup[i], result) -proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builder) = +proc genConstSeq(p: BProc, n: AnyNode, t: PType; isConst: bool; result: var Builder) = let base = t.skipTypes(abstractInst).elementType let tmpName = getTempName(p.module) @@ -4159,7 +4183,7 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builde result.add cCast(typ = getTypeDesc(p.module, t), value = cAddr(tmpName)) -proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builder) = +proc genConstSeqV2(p: BProc, n: AnyNode, t: PType; isConst: bool; result: var Builder) = let base = t.skipTypes(abstractInst).elementType let payload = getTempName(p.module) @@ -4192,7 +4216,7 @@ proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Buil result.addField(resultInit, name = "p"): result.add cCast(typ = ptrType(getSeqPayloadType(p.module, t)), value = cAddr(payload)) -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) = case n.kind of nkHiddenStdConv, nkHiddenSubConv: genBracedInit(p, n.secondSon, isConst, n.typ, result) @@ -4209,7 +4233,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul ty = typ.kind case ty of tySet: - let cs = toBitSet(p.config, n) + let cs = toBitSet(p.config, origin(n)) genRawSetData(cs, int(getSize(p.config, n.typ)), result) of tySequence: if optSeqDestructors in p.config.globalOptions: @@ -4241,7 +4265,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) of tyArray, tyVarargs: - if isDefaultBroadcastArray(n, p.config): + if isDefaultBroadcastArray(origin(n), p.config): # Compact zero/null-default array (see `isDefaultBroadcastArray`): the # whole thing is the null value of every slot, so a single C `{0}` # zero-fills all `lengthOrd` elements — no need to materialise them. diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index ddedabf6ad..3bcaab7f62 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -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) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f0010846a2..3f4eff1879 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -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) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 9993787b9f..d7156d1ca8 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -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) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index ac2692b525..b794858688 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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 diff --git a/compiler/cgen.nim b/compiler/cgen.nim index dbc424dc9b..d2933d8af0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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 . )` 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) diff --git a/compiler/trees.nim b/compiler/trees.nim index 7d8c87ec35..3291c0ef42 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -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 diff --git a/compiler/types.nim b/compiler/types.nim index fe4334fc34..3106dc843b 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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 =