mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-03 04:13:40 +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
2326 lines
95 KiB
Nim
2326 lines
95 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2017 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# included from cgen.nim
|
|
|
|
# ------------------------- Name Mangling --------------------------------
|
|
|
|
import sighashes, std/strscans
|
|
import ../dist/checksums/src/checksums/md5
|
|
import std/sequtils
|
|
|
|
type
|
|
TypeDescKind = enum
|
|
dkParam #skParam
|
|
dkRefParam #param passed by ref when {.byref.} is used. Cpp only. C goes straight to dkParam and is handled as a regular pointer
|
|
dkRefGenericParam #param passed by ref when {.byref.} is used that is also a generic. Cpp only. C goes straight to dkParam and is handled as a regular pointer
|
|
dkVar #skVar
|
|
dkField #skField
|
|
dkResult #skResult
|
|
dkConst #skConst
|
|
dkOther #skType, skTemp, skLet and skForVar so far
|
|
|
|
proc descKindFromSymKind(kind: TSymKind): TypeDescKind =
|
|
case kind
|
|
of skParam: dkParam
|
|
of skVar: dkVar
|
|
of skField: dkField
|
|
of skResult: dkResult
|
|
of skConst: dkConst
|
|
else: dkOther
|
|
|
|
proc isKeyword(w: PIdent): bool =
|
|
# Nim and C++ share some keywords
|
|
# it's more efficient to test the whole Nim keywords range
|
|
case w.id
|
|
of ccgKeywordsLow..ccgKeywordsHigh,
|
|
nimKeywordsLow..nimKeywordsHigh,
|
|
ord(wInline): return true
|
|
else: return false
|
|
|
|
proc mangleField(m: BModule; name: PIdent): string =
|
|
result = mangle(name.s)
|
|
# fields are tricky to get right and thanks to generic types producing
|
|
# duplicates we can end up mangling the same field multiple times. However
|
|
# if we do so, the 'cppDefines' table might be modified in the meantime
|
|
# meaning we produce inconsistent field names (see bug #5404).
|
|
# Hence we do not check for ``m.g.config.cppDefines.contains(result)`` here
|
|
# anymore:
|
|
if isKeyword(name):
|
|
result.add "_0"
|
|
|
|
proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
|
|
result = "_Z" # Common prefix in Itanium ABI
|
|
var params = ""
|
|
var staticLists = ""
|
|
if s.typ.paramsLen > 0: # we dont care about the return param
|
|
for _, pt in paramTypes(s.typ):
|
|
if pt.isNil: continue
|
|
params.add encodeType(m, pt, staticLists)
|
|
|
|
result.add encodeSym(m, s, makeUnique, staticLists)
|
|
result.add params
|
|
|
|
if result in m.g.mangledPrcs:
|
|
result = mangleProc(m, s, true)
|
|
else:
|
|
m.g.mangledPrcs.incl(result)
|
|
|
|
proc sharedInstanceCName(m: BModule; s: PSym): string =
|
|
## The module-free canonical C name for a content-keyed generic instance,
|
|
## or "" when the symbol must keep its module-suffixed name. With a shared
|
|
## name, every TU that instantiated the same generic with the same type
|
|
## arguments calls one extern definition (first claimant's TU embeds it,
|
|
## see `genProcLvl3`) instead of compiling its own static copy.
|
|
##
|
|
## The name is program-unique only if the 30-bit content hash does not
|
|
## collide for same-named instances of *different* instantiations across
|
|
## modules — the per-module probe in `setInstanceDisamb` cannot see that.
|
|
## Claimants therefore must present the same signature; on mismatch the
|
|
## later one keeps its module-suffixed name (no merge, still correct).
|
|
## Residual risk: same name and signature, different generic args, AND a
|
|
## 30-bit collision — vanishingly unlikely; a full-typeKey verification
|
|
## channel can close it later.
|
|
result = ""
|
|
if m.config.cmd == cmdNifC and s.kind in routineKinds and
|
|
(s.disamb and InstanceDisambBit) != 0'i32 and
|
|
s.typ != nil and s.typ.callConv != ccInline and not m.hcrOn and
|
|
{sfImportc, sfExportc, sfCodegenDecl} * s.flags == {}:
|
|
# The content-derived `disamb` is unique per process (collision-probed in
|
|
# `setInstanceDisamb`), so the mint-site-independent `_i<disamb>` name is
|
|
# safe to use directly; identical instances across modules collide on it
|
|
# exactly and the merge stage keeps one.
|
|
result = s.name.s.mangle & "_i" & $s.disamb
|
|
|
|
proc isSharedInstanceCName(m: BModule; s: PSym): bool =
|
|
m.config.cmd == cmdNifC and s.kind in routineKinds and
|
|
(s.disamb and InstanceDisambBit) != 0'i32 and
|
|
stripCnifMarks(s.loc.snippet) == s.name.s.mangle & "_i" & $s.disamb
|
|
|
|
proc fillBackendName(m: BModule; s: PSym) =
|
|
if s.loc.snippet == "":
|
|
var result: Rope
|
|
if s.kind in routineKinds and {optCDebug, optItaniumMangle} * m.g.config.globalOptions == {optCDebug, optItaniumMangle} and
|
|
m.g.config.symbolFiles == disabledSf:
|
|
# Under the per-module IC backend the bare-name uniqueness probe
|
|
# (`m.g.mangledPrcs`) only sees the routines of the CURRENT module, so the
|
|
# clean-vs-`makeUnique` decision is made independently per process: a
|
|
# method base mangles clean at its owner but loses the in-module race to
|
|
# its same-signature dispatcher elsewhere (clean `speak` defined twice ->
|
|
# "multiple definition"; demanders call `speak_u<n>` that nobody defines).
|
|
# Force the stable, disamb-based unique name so every process agrees.
|
|
result = mangleProc(m, s, makeUnique = m.config.cmd == cmdNifC).rope
|
|
else:
|
|
let shared = sharedInstanceCName(m, s)
|
|
if shared.len > 0:
|
|
result = shared.rope
|
|
else:
|
|
result = s.name.s.mangle.rope
|
|
result.add mangleProcNameExt(m.g.graph, s)
|
|
if m.hcrOn:
|
|
result.add '_'
|
|
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
|
|
backendEnsureMutable s
|
|
if m.config.cmd == cmdNifC:
|
|
# mark the name so the cnif artifact writer can turn every occurrence
|
|
# into a Symbol token; stripped from the actual C output in genModule
|
|
s.locImpl.snippet = markCName(result)
|
|
else:
|
|
s.locImpl.snippet = result
|
|
|
|
proc fillParamName(m: BModule; s: PSym) =
|
|
if s.loc.snippet == "":
|
|
var res = s.name.s.mangle
|
|
res.add mangleParamExt(s)
|
|
#res.add idOrSig(s, res, m.sigConflicts, m.config)
|
|
# Take into account if HCR is on because of the following scenario:
|
|
# if a module gets imported and it has some more importc symbols in it,
|
|
# some param names might receive the "_0" suffix to distinguish from what
|
|
# is newly available. That might lead to changes in the C code in nimcache
|
|
# that contain only a parameter name change, but that is enough to mandate
|
|
# recompilation of that source file and thus a new shared object will be
|
|
# relinked. That may lead to a module getting reloaded which wasn't intended
|
|
# and that may be fatal when parts of the current active callstack when
|
|
# performCodeReload() was called are from the module being reloaded
|
|
# unintentionally - example (3 modules which import one another):
|
|
# main => proxy => reloadable
|
|
# we call performCodeReload() in proxy to reload only changes in reloadable
|
|
# but there is a new import which introduces an importc symbol `socket`
|
|
# and a function called in main or proxy uses `socket` as a parameter name.
|
|
# That would lead to either needing to reload `proxy` or to overwrite the
|
|
# executable file for the main module, which is running (or both!) -> error.
|
|
backendEnsureMutable s
|
|
s.locImpl.snippet = res.rope
|
|
|
|
proc fillLocalName(p: BProc; s: PSym) =
|
|
assert s.kind in skLocalVars+{skTemp}
|
|
#assert sfGlobal notin s.flags
|
|
if s.loc.snippet == "":
|
|
var key = s.name.s.mangle
|
|
let counter = p.sigConflicts.getOrDefault(key)
|
|
var result = key.rope
|
|
if s.kind == skTemp:
|
|
# speed up conflict search for temps (these are quite common):
|
|
if counter != 0: result.add "_" & rope(counter+1)
|
|
elif s.kind != skResult:
|
|
result.add "_" & rope(counter+1)
|
|
p.sigConflicts.inc(key)
|
|
backendEnsureMutable s
|
|
s.locImpl.snippet = result
|
|
|
|
proc scopeMangledParam(p: BProc; param: PSym) =
|
|
## parameter generation only takes BModule, not a BProc, so we have to
|
|
## remember these parameter names are already in scope to be able to
|
|
## generate unique identifiers reliably (consider that ``var a = a`` is
|
|
## even an idiom in Nim).
|
|
var key = param.name.s.mangle
|
|
p.sigConflicts.inc(key)
|
|
|
|
const
|
|
irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
|
|
tyDistinct, tyRange, tyStatic, tyAlias, tySink,
|
|
tyInferred, tyOwned}
|
|
|
|
proc typeName(typ: PType; result: var Rope) =
|
|
let typ = typ.skipTypes(irrelevantForBackend)
|
|
result.add $typ.kind
|
|
if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
|
|
result.add "_"
|
|
result.add typ.sym.name.s.mangle
|
|
|
|
proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
|
|
var t = typ
|
|
while true:
|
|
if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
|
|
return t.sym.loc.snippet
|
|
|
|
if t.kind in irrelevantForBackend:
|
|
t = t.skipModifier
|
|
else:
|
|
break
|
|
let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ
|
|
if typ.loc.snippet == "":
|
|
backendEnsureMutable typ
|
|
typ.typeName(typ.locImpl.snippet)
|
|
typ.locImpl.snippet.add $sig
|
|
else:
|
|
when defined(debugSigHashes):
|
|
# check consistency:
|
|
var tn = newRopeAppender()
|
|
typ.typeName(tn)
|
|
assert($typ.loc.snippet == $(tn & $sig))
|
|
result = typ.loc.snippet
|
|
if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
|
|
|
|
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 mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind =
|
|
## Maps a Nim type to a C type
|
|
case typ.kind
|
|
of tyNone, tyTyped: result = ctVoid
|
|
of tyBool: result = ctBool
|
|
of tyChar: result = ctChar
|
|
of tyNil: result = ctPtr
|
|
of tySet: result = mapSetType(conf, typ)
|
|
of tyOpenArray, tyVarargs:
|
|
if isParam: result = ctArray
|
|
else: result = ctStruct
|
|
of tyArray, tyUncheckedArray: result = ctArray
|
|
of tyObject, tyTuple: result = ctStruct
|
|
of tyUserTypeClasses:
|
|
doAssert typ.isResolvedUserTypeClass
|
|
result = mapType(conf, typ.skipModifier, isParam)
|
|
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
|
|
tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
|
|
result = mapType(conf, skipModifier(typ), isParam)
|
|
of tyEnum:
|
|
if firstOrd(conf, typ) < 0:
|
|
result = ctInt32
|
|
else:
|
|
case int(getSize(conf, typ))
|
|
of 1: result = ctUInt8
|
|
of 2: result = ctUInt16
|
|
of 4: result = ctInt32
|
|
of 8: result = ctInt64
|
|
else: result = ctInt32
|
|
of tyRange: result = mapType(conf, typ.elementType, isParam)
|
|
of tyPtr, tyVar, tyLent, tyRef:
|
|
var base = skipTypes(typ.elementType, typedescInst)
|
|
case base.kind
|
|
of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctPtrToArray
|
|
of tySet:
|
|
if mapSetType(conf, base) == ctArray: result = ctPtrToArray
|
|
else: result = ctPtr
|
|
else: result = ctPtr
|
|
of tyPointer: result = ctPtr
|
|
of tySequence: result = ctNimSeq
|
|
of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
|
|
of tyString: result = ctNimStr
|
|
of tyCstring: result = ctCString
|
|
of tyInt..tyUInt64:
|
|
result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
|
|
of tyStatic:
|
|
if typ.n != nil: result = mapType(conf, typ.skipModifier, isParam)
|
|
else:
|
|
result = ctVoid
|
|
doAssert(false, "mapType: " & $typ.kind)
|
|
else:
|
|
result = ctVoid
|
|
doAssert(false, "mapType: " & $typ.kind)
|
|
|
|
|
|
proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
|
|
#if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
|
|
#else:
|
|
result = mapType(conf, typ, false)
|
|
|
|
proc isImportedType(t: PType): bool =
|
|
result = t.sym != nil and sfImportc in t.sym.flags
|
|
|
|
proc isImportedCppType(t: PType): bool =
|
|
let x = t.skipTypes(irrelevantForBackend)
|
|
result = (t.sym != nil and sfInfixCall in t.sym.flags) or
|
|
(x.sym != nil and sfInfixCall in x.sym.flags)
|
|
|
|
proc isOrHasImportedCppType(typ: PType): bool =
|
|
searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)
|
|
|
|
proc hasNoInit(t: PType): bool =
|
|
let t = skipTypes(t, {tyGenericInst})
|
|
result = t.sym != nil and sfNoInit in t.sym.flags
|
|
|
|
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope
|
|
|
|
proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
|
|
# Arrays and sets cannot be returned by a C procedure, because C is
|
|
# such a poor programming language.
|
|
# We exclude records with refs too. This enhances efficiency and
|
|
# is necessary for proper code generation of assignments.
|
|
var rettype = typ
|
|
var isAllowedCall = true
|
|
if isProc:
|
|
rettype = rettype.returnType
|
|
isAllowedCall = typ.callConv in {ccClosure, ccInline, ccNimCall}
|
|
if rettype == nil or (isAllowedCall and
|
|
getSize(conf, rettype) > conf.target.floatSize*3):
|
|
result = true
|
|
else:
|
|
case mapType(conf, rettype, false)
|
|
of ctArray:
|
|
result = not (skipTypes(rettype, typedescInst).kind in
|
|
{tyVar, tyLent, tyRef, tyPtr})
|
|
of ctStruct:
|
|
let t = skipTypes(rettype, typedescInst)
|
|
if rettype.isImportedCppType or t.isImportedCppType or
|
|
(typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}):
|
|
# prevents nrvo for cdecl procs; # bug #23401
|
|
result = false
|
|
else:
|
|
result = containsGarbageCollectedRef(t) or
|
|
(t.kind == tyObject and not isObjLackingTypeField(t)) or
|
|
(getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags))
|
|
|
|
else: result = false
|
|
|
|
proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
|
|
# returns nil if we need to declare this type
|
|
# since types are now unique via the ``getUniqueType`` mechanism, this slow
|
|
# linear search is not necessary anymore:
|
|
result = tab.getOrDefault(sig)
|
|
|
|
proc addAbiCheck(m: BModule; t: PType, name: Rope) =
|
|
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize) and
|
|
not (t.kind == tyObject and searchTypeFor(t, proc (t: PType): bool {.nimcall.} = t.kind == tyUncheckedArray)):
|
|
# `UncheckedArray`, not `ptr UncheckedArray` type field in object types is a flexible array.
|
|
# `sizeof` in C and Nim doesn't always return the same value for object types containing it.
|
|
# making `getSize` in Nim always returns the same value as `sizeof` in C from flexible arrays seems hard.
|
|
# See `SEQ_DECL_SIZE` in lib/nimbase.h
|
|
var msg = "backend & Nim disagree on size for: "
|
|
msg.addTypeHeader(m.config, t)
|
|
var msg2 = ""
|
|
msg2.addQuoted msg # not a hostspot so extra allocation doesn't matter
|
|
m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope])
|
|
# see `testCodegenABICheck` for example error message it generates
|
|
|
|
|
|
proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
|
|
backendEnsureMutable param.sym
|
|
fillLoc(param.sym.locImpl, locParam, param, "Result",
|
|
OnStack)
|
|
let t = param.sym.typ
|
|
if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype):
|
|
incl(param.sym.locImpl.flags, lfIndirect)
|
|
param.sym.locImpl.storage = OnUnknown
|
|
|
|
proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
|
|
if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
|
|
useHeader(m, t.sym)
|
|
result = t.sym.loc.snippet
|
|
else:
|
|
result = rope(literal)
|
|
|
|
proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
|
|
const
|
|
NumericalTypeToStr: array[tyInt..tyUInt64, string] = [
|
|
NimInt, NimInt8, NimInt16, NimInt32, NimInt64,
|
|
NimFloat, NimFloat32, NimFloat64, NimFloat128,
|
|
NimUint, NimUint8, NimUint16, NimUint32, NimUint64]
|
|
case typ.kind
|
|
of tyPointer:
|
|
result = typeNameOrLiteral(m, typ, CPointer)
|
|
of tyString:
|
|
case detectStrVersion(m)
|
|
of 2:
|
|
cgsym(m, "NimStrPayload")
|
|
cgsym(m, "NimStringV2")
|
|
result = typeNameOrLiteral(m, typ, "NimStringV2")
|
|
of 3:
|
|
cgsym(m, "LongString")
|
|
cgsym(m, "SmallString")
|
|
result = typeNameOrLiteral(m, typ, "SmallString")
|
|
else:
|
|
cgsym(m, "NimStringDesc")
|
|
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
|
|
of tyCstring: result = typeNameOrLiteral(m, typ, NimCstring)
|
|
of tyBool: result = typeNameOrLiteral(m, typ, NimBool)
|
|
of tyChar: result = typeNameOrLiteral(m, typ, NimChar)
|
|
of tyNil: result = typeNameOrLiteral(m, typ, CPointer)
|
|
of tyInt..tyUInt64:
|
|
result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind])
|
|
of tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
|
|
of tyDistinct:
|
|
result = getSimpleTypeDesc(m, typ.skipModifier)
|
|
if isImportedType(typ) and result != "":
|
|
useHeader(m, typ.sym)
|
|
result = typ.sym.loc.snippet
|
|
of tyStatic:
|
|
if typ.n != nil: result = getSimpleTypeDesc(m, skipModifier typ)
|
|
else:
|
|
result = ""
|
|
internalError(m.config, "tyStatic for getSimpleTypeDesc")
|
|
of tyGenericInst, tyAlias, tySink, tyOwned:
|
|
result = getSimpleTypeDesc(m, skipModifier typ)
|
|
else: result = ""
|
|
|
|
if result != "" and typ.isImportedType():
|
|
let sig = hashType(typ, m.config)
|
|
if cacheGetType(m.typeCache, sig) == "":
|
|
m.typeCache[sig] = result
|
|
|
|
proc pushType(m: BModule; typ: PType) =
|
|
when defined(icDbgRefc):
|
|
if typ.kind == tySequence and
|
|
typ.elementType.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyGenericParam:
|
|
echo "[icRefc] pushType seq-of-genericparam t=", typeToString(typ),
|
|
" itemId=", typ.itemId.module, ".", typ.itemId.item, " mod=", m.module.name.s
|
|
echo getStackTrace()
|
|
for i in 0..high(m.typeStack):
|
|
# pointer equality is good enough here:
|
|
if m.typeStack[i] == typ: return
|
|
m.typeStack.add(typ)
|
|
|
|
proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
|
|
if typ == nil: result = CVoid
|
|
else:
|
|
result = getSimpleTypeDesc(m, typ)
|
|
if result == "": result = cacheGetType(m.typeCache, sig)
|
|
|
|
proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
|
|
# XXX should be no-op in NIFC
|
|
if m.compileToCpp:
|
|
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
|
|
else:
|
|
m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
|
|
|
|
proc seqStar(m: BModule): string =
|
|
if optSeqDestructors in m.config.globalOptions: result = ""
|
|
else: result = "*"
|
|
|
|
proc getTypeForward(m: BModule; typ: PType; sig: SigHash): Rope =
|
|
result = cacheGetType(m.forwTypeCache, sig)
|
|
if result != "": return
|
|
result = getTypePre(m, typ, sig)
|
|
if result != "": return
|
|
let concrete = typ.skipTypes(abstractInst)
|
|
case concrete.kind
|
|
of tySequence, tyTuple, tyObject:
|
|
result = getTypeName(m, typ, sig)
|
|
m.forwTypeCache[sig] = result
|
|
if not isImportedType(concrete):
|
|
addForwardStructFormat(m, structOrUnion(typ), result)
|
|
else:
|
|
pushType(m, concrete)
|
|
doAssert m.forwTypeCache[sig] == result
|
|
else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
|
|
|
|
proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TypeDescKind): Rope =
|
|
## like getTypeDescAux but creates only a *weak* dependency. In other words
|
|
## we know we only need a pointer to it so we only generate a struct forward
|
|
## declaration:
|
|
let etB = t.skipTypes(abstractInst)
|
|
case etB.kind
|
|
of tyObject, tyTuple:
|
|
if isImportedCppType(etB) and t.kind == tyGenericInst:
|
|
result = getTypeDescAux(m, t, check, kind)
|
|
else:
|
|
result = getTypeForward(m, t, hashType(t, m.config))
|
|
pushType(m, t)
|
|
of tySequence:
|
|
let sig = hashType(t, m.config)
|
|
if optSeqDestructors in m.config.globalOptions:
|
|
if skipTypes(etB.elementType, typedescInst).kind == tyEmpty:
|
|
internalError(m.config, "cannot map the empty seq type to a C type")
|
|
|
|
result = cacheGetType(m.forwTypeCache, sig)
|
|
if result == "":
|
|
result = getTypeName(m, t, sig)
|
|
if not isImportedType(t):
|
|
m.forwTypeCache[sig] = result
|
|
addForwardStructFormat(m, rope"struct", result)
|
|
let payload = result & "_Content"
|
|
addForwardStructFormat(m, rope"struct", payload)
|
|
|
|
if cacheGetType(m.typeCache, sig) == "":
|
|
m.typeCache[sig] = result
|
|
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
|
|
m.s[cfsTypes].addSimpleStruct(m, name = result, baseType = ""):
|
|
m.s[cfsTypes].addField(name = "len", typ = NimInt)
|
|
m.s[cfsTypes].addField(name = "p", typ = ptrType(result & "_Content"))
|
|
pushType(m, t)
|
|
else:
|
|
result = getTypeForward(m, t, sig) & seqStar(m)
|
|
pushType(m, t)
|
|
else:
|
|
result = getTypeDescAux(m, t, check, kind)
|
|
|
|
proc getSeqPayloadType(m: BModule; t: PType): Rope =
|
|
var check = initIntSet()
|
|
result = getTypeDescWeak(m, t, check, dkParam) & "_Content"
|
|
#result = getTypeForward(m, t, hashType(t)) & "_Content"
|
|
|
|
proc seqPayloadElem(m: BModule; t: PType): Snippet =
|
|
## Returns the C type name for a seq's element as stored in the payload,
|
|
## suitable for sizeof()/alignof(). Must use dkVar, not the dkParam default,
|
|
## because reified openArrays (experimental views) differ: dkParam gives a
|
|
## bare pointer (T*) while dkVar gives the two-word struct actually stored.
|
|
var check = initIntSet()
|
|
result = getTypeDescAux(m, t.elementType, check, dkVar)
|
|
|
|
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
|
|
let sig = hashType(t, m.config)
|
|
let result = cacheGetType(m.typeCache, sig)
|
|
if result == "":
|
|
discard getTypeDescAux(m, t, check, dkVar)
|
|
else:
|
|
let dataTyp = getTypeDescAux(m, t.skipTypes(abstractInst).elementType, check, dkVar)
|
|
m.s[cfsTypes].addSimpleStruct(m, name = result & "_Content", baseType = ""):
|
|
m.s[cfsTypes].addField(name = "cap", typ = NimInt)
|
|
m.s[cfsTypes].addField(name = "data",
|
|
typ = dataTyp,
|
|
isFlexArray = true)
|
|
|
|
proc paramStorageLoc(param: PSym): TStorageLoc =
|
|
if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
|
|
tyArray, tyOpenArray, tyVarargs}:
|
|
result = OnStack
|
|
else:
|
|
result = OnUnknown
|
|
|
|
macro unrollChars(x: static openArray[char], name, body: untyped) =
|
|
result = newStmtList()
|
|
for a in x:
|
|
result.add(newBlockStmt(newStmtList(
|
|
newConstStmt(name, newLit(a)),
|
|
copy body
|
|
)))
|
|
|
|
proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArray[seq[string]]) =
|
|
var res: string
|
|
unrollChars(chars, c):
|
|
res = ""
|
|
let arg = args[find(chars, c)]
|
|
var i = 0
|
|
var num = 0
|
|
while i < frmt.len:
|
|
if frmt[i] == c:
|
|
inc(i)
|
|
case frmt[i]
|
|
of c:
|
|
res.add(c)
|
|
inc(i)
|
|
of '0'..'9':
|
|
var j = 0
|
|
while true:
|
|
j = j * 10 + ord(frmt[i]) - ord('0')
|
|
inc(i)
|
|
if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
|
|
num = j
|
|
if j > high(arg) + 1:
|
|
raiseAssert "invalid format string: " & frmt
|
|
else:
|
|
res.add(arg[j-1])
|
|
else:
|
|
raiseAssert "invalid format string: " & frmt
|
|
var start = i
|
|
while i < frmt.len:
|
|
if frmt[i] != c: inc(i)
|
|
else: break
|
|
if i - 1 >= start:
|
|
res.add(substr(frmt, start, i - 1))
|
|
frmt = res
|
|
|
|
proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string,
|
|
check: var IntSet, declareEnvironment=true;
|
|
weakDep=false;) =
|
|
let t = prc.typ
|
|
let isCtor = sfConstructor in prc.flags
|
|
if isCtor or (name[0] == '~' and sfMember in prc.flags):
|
|
# destructors can't have void
|
|
rettype = ""
|
|
elif t.returnType == nil or isInvalidReturnType(m.config, t):
|
|
rettype = CVoid
|
|
else:
|
|
if rettype == "":
|
|
rettype = getTypeDescAux(m, t.returnType, check, dkResult)
|
|
else:
|
|
rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t.returnType, check, dkResult)])
|
|
var types, names, args: seq[string] = @[]
|
|
if not isCtor:
|
|
var this = t.n.secondSon.sym
|
|
backendEnsureMutable this
|
|
fillParamName(m, this)
|
|
fillLoc(this.locImpl, locParam, t.n.secondSon,
|
|
this.paramStorageLoc)
|
|
if this.typ.kind == tyPtr:
|
|
this.locImpl.snippet = "this"
|
|
else:
|
|
this.locImpl.snippet = "(*this)"
|
|
names.add this.locImpl.snippet
|
|
types.add getTypeDescWeak(m, this.typ, check, dkParam)
|
|
|
|
let firstParam = if isCtor: 1 else: 2
|
|
for it in sonsFrom(t.n, firstParam):
|
|
if it.kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
|
|
var param = it.sym
|
|
var descKind = dkParam
|
|
if optByRef in param.options:
|
|
if param.typ.kind == tyGenericInst:
|
|
descKind = dkRefGenericParam
|
|
else:
|
|
descKind = dkRefParam
|
|
var typ, name: string
|
|
backendEnsureMutable param
|
|
fillParamName(m, param)
|
|
fillLoc(param.locImpl, locParam, it,
|
|
param.paramStorageLoc)
|
|
if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
|
|
typ = getTypeDescWeak(m, param.typ, check, descKind) & "*"
|
|
incl(param.locImpl.flags, lfIndirect)
|
|
param.locImpl.storage = OnUnknown
|
|
elif weakDep:
|
|
typ = getTypeDescWeak(m, param.typ, check, descKind)
|
|
else:
|
|
typ = getTypeDescAux(m, param.typ, check, descKind)
|
|
if sfNoalias in param.flags:
|
|
typ.add("NIM_NOALIAS ")
|
|
|
|
name = param.locImpl.snippet
|
|
types.add typ
|
|
names.add name
|
|
if sfCodegenDecl notin param.flags:
|
|
args.add types[^1] & " " & names[^1]
|
|
else:
|
|
args.add runtimeFormat(param.cgDeclFrmt, [types[^1], names[^1]])
|
|
|
|
multiFormat(params, @['\'', '#'], [types, names])
|
|
multiFormat(superCall, @['\'', '#'], [types, names])
|
|
multiFormat(name, @['\'', '#'], [types, names]) #so we can ~'1 on members
|
|
if params == "()":
|
|
if types.len == 0:
|
|
params = "(void)"
|
|
else:
|
|
params = "(" & args.join(", ") & ")"
|
|
if tfVarargs in t.flags:
|
|
if params != "(":
|
|
params[^1] = ','
|
|
else:
|
|
params.delete(params.len()-1..params.len()-1)
|
|
params.add("...)")
|
|
|
|
proc genProcParams(m: BModule; t: PType, rettype: var Rope, params: var Builder,
|
|
check: var IntSet, declareEnvironment=true;
|
|
weakDep=false;) =
|
|
if t.returnType == nil or isInvalidReturnType(m.config, t):
|
|
rettype = CVoid
|
|
else:
|
|
rettype = getTypeDescWeak(m, t.returnType, check, dkResult)
|
|
var paramBuilder: ProcParamBuilder
|
|
params.addProcParams(paramBuilder):
|
|
for child in sonsFrom(t.n, 1):
|
|
if child.kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
|
|
var param = child.sym
|
|
# The hidden closure environment param (`:envP`) is not a real C parameter:
|
|
# the environment is passed via the trailing `ClE_0` (added below) and
|
|
# `closureSetup` materialises `:envP` as a local cast of it. In a from-source
|
|
# build `:envP` only lives in the routine's AST params, never in the proc
|
|
# *type's* `n`, so it never reaches here. Under IC `closureParams` re-shares
|
|
# the AST param node with `typ.n`, so the lifted `:envP` leaks into `t.n`;
|
|
# emitting it would produce a bogus extra parameter that collides with the
|
|
# `closureSetup` local (the "redeclared as different kind of symbol" / env
|
|
# pointer-type mismatch). We still must fill its name/loc (later passes such
|
|
# as `assignParam` and `closureSetup` reference it), but it is omitted from
|
|
# the C signature to match the from-source ABI.
|
|
let isClosureEnv = t.callConv == ccClosure and param.name.s == ":envP"
|
|
var descKind = dkParam
|
|
if m.config.backend == backendCpp and optByRef in param.options:
|
|
if param.typ.kind == tyGenericInst:
|
|
descKind = dkRefGenericParam
|
|
else:
|
|
descKind = dkRefParam
|
|
if isCompileTimeOnly(param.typ): continue
|
|
backendEnsureMutable param
|
|
fillParamName(m, param)
|
|
fillLoc(param.locImpl, locParam, child,
|
|
param.paramStorageLoc)
|
|
if isClosureEnv: continue # name/loc filled, but not part of the C signature
|
|
var typ: Rope
|
|
if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
|
|
typ = ptrType(getTypeDescWeak(m, param.typ, check, descKind))
|
|
incl(param.locImpl.flags, lfIndirect)
|
|
param.locImpl.storage = OnUnknown
|
|
elif weakDep:
|
|
typ = (getTypeDescWeak(m, param.typ, check, descKind))
|
|
else:
|
|
typ = (getTypeDescAux(m, param.typ, check, descKind))
|
|
params.addParam(paramBuilder, param, typ = typ)
|
|
# declare the len field for open arrays:
|
|
var arr = param.typ.skipTypes({tyGenericInst})
|
|
if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
|
|
var j = 0
|
|
while arr.kind in {tyOpenArray, tyVarargs}:
|
|
# this fixes the 'sort' bug:
|
|
if param.typ.kind in {tyVar, tyLent}: param.locImpl.storage = OnUnknown
|
|
# need to pass hidden parameter:
|
|
params.addParam(paramBuilder, name = param.locImpl.snippet & "Len_" & $j, typ = NimInt)
|
|
inc(j)
|
|
arr = arr.elementType.skipTypes({tySink})
|
|
if t.returnType != nil and isInvalidReturnType(m.config, t):
|
|
var arr = t.returnType
|
|
var typ: Snippet
|
|
if mapReturnType(m.config, arr) != ctArray:
|
|
if isHeaderFile in m.flags:
|
|
# still generates types for `--header`
|
|
typ = ptrType(getTypeDescAux(m, arr, check, dkResult))
|
|
else:
|
|
typ = ptrType(getTypeDescWeak(m, arr, check, dkResult))
|
|
else:
|
|
typ = getTypeDescAux(m, arr, check, dkResult)
|
|
params.addParam(paramBuilder, name = "Result", typ = typ)
|
|
if t.callConv == ccClosure and declareEnvironment:
|
|
params.addParam(paramBuilder, name = "ClE_0", typ = CPointer)
|
|
if tfVarargs in t.flags:
|
|
params.addVarargsParam(paramBuilder)
|
|
|
|
proc mangleRecFieldName(m: BModule; field: PSym): Rope =
|
|
if {sfImportc, sfExportc} * field.flags != {}:
|
|
result = field.loc.snippet
|
|
else:
|
|
result = rope(mangleField(m, field.name))
|
|
if result == "": internalError(m.config, field.info, "mangleRecFieldName")
|
|
|
|
proc hasCppCtor(m: BModule; typ: PType): bool =
|
|
result = false
|
|
if m.compileToCpp and typ != nil and typ.bindingId in m.g.graph.memberProcsPerType:
|
|
for prc in m.g.graph.memberProcsPerType[typ.bindingId]:
|
|
if sfConstructor in prc.flags:
|
|
return true
|
|
|
|
proc genCppParamsForCtor(p: BProc; call: PNode; 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
|
|
result = "{}"
|
|
if typ.bindingId in m.g.graph.initializersPerType:
|
|
let call = m.g.graph.initializersPerType[typ.bindingId]
|
|
if call != nil:
|
|
var p = prc
|
|
if p == nil:
|
|
p = BProc(module: m)
|
|
result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}"
|
|
if prc == nil:
|
|
assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
|
|
|
|
proc genRecordFieldsAux(m: BModule; n: PNode,
|
|
rectype: PType,
|
|
check: var IntSet; result: var Builder; unionPrefix = "") =
|
|
case n.kind
|
|
of nkRecList:
|
|
for ni in n.sons:
|
|
genRecordFieldsAux(m, ni, rectype, check, result, unionPrefix)
|
|
of nkRecCase:
|
|
if n.firstSon.kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
|
|
genRecordFieldsAux(m, n.firstSon, rectype, check, result, unionPrefix)
|
|
# prefix mangled name with "_U" to avoid clashes with other field names,
|
|
# since identifiers are not allowed to start with '_'
|
|
var unionBody = newBuilder("")
|
|
for i, it in isons(n, 1):
|
|
case it.kind
|
|
of nkOfBranch, nkElse:
|
|
let k = lastSon(it)
|
|
if k.kind != nkSym:
|
|
let structName = "_" & mangleRecFieldName(m, n.firstSon.sym) & "_" & $i
|
|
var a = newBuilder("")
|
|
genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
|
|
if a.buf.len != 0:
|
|
unionBody.addFieldWithStructType(m, rectype, structName):
|
|
unionBody.add(extract(a))
|
|
else:
|
|
genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
|
|
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
|
if unionBody.buf.len != 0:
|
|
result.addAnonUnion:
|
|
# XXX this has to be a named field for NIFC
|
|
result.add(extract(unionBody))
|
|
of nkSym:
|
|
let field = n.sym
|
|
if field.typ.kind == tyVoid: return
|
|
#assert(field.ast == nil)
|
|
let sname = mangleRecFieldName(m, field)
|
|
backendEnsureMutable field
|
|
fillLoc(field.locImpl, locField, n, unionPrefix & sname, OnUnknown)
|
|
# for importcpp'ed objects, we only need to set field.loc, but don't
|
|
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
|
# with heavily templatized C++ code:
|
|
if not isImportedCppType(rectype):
|
|
let fieldType = field.loc.t.skipTypes(abstractInst)
|
|
var typ: Rope = ""
|
|
var isFlexArray = false
|
|
var initializer = ""
|
|
if fieldType.kind == tyUncheckedArray:
|
|
typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
|
|
isFlexArray = true
|
|
elif fieldType.kind == tySequence:
|
|
# we need to use a weak dependency here for trecursive_table.
|
|
typ = getTypeDescWeak(m, field.loc.t, check, dkField)
|
|
else:
|
|
typ = getTypeDescAux(m, field.loc.t, check, dkField)
|
|
# don't use fieldType here because we need the
|
|
# tyGenericInst for C++ template support
|
|
let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
|
|
# Under `nim ic`, object fields are local NIF syms restored without an
|
|
# `owner`; `rectype` is the owning record type, so fall back to it rather
|
|
# than deref a nil `field.owner`.
|
|
let ownerTyp = if field.owner != nil: field.owner.typ else: rectype
|
|
if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, ownerTyp)):
|
|
var didGenTemp = false
|
|
initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
|
|
result.addField(field, sname, typ, isFlexArray, initializer)
|
|
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
|
|
|
proc genMemberProcHeader(m: BModule; prc: PSym; result: var Builder; asPtr: bool = false, isFwdDecl:bool = false)
|
|
|
|
proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
|
|
genRecordFieldsAux(m, typ.n, typ, check, result)
|
|
if typ.bindingId in m.g.graph.memberProcsPerType:
|
|
let procs = m.g.graph.memberProcsPerType[typ.bindingId]
|
|
var isDefaultCtorGen, isCtorGen: bool = false
|
|
for prc in procs:
|
|
if sfConstructor in prc.flags:
|
|
isCtorGen = true
|
|
if prc.typ.n.len == 1:
|
|
isDefaultCtorGen = true
|
|
if lfNoDecl in prc.loc.flags: continue
|
|
var header = newBuilder("")
|
|
genMemberProcHeader(m, prc, header, false, true)
|
|
result.addStmt():
|
|
result.add(extract(header))
|
|
if isCtorGen and not isDefaultCtorGen:
|
|
var ch: IntSet = default(IntSet)
|
|
result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
|
|
|
|
proc fillObjectFields*(m: BModule; typ: PType) =
|
|
# sometimes generic objects are not consistently merged. We patch over
|
|
# this fact here.
|
|
var check = initIntSet()
|
|
var ignored = newBuilder("")
|
|
addRecordFields(ignored, m, typ, check)
|
|
if typ.baseClass != nil:
|
|
fillObjectFields(m, typ.baseClass.skipTypes(skipPtrs))
|
|
|
|
proc mangleDynLibProc(sym: PSym): Rope
|
|
|
|
proc getRecordDesc(m: BModule; typ: PType, name: Rope,
|
|
check: var IntSet): Rope =
|
|
# declare the record:
|
|
var baseType: string = ""
|
|
if typ.baseClass != nil:
|
|
baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
|
|
if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
|
|
var res = newBuilder("")
|
|
res.addStruct(m, typ, name, baseType):
|
|
res.addRecordFields(m, typ, check)
|
|
result = extract(res)
|
|
else:
|
|
var desc = newBuilder("")
|
|
desc.addRecordFields(m, typ, check)
|
|
result = runtimeFormat(typ.sym.cgDeclFrmt, [name, extract(desc), baseType])
|
|
|
|
proc getTupleDesc(m: BModule; typ: PType, name: Rope,
|
|
check: var IntSet): Rope =
|
|
var res = newBuilder("")
|
|
res.addStruct(m, typ, name, ""):
|
|
for i, a in typ.ikids:
|
|
# Do not produce code for void types
|
|
if isEmptyType(a): continue
|
|
res.addField(
|
|
name = "Field" & $i,
|
|
typ = getTypeDescAux(m, a, check, dkField))
|
|
result = extract(res)
|
|
|
|
proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
|
|
# A helper proc for handling cppimport patterns, involving numeric
|
|
# placeholders for generic types (e.g. '0, '**2, etc).
|
|
# pre: the cursor must be placed at the ' symbol
|
|
# post: the cursor will be placed after the final digit
|
|
# false will returned if the input is not recognized as a placeholder
|
|
inc cursor
|
|
let begin = cursor
|
|
while pat[cursor] == '*': inc cursor
|
|
if pat[cursor] in Digits:
|
|
outIdx = pat[cursor].ord - '0'.ord
|
|
outStars = cursor - begin
|
|
inc cursor
|
|
return true
|
|
else:
|
|
return false
|
|
|
|
proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
|
|
# Make sure the index refers to one of the generic params of the type.
|
|
# XXX: we should catch this earlier and report it as a semantic error.
|
|
if idx >= typ.kidsLen:
|
|
raiseAssert "invalid apostrophe type parameter index"
|
|
|
|
result = typ[idx]
|
|
for i in 1..stars:
|
|
if result != nil and result.kidsLen > 0:
|
|
result = if result.kind == tyGenericInst: result.firstGenericParam
|
|
else: result.elemType
|
|
|
|
proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKind): Rope =
|
|
let sig = hashType(t, m.config)
|
|
if kind == dkParam:
|
|
result = getTypeDescWeak(m, t.elementType, check, kind) & "*"
|
|
else:
|
|
result = cacheGetType(m.typeCache, sig)
|
|
if result == "":
|
|
result = getTypeName(m, t, sig)
|
|
m.typeCache[sig] = result
|
|
let elemType = getTypeDescWeak(m, t.elementType, check, kind)
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].addSimpleStruct(m, name = "", baseType = ""):
|
|
m.s[cfsTypes].addField(name = "Field0", typ = ptrType(elemType))
|
|
m.s[cfsTypes].addField(name = "Field1", typ = NimInt)
|
|
|
|
proc importedCppObject(m: BModule; t, tt: PType; check: var IntSet; kind: TypeDescKind; sig: SigHash; result: var Rope) =
|
|
let cppNameAsRope = getTypeName(m, t, sig)
|
|
let cppName = $cppNameAsRope
|
|
var i = 0
|
|
var chunkStart = 0
|
|
|
|
template addResultType(ty: untyped) =
|
|
if ty == nil or ty.kind == tyVoid:
|
|
result.add(CVoid)
|
|
elif ty.kind == tyStatic:
|
|
internalAssert m.config, ty.n != nil
|
|
result.add ty.n.renderTree
|
|
else:
|
|
result.add getTypeDescAux(m, ty, check, kind)
|
|
|
|
while i < cppName.len:
|
|
if cppName[i] == '\'':
|
|
var chunkEnd = i-1
|
|
var idx, stars: int = 0
|
|
if scanCppGenericSlot(cppName, i, idx, stars):
|
|
result.add cppName.substr(chunkStart, chunkEnd)
|
|
chunkStart = i
|
|
|
|
let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars)
|
|
addResultType(typeInSlot)
|
|
else:
|
|
inc i
|
|
|
|
if chunkStart != 0:
|
|
result.add cppName.substr(chunkStart)
|
|
else:
|
|
result = cppNameAsRope & "<"
|
|
for needsComma, a in tt.genericInstParams:
|
|
if needsComma: result.add(" COMMA ")
|
|
addResultType(a)
|
|
result.add("> ")
|
|
# always call for sideeffects:
|
|
assert t.kind != tyTuple
|
|
discard getRecordDesc(m, t, result, check)
|
|
# The resulting type will include commas and these won't play well
|
|
# with the C macros for defining procs such as N_NIMCALL. We must
|
|
# create a typedef for the type and use it in the proc signature:
|
|
let typedefName = "TY" & $sig
|
|
m.s[cfsTypes].addTypedef(name = typedefName):
|
|
m.s[cfsTypes].add(result)
|
|
m.typeCache[sig] = typedefName
|
|
result = typedefName
|
|
|
|
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
|
|
# returns only the type's name
|
|
var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
|
|
if containsOrIncl(check, t.id):
|
|
if not (isImportedCppType(origTyp) or isImportedCppType(t)):
|
|
internalError(m.config, "cannot generate C type for: " & typeToString(origTyp))
|
|
# XXX: this BUG is hard to fix -> we need to introduce helper structs,
|
|
# but determining when this needs to be done is hard. We should split
|
|
# C type generation into an analysis and a code generation phase somehow.
|
|
if t.sym != nil: useHeader(m, t.sym)
|
|
if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
|
|
let sig = hashType(origTyp, m.config)
|
|
|
|
# tyDistinct matters if it is an importc type
|
|
result = getTypePre(m, origTyp.skipTypes(irrelevantForBackend-{tyOwned, tyDistinct}), sig)
|
|
defer:
|
|
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
|
|
addAbiCheck(m, t, result)
|
|
|
|
if result != "" and t.kind != tyOpenArray:
|
|
excl(check, t.id)
|
|
if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst:
|
|
result.add("&")
|
|
return
|
|
case t.kind
|
|
of tyRef, tyPtr, tyVar, tyLent:
|
|
var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and
|
|
compileToCpp(m): "&" else: "*"
|
|
var et = origTyp.skipTypes(abstractInst).elementType
|
|
var etB = et.skipTypes(abstractInst)
|
|
if mapType(m.config, t, kind == dkParam) == ctPtrToArray and (etB.kind != tyOpenArray or kind == dkParam):
|
|
if etB.kind == tySet:
|
|
et = getSysType(m.g.graph, unknownLineInfo, tyUInt8)
|
|
else:
|
|
et = elemType(etB)
|
|
etB = et.skipTypes(abstractInst)
|
|
star[0] = '*'
|
|
case etB.kind
|
|
of tyObject, tyTuple:
|
|
if isImportedCppType(etB) and et.kind == tyGenericInst:
|
|
result = getTypeDescAux(m, et, check, kind) & star
|
|
else:
|
|
# no restriction! We have a forward declaration for structs
|
|
let name = getTypeForward(m, et, hashType(et, m.config))
|
|
result = name & star
|
|
m.typeCache[sig] = result
|
|
of tySequence:
|
|
if optSeqDestructors in m.config.globalOptions:
|
|
result = getTypeDescWeak(m, et, check, kind) & star
|
|
m.typeCache[sig] = result
|
|
else:
|
|
# no restriction! We have a forward declaration for structs
|
|
let name = getTypeForward(m, et, hashType(et, m.config))
|
|
result = name & seqStar(m) & star
|
|
m.typeCache[sig] = result
|
|
pushType(m, et)
|
|
else:
|
|
# else we have a strong dependency :-(
|
|
result = getTypeDescAux(m, et, check, kind) & star
|
|
m.typeCache[sig] = result
|
|
of tyOpenArray, tyVarargs:
|
|
result = getOpenArrayDesc(m, t, check, kind)
|
|
of tyEnum:
|
|
result = cacheGetType(m.typeCache, sig)
|
|
if result == "":
|
|
result = getTypeName(m, origTyp, sig)
|
|
if not (isImportedCppType(t) or
|
|
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
|
|
m.typeCache[sig] = result
|
|
var size: int
|
|
if firstOrd(m.config, t) < 0:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].add(NimInt32)
|
|
size = 4
|
|
else:
|
|
size = int(getSize(m.config, t))
|
|
case size
|
|
of 1:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].add(NimUint8)
|
|
of 2:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].add(NimUint16)
|
|
of 4:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].add(NimInt32)
|
|
of 8:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].add(NimInt64)
|
|
else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
|
|
when false:
|
|
let owner = hashOwner(t.sym)
|
|
if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
|
|
var vals: seq[(string, int)] = @[]
|
|
for son in t.n.sons:
|
|
assert(son.kind == nkSym)
|
|
let field = son.sym
|
|
vals.add((field.name.s, field.position.int))
|
|
gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id,
|
|
name: t.sym.name.s, values: vals))
|
|
of tyProc:
|
|
result = getTypeName(m, origTyp, sig)
|
|
m.typeCache[sig] = result
|
|
var rettype: Snippet = ""
|
|
var desc = newBuilder("")
|
|
genProcParams(m, t, rettype, desc, check, true, true)
|
|
let params = extract(desc)
|
|
if not isImportedType(t):
|
|
if t.callConv != ccClosure: # procedure vars may need a closure!
|
|
m.s[cfsTypes].addProcTypedef(callConv = t.callConv, name = result, rettype = rettype, params = params)
|
|
else:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].addSimpleStruct(m, name = "", baseType = ""):
|
|
m.s[cfsTypes].addProcField(name = "ClP_0", callConv = ccNimCall, rettype = rettype, params = params)
|
|
m.s[cfsTypes].addField(name = "ClE_0", typ = CPointer)
|
|
of tySequence:
|
|
if optSeqDestructors in m.config.globalOptions:
|
|
result = getTypeDescWeak(m, t, check, kind)
|
|
else:
|
|
# we cannot use getTypeForward here because then t would be associated
|
|
# with the name of the struct, not with the pointer to the struct:
|
|
result = cacheGetType(m.forwTypeCache, sig)
|
|
if result == "":
|
|
result = getTypeName(m, origTyp, sig)
|
|
if not isImportedType(t):
|
|
addForwardStructFormat(m, structOrUnion(t), result)
|
|
m.forwTypeCache[sig] = result
|
|
assert(cacheGetType(m.typeCache, sig) == "")
|
|
m.typeCache[sig] = result & seqStar(m)
|
|
if not isImportedType(t):
|
|
if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
|
|
let et = getTypeDescAux(m, t.elementType, check, kind)
|
|
let baseType = cgsymValue(m, "TGenericSeq")
|
|
m.s[cfsSeqTypes].addSimpleStruct(m, name = result, baseType = baseType):
|
|
m.s[cfsSeqTypes].addField(
|
|
name = "data",
|
|
typ = et,
|
|
isFlexArray = true)
|
|
else:
|
|
result = rope("TGenericSeq")
|
|
result.add(seqStar(m))
|
|
of tyUncheckedArray:
|
|
result = getTypeName(m, origTyp, sig)
|
|
m.typeCache[sig] = result
|
|
if not isImportedType(t):
|
|
let et = getTypeDescAux(m, t.elementType, check, kind)
|
|
m.s[cfsTypes].addArrayTypedef(name = result, len = 1):
|
|
m.s[cfsTypes].add(et)
|
|
of tyArray:
|
|
var n = toInt64(lengthOrd(m.config, t))
|
|
if n <= 0: n = 1 # make an array of at least one element
|
|
result = getTypeName(m, origTyp, sig)
|
|
m.typeCache[sig] = result
|
|
if not isImportedType(t):
|
|
let e = getTypeDescAux(m, t.elementType, check, kind)
|
|
m.s[cfsTypes].addArrayTypedef(name = result, len = n):
|
|
m.s[cfsTypes].add(e)
|
|
of tyObject, tyTuple:
|
|
let tt = origTyp.skipTypes({tyDistinct})
|
|
if isImportedCppType(t) and tt.kind == tyGenericInst:
|
|
importedCppObject(m, t, tt, check, kind, sig, result)
|
|
else:
|
|
result = cacheGetType(m.forwTypeCache, sig)
|
|
if result == "":
|
|
result = getTypeName(m, origTyp, sig)
|
|
m.forwTypeCache[sig] = result
|
|
if not isImportedType(t):
|
|
addForwardStructFormat(m, structOrUnion(t), result)
|
|
assert m.forwTypeCache[sig] == result
|
|
m.typeCache[sig] = result # always call for sideeffects:
|
|
if not incompleteType(t):
|
|
let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
|
|
else: getTupleDesc(m, t, result, check)
|
|
if not isImportedType(t):
|
|
m.s[cfsTypes].add(recdesc)
|
|
addAbiCheck(m, t, result)
|
|
elif tfIncompleteStruct notin t.flags:
|
|
discard # addAbiCheck(m, t, result) # already handled elsewhere
|
|
of tySet:
|
|
# Don't use the imported name as it may be scoped: 'Foo::SomeKind'
|
|
result = rope("tySet_")
|
|
t.elementType.typeName(result)
|
|
result.add $t.elementType.hashType(m.config)
|
|
m.typeCache[sig] = result
|
|
if not isImportedType(t):
|
|
let s = int(getSize(m.config, t))
|
|
case s
|
|
of 1, 2, 4, 8:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].add(cUintType(s*8))
|
|
else:
|
|
m.s[cfsTypes].addArrayTypedef(name = result, len = s):
|
|
m.s[cfsTypes].add(NimUint8)
|
|
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
|
|
tyUserTypeClass, tyUserTypeClassInst, tyInferred:
|
|
result = getTypeDescAux(m, skipModifier(t), check, kind)
|
|
else:
|
|
when defined(icDbgRefc):
|
|
echo "[icRefc] getTypeDescAux ", t.kind, " t=", typeToString(t),
|
|
" origTyp=", typeToString(origTyp), " t.itemId=", t.itemId.module, ".", t.itemId.item,
|
|
" sym=", (if t.sym != nil: t.sym.name.s else: "nil"),
|
|
" owner=", (if t.owner != nil: t.owner.name.s else: "nil")
|
|
internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
|
|
result = ""
|
|
# fixes bug #145:
|
|
excl(check, t.id)
|
|
|
|
|
|
proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope =
|
|
var check = initIntSet()
|
|
result = getTypeDescAux(m, typ, check, kind)
|
|
|
|
type
|
|
TClosureTypeKind = enum ## In C closures are mapped to 3 different things.
|
|
clHalf, ## fn(args) type without the trailing 'void* env' parameter
|
|
clHalfWithEnv, ## fn(args, void* env) type with trailing 'void* env' parameter
|
|
clFull ## struct {fn(args, void* env), env}
|
|
|
|
proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope =
|
|
assert t.kind == tyProc
|
|
var check = initIntSet()
|
|
result = getTempName(m)
|
|
var rettype: Snippet = ""
|
|
var desc = newBuilder("")
|
|
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
|
|
let params = extract(desc)
|
|
if not isImportedType(t):
|
|
if t.callConv != ccClosure or kind != clFull:
|
|
m.s[cfsTypes].addProcTypedef(callConv = t.callConv, name = result, rettype = rettype, params = params)
|
|
else:
|
|
m.s[cfsTypes].addTypedef(name = result):
|
|
m.s[cfsTypes].addSimpleStruct(m, name = "", baseType = ""):
|
|
m.s[cfsTypes].addProcField(name = "ClP_0", callConv = ccNimCall, rettype = rettype, params = params)
|
|
m.s[cfsTypes].addField(name = "ClE_0", typ = CPointer)
|
|
|
|
proc finishTypeDescriptions(m: BModule) =
|
|
var i = 0
|
|
var check = initIntSet()
|
|
while i < m.typeStack.len:
|
|
let t = m.typeStack[i]
|
|
when defined(icDbgRefc):
|
|
echo "[icRefc] finishTypeDescriptions[", i, "] mod=", m.module.name.s,
|
|
" t=", typeToString(t), " kind=", t.kind,
|
|
" itemId=", t.itemId.module, ".", t.itemId.item
|
|
if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
|
|
seqV2ContentType(m, t, check)
|
|
else:
|
|
discard getTypeDescAux(m, t, check, dkParam)
|
|
inc(i)
|
|
m.typeStack.setLen 0
|
|
|
|
proc isReloadable(m: BModule; prc: PSym): bool =
|
|
return m.hcrOn and sfNonReloadable notin prc.flags
|
|
|
|
proc isNonReloadable(m: BModule; prc: PSym): bool =
|
|
return m.hcrOn and sfNonReloadable in prc.flags
|
|
|
|
proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual, isStatic: var bool; isCtor: bool, isFunctor=false) =
|
|
var afterParams: string = ""
|
|
if scanf(val, "$*($*)$s$*", name, params, afterParams):
|
|
if name.strip() == "operator" and params == "": #isFunctor?
|
|
parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor, true)
|
|
return
|
|
if name.find("static ") > -1:
|
|
isStatic = true
|
|
name = name.replace("static ", "")
|
|
isFnConst = afterParams.find("const") > -1
|
|
isOverride = afterParams.find("override") > -1
|
|
isMemberVirtual = name.find("virtual ") > -1
|
|
if isMemberVirtual:
|
|
name = name.replace("virtual ", "")
|
|
if isFunctor:
|
|
name = "operator ()"
|
|
if isCtor:
|
|
discard scanf(afterParams, ":$s$*", superCall)
|
|
else:
|
|
discard scanf(afterParams, "->$s$* ", retType)
|
|
|
|
params = "(" & params & ")"
|
|
|
|
proc genMemberProcHeader(m: BModule; prc: PSym; result: var Builder; asPtr: bool = false, isFwdDecl: bool = false) =
|
|
assert sfCppMember * prc.flags != {}
|
|
let isCtor = sfConstructor in prc.flags
|
|
var check = initIntSet()
|
|
fillBackendName(m, prc)
|
|
backendEnsureMutable prc
|
|
fillLoc(prc.locImpl, locProc, son(prc.ast, namePos), OnUnknown)
|
|
var memberOp = "#." #only virtual
|
|
var typ: PType
|
|
if isCtor:
|
|
typ = prc.typ.returnType
|
|
else:
|
|
typ = prc.typ.firstParamType
|
|
if typ.kind == tyPtr:
|
|
typ = typ.elementType
|
|
memberOp = "#->"
|
|
var typDesc = getTypeDescWeak(m, typ, check, dkParam)
|
|
let asPtrStr = rope(if asPtr: "_PTR" else: "")
|
|
var name, params, rettype, superCall: string = ""
|
|
var isFnConst, isOverride, isMemberVirtual, isStatic: bool = false
|
|
parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor)
|
|
genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false)
|
|
let isVirtual = sfVirtual in prc.flags or isMemberVirtual
|
|
var fnConst, override: string = ""
|
|
if isCtor:
|
|
name = typDesc
|
|
if isFnConst:
|
|
fnConst = " const"
|
|
if not isCtor:
|
|
# The call-site form (`x->salute(@)`), not the mangled Nim name. Set it on
|
|
# BOTH paths: whole-program cgen always emitted the out-of-class definition
|
|
# (the `else` branch) before any caller, but the per-module backend emits a
|
|
# foreign member proc's body in ITS OWN module, so the caller's TU only ever
|
|
# reaches the in-class declaration below — and called the member by the
|
|
# mangled name (`loo->salute_u0__vireouyks1()`, "struct Loo has no member").
|
|
prc.locImpl.snippet = "$1$2(@)" % [memberOp, name]
|
|
if isFwdDecl:
|
|
if isStatic:
|
|
result.add "static "
|
|
if isVirtual:
|
|
rettype = "virtual " & rettype
|
|
if isOverride:
|
|
override = " override"
|
|
superCall = ""
|
|
else:
|
|
if isCtor and superCall != "":
|
|
superCall = " : " & superCall
|
|
|
|
name = "$1::$2" % [typDesc, name]
|
|
|
|
result.add "N_LIB_PRIVATE "
|
|
result.addf("$1$2($3, $4)$5$6$7$8",
|
|
[rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
|
|
params, fnConst, override, superCall])
|
|
|
|
proc genProcHeader(m: BModule; prc: PSym; result: var Builder; visibility: var DeclVisibility, asPtr: bool, addAttributes: bool) =
|
|
# using static is needed for inline procs
|
|
var check = initIntSet()
|
|
fillBackendName(m, prc)
|
|
backendEnsureMutable prc
|
|
fillLoc(prc.locImpl, locProc, son(prc.ast, namePos), OnUnknown)
|
|
var rettype: Snippet = ""
|
|
var desc = newBuilder("")
|
|
genProcParams(m, prc.typ, rettype, desc, check, true, false)
|
|
let params = extract(desc)
|
|
# handle the 2 options for hotcodereloading codegen - function pointer
|
|
# (instead of forward declaration) or header for function body with "_actual" postfix
|
|
var name = prc.locImpl.snippet
|
|
if not asPtr and isReloadable(m, prc):
|
|
name.add("_actual")
|
|
# careful here! don't access ``prc.ast`` as that could reload large parts of
|
|
# the object graph!
|
|
if sfCodegenDecl notin prc.flags:
|
|
var isStaticVar = false
|
|
if lfExportLib in prc.loc.flags:
|
|
if isHeaderFile in m.flags:
|
|
visibility = ImportLib
|
|
else:
|
|
visibility = ExportLib
|
|
elif asPtr:
|
|
isStaticVar = true
|
|
elif prc.typ.callConv == ccInline or isNonReloadable(m, prc):
|
|
visibility = StaticProc
|
|
elif sfImportc notin prc.flags:
|
|
if not isSharedInstanceCName(m, prc):
|
|
visibility = Private
|
|
# else: plain extern — the definition is shared across TUs
|
|
if asPtr:
|
|
result.addProcVar(m, prc, name, params, rettype, isStatic = isStaticVar, ignoreAttributes = true)
|
|
else:
|
|
result.addProcHeader(m, prc, name, params, rettype, addAttributes)
|
|
else:
|
|
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
|
|
result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
|
|
|
|
|
|
# ------------------ type info generation -------------------------------------
|
|
|
|
proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope
|
|
proc getNimNode(m: BModule): Rope =
|
|
result = subscript(m.typeNodesName, cIntValue(m.typeNodes))
|
|
inc(m.typeNodes)
|
|
|
|
proc tiNameForHcr(m: BModule; name: Rope): Rope =
|
|
return if m.hcrOn: cDeref(name) else: name
|
|
|
|
proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
|
name, base: Rope; info: TLineInfo) =
|
|
var nimtypeKind: int
|
|
#allocMemTI(m, typ, name)
|
|
if isObjLackingTypeField(typ):
|
|
nimtypeKind = ord(tyPureObject)
|
|
else:
|
|
nimtypeKind = ord(typ.kind)
|
|
|
|
let nameHcr = tiNameForHcr(m, name)
|
|
|
|
var size: Rope
|
|
if tfIncompleteStruct in typ.flags:
|
|
size = CPointer
|
|
else:
|
|
size = getTypeDesc(m, origType, dkVar)
|
|
m.s[cfsTypeInit3].addFieldAssignmentWithValue(nameHcr, "size"):
|
|
m.s[cfsTypeInit3].addSizeof(size)
|
|
m.s[cfsTypeInit3].addFieldAssignmentWithValue(nameHcr, "align"):
|
|
m.s[cfsTypeInit3].addAlignof(size)
|
|
m.s[cfsTypeInit3].addFieldAssignment(nameHcr, "kind", nimtypeKind)
|
|
m.s[cfsTypeInit3].addFieldAssignment(nameHcr, "base", base)
|
|
# compute type flags for GC optimization
|
|
var flags = 0
|
|
if not containsGarbageCollectedRef(typ): flags = flags or 1
|
|
if not canFormAcycle(m.g.graph, typ): flags = flags or 2
|
|
#else echo("can contain a cycle: " & typeToString(typ))
|
|
if flags != 0:
|
|
m.s[cfsTypeInit3].addFieldAssignment(nameHcr, "flags", cIntValue(flags))
|
|
cgsym(m, "TNimType")
|
|
if isDefined(m.config, "nimTypeNames"):
|
|
var typename = typeToString(if origType.typeInst != nil: origType.typeInst
|
|
else: origType, preferName)
|
|
if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
|
|
typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
|
|
m.s[cfsTypeInit3].addFieldAssignment(nameHcr, "name", makeCString typename)
|
|
cgsym(m, "nimTypeRoot")
|
|
m.s[cfsTypeInit3].addFieldAssignment(nameHcr, "nextType", "nimTypeRoot")
|
|
m.s[cfsTypeInit3].addAssignment("nimTypeRoot", cAddr(nameHcr))
|
|
|
|
if m.hcrOn:
|
|
m.s[cfsStrData].addVar(kind = Global, name = name, typ = ptrType("TNimType"))
|
|
m.hcrCreateTypeInfosProc.add('\t')
|
|
var registerHcr: CallBuilder
|
|
m.hcrCreateTypeInfosProc.addStmt():
|
|
m.hcrCreateTypeInfosProc.addCall(registerHcr, callee = "hcrRegisterGlobal"):
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.add(getModuleDllPath(m, m.module))
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.add(makeCString(name))
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.addSizeof("TNimType")
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.add(CNil)
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.addCast(typ = ptrType(CPointer)):
|
|
m.hcrCreateTypeInfosProc.add(cAddr(name))
|
|
else:
|
|
if m.config.cmd == cmdNifC:
|
|
# Emit-everywhere (see genTypeInfoV1's perModuleCg gate): every demanding
|
|
# `cg` process emits this type info's tentative definition. Declare it
|
|
# `extern` first (the data analogue of a proc prototype) so a TU whose copy
|
|
# the merge stage drops still has a valid declaration; wrap the definition
|
|
# as a droppable `'d'` unit the merge stage assigns to a single owner so
|
|
# exactly one external-linkage tentative definition survives (preserving
|
|
# the RTTI pointer identity refc relies on).
|
|
m.s[cfsStrData].addDeclWithVisibility(Extern):
|
|
m.s[cfsStrData].addVar(kind = Local, name = name, typ = "TNimType")
|
|
m.s[cfsStrData].add(cnifDefDirective(name, "d", icNifName(m, origType)))
|
|
m.s[cfsStrData].addDeclWithVisibility(Private):
|
|
m.s[cfsStrData].addVar(kind = Local, name = name, typ = "TNimType")
|
|
m.s[cfsStrData].add(cnifEndDefs())
|
|
m.icDataDefs.add (name, icNifName(m, origType))
|
|
else:
|
|
m.s[cfsStrData].addDeclWithVisibility(Private):
|
|
m.s[cfsStrData].addVar(kind = Local, name = name, typ = "TNimType")
|
|
|
|
proc genTypeInfoAux(m: BModule; typ, origType: PType, name: Rope;
|
|
info: TLineInfo) =
|
|
var base: Rope
|
|
if typ.hasElementType and typ.last != nil:
|
|
var x = typ.last
|
|
if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
|
|
if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
|
|
base = cIntValue(0)
|
|
else:
|
|
base = genTypeInfoV1(m, x, info)
|
|
else:
|
|
base = cIntValue(0)
|
|
genTypeInfoAuxBase(m, typ, origType, name, base, info)
|
|
|
|
proc discriminatorTableName(m: BModule; objtype: PType, d: PSym): Rope =
|
|
# bugfix: we need to search the type that contains the discriminator:
|
|
var objtype = objtype.skipTypes(abstractPtrs)
|
|
while lookupInRecord(objtype.n, d.name) == nil:
|
|
objtype = objtype.baseClass.skipTypes(abstractPtrs)
|
|
if objtype.sym == nil:
|
|
internalError(m.config, d.info, "anonymous obj with discriminator")
|
|
result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)]
|
|
|
|
proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym, result: var Builder) =
|
|
cgsym(m, "TNimNode")
|
|
var tmp = discriminatorTableName(m, objtype, d)
|
|
result.addArrayVar(kind = Local,
|
|
name = tmp,
|
|
elementType = ptrType("TNimNode"),
|
|
len = toInt(lengthOrd(m.config, d.typ)) + 1)
|
|
|
|
proc genTNimNodeArray(m: BModule; name: Rope, size: int) =
|
|
if m.hcrOn:
|
|
m.s[cfsData].addVar(kind = Global, name = name, typ = ptrType(ptrType("TNimNode")))
|
|
var registerHcr: CallBuilder
|
|
m.hcrCreateTypeInfosProc.addStmt():
|
|
m.hcrCreateTypeInfosProc.addCall(registerHcr, callee = "hcrRegisterGlobal"):
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.add(getModuleDllPath(m, m.module))
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.add(makeCString(name))
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.addOp(Mul, NimInt, cSizeof(ptrType("TNimNode")), cIntValue(size))
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.add(CNil)
|
|
m.hcrCreateTypeInfosProc.addArgument(registerHcr):
|
|
m.hcrCreateTypeInfosProc.addCast(typ = ptrType(CPointer)):
|
|
m.hcrCreateTypeInfosProc.add(cAddr(name))
|
|
else:
|
|
m.s[cfsTypeInit1].addArrayVar(kind = Global, name = name,
|
|
elementType = ptrType("TNimNode"), len = size)
|
|
|
|
proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
|
|
info: TLineInfo) =
|
|
case n.kind
|
|
of nkRecList:
|
|
if n.len == 1:
|
|
genObjectFields(m, typ, origType, n.firstSon, expr, info)
|
|
elif n.len > 0:
|
|
var tmp = getTempName(m) & "_" & $n.len
|
|
genTNimNodeArray(m, tmp, n.len)
|
|
for i, ni in isons(n):
|
|
var tmp2 = getNimNode(m)
|
|
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(i), cAddr(tmp2))
|
|
genObjectFields(m, typ, origType, ni, tmp2, info)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", n.len)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "sons",
|
|
cAddr(subscript(tmp, cIntValue(0))))
|
|
else:
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", n.len)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
|
|
of nkRecCase:
|
|
assert(n.firstSon.kind == nkSym)
|
|
var field = n.firstSon.sym
|
|
var tmp = discriminatorTableName(m, typ, field)
|
|
var L = lengthOrd(m.config, field.typ)
|
|
assert L > 0
|
|
if field.loc.snippet == "": fillObjectFields(m, typ)
|
|
if field.loc.t == nil:
|
|
internalError(m.config, n.info, "genObjectFields")
|
|
let fieldTypInfo = genTypeInfoV1(m, field.typ, info)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 3)
|
|
m.s[cfsTypeInit3].addFieldAssignmentWithValue(expr, "offset"):
|
|
m.s[cfsTypeInit3].addOffsetof(getTypeDesc(m, origType, dkVar), field.loc.snippet)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "typ", fieldTypInfo)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "name", makeCString(field.name.s))
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "sons", cAddr(subscript(tmp, cIntValue(0))))
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", L)
|
|
if m.config.cmd == cmdNifC:
|
|
# The discriminator table has a content-addressed name
|
|
# (`NimDT_<hashType>_<field>`) and is emitted by every module that demands
|
|
# this variant type's RTTI (emit-everywhere; RTTI has no single owner —
|
|
# emission is lazy and often skipped). Declare it `extern` + wrap the
|
|
# tentative definition as a droppable `'d'` unit so the merge stage keeps
|
|
# exactly one external-linkage definition (mirrors the `TNimType` var and
|
|
# consts); otherwise the identical name collides across modules at link.
|
|
m.s[cfsData].addDeclWithVisibility(Extern):
|
|
m.s[cfsData].addArrayVar(kind = Local, name = tmp,
|
|
elementType = ptrType("TNimNode"), len = toInt(L)+1)
|
|
m.s[cfsData].add(cnifDefDirective(tmp, "d", ""))
|
|
m.s[cfsData].addArrayVar(kind = Local, name = tmp,
|
|
elementType = ptrType("TNimNode"), len = toInt(L)+1)
|
|
m.s[cfsData].add(cnifEndDefs())
|
|
m.icDataDefs.add (tmp, "")
|
|
else:
|
|
m.s[cfsData].addArrayVar(kind = Local, name = tmp,
|
|
elementType = ptrType("TNimNode"), len = toInt(L)+1)
|
|
for b in sonsFrom(n, 1):
|
|
var tmp2 = getNimNode(m)
|
|
genObjectFields(m, typ, origType, lastSon(b), tmp2, info)
|
|
case b.kind
|
|
of nkOfBranch:
|
|
if b.len < 2:
|
|
internalError(m.config, b.info, "genObjectFields; nkOfBranch broken")
|
|
for label in sonsButLast(b):
|
|
if label.kind == nkRange:
|
|
var x = toInt(getOrdValue(label.firstSon))
|
|
var y = toInt(getOrdValue(label.secondSon))
|
|
while x <= y:
|
|
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(x), cAddr(tmp2))
|
|
inc(x)
|
|
else:
|
|
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(getOrdValue(label)), cAddr(tmp2))
|
|
of nkElse:
|
|
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(L), cAddr(tmp2))
|
|
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
|
|
of nkSym:
|
|
var field = n.sym
|
|
# Do not produce code for void types
|
|
if isEmptyType(field.typ): return
|
|
if field.bitsize == 0:
|
|
if field.loc.snippet == "": fillObjectFields(m, typ)
|
|
if field.loc.t == nil:
|
|
internalError(m.config, n.info, "genObjectFields")
|
|
let fieldTypInfo = genTypeInfoV1(m, field.typ, info)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 1)
|
|
m.s[cfsTypeInit3].addFieldAssignmentWithValue(expr, "offset"):
|
|
m.s[cfsTypeInit3].addOffsetof(getTypeDesc(m, origType, dkVar), field.loc.snippet)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "typ", fieldTypInfo)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "name", makeCString(field.name.s))
|
|
else: internalError(m.config, n.info, "genObjectFields")
|
|
|
|
proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
|
|
assert typ.kind == tyObject
|
|
if incompleteType(typ):
|
|
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
|
typeToString(typ))
|
|
genTypeInfoAux(m, typ, origType, name, info)
|
|
var tmp = getNimNode(m)
|
|
if (not isImportedType(typ)) or tfCompleteStruct in typ.flags:
|
|
genObjectFields(m, typ, origType, typ.n, tmp, info)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "node", cAddr(tmp))
|
|
var t = typ.baseClass
|
|
while t != nil:
|
|
t = t.skipTypes(skipPtrs)
|
|
t.incl tfObjHasKids
|
|
t = t.baseClass
|
|
|
|
proc validTupleTypeFields(t: PType): int =
|
|
# we want to treat tuples with only void fields as empty, so we need to exclude void types here:
|
|
result = 0
|
|
for a in t.kids:
|
|
if not isEmptyType(a): inc result
|
|
|
|
proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
|
|
genTypeInfoAuxBase(m, typ, typ, name, cIntValue(0), info)
|
|
var expr = getNimNode(m)
|
|
let nonVoidKids = validTupleTypeFields(typ)
|
|
if nonVoidKids > 0:
|
|
var tmp = getTempName(m) & "_" & $nonVoidKids
|
|
genTNimNodeArray(m, tmp, nonVoidKids)
|
|
var j = 0
|
|
for i, a in typ.ikids:
|
|
# Do not produce code for void types
|
|
if isEmptyType(a): continue
|
|
var tmp2 = getNimNode(m)
|
|
let fieldTypInfo = genTypeInfoV1(m, a, info)
|
|
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(j), cAddr(tmp2))
|
|
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "kind", 1)
|
|
m.s[cfsTypeInit3].addFieldAssignmentWithValue(tmp2, "offset"):
|
|
m.s[cfsTypeInit3].addOffsetof(getTypeDesc(m, origType, dkVar), "Field" & $i)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "typ", fieldTypInfo)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "name", "\"Field" & $i & "\"")
|
|
inc j
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", nonVoidKids)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "sons",
|
|
cAddr(subscript(tmp, cIntValue(0))))
|
|
else:
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", cIntValue(0))
|
|
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "node", cAddr(expr))
|
|
|
|
proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
|
|
# Type information for enumerations is quite heavy, so we do some
|
|
# optimizations here: The ``typ`` field is never set, as it is redundant
|
|
# anyway. We generate a cstring array and a loop over it. Exceptional
|
|
# positions will be reset after the loop.
|
|
genTypeInfoAux(m, typ, typ, name, info)
|
|
var nodePtrs = getTempName(m) & "_" & $typ.n.len
|
|
genTNimNodeArray(m, nodePtrs, typ.n.len)
|
|
var enumNames = newBuilder("")
|
|
var enumNamesInit: StructInitializer
|
|
var specialCases = newBuilder("")
|
|
var firstNimNode = m.typeNodes
|
|
var hasHoles = false
|
|
enumNames.addStructInitializer(enumNamesInit, kind = siArray):
|
|
for i, son in isons(typ.n):
|
|
assert(son.kind == nkSym)
|
|
var field = son.sym
|
|
var elemNode = getNimNode(m)
|
|
enumNames.addField(enumNamesInit, name = ""):
|
|
if field.ast == nil:
|
|
# no explicit string literal for the enum field, so use field.name:
|
|
enumNames.add(makeCString(field.name.s))
|
|
else:
|
|
enumNames.add(makeCString(field.ast.strVal))
|
|
if field.position != i or tfEnumHasHoles in typ.flags:
|
|
specialCases.addFieldAssignment(elemNode, "offset", field.position)
|
|
hasHoles = true
|
|
var enumArray = getTempName(m)
|
|
var counter = getTempName(m)
|
|
m.s[cfsTypeInit1].addVar(kind = Local, name = counter, typ = NimInt)
|
|
m.s[cfsTypeInit1].addArrayVarWithInitializer(
|
|
kind = Global,
|
|
name = enumArray,
|
|
elementType = constPtrType(CChar),
|
|
len = typ.n.len):
|
|
m.s[cfsTypeInit1].add(extract(enumNames))
|
|
m.s[cfsTypeInit3].addForRangeExclusive(i = counter,
|
|
start = cIntValue(0),
|
|
bound = cIntValue(typ.n.len)):
|
|
let nodeLoc = subscript(m.typeNodesName,
|
|
cOp(Add, NimInt, counter, cIntValue(firstNimNode)))
|
|
m.s[cfsTypeInit3].addFieldAssignment(nodeLoc, "kind", cIntValue(1))
|
|
m.s[cfsTypeInit3].addFieldAssignment(nodeLoc, "offset", counter)
|
|
m.s[cfsTypeInit3].addFieldAssignment(nodeLoc, "name",
|
|
subscript(enumArray, counter))
|
|
m.s[cfsTypeInit3].addSubscriptAssignment(nodePtrs, counter,
|
|
cAddr(nodeLoc))
|
|
m.s[cfsTypeInit3].add(extract(specialCases))
|
|
let n = getNimNode(m)
|
|
m.s[cfsTypeInit3].addFieldAssignment(n, "len", typ.n.len)
|
|
m.s[cfsTypeInit3].addFieldAssignment(n, "kind", 0)
|
|
m.s[cfsTypeInit3].addFieldAssignment(n, "sons",
|
|
cAddr(subscript(nodePtrs, cIntValue(0))))
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "node", cAddr(n))
|
|
if hasHoles:
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "flags",
|
|
# 1 << 2 is {ntfEnumHole}
|
|
cOp(Shl, NimUint8, cIntValue(1), cIntValue(2)))
|
|
|
|
proc genSetInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
|
|
assert(typ.elementType != nil)
|
|
genTypeInfoAux(m, typ, typ, name, info)
|
|
var tmp = getNimNode(m)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tmp, "len", firstOrd(m.config, typ))
|
|
m.s[cfsTypeInit3].addFieldAssignment(tmp, "kind", 0)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "node", cAddr(tmp))
|
|
|
|
proc genArrayInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
|
|
genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ.elementType, info), info)
|
|
|
|
proc fakeClosureType(m: BModule; owner: PSym): PType =
|
|
# we generate the same RTTI as for a tuple[pointer, ref tuple[]]
|
|
result = newType(tyTuple, m.idgen, owner)
|
|
result.rawAddSon(newType(tyPointer, m.idgen, owner))
|
|
var r = newType(tyRef, m.idgen, owner)
|
|
let obj = createObj(m.g.graph, m.idgen, owner, owner.info, final=false)
|
|
r.rawAddSon(obj)
|
|
result.rawAddSon(r)
|
|
|
|
include ccgtrav
|
|
|
|
proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
|
|
genProc(m, s)
|
|
var desc = newBuilder("")
|
|
var paramBuilder: ProcParamBuilder
|
|
desc.addProcParams(paramBuilder):
|
|
desc.addUnnamedParam(paramBuilder, typ = CPointer)
|
|
let params = extract(desc)
|
|
let pt = procPtrTypeUnnamedNimCall(rettype = CPointer, params = params)
|
|
m.s[cfsTypeInit3].addFieldAssignmentWithValue(result, "deepcopy"):
|
|
m.s[cfsTypeInit3].add(cCast(pt, s.loc.snippet))
|
|
|
|
proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
|
|
let nr = rope(name)
|
|
if m.hcrOn:
|
|
m.s[cfsStrData].addVar(kind = Global, name = str, typ = ptrType(nr))
|
|
m.s[cfsTypeInit1].add('\t')
|
|
m.s[cfsTypeInit1].addAssignmentWithValue(str):
|
|
m.s[cfsTypeInit1].addCast(typ = ptrType(nr)):
|
|
var hcrGlobal: CallBuilder
|
|
m.s[cfsTypeInit1].addCall(hcrGlobal, "hcrGetGlobal"):
|
|
m.s[cfsTypeInit1].addArgument(hcrGlobal):
|
|
m.s[cfsTypeInit1].add(getModuleDllPath(m, module))
|
|
m.s[cfsTypeInit1].addArgument(hcrGlobal):
|
|
m.s[cfsTypeInit1].add("\"" & str & "\"")
|
|
else:
|
|
# cnif-mark the name: this extern declaration is the reference the
|
|
# def-retention check consults when the defining TU regenerates and
|
|
# the typeinfo cannot be re-demanded (type vanished) — the referencing
|
|
# TU must lose its reuse then instead of producing a link error
|
|
let declName = if m.config.cmd == cmdNifC: markCName(str) else: str
|
|
m.s[cfsStrData].addDeclWithVisibility(Extern):
|
|
m.s[cfsStrData].addVar(kind = Local, name = declName, typ = nr)
|
|
|
|
proc genTypeInfo2Name(m: BModule; t: PType): Rope =
|
|
var it = t
|
|
it = it.skipTypes(skipPtrs)
|
|
if it.sym != nil and tfFromGeneric notin it.flags:
|
|
var m = it.sym.owner
|
|
while m != nil and m.kind != skModule: m = m.owner
|
|
if m == nil or sfSystemModule in m.flags:
|
|
# produce short names for system types:
|
|
result = it.sym.name.s
|
|
else:
|
|
var p = m.owner
|
|
result = ""
|
|
if p != nil and p.kind == skPackage:
|
|
result.add p.name.s & "."
|
|
result.add m.name.s & "."
|
|
result.add it.sym.name.s
|
|
else:
|
|
result = $hashType(it, m.config)
|
|
result = makeCString(result)
|
|
|
|
proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
|
|
|
|
proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
|
info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym =
|
|
# the wrapper is roughly like:
|
|
# proc rttiDestroy(x: pointer) =
|
|
# `=destroy`(cast[ptr T](x)[])
|
|
let procname = getIdent(g.cache, "rttiDestroy")
|
|
result = newSym(skProc, procname, idgen, owner, info)
|
|
let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
|
|
|
|
dest.typ = getSysType(g, info, tyPointer)
|
|
|
|
result.typ = newProcType(info, idgen, result)
|
|
result.typ.addParam dest
|
|
|
|
var n = newNodeI(nkProcDef, info, bodyPos+1)
|
|
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
|
n[namePos] = newSymNode(result)
|
|
n[paramsPos] = result.typ.n
|
|
let body = newNodeI(nkStmtList, info)
|
|
let castType = makePtrType(typ, idgen)
|
|
if theProc.typ.firstParamType.kind != tyVar:
|
|
body.add newTreeI(nkCall, info, newSymNode(theProc), newDeref(newTreeIT(
|
|
nkCast, info, castType, newNodeIT(nkType, info, castType),
|
|
newSymNode(dest)
|
|
))
|
|
)
|
|
else:
|
|
let addrOf = newNodeIT(nkHiddenAddr, info, theProc.typ.firstParamType)
|
|
addrOf.add newDeref(newTreeIT(
|
|
nkCast, info, castType, newNodeIT(nkType, info, castType),
|
|
newSymNode(dest)
|
|
))
|
|
body.add newTreeI(nkCall, info, newSymNode(theProc),
|
|
addrOf
|
|
)
|
|
n[bodyPos] = body
|
|
result.ast = n
|
|
|
|
incl result.flagsImpl, sfFromGeneric
|
|
incl result.flagsImpl, sfGeneratedOp
|
|
# Under IC the `rttiDestroy` wrapper is generated independently in every cg
|
|
# process that emits `typ`'s RTTI (the type-info is emit-everywhere). A plain
|
|
# counter `disamb` renumbers per process, so the RTTI table baked in module A
|
|
# references `rttiDestroy_c<n>` while module B (the =destroy owner) defines a
|
|
# different number → undefined at link. Give it a content-derived `disamb`
|
|
# (stable across processes) + `HookDisambBit`, exactly like `symPrototype` does
|
|
# for the hook itself: same `typ` ⇒ same C name everywhere, and the bit makes
|
|
# `emitsBodyInThisModule` emit the body in every demander (merge dedups). The
|
|
# `"rttiDestroy"` op-name keeps its key disjoint from the real `=destroy` hook's.
|
|
setHookDisamb(g, result, "rttiDestroy", typ)
|
|
|
|
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: var Builder) =
|
|
let theProc = getAttachedOp(m.g.graph, t, op)
|
|
if theProc != nil and not isTrivialProc(m.g.graph, theProc):
|
|
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
|
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
|
# convention at least:
|
|
if theProc.typ == nil or theProc.typ.callConv != ccNimCall:
|
|
localError(m.config, info,
|
|
theProc.name.s & " needs to have the 'nimcall' calling convention")
|
|
|
|
if op == attachedDestructor:
|
|
let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor,
|
|
theProc.info, m.idgen, theProc)
|
|
genProc(m, wrapper)
|
|
result.add wrapper.loc.snippet
|
|
else:
|
|
genProc(m, theProc)
|
|
result.add theProc.loc.snippet
|
|
|
|
when false:
|
|
if not canFormAcycle(m.g.graph, t) and op == attachedTrace:
|
|
echo "ayclic but has this =trace ", t, " ", theProc.ast
|
|
else:
|
|
when false:
|
|
if op == attachedTrace and m.config.selectedGC in {gcOrc, gcYrc} and
|
|
containsGarbageCollectedRef(t):
|
|
# unfortunately this check is wrong for an object type that only contains
|
|
# .cursor fields like 'Node' inside 'cycleleak'.
|
|
internalError(m.config, info, "no attached trace proc found")
|
|
result.add NimNil
|
|
|
|
proc getObjDepth(t: PType): int16 =
|
|
var x = t
|
|
result = -1
|
|
while x != nil:
|
|
x = skipTypes(x, skipPtrs)
|
|
x = x.baseClass
|
|
inc(result)
|
|
|
|
proc genDisplayElem(d: MD5Digest): uint32 =
|
|
result = 0
|
|
for i in 0..3:
|
|
result += uint32(d[i])
|
|
result = result shl 8
|
|
|
|
proc genDisplay(result: var Builder, m: BModule; t: PType, depth: int) =
|
|
var x = t
|
|
var seqs = newSeq[Snippet](depth+1)
|
|
var i = 0
|
|
while x != nil:
|
|
x = skipTypes(x, skipPtrs)
|
|
seqs[i] = cIntValue(genDisplayElem(MD5Digest(hashType(x, m.config))))
|
|
x = x.baseClass
|
|
inc i
|
|
|
|
var arr: StructInitializer
|
|
result.addStructInitializer(arr, siArray):
|
|
for i in countdown(depth, 1):
|
|
result.addField(arr, ""):
|
|
result.add(seqs[i])
|
|
result.addField(arr, ""):
|
|
result.add(seqs[0])
|
|
|
|
proc genVTable(result: var Builder, seqs: seq[PSym]) =
|
|
var table: StructInitializer
|
|
result.addStructInitializer(table, siArray):
|
|
for i in 0..<seqs.len:
|
|
result.addField(table, ""):
|
|
result.add(cCast(CPointer, seqs[i].loc.snippet))
|
|
|
|
proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
|
|
## The C++/HCR flavour: C++ has no designated initializers, so the RTTI record
|
|
## is a bare variable that the module's `DatInit` fills field by field.
|
|
cgsym(m, "TNimTypeV2")
|
|
if m.config.cmd == cmdNifC:
|
|
# Same emit-everywhere split as `genTypeInfoV2Impl`: every `cg` process that
|
|
# demands this type declares it `extern`, and the DEFINITION is a droppable
|
|
# `'d'` unit the merge stage gives a single owner. Without the split the bare
|
|
# `TNimTypeV2 x;` in each TU is a tentative definition — which C's linker
|
|
# merges but C++'s does not, so `nim cpp --ic:on` died at link with
|
|
# "multiple definition of NTIv2__…". The field ASSIGNMENTS stay in every
|
|
# TU's `DatInit`: they are top-level code, not a definition, and every module
|
|
# computes the same values.
|
|
m.s[cfsStrData].addDeclWithVisibility(Extern):
|
|
m.s[cfsStrData].addVar(kind = Local, name = name, typ = "TNimTypeV2")
|
|
m.s[cfsVars].add(cnifDefDirective(name, "d", icNifName(m, origType)))
|
|
var def = newBuilder("")
|
|
def.addDeclWithVisibility(Private):
|
|
def.addVar(kind = Local, name = name, typ = "TNimTypeV2")
|
|
m.s[cfsVars].add extract(def)
|
|
m.s[cfsVars].add(cnifEndDefs())
|
|
m.icDataDefs.add (name, icNifName(m, origType))
|
|
else:
|
|
m.s[cfsStrData].addDeclWithVisibility(Private):
|
|
m.s[cfsStrData].addVar(kind = Local, name = name, typ = "TNimTypeV2")
|
|
|
|
var flags = 0
|
|
if not canFormAcycle(m.g.graph, t): flags = flags or 1
|
|
|
|
var typeEntry = newBuilder("")
|
|
typeEntry.addFieldAssignmentWithValue(name, "destructor"):
|
|
typeEntry.addCast(CPointer):
|
|
genHook(m, t, info, attachedDestructor, typeEntry)
|
|
typeEntry.addFieldAssignmentWithValue(name, "traceImpl"):
|
|
typeEntry.addCast(CPointer):
|
|
genHook(m, t, info, attachedTrace, typeEntry)
|
|
|
|
let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
|
|
|
|
if t.kind in {tyObject, tyDistinct} and incompleteType(t):
|
|
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
|
typeToString(t))
|
|
|
|
if isDefined(m.config, "nimTypeNames"):
|
|
var typeName: Snippet
|
|
if t.kind in {tyObject, tyDistinct}:
|
|
typeName = genTypeInfo2Name(m, t)
|
|
else:
|
|
typeName = NimNil
|
|
typeEntry.addFieldAssignment(name, "name", typeName)
|
|
let sizeTyp = getTypeDesc(m, t)
|
|
typeEntry.addFieldAssignmentWithValue(name, "size"):
|
|
typeEntry.addSizeof(sizeTyp)
|
|
typeEntry.addFieldAssignmentWithValue(name, "align"):
|
|
typeEntry.addCast(typ = NimInt16):
|
|
typeEntry.addAlignof(sizeTyp)
|
|
typeEntry.addFieldAssignment(name, "depth", objDepth)
|
|
typeEntry.addFieldAssignment(name, "flags", flags)
|
|
|
|
if objDepth >= 0:
|
|
let objDisplayStore = getTempName(m)
|
|
m.s[cfsVars].addArrayVarWithInitializer(kind = Global,
|
|
name = objDisplayStore,
|
|
elementType = getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar),
|
|
len = objDepth + 1):
|
|
genDisplay(m.s[cfsVars], m, t, objDepth)
|
|
typeEntry.addFieldAssignment(name, "display", objDisplayStore)
|
|
|
|
let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
|
|
if dispatchMethods.len > 0:
|
|
let vTablePointerName = getTempName(m)
|
|
m.s[cfsVars].addArrayVarWithInitializer(kind = Global,
|
|
name = vTablePointerName,
|
|
elementType = CPointer,
|
|
len = dispatchMethods.len):
|
|
genVTable(m.s[cfsVars], dispatchMethods)
|
|
for i in dispatchMethods:
|
|
genProcPrototype(m, i)
|
|
typeEntry.addFieldAssignment(name, "vTable", vTablePointerName)
|
|
|
|
m.s[cfsTypeInit3].add extract(typeEntry)
|
|
|
|
if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
|
|
discard genTypeInfoV1(m, t, info)
|
|
|
|
proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
|
|
cgsym(m, "TNimTypeV2")
|
|
# Under `nim nifc` every `cg` process that demands this type's RTTI emits its
|
|
# definition (emit-everywhere). The forward declaration must therefore be a
|
|
# real `extern` (not a tentative definition) so a TU whose copy the merge
|
|
# stage drops still only *declares* it; the definition itself is wrapped as a
|
|
# droppable `'d'` unit below and assigned to a single owner.
|
|
m.s[cfsStrData].addDeclWithVisibility(if m.config.cmd == cmdNifC: Extern else: Private):
|
|
m.s[cfsStrData].addVar(kind = Local, name = name, typ = "TNimTypeV2")
|
|
if m.config.cmd == cmdNifC:
|
|
m.icDataDefs.add (name, icNifName(m, origType))
|
|
|
|
var flags = 0
|
|
if not canFormAcycle(m.g.graph, t): flags = flags or 1
|
|
|
|
var typeEntry = newBuilder("")
|
|
typeEntry.addDeclWithVisibility(Private):
|
|
typeEntry.addVarWithInitializer(kind = Local, name = name, typ = "TNimTypeV2"):
|
|
var typeInit: StructInitializer
|
|
typeEntry.addStructInitializer(typeInit, kind = siNamedStruct):
|
|
typeEntry.addField(typeInit, name = "destructor"):
|
|
typeEntry.addCast(CPointer):
|
|
genHook(m, t, info, attachedDestructor, typeEntry)
|
|
|
|
let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
|
|
|
|
if t.kind in {tyObject, tyDistinct} and incompleteType(t):
|
|
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
|
typeToString(t))
|
|
|
|
let sizeType = getTypeDesc(m, t)
|
|
typeEntry.addField(typeInit, name = "size"):
|
|
typeEntry.addSizeof(sizeType)
|
|
typeEntry.addField(typeInit, name = "align"):
|
|
typeEntry.addCast(NimInt16):
|
|
typeEntry.addAlignof(sizeType)
|
|
typeEntry.addField(typeInit, name = "depth"):
|
|
typeEntry.addIntValue(objDepth)
|
|
|
|
if objDepth >= 0:
|
|
let objDisplayStore = getTempName(m)
|
|
m.s[cfsVars].addArrayVarWithInitializer(kind = Const,
|
|
name = objDisplayStore,
|
|
elementType = getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar),
|
|
len = objDepth + 1):
|
|
genDisplay(m.s[cfsVars], m, t, objDepth)
|
|
typeEntry.addField(typeInit, name = "display"):
|
|
typeEntry.add(objDisplayStore)
|
|
if isDefined(m.config, "nimTypeNames"):
|
|
var typeName: Rope
|
|
if t.kind in {tyObject, tyDistinct}:
|
|
typeName = genTypeInfo2Name(m, t)
|
|
else:
|
|
typeName = NimNil
|
|
typeEntry.addField(typeInit, name = "name"):
|
|
typeEntry.add(typeName)
|
|
typeEntry.addField(typeInit, name = "traceImpl"):
|
|
typeEntry.addCast(CPointer):
|
|
genHook(m, t, info, attachedTrace, typeEntry)
|
|
|
|
let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
|
|
if dispatchMethods.len > 0:
|
|
typeEntry.addField(typeInit, name = "flags"):
|
|
typeEntry.addIntValue(flags)
|
|
for i in dispatchMethods:
|
|
genProcPrototype(m, i)
|
|
typeEntry.addField(typeInit, name = "vTable"):
|
|
genVTable(typeEntry, dispatchMethods)
|
|
else:
|
|
typeEntry.addField(typeInit, name = "flags"):
|
|
typeEntry.addIntValue(flags)
|
|
if m.config.cmd == cmdNifC:
|
|
m.s[cfsVars].add(cnifDefDirective(name, "d", icNifName(m, origType)))
|
|
m.s[cfsVars].add extract(typeEntry)
|
|
m.s[cfsVars].add(cnifEndDefs())
|
|
else:
|
|
m.s[cfsVars].add extract(typeEntry)
|
|
|
|
if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
|
|
discard genTypeInfoV1(m, t, info)
|
|
|
|
proc myModuleOpenForCodegen(m: BModule; idx: FileIndex): bool {.inline.} =
|
|
if moduleOpenForCodegen(m.g.graph, idx):
|
|
result = idx.int < m.g.mods.len and m.g.mods[idx.int] != nil
|
|
else:
|
|
result = false
|
|
|
|
proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
|
|
let origType = t
|
|
# distinct types can have their own destructors
|
|
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct})
|
|
|
|
template prefixTI(x: Snippet): Snippet =
|
|
if m.hcrOn:
|
|
wrapPar(x)
|
|
else:
|
|
cAddr(x)
|
|
|
|
let sig = hashType(origType, m.config)
|
|
result = m.typeInfoMarkerV2.getOrDefault(sig)
|
|
if result != "":
|
|
return prefixTI(result)
|
|
|
|
let marker = m.g.typeInfoMarkerV2.getOrDefault(sig)
|
|
if marker.str != "":
|
|
cgsym(m, "TNimTypeV2")
|
|
declareNimType(m, "TNimTypeV2", marker.str, marker.owner)
|
|
# also store in local type section:
|
|
m.typeInfoMarkerV2[sig] = marker.str
|
|
return prefixTI(marker.str)
|
|
|
|
result = "NTIv2$1_" % [rope($sig)]
|
|
m.typeInfoMarkerV2[sig] = result
|
|
|
|
let owner = t.skipTypes(typedescPtrs).bindingId.module
|
|
# In the per-module backend (`cg`) RTTI is emit-everywhere like procs and
|
|
# consts: every demanding module emits the `'d'` definition (deduped to one
|
|
# owner by the merge stage). The owner-routing below would instead push the
|
|
# definition into the owner module's *unwritten* backend module (discarded in
|
|
# this process) and emit only an extern here, leaving the symbol undefined.
|
|
let perModuleCg = m.config.cmd == cmdNifC and m.config.icBackendStage == "cg"
|
|
if not perModuleCg and owner != m.module.position and myModuleOpenForCodegen(m, FileIndex owner):
|
|
# make sure the type info is created in the owner module
|
|
discard genTypeInfoV2(m.g.mods[owner], origType, info)
|
|
# reference the type info as extern here
|
|
cgsym(m, "TNimTypeV2")
|
|
declareNimType(m, "TNimTypeV2", result, owner)
|
|
return prefixTI(result)
|
|
|
|
m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner)
|
|
if m.compileToCpp or m.hcrOn:
|
|
genTypeInfoV2OldImpl(m, t, origType, result, info)
|
|
else:
|
|
genTypeInfoV2Impl(m, t, origType, result, info)
|
|
result = prefixTI(result)
|
|
|
|
proc openArrayToTuple(m: BModule; t: PType): PType =
|
|
result = newType(tyTuple, m.idgen, t.owner)
|
|
let p = newType(tyPtr, m.idgen, t.owner)
|
|
let a = newType(tyUncheckedArray, m.idgen, t.owner)
|
|
a.add t.elementType
|
|
p.add a
|
|
result.add p
|
|
result.add getSysType(m.g.graph, t.owner.info, tyInt)
|
|
|
|
proc typeToC(t: PType): string =
|
|
## Just for more readable names, the result doesn't have
|
|
## to be unique.
|
|
let s = typeToString(t)
|
|
result = newStringOfCap(s.len)
|
|
for c in s:
|
|
case c
|
|
of 'a'..'z':
|
|
result.add c
|
|
of 'A'..'Z':
|
|
result.add toLowerAscii(c)
|
|
of ' ':
|
|
discard
|
|
of ',':
|
|
result.add '_'
|
|
of '.':
|
|
result.add 'O'
|
|
of '[', '(', '{':
|
|
result.add 'L'
|
|
of ']', ')', '}':
|
|
result.add 'T'
|
|
else:
|
|
# We mangle upper letters and digits too so that there cannot
|
|
# be clashes with our special meanings
|
|
result.addInt ord(c)
|
|
|
|
proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
|
|
let origType = t
|
|
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
|
|
|
|
template prefixTI(x: Snippet): Snippet =
|
|
if m.hcrOn:
|
|
wrapPar(x)
|
|
else:
|
|
cAddr(x)
|
|
|
|
let sig = hashType(origType, m.config)
|
|
result = m.typeInfoMarker.getOrDefault(sig)
|
|
if result != "":
|
|
return prefixTI(result)
|
|
|
|
let marker = m.g.typeInfoMarker.getOrDefault(sig)
|
|
if marker.str != "":
|
|
when defined(icDbgRefc):
|
|
if "catchableerror" in marker.str:
|
|
echo "[icNti] ", marker.str, " in mod=", m.module.name.s,
|
|
" -> extern:globalMarker owner=", marker.owner
|
|
cgsym(m, "TNimType")
|
|
cgsym(m, "TNimNode")
|
|
declareNimType(m, "TNimType", marker.str, marker.owner)
|
|
# also store in local type section:
|
|
m.typeInfoMarker[sig] = marker.str
|
|
return prefixTI(marker.str)
|
|
|
|
result = "NTI$1$2_" % [rope(typeToC(t)), rope($sig)]
|
|
m.typeInfoMarker[sig] = result
|
|
|
|
when defined(icDbgRefc):
|
|
template dbgNti(branch: string) =
|
|
if "catchableerror" in result:
|
|
echo "[icNti] ", result, " in mod=", m.module.name.s, " -> ", branch
|
|
else:
|
|
template dbgNti(branch: string) = discard
|
|
|
|
let old = m.g.graph.emittedTypeInfo.getOrDefault($result)
|
|
if old != FileIndex(0):
|
|
dbgNti "extern:emittedTypeInfo"
|
|
cgsym(m, "TNimType")
|
|
cgsym(m, "TNimNode")
|
|
declareNimType(m, "TNimType", result, old.int)
|
|
return prefixTI(result)
|
|
|
|
var owner = t.skipTypes(typedescPtrs).bindingId.module
|
|
# In the per-module backend (`cg`) V1 RTTI is emit-everywhere like procs,
|
|
# consts and V2 type info: every demanding module emits the `'d'` definition
|
|
# (deduped to one owner by the merge stage). The owner-routing below would
|
|
# instead push the definition into the owner module's *unwritten* backend
|
|
# module (discarded in this process) and emit only an extern here, leaving the
|
|
# symbol undefined at link — the refc `NTI*` undefined-reference bug. (V2 got
|
|
# this gate in 8e0dd4bfb; V1, only reached under `--mm:refc`, was missed.)
|
|
let perModuleCg = m.config.cmd == cmdNifC and m.config.icBackendStage == "cg"
|
|
if not perModuleCg and owner != m.module.position and myModuleOpenForCodegen(m, FileIndex owner):
|
|
dbgNti "extern:ownerRouted"
|
|
# make sure the type info is created in the owner module
|
|
discard genTypeInfoV1(m.g.mods[owner], origType, info)
|
|
# reference the type info as extern here
|
|
cgsym(m, "TNimType")
|
|
cgsym(m, "TNimNode")
|
|
declareNimType(m, "TNimType", result, owner)
|
|
return prefixTI(result)
|
|
else:
|
|
owner = m.module.position.int32
|
|
|
|
dbgNti "DEFINED-HERE"
|
|
m.g.typeInfoMarker[sig] = (str: result, owner: owner)
|
|
#rememberEmittedTypeInfo(m.g.graph, FileIndex(owner), $result)
|
|
|
|
case t.kind
|
|
of tyEmpty, tyVoid: result = cIntValue(0)
|
|
of tyPointer, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64, tyVar, tyLent:
|
|
genTypeInfoAuxBase(m, t, t, result, cIntValue(0), info)
|
|
of tyStatic:
|
|
if t.n != nil: result = genTypeInfoV1(m, skipModifier t, info)
|
|
else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
|
|
of tyUserTypeClasses:
|
|
internalAssert m.config, t.isResolvedUserTypeClass
|
|
return genTypeInfoV1(m, t.skipModifier, info)
|
|
of tyProc:
|
|
if t.callConv != ccClosure:
|
|
genTypeInfoAuxBase(m, t, t, result, cIntValue(0), info)
|
|
else:
|
|
let x = fakeClosureType(m, t.owner)
|
|
genTupleInfo(m, x, x, result, info)
|
|
of tySequence:
|
|
genTypeInfoAux(m, t, t, result, info)
|
|
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
|
|
let markerProc = genTraverseProc(m, origType, sig)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, result), "marker", markerProc)
|
|
of tyRef:
|
|
genTypeInfoAux(m, t, t, result, info)
|
|
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
|
|
# it may not be used in other places except in `genTraverseProc`,
|
|
# we have to generate a typedesc for this case, not a weak one
|
|
discard getTypeDesc(m, origType.last)
|
|
let markerProc = genTraverseProc(m, origType, sig)
|
|
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, result), "marker", markerProc)
|
|
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
|
of tyArray: genArrayInfo(m, t, result, info)
|
|
of tySet: genSetInfo(m, t, result, info)
|
|
of tyEnum: genEnumInfo(m, t, result, info)
|
|
of tyObject:
|
|
genObjectInfo(m, t, origType, result, info)
|
|
of tyTuple:
|
|
# if t.n != nil: genObjectInfo(m, t, result)
|
|
# else:
|
|
# BUGFIX: use consistently RTTI without proper field names; otherwise
|
|
# results are not deterministic!
|
|
genTupleInfo(m, t, origType, result, info)
|
|
of tyOpenArray:
|
|
let x = openArrayToTuple(m, t)
|
|
genTupleInfo(m, x, origType, result, info)
|
|
else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
|
|
|
|
var op = getAttachedOp(m.g.graph, t, attachedDeepCopy)
|
|
if op == nil:
|
|
op = getAttachedOp(m.g.graph, origType, attachedDeepCopy)
|
|
if op != nil:
|
|
genDeepCopyProc(m, op, result)
|
|
|
|
if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags:
|
|
let v2info = genTypeInfoV2(m, origType, info)
|
|
m.s[cfsTypeInit3].addDerefFieldAssignment(v2info, "typeInfoV1", cCast(CPointer, cAddr(result)))
|
|
m.s[cfsTypeInit3].addFieldAssignment(result, "typeInfoV2", cCast(CPointer, v2info))
|
|
|
|
result = prefixTI(result)
|
|
|
|
proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rope =
|
|
if optTinyRtti in config.globalOptions:
|
|
result = genTypeInfoV2(m, t, info)
|
|
else:
|
|
result = genTypeInfoV1(m, t, info)
|
|
|
|
proc retrieveSym(n: PNode): PSym =
|
|
case n.kind
|
|
of nkPostfix: result = retrieveSym(n.secondSon)
|
|
of nkPragmaExpr, nkTypeDef: result = retrieveSym(n.firstSon)
|
|
of nkSym: result = n.sym
|
|
else: result = nil
|
|
|
|
proc genTypeSection(m: BModule, n: PNode) =
|
|
var intSet = initIntSet()
|
|
let compress = optCompress in m.config.globalOptions
|
|
for typedef in n:
|
|
let s = retrieveSym(typedef)
|
|
if s != nil and ({sfExportc, sfCompilerProc} * s.flags == {sfExportc} or compress) and s.typ != nil and
|
|
not containsGenericType(s.typ) and
|
|
s.typ.kind notin {tyVoid, tyNot, tyAnything, tyOr, tyAnd, tyUntyped, tyTyped, tyNone, tyNil, tySink}:
|
|
discard getTypeDescAux(m, s.typ, intSet, descKindFromSymKind(s.kind))
|
|
if m.g.generatedHeader != nil:
|
|
discard getTypeDescAux(m.g.generatedHeader, s.typ, intSet, descKindFromSymKind(s.kind))
|
|
|
|
# Unlike genCppInitializer which returns just the braced value list (e.g. "{a, b}"),
|
|
# genCppConstructorExpr returns a full type-prefixed expression (e.g. "Foo(a, b)").
|
|
# This is used when a standalone construction expression is needed — e.g. on the
|
|
# right-hand side of an assignment — whereas genCppInitializer is used in variable
|
|
# declarations where the type is already written separately before the initializer.
|
|
proc genCppConstructorExpr(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): Snippet =
|
|
var params = ""
|
|
if typ.bindingId in m.g.graph.initializersPerType:
|
|
let call = m.g.graph.initializersPerType[typ.bindingId]
|
|
if call != nil:
|
|
var p = prc
|
|
if p == nil:
|
|
p = BProc(module: m)
|
|
params = genCppParamsForCtor(p, call, didGenTemp)
|
|
if prc == nil:
|
|
assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
|
|
result = getTypeDesc(m, typ, dkVar) & "(" & params & ")"
|