diff --git a/Cargo.lock b/Cargo.lock index 20bb8a7ca0d..d87a23d5cb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "acir" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcc9e8065170b7d47cf6718e7788c65c01609a86127eacd885a952b16f46b72" +checksum = "cafbc2adec7783509c6e06831ef57e672fc89c963c855a5fe0449c1ebb2822bc" dependencies = [ "acir_field", "brillig_vm", @@ -18,9 +18,9 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a43b59e8419f485a2a15ababa828e27fd20c00ba2ce90e78efe165df7d47ac0" +checksum = "6a33855c911b77f0eddd9e0a6b9410238a5ba80a4956dcf335dd65dd4d2303d6" dependencies = [ "ark-bn254", "ark-ff", @@ -32,9 +32,9 @@ dependencies = [ [[package]] name = "acvm" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce0a3b770cf70b29fd41313bb0baf2a920475fd782bd7a37822f5ee1406b2231" +checksum = "0ad7d5020f3cee249e162504deeeb1a83fa2a727e4c6a5277136bdecbe82cf2b" dependencies = [ "acir", "acvm_stdlib", @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "acvm_stdlib" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9adc16581262b8571551d029a48118c05b5d02371eb2d10af6a633bb670aec" +checksum = "4c9bf39b1e3b486f090ca1c0ef1e7d47fabfdb522af58117d1d86de048ac0571" dependencies = [ "acir", ] @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f81af1fa12f95a86b2c1a1c15aadc186265853698e3098f5ce9966d1aa64cca" +checksum = "a577e930e991623dd1ec5f9540b20eb601d06ae9d7f66181b81ff699441fc159" dependencies = [ "acir_field", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 8459010651c..38231e4bb24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ edition = "2021" rust-version = "1.66" [workspace.dependencies] -acvm = "=0.14.1" +acvm = "=0.14.2" arena = { path = "crates/arena" } fm = { path = "crates/fm" } iter-extended = { path = "crates/iter-extended" } diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/Nargo.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/Nargo.toml new file mode 100644 index 00000000000..e0b467ce5da --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/Prover.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/Prover.toml new file mode 100644 index 00000000000..22cd5b7c12f --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/Prover.toml @@ -0,0 +1 @@ +sum = "6" diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/src/main.nr new file mode 100644 index 00000000000..7240264b2a8 --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_loop/src/main.nr @@ -0,0 +1,14 @@ +// Tests a very simple program. +// +// The features being tested is basic looping on brillig +fn main(sum: u32){ + assert(loop(4) == sum); +} + +unconstrained fn loop(x: u32) -> u32 { + let mut sum = 0; + for i in 0..x { + sum = sum + i; + } + sum +} diff --git a/crates/noirc_evaluator/src/brillig/binary.rs b/crates/noirc_evaluator/src/brillig/binary.rs new file mode 100644 index 00000000000..70dcc582fb7 --- /dev/null +++ b/crates/noirc_evaluator/src/brillig/binary.rs @@ -0,0 +1,106 @@ +use crate::ssa_refactor::ir::{ + instruction::BinaryOp, + types::{NumericType, Type}, +}; +use acvm::acir::brillig_vm::{BinaryFieldOp, BinaryIntOp}; + +/// Type to encapsulate the binary operation types in Brillig +pub(crate) enum BrilligBinaryOp { + Field { op: BinaryFieldOp }, + Integer { op: BinaryIntOp, bit_size: u32 }, +} + +impl BrilligBinaryOp { + /// Convert an SSA binary operation into: + /// - Brillig Binary Integer Op, if it is a integer type + /// - Brillig Binary Field Op, if it is a field type + pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( + ssa_op: BinaryOp, + typ: Type, + ) -> BrilligBinaryOp { + // First get the bit size and whether its a signed integer, if it is a numeric type + // if it is not,then we return None, indicating that + // it is a Field. + let bit_size_signedness = match typ { + Type::Numeric(numeric_type) => match numeric_type { + NumericType::Signed { bit_size } => Some((bit_size, true)), + NumericType::Unsigned { bit_size } => Some((bit_size, false)), + NumericType::NativeField => None, + }, + _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), + }; + + fn binary_op_to_field_op(op: BinaryOp) -> BinaryFieldOp { + match op { + BinaryOp::Add => BinaryFieldOp::Add, + BinaryOp::Sub => BinaryFieldOp::Sub, + BinaryOp::Mul => BinaryFieldOp::Mul, + BinaryOp::Div => BinaryFieldOp::Div, + BinaryOp::Eq => BinaryFieldOp::Equals, + _ => unreachable!( + "Field type cannot be used with {op}. This should have been caught by the frontend" + ), + } + } + fn binary_op_to_int_op(op: BinaryOp, is_signed: bool) -> BinaryIntOp { + match op { + BinaryOp::Add => BinaryIntOp::Add, + BinaryOp::Sub => BinaryIntOp::Sub, + BinaryOp::Mul => BinaryIntOp::Mul, + BinaryOp::Div => { + if is_signed { + BinaryIntOp::SignedDiv + } else { + BinaryIntOp::UnsignedDiv + } + }, + BinaryOp::Mod => todo!("This is not supported by Brillig. It should either be added into Brillig or legalized by the SSA IR"), + BinaryOp::Eq => BinaryIntOp::Equals, + BinaryOp::Lt => BinaryIntOp::LessThan, + BinaryOp::And => BinaryIntOp::And, + BinaryOp::Or => BinaryIntOp::Or, + BinaryOp::Xor => BinaryIntOp::Xor, + BinaryOp::Shl => BinaryIntOp::Shl, + BinaryOp::Shr => BinaryIntOp::Shr, + } + } + // If bit size is available then it is a binary integer operation + match bit_size_signedness { + Some((bit_size, is_signed)) => { + let binary_int_op = binary_op_to_int_op(ssa_op, is_signed); + BrilligBinaryOp::Integer { op: binary_int_op, bit_size } + } + None => { + let binary_field_op = binary_op_to_field_op(ssa_op); + BrilligBinaryOp::Field { op: binary_field_op } + } + } + } +} + +/// Returns the type of the operation considering the types of the operands +/// TODO: SSA issues binary operations between fields and integers. +/// This probably should be explicitely casted in SSA to avoid having to coerce at this level. +pub(crate) fn type_of_binary_operation(lhs_type: Type, rhs_type: Type) -> Type { + match (lhs_type, rhs_type) { + // If either side is a Field constant then, we coerce into the type + // of the other operand + (Type::Numeric(NumericType::NativeField), typ) + | (typ, Type::Numeric(NumericType::NativeField)) => typ, + // If both sides are numeric type, then we expect their types to be + // the same. + (Type::Numeric(lhs_type), Type::Numeric(rhs_type)) => { + assert_eq!( + lhs_type, rhs_type, + "lhs and rhs types in a binary operation are always the same" + ); + Type::Numeric(lhs_type) + } + _ => { + unreachable!( + "ICE: Binary operation between types {:?} and {:?} is not allowed", + lhs_type, rhs_type + ) + } + } +} diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen.rs b/crates/noirc_evaluator/src/brillig/brillig_gen.rs index aae46952813..7a354a70680 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen.rs @@ -1,16 +1,20 @@ -use super::artifact::BrilligArtifact; +use super::{ + artifact::BrilligArtifact, + binary::{type_of_binary_operation, BrilligBinaryOp}, + memory::BrilligMemory, +}; use crate::ssa_refactor::ir::{ basic_block::{BasicBlock, BasicBlockId}, dfg::DataFlowGraph, function::Function, - instruction::{Binary, BinaryOp, Instruction, InstructionId, TerminatorInstruction}, + instruction::{Binary, Instruction, InstructionId, TerminatorInstruction}, post_order::PostOrder, - types::{NumericType, Type}, + types::Type, value::{Value, ValueId}, }; use acvm::{ acir::brillig_vm::{ - BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value as BrilligValue, + BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value as BrilligValue, }, FieldElement, }; @@ -24,6 +28,8 @@ pub(crate) struct BrilligGen { latest_register: usize, /// Map from SSA values to Register Indices. ssa_value_to_register: HashMap, + /// Tracks memory allocations + memory: BrilligMemory, } impl BrilligGen { @@ -63,7 +69,7 @@ impl BrilligGen { /// Converts an SSA Basic block into a sequence of Brillig opcodes fn convert_block(&mut self, block_id: BasicBlockId, dfg: &DataFlowGraph) { self.obj.add_block_label(block_id); - let block = &dfg[dbg!(block_id)]; + let block = &dfg[block_id]; self.convert_block_params(block, dfg); for instruction_id in block.instructions() { @@ -112,14 +118,6 @@ impl BrilligGen { /// the Register starting at register index 0. `N` indicates the number of /// return values expected. fn convert_ssa_return(&mut self, return_values: &[ValueId], dfg: &DataFlowGraph) { - // Check if the program returns the `Unit/None` type. - // This type signifies that the program returns nothing. - let is_return_unit_type = - return_values.len() == 1 && dfg.type_of_value(return_values[0]) == Type::Unit; - if is_return_unit_type { - return; - } - for (destination_index, value_id) in return_values.iter().enumerate() { let return_register = self.convert_ssa_value(*value_id, dfg); if destination_index > self.latest_register { @@ -130,6 +128,7 @@ impl BrilligGen { source: return_register, }); } + self.push_code(BrilligOpcode::Stop); } /// Converts SSA Block parameters into Brillig Registers. @@ -161,6 +160,28 @@ impl BrilligGen { let result_register = self.get_or_create_register(result_ids[0]); self.convert_ssa_binary(binary, dfg, result_register); } + Instruction::Allocate { size } => { + let pointer_register = + self.get_or_create_register(dfg.instruction_results(instruction_id)[0]); + self.allocate_array(pointer_register, *size); + } + Instruction::Store { address, value } => { + let address_register = self.convert_ssa_value(*address, dfg); + let value_register = self.convert_ssa_value(*value, dfg); + self.push_code(BrilligOpcode::Store { + destination_pointer: address_register, + source: value_register, + }); + } + Instruction::Load { address } => { + let target_register = + self.get_or_create_register(dfg.instruction_results(instruction_id)[0]); + let address_register = self.convert_ssa_value(*address, dfg); + self.push_code(BrilligOpcode::Load { + destination: target_register, + source_pointer: address_register, + }); + } Instruction::Not(value) => { let result_ids = dfg.instruction_results(instruction_id); let result_register = self.get_or_create_register(result_ids[0]); @@ -188,6 +209,14 @@ impl BrilligGen { }; } + fn allocate_array(&mut self, pointer_register: RegisterIndex, size: u32) { + let array_pointer = self.memory.allocate(size as usize); + self.push_code(BrilligOpcode::Const { + destination: pointer_register, + value: BrilligValue::from(array_pointer), + }); + } + /// Returns a register which holds the value of a constant fn make_constant(&mut self, constant: FieldElement) -> RegisterIndex { let register = self.create_register(); @@ -206,17 +235,16 @@ impl BrilligGen { dfg: &DataFlowGraph, result_register: RegisterIndex, ) { - let left_type = dfg[binary.lhs].get_type(); - let right_type = dfg[binary.rhs].get_type(); - if left_type != right_type { - todo!("ICE: Binary operands must have the same type") - } + let binary_type = + type_of_binary_operation(dfg[binary.lhs].get_type(), dfg[binary.rhs].get_type()); let left = self.convert_ssa_value(binary.lhs, dfg); let right = self.convert_ssa_value(binary.rhs, dfg); - let brillig_binary_op = - BrilligBinaryOp::convert_ssa_binary_op_to_brillig_binary_op(binary.operator, left_type); + let brillig_binary_op = BrilligBinaryOp::convert_ssa_binary_op_to_brillig_binary_op( + binary.operator, + binary_type, + ); match brillig_binary_op { BrilligBinaryOp::Field { op } => { let opcode = BrilligOpcode::BinaryFieldOp { @@ -272,8 +300,6 @@ impl BrilligGen { brillig.convert_ssa_function(func); - brillig.push_code(BrilligOpcode::Stop); - brillig.obj } @@ -292,74 +318,3 @@ impl BrilligGen { } } } - -/// Type to encapsulate the binary operation types in Brillig -pub(crate) enum BrilligBinaryOp { - Field { op: BinaryFieldOp }, - Integer { op: BinaryIntOp, bit_size: u32 }, -} - -impl BrilligBinaryOp { - /// Convert an SSA binary operation into: - /// - Brillig Binary Integer Op, if it is a integer type - /// - Brillig Binary Field Op, if it is a field type - fn convert_ssa_binary_op_to_brillig_binary_op(ssa_op: BinaryOp, typ: Type) -> BrilligBinaryOp { - // First get the bit size and whether its a signed integer, if it is a numeric type - // if it is not,then we return None, indicating that - // it is a Field. - let bit_size_signedness = match typ { - Type::Numeric(numeric_type) => match numeric_type { - NumericType::Signed { bit_size } => Some((bit_size, true)), - NumericType::Unsigned { bit_size } => Some((bit_size, false)), - NumericType::NativeField => None, - }, - _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), - }; - - fn binary_op_to_field_op(op: BinaryOp) -> BinaryFieldOp { - match op { - BinaryOp::Add => BinaryFieldOp::Add, - BinaryOp::Sub => BinaryFieldOp::Sub, - BinaryOp::Mul => BinaryFieldOp::Mul, - BinaryOp::Div => BinaryFieldOp::Div, - BinaryOp::Eq => BinaryFieldOp::Equals, - _ => unreachable!( - "Field type cannot be used with {op}. This should have been caught by the frontend" - ), - } - } - fn binary_op_to_int_op(op: BinaryOp, is_signed: bool) -> BinaryIntOp { - match op { - BinaryOp::Add => BinaryIntOp::Add, - BinaryOp::Sub => BinaryIntOp::Sub, - BinaryOp::Mul => BinaryIntOp::Mul, - BinaryOp::Div => { - if is_signed { - BinaryIntOp::SignedDiv - } else { - BinaryIntOp::UnsignedDiv - } - }, - BinaryOp::Mod => todo!("This is not supported by Brillig. It should either be added into Brillig or legalized by the SSA IR"), - BinaryOp::Eq => BinaryIntOp::Equals, - BinaryOp::Lt => BinaryIntOp::LessThan, - BinaryOp::And => BinaryIntOp::And, - BinaryOp::Or => BinaryIntOp::Or, - BinaryOp::Xor => BinaryIntOp::Xor, - BinaryOp::Shl => BinaryIntOp::Shl, - BinaryOp::Shr => BinaryIntOp::Shr, - } - } - // If bit size is available then it is a binary integer operation - match bit_size_signedness { - Some((bit_size, is_signed)) => { - let binary_int_op = binary_op_to_int_op(ssa_op, is_signed); - BrilligBinaryOp::Integer { op: binary_int_op, bit_size } - } - None => { - let binary_field_op = binary_op_to_field_op(ssa_op); - BrilligBinaryOp::Field { op: binary_field_op } - } - } - } -} diff --git a/crates/noirc_evaluator/src/brillig/memory.rs b/crates/noirc_evaluator/src/brillig/memory.rs new file mode 100644 index 00000000000..099dbfb7704 --- /dev/null +++ b/crates/noirc_evaluator/src/brillig/memory.rs @@ -0,0 +1,15 @@ +/// Simple memory allocator for brillig +/// For now it just tracks a free memory pointer +/// Will probably get smarter in the future +#[derive(Default)] +pub(crate) struct BrilligMemory { + free_mem_pointer: usize, +} + +impl BrilligMemory { + pub(crate) fn allocate(&mut self, size: usize) -> usize { + let start = self.free_mem_pointer; + self.free_mem_pointer += size; + start + } +} diff --git a/crates/noirc_evaluator/src/brillig/mod.rs b/crates/noirc_evaluator/src/brillig/mod.rs index 4c62554615d..5c28619194f 100644 --- a/crates/noirc_evaluator/src/brillig/mod.rs +++ b/crates/noirc_evaluator/src/brillig/mod.rs @@ -3,7 +3,9 @@ use std::collections::HashMap; use self::{artifact::BrilligArtifact, brillig_gen::BrilligGen}; pub(crate) mod artifact; +pub(crate) mod binary; pub(crate) mod brillig_gen; +pub(crate) mod memory; use crate::ssa_refactor::{ ir::function::{Function, FunctionId, RuntimeType}, diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen.rs b/crates/noirc_evaluator/src/ssa/ssa_gen.rs index 57187a9fcc1..31817b0bded 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen.rs @@ -189,7 +189,9 @@ impl IrGenerator { let function_node_id = self.context.get_or_create_opcode_node_id(opcode); Ok(Value::Node(function_node_id)) } - Definition::Oracle(_, _) => unimplemented!("oracles not supported by deprecated SSA") + Definition::Oracle(_, _) => { + unimplemented!("oracles not supported by deprecated SSA") + } } } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs index 05ff22808f6..c0be297e5d8 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs @@ -184,6 +184,15 @@ struct Branch { } fn flatten_function_cfg(function: &mut Function) { + // TODO This pass will run forever on a brillig function. + // TODO In particular, analyze will check if the predecessors + // TODO have been processed and push the block to the back of the queue + // TODO This loops forever, if the predecessors are not then processed + // TODO Because it will visit the same block again, pop it out of the queue + // TODO then back into the queue again. + if let crate::ssa_refactor::ir::function::RuntimeType::Brillig = function.runtime() { + return; + } let mut context = Context { cfg: ControlFlowGraph::with_function(function), function, diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs index e46fa63200d..0e9d7a88b91 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs @@ -89,12 +89,12 @@ impl<'a> FunctionContext<'a> { match &ident.definition { ast::Definition::Local(id) => self.lookup(*id).map(|value| value.eval(self).into()), ast::Definition::Function(id) => self.get_or_queue_function(*id), - ast::Definition::Builtin(name) | ast::Definition::LowLevel(name) | ast::Definition::Oracle(name, _) => { - match self.builder.import_intrinsic(name) { - Some(builtin) => builtin.into(), - None => panic!("No builtin function named '{name}' found"), - } - } + ast::Definition::Builtin(name) + | ast::Definition::LowLevel(name) + | ast::Definition::Oracle(name, _) => match self.builder.import_intrinsic(name) { + Some(builtin) => builtin.into(), + None => panic!("No builtin function named '{name}' found"), + }, } } @@ -243,7 +243,8 @@ impl<'a> FunctionContext<'a> { let loop_end = self.builder.insert_block(); // this is the 'i' in `for i in start .. end { block }` - let loop_index = self.builder.add_block_parameter(loop_entry, Type::field()); + let index_type = Self::convert_non_tuple_type(&for_expr.index_type); + let loop_index = self.builder.add_block_parameter(loop_entry, index_type); let start_index = self.codegen_non_tuple_expression(&for_expr.start_range); let end_index = self.codegen_non_tuple_expression(&for_expr.end_range); diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index dbdfda985d5..67d0e2cf2a4 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -307,7 +307,9 @@ impl<'a> Resolver<'a> { let func_meta = self.extract_meta(&func, id); let hir_func = match func.kind { - FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => HirFunction::empty(), + FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => { + HirFunction::empty() + } FunctionKind::Normal => { let expr_id = self.intern_block(func.def.body); self.interner.push_expr_location(expr_id, func.def.span, self.file); diff --git a/crates/noirc_frontend/src/hir/type_check/expr.rs b/crates/noirc_frontend/src/hir/type_check/expr.rs index 5a9077c2a7e..feb85f90a21 100644 --- a/crates/noirc_frontend/src/hir/type_check/expr.rs +++ b/crates/noirc_frontend/src/hir/type_check/expr.rs @@ -153,25 +153,33 @@ impl<'interner> TypeChecker<'interner> { let start_span = self.interner.expr_span(&for_expr.start_range); let end_span = self.interner.expr_span(&for_expr.end_range); - let mut unify_loop_range = |actual_type, span| { - let expected_type = if self.is_unconstrained() { - Type::FieldElement(CompTime::new(self.interner)) - } else { - Type::comp_time(Some(span)) - }; + // Check that start range and end range have the same types + let range_span = start_span.merge(end_span); + self.unify(&start_range_type, &end_range_type, range_span, || { + TypeCheckError::TypeMismatch { + expected_typ: start_range_type.to_string(), + expr_typ: end_range_type.to_string(), + expr_span: range_span, + } + }); - self.unify(actual_type, &expected_type, span, || { - TypeCheckError::TypeCannotBeUsed { - typ: start_range_type.clone(), - place: "for loop", - span, - } - .add_context("The range of a loop must be known at compile-time") - }); + let expected_comptime = if self.is_unconstrained() { + CompTime::new(self.interner) + } else { + CompTime::Yes(Some(range_span)) }; - - unify_loop_range(&start_range_type, start_span); - unify_loop_range(&end_range_type, end_span); + let fresh_id = self.interner.next_type_variable_id(); + let type_variable = Shared::new(TypeBinding::Unbound(fresh_id)); + let expected_type = Type::PolymorphicInteger(expected_comptime, type_variable); + + self.unify(&start_range_type, &expected_type, range_span, || { + TypeCheckError::TypeCannotBeUsed { + typ: start_range_type.clone(), + place: "for loop", + span: range_span, + } + .add_context("The range of a loop must be known at compile-time") + }); self.interner.push_definition_type(for_expr.identifier.id, start_range_type); diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 2c44898499b..0f8fb0ea059 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -21,7 +21,8 @@ use crate::{ stmt::{HirAssignStatement, HirLValue, HirLetStatement, HirPattern, HirStatement}, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId}, - CompTime, FunctionKind, TypeBinding, TypeBindings, token::Attribute, + token::Attribute, + CompTime, FunctionKind, TypeBinding, TypeBindings, }; use self::ast::{Definition, FuncId, Function, LocalId, Program};