mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-26 08:31:44 +00:00
WIP: SSO based strings for ARC/ORC, opt-in via -d:nimsso
This commit is contained in:
@@ -344,7 +344,8 @@ proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
|
||||
var a = initLocExpr(p, n[0])
|
||||
let ra = withTmpIfNeeded(p, a, needsTmp).rdLoc
|
||||
let tmp = withTmpIfNeeded(p, a, needsTmp)
|
||||
let ra = if p.config.isDefined("nimsso"): addrLoc(p.config, tmp) else: tmp.rdLoc
|
||||
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
|
||||
|
||||
@@ -320,12 +320,16 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
|
||||
p.s(cpsStmts).addCallStmt(
|
||||
cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
|
||||
let rd = d.rdLoc
|
||||
let ra = a.rdLoc
|
||||
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
|
||||
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
|
||||
let la = lenExpr(p, a)
|
||||
if p.config.isDefined("nimsso"):
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
|
||||
cCall(cgsymValue(p.module, "nimStrData"), bra))
|
||||
else:
|
||||
let ra = a.rdLoc
|
||||
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
|
||||
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
|
||||
p.s(cpsStmts).addFieldAssignment(rd, "Field1", la)
|
||||
else:
|
||||
internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
|
||||
@@ -1316,8 +1320,13 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
let ra = rdLoc(a)
|
||||
putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage)
|
||||
if p.config.isDefined("nimsso") and ty.kind == tyString:
|
||||
let bra = byRefLoc(p, a)
|
||||
putIntoDest(p, d, n,
|
||||
subscript(cCall(cgsymValue(p.module, "nimStrData"), bra), rcb), a.storage)
|
||||
else:
|
||||
let ra = rdLoc(a)
|
||||
putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage)
|
||||
|
||||
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
|
||||
var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
|
||||
@@ -2124,12 +2133,20 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
||||
let ra = rdLoc(a)
|
||||
putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage)
|
||||
of tyString, tySequence:
|
||||
let ra = rdLoc(a)
|
||||
let la = lenExpr(p, a)
|
||||
putIntoDest(p, b, e,
|
||||
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil) &
|
||||
cArgumentSeparator & la,
|
||||
a.storage)
|
||||
if p.config.isDefined("nimsso") and
|
||||
skipTypes(a.t, abstractVarRange).kind == tyString:
|
||||
let bra = byRefLoc(p, a)
|
||||
putIntoDest(p, b, e,
|
||||
cCall(cgsymValue(p.module, "nimStrData"), bra) &
|
||||
cArgumentSeparator & la,
|
||||
a.storage)
|
||||
else:
|
||||
let ra = rdLoc(a)
|
||||
putIntoDest(p, b, e,
|
||||
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil) &
|
||||
cArgumentSeparator & la,
|
||||
a.storage)
|
||||
of tyArray:
|
||||
let ra = rdLoc(a)
|
||||
let la = cIntValue(lengthOrd(p.config, a.t))
|
||||
@@ -2710,9 +2727,9 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
|
||||
var a: TLoc = initLocExpr(p, n[0])
|
||||
let arg = if p.config.isDefined("nimsso"): addrLoc(p.config, a) else: rdLoc(a)
|
||||
putIntoDest(p, d, n,
|
||||
cgCall(p, "nimToCStringConv", rdLoc(a)),
|
||||
# "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
|
||||
cgCall(p, "nimToCStringConv", arg),
|
||||
a.storage)
|
||||
|
||||
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
|
||||
@@ -2789,13 +2806,19 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
var src: TLoc = initLocExpr(p, n[2])
|
||||
let destVal = rdLoc(a)
|
||||
let srcVal = rdLoc(src)
|
||||
p.s(cpsStmts).addSingleIfStmt(
|
||||
cOp(NotEqual,
|
||||
dotField(destVal, "p"),
|
||||
dotField(srcVal, "p"))):
|
||||
if p.config.isDefined("nimsso") and
|
||||
n[1].typ.skipTypes(abstractVar).kind == tyString:
|
||||
# SmallString: destroy dst then struct-copy src; no .p field aliasing needed
|
||||
genStmts(p, n[3])
|
||||
p.s(cpsStmts).addFieldAssignment(destVal, "len", dotField(srcVal, "len"))
|
||||
p.s(cpsStmts).addFieldAssignment(destVal, "p", dotField(srcVal, "p"))
|
||||
genAssignment(p, a, src, {})
|
||||
else:
|
||||
p.s(cpsStmts).addSingleIfStmt(
|
||||
cOp(NotEqual,
|
||||
dotField(destVal, "p"),
|
||||
dotField(srcVal, "p"))):
|
||||
genStmts(p, n[3])
|
||||
p.s(cpsStmts).addFieldAssignment(destVal, "len", dotField(srcVal, "len"))
|
||||
p.s(cpsStmts).addFieldAssignment(destVal, "p", dotField(srcVal, "p"))
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, n.typ)
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}:
|
||||
@@ -2832,15 +2855,19 @@ proc genDestroy(p: BProc; n: PNode) =
|
||||
case t.kind
|
||||
of tyString:
|
||||
var a: TLoc = initLocExpr(p, arg)
|
||||
let ra = rdLoc(a)
|
||||
let rp = dotField(ra, "p")
|
||||
p.s(cpsStmts).addSingleIfStmt(
|
||||
cOp(And, rp,
|
||||
cOp(Not, cOp(BitAnd, NimInt,
|
||||
derefField(rp, "cap"),
|
||||
NimStrlitFlag)))):
|
||||
let fn = if optThreads in p.config.globalOptions: "deallocShared" else: "dealloc"
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, fn), rp)
|
||||
if p.config.isDefined("nimsso"):
|
||||
# SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a))
|
||||
else:
|
||||
let ra = rdLoc(a)
|
||||
let rp = dotField(ra, "p")
|
||||
p.s(cpsStmts).addSingleIfStmt(
|
||||
cOp(And, rp,
|
||||
cOp(Not, cOp(BitAnd, NimInt,
|
||||
derefField(rp, "cap"),
|
||||
NimStrlitFlag)))):
|
||||
let fn = if optThreads in p.config.globalOptions: "deallocShared" else: "dealloc"
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, fn), rp)
|
||||
of tySequence:
|
||||
var a: TLoc = initLocExpr(p, arg)
|
||||
let ra = rdLoc(a)
|
||||
@@ -4200,7 +4227,10 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
|
||||
genConstObjConstr(p, n, isConst, result)
|
||||
of tyString, tyCstring:
|
||||
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
|
||||
genStringLiteralV2Const(p.module, n, isConst, result)
|
||||
if p.config.isDefined("nimsso"):
|
||||
genStringLiteralV3Const(p.module, n, isConst, result)
|
||||
else:
|
||||
genStringLiteralV2Const(p.module, n, isConst, result)
|
||||
else:
|
||||
var d: TLoc = initLocExpr(p, n)
|
||||
result.add rdLoc(d)
|
||||
|
||||
@@ -22,7 +22,11 @@ template detectVersion(field, corename) =
|
||||
result = 1
|
||||
|
||||
proc detectStrVersion(m: BModule): int =
|
||||
detectVersion(strVersion, "nimStrVersion")
|
||||
if m.g.config.isDefined("nimsso") and
|
||||
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
|
||||
result = 3
|
||||
else:
|
||||
detectVersion(strVersion, "nimStrVersion")
|
||||
|
||||
proc detectSeqVersion(m: BModule): int =
|
||||
detectVersion(seqVersion, "nimSeqVersion")
|
||||
@@ -128,6 +132,155 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Bu
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
|
||||
proc ssoCharLit(ch: char): string =
|
||||
## Return a C char literal for ch, with proper escaping.
|
||||
const hexDigits = "0123456789abcdef"
|
||||
result = "'"
|
||||
case ch
|
||||
of '\'': result.add("\\'")
|
||||
of '\\': result.add("\\\\")
|
||||
of '\0': result.add("\\0")
|
||||
of '\n': result.add("\\n")
|
||||
of '\r': result.add("\\r")
|
||||
of '\t': result.add("\\t")
|
||||
elif ch.ord < 32 or ch.ord == 127:
|
||||
result.add("\\x")
|
||||
result.add(hexDigits[ch.ord shr 4])
|
||||
result.add(hexDigits[ch.ord and 0xf])
|
||||
else:
|
||||
result.add(ch)
|
||||
result.add('\'')
|
||||
|
||||
proc ssoPayloadLit(src: string; maxLen: int): string =
|
||||
const AlwaysAvail = 7
|
||||
result = "{"
|
||||
for i in 0..<AlwaysAvail:
|
||||
if i > 0: result.add(',')
|
||||
let ch = if i < maxLen: src[i] else: '\0'
|
||||
result.add(ssoCharLit(ch))
|
||||
result.add('}')
|
||||
|
||||
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
# Inline SmallString struct initializer for use inside const aggregate types.
|
||||
# Short strings (<=7 chars) embed all chars directly. Long strings reference
|
||||
# a static LongString block emitted separately into cfsStrData.
|
||||
const AlwaysAvail = 7
|
||||
let s = n.strVal
|
||||
|
||||
cgsym(m, "SmallString")
|
||||
cgsym(m, "LongString")
|
||||
|
||||
var si: StructInitializer
|
||||
result.addStructInitializer(si, kind = siOrderedStruct):
|
||||
if s.len <= AlwaysAvail:
|
||||
result.addField(si, name = "slen"):
|
||||
result.addIntValue(s.len)
|
||||
result.addField(si, name = "payload"):
|
||||
result.add(ssoPayloadLit(s, s.len))
|
||||
result.addField(si, name = "more"):
|
||||
result.add(NimNil)
|
||||
else:
|
||||
# Emit the LongString block into cfsStrData and reference it inline.
|
||||
let dataName = getTempName(m)
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = dataName):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "rc", typ = NimInt)
|
||||
res.addField(name = "fullLen", typ = NimInt)
|
||||
res.addField(name = "capImpl", typ = NimInt)
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
do:
|
||||
var di: StructInitializer
|
||||
res.addStructInitializer(di, kind = siOrderedStruct):
|
||||
res.addField(di, name = "rc"):
|
||||
res.addIntValue(1)
|
||||
res.addField(di, name = "fullLen"):
|
||||
res.addIntValue(s.len)
|
||||
res.addField(di, name = "capImpl"):
|
||||
res.addIntValue(0) # static, never freed
|
||||
res.addField(di, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
result.addField(si, name = "slen"):
|
||||
result.addIntValue(255)
|
||||
result.addField(si, name = "payload"):
|
||||
result.add(ssoPayloadLit(s, AlwaysAvail))
|
||||
result.addField(si, name = "more"):
|
||||
result.add(cCast(ptrType("LongString"), cAddr(dataName)))
|
||||
|
||||
# ------ Version 3: SmallString (SSO) strings --------------------------------
|
||||
|
||||
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
# SmallString literal. Always generate a fresh SmallString variable (like v2
|
||||
# always generates a fresh outer NimStringV2). For long strings, cache the
|
||||
# LongString payload to avoid duplicates within a module.
|
||||
const AlwaysAvail = 7 # must match strs_v3.nim
|
||||
let s = n.strVal
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
|
||||
cgsym(m, "SmallString")
|
||||
cgsym(m, "LongString")
|
||||
|
||||
var res = newBuilder("")
|
||||
if s.len <= AlwaysAvail:
|
||||
# Short: all chars fit in payload, more = NULL.
|
||||
res.addVarWithInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp, typ = "SmallString"):
|
||||
var si: StructInitializer
|
||||
res.addStructInitializer(si, kind = siOrderedStruct):
|
||||
res.addField(si, name = "slen"):
|
||||
res.addIntValue(s.len)
|
||||
res.addField(si, name = "payload"):
|
||||
res.add(ssoPayloadLit(s, s.len))
|
||||
res.addField(si, name = "more"):
|
||||
res.add(NimNil)
|
||||
else:
|
||||
# Long: cache the LongString block to emit it only once per module per string.
|
||||
# Always generate a fresh SmallString pointing at the (possibly cached) block.
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var dataName: string
|
||||
if id == m.labels:
|
||||
dataName = getTempName(m)
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = dataName):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "rc", typ = NimInt)
|
||||
res.addField(name = "fullLen", typ = NimInt)
|
||||
res.addField(name = "capImpl", typ = NimInt)
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
do:
|
||||
var di: StructInitializer
|
||||
res.addStructInitializer(di, kind = siOrderedStruct):
|
||||
res.addField(di, name = "rc"):
|
||||
res.addIntValue(1)
|
||||
res.addField(di, name = "fullLen"):
|
||||
res.addIntValue(s.len)
|
||||
res.addField(di, name = "capImpl"):
|
||||
res.addIntValue(0) # bit 0 = 0: static, never freed
|
||||
res.addField(di, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
else:
|
||||
dataName = m.tmpBase & $id
|
||||
# PayloadSize = AlwaysAvail + sizeof(pointer) - 1; sentinel slen = PayloadSize+1
|
||||
# We just use a large value (255) that is guaranteed > PayloadSize on all platforms.
|
||||
res.addVarWithInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp, typ = "SmallString"):
|
||||
var si: StructInitializer
|
||||
res.addStructInitializer(si, kind = siOrderedStruct):
|
||||
res.addField(si, name = "slen"):
|
||||
res.addIntValue(255) # > PayloadSize on all platforms => long sentinel
|
||||
res.addField(si, name = "payload"):
|
||||
res.add(ssoPayloadLit(s, AlwaysAvail))
|
||||
res.addField(si, name = "more"):
|
||||
res.add(cCast(ptrType("LongString"), cAddr(dataName)))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
@@ -138,6 +291,8 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
let tmp = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
|
||||
result.add tmp
|
||||
of 3:
|
||||
localError(m.config, info, "genStringLiteralDataOnly not supported for SmallString (nimsso)")
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
@@ -148,5 +303,6 @@ proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralV1(m, n, result)
|
||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
||||
of 3: genStringLiteralV3(m, n, isConst = true, result)
|
||||
else:
|
||||
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
||||
|
||||
@@ -339,6 +339,10 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
|
||||
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*")
|
||||
|
||||
@@ -389,7 +389,11 @@ proc lenField(p: BProc, val: Rope): Rope {.inline.} =
|
||||
|
||||
proc lenExpr(p: BProc; a: TLoc): Rope =
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = dotField(rdLoc(a), "len")
|
||||
if p.config.isDefined("nimsso") and a.t != nil and
|
||||
a.t.skipTypes(abstractInst).kind == tyString:
|
||||
result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a))
|
||||
else:
|
||||
result = dotField(rdLoc(a), "len")
|
||||
else:
|
||||
let ra = rdLoc(a)
|
||||
result = cIfExpr(ra, lenField(p, ra), cIntValue(0))
|
||||
@@ -530,7 +534,15 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
|
||||
let atyp = skipTypes(loc.t, abstractInst)
|
||||
let rl = rdLoc(loc)
|
||||
if atyp.kind in {tyVar, tyLent}:
|
||||
if typ.kind == tyString and p.config.isDefined("nimsso"):
|
||||
# SmallString zero state: slen=0 suffices (slen<=AlwaysAvail => inline, no heap)
|
||||
if atyp.kind in {tyVar, tyLent}:
|
||||
p.s(cpsStmts).addAssignment(derefField(rl, "slen"), cIntValue(0))
|
||||
p.s(cpsStmts).addAssignment(derefField(rl, "more"), NimNil)
|
||||
else:
|
||||
p.s(cpsStmts).addAssignment(dotField(rl, "slen"), cIntValue(0))
|
||||
p.s(cpsStmts).addAssignment(dotField(rl, "more"), NimNil)
|
||||
elif atyp.kind in {tyVar, tyLent}:
|
||||
p.s(cpsStmts).addAssignment(derefField(rl, "len"), cIntValue(0))
|
||||
p.s(cpsStmts).addAssignment(derefField(rl, "p"), NimNil)
|
||||
else:
|
||||
@@ -580,8 +592,13 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
|
||||
let typ = loc.t
|
||||
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
|
||||
let rl = rdLoc(loc)
|
||||
p.s(cpsStmts).addFieldAssignment(rl, "len", cIntValue(0))
|
||||
p.s(cpsStmts).addFieldAssignment(rl, "p", NimNil)
|
||||
if skipTypes(typ, abstractInst + {tyStatic}).kind == tyString and p.config.isDefined("nimsso"):
|
||||
# SmallString zero state: slen=0 suffices
|
||||
p.s(cpsStmts).addFieldAssignment(rl, "slen", cIntValue(0))
|
||||
p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil)
|
||||
else:
|
||||
p.s(cpsStmts).addFieldAssignment(rl, "len", cIntValue(0))
|
||||
p.s(cpsStmts).addFieldAssignment(rl, "p", NimNil)
|
||||
elif not isComplexValueType(typ):
|
||||
if containsGarbageCollectedRef(loc.t):
|
||||
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
|
||||
|
||||
@@ -701,11 +701,18 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedAsgn, attachedDeepCopy, attachedDup:
|
||||
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c, t.destructor, x)
|
||||
body.add moveCall
|
||||
if c.g.config.isDefined("nimsso"):
|
||||
# SmallString: destroy old dst, then bit-copy src (no rc increment — this is a move).
|
||||
# No .p aliasing check needed; rc-based destroy handles COW sharing correctly.
|
||||
doAssert t.destructor != nil
|
||||
body.add destructorCall(c, t.destructor, x)
|
||||
body.add newAsgnStmt(x, y)
|
||||
else:
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c, t.destructor, x)
|
||||
body.add moveCall
|
||||
of attachedDestructor:
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
|
||||
Reference in New Issue
Block a user