diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index adac10f548e..dc9955e0465 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -228,7 +228,7 @@ impl Elaborator<'_> { } if i + 1 == statements.len() { - block_type = if is_break_or_continue { Type::Unit } else { stmt_type }; + block_type = stmt_type; } } diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 550f71fd115..271fba8400f 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -210,8 +210,6 @@ impl Elaborator<'_> { self.current_loop = Some(Loop { is_for: true, has_break: false }); self.push_scope(); - // TODO: For loop variables are currently mutable by default since we haven't - // yet implemented syntax for them to be optionally mutable. let kind = DefinitionKind::Local(None); let identifier = self.add_variable_decl( identifier, false, // mutable @@ -346,7 +344,7 @@ impl Elaborator<'_> { } let expr = if is_break { HirStatement::Break } else { HirStatement::Continue }; - (expr, self.interner.next_type_variable()) + (expr, Type::Unit) } fn get_lvalue_error_info(&self, lvalue: &HirLValue) -> (DefinitionId, String, Location) { diff --git a/compiler/noirc_frontend/src/tests/control_flow.rs b/compiler/noirc_frontend/src/tests/control_flow.rs index 00642d38fae..803352f5d34 100644 --- a/compiler/noirc_frontend/src/tests/control_flow.rs +++ b/compiler/noirc_frontend/src/tests/control_flow.rs @@ -24,6 +24,62 @@ fn resolve_for_expr_incl() { assert_no_errors(src); } +#[test] +fn for_loop_empty_range() { + let src = r#" + fn main() { + let mut x = 0; + for _i in 0..0 { + x = 1; + } + assert(x == 0); + } + "#; + assert_no_errors(src); +} + +#[test] +fn for_loop_backwards_range() { + let src = r#" + fn main() { + let mut x = 0; + for _i in 10..5 { + x = 1; + } + assert(x == 0); + } + "#; + assert_no_errors(src); +} + +#[test] +fn for_loop_single_elem_inclusive_max_value() { + let src = r#" + fn main() { + let mut count = 0; + for i in 4294967295..=4294967295 { + count += 1; + let _x: u32 = i; + } + assert(count == 1); + } + "#; + assert_no_errors(src); +} + +#[test] +fn for_loop_mutate_induction_var() { + let src = r#" + fn main() { + for i in 0..10 { + i = 5; + ^ Variable `i` must be mutable to be assigned to + } + } + "#; + check_errors(src); +} + #[test] fn break_and_continue_outside_loop() { let src = r#" @@ -138,6 +194,56 @@ fn errors_on_loop_without_break_with_nested_loop() { check_errors(src); } +#[test] +fn break_in_nested_and_outer_loops() { + let src = r#" + unconstrained fn main() { + let mut x = 1; + loop { + x += 1; + loop { + x += 2; + break; // Breaks from nested loop only + } + if x > 2 { + break; // Breaks from outer loop + } + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn continue_in_loop() { + let src = r#" + unconstrained fn main() { + let mut x = 0; + loop { + x += 1; + if x < 5 { + continue; + } + break; + } + + for i in 0..10 { + if i == 5 { + continue; + } + } + + while x > 0 { + x -= 1; + if x == 3 { + continue; + } + } + } + "#; + assert_no_errors(src); +} + #[test] fn errors_if_for_body_type_is_not_unit() { let src = r#" @@ -190,3 +296,37 @@ fn overflowing_int_in_for_loop() { "#; check_errors(src); } + +#[test] +fn break_type_mismatch() { + let src = r#" + unconstrained fn main() { + loop { + if true { + break; + } else { + 5 + ^ Expected type (), found type Field + }; + } + } + "#; + check_errors(src); +} + +#[test] +fn continue_type_mismatch() { + let src = r#" + unconstrained fn main() { + for _ in 0..1 { + if true { + continue; + } else { + 5 + ^ Expected type (), found type Field + } + } + } + "#; + check_errors(src); +}