diff --git a/crates/concrete_driver/tests/examples.rs b/crates/concrete_driver/tests/examples.rs index f26846a..8ca3e6c 100644 --- a/crates/concrete_driver/tests/examples.rs +++ b/crates/concrete_driver/tests/examples.rs @@ -18,6 +18,8 @@ mod common; #[test_case(include_str!("../../../examples/malloc.con"), "malloc", false, 5 ; "malloc.con")] #[test_case(include_str!("../../../examples/while_if_false.con"), "while_if_false", false, 7 ; "while_if_false.con")] #[test_case(include_str!("../../../examples/if_if_false.con"), "if_if_false", false, 7 ; "if_if_false.con")] +#[test_case(include_str!("../../../examples/for.con"), "for", false, 10 ; "for.con")] +#[test_case(include_str!("../../../examples/for_while.con"), "for_while", false, 10 ; "for_while.con")] fn example_tests(source: &str, name: &str, is_library: bool, status_code: i32) { assert_eq!( status_code, diff --git a/crates/concrete_ir/src/lowering.rs b/crates/concrete_ir/src/lowering.rs index c0bf5c7..d3d1f2e 100644 --- a/crates/concrete_ir/src/lowering.rs +++ b/crates/concrete_ir/src/lowering.rs @@ -9,7 +9,7 @@ use concrete_ast::{ }, functions::{FunctionDecl, FunctionDef}, modules::{Module, ModuleDefItem}, - statements::{self, AssignStmt, LetStmt, LetStmtTarget, ReturnStmt, WhileStmt}, + statements::{self, AssignStmt, ForStmt, LetStmt, LetStmtTarget, ReturnStmt, WhileStmt}, structs::StructDecl, types::{TypeQualifier, TypeSpec}, Program, @@ -291,6 +291,24 @@ fn lower_func( } LetStmtTarget::Destructure(_) => todo!(), } + } else if let statements::Statement::For(info) = stmt { + if let Some(info) = &info.init { + match &info.target { + LetStmtTarget::Simple { name, r#type } => { + let ty = lower_type(&builder.ctx, r#type, builder.local_module)?; + builder + .name_to_local + .insert(name.name.clone(), builder.body.locals.len()); + builder.body.locals.push(Local::new( + Some(name.span), + LocalKind::Temp, + ty, + Some(name.name.clone()), + )); + } + LetStmtTarget::Destructure(_) => todo!(), + } + } } } @@ -363,7 +381,10 @@ fn lower_statement( match info { statements::Statement::Assign(info) => lower_assign(builder, info)?, statements::Statement::Match(_) => todo!(), - statements::Statement::For(_) => todo!(), + statements::Statement::For(info) => { + lower_for(builder, info)?; + assert!(builder.statements.is_empty()); + } statements::Statement::If(info) => { lower_if_statement(builder, info)?; assert!(builder.statements.is_empty()); @@ -460,6 +481,107 @@ fn lower_while(builder: &mut FnBodyBuilder, info: &WhileStmt) -> Result<(), Lowe Ok(()) } +fn lower_for(builder: &mut FnBodyBuilder, info: &ForStmt) -> Result<(), LoweringError> { + if let Some(init) = &info.init { + lower_let(builder, init)?; + } + + let statements = std::mem::take(&mut builder.statements); + builder.body.basic_blocks.push(BasicBlock { + statements, + terminator: Box::new(Terminator { + span: None, + kind: TerminatorKind::Goto { + target: builder.body.basic_blocks.len() + 1, + }, + }), + }); + + let (discriminator, discriminator_type, _disc_span) = if let Some(condition) = &info.condition { + let (discriminator, discriminator_type, span) = lower_expression(builder, condition, None)?; + + (discriminator, discriminator_type, Some(span)) + } else { + // todo: don't use discriminator when no loop condition + let discriminator_type = Ty { + span: None, + kind: TyKind::Bool, + }; + + let discriminator = Rvalue::Use(Operand::Const(ConstData { + ty: discriminator_type.clone(), + data: ConstKind::Value(ValueTree::Leaf(ConstValue::Bool(true))), + })); + + (discriminator, discriminator_type, None) + }; + + let local = builder.add_temp_local(TyKind::Bool); + let place = Place { + local, + projection: vec![], + }; + + builder.statements.push(Statement { + span: None, + kind: StatementKind::Assign(place.clone(), discriminator), + }); + + // keep idx to change terminator + let check_block_idx = builder.body.basic_blocks.len(); + + let statements = std::mem::take(&mut builder.statements); + builder.body.basic_blocks.push(BasicBlock { + statements, + terminator: Box::new(Terminator { + span: None, + kind: TerminatorKind::Unreachable, + }), + }); + + // keep idx for switch targets + let first_then_block_idx = builder.body.basic_blocks.len(); + + for stmt in &info.contents { + lower_statement( + builder, + stmt, + builder.body.locals[builder.ret_local].ty.clone(), + )?; + } + + if let Some(post) = &info.post { + lower_assign(builder, post)?; + } + + builder.body.basic_blocks.len(); + let statements = std::mem::take(&mut builder.statements); + builder.body.basic_blocks.push(BasicBlock { + statements, + terminator: Box::new(Terminator { + span: None, + kind: TerminatorKind::Goto { + target: check_block_idx, + }, + }), + }); + + let otherwise_block_idx = builder.body.basic_blocks.len(); + + let targets = SwitchTargets { + values: vec![discriminator_type.kind.get_falsy_value()], + targets: vec![otherwise_block_idx, first_then_block_idx], + }; + + let kind = TerminatorKind::SwitchInt { + discriminator: Operand::Place(place), + targets, + }; + builder.body.basic_blocks[check_block_idx].terminator.kind = kind; + + Ok(()) +} + fn lower_if_statement(builder: &mut FnBodyBuilder, info: &IfExpr) -> Result<(), LoweringError> { let (discriminator, discriminator_type, _disc_span) = lower_expression(builder, &info.value, None)?; diff --git a/examples/for_loop.con b/examples/for_loop.con index fb57b29..35590fa 100644 --- a/examples/for_loop.con +++ b/examples/for_loop.con @@ -7,11 +7,6 @@ mod Example { let mut result: i64 = 0; let n: i64 = 1; - for (;;) { - result = result + n; - n = n + 1; - } - for { result = result + n; n = n + 1; diff --git a/examples/for_while.con b/examples/for_while.con index 80f9d3e..d87f1d7 100644 --- a/examples/for_while.con +++ b/examples/for_while.con @@ -7,12 +7,6 @@ mod Example { let mut result: i64 = 0; let n: i64 = 1; - for (; n <= limit ;) { - result = result + n; - n = n + 1; - } - - n = 1; for (n <= limit) { result = result + n; n = n + 1;