mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-03 18:34:43 +00:00
This commit is contained in:
@@ -177,6 +177,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
|
||||
else:
|
||||
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,
|
||||
varSection: PNode): tuple[outer, inner: PNode] =
|
||||
# Accepts a var or let section.
|
||||
|
||||
@@ -2259,7 +2259,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkStaticStmt:
|
||||
result = semStaticStmt(c, n)
|
||||
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:
|
||||
localError(n.info, errInvalidExpressionX,
|
||||
renderTree(n, {renderNoComments}))
|
||||
|
||||
@@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||
else:
|
||||
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 =
|
||||
if v.name.s == "_":
|
||||
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]
|
||||
v.typ = tup.sons[j]
|
||||
b.sons[j] = newSymNode(v)
|
||||
addDefer(c, result, v)
|
||||
checkNilable(v)
|
||||
if sfCompileTime in v.flags: hasCompileTime = true
|
||||
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):
|
||||
let k = n.sons[i].kind
|
||||
case k
|
||||
of nkFinally, nkExceptBranch, nkDefer:
|
||||
of nkFinally, nkExceptBranch:
|
||||
# stand-alone finally and except blocks are
|
||||
# transformed into regular try blocks:
|
||||
#
|
||||
@@ -1424,14 +1434,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
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:
|
||||
for j in countup(i + 1, length - 1):
|
||||
case n.sons[j].kind
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# * converts "continue" to "break"; disambiguates "break"
|
||||
# * introduces method dispatchers
|
||||
# * performs lambda lifting for closure support
|
||||
# * transforms 'defer' into a 'try finally' statement
|
||||
|
||||
import
|
||||
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)
|
||||
nestedProcs: int # > 0 if we are in a nested proc
|
||||
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
||||
deferDetected: bool
|
||||
PTransf = ref TTransfContext
|
||||
|
||||
proc newTransNode(a: PNode): PTransNode {.inline.} =
|
||||
@@ -680,6 +682,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
|
||||
result = n
|
||||
|
||||
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
|
||||
of nkSym:
|
||||
result = transformSym(c, n)
|
||||
@@ -712,13 +722,36 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
result = transformFor(c, n)
|
||||
of nkParForStmt:
|
||||
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:
|
||||
result = PTransNode(newNodeI(nkBreakStmt, n.info))
|
||||
var labl = c.contSyms[c.contSyms.high]
|
||||
add(result, PTransNode(newSymNode(labl)))
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkWhileStmt: result = transformWhile(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkAddr, nkHiddenAddr:
|
||||
@@ -754,8 +787,6 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
result = transformYield(c, n)
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
of nkBlockStmt, nkBlockExpr:
|
||||
result = transformBlock(c, n)
|
||||
of nkIdentDefs, nkConstDef:
|
||||
result = transformSons(c, n)
|
||||
# XXX comment handling really sucks:
|
||||
@@ -764,6 +795,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
of nkClosure: return PTransNode(n)
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
when false:
|
||||
if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
|
||||
var cnst = getConstExpr(c.module, PNode(result))
|
||||
# we inline constants if they are not complex constants:
|
||||
if cnst != nil and not dontInlineConstant(n, cnst):
|
||||
@@ -785,12 +818,52 @@ proc openTransf(module: PSym, filename: string): PTransf =
|
||||
result.breakSyms = @[]
|
||||
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 =
|
||||
if nfTransf in n.flags or prc.kind in {skTemplate}:
|
||||
result = n
|
||||
else:
|
||||
var c = openTransf(module, "")
|
||||
result = processTransf(c, n, prc)
|
||||
liftDefer(c, result)
|
||||
result = liftLambdas(prc, result)
|
||||
#if prc.kind == skClosureIterator:
|
||||
# result = lambdalifting.liftIterator(prc, result)
|
||||
@@ -805,6 +878,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
|
||||
else:
|
||||
var c = openTransf(module, "")
|
||||
result = processTransf(c, n, module)
|
||||
liftDefer(c, result)
|
||||
result = liftLambdasForTopLevel(module, result)
|
||||
incl(result.flags, nfTransf)
|
||||
when useEffectSystem: trackTopLevelStmt(module, result)
|
||||
@@ -815,4 +889,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
|
||||
else:
|
||||
var c = openTransf(module, "")
|
||||
result = processTransf(c, n, module)
|
||||
liftDefer(c, result)
|
||||
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 """
|
||||
output: '''hi
|
||||
hi'''
|
||||
hi
|
||||
1
|
||||
hi
|
||||
B
|
||||
A'''
|
||||
"""
|
||||
|
||||
# bug #1742
|
||||
@@ -16,3 +20,23 @@ import strutils
|
||||
let x = try: parseInt("133a")
|
||||
except: -1
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user