diff options
Diffstat (limited to 'compiler')
-rwxr-xr-x | compiler/ccgstmts.nim | 6 | ||||
-rw-r--r-- | compiler/cgendata.nim | 1 | ||||
-rwxr-xr-x | compiler/ecmasgen.nim | 9 | ||||
-rwxr-xr-x | compiler/evals.nim | 9 | ||||
-rwxr-xr-x | compiler/transf.nim | 51 |
5 files changed, 62 insertions, 14 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 9fd27bfeb..db6d5bd67 100755 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -235,6 +235,7 @@ proc genWhileStmt(p: BProc, t: PNode) = preserveBreakIdx: p.breakIdx = startBlock(p, "while (1) {$n") + p.blocks[p.breakIdx].isLoop = true initLocExpr(p, t.sons[0], a) if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0): let label = assignLabel(p.blocks[p.breakIdx]) @@ -265,6 +266,11 @@ proc genBreakStmt(p: BProc, t: PNode) = var sym = t.sons[0].sym assert(sym.loc.k == locOther) idx = sym.loc.a + else: + # an unnamed 'break' can only break a loop after 'transf' pass: + while idx >= 0 and not p.blocks[idx].isLoop: dec idx + if idx < 0 or not p.blocks[idx].isLoop: + InternalError(t.info, "no loop to break") let label = assignLabel(p.blocks[idx]) blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts) genLineDir(p, t) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index f1463bbdc..a22c13f3a 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -54,6 +54,7 @@ type # nil if label is not used nestedTryStmts*: int # how many try statements is it nested into sections*: TCProcSections # the code beloging + isLoop*: bool # whether block is a loop TCProc{.final.} = object # represents C proc that is currently generated prc*: PSym # the Nimrod proc that this C proc belongs to diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index 6499f7a6f..3ab4303c2 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -47,6 +47,7 @@ type id*: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) nestedTryStmts*: int # how many try statements is it nested into + isLoop: bool # whether it's a 'block' or 'while' TGlobals{.final.} = object typeInfo*, code*: PRope @@ -467,6 +468,7 @@ proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = setlen(p.blocks, length + 1) p.blocks[length].id = - p.unique p.blocks[length].nestedTryStmts = p.nestedTryStmts + p.blocks[length].isLoop = true labl = p.unique gen(p, n.sons[0], cond) genStmt(p, n.sons[1], stmt) @@ -635,13 +637,18 @@ proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) = idx: int sym: PSym genLineDir(p, n, r) - idx = len(p.blocks) - 1 if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) sym = n.sons[0].sym assert(sym.loc.k == locOther) idx = sym.loc.a + else: + # an unnamed 'break' can only break a loop after 'transf' pass: + idx = len(p.blocks) - 1 + while idx >= 0 and not p.blocks[idx].isLoop: dec idx + if idx < 0 or not p.blocks[idx].isLoop: + InternalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts) appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)]) diff --git a/compiler/evals.nim b/compiler/evals.nim index 92726ae31..a4d4cdd9e 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -162,16 +162,17 @@ proc evalWhile(c: PEvalContext, n: PNode): PNode = stackTrace(c, n, errTooManyIterations) break -proc evalBlock(c: PEvalContext, n: PNode): PNode = +proc evalBlock(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) - if result.kind == nkBreakStmt: + if result.kind == nkBreakStmt: if result.sons[0] != nil: assert(result.sons[0].kind == nkSym) if n.sons[0].kind != nkEmpty: assert(n.sons[0].kind == nkSym) if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode - else: - result = emptyNode # consume ``break`` token + # blocks can only be left with an explicit label now! + #else: + # result = emptyNode # consume ``break`` token proc evalFinally(c: PEvalContext, n, exc: PNode): PNode = var finallyNode = lastSon(n) diff --git a/compiler/transf.nim b/compiler/transf.nim index 569210f2c..9e0d4051f 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -46,7 +46,8 @@ type transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc - blocksyms: seq[PSym] + contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' + inLoop: int # > 0 if we are in a loop transformedInnerProcs: TIntSet PTransf = ref TTransfContext @@ -252,6 +253,31 @@ proc hasContinue(n: PNode): bool = for i in countup(0, sonsLen(n) - 1): if hasContinue(n.sons[i]): return true +proc newLabel(c: PTransf, n: PNode): PSym = + result = newSym(skLabel, nil, getCurrOwner(c)) + result.name = getIdent(genPrefix & $result.id) + result.info = n.info + +proc transformBlock(c: PTransf, n: PNode): PTransNode = + var labl: PSym + if n.sons[0].kind != nkEmpty: + # already named block? -> Push symbol on the stack: + labl = n.sons[0].sym + else: + labl = newLabel(c, n) + c.breakSyms.add(labl) + result = transformSons(c, n) + discard c.breakSyms.pop + result[0] = newSymNode(labl).PTransNode + +proc transformBreak(c: PTransf, n: PNode): PTransNode = + if c.inLoop > 0 or n.sons[0].kind != nkEmpty: + result = n.ptransNode + else: + let labl = c.breakSyms[c.breakSyms.high] + result = transformSons(c, n) + result[0] = newSymNode(labl).PTransNode + proc transformLoopBody(c: PTransf, n: PNode): PTransNode = # XXX BUG: What if it contains "continue" and "break"? "break" needs # an explicit label too, but not the same! @@ -260,15 +286,13 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode = # and changing all breaks that belong to a 'block' by annotating it with # a label (if it hasn't one already). if hasContinue(n): - var labl = newSym(skLabel, nil, getCurrOwner(c)) - labl.name = getIdent(genPrefix & $labl.id) - labl.info = n.info - c.blockSyms.add(labl) + let labl = newLabel(c, n) + c.contSyms.add(labl) result = newTransNode(nkBlockStmt, n.info, 2) result[0] = newSymNode(labl).PTransNode result[1] = transform(c, n) - discard c.blockSyms.pop() + discard c.contSyms.pop() else: result = transform(c, n) @@ -631,16 +655,22 @@ proc transform(c: PTransf, n: PNode): PTransNode = n.sons[bodyPos] = PNode(transform(c, s.getBody)) if n.kind == nkMethodDef: methodDef(s, false) result = PTransNode(n) - of nkForStmt: result = transformFor(c, n) + of nkForStmt: + inc c.inLoop + result = transformFor(c, n) + dec c.inLoop of nkCaseStmt: result = transformCase(c, n) of nkContinueStmt: result = PTransNode(newNode(nkBreakStmt)) - var labl = c.blockSyms[c.blockSyms.high] + var labl = c.contSyms[c.contSyms.high] add(result, PTransNode(newSymNode(labl))) + of nkBreakStmt: result = transformBreak(c, n) of nkWhileStmt: + inc c.inLoop result = newTransNode(n) result[0] = transform(c, n.sons[0]) result[1] = transformLoopBody(c, n.sons[1]) + dec c.inLoop of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, nkCallStrLit: result = transformCall(c, n) @@ -671,6 +701,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformYield(c, n) else: result = transformSons(c, n) + of nkBlockStmt, nkBlockExpr: + result = transformBlock(c, n) else: result = transformSons(c, n) var cnst = getConstExpr(c.module, PNode(result)) @@ -692,7 +724,8 @@ proc processTransf(context: PPassContext, n: PNode): PNode = proc openTransf(module: PSym, filename: string): PPassContext = var n: PTransf new(n) - n.blocksyms = @[] + n.contSyms = @[] + n.breakSyms = @[] n.module = module n.transformedInnerProcs = initIntSet() result = n |