diff options
author | Araq <rumpf_a@web.de> | 2012-05-20 10:56:17 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2012-05-20 10:56:17 +0200 |
commit | 15dd3a225134afab649a3c1994cbd862f1b11439 (patch) | |
tree | ec3d43cd939b7ab91f25c7b5ac57981967dcbf8f | |
parent | e6019b6f98e3e16205f0b9775437352da9324bc6 (diff) | |
download | Nim-15dd3a225134afab649a3c1994cbd862f1b11439.tar.gz |
fixes #109
-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 | ||||
-rw-r--r-- | doc/gc.txt | 1 | ||||
-rwxr-xr-x | lib/pure/strutils.nim | 4 | ||||
-rw-r--r-- | tests/run/tcontinue.nim | 28 | ||||
-rwxr-xr-x | todo.txt | 2 |
9 files changed, 95 insertions, 16 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 diff --git a/doc/gc.txt b/doc/gc.txt index b29d6caa5..dfcfa07f3 100644 --- a/doc/gc.txt +++ b/doc/gc.txt @@ -5,6 +5,7 @@ Nimrod's Garbage Collector :Author: Andreas Rumpf :Version: |nimrodversion| + "The road to hell is paved with good intentions." Introduction ============ diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index f2c1c100e..aaff0d151 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -947,7 +947,9 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, ## If ``format == ffScientific`` then precision is the maximum number ## of significant digits to be printed. ## `precision`'s default value is the maximum number of meaningful digits - ## after the decimal point for Nimrod's ``biggestFloat`` type. + ## after the decimal point for Nimrod's ``biggestFloat`` type. + ## + ## If ``precision == 0``, it uses the const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e'] var frmtstr {.noinit.}: array[0..5, char] diff --git a/tests/run/tcontinue.nim b/tests/run/tcontinue.nim new file mode 100644 index 000000000..092026e8c --- /dev/null +++ b/tests/run/tcontinue.nim @@ -0,0 +1,28 @@ +discard """ + output: "came here" +""" + +var i = 0 +while i < 400: + + if i == 10: break + elif i == 3: + inc i + continue + inc i + +var f = "failure" +var j = 0 +while j < 300: + for x in 0..34: + if j < 300: continue + if x == 10: + echo "failure: should never happen" + break + f = "came here" + break + +if i == 10: + echo f +else: + echo "failure" diff --git a/todo.txt b/todo.txt index 4561be577..6267e3497 100755 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,7 @@ version 0.9.0 ============= -- fix transformLoopBody +- make stack traces part of the exception in debug builds - make templates hygienic by default - ``bind`` for overloaded symbols does not work apparently - ``=`` should be overloadable; requires specialization for ``=`` |