Skip to content

Commit 0e266eb

Browse files
author
jfecher
authored
fix!: Make for loops a statement (#2975)
1 parent 982380e commit 0e266eb

File tree

11 files changed

+245
-230
lines changed

11 files changed

+245
-230
lines changed

compiler/noirc_frontend/src/ast/expression.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ pub enum ExpressionKind {
2222
MemberAccess(Box<MemberAccessExpression>),
2323
Cast(Box<CastExpression>),
2424
Infix(Box<InfixExpression>),
25-
For(Box<ForExpression>),
2625
If(Box<IfExpression>),
2726
Variable(Path),
2827
Tuple(Vec<Expression>),
@@ -181,14 +180,6 @@ impl Expression {
181180
}
182181
}
183182

184-
#[derive(Debug, PartialEq, Eq, Clone)]
185-
pub struct ForExpression {
186-
pub identifier: Ident,
187-
pub start_range: Expression,
188-
pub end_range: Expression,
189-
pub block: Expression,
190-
}
191-
192183
pub type BinaryOp = Spanned<BinaryOpKind>;
193184

194185
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)]
@@ -469,7 +460,6 @@ impl Display for ExpressionKind {
469460
MethodCall(call) => call.fmt(f),
470461
Cast(cast) => cast.fmt(f),
471462
Infix(infix) => infix.fmt(f),
472-
For(for_loop) => for_loop.fmt(f),
473463
If(if_expr) => if_expr.fmt(f),
474464
Variable(path) => path.fmt(f),
475465
Constructor(constructor) => constructor.fmt(f),
@@ -603,16 +593,6 @@ impl Display for BinaryOpKind {
603593
}
604594
}
605595

