diff options
-rw-r--r-- | compiler/semdestruct.nim | 9 | ||||
-rw-r--r-- | compiler/semexprs.nim | 5 | ||||
-rw-r--r-- | compiler/semstmts.nim | 20 | ||||
-rw-r--r-- | compiler/transf.nim | 83 | ||||
-rw-r--r-- | tests/destructor/tdestructor3.nim | 47 | ||||
-rw-r--r-- | tests/exception/tdefer1.nim | 26 | ||||
-rw-r--r-- | tests/generics/tdictdestruct.nim (renamed from tests/destructor/tdictdestruct.nim) | 0 |
7 files changed, 175 insertions, 15 deletions
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index aaab49a10..af671f6e0 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -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. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b4308def3..af6919d97 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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})) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 84a09a7e6..b9b175962 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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 diff --git a/compiler/transf.nim b/compiler/transf.nim index dddbd51c4..2ac5e1f39 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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) diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim new file mode 100644 index 000000000..0968f1fd7 --- /dev/null +++ b/tests/destructor/tdestructor3.nim @@ -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() diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim index 61439530a..d7c691713 100644 --- a/tests/exception/tdefer1.nim +++ b/tests/exception/tdefer1.nim @@ -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() diff --git a/tests/destructor/tdictdestruct.nim b/tests/generics/tdictdestruct.nim index 17ded4853..17ded4853 100644 --- a/tests/destructor/tdictdestruct.nim +++ b/tests/generics/tdictdestruct.nim |