diff options
Diffstat (limited to 'compiler/semstmts.nim')
-rwxr-xr-x | compiler/semstmts.nim | 106 |
1 files changed, 98 insertions, 8 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 202b7416a..82f43e787 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -27,7 +27,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = case it.kind of nkElifBranch, nkElifExpr: checkSonsLen(it, 2) - var e = semAndEvalConstExpr(c, it.sons[0]) + var e = semConstExpr(c, it.sons[0]) if e.kind != nkIntLit: InternalError(n.info, "semWhen") if e.intVal != 0 and result == nil: setResult(it.sons[1]) @@ -772,6 +772,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, incl(s.flags, sfForward) elif sfBorrow in s.flags: semBorrow(c, n, 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: localError(s.info, errXCannotBeClosure, s.name.s) closeScope(c.tab) # close scope for parameters @@ -860,6 +864,62 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = if result.isNil: 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 = const # must be last statements in a block: LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} @@ -875,13 +935,43 @@ proc SemStmt(c: PContext, n: PNode): PNode = of nkBlockStmt: result = semBlock(c, n) of nkStmtList: var length = sonsLen(n) - for i in countup(0, length - 1): - n.sons[i] = semStmt(c, n.sons[i]) - if n.sons[i].kind in LastBlockStmts: - for j in countup(i + 1, length - 1): - case n.sons[j].kind - of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil - else: localError(n.sons[j].info, errStmtInvalidAfterReturn) + for i in countup(0, length - 1): + case n.sons[i].kind + of nkFinally, nkExceptBranch: + # stand-alone finally and except blocks are + # transformed into regular try blocks: + # + # var f = fopen("somefile") | var f = fopen("somefile") + # finally: fcsole(f) | try: + # ... | ... + # | finally: + # | fclose(f) + var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) + var body = newNodeI(nkStmtList, n.sons[i].info) + if i < n.sonsLen - 1: + body.sons = n.sons[(i+1)..(-1)] + tryStmt.addSon(body) + tryStmt.addSon(n.sons[i]) + n.sons[i] = semTry(c, tryStmt) + n.sons.setLen(i+1) + return + else: + n.sons[i] = semStmt(c, n.sons[i]) + 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): + case n.sons[j].kind + of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil + else: localError(n.sons[j].info, errStmtInvalidAfterReturn) + else: nil of nkRaiseStmt: result = semRaise(c, n) of nkVarSection: result = semVarOrLet(c, n, skVar) of nkLetSection: result = semVarOrLet(c, n, skLet) |