606-
impl Display for ForExpression {
607-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
608-
write!(
609-
f,
610-
"for {} in {} .. {} {}",
611-
self.identifier, self.start_range, self.end_range, self.block
612-
)
613-
}
614-
}
615-
616596
impl Display for IfExpression {
617597
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
618598
write!(f, "if {} {}", self.condition, self.consequence)?;

compiler/noirc_frontend/src/ast/statement.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub enum Statement {
2323
Constrain(ConstrainStatement),
2424
Expression(Expression),
2525
Assign(AssignStatement),
26+
For(ForLoopStatement),
2627
// This is an expression with a trailing semi-colon
2728
Semi(Expression),
2829
// This statement is the result of a recovered parse error.
@@ -65,13 +66,13 @@ impl Statement {
6566
}
6667
self
6768
}
69+
// A semicolon on a for loop is optional and does nothing
70+
Statement::For(_) => self,
6871

6972
Statement::Expression(expr) => {
7073
match (&expr.kind, semi, last_statement_in_block) {
7174
// Semicolons are optional for these expressions
72-
(ExpressionKind::Block(_), semi, _)
73-
| (ExpressionKind::For(_), semi, _)
74-
| (ExpressionKind::If(_), semi, _) => {
75+
(ExpressionKind::Block(_), semi, _) | (ExpressionKind::If(_), semi, _) => {
7576
if semi.is_some() {
7677
Statement::Semi(expr)
7778
} else {
@@ -459,13 +460,22 @@ impl LValue {
459460
}
460461
}
461462

463+
#[derive(Debug, PartialEq, Eq, Clone)]
464+
pub struct ForLoopStatement {
465+
pub identifier: Ident,
466+
pub start_range: Expression,
467+
pub end_range: Expression,
468+
pub block: Expression,
469+
}
470+
462471
impl Display for Statement {
463472
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464473
match self {
465474
Statement::Let(let_statement) => let_statement.fmt(f),
466475
Statement::Constrain(constrain) => constrain.fmt(f),
467476
Statement::Expression(expression) => expression.fmt(f),
468477
Statement::Assign(assign) => assign.fmt(f),
478+
Statement::For(for_loop) => for_loop.fmt(f),
469479
Statement::Semi(semi) => write!(f, "{semi};"),
470480
Statement::Error => write!(f, "Error"),
471481
}
@@ -544,3 +554,13 @@ impl Display for Pattern {
544554
}
545555
}
546556
}
557+
558+
impl Display for ForLoopStatement {
559+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
560+
write!(
561+
f,
562+
"for {} in {} .. {} {}",
563+
self.identifier, self.start_range, self.end_range, self.block
564+
)
565+
}
566+
}

compiler/noirc_frontend/src/hir/resolution/resolver.rs

Lines changed: 35 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
// XXX: Resolver does not check for unused functions
1414
use crate::hir_def::expr::{
1515
HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCapturedVar,
16-
HirCastExpression, HirConstructorExpression, HirExpression, HirForExpression, HirIdent,
17-
HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral,
18-
HirMemberAccess, HirMethodCallExpression, HirPrefixExpression,
16+
HirCastExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression,
17+
HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess,
18+
HirMethodCallExpression, HirPrefixExpression,
1919
};
2020

2121
use crate::hir_def::traits::{Trait, TraitConstraint};
@@ -26,7 +26,7 @@ use std::rc::Rc;
2626

2727
use crate::graph::CrateId;
2828
use crate::hir::def_map::{LocalModuleId, ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION};
29-
use crate::hir_def::stmt::{HirAssignStatement, HirLValue, HirPattern};
29+
use crate::hir_def::stmt::{HirAssignStatement, HirForStatement, HirLValue, HirPattern};
3030
use crate::node_interner::{
3131
DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, StructId, TraitId,
3232
};
@@ -957,6 +957,25 @@ impl<'a> Resolver<'a> {
957957
let stmt = HirAssignStatement { lvalue: identifier, expression };
958958
HirStatement::Assign(stmt)
959959
}
960+
Statement::For(for_loop) => {
961+
let start_range = self.resolve_expression(for_loop.start_range);
962+
let end_range = self.resolve_expression(for_loop.end_range);
963+
let (identifier, block) = (for_loop.identifier, for_loop.block);
964+
965+
// TODO: For loop variables are currently mutable by default since we haven't
966+
// yet implemented syntax for them to be optionally mutable.
967+
let (identifier, block) = self.in_new_scope(|this| {
968+
let decl = this.add_variable_decl(
969+
identifier,
970+
false,
971+
true,
972+
DefinitionKind::Local(None),
973+
);
974+
(decl, this.resolve_expression(block))
975+
});
976+
977+
HirStatement::For(HirForStatement { start_range, end_range, block, identifier })
978+
}
960979
Statement::Error => HirStatement::Error,
961980
}
962981
}
@@ -1169,30 +1188,6 @@ impl<'a> Resolver<'a> {
11691188
lhs: self.resolve_expression(cast_expr.lhs),
11701189
r#type: self.resolve_type(cast_expr.r#type),
11711190
}),
1172-
ExpressionKind::For(for_expr) => {
1173-
let start_range = self.resolve_expression(for_expr.start_range);
1174-
let end_range = self.resolve_expression(for_expr.end_range);
1175-
let (identifier, block) = (for_expr.identifier, for_expr.block);
1176-
1177-
// TODO: For loop variables are currently mutable by default since we haven't
1178-
// yet implemented syntax for them to be optionally mutable.
1179-
let (identifier, block_id) = self.in_new_scope(|this| {
1180-
let decl = this.add_variable_decl(
1181-
identifier,
1182-
false,
1183-
true,
1184-
DefinitionKind::Local(None),
1185-
);
1186-
(decl, this.resolve_expression(block))
1187-
});
1188-
1189-
HirExpression::For(HirForExpression {
1190-
start_range,
1191-
end_range,
1192-
block: block_id,
1193-
identifier,
1194-
})
1195-
}
11961191
ExpressionKind::If(if_expr) => HirExpression::If(HirIfExpression {
11971192
condition: self.resolve_expression(if_expr.condition),
11981193
consequence: self.resolve_expression(if_expr.consequence),
@@ -1738,7 +1733,7 @@ mod test {
17381733
let (hir_func, _, _) = resolver.resolve_function(func, id);
17391734

17401735
// Iterate over function statements and apply filtering function
1741-
parse_statement_blocks(
1736+
find_lambda_captures(
17421737
hir_func.block(&interner).statements(),
17431738
&interner,
17441739
&mut all_captures,
@@ -1747,33 +1742,23 @@ mod test {
17471742
all_captures
17481743
}
17491744

1750-
fn parse_statement_blocks(
1745+
fn find_lambda_captures(
17511746
stmts: &[StmtId],
17521747
interner: &NodeInterner,
17531748
result: &mut Vec<Vec<String>>,
17541749
) {
1755-
let mut expr: HirExpression;
1756-
17571750
for stmt_id in stmts.iter() {
17581751
let hir_stmt = interner.statement(stmt_id);
1759-
match hir_stmt {
1760-
HirStatement::Expression(expr_id) => {
1761-
expr = interner.expression(&expr_id);
1762-
}
1763-
HirStatement::Let(let_stmt) => {
1764-
expr = interner.expression(&let_stmt.expression);
1765-
}
1766-
HirStatement::Assign(assign_stmt) => {
1767-
expr = interner.expression(&assign_stmt.expression);
1768-
}
1769-
HirStatement::Constrain(constr_stmt) => {
1770-
expr = interner.expression(&constr_stmt.0);
1771-
}
1772-
HirStatement::Semi(semi_expr) => {
1773-
expr = interner.expression(&semi_expr);
1774-
}
1752+
let expr_id = match hir_stmt {
1753+
HirStatement::Expression(expr_id) => expr_id,
1754+
HirStatement::Let(let_stmt) => let_stmt.expression,
1755+
HirStatement::Assign(assign_stmt) => assign_stmt.expression,
1756+
HirStatement::Constrain(constr_stmt) => constr_stmt.0,
1757+
HirStatement::Semi(semi_expr) => semi_expr,
1758+
HirStatement::For(for_loop) => for_loop.block,
17751759
HirStatement::Error => panic!("Invalid HirStatement!"),
1776-
}
1760+
};
1761+
let expr = interner.expression(&expr_id);
17771762
get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter
17781763
}
17791764
}
@@ -1794,7 +1779,7 @@ mod test {
17941779
// Check for other captures recursively within the lambda body
17951780
let hir_body_expr = interner.expression(&lambda_expr.body);
17961781
if let HirExpression::Block(block_expr) = hir_body_expr {
1797-
parse_statement_blocks(block_expr.statements(), interner, result);
1782+
find_lambda_captures(block_expr.statements(), interner, result);
17981783
}
17991784
}
18001785
}

compiler/noirc_frontend/src/hir/type_check/expr.rs

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
types::Type,
1212
},
1313
node_interner::{DefinitionKind, ExprId, FuncId, TraitMethodId},
14-
Shared, Signedness, TypeBinding, TypeVariableKind, UnaryOp,
14+
Signedness, TypeBinding, TypeVariableKind, UnaryOp,
1515
};
1616

1717
use super::{errors::TypeCheckError, TypeChecker};
@@ -194,40 +194,6 @@ impl<'interner> TypeChecker<'interner> {
194194
let span = self.interner.expr_span(expr_id);
195195
self.check_cast(lhs_type, cast_expr.r#type, span)
196196
}
197-
HirExpression::For(for_expr) => {
198-
let start_range_type = self.check_expression(&for_expr.start_range);
199-
let end_range_type = self.check_expression(&for_expr.end_range);
200-
201-
let start_span = self.interner.expr_span(&for_expr.start_range);
202-
let end_span = self.interner.expr_span(&for_expr.end_range);
203-
204-
// Check that start range and end range have the same types
205-
let range_span = start_span.merge(end_span);
206-
self.unify(&start_range_type, &end_range_type, || TypeCheckError::TypeMismatch {
207-
expected_typ: start_range_type.to_string(),
208-
expr_typ: end_range_type.to_string(),
209-
expr_span: range_span,
210-
});
211-
212-
let fresh_id = self.interner.next_type_variable_id();
213-
let type_variable = Shared::new(TypeBinding::Unbound(fresh_id));
214-
let expected_type =
215-
Type::TypeVariable(type_variable, TypeVariableKind::IntegerOrField);
216-
217-
self.unify(&start_range_type, &expected_type, || {
218-
TypeCheckError::TypeCannotBeUsed {
219-
typ: start_range_type.clone(),
220-
place: "for loop",
221-
span: range_span,
222-
}
223-
.add_context("The range of a loop must be known at compile-time")
224-
});
225-
226-
self.interner.push_definition_type(for_expr.identifier.id, start_range_type);
227-
228-
self.check_expression(&for_expr.block);
229-
Type::Unit
230-
}
231197
HirExpression::Block(block_expr) => {
232198
let mut block_type = Type::Unit;
233199

compiler/noirc_frontend/src/hir/type_check/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,11 @@ mod test {
325325
fn basic_for_expr() {
326326
let src = r#"
327327
fn main(_x : Field) {
328-
let _j = for _i in 0..10 {
328+
for _i in 0..10 {
329329
for _k in 0..100 {
330330
331331
}
332-
};
332+
}
333333
}
334334
335335
"#;

compiler/noirc_frontend/src/hir/type_check/stmt.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use noirc_errors::{Location, Span};
33

44
use crate::hir_def::expr::{HirExpression, HirIdent, HirLiteral};
55
use crate::hir_def::stmt::{
6-
HirAssignStatement, HirConstrainStatement, HirLValue, HirLetStatement, HirPattern, HirStatement,
6+
HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement,
7+
HirPattern, HirStatement,
78
};
89
use crate::hir_def::types::Type;
910
use crate::node_interner::{DefinitionId, ExprId, StmtId};
11+
use crate::{Shared, TypeBinding, TypeVariableKind};
1012

1113
use super::errors::{Source, TypeCheckError};
1214
use super::TypeChecker;
@@ -48,11 +50,45 @@ impl<'interner> TypeChecker<'interner> {
4850
HirStatement::Let(let_stmt) => self.check_let_stmt(let_stmt),
4951
HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt),
5052
HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id),
53+
HirStatement::For(for_loop) => self.check_for_loop(for_loop),
5154
HirStatement::Error => (),
5255
}
5356
Type::Unit
5457
}
5558

59+
fn check_for_loop(&mut self, for_loop: HirForStatement) {
60+
let start_range_type = self.check_expression(&for_loop.start_range);
61+
let end_range_type = self.check_expression(&for_loop.end_range);
62+
63+
let start_span = self.interner.expr_span(&for_loop.start_range);
64+
let end_span = self.interner.expr_span(&for_loop.end_range);
65+
66+
// Check that start range and end range have the same types
67+
let range_span = start_span.merge(end_span);
68+
self.unify(&start_range_type, &end_range_type, || TypeCheckError::TypeMismatch {
69+
expected_typ: start_range_type.to_string(),
70+
expr_typ: end_range_type.to_string(),
71+
expr_span: range_span,
72+
});
73+
74+
let fresh_id = self.interner.next_type_variable_id();
75+
let type_variable = Shared::new(TypeBinding::Unbound(fresh_id));
76+
let expected_type = Type::TypeVariable(type_variable, TypeVariableKind::IntegerOrField);
77+
78+
self.unify(&start_range_type, &expected_type, || {
79+
TypeCheckError::TypeCannotBeUsed {
80+
typ: start_range_type.clone(),
81+
place: "for loop",
82+
span: range_span,
83+
}
84+
.add_context("The range of a loop must be known at compile-time")
85+
});
86+
87+
self.interner.push_definition_type(for_loop.identifier.id, start_range_type);
88+
89+
self.check_expression(&for_loop.block);
90+
}
91+
5692
/// Associate a given HirPattern with the given Type, and remember
5793
/// this association in the NodeInterner.
5894
pub(crate) fn bind_pattern(&mut self, pattern: &HirPattern, typ: Type) {

compiler/noirc_frontend/src/hir_def/expr.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ pub enum HirExpression {
2626
Call(HirCallExpression),
2727
MethodCall(HirMethodCallExpression),
2828
Cast(HirCastExpression),
29-
For(HirForExpression),
3029
If(HirIfExpression),
3130
Tuple(Vec<ExprId>),
3231
Lambda(HirLambda),
@@ -48,14 +47,6 @@ pub struct HirIdent {
4847
pub id: DefinitionId,
4948
}
5049

51-
#[derive(Debug, Clone)]
52-
pub struct HirForExpression {
53-
pub identifier: HirIdent,
54-
pub start_range: ExprId,
55-
pub end_range: ExprId,
56-
pub block: ExprId,
57-
}
58-
5950
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6051
pub struct HirBinaryOp {
6152
pub kind: BinaryOpKind,

0 commit comments

Comments
 (0)