This commit is contained in:
parent
e2886eebb4
commit
0d8942d45e
7 changed files with 175 additions and 15 deletions
|
|
@ -177,6 +177,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
|
||||||
else:
|
else:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
proc createDestructorCall(c: PContext, s: PSym): PNode =
|
||||||
|
let varTyp = s.typ
|
||||||
|
if varTyp == nil or sfGlobal in s.flags: return
|
||||||
|
let destructableT = instantiateDestructor(c, varTyp)
|
||||||
|
if destructableT != nil:
|
||||||
|
let call = semStmt(c, newNode(nkCall, s.info, @[
|
||||||
|
useSym(destructableT.destructor), useSym(s)]))
|
||||||
|
result = newNode(nkDefer, s.info, @[call])
|
||||||
|
|
||||||
proc insertDestructors(c: PContext,
|
proc insertDestructors(c: PContext,
|
||||||
varSection: PNode): tuple[outer, inner: PNode] =
|
varSection: PNode): tuple[outer, inner: PNode] =
|
||||||
# Accepts a var or let section.
|
# Accepts a var or let section.
|
||||||
|
|
|
||||||
|
|
@ -2259,7 +2259,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkStaticStmt:
|
of nkStaticStmt:
|
||||||
result = semStaticStmt(c, n)
|
result = semStaticStmt(c, n)
|
||||||
of nkDefer:
|
of nkDefer:
|
||||||
localError(n.info, errGenerated, "'defer' not allowed in this context")
|
n.sons[0] = semExpr(c, n.sons[0])
|
||||||
|
if not n.sons[0].typ.isEmptyType:
|
||||||
|
localError(n.info, errGenerated, "'defer' takes a 'void' expression")
|
||||||
|
#localError(n.info, errGenerated, "'defer' not allowed in this context")
|
||||||
else:
|
else:
|
||||||
localError(n.info, errInvalidExpressionX,
|
localError(n.info, errInvalidExpressionX,
|
||||||
renderTree(n, {renderNoComments}))
|
renderTree(n, {renderNoComments}))
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||||
else:
|
else:
|
||||||
result.add identDefs
|
result.add identDefs
|
||||||
|
|
||||||
|
proc addDefer(c: PContext; result: var PNode; s: PSym) =
|
||||||
|
let deferDestructorCall = createDestructorCall(c, s)
|
||||||
|
if deferDestructorCall != nil:
|
||||||
|
if result.kind != nkStmtList:
|
||||||
|
let oldResult = result
|
||||||
|
result = newNodeI(nkStmtList, result.info)
|
||||||
|
result.add oldResult
|
||||||
|
result.add deferDestructorCall
|
||||||
|
|
||||||
proc isDiscardUnderscore(v: PSym): bool =
|
proc isDiscardUnderscore(v: PSym): bool =
|
||||||
if v.name.s == "_":
|
if v.name.s == "_":
|
||||||
v.flags.incl(sfGenSym)
|
v.flags.incl(sfGenSym)
|
||||||
|
|
@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if def.kind == nkPar: v.ast = def[j]
|
if def.kind == nkPar: v.ast = def[j]
|
||||||
v.typ = tup.sons[j]
|
v.typ = tup.sons[j]
|
||||||
b.sons[j] = newSymNode(v)
|
b.sons[j] = newSymNode(v)
|
||||||
|
addDefer(c, result, v)
|
||||||
checkNilable(v)
|
checkNilable(v)
|
||||||
if sfCompileTime in v.flags: hasCompileTime = true
|
if sfCompileTime in v.flags: hasCompileTime = true
|
||||||
if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
|
if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
|
||||||
|
|
@ -1371,7 +1381,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
for i in countup(0, length - 1):
|
for i in countup(0, length - 1):
|
||||||
let k = n.sons[i].kind
|
let k = n.sons[i].kind
|
||||||
case k
|
case k
|
||||||
of nkFinally, nkExceptBranch, nkDefer:
|
of nkFinally, nkExceptBranch:
|
||||||
# stand-alone finally and except blocks are
|
# stand-alone finally and except blocks are
|
||||||
# transformed into regular try blocks:
|
# transformed into regular try blocks:
|
||||||
#
|
#
|
||||||
|
|
@ -1424,14 +1434,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkVarSection, nkLetSection:
|
|
||||||
let (outer, inner) = insertDestructors(c, n.sons[i])
|
|
||||||
if outer != nil:
|
|
||||||
n.sons[i] = outer
|
|
||||||
var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
|
|
||||||
inner.addSon(semStmtList(c, rest, flags))
|
|
||||||
n.sons.setLen(i+1)
|
|
||||||
return
|
|
||||||
of LastBlockStmts:
|
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
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
# * converts "continue" to "break"; disambiguates "break"
|
# * converts "continue" to "break"; disambiguates "break"
|
||||||
# * introduces method dispatchers
|
# * introduces method dispatchers
|
||||||
# * performs lambda lifting for closure support
|
# * performs lambda lifting for closure support
|
||||||
|
# * transforms 'defer' into a 'try finally' statement
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
||||||
|
|
@ -44,6 +45,7 @@ type
|
||||||
inlining: int # > 0 if we are in inlining context (copy vars)
|
inlining: int # > 0 if we are in inlining context (copy vars)
|
||||||
nestedProcs: int # > 0 if we are in a nested proc
|
nestedProcs: int # > 0 if we are in a nested proc
|
||||||
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
||||||
|
deferDetected: bool
|
||||||
PTransf = ref TTransfContext
|
PTransf = ref TTransfContext
|
||||||
|
|
||||||
proc newTransNode(a: PNode): PTransNode {.inline.} =
|
proc newTransNode(a: PNode): PTransNode {.inline.} =
|
||||||
|
|
@ -680,6 +682,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc transform(c: PTransf, n: PNode): PTransNode =
|
proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
|
when false:
|
||||||
|
var oldDeferAnchor: PNode
|
||||||
|
if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
|
||||||
|
nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally,
|
||||||
|
nkBlockStmt, nkBlockExpr}:
|
||||||
|
oldDeferAnchor = c.deferAnchor
|
||||||
|
c.deferAnchor = n
|
||||||
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
result = transformSym(c, n)
|
result = transformSym(c, n)
|
||||||
|
|
@ -712,13 +722,36 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
result = transformFor(c, n)
|
result = transformFor(c, n)
|
||||||
of nkParForStmt:
|
of nkParForStmt:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
of nkCaseStmt: result = transformCase(c, n)
|
of nkCaseStmt:
|
||||||
|
result = transformCase(c, n)
|
||||||
|
of nkWhileStmt: result = transformWhile(c, n)
|
||||||
|
of nkBlockStmt, nkBlockExpr:
|
||||||
|
result = transformBlock(c, n)
|
||||||
|
of nkDefer:
|
||||||
|
c.deferDetected = true
|
||||||
|
result = transformSons(c, n)
|
||||||
|
when false:
|
||||||
|
let deferPart = newNodeI(nkFinally, n.info)
|
||||||
|
deferPart.add n.sons[0]
|
||||||
|
let tryStmt = newNodeI(nkTryStmt, n.info)
|
||||||
|
if c.deferAnchor.isNil:
|
||||||
|
tryStmt.add c.root
|
||||||
|
c.root = tryStmt
|
||||||
|
result = PTransNode(tryStmt)
|
||||||
|
else:
|
||||||
|
# modify the corresponding *action*, don't rely on nkStmtList:
|
||||||
|
let L = c.deferAnchor.len-1
|
||||||
|
tryStmt.add c.deferAnchor.sons[L]
|
||||||
|
c.deferAnchor.sons[L] = tryStmt
|
||||||
|
result = newTransNode(nkCommentStmt, n.info, 0)
|
||||||
|
tryStmt.addSon(deferPart)
|
||||||
|
# disable the original 'defer' statement:
|
||||||
|
n.kind = nkCommentStmt
|
||||||
of nkContinueStmt:
|
of nkContinueStmt:
|
||||||
result = PTransNode(newNodeI(nkBreakStmt, n.info))
|
result = PTransNode(newNodeI(nkBreakStmt, n.info))
|
||||||
var labl = c.contSyms[c.contSyms.high]
|
var labl = c.contSyms[c.contSyms.high]
|
||||||
add(result, PTransNode(newSymNode(labl)))
|
add(result, PTransNode(newSymNode(labl)))
|
||||||
of nkBreakStmt: result = transformBreak(c, n)
|
of nkBreakStmt: result = transformBreak(c, n)
|
||||||
of nkWhileStmt: result = transformWhile(c, n)
|
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
result = transformCall(c, n)
|
result = transformCall(c, n)
|
||||||
of nkAddr, nkHiddenAddr:
|
of nkAddr, nkHiddenAddr:
|
||||||
|
|
@ -754,8 +787,6 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
result = transformYield(c, n)
|
result = transformYield(c, n)
|
||||||
else:
|
else:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
of nkBlockStmt, nkBlockExpr:
|
|
||||||
result = transformBlock(c, n)
|
|
||||||
of nkIdentDefs, nkConstDef:
|
of nkIdentDefs, nkConstDef:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
# XXX comment handling really sucks:
|
# XXX comment handling really sucks:
|
||||||
|
|
@ -764,6 +795,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
of nkClosure: return PTransNode(n)
|
of nkClosure: return PTransNode(n)
|
||||||
else:
|
else:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
|
when false:
|
||||||
|
if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
|
||||||
var cnst = getConstExpr(c.module, PNode(result))
|
var cnst = getConstExpr(c.module, PNode(result))
|
||||||
# we inline constants if they are not complex constants:
|
# we inline constants if they are not complex constants:
|
||||||
if cnst != nil and not dontInlineConstant(n, cnst):
|
if cnst != nil and not dontInlineConstant(n, cnst):
|
||||||
|
|
@ -785,12 +818,52 @@ proc openTransf(module: PSym, filename: string): PTransf =
|
||||||
result.breakSyms = @[]
|
result.breakSyms = @[]
|
||||||
result.module = module
|
result.module = module
|
||||||
|
|
||||||
|
proc flattenStmts(n: PNode) =
|
||||||
|
var goOn = true
|
||||||
|
while goOn:
|
||||||
|
goOn = false
|
||||||
|
for i in 0..<n.len:
|
||||||
|
let it = n[i]
|
||||||
|
if it.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
|
n.sons[i..i] = it.sons[0..<it.len]
|
||||||
|
goOn = true
|
||||||
|
|
||||||
|
proc liftDeferAux(n: PNode) =
|
||||||
|
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
|
flattenStmts(n)
|
||||||
|
var goOn = true
|
||||||
|
while goOn:
|
||||||
|
goOn = false
|
||||||
|
let last = n.len-1
|
||||||
|
for i in 0..last:
|
||||||
|
if n.sons[i].kind == nkDefer:
|
||||||
|
let deferPart = newNodeI(nkFinally, n.sons[i].info)
|
||||||
|
deferPart.add n.sons[i].sons[0]
|
||||||
|
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
|
||||||
|
var body = newNodeI(nkStmtList, n.sons[i].info)
|
||||||
|
if i < last:
|
||||||
|
body.sons = n.sons[(i+1)..last]
|
||||||
|
tryStmt.addSon(body)
|
||||||
|
tryStmt.addSon(deferPart)
|
||||||
|
n.sons[i] = tryStmt
|
||||||
|
n.sons.setLen(i+1)
|
||||||
|
n.typ = n.sons[i].typ
|
||||||
|
goOn = true
|
||||||
|
break
|
||||||
|
for i in 0..n.safeLen-1:
|
||||||
|
liftDeferAux(n.sons[i])
|
||||||
|
|
||||||
|
template liftDefer(c, root) =
|
||||||
|
if c.deferDetected:
|
||||||
|
liftDeferAux(root)
|
||||||
|
|
||||||
proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
||||||
if nfTransf in n.flags or prc.kind in {skTemplate}:
|
if nfTransf in n.flags or prc.kind in {skTemplate}:
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n, prc)
|
result = processTransf(c, n, prc)
|
||||||
|
liftDefer(c, result)
|
||||||
result = liftLambdas(prc, result)
|
result = liftLambdas(prc, result)
|
||||||
#if prc.kind == skClosureIterator:
|
#if prc.kind == skClosureIterator:
|
||||||
# result = lambdalifting.liftIterator(prc, result)
|
# result = lambdalifting.liftIterator(prc, result)
|
||||||
|
|
@ -805,6 +878,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n, module)
|
result = processTransf(c, n, module)
|
||||||
|
liftDefer(c, result)
|
||||||
result = liftLambdasForTopLevel(module, result)
|
result = liftLambdasForTopLevel(module, result)
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
when useEffectSystem: trackTopLevelStmt(module, result)
|
when useEffectSystem: trackTopLevelStmt(module, result)
|
||||||
|
|
@ -815,4 +889,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n, module)
|
result = processTransf(c, n, module)
|
||||||
|
liftDefer(c, result)
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
|
|
|
||||||
47
tests/destructor/tdestructor3.nim
Normal file
47
tests/destructor/tdestructor3.nim
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
discard """
|
||||||
|
output: '''assign
|
||||||
|
destroy
|
||||||
|
destroy
|
||||||
|
destroy Foo: 5
|
||||||
|
5
|
||||||
|
destroy Foo: 123
|
||||||
|
123'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #2821
|
||||||
|
{.experimental.}
|
||||||
|
|
||||||
|
type T = object
|
||||||
|
|
||||||
|
proc `=`(lhs: var T, rhs: T) =
|
||||||
|
echo "assign"
|
||||||
|
|
||||||
|
proc `=destroy`(v: var T) =
|
||||||
|
echo "destroy"
|
||||||
|
|
||||||
|
block:
|
||||||
|
var v1 : T
|
||||||
|
var v2 : T = v1
|
||||||
|
|
||||||
|
|
||||||
|
# bug #1632
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo = object of RootObj
|
||||||
|
x: int
|
||||||
|
|
||||||
|
proc `=destroy`(a: var Foo) =
|
||||||
|
echo "destroy Foo: " & $a.x
|
||||||
|
|
||||||
|
template toFooPtr(a: int{lit}): ptr Foo =
|
||||||
|
var temp = Foo(x:a)
|
||||||
|
temp.addr
|
||||||
|
|
||||||
|
proc test(a: ptr Foo) =
|
||||||
|
echo a[].x
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
test(toFooPtr(5))
|
||||||
|
test(toFooPtr(123))
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
discard """
|
discard """
|
||||||
output: '''hi
|
output: '''hi
|
||||||
hi'''
|
hi
|
||||||
|
1
|
||||||
|
hi
|
||||||
|
B
|
||||||
|
A'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #1742
|
# bug #1742
|
||||||
|
|
@ -16,3 +20,23 @@ import strutils
|
||||||
let x = try: parseInt("133a")
|
let x = try: parseInt("133a")
|
||||||
except: -1
|
except: -1
|
||||||
finally: echo "hi"
|
finally: echo "hi"
|
||||||
|
|
||||||
|
|
||||||
|
template atFuncEnd =
|
||||||
|
defer:
|
||||||
|
echo "A"
|
||||||
|
defer:
|
||||||
|
echo "B"
|
||||||
|
|
||||||
|
template testB(): expr =
|
||||||
|
let a = 0
|
||||||
|
defer: echo "hi" # Delete this line to make it work
|
||||||
|
a
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
atFuncEnd()
|
||||||
|
echo 1
|
||||||
|
let i = testB()
|
||||||
|
echo 2
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue