codegen for user-defined destructors
This commit is contained in:
parent
4f45097f87
commit
337e1ecc7c
4 changed files with 87 additions and 2 deletions
|
|
@ -567,6 +567,9 @@ type
|
||||||
# for record types a nkRecord node
|
# for record types a nkRecord node
|
||||||
# for enum types a list of symbols
|
# for enum types a list of symbols
|
||||||
# else: unused
|
# else: unused
|
||||||
|
destructor*: PSym # destructor. warning: nil here may not necessary
|
||||||
|
# mean that there is no destructor.
|
||||||
|
# see instantiateDestructor in types.nim
|
||||||
owner*: PSym # the 'owner' of the type
|
owner*: PSym # the 'owner' of the type
|
||||||
sym*: PSym # types have the sym associated with them
|
sym*: PSym # types have the sym associated with them
|
||||||
# it is used for converting types to strings
|
# it is used for converting types to strings
|
||||||
|
|
|
||||||
|
|
@ -768,6 +768,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
incl(s.flags, sfForward)
|
incl(s.flags, sfForward)
|
||||||
elif sfBorrow in s.flags: semBorrow(c, n, s)
|
elif sfBorrow in s.flags: semBorrow(c, n, s)
|
||||||
sideEffectsCheck(c, s)
|
sideEffectsCheck(c, s)
|
||||||
|
if result.sons[namePos].sym.name.id == ord(wDestroy):
|
||||||
|
if s.typ.sons.len == 2:
|
||||||
|
let typ = s.typ.sons[1].skipTypes({tyVar})
|
||||||
|
typ.destructor = s
|
||||||
if s.typ.callConv == ccClosure and s.owner.kind == skModule:
|
if s.typ.callConv == ccClosure and s.owner.kind == skModule:
|
||||||
localError(s.info, errXCannotBeClosure, s.name.s)
|
localError(s.info, errXCannotBeClosure, s.name.s)
|
||||||
closeScope(c.tab) # close scope for parameters
|
closeScope(c.tab) # close scope for parameters
|
||||||
|
|
@ -856,6 +860,62 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
|
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
|
||||||
|
|
||||||
|
proc insertDestructors(c: PContext, varSection: PNode):
|
||||||
|
tuple[outer: PNode, inner: PNode] =
|
||||||
|
# Accepts a var or let section.
|
||||||
|
#
|
||||||
|
# When a var section has variables with destructors
|
||||||
|
# the var section is split up and finally blocks are inserted
|
||||||
|
# immediately after all "destructable" vars
|
||||||
|
#
|
||||||
|
# In case there were no destrucable variables, the proc returns
|
||||||
|
# (nil, nil) and the enclosing stmt-list requires no modifications.
|
||||||
|
#
|
||||||
|
# Otherwise, after the try blocks are created, the rest of the enclosing
|
||||||
|
# stmt-list should be inserted in the most `inner` such block (corresponding
|
||||||
|
# to the last variable).
|
||||||
|
#
|
||||||
|
# `outer` is a statement list that should replace the original var section.
|
||||||
|
# It will include the new truncated var section followed by the outermost
|
||||||
|
# try block.
|
||||||
|
let totalVars = varSection.sonsLen
|
||||||
|
for j in countup(0, totalVars - 1):
|
||||||
|
let
|
||||||
|
varId = varSection[j][0]
|
||||||
|
varTyp = varId.sym.typ
|
||||||
|
info = varId.info
|
||||||
|
|
||||||
|
if varTyp != nil and instantiateDestructor(varTyp):
|
||||||
|
var tryStmt = newNodeI(nkTryStmt, info)
|
||||||
|
|
||||||
|
if j < totalVars - 1:
|
||||||
|
var remainingVars = newNodeI(varSection.kind, info)
|
||||||
|
remainingVars.sons = varSection.sons[(j+1)..(-1)]
|
||||||
|
let (outer, inner) = insertDestructors(c, remainingVars)
|
||||||
|
if outer != nil:
|
||||||
|
tryStmt.addSon(outer)
|
||||||
|
result.inner = inner
|
||||||
|
else:
|
||||||
|
result.inner = newNodeI(nkStmtList, info)
|
||||||
|
result.inner.addSon(remainingVars)
|
||||||
|
tryStmt.addSon(result.inner)
|
||||||
|
else:
|
||||||
|
result.inner = newNodeI(nkStmtList, info)
|
||||||
|
tryStmt.addSon(result.inner)
|
||||||
|
|
||||||
|
tryStmt.addSon(
|
||||||
|
newNode(nkFinally, info, @[
|
||||||
|
semStmt(c, newNode(nkCall, info, @[
|
||||||
|
semSym(c, varId, varTyp.destructor, {}),
|
||||||
|
semSym(c, varId, varId.sym, {})]))]))
|
||||||
|
|
||||||
|
result.outer = newNodeI(nkStmtList, info)
|
||||||
|
varSection.sons.setLen(j+1)
|
||||||
|
result.outer.addSon(varSection)
|
||||||
|
result.outer.addSon(tryStmt)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
proc SemStmt(c: PContext, n: PNode): PNode =
|
proc SemStmt(c: PContext, n: PNode): PNode =
|
||||||
const # must be last statements in a block:
|
const # must be last statements in a block:
|
||||||
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
||||||
|
|
@ -893,11 +953,21 @@ proc SemStmt(c: PContext, n: PNode): PNode =
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
n.sons[i] = semStmt(c, n.sons[i])
|
n.sons[i] = semStmt(c, n.sons[i])
|
||||||
if n.sons[i].kind in LastBlockStmts:
|
case n.sons[i].kind
|
||||||
|
of nkVarSection, nkLetSection:
|
||||||
|
let (outer, inner) = insertDestructors(c, n.sons[i])
|
||||||
|
if outer != nil:
|
||||||
|
n.sons[i] = outer
|
||||||
|
for j in countup(i+1, length-1):
|
||||||
|
inner.addSon(SemStmt(c, n.sons[j]))
|
||||||
|
n.sons.setLen(i+1)
|
||||||
|
return
|
||||||
|
of LastBlockStmts:
|
||||||
for j in countup(i + 1, length - 1):
|
for j in countup(i + 1, length - 1):
|
||||||
case n.sons[j].kind
|
case n.sons[j].kind
|
||||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
|
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
|
||||||
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
|
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
|
||||||
|
else: nil
|
||||||
of nkRaiseStmt: result = semRaise(c, n)
|
of nkRaiseStmt: result = semRaise(c, n)
|
||||||
of nkVarSection: result = semVarOrLet(c, n, skVar)
|
of nkVarSection: result = semVarOrLet(c, n, skVar)
|
||||||
of nkLetSection: result = semVarOrLet(c, n, skLet)
|
of nkLetSection: result = semVarOrLet(c, n, skLet)
|
||||||
|
|
|
||||||
|
|
@ -1054,4 +1054,11 @@ proc getReturnType*(s: PSym): PType =
|
||||||
proc getSize(typ: PType): biggestInt =
|
proc getSize(typ: PType): biggestInt =
|
||||||
result = computeSize(typ)
|
result = computeSize(typ)
|
||||||
if result < 0: InternalError("getSize(" & $typ.kind & ')')
|
if result < 0: InternalError("getSize(" & $typ.kind & ')')
|
||||||
|
|
||||||
|
proc instantiateDestructor*(typ: PType): bool =
|
||||||
|
# return true if the type already had a user-defined
|
||||||
|
# destructor or if the compiler generated a default
|
||||||
|
# member-wise one
|
||||||
|
if typ.destructor != nil: return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ type
|
||||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||||
wStar, wMinus,
|
wStar, wMinus,
|
||||||
wMagic, wThread, wFinal, wProfiler, wObjChecks,
|
wMagic, wThread, wFinal, wProfiler, wObjChecks,
|
||||||
|
|
||||||
|
wDestroy,
|
||||||
|
|
||||||
wImmediate, wImportCpp, wImportObjC,
|
wImmediate, wImportCpp, wImportObjC,
|
||||||
wImportCompilerProc,
|
wImportCompilerProc,
|
||||||
wImportc, wExportc, wIncompleteStruct,
|
wImportc, wExportc, wIncompleteStruct,
|
||||||
|
|
@ -110,7 +113,9 @@ const
|
||||||
|
|
||||||
":", "::", "=", ".", "..",
|
":", "::", "=", ".", "..",
|
||||||
"*", "-",
|
"*", "-",
|
||||||
"magic", "thread", "final", "profiler", "objchecks",
|
"magic", "thread", "final", "profiler", "objchecks",
|
||||||
|
|
||||||
|
"destroy",
|
||||||
|
|
||||||
"immediate", "importcpp", "importobjc",
|
"immediate", "importcpp", "importobjc",
|
||||||
"importcompilerproc", "importc", "exportc", "incompletestruct",
|
"importcompilerproc", "importc", "exportc", "incompletestruct",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue