Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use value-initialization in C++ to gracefully handle non-POD types #9603

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1308,15 +1308,13 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
var tmp: TLoc
var r: Rope
if useTemp:
getTemp(p, t, tmp)
getTemp(p, t, tmp, not isRef)
r = rdLoc(tmp)
if isRef:
rawGenNew(p, tmp, nil)
t = t.lastSon.skipTypes(abstractInst)
r = "(*$1)" % [r]
gcUsage(p.config, e)
else:
constructLoc(p, tmp)
else:
resetLoc(p, d)
r = rdLoc(d)
Expand Down
40 changes: 31 additions & 9 deletions compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,12 @@ proc resetLoc(p: BProc, loc: var TLoc) =
else:
# array passed as argument decayed into pointer, bug #7332
# so we use getTypeDesc here rather than rdLoc(loc)
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
addrLoc(p.config, loc), getTypeDesc(p.module, loc.t))
let typeName = getTypeDesc(p.module, loc.t)
if p.module.compileToCpp:
linefmt(p, cpsStmts, "$1 = $2{};$n", rdLoc(loc), typeName)
else:
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
addrLoc(p.config, loc), typeName)
# XXX: We can be extra clever here and call memset only
# on the bytes following the m_type field?
genObjectInit(p, cpsStmts, loc.t, loc, true)
Expand All @@ -340,10 +344,11 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc),
getTypeDesc(p.module, typ))
else:
# don't zero temporary values for performance if we can avoid it
if not isTemp or containsGarbageCollectedRef(loc.t):
# don't use nimZeroMem for temporary values for performance if we can
# avoid it:
if not isImportedCppType(typ):
# Only zero in plain C. In C++ variables are value-initialized
# in `assignLocalVar`
if not isImportedCppType(typ) and not p.module.compileToCpp:
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
addrLoc(p.config, loc), getTypeDesc(p.module, typ))
genObjectInit(p, cpsStmts, loc.t, loc, true)
Expand All @@ -363,7 +368,15 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
inc(p.labels)
result.r = "T" & rope(p.labels) & "_"
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)

# Value initialize locals in C++. In C they are zeroed in `initLocalVar`
var decl = result.r
if p.module.compileToCpp and isComplexValueType(t) and
(needsInit or containsGarbageCollectedRef(t)):
decl = decl & "{}"

linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), decl)

result.k = locTemp
result.lode = lodeTyp t
result.storage = OnStack
Expand Down Expand Up @@ -406,6 +419,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
result = getTypeDesc(p.module, s.typ)

if s.constraint.isNil:
if sfRegister in s.flags: add(result, " register")
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
Expand All @@ -416,12 +430,20 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
else:
result = s.cgDeclFrmt % [result, s.loc.r]

proc assignLocalVar(p: BProc, n: PNode) =
proc assignLocalVar(p: BProc, n: PNode, valueInit = false) =
#assert(s.loc.k == locNone) # not yet assigned
# this need not be fulfilled for inline procs; they are regenerated
# for each module that uses them!
let nl = if optLineDir in p.config.options: "" else: "\L"
let decl = localVarDecl(p, n) & ";" & nl
var decl = localVarDecl(p, n)

# Value initialize locals in C++. In C they are zeroed in `initLocalVar`
if p.module.compileToCpp and (sfNoInit notin n.sym.flags) and
isComplexValueType(n.sym.typ):
decl = decl & "{}"

decl = decl & ";" & nl

line(p, cpsLocals, decl)
localDebugInfo(p, n.sym)

Expand Down Expand Up @@ -862,7 +884,7 @@ proc genProcAux(m: BModule, prc: PSym) =
linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
else:
# declare the result symbol:
assignLocalVar(p, resNode)
assignLocalVar(p, resNode, valueInit = true)
assert(res.loc.r != nil)
initLocalVar(p, res, immediateAsgn=false)
returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc))
Expand Down
39 changes: 39 additions & 0 deletions tests/cpp/tvalue_initialization.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
discard """
targets: "cpp"
"""

{.emit: """/*TYPESECTION*/
struct Foo
{
bool isConstructed = true;
};
""".}

type
Foo {.importc.} = object

Bar = object
impl: Foo

proc isConstructed(self: Foo): bool {.importcpp: "#.$1".}

proc test_init_locals(): Bar =
var
x: Bar
y: (Foo, Bar)
z = Bar()

doAssert result.impl.isConstructed
doAssert x.impl.isConstructed
doAssert y[0].isConstructed
doAssert y[1].impl.isConstructed
doAssert z.impl.isConstructed

proc test_reset(): Bar =
if true:
result = Bar()

doAssert result.impl.isConstructed

discard test_init_locals()
discard test_reset()