Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-6844

"Break label" instruction broken when combining nested loops with "old-school" labeled code blocks

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 2.3.2
    • None
    • Compiler
    • None

    Description

      I've seen in groovy the break statement with named label is only allowed inside loops, in fact executing the following snippet from groovyConsole

      foo: {
          println "foo"
          break foo
      }
      

      I get this output

      2 compilation errors:

      the break statement with named label is only allowed inside loops
      at line: 5, column: 13

      break to missing label
      at line: 5, column: 13

      Bu this morning a colleague of mine discovered that if you combine old-school label definition with a break label used within two nested loops you obtain no compile/runtime errors. Instead the executed logic is not the expected one, at least not what you would expect when you write that kind of code.
      It seems that the break label instruction simply breaks the innermost loop execution, continuing with the outer one (hence ignoring the label).

      Follows a test based on the example took from http://docs.codehaus.org/display/GROOVY/JN2535-Control:
      Its output shows how the labeled code block is re-executed after the break

      Foo.groovy
      def i=0, j=0
      outer: {
        println "executing outer block"
        while( i<5 ){ //labelling a while loop is especially useful...
          j= 0
          i++
        println "executing outer while, i: $i, j: $j"
          while( j<5 ){
            j++
        println "executing inner while, i: $i, j: $j"
            if( i==3 && j==2 ) break outer
                  //...because we can break out of a specified labelled while loop
          }
        }
      }
      assert i==3 && j==2
      

      This is its execution output:

      executing outer block
      executing outer while, i: 1, j: 0
      executing inner while, i: 1, j: 1
      executing inner while, i: 1, j: 2
      executing inner while, i: 1, j: 3
      executing inner while, i: 1, j: 4
      executing inner while, i: 1, j: 5
      executing outer while, i: 2, j: 0
      executing inner while, i: 2, j: 1
      executing inner while, i: 2, j: 2
      executing inner while, i: 2, j: 3
      executing inner while, i: 2, j: 4
      executing inner while, i: 2, j: 5
      executing outer while, i: 3, j: 0
      executing inner while, i: 3, j: 1
      executing inner while, i: 3, j: 2
      executing outer while, i: 4, j: 0
      executing inner while, i: 4, j: 1
      executing inner while, i: 4, j: 2
      executing inner while, i: 4, j: 3
      executing inner while, i: 4, j: 4
      executing inner while, i: 4, j: 5
      executing outer while, i: 5, j: 0
      executing inner while, i: 5, j: 1
      executing inner while, i: 5, j: 2
      executing inner while, i: 5, j: 3
      executing inner while, i: 5, j: 4
      executing inner while, i: 5, j: 5
      Exception in thread "main" Assertion failed:

      assert i==3 && j==2

       

      5| false
      false

      at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:398)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:646)
      at Bar.main(Bar.groovy:27)

      Follows the java counterpart, whose output shows how the labeled code block is not executed after the break

      Foo.java
      public class Foo {
          public static void main(String[] args) {
              int i = 0, j = 0;
              outer: {
                  println("executing outer block");
                  while (i < 5) { // labelling a while loop is especially useful...
                      j = 0;
                      i++;
                      println("executing outer while, i: " + i + ", j: " + j);
                      while (j < 5) {
                          j++;
                          println("executing inner while, i: " + i + ", j: " + j);
                          if (i == 3 && j == 2)
                              break outer;
                          // ...because we can break out of a specified labelled while loop
                      }
                  }
              }
              assert i == 3 && j == 2;
          }
      
          static void println(String s) {
              System.out.println(s);
          }
      }
      

      And this is its execution output

      executing outer block
      executing outer while, i: 1, j: 0
      executing inner while, i: 1, j: 1
      executing inner while, i: 1, j: 2
      executing inner while, i: 1, j: 3
      executing inner while, i: 1, j: 4
      executing inner while, i: 1, j: 5
      executing outer while, i: 2, j: 0
      executing inner while, i: 2, j: 1
      executing inner while, i: 2, j: 2
      executing inner while, i: 2, j: 3
      executing inner while, i: 2, j: 4
      executing inner while, i: 2, j: 5
      executing outer while, i: 3, j: 0
      executing inner while, i: 3, j: 1
      executing inner while, i: 3, j: 2


      (even running it with assertions enabled through the -ea switch)

      Attachments

        Activity

          People

            Unassigned Unassigned
            davide.cavestro Davide Cavestro
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated: