E3064

E3064 — A break or continue with no loop around it in its own function or closure. A closure body is not inside a loop around the closure, because the closure can run after the loop ends.

What the compiler reports

Error[E3064] at {{.*}}.vx:[[@LINE+1]]:5: a `break` must be inside a loop{{$}}
Error[E3064] at {{.*}}.vx:[[@LINE+1]]:5: a `continue` must be inside a loop{{$}}
Error[E3064] at {{.*}}.vx:[[@LINE+1]]:9: a `continue` must be inside a loop. A closure body is not inside the loop around the closure{{$}}
Error[E3064] at {{.*}}.vx:[[@LINE+1]]:7: a `break` must be inside a loop{{$}}
Error[E3064] at {{.*}}.vx:[[@LINE+1]]:5: a `break` must be inside a loop{{$}}
Error[E3064] at {{.*}}.vx:[[@LINE+1]]:5: a `continue` must be inside a loop{{$}}

The fragments the test suite holds the compiler to. An ellipsis marks text the test does not constrain; the emitted message also carries a source location.

A program that triggers it

//
// Part of the Vx Project, under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// A `break` or `continue` must be inside a loop of its own function or closure. A closure body
// and a function body are not inside a loop around the closure or around the call.
//

fn break_with_no_loop(n : i64) -> i64 {
  if n > 0i64 {
    break;
  }
  return n;
}

fn continue_with_no_loop(n : i64) -> i64 {
  if n > 0i64 {
    continue;
  }
  return n;
}

fn continue_in_a_closure_in_a_loop() -> i64 {
  let mut total = 0i64;
  for i in 0i64..3i64 {
    let f = | x : i64 | -> i64 {
      if x > 1i64 {
        continue;
      }
      return x;
    };
    total = total + f(i);
  }
  return total;
}

fn break_in_a_closure_with_no_loop(n : i64) -> i64 {
  let f = | x : i64 | -> i64 {
    if x > 1i64 {
      break;
    }
    return x;
  };
  return f(n);
}

// A loop inside the closure is a loop of the closure, so this `break` is correct.
fn break_in_a_loop_of_a_closure() -> i64 {
  let mut total = 0i64;
  for i in 0i64..3i64 {
    let f = | x : i64 | -> i64 {
      let mut j = 0i64;
      loop {
        if j >= x {
          break;
        }
        j = j + 1i64;
      }
      return j;
    };
    total = total + f(i);
  }
  return total;
}

// The checker checks a generic function at its first call, here inside a loop. That loop is
// not around the `break`.
fn first<T>(x : T, stop : bool) -> T {
  if stop {
    break;
  }
  return x;
}

fn calls_first() -> i64 {
  let mut total = 0i64;
  for i in 0i64..3i64 {
    total = total + first(i, i > 1i64);
  }
  return total;
}

// Here the first call is in a closure inside a loop. The `continue` is in neither of them.
fn second<T>(x : T, stop : bool) -> T {
  if stop {
    continue;
  }
  return x;
}

fn calls_second() -> i64 {
  let mut total = 0i64;
  for i in 0i64..3i64 {
    let f = | x : i64 | -> i64 {
      return second(x, x > 1i64);
    };
    total = total + f(i);
  }
  return total;
}

fn main() -> i32 {
  return 0;
}

From tests/frontend/fail/break_and_continue_outside_a_loop.vx, which asserts this diagnostic on every commit.

Related