summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/ccgstmts.nim6
-rw-r--r--compiler/cgendata.nim1
-rwxr-xr-xcompiler/ecmasgen.nim9
-rwxr-xr-xcompiler/evals.nim9
-rwxr-xr-xcompiler/transf.nim51
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