diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index b000383f0e2..eb123f27e6f 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1594,17 +1594,27 @@ impl Elaborator<'_> { ) -> (HirExpression, Type) { let expr_location = if_expr.condition.type_location(); let consequence_location = if_expr.consequence.type_location(); + let constant_condition = match &if_expr.condition.kind { + ExpressionKind::Literal(Literal::Bool(value)) => Some(*value), + _ => None, + }; let (condition, cond_type) = self.elaborate_expression(if_expr.condition); - let (consequence, mut ret_type) = - self.elaborate_expression_with_target_type(if_expr.consequence, target_type); + let (consequence, mut ret_type) = self.elaborate_expression_counting_loop_breaks( + if_expr.consequence, + target_type, + constant_condition != Some(false), + ); self.unify_or_type_mismatch(&cond_type, &Type::Bool, expr_location); let (alternative, else_type, error_location) = if let Some(alternative) = if_expr.alternative { let alternative_location = alternative.type_location(); - let (else_, else_type) = - self.elaborate_expression_with_target_type(alternative, target_type); + let (else_, else_type) = self.elaborate_expression_counting_loop_breaks( + alternative, + target_type, + constant_condition != Some(true), + ); (Some(else_), else_type, alternative_location) } else { (None, Type::Unit, consequence_location) @@ -1632,6 +1642,22 @@ impl Elaborator<'_> { (HirExpression::If(if_expr), ret_type) } + fn elaborate_expression_counting_loop_breaks( + &mut self, + expr: Expression, + target_type: Option<&Type>, + count_breaks: bool, + ) -> (ExprId, Type) { + if count_breaks { + self.elaborate_expression_with_target_type(expr, target_type) + } else { + let current_loop = self.current_loop; + let result = self.elaborate_expression_with_target_type(expr, target_type); + self.current_loop = current_loop; + result + } + } + /// Elaborate a `match { }` expression by creating an block such as this: /// ```text /// { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index c345160ec13..165dabb5265 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -195,6 +195,7 @@ enum UnsafeBlockStatus { InUnsafeBlockWithUnconstrainedCalls, } +#[derive(Clone, Copy)] pub struct Loop { pub is_for: bool, pub has_break: bool, diff --git a/compiler/noirc_frontend/src/tests/control_flow.rs b/compiler/noirc_frontend/src/tests/control_flow.rs index 76308fe5b9a..68c3424a6a8 100644 --- a/compiler/noirc_frontend/src/tests/control_flow.rs +++ b/compiler/noirc_frontend/src/tests/control_flow.rs @@ -367,7 +367,7 @@ fn errors_if_loop_body_type_is_not_unit() { let src = r#" unconstrained fn main() { loop { - if false { break; } + if true { break; } 1 ^ Expected type (), found type Field @@ -377,6 +377,40 @@ fn errors_if_loop_body_type_is_not_unit() { check_errors(src); } +#[test] +fn errors_on_loop_with_break_only_in_false_if() { + let src = r#" + unconstrained fn main() { + loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed + if false { + break; + } + } + } + "#; + check_errors(src); +} + +#[test] +fn errors_on_loop_with_break_only_in_true_if_alternative() { + let src = r#" + unconstrained fn main() { + loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed + if true { + continue; + } else { + break; + } + } + } + "#; + check_errors(src); +} + #[test] fn errors_if_while_body_type_is_not_unit() { let src = r#"