mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-02 11:53:45 +00:00
The same treatment the `PNode` side just got, for the reason that applies to types: `t[0]` is the return type, the base class, the index type or the generic head depending on the kind, and the subscript says none of that. Every child access in the cgen files that has a named accessor now uses it — `baseClass` for the eleven object-hierarchy walks, `elementType` for the seq/openArray element, `returnType`, `genericHead`, `firstGenericParam` — and the two loops that walked a type's children become `paramTypes` and `kids`. Left indexed on purpose: a parameter reached by ARGUMENT position (`typ[i]` in ccgcalls/ccgstmts), a tuple field, and a generic parameter at an explicit index. There the index is the clearest thing to write. Every substitution is exact rather than merely close. `[]` with index 0 is unconditionally `sonsImpl[0]`, so `baseClass`/`returnType`/`genericHead` cannot diverge; `elementType` is `sonsImpl[^1]` and is used only where the type has a single son; `paramTypes` and `kids` are literally the loops they replace. `ast.sons(t: PType)` gets the warning it has been missing. Despite the name it is not the counterpart of the `sons` ITERATOR over a `PNode`: it returns the raw seq, and a `tyProc` keeps its parameter types in `n`, so that seq holds only the return type while `[]`/`len`/`kids` route parameters through `n[i].sym.typ`. `for x in t.sons` therefore compiles, reads exactly like the `PNode` idiom, and visits a different set of types — which is what `ccgutils.encodeType` would have started doing had it been converted to `sons` rather than `kids`. Marking the proc deprecated and rebuilding shows one call site in the whole compiler (`previouslyInferred`), so the trap is latent, not active. `bnode.nim` also records that there is deliberately no `BType` beside `BNode`: types stay `PType`s under `newIcBackend` — `BNode.typ` returns one — because the backend asks them questions (`skipTypes`, `getSize`, `lengthOrd`, the record walk over `t.n`) that a raw cursor cannot answer. Pure refactor: all 216 generated `.c` files byte-identical to the parent commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE
188 lines
6.4 KiB
Nim
188 lines
6.4 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2012 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# This module declares some helpers for the C code generator.
|
|
|
|
import
|
|
ast, types, msgs, wordrecg,
|
|
platform, trees, options, cgendata, mangleutils, renderer, modulegraphs
|
|
|
|
import std/[hashes, strutils, formatfloat]
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/assertions
|
|
|
|
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
|
case n.kind
|
|
of nkStmtList:
|
|
result = nil
|
|
for it in sons(n):
|
|
result = getPragmaStmt(it, w)
|
|
if result != nil: break
|
|
of nkPragma:
|
|
result = nil
|
|
for it in sons(n):
|
|
if whichPragma(it) == w: return it
|
|
else:
|
|
result = nil
|
|
|
|
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
|
|
result = getPragmaStmt(n, w) != nil
|
|
|
|
proc hashString*(conf: ConfigRef; s: string): BiggestInt =
|
|
# has to be the same algorithm as strmantle.hashString!
|
|
if CPU[conf.target.targetCPU].bit == 64:
|
|
# we have to use the same bitwidth
|
|
# as the target CPU
|
|
var b = 0'u64
|
|
for i in 0..<s.len:
|
|
b = b + uint(s[i])
|
|
b = b + (b shl 10)
|
|
b = b xor (b shr 6)
|
|
b = b + (b shl 3)
|
|
b = b xor (b shr 11)
|
|
b = b + (b shl 15)
|
|
result = cast[Hash](b)
|
|
else:
|
|
var a = 0'u32
|
|
for i in 0..<s.len:
|
|
a = a + uint32(s[i])
|
|
a = a + (a shl 10)
|
|
a = a xor (a shr 6)
|
|
a = a + (a shl 3)
|
|
a = a xor (a shr 11)
|
|
a = a + (a shl 15)
|
|
result = cast[Hash](uint(a))
|
|
|
|
template getUniqueType*(key: PType): PType = key
|
|
|
|
proc makeSingleLineCString*(s: string): string =
|
|
result = "\""
|
|
for c in items(s):
|
|
c.toCChar(result)
|
|
result.add('\"')
|
|
|
|
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
|
|
case int(getSize(conf, typ))
|
|
of 1: result = ctInt8
|
|
of 2: result = ctInt16
|
|
of 4: result = ctInt32
|
|
of 8: result = ctInt64
|
|
else: result = ctArray
|
|
|
|
proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
|
var pt = skipTypes(s.typ, typedescInst)
|
|
assert skResult != s.kind
|
|
|
|
#note precedence: params override types
|
|
if optByRef in s.options: return true
|
|
elif sfByCopy in s.flags: return false
|
|
elif tfByRef in pt.flags: return true
|
|
elif tfByCopy in pt.flags: return false
|
|
case pt.kind
|
|
of tyObject:
|
|
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
|
|
# forwarded objects are *always* passed by pointers for consistency!
|
|
result = true
|
|
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
|
|
result = true # requested anyway
|
|
elif (tfFinal in pt.flags) and (pt.baseClass == nil):
|
|
result = false # no need, because no subtyping possible
|
|
else:
|
|
result = true # ordinary objects are always passed by reference,
|
|
# otherwise casting doesn't work
|
|
of tyTuple:
|
|
result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
|
|
else:
|
|
result = false
|
|
# first parameter and return type is 'lent T'? --> use pass by pointer
|
|
if s.position == 0 and retType != nil and retType.kind == tyLent:
|
|
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
|
|
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
|
|
|
|
proc encodeName*(name: string): string =
|
|
result = mangle(name)
|
|
result = $result.len & result
|
|
|
|
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
|
result = if name == "": s.name.s else: name
|
|
# keep backend-minted ids out of the `_u` namespace; their item counter
|
|
# restarts at 0 and would collide with loaded symbols' ids. Which integer
|
|
# identifies such a symbol is decided ONCE, in `astdef.backendMintedDisamb`,
|
|
# shared with `mangleProcNameExt` and `ast2nif.toNifSymName`.
|
|
if s.itemId.isBackendMinted:
|
|
result.add "_c"
|
|
result.add $backendMintedDisamb(s)
|
|
else:
|
|
result.add "_u"
|
|
# Mirror `mangleProcNameExt`: use the per-(module,name) `disamb`, NOT
|
|
# `itemId.item`. Under the per-module IC backend the same symbol is loaded
|
|
# from a NIF in many processes and `itemId.item` is a fresh, load-order
|
|
# dependent counter — so a method base would mangle to `_u1` in one module,
|
|
# `_u3` in another and clean at its owner, none of which link. `disamb` is
|
|
# assigned deterministically per (module, name) and is serialized, so every
|
|
# process that touches the symbol derives the identical C name.
|
|
result.add $s.disamb
|
|
# module suffix LAST (a strippable trailing token; see `mangleProcNameExt`)
|
|
result.add "__"
|
|
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
|
|
|
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
|
|
#Module::Type
|
|
var name = s.name.s & extra
|
|
if makeUnique:
|
|
name = makeUnique(m, s, name)
|
|
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
|
|
|
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
|
result = ""
|
|
var kindName = ($t.kind)[2..^1]
|
|
kindName[0] = toLower($kindName[0])[0]
|
|
case t.kind
|
|
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
|
|
result = encodeSym(m, t.sym)
|
|
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
|
|
result = encodeName(t.genericHead.sym.name.s)
|
|
result.add "I"
|
|
for i in 1..<t.len - 1:
|
|
result.add encodeType(m, t[i], staticLists)
|
|
result.add "E"
|
|
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
|
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
|
result =
|
|
case t.kind:
|
|
of tySequence: encodeName("seq")
|
|
else: encodeName(kindName)
|
|
result.add "I"
|
|
for s in kids(t):
|
|
if s.isNil: continue
|
|
result.add encodeType(m, s, staticLists)
|
|
result.add "E"
|
|
of tyStatic:
|
|
if t.n != nil:
|
|
staticLists.add "_s" & renderTree(t.n)
|
|
else:
|
|
raiseAssert "unreachable"
|
|
of tyRange:
|
|
var val = "range_"
|
|
if t.n.firstSon.typ.kind in {tyFloat..tyFloat128}:
|
|
val.addFloat t.n.firstSon.floatVal
|
|
val.add "_"
|
|
val.addFloat t.n.secondSon.floatVal
|
|
else:
|
|
val.add $t.n.firstSon.intVal & "_" & $t.n.secondSon.intVal
|
|
result = encodeName(val)
|
|
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
|
result = encodeName(kindName)
|
|
of tyAlias, tyInferred, tyOwned:
|
|
result = encodeType(m, t.elementType, staticLists)
|
|
else:
|
|
assert false, "encodeType " & $t.kind
|
|
|