diff --git a/Cargo.lock b/Cargo.lock index cdc1d75a421..4b56812e96d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2619,6 +2619,7 @@ name = "nargo_fmt" version = "0.34.0" dependencies = [ "bytecount", + "noirc_errors", "noirc_frontend", "serde", "similar-asserts", diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index be89b24fee5..ad54d0f7d6b 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -7,13 +7,17 @@ use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, }; use noirc_frontend::ast::{Signedness, Visibility}; +use noirc_frontend::TypeBinding; use noirc_frontend::{ hir::Context, - hir_def::{expr::HirArrayLiteral, function::Param, stmt::HirPattern, types::Type}, - macros_api::{HirExpression, HirLiteral}, + hir_def::{ + expr::{HirArrayLiteral, HirExpression, HirLiteral}, + function::Param, + stmt::HirPattern, + types::Type, + }, node_interner::{FuncId, NodeInterner}, }; -use noirc_frontend::{TypeBinding, TypeVariableKind}; /// Arranges a function signature and a generated circuit's return witnesses into a /// `noirc_abi::Abi`. @@ -68,13 +72,18 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { AbiType::Integer { sign, width: (*bit_width).into() } } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { - TypeBinding::Bound(typ) => abi_type_from_hir_type(context, typ), - TypeBinding::Unbound(_) => { - abi_type_from_hir_type(context, &Type::default_int_or_field_type()) + Type::TypeVariable(binding) => { + if binding.is_integer() || binding.is_integer_or_field() { + match &*binding.borrow() { + TypeBinding::Bound(typ) => abi_type_from_hir_type(context, typ), + TypeBinding::Unbound(_id, _kind) => { + abi_type_from_hir_type(context, &Type::default_int_or_field_type()) + } + } + } else { + unreachable!("{typ} cannot be used in the abi") } - }, + } Type::Bool => AbiType::Boolean, Type::String(size) => { let size = size @@ -102,7 +111,6 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::Constant(..) | Type::InfixExpr(..) | Type::TraitAsType(..) - | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) | Type::Quoted(_) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index ff9b5ea67eb..c28e9c66011 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -9,7 +9,7 @@ use super::{ }; use acvm::acir::{brillig::MemoryAddress, AcirField}; -pub(crate) const MAX_STACK_SIZE: usize = 2048; +pub(crate) const MAX_STACK_SIZE: usize = 32768; pub(crate) const MAX_SCRATCH_SPACE: usize = 64; impl BrilligContext { diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index ad6645df228..efc7c6018c1 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -118,6 +118,7 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::remove_enable_side_effects, "After EnableSideEffectsIf removal:") .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") + .run_pass(Ssa::simplify_cfg, "After Simplifying:") .run_pass(Ssa::array_set_optimization, "After Array Set Optimizations:") .finish(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/cfg.rs b/compiler/noirc_evaluator/src/ssa/ir/cfg.rs index b9166bf1d56..38e6efa5b9a 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/cfg.rs @@ -59,7 +59,7 @@ impl ControlFlowGraph { /// Clears out a given block's successors. This also removes the given block from /// being a predecessor of any of its previous successors. - fn invalidate_block_successors(&mut self, basic_block_id: BasicBlockId) { + pub(crate) fn invalidate_block_successors(&mut self, basic_block_id: BasicBlockId) { let node = self .data .get_mut(&basic_block_id) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index e30707effed..676bb48c4d9 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -630,9 +630,9 @@ impl Instruction { } } Instruction::ArraySet { array, index, value, .. } => { - let array = dfg.get_array_constant(*array); - let index = dfg.get_numeric_constant(*index); - if let (Some((array, element_type)), Some(index)) = (array, index) { + let array_const = dfg.get_array_constant(*array); + let index_const = dfg.get_numeric_constant(*index); + if let (Some((array, element_type)), Some(index)) = (array_const, index_const) { let index = index.try_to_u32().expect("Expected array index to fit in u32") as usize; @@ -641,7 +641,8 @@ impl Instruction { return SimplifiedTo(new_array); } } - None + + try_optimize_array_set_from_previous_get(dfg, *array, *index, *value) } Instruction::Truncate { value, bit_size, max_bit_size } => { if bit_size == max_bit_size { @@ -817,6 +818,27 @@ fn try_optimize_array_get_from_previous_set( SimplifyResult::None } +fn try_optimize_array_set_from_previous_get( + dfg: &DataFlowGraph, + array_id: ValueId, + target_index: ValueId, + target_value: ValueId, +) -> SimplifyResult { + match &dfg[target_value] { + Value::Instruction { instruction, .. } => match &dfg[*instruction] { + Instruction::ArrayGet { array, index } => { + if *array == array_id && *index == target_index { + SimplifyResult::SimplifiedTo(array_id) + } else { + SimplifyResult::None + } + } + _ => SimplifyResult::None, + }, + _ => SimplifyResult::None, + } +} + pub(crate) type ErrorType = HirType; pub(crate) fn error_selector_from_type(typ: &ErrorType) -> ErrorSelector { diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index 6d48b8c0d67..b2fe137c8bc 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -1,7 +1,10 @@ +use std::mem; + use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, + function::{Function, RuntimeType}, instruction::{Instruction, InstructionId, TerminatorInstruction}, types::Type::{Array, Slice}, value::ValueId, @@ -17,94 +20,126 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn array_set_optimization(mut self) -> Self { for func in self.functions.values_mut() { - let reachable_blocks = func.reachable_blocks(); - - if !func.runtime().is_entry_point() { - assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization"); - } - let mut array_to_last_use = HashMap::default(); - let mut instructions_to_update = HashSet::default(); - let mut arrays_from_load = HashSet::default(); - - for block in reachable_blocks.iter() { - analyze_last_uses( - &func.dfg, - *block, - &mut array_to_last_use, - &mut instructions_to_update, - &mut arrays_from_load, - ); - } - for block in reachable_blocks { - make_mutable(&mut func.dfg, block, &instructions_to_update); - } + func.array_set_optimization(); } self } } -/// Builds the set of ArraySet instructions that can be made mutable -/// because their input value is unused elsewhere afterward. -fn analyze_last_uses( - dfg: &DataFlowGraph, - block_id: BasicBlockId, - array_to_last_use: &mut HashMap, - instructions_that_can_be_made_mutable: &mut HashSet, - arrays_from_load: &mut HashSet, -) { - let block = &dfg[block_id]; +impl Function { + pub(crate) fn array_set_optimization(&mut self) { + let reachable_blocks = self.reachable_blocks(); - for instruction_id in block.instructions() { - match &dfg[*instruction_id] { - Instruction::ArrayGet { array, .. } => { - let array = dfg.resolve(*array); + if !self.runtime().is_entry_point() { + assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization"); + } - if let Some(existing) = array_to_last_use.insert(array, *instruction_id) { - instructions_that_can_be_made_mutable.remove(&existing); - } - } - Instruction::ArraySet { array, .. } => { - let array = dfg.resolve(*array); + let mut context = Context::new(&self.dfg, matches!(self.runtime(), RuntimeType::Brillig)); - if let Some(existing) = array_to_last_use.insert(array, *instruction_id) { - instructions_that_can_be_made_mutable.remove(&existing); - } - // If the array we are setting does not come from a load we can safely mark it mutable. - // If the array comes from a load we may potentially being mutating an array at a reference - // that is loaded from by other values. - let terminator = dfg[block_id].unwrap_terminator(); - // If we are in a return block we are not concerned about the array potentially being mutated again. - let is_return_block = matches!(terminator, TerminatorInstruction::Return { .. }); - // We also want to check that the array is not part of the terminator arguments, as this means it is used again. - let mut array_in_terminator = false; - terminator.for_each_value(|value| { - if value == array { - array_in_terminator = true; + for block in reachable_blocks.iter() { + context.analyze_last_uses(*block); + } + + let instructions_to_update = mem::take(&mut context.instructions_that_can_be_made_mutable); + for block in reachable_blocks { + make_mutable(&mut self.dfg, block, &instructions_to_update); + } + } +} + +struct Context<'f> { + dfg: &'f DataFlowGraph, + is_brillig_runtime: bool, + array_to_last_use: HashMap, + instructions_that_can_be_made_mutable: HashSet, + arrays_from_load: HashSet, + inner_nested_arrays: HashMap, +} + +impl<'f> Context<'f> { + fn new(dfg: &'f DataFlowGraph, is_brillig_runtime: bool) -> Self { + Context { + dfg, + is_brillig_runtime, + array_to_last_use: HashMap::default(), + instructions_that_can_be_made_mutable: HashSet::default(), + arrays_from_load: HashSet::default(), + inner_nested_arrays: HashMap::default(), + } + } + + /// Builds the set of ArraySet instructions that can be made mutable + /// because their input value is unused elsewhere afterward. + fn analyze_last_uses(&mut self, block_id: BasicBlockId) { + let block = &self.dfg[block_id]; + + for instruction_id in block.instructions() { + match &self.dfg[*instruction_id] { + Instruction::ArrayGet { array, .. } => { + let array = self.dfg.resolve(*array); + + if let Some(existing) = self.array_to_last_use.insert(array, *instruction_id) { + self.instructions_that_can_be_made_mutable.remove(&existing); } - }); - if (!arrays_from_load.contains(&array) || is_return_block) && !array_in_terminator { - instructions_that_can_be_made_mutable.insert(*instruction_id); } - } - Instruction::Call { arguments, .. } => { - for argument in arguments { - if matches!(dfg.type_of_value(*argument), Array { .. } | Slice { .. }) { - let argument = dfg.resolve(*argument); + Instruction::ArraySet { array, value, .. } => { + let array = self.dfg.resolve(*array); + + if let Some(existing) = self.array_to_last_use.insert(array, *instruction_id) { + self.instructions_that_can_be_made_mutable.remove(&existing); + } + if self.is_brillig_runtime { + let value = self.dfg.resolve(*value); + + if let Some(existing) = self.inner_nested_arrays.get(&value) { + self.instructions_that_can_be_made_mutable.remove(existing); + } + let result = self.dfg.instruction_results(*instruction_id)[0]; + self.inner_nested_arrays.insert(result, *instruction_id); + } - if let Some(existing) = array_to_last_use.insert(argument, *instruction_id) + // If the array we are setting does not come from a load we can safely mark it mutable. + // If the array comes from a load we may potentially being mutating an array at a reference + // that is loaded from by other values. + let terminator = self.dfg[block_id].unwrap_terminator(); + // If we are in a return block we are not concerned about the array potentially being mutated again. + let is_return_block = + matches!(terminator, TerminatorInstruction::Return { .. }); + // We also want to check that the array is not part of the terminator arguments, as this means it is used again. + let mut array_in_terminator = false; + terminator.for_each_value(|value| { + if value == array { + array_in_terminator = true; + } + }); + if (!self.arrays_from_load.contains(&array) || is_return_block) + && !array_in_terminator + { + self.instructions_that_can_be_made_mutable.insert(*instruction_id); + } + } + Instruction::Call { arguments, .. } => { + for argument in arguments { + if matches!(self.dfg.type_of_value(*argument), Array { .. } | Slice { .. }) { - instructions_that_can_be_made_mutable.remove(&existing); + let argument = self.dfg.resolve(*argument); + + if let Some(existing) = + self.array_to_last_use.insert(argument, *instruction_id) + { + self.instructions_that_can_be_made_mutable.remove(&existing); + } } } } - } - Instruction::Load { .. } => { - let result = dfg.instruction_results(*instruction_id)[0]; - if matches!(dfg.type_of_value(result), Array { .. } | Slice { .. }) { - arrays_from_load.insert(result); + Instruction::Load { .. } => { + let result = self.dfg.instruction_results(*instruction_id)[0]; + if matches!(self.dfg.type_of_value(result), Array { .. } | Slice { .. }) { + self.arrays_from_load.insert(result); + } } + _ => (), } - _ => (), } } } @@ -162,29 +197,31 @@ mod tests { // from and cloned in a loop. If the array is inadvertently marked mutable, and is cloned in a previous iteration // of the loop, its clone will also be altered. // - // acir(inline) fn main f0 { + // brillig fn main f0 { // b0(): - // v2 = allocate - // store [Field 0, Field 0, Field 0, Field 0, Field 0] at v2 // v3 = allocate - // store [Field 0, Field 0, Field 0, Field 0, Field 0] at v3 + // store [[Field 0, Field 0, Field 0, Field 0, Field 0], [Field 0, Field 0, Field 0, Field 0, Field 0]] at v3 + // v4 = allocate + // store [[Field 0, Field 0, Field 0, Field 0, Field 0], [Field 0, Field 0, Field 0, Field 0, Field 0]] at v4 // jmp b1(u32 0) - // b1(v5: u32): - // v7 = lt v5, u32 5 - // jmpif v7 then: b3, else: b2 + // b1(v6: u32): + // v8 = lt v6, u32 5 + // jmpif v8 then: b3, else: b2 // b3(): - // v8 = eq v5, u32 5 - // jmpif v8 then: b4, else: b5 + // v9 = eq v6, u32 5 + // jmpif v9 then: b4, else: b5 // b4(): - // v9 = load v2 - // store v9 at v3 + // v10 = load v3 + // store v10 at v4 // jmp b5() // b5(): - // v10 = load v2 - // v12 = array_set v10, index v5, value Field 20 - // store v12 at v2 - // v14 = add v5, u32 1 - // jmp b1(v14) + // v11 = load v3 + // v13 = array_get v11, index Field 0 + // v14 = array_set v13, index v6, value Field 20 + // v15 = array_set v11, index v6, value v14 + // store v15 at v3 + // v17 = add v6, u32 1 + // jmp b1(v17) // b2(): // return // } @@ -196,13 +233,16 @@ mod tests { let zero = builder.field_constant(0u128); let array_constant = builder.array_constant(vector![zero, zero, zero, zero, zero], array_type.clone()); + let nested_array_type = Type::Array(Arc::new(vec![array_type.clone()]), 2); + let nested_array_constant = builder + .array_constant(vector![array_constant, array_constant], nested_array_type.clone()); - let v2 = builder.insert_allocate(array_type.clone()); + let v3 = builder.insert_allocate(array_type.clone()); - builder.insert_store(v2, array_constant); + builder.insert_store(v3, nested_array_constant); - let v3 = builder.insert_allocate(array_type.clone()); - builder.insert_store(v3, array_constant); + let v4 = builder.insert_allocate(array_type.clone()); + builder.insert_store(v4, nested_array_constant); let b1 = builder.insert_block(); let zero_u32 = builder.numeric_constant(0u128, Type::unsigned(32)); @@ -212,35 +252,38 @@ mod tests { builder.switch_to_block(b1); let v5 = builder.add_block_parameter(b1, Type::unsigned(32)); let five = builder.numeric_constant(5u128, Type::unsigned(32)); - let v7 = builder.insert_binary(v5, BinaryOp::Lt, five); + let v8 = builder.insert_binary(v5, BinaryOp::Lt, five); let b2 = builder.insert_block(); let b3 = builder.insert_block(); let b4 = builder.insert_block(); let b5 = builder.insert_block(); - builder.terminate_with_jmpif(v7, b3, b2); + builder.terminate_with_jmpif(v8, b3, b2); // Loop body // b3 is the if statement conditional builder.switch_to_block(b3); let two = builder.numeric_constant(5u128, Type::unsigned(32)); - let v8 = builder.insert_binary(v5, BinaryOp::Eq, two); - builder.terminate_with_jmpif(v8, b4, b5); + let v9 = builder.insert_binary(v5, BinaryOp::Eq, two); + builder.terminate_with_jmpif(v9, b4, b5); // b4 is the rest of the loop after the if statement builder.switch_to_block(b4); - let v9 = builder.insert_load(v2, array_type.clone()); - builder.insert_store(v3, v9); + let v10 = builder.insert_load(v3, nested_array_type.clone()); + builder.insert_store(v4, v10); builder.terminate_with_jmp(b5, vec![]); builder.switch_to_block(b5); - let v10 = builder.insert_load(v2, array_type.clone()); + let v11 = builder.insert_load(v3, nested_array_type.clone()); let twenty = builder.field_constant(20u128); - let v12 = builder.insert_array_set(v10, v5, twenty); - builder.insert_store(v2, v12); + let v13 = builder.insert_array_get(v11, zero, array_type.clone()); + let v14 = builder.insert_array_set(v13, v5, twenty); + let v15 = builder.insert_array_set(v11, v5, v14); + + builder.insert_store(v3, v15); let one = builder.numeric_constant(1u128, Type::unsigned(32)); - let v14 = builder.insert_binary(v5, BinaryOp::Add, one); - builder.terminate_with_jmp(b1, vec![v14]); + let v17 = builder.insert_binary(v5, BinaryOp::Add, one); + builder.terminate_with_jmp(b1, vec![v17]); builder.switch_to_block(b2); builder.terminate_with_return(vec![]); @@ -258,7 +301,7 @@ mod tests { .filter(|instruction| matches!(&main.dfg[**instruction], Instruction::ArraySet { .. })) .collect::>(); - assert_eq!(array_set_instructions.len(), 1); + assert_eq!(array_set_instructions.len(), 2); if let Instruction::ArraySet { mutable, .. } = &main.dfg[*array_set_instructions[0]] { // The single array set should not be marked mutable assert!(!mutable); diff --git a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs index 69eab1da0ed..59917e8589b 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs @@ -20,13 +20,19 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn as_slice_optimization(mut self) -> Self { for func in self.functions.values_mut() { - let known_slice_lengths = known_slice_lengths(func); - replace_known_slice_lengths(func, known_slice_lengths); + func.as_slice_optimization(); } self } } +impl Function { + pub(crate) fn as_slice_optimization(&mut self) { + let known_slice_lengths = known_slice_lengths(self); + replace_known_slice_lengths(self, known_slice_lengths); + } +} + fn known_slice_lengths(func: &Function) -> HashMap { let mut known_slice_lengths = HashMap::default(); for block_id in func.reachable_blocks() { diff --git a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs index ae0681a55ff..348c78683a0 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs @@ -26,22 +26,31 @@ impl Ssa { mut self, ) -> Result { for function in self.functions.values_mut() { - for block in function.reachable_blocks() { - // Unfortunately we can't just use instructions.retain(...) here since - // check_instruction can also return an error - let instructions = function.dfg[block].take_instructions(); - let mut filtered_instructions = Vec::with_capacity(instructions.len()); + function.evaluate_static_assert_and_assert_constant()?; + } + Ok(self) + } +} - for instruction in instructions { - if check_instruction(function, instruction)? { - filtered_instructions.push(instruction); - } - } +impl Function { + pub(crate) fn evaluate_static_assert_and_assert_constant( + &mut self, + ) -> Result<(), RuntimeError> { + for block in self.reachable_blocks() { + // Unfortunately we can't just use instructions.retain(...) here since + // check_instruction can also return an error + let instructions = self.dfg[block].take_instructions(); + let mut filtered_instructions = Vec::with_capacity(instructions.len()); - *function.dfg[block].instructions_mut() = filtered_instructions; + for instruction in instructions { + if check_instruction(self, instruction)? { + filtered_instructions.push(instruction); + } } + + *self.dfg[block].instructions_mut() = filtered_instructions; } - Ok(self) + Ok(()) } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index ff9a63c8d79..ea422fdff09 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -44,7 +44,7 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn fold_constants(mut self) -> Ssa { for function in self.functions.values_mut() { - constant_fold(function, false); + function.constant_fold(false); } self } @@ -57,25 +57,27 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn fold_constants_using_constraints(mut self) -> Ssa { for function in self.functions.values_mut() { - constant_fold(function, true); + function.constant_fold(true); } self } } -/// The structure of this pass is simple: -/// Go through each block and re-insert all instructions. -fn constant_fold(function: &mut Function, use_constraint_info: bool) { - let mut context = Context { use_constraint_info, ..Default::default() }; - context.block_queue.push(function.entry_block()); +impl Function { + /// The structure of this pass is simple: + /// Go through each block and re-insert all instructions. + pub(crate) fn constant_fold(&mut self, use_constraint_info: bool) { + let mut context = Context { use_constraint_info, ..Default::default() }; + context.block_queue.push(self.entry_block()); - while let Some(block) = context.block_queue.pop() { - if context.visited_blocks.contains(&block) { - continue; - } + while let Some(block) = context.block_queue.pop() { + if context.visited_blocks.contains(&block) { + continue; + } - context.visited_blocks.insert(block); - context.fold_constants_in_block(function, block); + context.visited_blocks.insert(block); + context.fold_constants_in_block(self, block); + } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 33351405606..21b10e918f9 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -25,44 +25,46 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn dead_instruction_elimination(mut self) -> Ssa { for function in self.functions.values_mut() { - dead_instruction_elimination(function, true); + function.dead_instruction_elimination(true); } self } } -/// Removes any unused instructions in the reachable blocks of the given function. -/// -/// The blocks of the function are iterated in post order, such that any blocks containing -/// instructions that reference results from an instruction in another block are evaluated first. -/// If we did not iterate blocks in this order we could not safely say whether or not the results -/// of its instructions are needed elsewhere. -fn dead_instruction_elimination(function: &mut Function, insert_out_of_bounds_checks: bool) { - let mut context = Context::default(); - for call_data in &function.dfg.data_bus.call_data { - context.mark_used_instruction_results(&function.dfg, call_data.array_id); - } +impl Function { + /// Removes any unused instructions in the reachable blocks of the given function. + /// + /// The blocks of the function are iterated in post order, such that any blocks containing + /// instructions that reference results from an instruction in another block are evaluated first. + /// If we did not iterate blocks in this order we could not safely say whether or not the results + /// of its instructions are needed elsewhere. + pub(crate) fn dead_instruction_elimination(&mut self, insert_out_of_bounds_checks: bool) { + let mut context = Context::default(); + for call_data in &self.dfg.data_bus.call_data { + context.mark_used_instruction_results(&self.dfg, call_data.array_id); + } - let mut inserted_out_of_bounds_checks = false; + let mut inserted_out_of_bounds_checks = false; - let blocks = PostOrder::with_function(function); - for block in blocks.as_slice() { - inserted_out_of_bounds_checks |= context.remove_unused_instructions_in_block( - function, - *block, - insert_out_of_bounds_checks, - ); - } + let blocks = PostOrder::with_function(self); + for block in blocks.as_slice() { + inserted_out_of_bounds_checks |= context.remove_unused_instructions_in_block( + self, + *block, + insert_out_of_bounds_checks, + ); + } - // If we inserted out of bounds check, let's run the pass again with those new - // instructions (we don't want to remove those checks, or instructions that are - // dependencies of those checks) - if inserted_out_of_bounds_checks { - dead_instruction_elimination(function, false); - return; - } + // If we inserted out of bounds check, let's run the pass again with those new + // instructions (we don't want to remove those checks, or instructions that are + // dependencies of those checks) + if inserted_out_of_bounds_checks { + self.dead_instruction_elimination(false); + return; + } - context.remove_rc_instructions(&mut function.dfg); + context.remove_rc_instructions(&mut self.dfg); + } } /// Per function context for tracking unused values and which instructions to remove. diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 68c04e3b4b4..165e0e36b71 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -123,16 +123,22 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn mem2reg(mut self) -> Ssa { for function in self.functions.values_mut() { - let mut context = PerFunctionContext::new(function); - context.mem2reg(); - context.remove_instructions(); - context.update_data_bus(); + function.mem2reg(); } self } } +impl Function { + pub(crate) fn mem2reg(&mut self) { + let mut context = PerFunctionContext::new(self); + context.mem2reg(); + context.remove_instructions(); + context.update_data_bus(); + } +} + struct PerFunctionContext<'f> { cfg: ControlFlowGraph, post_order: PostOrder, diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index 06025fd9e8b..c879f6c8fff 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -22,7 +22,7 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn remove_paired_rc(mut self) -> Ssa { for function in self.functions.values_mut() { - remove_paired_rc(function); + function.remove_paired_rc(); } self } @@ -44,26 +44,28 @@ pub(crate) struct RcInstruction { pub(crate) possibly_mutated: bool, } -/// This function is very simplistic for now. It takes advantage of the fact that dec_rc -/// instructions are currently issued only at the end of a function for parameters and will -/// only check the first and last block for inc & dec rc instructions to be removed. The rest -/// of the function is still checked for array_set instructions. -/// -/// This restriction lets this function largely ignore merging intermediate results from other -/// blocks and handling loops. -fn remove_paired_rc(function: &mut Function) { - // `dec_rc` is only issued for parameters currently so we can speed things - // up a bit by skipping any functions without them. - if !contains_array_parameter(function) { - return; - } +impl Function { + /// This function is very simplistic for now. It takes advantage of the fact that dec_rc + /// instructions are currently issued only at the end of a function for parameters and will + /// only check the first and last block for inc & dec rc instructions to be removed. The rest + /// of the function is still checked for array_set instructions. + /// + /// This restriction lets this function largely ignore merging intermediate results from other + /// blocks and handling loops. + pub(crate) fn remove_paired_rc(&mut self) { + // `dec_rc` is only issued for parameters currently so we can speed things + // up a bit by skipping any functions without them. + if !contains_array_parameter(self) { + return; + } - let mut context = Context::default(); + let mut context = Context::default(); - context.find_rcs_in_entry_block(function); - context.scan_for_array_sets(function); - let to_remove = context.find_rcs_to_remove(function); - remove_instructions(to_remove, function); + context.find_rcs_in_entry_block(self); + context.scan_for_array_sets(self); + let to_remove = context.find_rcs_to_remove(self); + remove_instructions(to_remove, self); + } } fn contains_array_parameter(function: &mut Function) -> bool { diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index 984c0de04ca..4b2d753f072 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -21,24 +21,30 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn remove_bit_shifts(mut self) -> Ssa { for function in self.functions.values_mut() { - remove_bit_shifts(function); + function.remove_bit_shifts(); } self } } -/// The structure of this pass is simple: -/// Go through each block and re-insert all instructions. -fn remove_bit_shifts(function: &mut Function) { - if let RuntimeType::Brillig = function.runtime() { - return; - } +impl Function { + /// The structure of this pass is simple: + /// Go through each block and re-insert all instructions. + pub(crate) fn remove_bit_shifts(&mut self) { + if let RuntimeType::Brillig = self.runtime() { + return; + } - let block = function.entry_block(); - let mut context = - Context { function, new_instructions: Vec::new(), block, call_stack: CallStack::default() }; + let block = self.entry_block(); + let mut context = Context { + function: self, + new_instructions: Vec::new(), + block, + call_stack: CallStack::default(), + }; - context.remove_bit_shifts(); + context.remove_bit_shifts(); + } } struct Context<'f> { diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index a56786b2603..daae2cb08ce 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -16,7 +16,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, - function::Function, + function::{Function, RuntimeType}, instruction::{BinaryOp, Instruction, Intrinsic}, types::Type, value::Value, @@ -29,23 +29,30 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn remove_enable_side_effects(mut self) -> Ssa { for function in self.functions.values_mut() { - remove_enable_side_effects(function); + function.remove_enable_side_effects(); } self } } -fn remove_enable_side_effects(function: &mut Function) { - let mut context = Context::default(); - context.block_queue.push(function.entry_block()); - - while let Some(block) = context.block_queue.pop() { - if context.visited_blocks.contains(&block) { - continue; +impl Function { + pub(crate) fn remove_enable_side_effects(&mut self) { + if matches!(self.runtime(), RuntimeType::Brillig) { + // Brillig functions do not make use of the `EnableSideEffects` instruction so are unaffected by this pass. + return; } - context.visited_blocks.insert(block); - context.remove_enable_side_effects_in_block(function, block); + let mut context = Context::default(); + context.block_queue.push(self.entry_block()); + + while let Some(block) = context.block_queue.pop() { + if context.visited_blocks.contains(&block) { + continue; + } + + context.visited_blocks.insert(block); + context.remove_enable_side_effects_in_block(self, block); + } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 8d6225afabf..9f01800bca6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -28,17 +28,23 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn remove_if_else(mut self) -> Ssa { for function in self.functions.values_mut() { - // This should match the check in flatten_cfg - if let crate::ssa::ir::function::RuntimeType::Brillig = function.runtime() { - continue; - } - - Context::default().remove_if_else(function); + function.remove_if_else(); } self } } +impl Function { + pub(crate) fn remove_if_else(&mut self) { + // This should match the check in flatten_cfg + if let crate::ssa::ir::function::RuntimeType::Brillig = self.runtime() { + // skip + } else { + Context::default().remove_if_else(self); + } + } +} + #[derive(Default)] struct Context { slice_sizes: HashMap, diff --git a/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs b/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs index 2c9e33ae528..1768cbddec3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs @@ -17,40 +17,42 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn resolve_is_unconstrained(mut self) -> Self { for func in self.functions.values_mut() { - replace_is_unconstrained_result(func); + func.replace_is_unconstrained_result(); } self } } -fn replace_is_unconstrained_result(func: &mut Function) { - let mut is_unconstrained_calls = HashSet::default(); - // Collect all calls to is_unconstrained - for block_id in func.reachable_blocks() { - for &instruction_id in func.dfg[block_id].instructions() { - let target_func = match &func.dfg[instruction_id] { - Instruction::Call { func, .. } => *func, - _ => continue, - }; +impl Function { + pub(crate) fn replace_is_unconstrained_result(&mut self) { + let mut is_unconstrained_calls = HashSet::default(); + // Collect all calls to is_unconstrained + for block_id in self.reachable_blocks() { + for &instruction_id in self.dfg[block_id].instructions() { + let target_func = match &self.dfg[instruction_id] { + Instruction::Call { func, .. } => *func, + _ => continue, + }; - if let Value::Intrinsic(Intrinsic::IsUnconstrained) = &func.dfg[target_func] { - is_unconstrained_calls.insert(instruction_id); + if let Value::Intrinsic(Intrinsic::IsUnconstrained) = &self.dfg[target_func] { + is_unconstrained_calls.insert(instruction_id); + } } } - } - for instruction_id in is_unconstrained_calls { - let call_returns = func.dfg.instruction_results(instruction_id); - let original_return_id = call_returns[0]; + for instruction_id in is_unconstrained_calls { + let call_returns = self.dfg.instruction_results(instruction_id); + let original_return_id = call_returns[0]; - // We replace the result with a fresh id. This will be unused, so the DIE pass will remove the leftover intrinsic call. - func.dfg.replace_result(instruction_id, original_return_id); + // We replace the result with a fresh id. This will be unused, so the DIE pass will remove the leftover intrinsic call. + self.dfg.replace_result(instruction_id, original_return_id); - let is_within_unconstrained = func.dfg.make_constant( - FieldElement::from(matches!(func.runtime(), RuntimeType::Brillig)), - Type::bool(), - ); - // Replace all uses of the original return value with the constant - func.dfg.set_value_from_id(original_return_id, is_within_unconstrained); + let is_within_unconstrained = self.dfg.make_constant( + FieldElement::from(matches!(self.runtime(), RuntimeType::Brillig)), + Type::bool(), + ); + // Replace all uses of the original return value with the constant + self.dfg.set_value_from_id(original_return_id, is_within_unconstrained); + } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index 6887873dbc3..46941775c5e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -36,48 +36,50 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn simplify_cfg(mut self) -> Self { for function in self.functions.values_mut() { - simplify_function(function); + function.simplify_function(); } self } } -/// Simplify a function's cfg by going through each block to check for any simple blocks that can -/// be inlined into their predecessor. -fn simplify_function(function: &mut Function) { - let mut cfg = ControlFlowGraph::with_function(function); - let mut stack = vec![function.entry_block()]; - let mut visited = HashSet::new(); - - while let Some(block) = stack.pop() { - if visited.insert(block) { - stack.extend(function.dfg[block].successors().filter(|block| !visited.contains(block))); - } - - // This call is before try_inline_into_predecessor so that if it succeeds in changing a - // jmpif into a jmp, the block may then be inlined entirely into its predecessor in try_inline_into_predecessor. - check_for_constant_jmpif(function, block, &mut cfg); - - let mut predecessors = cfg.predecessors(block); - - if predecessors.len() == 1 { - let predecessor = predecessors.next().expect("Already checked length of predecessors"); - drop(predecessors); - - // If the block has only 1 predecessor, we can safely remove its block parameters - remove_block_parameters(function, block, predecessor); - - // Note: this function relies on `remove_block_parameters` being called first. - // Otherwise the inlined block will refer to parameters that no longer exist. - // - // If successful, `block` will be empty and unreachable after this call, so any - // optimizations performed after this point on the same block should check if - // the inlining here was successful before continuing. - try_inline_into_predecessor(function, &mut cfg, block, predecessor); - } else { - drop(predecessors); +impl Function { + /// Simplify a function's cfg by going through each block to check for any simple blocks that can + /// be inlined into their predecessor. + pub(crate) fn simplify_function(&mut self) { + let mut cfg = ControlFlowGraph::with_function(self); + let mut stack = vec![self.entry_block()]; + let mut visited = HashSet::new(); + + while let Some(block) = stack.pop() { + if visited.insert(block) { + stack.extend(self.dfg[block].successors().filter(|block| !visited.contains(block))); + } - check_for_double_jmp(function, block, &mut cfg); + // This call is before try_inline_into_predecessor so that if it succeeds in changing a + // jmpif into a jmp, the block may then be inlined entirely into its predecessor in try_inline_into_predecessor. + check_for_constant_jmpif(self, block, &mut cfg); + + let mut predecessors = cfg.predecessors(block); + if predecessors.len() == 1 { + let predecessor = + predecessors.next().expect("Already checked length of predecessors"); + drop(predecessors); + + // If the block has only 1 predecessor, we can safely remove its block parameters + remove_block_parameters(self, block, predecessor); + + // Note: this function relies on `remove_block_parameters` being called first. + // Otherwise the inlined block will refer to parameters that no longer exist. + // + // If successful, `block` will be empty and unreachable after this call, so any + // optimizations performed after this point on the same block should check if + // the inlining here was successful before continuing. + try_inline_into_predecessor(self, &mut cfg, block, predecessor); + } else { + drop(predecessors); + + check_for_double_jmp(self, block, &mut cfg); + } } } } @@ -96,14 +98,23 @@ fn check_for_constant_jmpif( }) = function.dfg[block].terminator() { if let Some(constant) = function.dfg.get_numeric_constant(*condition) { - let destination = - if constant.is_zero() { *else_destination } else { *then_destination }; + let (destination, unchosen_destination) = if constant.is_zero() { + (*else_destination, *then_destination) + } else { + (*then_destination, *else_destination) + }; let arguments = Vec::new(); let call_stack = call_stack.clone(); let jmp = TerminatorInstruction::Jmp { destination, arguments, call_stack }; function.dfg[block].set_terminator(jmp); cfg.recompute_block(function, block); + + // If `block` was the only predecessor to `unchosen_destination` then it's no long reachable through the CFG, + // we can then invalidate it successors as it's an invalid predecessor. + if cfg.predecessors(unchosen_destination).len() == 0 { + cfg.invalidate_block_successors(unchosen_destination); + } } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index f3e11e04e3a..d6ed11ddf0e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -74,16 +74,49 @@ impl Ssa { pub(crate) fn try_to_unroll_loops(mut self) -> (Ssa, Vec) { let mut errors = vec![]; for function in self.functions.values_mut() { - // Loop unrolling in brillig can lead to a code explosion currently. This can - // also be true for ACIR, but we have no alternative to unrolling in ACIR. - // Brillig also generally prefers smaller code rather than faster code. - if function.runtime() == RuntimeType::Brillig { - continue; + function.try_to_unroll_loops(&mut errors); + } + (self, errors) + } +} + +impl Function { + // TODO(https://github.com/noir-lang/noir/issues/6192): are both this and + // TODO: Ssa::unroll_loops_iteratively needed? Likely able to be combined + pub(crate) fn unroll_loops_iteratively(&mut self) -> Result<(), RuntimeError> { + // Try to unroll loops first: + let mut unroll_errors = vec![]; + self.try_to_unroll_loops(&mut unroll_errors); + + // Keep unrolling until no more errors are found + while !unroll_errors.is_empty() { + let prev_unroll_err_count = unroll_errors.len(); + + // Simplify the SSA before retrying + + // Do a mem2reg after the last unroll to aid simplify_cfg + self.mem2reg(); + self.simplify_function(); + // Do another mem2reg after simplify_cfg to aid the next unroll + self.mem2reg(); + + // Unroll again + self.try_to_unroll_loops(&mut unroll_errors); + // If we didn't manage to unroll any more loops, exit + if unroll_errors.len() >= prev_unroll_err_count { + return Err(unroll_errors.swap_remove(0)); } + } + Ok(()) + } - errors.extend(find_all_loops(function).unroll_each_loop(function)); + pub(crate) fn try_to_unroll_loops(&mut self, errors: &mut Vec) { + // Loop unrolling in brillig can lead to a code explosion currently. This can + // also be true for ACIR, but we have no alternative to unrolling in ACIR. + // Brillig also generally prefers smaller code rather than faster code. + if self.runtime() != RuntimeType::Brillig { + errors.extend(find_all_loops(self).unroll_each_loop(self)); } - (self, errors) } } diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 7b0a6d028de..362f94171d3 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -1,13 +1,15 @@ use std::borrow::Cow; use std::fmt::Display; +use thiserror::Error; + use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; -use crate::hir::def_collector::errors::DefCollectorErrorKind; -use crate::macros_api::StructId; -use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; +use crate::node_interner::{ + ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId, StructId, +}; use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; @@ -75,6 +77,13 @@ pub enum UnresolvedGeneric { Resolved(QuotedTypeId, Span), } +#[derive(Error, PartialEq, Eq, Debug, Clone)] +#[error("The only supported types of numeric generics are integers, fields, and booleans")] +pub struct UnsupportedNumericGenericType { + pub ident: Ident, + pub typ: UnresolvedTypeData, +} + impl UnresolvedGeneric { pub fn span(&self) -> Span { match self { @@ -84,7 +93,7 @@ impl UnresolvedGeneric { } } - pub fn kind(&self) -> Result { + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), UnresolvedGeneric::Numeric { typ, .. } => { @@ -100,14 +109,14 @@ impl UnresolvedGeneric { fn resolve_numeric_kind_type( &self, typ: &UnresolvedType, - ) -> Result { + ) -> Result { use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; match typ.typ { FieldElement => Ok(Type::FieldElement), Integer(sign, bits) => Ok(Type::Integer(sign, bits)), // Only fields and integers are supported for numeric kinds - _ => Err(DefCollectorErrorKind::UnsupportedNumericGenericType { + _ => Err(UnsupportedNumericGenericType { ident: self.ident().clone(), typ: typ.typ.clone(), }), diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 4abea8cebb4..2d835482082 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -10,12 +10,14 @@ use super::{ BlockExpression, ConstructorExpression, Expression, ExpressionKind, GenericTypeArgs, IndexExpression, ItemVisibility, MemberAccessExpression, MethodCallExpression, UnresolvedType, }; +use crate::ast::UnresolvedTypeData; use crate::elaborator::types::SELF_TYPE_NAME; use crate::lexer::token::SpannedToken; -use crate::macros_api::{NodeInterner, SecondaryAttribute, UnresolvedTypeData}; -use crate::node_interner::{InternedExpressionKind, InternedPattern, InternedStatementKind}; +use crate::node_interner::{ + InternedExpressionKind, InternedPattern, InternedStatementKind, NodeInterner, +}; use crate::parser::{ParserError, ParserErrorReason}; -use crate::token::Token; +use crate::token::{SecondaryAttribute, Token}; /// This is used when an identifier fails to parse in the parser. /// Instead of failing the parse, we can often recover using this diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index 3df9939dc70..d2fa95e4f5a 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -7,8 +7,8 @@ use crate::ast::{ BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, Path, UnresolvedGenerics, UnresolvedType, }; -use crate::macros_api::SecondaryAttribute; use crate::node_interner::TraitId; +use crate::token::SecondaryAttribute; use super::{Documented, GenericTypeArgs, ItemVisibility}; diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 560be895628..4d6095724f4 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -6,7 +6,7 @@ use iter_extended::vecmap; use noirc_errors::{Location, Span}; use crate::{ - ast::Documented, + ast::{Documented, Expression, ExpressionKind}, hir::{ comptime::{Interpreter, InterpreterError, Value}, def_collector::{ @@ -19,13 +19,11 @@ use crate::{ def_map::{LocalModuleId, ModuleId}, resolution::errors::ResolverError, }, - hir_def::expr::HirIdent, + hir_def::expr::{HirExpression, HirIdent}, lexer::Lexer, - macros_api::{ - Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, - }, - node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, + node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, StructId, TraitId}, parser::{self, TopLevelStatement, TopLevelStatementKind}, + token::SecondaryAttribute, Type, TypeBindings, UnificationError, }; diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 46a22bb232f..cbd72788c85 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -5,8 +5,10 @@ use rustc_hash::FxHashSet as HashSet; use crate::{ ast::{ - ArrayLiteral, ConstructorExpression, IfExpression, InfixExpression, Lambda, UnaryOp, - UnresolvedTypeData, UnresolvedTypeExpression, + ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, + Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, Lambda, + Literal, MemberAccessExpression, MethodCallExpression, PrefixExpression, StatementKind, + UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, }, hir::{ comptime::{self, InterpreterError}, @@ -17,16 +19,12 @@ use crate::{ expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstructorExpression, HirExpression, HirIfExpression, HirIndexExpression, - HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, + HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, }, + stmt::HirStatement, traits::TraitConstraint, }, - macros_api::{ - BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirLiteral, - HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, - MethodCallExpression, PrefixExpression, StatementKind, - }, node_interner::{DefinitionKind, ExprId, FuncId, InternedStatementKind, TraitMethodId}, token::Tokens, Kind, QuotedType, Shared, StructType, Type, diff --git a/compiler/noirc_frontend/src/elaborator/lints.rs b/compiler/noirc_frontend/src/elaborator/lints.rs index 8253921d305..c0a18d219b7 100644 --- a/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/compiler/noirc_frontend/src/elaborator/lints.rs @@ -1,14 +1,15 @@ use crate::{ - ast::{FunctionKind, Ident}, + ast::{FunctionKind, Ident, NoirFunction, Signedness, UnaryOp, Visibility}, graph::CrateId, hir::{ resolution::errors::{PubPosition, ResolverError}, type_check::TypeCheckError, }, - hir_def::{expr::HirIdent, function::FuncMeta}, - macros_api::{ - HirExpression, HirLiteral, NodeInterner, NoirFunction, Signedness, UnaryOp, Visibility, + hir_def::{ + expr::{HirExpression, HirIdent, HirLiteral}, + function::FuncMeta, }, + node_interner::NodeInterner, node_interner::{DefinitionKind, ExprId, FuncId, FunctionModifiers}, Type, }; diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index c5e457c405b..bc1976febd4 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -3,56 +3,42 @@ use std::{ rc::Rc, }; +use crate::ast::ItemVisibility; use crate::{ - ast::{FunctionKind, GenericTypeArgs, UnresolvedTraitConstraint}, + ast::{ + BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, + Path, Pattern, TraitBound, UnresolvedGeneric, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedTypeData, UnsupportedNumericGenericType, + }, + graph::CrateId, hir::{ def_collector::dc_crate::{ - filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal, UnresolvedStruct, - UnresolvedTypeAlias, + filter_literal_globals, CompilationError, ImplMap, UnresolvedFunctions, + UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, UnresolvedTypeAlias, }, - def_map::DefMaps, + def_collector::{dc_crate::CollectedItems, errors::DefCollectorErrorKind}, + def_map::{DefMaps, ModuleData}, + def_map::{LocalModuleId, ModuleDefId, ModuleId, MAIN_FUNCTION}, resolution::errors::ResolverError, + resolution::import::PathResolution, scope::ScopeForest as GenericScopeForest, type_check::{generics::TraitGenerics, TypeCheckError}, + Context, }, + hir_def::traits::TraitImpl, hir_def::{ expr::{HirCapturedVar, HirIdent}, - function::FunctionBody, + function::{FuncMeta, FunctionBody, HirFunction}, traits::TraitConstraint, types::{Generics, Kind, ResolvedGeneric}, }, - macros_api::{ - BlockExpression, Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, - SecondaryAttribute, StructId, - }, node_interner::{ - DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, ReferenceId, - TraitId, TypeAliasId, + DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner, + ReferenceId, StructId, TraitId, TraitImplId, TypeAliasId, }, - token::CustomAttribute, + token::{CustomAttribute, SecondaryAttribute}, Shared, Type, TypeVariable, }; -use crate::{ - ast::{TraitBound, UnresolvedGeneric, UnresolvedGenerics}, - graph::CrateId, - hir::{ - def_collector::{dc_crate::CollectedItems, errors::DefCollectorErrorKind}, - def_map::{LocalModuleId, ModuleDefId, ModuleId, MAIN_FUNCTION}, - resolution::import::PathResolution, - Context, - }, - hir_def::function::{FuncMeta, HirFunction}, - macros_api::{Param, Path, UnresolvedTypeData}, - node_interner::TraitImplId, -}; -use crate::{ - hir::{ - def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, - def_map::ModuleData, - }, - hir_def::traits::TraitImpl, - macros_api::ItemVisibility, -}; mod comptime; mod expressions; @@ -359,8 +345,9 @@ impl<'context> Elaborator<'context> { fn introduce_generics_into_scope(&mut self, all_generics: Vec) { // Introduce all numeric generics into scope for generic in &all_generics { - if let Kind::Numeric(typ) = &generic.kind { - let definition = DefinitionKind::GenericType(generic.type_var.clone()); + if let Kind::Numeric(typ) = &generic.kind() { + let definition = + DefinitionKind::NumericGeneric(generic.type_var.clone(), typ.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); let hir_ident = self.add_variable_decl( ident, false, // mutable @@ -475,9 +462,9 @@ impl<'context> Elaborator<'context> { let context = self.function_context.pop().expect("Imbalanced function_context pushes"); for typ in context.type_variables { - if let Type::TypeVariable(variable, kind) = typ.follow_bindings() { + if let Type::TypeVariable(variable) = typ.follow_bindings() { let msg = "TypeChecker should only track defaultable type vars"; - variable.bind(kind.default_type().expect(msg)); + variable.bind(variable.kind().default_type().expect(msg)); } } @@ -515,11 +502,12 @@ impl<'context> Elaborator<'context> { trait_constraints: &mut Vec, ) -> Type { let new_generic_id = self.interner.next_type_variable_id(); - let new_generic = TypeVariable::unbound(new_generic_id); + + let new_generic = TypeVariable::unbound(new_generic_id, Kind::Normal); generics.push(new_generic.clone()); let name = format!("impl {trait_path}"); - let generic_type = Type::NamedGeneric(new_generic, Rc::new(name), Kind::Normal); + let generic_type = Type::NamedGeneric(new_generic, Rc::new(name)); let trait_bound = TraitBound { trait_path, trait_id: None, trait_generics }; if let Some(new_constraint) = self.resolve_trait_bound(&trait_bound, generic_type.clone()) { @@ -534,19 +522,20 @@ impl<'context> Elaborator<'context> { pub fn add_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { vecmap(generics, |generic| { let mut is_error = false; - let (type_var, name, kind) = match self.resolve_generic(generic) { + let (type_var, name) = match self.resolve_generic(generic) { Ok(values) => values, Err(error) => { self.push_err(error); is_error = true; let id = self.interner.next_type_variable_id(); - (TypeVariable::unbound(id), Rc::new("(error)".into()), Kind::Normal) + let kind = self.resolve_generic_kind(generic); + (TypeVariable::unbound(id, kind), Rc::new("(error)".into())) } }; let span = generic.span(); let name_owned = name.as_ref().clone(); - let resolved_generic = ResolvedGeneric { name, type_var, kind, span }; + let resolved_generic = ResolvedGeneric { name, type_var, span }; // Check for name collisions of this generic // Checking `is_error` here prevents DuplicateDefinition errors when @@ -571,25 +560,22 @@ impl<'context> Elaborator<'context> { fn resolve_generic( &mut self, generic: &UnresolvedGeneric, - ) -> Result<(TypeVariable, Rc, Kind), ResolverError> { + ) -> Result<(TypeVariable, Rc), ResolverError> { // Map the generic to a fresh type variable match generic { UnresolvedGeneric::Variable(_) | UnresolvedGeneric::Numeric { .. } => { let id = self.interner.next_type_variable_id(); - let typevar = TypeVariable::unbound(id); - let ident = generic.ident(); - let kind = self.resolve_generic_kind(generic); + let typevar = TypeVariable::unbound(id, kind); + let ident = generic.ident(); let name = Rc::new(ident.0.contents.clone()); - Ok((typevar, name, kind)) + Ok((typevar, name)) } // An already-resolved generic is only possible if it is the result of a // previous macro call being inserted into a generics list. UnresolvedGeneric::Resolved(id, span) => { match self.interner.get_quoted_type(*id).follow_bindings() { - Type::NamedGeneric(type_variable, name, kind) => { - Ok((type_variable, name, kind)) - } + Type::NamedGeneric(type_variable, name) => Ok((type_variable.clone(), name)), other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { span: *span, typ: other.clone(), @@ -604,17 +590,21 @@ impl<'context> Elaborator<'context> { /// sure only primitive numeric types are being used. pub(super) fn resolve_generic_kind(&mut self, generic: &UnresolvedGeneric) -> Kind { if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let typ = typ.clone(); - let typ = if typ.is_type_expression() { - self.resolve_type_inner(typ, &Kind::Numeric(Box::new(Type::default_int_type()))) + let unresolved_typ = typ.clone(); + let typ = if unresolved_typ.is_type_expression() { + self.resolve_type_inner( + unresolved_typ.clone(), + &Kind::Numeric(Box::new(Type::default_int_type())), + ) } else { - self.resolve_type(typ.clone()) + self.resolve_type(unresolved_typ.clone()) }; if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = ResolverError::UnsupportedNumericGenericType { - ident: ident.clone(), - typ: typ.clone(), - }; + let unsupported_typ_err = + ResolverError::UnsupportedNumericGenericType(UnsupportedNumericGenericType { + ident: ident.clone(), + typ: unresolved_typ.typ.clone(), + }); self.push_err(unsupported_typ_err); } Kind::Numeric(Box::new(typ)) @@ -749,6 +739,7 @@ impl<'context> Elaborator<'context> { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) } + // Function parameters have Kind::Normal _ => self.resolve_type_inner(typ, &Kind::Normal), }; diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 6ed59a61e4e..132d1988b78 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -3,19 +3,20 @@ use noirc_errors::{Location, Span}; use rustc_hash::FxHashSet as HashSet; use crate::{ - ast::{TypePath, UnresolvedType, ERROR_IDENT}, + ast::{ + Expression, ExpressionKind, Ident, Path, Pattern, TypePath, UnresolvedType, ERROR_IDENT, + }, hir::{ def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, type_check::{Source, TypeCheckError}, }, hir_def::{ - expr::{HirIdent, HirMethodReference, ImplKind}, + expr::{HirExpression, HirIdent, HirMethodReference, ImplKind}, stmt::HirPattern, }, - macros_api::{Expression, ExpressionKind, HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - ResolvedGeneric, Shared, StructType, Type, TypeBindings, + Kind, ResolvedGeneric, Shared, StructType, Type, TypeBindings, }; use super::{Elaborator, ResolverMeta}; @@ -488,7 +489,7 @@ impl<'context> Elaborator<'context> { ) -> Vec { let generics_with_types = generics.iter().zip(turbofish_generics); vecmap(generics_with_types, |(generic, unresolved_type)| { - self.resolve_type_inner(unresolved_type, &generic.kind) + self.resolve_type_inner(unresolved_type, &generic.kind()) }) } @@ -557,13 +558,14 @@ impl<'context> Elaborator<'context> { self.interner.add_global_reference(global_id, hir_ident.location); } - DefinitionKind::GenericType(_) => { + DefinitionKind::NumericGeneric(_, ref numeric_typ) => { // Initialize numeric generics to a polymorphic integer type in case // they're used in expressions. We must do this here since type_check_variable // does not check definition kinds and otherwise expects parameters to // already be typed. if self.interner.definition_type(hir_ident.id) == Type::Error { - let typ = Type::polymorphic_integer_or_field(self.interner); + let type_var_kind = Kind::Numeric(numeric_typ.clone()); + let typ = self.type_variable_with_kind(type_var_kind); self.interner.push_definition_type(hir_ident.id, typ); } } diff --git a/compiler/noirc_frontend/src/elaborator/scope.rs b/compiler/noirc_frontend/src/elaborator/scope.rs index 8e746256142..0fb5a58035a 100644 --- a/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/compiler/noirc_frontend/src/elaborator/scope.rs @@ -1,11 +1,10 @@ use noirc_errors::{Location, Spanned}; -use crate::ast::{PathKind, ERROR_IDENT}; +use crate::ast::{Ident, Path, PathKind, ERROR_IDENT}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::resolution::import::{PathResolution, PathResolutionResult}; use crate::hir::resolution::path_resolver::{PathResolver, StandardPathResolver}; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; -use crate::macros_api::Ident; use crate::{ hir::{ def_map::{ModuleDefId, TryFromModuleDefId}, @@ -15,8 +14,7 @@ use crate::{ expr::{HirCapturedVar, HirIdent}, traits::Trait, }, - macros_api::{Path, StructId}, - node_interner::{DefinitionId, TraitId}, + node_interner::{DefinitionId, StructId, TraitId}, Shared, StructType, }; use crate::{Type, TypeAlias}; diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 55b641ca3d4..204a7f9cd75 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -3,7 +3,8 @@ use noirc_errors::{Location, Span, Spanned}; use crate::{ ast::{ AssignStatement, BinaryOpKind, ConstrainKind, ConstrainStatement, Expression, - ExpressionKind, InfixExpression, LValue, + ExpressionKind, ForLoopStatement, ForRange, InfixExpression, LValue, LetStatement, Path, + Statement, StatementKind, }, hir::{ resolution::errors::ResolverError, @@ -13,11 +14,9 @@ use crate::{ expr::HirIdent, stmt::{ HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, + HirStatement, }, }, - macros_api::{ - ForLoopStatement, ForRange, HirStatement, LetStatement, Path, Statement, StatementKind, - }, node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, Type, }; diff --git a/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/compiler/noirc_frontend/src/elaborator/trait_impls.rs index aa7e1cb89c5..858cfa5cdd6 100644 --- a/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -1,8 +1,7 @@ use crate::{ - ast::UnresolvedTypeExpression, + ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, graph::CrateId, hir::def_collector::{dc_crate::UnresolvedTraitImpl, errors::DefCollectorErrorKind}, - macros_api::{Ident, UnresolvedType, UnresolvedTypeData}, node_interner::TraitImplId, ResolvedGeneric, }; @@ -154,11 +153,15 @@ impl<'context> Elaborator<'context> { // Substitute each generic on the trait function with the corresponding generic on the impl function for ( ResolvedGeneric { type_var: trait_fn_generic, .. }, - ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, + ResolvedGeneric { name, type_var: impl_fn_generic, .. }, ) in method.direct_generics.iter().zip(&override_meta.direct_generics) { - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); + let trait_fn_kind = trait_fn_generic.kind(); + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); + bindings.insert( + trait_fn_generic.id(), + (trait_fn_generic.clone(), trait_fn_kind.clone(), arg), + ); } let mut substituted_method_ids = HashSet::default(); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index d7c8769620d..b4042bd3e31 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -5,16 +5,14 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ - FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, + BlockExpression, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, + ItemVisibility, NoirFunction, TraitItem, UnresolvedGeneric, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedType, }, hir::{def_collector::dc_crate::UnresolvedTrait, type_check::TypeCheckError}, hir_def::{function::Parameters, traits::TraitFunction}, - macros_api::{ - BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, - NodeInterner, NoirFunction, UnresolvedType, - }, - node_interner::{FuncId, ReferenceId, TraitId}, - Kind, ResolvedGeneric, Type, TypeBindings, TypeVariableKind, + node_interner::{FuncId, NodeInterner, ReferenceId, TraitId}, + ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; @@ -81,8 +79,7 @@ impl<'context> Elaborator<'context> { self.recover_generics(|this| { let the_trait = this.interner.get_trait(trait_id); let self_typevar = the_trait.self_type_typevar.clone(); - let self_type = - Type::TypeVariable(self_typevar.clone(), TypeVariableKind::Normal); + let self_type = Type::TypeVariable(self_typevar.clone()); let name_span = the_trait.name.span(); this.add_existing_generic( @@ -92,7 +89,6 @@ impl<'context> Elaborator<'context> { name: Rc::new("Self".to_owned()), type_var: self_typevar, span: name_span, - kind: Kind::Normal, }, ); this.self_type = Some(self_type.clone()); @@ -281,11 +277,12 @@ pub(crate) fn check_trait_impl_method_matches_declaration( // Substitute each generic on the trait function with the corresponding generic on the impl function for ( ResolvedGeneric { type_var: trait_fn_generic, .. }, - ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, + ResolvedGeneric { name, type_var: impl_fn_generic, .. }, ) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); + let trait_fn_kind = trait_fn_generic.kind(); + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind, arg)); } let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 264b83956f8..35cd5145579 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -7,8 +7,9 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - AsTraitPath, BinaryOpKind, GenericTypeArgs, IntegerBitSize, UnresolvedGeneric, - UnresolvedGenerics, UnresolvedTypeExpression, + AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, + Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, + UnresolvedTypeData, UnresolvedTypeExpression, }, hir::{ comptime::{Interpreter, Value}, @@ -22,22 +23,20 @@ use crate::{ }, hir_def::{ expr::{ - HirBinaryOp, HirCallExpression, HirMemberAccess, HirMethodReference, - HirPrefixExpression, + HirBinaryOp, HirCallExpression, HirExpression, HirLiteral, HirMemberAccess, + HirMethodReference, HirPrefixExpression, }, function::{FuncMeta, Parameters}, + stmt::HirStatement, traits::{NamedType, TraitConstraint}, }, - macros_api::{ - HirExpression, HirLiteral, HirStatement, Ident, NodeInterner, Path, PathKind, - SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, - }, node_interner::{ - DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, ImplSearchErrorKind, TraitId, - TraitImplKind, TraitMethodId, + DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, ImplSearchErrorKind, NodeInterner, + TraitId, TraitImplKind, TraitMethodId, }, + token::SecondaryAttribute, Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, TypeVariable, - TypeVariableKind, UnificationError, + UnificationError, }; use super::{lints, Elaborator}; @@ -126,7 +125,7 @@ impl<'context> Elaborator<'context> { let env = Box::new(self.resolve_type_inner(*env, kind)); match *env { - Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _, _) => { + Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { Type::Function(args, ret, env, unconstrained) } _ => { @@ -169,14 +168,10 @@ impl<'context> Elaborator<'context> { _ => (), } - if !kind.matches_opt(resolved_type.kind()) { + if !kind.unifies(&resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), - expr_kind: resolved_type - .kind() - .as_ref() - .map(Kind::to_string) - .unwrap_or("unknown".to_string()), + expr_kind: resolved_type.kind().to_string(), expr_span: span, }); self.errors.push((expected_typ_err, self.file)); @@ -230,7 +225,7 @@ impl<'context> Elaborator<'context> { return self_type; } } else if name == WILDCARD_TYPE { - return self.interner.next_type_variable(); + return self.interner.next_type_variable_with_kind(Kind::Any); } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { if !args.is_empty() { @@ -333,7 +328,7 @@ impl<'context> Elaborator<'context> { let ordered_args = expected_kinds.iter().zip(args.ordered_args); let ordered = - vecmap(ordered_args, |(generic, typ)| self.resolve_type_inner(typ, &generic.kind)); + vecmap(ordered_args, |(generic, typ)| self.resolve_type_inner(typ, &generic.kind())); let mut associated = Vec::new(); @@ -377,7 +372,7 @@ impl<'context> Elaborator<'context> { let expected = required_args.remove(index); seen_args.insert(name.0.contents.clone(), name.span()); - let typ = self.resolve_type_inner(typ, &expected.kind); + let typ = self.resolve_type_inner(typ, &expected.kind()); resolved.push(NamedType { name, typ }); } @@ -396,7 +391,7 @@ impl<'context> Elaborator<'context> { let name = path.last_name(); if let Some(generic) = self.find_generic(name) { let generic = generic.clone(); - return Some(Type::NamedGeneric(generic.type_var, generic.name, generic.kind)); + return Some(Type::NamedGeneric(generic.type_var, generic.name)); } } else if let Some(typ) = self.lookup_associated_type_on_self(path) { return Some(typ); @@ -452,7 +447,7 @@ impl<'context> Elaborator<'context> { }); return Type::Error; } - if let Some(result) = op.function(lhs, rhs) { + if let Some(result) = op.function(lhs, rhs, &lhs_kind) { Type::Constant(result, lhs_kind) } else { self.push_err(ResolverError::OverflowInType { lhs, op, rhs, span }); @@ -470,15 +465,13 @@ impl<'context> Elaborator<'context> { } fn check_kind(&mut self, typ: Type, expected_kind: &Kind, span: Span) -> Type { - if let Some(kind) = typ.kind() { - if !kind.unifies(expected_kind) { - self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: expected_kind.to_string(), - expr_kind: kind.to_string(), - expr_span: span, - }); - return Type::Error; - } + if !typ.kind().unifies(expected_kind) { + self.push_err(TypeCheckError::TypeKindMismatch { + expected_kind: expected_kind.to_string(), + expr_kind: typ.kind().to_string(), + expr_span: span, + }); + return Type::Error; } typ } @@ -580,7 +573,7 @@ impl<'context> Elaborator<'context> { } for constraint in self.trait_bounds.clone() { - if let Type::NamedGeneric(_, name, _) = &constraint.typ { + if let Type::NamedGeneric(_, name) = &constraint.typ { // if `path` is `T::method_name`, we're looking for constraint of the form `T: SomeTrait` if path.segments[0].ident.0.contents != name.as_str() { continue; @@ -697,11 +690,21 @@ impl<'context> Elaborator<'context> { typ } + /// Return a fresh integer type variable and log it + /// in self.type_variables to default it later. + pub(super) fn type_variable_with_kind(&mut self, type_var_kind: Kind) -> Type { + let typ = Type::type_variable_with_kind(self.interner, type_var_kind); + self.push_type_variable(typ.clone()); + typ + } + /// Translates a (possibly Unspecified) UnresolvedType to a Type. /// Any UnresolvedType::Unspecified encountered are replaced with fresh type variables. pub(super) fn resolve_inferred_type(&mut self, typ: UnresolvedType) -> Type { match &typ.typ { - UnresolvedTypeData::Unspecified => self.interner.next_type_variable(), + UnresolvedTypeData::Unspecified => { + self.interner.next_type_variable_with_kind(Kind::Any) + } _ => self.resolve_type(typ), } } @@ -790,7 +793,7 @@ impl<'context> Elaborator<'context> { // Could do a single unification for the entire function type, but matching beforehand // lets us issue a more precise error on the individual argument that fails to type check. match function { - Type::TypeVariable(binding, TypeVariableKind::Normal) => { + Type::TypeVariable(binding) if binding.kind() == Kind::Normal => { if let TypeBinding::Bound(typ) = &*binding.borrow() { return self.bind_function_type(typ.clone(), args, span); } @@ -801,7 +804,8 @@ impl<'context> Elaborator<'context> { let expected = Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); - if let Err(error) = binding.try_bind(expected, span) { + let expected_kind = expected.kind(); + if let Err(error) = binding.try_bind(expected, &expected_kind, span) { self.push_err(error); } ret @@ -821,16 +825,14 @@ impl<'context> Elaborator<'context> { pub(super) fn check_cast(&mut self, from: &Type, to: &Type, span: Span) -> Type { match from.follow_bindings() { - Type::Integer(..) - | Type::FieldElement - | Type::TypeVariable(_, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(_, TypeVariableKind::Integer) - | Type::Bool => (), + Type::Integer(..) | Type::FieldElement | Type::Bool => (), - Type::TypeVariable(_, _) => { + Type::TypeVariable(var) if var.is_integer() || var.is_integer_or_field() => (), + + Type::TypeVariable(_) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. - let expected = Type::polymorphic_integer_or_field(self.interner); + let expected = self.polymorphic_integer_or_field(); self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), span, @@ -878,8 +880,8 @@ impl<'context> Elaborator<'context> { // Matches on TypeVariable must be first to follow any type // bindings. - (TypeVariable(var, _), other) | (other, TypeVariable(var, _)) => { - if let TypeBinding::Bound(binding) = &*var.borrow() { + (TypeVariable(var), other) | (other, TypeVariable(var)) => { + if let TypeBinding::Bound(ref binding) = &*var.borrow() { return self.comparator_operand_type_rules(other, binding, op, span); } @@ -943,14 +945,14 @@ impl<'context> Elaborator<'context> { span, }); - let use_impl = !lhs_type.is_numeric(); + let use_impl = !lhs_type.is_numeric_value(); // If this operator isn't valid for fields we have to possibly narrow - // TypeVariableKind::IntegerOrField to TypeVariableKind::Integer. + // Kind::IntegerOrField to Kind::Integer. // Doing so also ensures a type error if Field is used. // The is_numeric check is to allow impls for custom types to bypass this. - if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric() { - let target = Type::polymorphic_integer(self.interner); + if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric_value() { + let target = self.polymorphic_integer(); use crate::ast::BinaryOpKind::*; use TypeCheckError::*; @@ -991,22 +993,22 @@ impl<'context> Elaborator<'context> { // Matches on TypeVariable must be first so that we follow any type // bindings. - (TypeVariable(int, _), other) | (other, TypeVariable(int, _)) => { + (TypeVariable(int), other) | (other, TypeVariable(int)) => { if op.kind == BinaryOpKind::ShiftLeft || op.kind == BinaryOpKind::ShiftRight { self.unify( rhs_type, &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), || TypeCheckError::InvalidShiftSize { span }, ); - let use_impl = if lhs_type.is_numeric() { - let integer_type = Type::polymorphic_integer(self.interner); + let use_impl = if lhs_type.is_numeric_value() { + let integer_type = self.polymorphic_integer(); self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) } else { true }; return Ok((lhs_type.clone(), use_impl)); } - if let TypeBinding::Bound(binding) = &*int.borrow() { + if let TypeBinding::Bound(ref binding) = &*int.borrow() { return self.infix_operand_type_rules(binding, op, other, span); } let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); @@ -1091,21 +1093,21 @@ impl<'context> Elaborator<'context> { // Matches on TypeVariable must be first so that we follow any type // bindings. - TypeVariable(int, _) => { - if let TypeBinding::Bound(binding) = &*int.borrow() { + TypeVariable(int) => { + if let TypeBinding::Bound(ref binding) = &*int.borrow() { return self.prefix_operand_type_rules(op, binding, span); } // The `!` prefix operator is not valid for Field, so if this is a numeric // type we constrain it to just (non-Field) integer types. - if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric() { + if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric_value() { let integer_type = Type::polymorphic_integer(self.interner); self.unify(rhs_type, &integer_type, || { TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } }); } - Ok((rhs_type.clone(), !rhs_type.is_numeric())) + Ok((rhs_type.clone(), !rhs_type.is_numeric_value())) } Integer(sign_x, bit_width_x) => { if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { @@ -1187,7 +1189,11 @@ impl<'context> Elaborator<'context> { let object_type = object_type.substitute(&bindings); bindings.insert( the_trait.self_type_typevar.id(), - (the_trait.self_type_typevar.clone(), object_type.clone()), + ( + the_trait.self_type_typevar.clone(), + the_trait.self_type_typevar.kind(), + object_type.clone(), + ), ); self.interner.select_impl_for_expression( expr_id, @@ -1268,7 +1274,7 @@ impl<'context> Elaborator<'context> { }); None } - Type::NamedGeneric(_, _, _) => { + Type::NamedGeneric(_, _) => { self.lookup_method_in_trait_constraints(object_type, method_name, span) } // Mutable references to another type should resolve to methods of their element type. @@ -1284,7 +1290,7 @@ impl<'context> Elaborator<'context> { Type::Error => None, // The type variable must be unbound at this point since follow_bindings was called - Type::TypeVariable(_, TypeVariableKind::Normal) => { + Type::TypeVariable(var) if var.kind() == Kind::Normal => { self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); None } @@ -1631,9 +1637,9 @@ impl<'context> Elaborator<'context> { | Type::Bool | Type::Unit | Type::Error - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::Constant(..) - | Type::NamedGeneric(_, _, _) + | Type::NamedGeneric(_, _) | Type::Quoted(_) | Type::Forall(_, _) => (), @@ -1647,7 +1653,7 @@ impl<'context> Elaborator<'context> { } Type::Array(length, element_type) => { - if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(element_type, found); @@ -1672,7 +1678,7 @@ impl<'context> Elaborator<'context> { Type::Struct(struct_type, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name, _) = generic { + if let Type::NamedGeneric(type_variable, name) = generic { if struct_type.borrow().generics[i].is_numeric() { found.insert(name.to_string(), type_variable.clone()); } @@ -1683,7 +1689,7 @@ impl<'context> Elaborator<'context> { } Type::Alias(alias, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name, _) = generic { + if let Type::NamedGeneric(type_variable, name) = generic { if alias.borrow().generics[i].is_numeric() { found.insert(name.to_string(), type_variable.clone()); } @@ -1694,12 +1700,12 @@ impl<'context> Elaborator<'context> { } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { - if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } } Type::FmtString(length, fields) => { - if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(fields, found); @@ -1752,7 +1758,10 @@ impl<'context> Elaborator<'context> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics.ordered) { // Avoid binding t = t if !arg.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.clone())); + bindings.insert( + param.type_var.id(), + (param.type_var.clone(), param.kind(), arg.clone()), + ); } } @@ -1771,7 +1780,10 @@ impl<'context> Elaborator<'context> { // Avoid binding t = t if !arg.typ.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.typ.clone())); + bindings.insert( + param.type_var.id(), + (param.type_var.clone(), param.kind(), arg.typ.clone()), + ); } } @@ -1780,7 +1792,8 @@ impl<'context> Elaborator<'context> { // to specify a redundant type annotation. if assumed { let self_type = the_trait.self_type_typevar.clone(); - bindings.insert(self_type.id(), (self_type, constraint.typ.clone())); + let kind = the_trait.self_type_typevar.kind(); + bindings.insert(self_type.id(), (self_type, kind, constraint.typ.clone())); } } } diff --git a/compiler/noirc_frontend/src/elaborator/unquote.rs b/compiler/noirc_frontend/src/elaborator/unquote.rs index fd7e02df905..982ad3d2e1f 100644 --- a/compiler/noirc_frontend/src/elaborator/unquote.rs +++ b/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -1,5 +1,5 @@ use crate::{ - macros_api::Path, + ast::Path, token::{SpannedToken, Token, Tokens}, }; diff --git a/compiler/noirc_frontend/src/hir/comptime/display.rs b/compiler/noirc_frontend/src/hir/comptime/display.rs index 105f6e09395..3f2ecb395d0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -13,8 +13,7 @@ use crate::{ UnresolvedTypeData, }, hir_def::traits::TraitConstraint, - macros_api::NodeInterner, - node_interner::InternedStatementKind, + node_interner::{InternedStatementKind, NodeInterner}, token::{Keyword, Token}, Type, }; diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 4344d19829a..63c32bc7e5f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -9,10 +9,11 @@ use crate::ast::{ UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }; use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; -use crate::hir_def::expr::{HirArrayLiteral, HirBlockExpression, HirExpression, HirIdent}; +use crate::hir_def::expr::{ + HirArrayLiteral, HirBlockExpression, HirExpression, HirIdent, HirLiteral, +}; use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; use crate::hir_def::types::{Type, TypeBinding}; -use crate::macros_api::HirLiteral; use crate::node_interner::{ExprId, NodeInterner, StmtId}; // TODO: @@ -315,10 +316,10 @@ impl Type { let name = Path::from_ident(type_def.name.clone()); UnresolvedTypeData::Named(name, generics, false) } - Type::TypeVariable(binding, kind) => match &*binding.borrow() { + Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(typ) => return typ.to_display_ast(), - TypeBinding::Unbound(id) => { - let name = format!("var_{:?}_{}", kind, id); + TypeBinding::Unbound(id, type_var_kind) => { + let name = format!("var_{:?}_{}", type_var_kind, id); let path = Path::from_single(name, Span::empty(0)); let expression = UnresolvedTypeExpression::Variable(path); UnresolvedTypeData::Expression(expression) @@ -333,7 +334,7 @@ impl Type { let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::TraitAsType(name, generics) } - Type::NamedGeneric(_var, name, _kind) => { + Type::NamedGeneric(_var, name) => { let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::Named(name, GenericTypeArgs::default(), true) } @@ -373,7 +374,7 @@ impl Type { match self.follow_bindings() { Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, span), - Type::NamedGeneric(_var, name, _kind) => { + Type::NamedGeneric(_var, name) => { let path = Path::from_single(name.as_ref().clone(), span); UnresolvedTypeExpression::Variable(path) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index e920073b453..b981dcb348f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -8,14 +8,13 @@ use iter_extended::try_vecmap; use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; -use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness}; +use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness, UnaryOp}; use crate::elaborator::Elaborator; use crate::graph::CrateId; use crate::hir::def_map::ModuleId; use crate::hir::type_check::TypeCheckError; use crate::hir_def::expr::ImplKind; use crate::hir_def::function::FunctionBody; -use crate::macros_api::UnaryOp; use crate::monomorphization::{ perform_impl_bindings, perform_instantiation_bindings, resolve_trait_method, undo_instantiation_bindings, @@ -27,17 +26,17 @@ use crate::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, - HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, + HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, }, stmt::{ HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, - HirPattern, + HirPattern, HirStatement, }, + types::Kind, }, - macros_api::{HirLiteral, HirStatement, NodeInterner}, - node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, - Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, + node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId}, + Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; @@ -62,7 +61,7 @@ pub struct Interpreter<'local, 'interner> { /// Since the interpreter monomorphizes as it interprets, we can bind over the same generic /// multiple times. Without this map, when one of these inner functions exits we would /// unbind the generic completely instead of resetting it to its previous binding. - bound_generics: Vec>, + bound_generics: Vec>, } #[allow(unused)] @@ -89,7 +88,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { // To match the monomorphizer, we need to call follow_bindings on each of // the instantiation bindings before we unbind the generics from the previous function. // This is because the instantiation bindings refer to variables from the call site. - for (_, binding) in instantiation_bindings.values_mut() { + for (_, kind, binding) in instantiation_bindings.values_mut() { + *kind = kind.follow_bindings(); *binding = binding.follow_bindings(); } @@ -98,7 +98,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut impl_bindings = perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; - for (_, binding) in impl_bindings.values_mut() { + for (_, kind, binding) in impl_bindings.values_mut() { + *kind = kind.follow_bindings(); *binding = binding.follow_bindings(); } @@ -335,8 +336,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn unbind_generics_from_previous_function(&mut self) { if let Some(bindings) = self.bound_generics.last() { - for var in bindings.keys() { - var.unbind(var.id()); + for (var, (_, kind)) in bindings { + var.unbind(var.id(), kind.clone()); } } // Push a new bindings list for the current function @@ -348,7 +349,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.bound_generics.pop(); if let Some(bindings) = self.bound_generics.last() { - for (var, binding) in bindings { + for (var, (binding, _kind)) in bindings { var.force_bind(binding.clone()); } } @@ -360,12 +361,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { .last_mut() .expect("remember_bindings called with no bound_generics on the stack"); - for (var, binding) in main_bindings.values() { - bound_generics.insert(var.clone(), binding.follow_bindings()); + for (var, kind, binding) in main_bindings.values() { + bound_generics.insert(var.clone(), (binding.follow_bindings(), kind.clone())); } - for (var, binding) in impl_bindings.values() { - bound_generics.insert(var.clone(), binding.follow_bindings()); + for (var, kind, binding) in impl_bindings.values() { + bound_generics.insert(var.clone(), (binding.follow_bindings(), kind.clone())); } } @@ -582,9 +583,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(value) } } - DefinitionKind::GenericType(type_variable) => { + DefinitionKind::NumericGeneric(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { - TypeBinding::Unbound(_) => None, + TypeBinding::Unbound(_, _) => None, TypeBinding::Bound(binding) => binding.evaluate_to_u32(), }; @@ -593,7 +594,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.evaluate_integer((value as u128).into(), false, id) } else { let location = self.elaborator.interner.expr_location(&id); - let typ = Type::TypeVariable(type_variable.clone(), TypeVariableKind::Normal); + let typ = Type::TypeVariable(type_variable.clone()); Err(InterpreterError::NonIntegerArrayLength { typ, location }) } } @@ -752,14 +753,18 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(Value::I64(value)) } } - } else if let Type::TypeVariable(variable, TypeVariableKind::IntegerOrField) = &typ { - Ok(Value::Field(value)) - } else if let Type::TypeVariable(variable, TypeVariableKind::Integer) = &typ { - let value: u64 = value - .try_to_u64() - .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; - Ok(Value::U64(value)) + } else if let Type::TypeVariable(variable) = &typ { + if variable.is_integer_or_field() { + Ok(Value::Field(value)) + } else if variable.is_integer() { + let value: u64 = value + .try_to_u64() + .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; + let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + Ok(Value::U64(value)) + } else { + Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) + } } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 9960486120e..48827c087f9 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -20,8 +20,9 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, ForRange, - FunctionKind, FunctionReturnType, IntegerBitSize, LValue, Literal, Pattern, Statement, - StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, + FunctionKind, FunctionReturnType, Ident, IntegerBitSize, LValue, Literal, Pattern, + Signedness, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, + Visibility, }, hir::{ comptime::{ @@ -30,10 +31,13 @@ use crate::{ InterpreterError, Value, }, def_collector::dc_crate::CollectedItems, + def_map::ModuleDefId, }, - hir_def::function::FunctionBody, - macros_api::{HirExpression, HirLiteral, Ident, ModuleDefId, NodeInterner, Signedness}, - node_interner::{DefinitionKind, TraitImplKind}, + hir_def::{ + expr::{HirExpression, HirLiteral}, + function::FunctionBody, + }, + node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, parser, token::{Attribute, SecondaryAttribute, Token}, Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, @@ -400,11 +404,11 @@ fn struct_def_add_generic( } } - let type_var = TypeVariable::unbound(interner.next_type_variable_id()); + let type_var_kind = Kind::Normal; + let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); let span = generic_location.span; - let kind = Kind::Normal; - let typ = Type::NamedGeneric(type_var.clone(), name.clone(), kind.clone()); - let new_generic = ResolvedGeneric { name, type_var, span, kind }; + let typ = Type::NamedGeneric(type_var.clone(), name.clone()); + let new_generic = ResolvedGeneric { name, type_var, span }; the_struct.generics.push(new_generic); Ok(Value::Type(typ)) @@ -422,7 +426,7 @@ fn struct_def_as_type( let struct_def = struct_def_rc.borrow(); let generics = vecmap(&struct_def.generics, |generic| { - Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), generic.kind.clone()) + Type::NamedGeneric(generic.type_var.clone(), generic.name.clone()) }); drop(struct_def); @@ -1192,14 +1196,14 @@ fn zeroed(return_type: Type) -> IResult { Ok(Value::Pointer(Shared::new(element), false)) } // Optimistically assume we can resolve this type later or that the value is unused - Type::TypeVariable(_, _) + Type::TypeVariable(_) | Type::Forall(_, _) | Type::Constant(..) | Type::InfixExpr(..) | Type::Quoted(_) | Type::Error | Type::TraitAsType(..) - | Type::NamedGeneric(_, _, _) => Ok(Value::Zeroed(return_type)), + | Type::NamedGeneric(_, _) => Ok(Value::Zeroed(return_type)), } } @@ -2075,7 +2079,7 @@ fn fmtstr_quoted_contents( // fn fresh_type_variable() -> Type fn fresh_type_variable(interner: &NodeInterner) -> IResult { - Ok(Value::Type(interner.next_type_variable())) + Ok(Value::Type(interner.next_type_variable_with_kind(Kind::Any))) } // fn add_attribute(self, attribute: str) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 20303e49e15..a355b23b74f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -24,8 +24,7 @@ use crate::{ function::{FuncMeta, FunctionBody}, stmt::HirPattern, }, - macros_api::{NodeInterner, StructId}, - node_interner::{FuncId, TraitId, TraitImplId}, + node_interner::{FuncId, NodeInterner, StructId, TraitId, TraitImplId}, parser::NoirParser, token::{SecondaryAttribute, Token, Tokens}, QuotedType, Type, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 5ae60bb4d00..d1ab6a1dabd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -6,7 +6,7 @@ use noirc_errors::Location; use crate::{ hir::comptime::{errors::IResult, InterpreterError, Value}, - macros_api::NodeInterner, + node_interner::NodeInterner, }; use super::builtin::builtin_helpers::{ diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index f01e188e498..4c968234f04 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -9,16 +9,16 @@ use strum_macros::Display; use crate::{ ast::{ - ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, LValue, - Pattern, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, + ArrayLiteral, BlockExpression, ConstructorExpression, Expression, ExpressionKind, Ident, + IntegerBitSize, LValue, Literal, Path, Pattern, Signedness, Statement, StatementKind, + UnresolvedType, UnresolvedTypeData, }, hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, - hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, - macros_api::{ - Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, - StructId, + hir_def::expr::{ + HirArrayLiteral, HirConstructorExpression, HirExpression, HirIdent, HirLambda, HirLiteral, + ImplKind, }, - node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, + node_interner::{ExprId, FuncId, NodeInterner, StmtId, StructId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, Kind, QuotedType, Shared, Type, TypeBindings, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index faf72e86fb4..4d348d998b7 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -14,7 +14,7 @@ use crate::{Generics, Type}; use crate::hir::resolution::import::{resolve_import, ImportDirective, PathResolution}; use crate::hir::Context; -use crate::macros_api::Expression; +use crate::ast::Expression; use crate::node_interner::{ FuncId, GlobalId, ModuleAttributes, NodeInterner, ReferenceId, StructId, TraitId, TraitImplId, TypeAliasId, @@ -23,7 +23,7 @@ use crate::node_interner::{ use crate::ast::{ ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias, Path, PathKind, PathSegment, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, + UnresolvedTraitConstraint, UnresolvedType, UnsupportedNumericGenericType, }; use crate::parser::{ParserError, SortedModule}; @@ -231,12 +231,19 @@ impl From for CompilationError { CompilationError::ResolverError(value) } } + impl From for CompilationError { fn from(value: TypeCheckError) -> Self { CompilationError::TypeError(value) } } +impl From for CompilationError { + fn from(value: UnsupportedNumericGenericType) -> Self { + Self::ResolverError(value.into()) + } +} + impl DefCollector { pub fn new(def_map: CrateDefMap) -> DefCollector { DefCollector { @@ -507,7 +514,7 @@ impl DefCollector { } fn add_import_reference( - def_id: crate::macros_api::ModuleDefId, + def_id: crate::hir::def_map::ModuleDefId, name: &Ident, interner: &mut NodeInterner, file_id: FileId, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index b530e023152..0e20ec3a304 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -11,13 +11,12 @@ use num_traits::Num; use rustc_hash::FxHashMap as HashMap; use crate::ast::{ - Documented, FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, - NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, TraitImplItemKind, - TraitItem, TypeImpl, + Documented, Expression, FunctionDefinition, Ident, ItemVisibility, LetStatement, + ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, + TraitImplItemKind, TraitItem, TypeImpl, UnresolvedType, UnresolvedTypeData, }; use crate::hir::resolution::errors::ResolverError; -use crate::macros_api::{Expression, NodeInterner, StructId, UnresolvedType, UnresolvedTypeData}; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, StructId}; use crate::token::SecondaryAttribute; use crate::usage_tracker::UnusedItem; use crate::{ @@ -531,8 +530,10 @@ impl<'a> ModCollector<'a> { associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id), - kind: Kind::Numeric(Box::new(typ)), + type_var: TypeVariable::unbound( + type_variable_id, + Kind::Numeric(Box::new(typ)), + ), span: name.span(), }); } @@ -556,8 +557,7 @@ impl<'a> ModCollector<'a> { let type_variable_id = context.def_interner.next_type_variable_id(); associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id), - kind: Kind::Normal, + type_var: TypeVariable::unbound(type_variable_id, Kind::Normal), span: name.span(), }); } diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index 2f47505db94..d72f493092d 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -1,4 +1,4 @@ -use crate::ast::{Ident, ItemVisibility, Path, UnresolvedTypeData}; +use crate::ast::{Ident, ItemVisibility, Path, UnsupportedNumericGenericType}; use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::generics::TraitGenerics; @@ -71,8 +71,6 @@ pub enum DefCollectorErrorKind { "Either the type or the trait must be from the same crate as the trait implementation" )] TraitImplOrphaned { span: Span }, - #[error("The only supported types of numeric generics are integers, fields, and booleans")] - UnsupportedNumericGenericType { ident: Ident, typ: UnresolvedTypeData }, #[error("impl has stricter requirements than trait")] ImplIsStricterThanTrait { constraint_typ: crate::Type, @@ -82,6 +80,8 @@ pub enum DefCollectorErrorKind { trait_method_name: String, trait_method_span: Span, }, + #[error("{0}")] + UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), } impl DefCollectorErrorKind { @@ -90,6 +90,19 @@ impl DefCollectorErrorKind { } } +impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { + fn from(error: &'a UnsupportedNumericGenericType) -> Diagnostic { + let name = &error.ident.0.contents; + let typ = &error.typ; + + Diagnostic::simple_error( + format!("{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`."), + "Unsupported numeric generic type".to_string(), + error.ident.0.span(), + ) + } +} + impl fmt::Display for DuplicateType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -266,15 +279,6 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { "Either the type or the trait must be from the same crate as the trait implementation".into(), *span, ), - DefCollectorErrorKind::UnsupportedNumericGenericType { ident, typ } => { - let name = &ident.0.contents; - - Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported types of numeric generics are integers and fields"), - "Unsupported numeric generic type".to_string(), - ident.0.span(), - ) - } DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_span, trait_method_name, trait_method_span } => { let constraint = format!("{}{}", constraint_name, constraint_generics); @@ -286,6 +290,7 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_span); diag } + DefCollectorErrorKind::UnsupportedNumericGenericType(err) => err.into(), } } } diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index c631edfa889..015b7deb6e0 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -11,7 +11,7 @@ use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::ParserError; -use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, Type, TypeVariable}; +use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, TypeVariable}; use def_collector::dc_crate::CompilationError; use def_map::{Contract, CrateDefMap}; use fm::{FileId, FileManager}; @@ -280,19 +280,20 @@ impl Context<'_, '_> { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = interner.next_type_variable_id(); - let type_var = TypeVariable::unbound(id); + + let type_var_kind = generic.kind().unwrap_or_else(|err| { + errors.push((err.into(), file_id)); + // When there's an error, unify with any other kinds + Kind::Any + }); + let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); let span = ident.0.span(); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - let kind = generic.kind().unwrap_or_else(|err| { - errors.push((err.into(), file_id)); - Kind::Numeric(Box::new(Type::Error)) - }); - - ResolvedGeneric { name, type_var, kind, span } + ResolvedGeneric { name, type_var, span } }) } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index f3c61a7fbe2..4e9520ad761 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -3,7 +3,10 @@ use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; use crate::{ - ast::Ident, hir::comptime::InterpreterError, parser::ParserError, usage_tracker::UnusedItem, + ast::{Ident, UnsupportedNumericGenericType}, + hir::comptime::InterpreterError, + parser::ParserError, + usage_tracker::UnusedItem, Type, }; @@ -103,8 +106,6 @@ pub enum ResolverError { NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, - #[error("The only supported types of numeric generics are integers, fields, and booleans")] - UnsupportedNumericGenericType { ident: Ident, typ: Type }, #[error("expected type, found numeric generic parameter")] NumericGenericUsedForType { name: String, span: Span }, #[error("Invalid array length construction")] @@ -133,6 +134,8 @@ pub enum ResolverError { MutatingComptimeInNonComptimeContext { name: String, span: Span }, #[error("Failed to parse `{statement}` as an expression")] InvalidInternedStatementInExpr { statement: String, span: Span }, + #[error("{0}")] + UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("Type `{typ}` is more private than item `{item}`")] TypeIsMorePrivateThenItem { typ: String, item: String, span: Span }, } @@ -443,15 +446,6 @@ impl<'a> From<&'a ResolverError> for Diagnostic { diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diag } - ResolverError::UnsupportedNumericGenericType { ident , typ } => { - let name = &ident.0.contents; - - Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), - "Unsupported numeric generic type".to_string(), - ident.0.span(), - ) - } ResolverError::NumericGenericUsedForType { name, span } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), @@ -544,6 +538,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::UnsupportedNumericGenericType(err) => err.into(), ResolverError::TypeIsMorePrivateThenItem { typ, item, span } => { Diagnostic::simple_warning( format!("Type `{typ}` is more private than item `{item}`"), diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 00e73e682e8..54699792901 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -5,14 +5,14 @@ use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; use thiserror::Error; -use crate::ast::ConstrainKind; -use crate::ast::{BinaryOpKind, FunctionReturnType, IntegerBitSize, Signedness}; +use crate::ast::{ + BinaryOpKind, ConstrainKind, FunctionReturnType, Ident, IntegerBitSize, Signedness, +}; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::traits::TraitConstraint; use crate::hir_def::types::Type; -use crate::macros_api::Ident; -use crate::macros_api::NodeInterner; +use crate::node_interner::NodeInterner; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Source { diff --git a/compiler/noirc_frontend/src/hir/type_check/generics.rs b/compiler/noirc_frontend/src/hir/type_check/generics.rs index b86e2350279..86fc2d25d4e 100644 --- a/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -4,8 +4,7 @@ use iter_extended::vecmap; use crate::{ hir_def::traits::NamedType, - macros_api::NodeInterner, - node_interner::{FuncId, TraitId, TypeAliasId}, + node_interner::{FuncId, NodeInterner, TraitId, TypeAliasId}, ResolvedGeneric, StructType, Type, }; diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 39c87607446..6ecfdefe996 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -5,11 +5,10 @@ use noirc_errors::{Location, Span}; use super::expr::{HirBlockExpression, HirExpression, HirIdent}; use super::stmt::HirPattern; use super::traits::TraitConstraint; -use crate::ast::{FunctionKind, FunctionReturnType, Visibility}; +use crate::ast::{BlockExpression, FunctionKind, FunctionReturnType, Visibility}; use crate::graph::CrateId; use crate::hir::def_map::LocalModuleId; -use crate::macros_api::{BlockExpression, StructId}; -use crate::node_interner::{ExprId, NodeInterner, TraitId, TraitImplId}; +use crate::node_interner::{ExprId, NodeInterner, StructId, TraitId, TraitImplId}; use crate::token::CustomAttribute; use crate::{ResolvedGeneric, Type}; diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 0b4dbeb3006..b97e99583bb 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -1,7 +1,7 @@ use super::expr::HirIdent; use crate::ast::Ident; -use crate::macros_api::SecondaryAttribute; use crate::node_interner::{ExprId, StmtId}; +use crate::token::SecondaryAttribute; use crate::Type; use fm::FileId; use noirc_errors::{Location, Span}; diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 0572ba403a1..3859db26e39 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -3,12 +3,12 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::{Ident, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; +use crate::ResolvedGeneric; use crate::{ graph::CrateId, node_interner::{FuncId, TraitId, TraitMethodId}, Generics, Type, TypeBindings, TypeVariable, }; -use crate::{ResolvedGeneric, TypeVariableKind}; use fm::FileId; use noirc_errors::{Location, Span}; @@ -168,7 +168,7 @@ impl Trait { }); TraitConstraint { - typ: Type::TypeVariable(self.self_type_typevar.clone(), TypeVariableKind::Normal), + typ: Type::TypeVariable(self.self_type_typevar.clone()), trait_generics: TraitGenerics { ordered, named }, trait_id: self.id, span, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index c170d2cc08f..b8c98428bb0 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -78,7 +78,7 @@ pub enum Type { /// is a process that replaces each NamedGeneric in a generic function with a TypeVariable. /// Doing this at each call site of a generic function is how they can be called with /// different argument types each time. - TypeVariable(TypeVariable, TypeVariableKind), + TypeVariable(TypeVariable), /// `impl Trait` when used in a type position. /// These are only matched based on the TraitId. The trait name parameter is only @@ -87,7 +87,7 @@ pub enum Type { /// NamedGenerics are the 'T' or 'U' in a user-defined generic function /// like `fn foo(...) {}`. Unlike TypeVariables, they cannot be bound over. - NamedGeneric(TypeVariable, Rc, Kind), + NamedGeneric(TypeVariable, Rc), /// A functions with arguments, a return type and environment. /// the environment should be `Unit` by default, @@ -134,7 +134,25 @@ pub enum Type { /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { + /// Can bind to any type + // TODO(https://github.com/noir-lang/noir/issues/6194): evaluate need for and usage of + Any, + + /// Can bind to any type, except Type::Constant and Type::InfixExpr Normal, + + /// A generic integer or field type. This is a more specific kind of TypeVariable + /// that can only be bound to Type::Field, Type::Integer, or other polymorphic integers. + /// This is the type of undecorated integer literals like `46`. Typing them in this way + /// allows them to be polymorphic over the actual integer/field type used without requiring + /// type annotations on each integer literal. + IntegerOrField, + + /// A generic integer type. This is a more specific kind of TypeVariable + /// that can only be bound to Type::Integer, or other polymorphic integers. + Integer, + + /// Can bind to a Type::Constant or Type::InfixExpr of the given kind Numeric(Box), } @@ -150,18 +168,34 @@ impl Kind { matches!(self, Self::Numeric { .. }) } - pub(crate) fn matches_opt(&self, other: Option) -> bool { - other.as_ref().map_or(true, |other_kind| self.unifies(other_kind)) - } - pub(crate) fn u32() -> Self { Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } + pub(crate) fn follow_bindings(&self) -> Self { + match self { + Self::Any => Self::Any, + Self::Normal => Self::Normal, + Self::Integer => Self::Integer, + Self::IntegerOrField => Self::IntegerOrField, + Self::Numeric(typ) => Self::Numeric(Box::new(typ.follow_bindings())), + } + } + /// Unifies this kind with the other. Returns true on success pub(crate) fn unifies(&self, other: &Kind) -> bool { match (self, other) { - (Kind::Normal, Kind::Normal) => true, + // Kind::Any unifies with everything + (Kind::Any, _) | (_, Kind::Any) => true, + + // Kind::Normal unifies with Kind::Integer and Kind::IntegerOrField + (Kind::Normal, Kind::Integer | Kind::IntegerOrField) + | (Kind::Integer | Kind::IntegerOrField, Kind::Normal) => true, + + // Kind::Integer unifies with Kind::IntegerOrField + (Kind::Integer | Kind::IntegerOrField, Kind::Integer | Kind::IntegerOrField) => true, + + // Kind::Numeric unifies along its Type argument (Kind::Numeric(lhs), Kind::Numeric(rhs)) => { let mut bindings = TypeBindings::new(); let unifies = lhs.try_unify(rhs, &mut bindings).is_ok(); @@ -170,7 +204,9 @@ impl Kind { } unifies } - _ => false, + + // everything unifies with itself + (lhs, rhs) => lhs == rhs, } } @@ -181,12 +217,27 @@ impl Kind { Err(UnificationError) } } + + /// Returns the default type this type variable should be bound to if it is still unbound + /// during monomorphization. + pub(crate) fn default_type(&self) -> Option { + match self { + Kind::Any => None, + Kind::IntegerOrField => Some(Type::default_int_or_field_type()), + Kind::Integer => Some(Type::default_int_type()), + Kind::Normal => None, + Kind::Numeric(typ) => Some(*typ.clone()), + } + } } impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Kind::Any => write!(f, "any"), Kind::Normal => write!(f, "normal"), + Kind::Integer => write!(f, "int"), + Kind::IntegerOrField => write!(f, "intOrField"), Kind::Numeric(typ) => write!(f, "numeric {}", typ), } } @@ -209,10 +260,10 @@ pub enum QuotedType { CtString, } -/// A list of TypeVariableIds to bind to a type. Storing the +/// A list of (TypeVariableId, Kind)'s to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. -pub type TypeBindings = HashMap; +pub type TypeBindings = HashMap; /// Represents a struct type in the type system. Each instance of this /// rust struct will be shared across all Type::Struct variants that represent @@ -236,7 +287,7 @@ pub struct StructType { /// Corresponds to generic lists such as `` in the source program. /// Used mainly for resolved types which no longer need information such -/// as names or kinds. +/// as names or kinds pub type GenericTypeVars = Vec; /// Corresponds to generic lists such as `` with additional @@ -248,17 +299,20 @@ pub type Generics = Vec; pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, - pub kind: Kind, pub span: Span, } impl ResolvedGeneric { pub fn as_named_generic(self) -> Type { - Type::NamedGeneric(self.type_var, self.name, self.kind) + Type::NamedGeneric(self.type_var, self.name) + } + + pub fn kind(&self) -> Kind { + self.type_var.kind() } pub(crate) fn is_numeric(&self) -> bool { - self.kind.is_numeric() + self.kind().is_numeric() } } @@ -326,7 +380,12 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) + .map(|(old, new)| { + ( + old.type_var.id(), + (old.type_var.clone(), old.type_var.kind(), new.clone()), + ) + }) .collect(); (typ.substitute(&substitutions), i) @@ -342,7 +401,9 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) + .map(|(old, new)| { + (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone())) + }) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -380,7 +441,7 @@ impl StructType { /// Instantiate this struct type, returning a Vec of the new generic args (in /// the same order as self.generics) pub fn instantiate(&self, interner: &mut NodeInterner) -> Vec { - vecmap(&self.generics, |_| interner.next_type_variable()) + vecmap(&self.generics, |generic| interner.next_type_variable_with_kind(generic.kind())) } } @@ -454,7 +515,9 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) + .map(|(old, new)| { + (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone())) + }) .collect(); self.typ.substitute(&substitutions) @@ -527,31 +590,14 @@ pub enum BinaryTypeOperator { Modulo, } -#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] -pub enum TypeVariableKind { - /// Can bind to any type - Normal, - - /// A generic integer or field type. This is a more specific kind of TypeVariable - /// that can only be bound to Type::Field, Type::Integer, or other polymorphic integers. - /// This is the type of undecorated integer literals like `46`. Typing them in this way - /// allows them to be polymorphic over the actual integer/field type used without requiring - /// type annotations on each integer literal. - IntegerOrField, - - /// A generic integer type. This is a more specific kind of TypeVariable - /// that can only be bound to Type::Integer, or other polymorphic integers. - Integer, -} - /// A TypeVariable is a mutable reference that is either /// bound to some type, or unbound with a given TypeVariableId. #[derive(PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub struct TypeVariable(TypeVariableId, Shared); impl TypeVariable { - pub fn unbound(id: TypeVariableId) -> Self { - TypeVariable(id, Shared::new(TypeBinding::Unbound(id))) + pub fn unbound(id: TypeVariableId, type_var_kind: Kind) -> Self { + TypeVariable(id, Shared::new(TypeBinding::Unbound(id, type_var_kind))) } pub fn id(&self) -> TypeVariableId { @@ -568,19 +614,27 @@ impl TypeVariable { TypeBinding::Bound(binding) => { unreachable!("TypeVariable::bind, cannot bind bound var {} to {}", binding, typ) } - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; assert!(!typ.occurs(id), "{self:?} occurs within {typ:?}"); *self.1.borrow_mut() = TypeBinding::Bound(typ); } - pub fn try_bind(&self, binding: Type, span: Span) -> Result<(), TypeCheckError> { + pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { + if !binding.kind().unifies(kind) { + return Err(TypeCheckError::TypeKindMismatch { + expected_kind: format!("{}", kind), + expr_kind: format!("{}", binding.kind()), + expr_span: span, + }); + } + let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("Expected unbound, found bound to {binding}") } - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; if binding.occurs(id) { @@ -599,18 +653,47 @@ impl TypeVariable { /// Unbind this type variable, setting it to Unbound(id). /// /// This is generally a logic error to use outside of monomorphization. - pub fn unbind(&self, id: TypeVariableId) { - *self.1.borrow_mut() = TypeBinding::Unbound(id); + pub fn unbind(&self, id: TypeVariableId, type_var_kind: Kind) { + *self.1.borrow_mut() = TypeBinding::Unbound(id, type_var_kind); } /// Forcibly bind a type variable to a new type - even if the type /// variable is already bound to a different type. This generally /// a logic error to use outside of monomorphization. + /// + /// Asserts that the given type is compatible with the given Kind pub fn force_bind(&self, typ: Type) { if !typ.occurs(self.id()) { *self.1.borrow_mut() = TypeBinding::Bound(typ); } } + + pub fn kind(&self) -> Kind { + match &*self.borrow() { + TypeBinding::Bound(binding) => binding.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), + } + } + + /// Check that if bound, it's an integer + /// and if unbound, that it's a Kind::Integer + pub fn is_integer(&self) -> bool { + match &*self.borrow() { + TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..)), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::Integer), + } + } + + /// Check that if bound, it's an integer or field + /// and if unbound, that it's a Kind::IntegerOrField + pub fn is_integer_or_field(&self) -> bool { + match &*self.borrow() { + TypeBinding::Bound(binding) => { + matches!(binding, Type::Integer(..) | Type::FieldElement) + } + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), + } + } } /// TypeBindings are the mutable insides of a TypeVariable. @@ -618,12 +701,12 @@ impl TypeVariable { #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum TypeBinding { Bound(Type), - Unbound(TypeVariableId), + Unbound(TypeVariableId, Kind), } impl TypeBinding { pub fn is_unbound(&self) -> bool { - matches!(self, TypeBinding::Unbound(_)) + matches!(self, TypeBinding::Unbound(_, _)) } } @@ -647,22 +730,18 @@ impl std::fmt::Display for Type { Signedness::Signed => write!(f, "i{num_bits}"), Signedness::Unsigned => write!(f, "u{num_bits}"), }, - Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", Type::default_int_type()) - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - // Show a Field by default if this TypeVariableKind::IntegerOrField is unbound, since that is - // what they bind to by default anyway. It is less confusing than displaying it - // as a generic. - write!(f, "Field") - } else { - write!(f, "{}", binding.borrow()) + Type::TypeVariable(var) => { + let binding = &var.1; + match &*binding.borrow() { + TypeBinding::Unbound(_, type_var_kind) => match type_var_kind { + Kind::Any | Kind::Normal => write!(f, "{}", var.borrow()), + Kind::Integer => write!(f, "{}", Type::default_int_type()), + Kind::IntegerOrField => write!(f, "Field"), + Kind::Numeric(_typ) => write!(f, "_"), + }, + TypeBinding::Bound(binding) => { + write!(f, "{}", binding) + } } } Type::Struct(s, args) => { @@ -695,10 +774,10 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, _) => match &*binding.borrow() { + Type::NamedGeneric(binding, name) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.fmt(f), - TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), - TypeBinding::Unbound(_) => write!(f, "{name}"), + TypeBinding::Unbound(_, _) if name.is_empty() => write!(f, "_"), + TypeBinding::Unbound(_, _) => write!(f, "{name}"), }, Type::Constant(x, _kind) => write!(f, "{x}"), Type::Forall(typevars, typ) => { @@ -759,7 +838,7 @@ impl std::fmt::Display for TypeBinding { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TypeBinding::Bound(typ) => typ.fmt(f), - TypeBinding::Unbound(id) => id.fmt(f), + TypeBinding::Unbound(id, _) => id.fmt(f), } } } @@ -795,23 +874,25 @@ impl Type { Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) } + pub fn type_variable_with_kind(interner: &NodeInterner, type_var_kind: Kind) -> Type { + let id = interner.next_type_variable_id(); + let var = TypeVariable::unbound(id, type_var_kind); + Type::TypeVariable(var) + } + pub fn type_variable(id: TypeVariableId) -> Type { - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, TypeVariableKind::Normal) + let var = TypeVariable::unbound(id, Kind::Any); + Type::TypeVariable(var) } - pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { - let id = interner.next_type_variable_id(); - let kind = TypeVariableKind::IntegerOrField; - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, kind) + pub fn polymorphic_integer_or_field(interner: &NodeInterner) -> Type { + let type_var_kind = Kind::IntegerOrField; + Self::type_variable_with_kind(interner, type_var_kind) } - pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { - let id = interner.next_type_variable_id(); - let kind = TypeVariableKind::Integer; - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, kind) + pub fn polymorphic_integer(interner: &NodeInterner) -> Type { + let type_var_kind = Kind::Integer; + Self::type_variable_with_kind(interner, type_var_kind) } /// A bit of an awkward name for this function - this function returns @@ -820,9 +901,9 @@ impl Type { /// they shouldn't be bound over until monomorphization. pub fn is_bindable(&self) -> bool { match self { - Type::TypeVariable(binding, _) => match &*binding.borrow() { + Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.is_bindable(), - TypeBinding::Unbound(_) => true, + TypeBinding::Unbound(_, _) => true, }, Type::Alias(alias, args) => alias.borrow().get_type(args).is_bindable(), _ => false, @@ -849,21 +930,35 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) } - pub fn is_numeric(&self) -> bool { + /// While Kind::is_numeric refers to numeric _types_, + /// this method checks for numeric _values_ + pub fn is_numeric_value(&self) -> bool { + use Kind as K; use Type::*; - use TypeVariableKind as K; - matches!( - self.follow_bindings(), - FieldElement | Integer(..) | Bool | TypeVariable(_, K::Integer | K::IntegerOrField) - ) + match self.follow_bindings() { + FieldElement => true, + Integer(..) => true, + Bool => true, + TypeVariable(var) => match &*var.borrow() { + TypeBinding::Bound(typ) => typ.is_numeric_value(), + TypeBinding::Unbound(_, type_var_kind) => { + matches!(type_var_kind, K::Integer | K::IntegerOrField) + } + }, + _ => false, + } } pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { - // Return whether the named generic has a TypeKind::Numeric and save its name + // Return whether the named generic has a Kind::Numeric and save its name let named_generic_is_numeric = |typ: &Type, found_names: &mut Vec| { - if let Type::NamedGeneric(_, name, Kind::Numeric { .. }) = typ { - found_names.push(name.to_string()); - true + if let Type::NamedGeneric(var, name) = typ { + if var.kind().is_numeric() { + found_names.push(name.to_string()); + true + } else { + false + } } else { false } @@ -879,13 +974,13 @@ impl Type { | Type::Forall(_, _) | Type::Quoted(_) => {} - Type::TypeVariable(type_var, _) => { + Type::TypeVariable(type_var) => { if let TypeBinding::Bound(typ) = &*type_var.borrow() { named_generic_is_numeric(typ, found_names); } } - Type::NamedGeneric(_, _, _) => { + Type::NamedGeneric(_, _) => { named_generic_is_numeric(self, found_names); } @@ -962,8 +1057,8 @@ impl Type { | Type::Error => true, Type::FmtString(_, _) - | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _, _) + | Type::TypeVariable(_) + | Type::NamedGeneric(_, _) | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -1005,8 +1100,8 @@ impl Type { | Type::Bool | Type::Unit | Type::Constant(_, _) - | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _, _) + | Type::TypeVariable(_) + | Type::NamedGeneric(_, _) | Type::InfixExpr(..) | Type::Error => true, @@ -1054,7 +1149,7 @@ impl Type { | Type::InfixExpr(..) | Type::Error => true, - Type::TypeVariable(type_var, _) | Type::NamedGeneric(type_var, _, _) => { + Type::TypeVariable(type_var) | Type::NamedGeneric(type_var, _) => { if let TypeBinding::Bound(typ) = &*type_var.borrow() { typ.is_valid_for_unconstrained_boundary() } else { @@ -1094,10 +1189,10 @@ impl Type { pub fn generic_count(&self) -> usize { match self { Type::Forall(generics, _) => generics.len(), - Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _, _) => { + Type::TypeVariable(type_variable) | Type::NamedGeneric(type_variable, _) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => binding.generic_count(), - TypeBinding::Unbound(_) => 0, + TypeBinding::Unbound(_, _) => 0, } } _ => 0, @@ -1105,9 +1200,10 @@ impl Type { } /// Takes a monomorphic type and generalizes it over each of the type variables in the - /// given type bindings, ignoring what each type variable is bound to in the TypeBindings. + /// given type bindings, ignoring what each type variable is bound to in the TypeBindings + /// and their Kind's pub(crate) fn generalize_from_substitutions(self, type_bindings: TypeBindings) -> Type { - let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _))| type_var); + let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _kind, _))| type_var); Type::Forall(polymorphic_type_vars, Box::new(self)) } @@ -1130,15 +1226,15 @@ impl Type { } } - pub(crate) fn kind(&self) -> Option { + pub(crate) fn kind(&self) -> Kind { match self { - Type::NamedGeneric(_, _, kind) => Some(kind.clone()), - Type::Constant(_, kind) => Some(kind.clone()), - Type::TypeVariable(var, _) => match *var.borrow() { + Type::NamedGeneric(var, _) => var.kind(), + Type::Constant(_, kind) => kind.clone(), + Type::TypeVariable(var) => match &*var.borrow() { TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_) => None, + TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), }, - Type::InfixExpr(lhs, _op, rhs) => Some(lhs.infix_kind(rhs)), + Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs), Type::FieldElement | Type::Array(..) | Type::Slice(..) @@ -1155,26 +1251,18 @@ impl Type { | Type::MutableReference(..) | Type::Forall(..) | Type::Quoted(..) - | Type::Error => Some(Kind::Normal), + | Type::Error => Kind::Normal, } } - /// if both Kind's are equal to Some(_), return that Kind, - /// otherwise return a Kind error - /// if both Kind's are None, default to u32 - /// if exactly one Kind is None, return the other one + /// Unifies self and other kinds or fails with a Kind error fn infix_kind(&self, other: &Self) -> Kind { - match (self.kind(), other.kind()) { - (Some(self_kind), Some(other_kind)) => { - if self_kind == other_kind { - self_kind - } else { - Kind::Numeric(Box::new(Type::Error)) - } - } - (None, None) => Kind::u32(), - (Some(self_kind), None) => self_kind, - (None, Some(other_kind)) => other_kind, + let self_kind = self.kind(); + let other_kind = other.kind(); + if self_kind.unifies(&other_kind) { + self_kind + } else { + Kind::Numeric(Box::new(Type::Error)) } } @@ -1203,9 +1291,9 @@ impl Type { .expect("Cannot have variable sized strings as a parameter to main"), Type::FmtString(_, _) | Type::Unit - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::TraitAsType(..) - | Type::NamedGeneric(_, _, _) + | Type::NamedGeneric(_, _) | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -1261,71 +1349,62 @@ impl Type { ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; + if !self.kind().unifies(&Kind::IntegerOrField) { + return Err(UnificationError); + } + let this = self.substitute(bindings).follow_bindings(); match &this { Type::Integer(..) => { - bindings.insert(target_id, (var.clone(), this)); + bindings.insert(target_id, (var.clone(), Kind::Integer, this)); Ok(()) } Type::FieldElement if !only_integer => { - bindings.insert(target_id, (var.clone(), this)); + bindings.insert(target_id, (var.clone(), Kind::IntegerOrField, this)); Ok(()) } - Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { + Type::TypeVariable(self_var) => { let borrow = self_var.borrow(); match &*borrow { TypeBinding::Bound(typ) => { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(new_target_id) => { + TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(ref new_target_id, Kind::IntegerOrField) => { + let type_var_kind = Kind::IntegerOrField; if only_integer { + let var_clone = var.clone(); + Kind::Integer.unify(&type_var_kind)?; // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. - let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer); - bindings.insert(*new_target_id, (self_var.clone(), clone)); + let clone = Type::TypeVariable(var_clone); + bindings + .insert(*new_target_id, (self_var.clone(), type_var_kind, clone)); } else { - bindings.insert(target_id, (var.clone(), this.clone())); + bindings.insert( + target_id, + (var.clone(), Kind::IntegerOrField, this.clone()), + ); } Ok(()) } - } - } - Type::TypeVariable(self_var, TypeVariableKind::Integer) => { - let borrow = self_var.borrow(); - match &*borrow { - TypeBinding::Bound(typ) => { - typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - } - // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(_) => { - bindings.insert(target_id, (var.clone(), this.clone())); + TypeBinding::Unbound(_new_target_id, Kind::Integer) => { + Kind::Integer.unify(&Kind::Integer)?; + bindings.insert(target_id, (var.clone(), Kind::Integer, this.clone())); Ok(()) } - } - } - Type::TypeVariable(self_var, TypeVariableKind::Normal) => { - let borrow = self_var.borrow(); - match &*borrow { - TypeBinding::Bound(typ) => { - typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - } - // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(new_target_id) => { + TypeBinding::Unbound(new_target_id, ref type_var_kind) => { + let var_clone = var.clone(); // Bind to the most specific type variable kind - let clone_kind = if only_integer { - TypeVariableKind::Integer - } else { - TypeVariableKind::IntegerOrField - }; - let clone = Type::TypeVariable(var.clone(), clone_kind); - bindings.insert(*new_target_id, (self_var.clone(), clone)); + let clone_kind = + if only_integer { Kind::Integer } else { Kind::IntegerOrField }; + clone_kind.unify(type_var_kind)?; + let clone = Type::TypeVariable(var_clone); + bindings.insert(*new_target_id, (self_var.clone(), clone_kind, clone)); Ok(()) } } @@ -1335,7 +1414,7 @@ impl Type { } /// Try to bind the given type variable to self. Although the given type variable - /// is expected to be of TypeVariableKind::Normal, this binding can still fail + /// is expected to be of Kind::Normal, this binding can still fail /// if the given type variable occurs within `self` as that would create a recursive type. /// /// If successful, the binding is placed in the @@ -1344,18 +1423,23 @@ impl Type { &self, var: &TypeVariable, bindings: &mut TypeBindings, + kind: Kind, ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; + if !self.kind().unifies(&kind) { + return Err(UnificationError); + } + let this = self.substitute(bindings).follow_bindings(); - if let Some(binding) = this.get_inner_type_variable() { + if let Some((binding, kind)) = this.get_inner_type_variable() { match &*binding.borrow() { - TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings), + TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings, kind), // Don't recursively bind the same id to itself - TypeBinding::Unbound(id) if *id == target_id => return Ok(()), + TypeBinding::Unbound(id, _) if *id == target_id => return Ok(()), _ => (), } } @@ -1365,14 +1449,15 @@ impl Type { if this.occurs(target_id) { Err(UnificationError) } else { - bindings.insert(target_id, (var.clone(), this.clone())); + bindings.insert(target_id, (var.clone(), this.kind(), this.clone())); Ok(()) } } - fn get_inner_type_variable(&self) -> Option> { + fn get_inner_type_variable(&self) -> Option<(Shared, Kind)> { match self { - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => Some(var.1.clone()), + Type::TypeVariable(var) => Some((var.1.clone(), var.kind())), + Type::NamedGeneric(var, _) => Some((var.1.clone(), var.kind())), _ => None, } } @@ -1398,7 +1483,6 @@ impl Type { bindings: &mut TypeBindings, ) -> Result<(), UnificationError> { use Type::*; - use TypeVariableKind as Kind; let lhs = match self { Type::InfixExpr(..) => Cow::Owned(self.canonicalize()), @@ -1418,27 +1502,36 @@ impl Type { alias.try_unify(other, bindings) } - (TypeVariable(var, Kind::IntegerOrField), other) - | (other, TypeVariable(var, Kind::IntegerOrField)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = false; - other.try_bind_to_polymorphic_int(var, bindings, only_integer) - }) - } - - (TypeVariable(var, Kind::Integer), other) - | (other, TypeVariable(var, Kind::Integer)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = true; - other.try_bind_to_polymorphic_int(var, bindings, only_integer) - }) - } - - (TypeVariable(var, Kind::Normal), other) | (other, TypeVariable(var, Kind::Normal)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings) - }) - } + (TypeVariable(var), other) | (other, TypeVariable(var)) => match &*var.borrow() { + TypeBinding::Bound(typ) => { + if typ.is_numeric_value() { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = matches!(typ, Type::Integer(..)); + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } else { + other.try_unify_to_type_variable(var, bindings, |bindings| { + other.try_bind_to(var, bindings, typ.kind()) + }) + } + } + TypeBinding::Unbound(_id, Kind::IntegerOrField) => other + .try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = false; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }), + TypeBinding::Unbound(_id, Kind::Integer) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = true; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } + TypeBinding::Unbound(_id, type_var_kind) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + other.try_bind_to(var, bindings, type_var_kind.clone()) + }) + } + }, (Array(len_a, elem_a), Array(len_b, elem_b)) => { len_a.try_unify(len_b, bindings)?; @@ -1479,7 +1572,7 @@ impl Type { } } - (NamedGeneric(binding, _, _), other) | (other, NamedGeneric(binding, _, _)) + (NamedGeneric(binding, _), other) | (other, NamedGeneric(binding, _)) if !binding.borrow().is_unbound() => { if let TypeBinding::Bound(link) = &*binding.borrow() { @@ -1489,13 +1582,13 @@ impl Type { } } - (NamedGeneric(binding_a, name_a, kind_a), NamedGeneric(binding_b, name_b, kind_b)) => { + (NamedGeneric(binding_a, name_a), NamedGeneric(binding_b, name_b)) => { // Bound NamedGenerics are caught by the check above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); if name_a == name_b { - kind_a.unify(kind_b) + binding_a.kind().unify(&binding_b.kind()) } else { Err(UnificationError) } @@ -1541,8 +1634,9 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { + // TODO(https://github.com/noir-lang/noir/pull/6137): replace evaluate_to_u32 if let Some(other_value) = other.evaluate_to_u32() { - if *value == other_value && kind.matches_opt(other.kind()) { + if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { Err(UnificationError) @@ -1583,18 +1677,23 @@ impl Type { bindings: &mut TypeBindings, // Bind the type variable to a type. This is factored out since depending on the - // TypeVariableKind, there are different methods to check whether the variable can + // Kind, there are different methods to check whether the variable can // bind to the given type or not. bind_variable: impl FnOnce(&mut TypeBindings) -> Result<(), UnificationError>, ) -> Result<(), UnificationError> { match &*type_variable.borrow() { // If it is already bound, unify against what it is bound to TypeBinding::Bound(link) => link.try_unify(self, bindings), - TypeBinding::Unbound(id) => { + TypeBinding::Unbound(id, _) => { // We may have already "bound" this type variable in this call to // try_unify, so check those bindings as well. match bindings.get(id) { - Some((_, binding)) => binding.clone().try_unify(self, bindings), + Some((_, kind, binding)) => { + if !kind.unifies(&binding.kind()) { + return Err(UnificationError); + } + binding.clone().try_unify(self, bindings) + } // Otherwise, bind it None => bind_variable(bindings), @@ -1697,7 +1796,7 @@ impl Type { /// Apply the given type bindings, making them permanently visible for each /// clone of each type variable bound. pub fn apply_type_bindings(bindings: TypeBindings) { - for (type_variable, binding) in bindings.values() { + for (type_variable, _kind, binding) in bindings.values() { type_variable.bind(binding.clone()); } } @@ -1705,7 +1804,7 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - if let Some(binding) = self.get_inner_type_variable() { + if let Some((binding, _kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { return binding.evaluate_to_u32(); } @@ -1713,11 +1812,11 @@ impl Type { match self.canonicalize() { Type::Array(len, _elem) => len.evaluate_to_u32(), - Type::Constant(x, _) => Some(x), + Type::Constant(x, _kind) => Some(x), Type::InfixExpr(lhs, op, rhs) => { - let lhs = lhs.evaluate_to_u32()?; - let rhs = rhs.evaluate_to_u32()?; - op.function(lhs, rhs) + let lhs_u32 = lhs.evaluate_to_u32()?; + let rhs_u32 = rhs.evaluate_to_u32()?; + op.function(lhs_u32, rhs_u32, &lhs.infix_kind(&rhs)) } _ => None, } @@ -1771,9 +1870,9 @@ impl Type { match self { Type::Forall(typevars, typ) => { for var in typevars { - bindings - .entry(var.id()) - .or_insert_with(|| (var.clone(), interner.next_type_variable())); + bindings.entry(var.id()).or_insert_with(|| { + (var.clone(), var.kind(), interner.next_type_variable_with_kind(var.kind())) + }); } let instantiated = typ.force_substitute(&bindings); @@ -1792,8 +1891,8 @@ impl Type { let replacements = typevars .iter() .map(|var| { - let new = interner.next_type_variable(); - (var.id(), (var.clone(), new)) + let new = interner.next_type_variable_with_kind(var.kind()); + (var.id(), (var.clone(), var.kind(), new)) }) .collect(); @@ -1827,7 +1926,7 @@ impl Type { let replacements = typevars .iter() .zip(bindings) - .map(|(var, binding)| (var.id(), (var.clone(), binding))) + .map(|(var, binding)| (var.id(), (var.clone(), var.kind(), binding))) .collect(); let instantiated = typ.substitute(&replacements); @@ -1839,9 +1938,7 @@ impl Type { fn type_variable_id(&self) -> Option { match self { - Type::TypeVariable(variable, _) | Type::NamedGeneric(variable, _, _) => { - Some(variable.0) - } + Type::TypeVariable(variable) | Type::NamedGeneric(variable, _) => Some(variable.0), _ => None, } } @@ -1894,15 +1991,24 @@ impl Type { // type variables that have already been bound over. // This is needed for monomorphizing trait impl methods. match type_bindings.get(&binding.0) { - Some((_, replacement)) if substitute_bound_typevars => { + Some((_, _kind, replacement)) if substitute_bound_typevars => { recur_on_binding(binding.0, replacement) } _ => match &*binding.borrow() { TypeBinding::Bound(binding) => { binding.substitute_helper(type_bindings, substitute_bound_typevars) } - TypeBinding::Unbound(id) => match type_bindings.get(id) { - Some((_, replacement)) => recur_on_binding(binding.0, replacement), + TypeBinding::Unbound(id, _) => match type_bindings.get(id) { + Some((_, kind, replacement)) => { + assert!( + kind.unifies(&replacement.kind()), + "while substituting (unbound): expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + kind, + replacement.kind() + ); + + recur_on_binding(binding.0, replacement) + } None => self.clone(), }, }, @@ -1928,7 +2034,7 @@ impl Type { let fields = fields.substitute_helper(type_bindings, substitute_bound_typevars); Type::FmtString(Box::new(size), Box::new(fields)) } - Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding, _) => { + Type::NamedGeneric(binding, _) | Type::TypeVariable(binding) => { substitute_binding(binding) } // Do not substitute_helper fields, it can lead to infinite recursion @@ -2017,12 +2123,12 @@ impl Type { || args.named.iter().any(|arg| arg.typ.occurs(target_id)) } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), - Type::NamedGeneric(type_var, _, _) | Type::TypeVariable(type_var, _) => { + Type::NamedGeneric(type_var, _) | Type::TypeVariable(type_var) => { match &*type_var.borrow() { TypeBinding::Bound(binding) => { type_var.id() == target_id || binding.occurs(target_id) } - TypeBinding::Unbound(id) => *id == target_id, + TypeBinding::Unbound(id, _) => *id == target_id, } } Type::Forall(typevars, typ) => { @@ -2075,7 +2181,7 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var, _) | NamedGeneric(var, _, _) => { + TypeVariable(var) | NamedGeneric(var, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return typ.follow_bindings(); } @@ -2114,7 +2220,7 @@ impl Type { } pub fn from_generics(generics: &GenericTypeVars) -> Vec { - vecmap(generics, |var| Type::TypeVariable(var.clone(), TypeVariableKind::Normal)) + vecmap(generics, |var| Type::TypeVariable(var.clone())) } /// Replace any `Type::NamedGeneric` in this type with a `Type::TypeVariable` @@ -2156,12 +2262,11 @@ impl Type { typ.replace_named_generics_with_type_variables(); *self = typ; } - Type::TypeVariable(var, _) => { + Type::TypeVariable(var) => { let var = var.borrow(); if let TypeBinding::Bound(binding) = &*var { - let mut binding = binding.clone(); + let binding = binding.clone(); drop(var); - binding.replace_named_generics_with_type_variables(); *self = binding; } } @@ -2173,7 +2278,7 @@ impl Type { generic.typ.replace_named_generics_with_type_variables(); } } - Type::NamedGeneric(var, _, _) => { + Type::NamedGeneric(var, _) => { let type_binding = var.borrow(); if let TypeBinding::Bound(binding) = &*type_binding { let mut binding = binding.clone(); @@ -2182,7 +2287,7 @@ impl Type { *self = binding; } else { drop(type_binding); - *self = Type::TypeVariable(var.clone(), TypeVariableKind::Normal); + *self = Type::TypeVariable(var.clone()); } } Type::Function(args, ret, env, _unconstrained) => { @@ -2244,7 +2349,9 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - pub fn function(self, a: u32, b: u32) -> Option { + // TODO(https://github.com/noir-lang/noir/pull/6137): the Kind is included + // since it'll be needed for size checks + pub fn function(self, a: u32, b: u32, _kind: &Kind) -> Option { match self { BinaryTypeOperator::Addition => a.checked_add(b), BinaryTypeOperator::Subtraction => a.checked_sub(b), @@ -2270,18 +2377,6 @@ impl BinaryTypeOperator { } } -impl TypeVariableKind { - /// Returns the default type this type variable should be bound to if it is still unbound - /// during monomorphization. - pub(crate) fn default_type(&self) -> Option { - match self { - TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), - TypeVariableKind::Integer => Some(Type::default_int_type()), - TypeVariableKind::Normal => None, - } - } -} - impl From for PrintableType { fn from(value: Type) -> Self { Self::from(&value) @@ -2309,16 +2404,15 @@ impl From<&Type> for PrintableType { } Signedness::Signed => PrintableType::SignedInteger { width: (*bit_width).into() }, }, - Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { + Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_type().into(), - }, - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - match &*binding.borrow() { - TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_or_field_type().into(), + TypeBinding::Unbound(_, Kind::Integer) => Type::default_int_type().into(), + TypeBinding::Unbound(_, Kind::IntegerOrField) => { + Type::default_int_or_field_type().into() } - } + TypeBinding::Unbound(_, Kind::Numeric(typ)) => (*typ.clone()).into(), + TypeBinding::Unbound(_, Kind::Any | Kind::Normal) => unreachable!(), + }, Type::Bool => PrintableType::Boolean, Type::String(size) => { let size = size.evaluate_to_u32().expect("Cannot print variable sized strings"); @@ -2337,7 +2431,6 @@ impl From<&Type> for PrintableType { Type::Alias(alias, args) => alias.borrow().get_type(args).into(), Type::TraitAsType(..) => unreachable!(), Type::Tuple(types) => PrintableType::Tuple { types: vecmap(types, |typ| typ.into()) }, - Type::TypeVariable(_, _) => unreachable!(), Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), Type::Function(arguments, return_type, env, unconstrained) => PrintableType::Function { @@ -2371,12 +2464,18 @@ impl std::fmt::Debug for Type { Signedness::Signed => write!(f, "i{num_bits}"), Signedness::Unsigned => write!(f, "u{num_bits}"), }, - Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{:?}", var), - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - write!(f, "IntOrField{:?}", binding) - } - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - write!(f, "Int{:?}", binding) + Type::TypeVariable(var) => { + let binding = &var.1; + if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { + match type_var_kind { + Kind::Any | Kind::Normal => write!(f, "{:?}", var), + Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), + Kind::Integer => write!(f, "Int{:?}", binding), + Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), + } + } else { + write!(f, "{}", binding.borrow()) + } } Type::Struct(s, args) => { let args = vecmap(args, |arg| format!("{:?}", arg)); @@ -2406,8 +2505,8 @@ impl std::fmt::Debug for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, kind) => match kind { - Kind::Normal => { + Type::NamedGeneric(binding, name) => match binding.kind() { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { write!(f, "{}{:?}", name, binding) } Kind::Numeric(typ) => { @@ -2467,7 +2566,8 @@ impl std::fmt::Debug for StructType { impl std::hash::Hash for Type { fn hash(&self, state: &mut H) { - if let Some(variable) = self.get_inner_type_variable() { + if let Some((variable, kind)) = self.get_inner_type_variable() { + kind.hash(state); if let TypeBinding::Bound(typ) = &*variable.borrow() { typ.hash(state); return; @@ -2503,7 +2603,7 @@ impl std::hash::Hash for Type { alias.hash(state); args.hash(state); } - Type::TypeVariable(var, _) | Type::NamedGeneric(var, ..) => var.hash(state), + Type::TypeVariable(var) | Type::NamedGeneric(var, ..) => var.hash(state), Type::TraitAsType(trait_id, _, args) => { trait_id.hash(state); args.hash(state); @@ -2532,13 +2632,19 @@ impl std::hash::Hash for Type { impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { - if let Some(variable) = self.get_inner_type_variable() { + if let Some((variable, kind)) = self.get_inner_type_variable() { + if kind != other.kind() { + return false; + } if let TypeBinding::Bound(typ) = &*variable.borrow() { return typ == other; } } - if let Some(variable) = other.get_inner_type_variable() { + if let Some((variable, other_kind)) = other.get_inner_type_variable() { + if self.kind() != other_kind { + return false; + } if let TypeBinding::Bound(typ) = &*variable.borrow() { return self == typ; } @@ -2592,8 +2698,8 @@ impl PartialEq for Type { // still want them to be equal for canonicalization checks in arithmetic generics. // Without this we'd fail the `serialize` test. ( - NamedGeneric(lhs_var, _, _) | TypeVariable(lhs_var, _), - NamedGeneric(rhs_var, _, _) | TypeVariable(rhs_var, _), + NamedGeneric(lhs_var, _) | TypeVariable(lhs_var), + NamedGeneric(rhs_var, _) | TypeVariable(rhs_var), ) => lhs_var.id() == rhs_var.id(), _ => false, } diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 54b4c27a1f3..af94ef27535 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -20,8 +20,9 @@ impl Type { if let (Some(lhs_u32), Some(rhs_u32)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { - if let Some(result) = op.function(lhs_u32, rhs_u32) { - return Type::Constant(result, lhs.infix_kind(&rhs)); + let kind = lhs.infix_kind(&rhs); + if let Some(result) = op.function(lhs_u32, rhs_u32, &kind) { + return Type::Constant(result, kind); } } @@ -68,7 +69,7 @@ impl Type { queue.push(*rhs); } Type::Constant(new_constant, new_constant_kind) => { - if let Some(result) = op.function(constant, new_constant) { + if let Some(result) = op.function(constant, new_constant, &new_constant_kind) { constant = result; } else { let constant = Type::Constant(new_constant, new_constant_kind); @@ -205,7 +206,7 @@ impl Type { if l_op == Subtraction { op = op.inverse()?; } - let result = op.function(l_const, r_const)?; + let result = op.function(l_const, r_const, &lhs.infix_kind(rhs))?; let constant = Type::Constant(result, lhs.infix_kind(rhs)); Some(Type::InfixExpr(l_type, l_op, Box::new(constant))) } @@ -218,7 +219,7 @@ impl Type { if op == Division && (r_const == 0 || l_const % r_const != 0) { None } else { - let result = op.function(l_const, r_const)?; + let result = op.function(l_const, r_const, &lhs.infix_kind(rhs))?; let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); Some(Type::InfixExpr(l_type, l_op, constant)) } diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 4391c760701..9d98b125e32 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -36,31 +36,3 @@ pub use hir_def::types::*; // Unit tests that involve all modules pub mod tests; - -// API for experimental macros feature -pub mod macros_api { - - pub use acvm::FieldElement; - pub use fm::FileId; - pub use noirc_errors::Span; - - pub use crate::graph::CrateId; - pub use crate::hir_def::expr::{HirExpression, HirLiteral}; - pub use crate::hir_def::stmt::HirStatement; - pub use crate::node_interner::{NodeInterner, StructId}; - pub use crate::parser::{parse_program, SortedModule}; - pub use crate::token::SecondaryAttribute; - - pub use crate::ast::{ - BlockExpression, CallExpression, CastExpression, Documented, Expression, ExpressionKind, - FunctionReturnType, Ident, IndexExpression, ItemVisibility, LetStatement, Literal, - MemberAccessExpression, MethodCallExpression, NoirFunction, Path, PathKind, Pattern, - Statement, UnresolvedType, UnresolvedTypeData, Visibility, - }; - pub use crate::ast::{ - ForLoopStatement, ForRange, FunctionDefinition, ImportStatement, NoirStruct, Param, - PrefixExpression, Signedness, StatementKind, TypeImpl, UnaryOp, - }; - pub use crate::hir::{def_map::ModuleDefId, Context as HirContext}; - pub use crate::{StructType, Type}; -} diff --git a/compiler/noirc_frontend/src/locations.rs b/compiler/noirc_frontend/src/locations.rs index cba667d5dcb..65adf4ca9c4 100644 --- a/compiler/noirc_frontend/src/locations.rs +++ b/compiler/noirc_frontend/src/locations.rs @@ -6,8 +6,9 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, - macros_api::{NodeInterner, StructId}, - node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, + node_interner::{ + DefinitionId, FuncId, GlobalId, NodeInterner, ReferenceId, StructId, TraitId, TypeAliasId, + }, }; use petgraph::prelude::NodeIndex as PetGraphIndex; diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 12cc3b55b1f..63ef807d898 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -21,7 +21,7 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - Type, TypeBinding, TypeBindings, + Kind, Type, TypeBinding, TypeBindings, }; use acvm::{acir::AcirField, FieldElement}; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -857,7 +857,14 @@ impl<'interner> Monomorphizer<'interner> { // Ensure all instantiation bindings are bound. // This ensures even unused type variables like `fn foo() {}` have concrete types if let Some(bindings) = self.interner.try_get_instantiation_bindings(expr_id) { - for (_, binding) in bindings.values() { + for (_, kind, binding) in bindings.values() { + match kind { + Kind::Any => (), + Kind::Normal => (), + Kind::Integer => (), + Kind::IntegerOrField => (), + Kind::Numeric(typ) => Self::check_type(typ, ident.location)?, + } Self::check_type(binding, ident.location)?; } } @@ -916,20 +923,31 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Ident(ident) } }, - DefinitionKind::GenericType(type_variable) => { + DefinitionKind::NumericGeneric(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { - TypeBinding::Unbound(_) => { + TypeBinding::Unbound(_, _) => { unreachable!("Unbound type variable used in expression") } TypeBinding::Bound(binding) => binding.evaluate_to_u32().unwrap_or_else(|| { panic!("Non-numeric type variable used in expression expecting a value") }), }; - - let value = FieldElement::from(value as u128); let location = self.interner.id_location(expr_id); + + if !Kind::Numeric(numeric_typ.clone()) + .unifies(&Kind::Numeric(Box::new(typ.clone()))) + { + let message = "ICE: Generic's kind does not match expected type"; + return Err(MonomorphizationError::InternalError { location, message }); + } + let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) + ast::Expression::Literal(ast::Literal::Integer( + (value as u128).into(), + false, + typ, + location, + )) } }; @@ -967,8 +985,8 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _, _) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { + HirType::NamedGeneric(binding, _) => { + if let TypeBinding::Bound(ref binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -979,15 +997,18 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Field } - HirType::TypeVariable(binding, kind) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { - return Self::convert_type(binding, location); - } + HirType::TypeVariable(ref binding) => { + let type_var_kind = match &*binding.borrow() { + TypeBinding::Bound(ref binding) => { + return Self::convert_type(binding, location); + } + TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + }; // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = match kind.default_type() { + let default = match type_var_kind.default_type() { Some(typ) => typ, None => return Err(MonomorphizationError::NoDefaultType { location }), }; @@ -1085,23 +1106,26 @@ impl<'interner> Monomorphizer<'interner> { HirType::FmtString(_size, fields) => Self::check_type(fields.as_ref(), location), HirType::Array(_length, element) => Self::check_type(element.as_ref(), location), HirType::Slice(element) => Self::check_type(element.as_ref(), location), - HirType::NamedGeneric(binding, _, _) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { + HirType::NamedGeneric(binding, _) => { + if let TypeBinding::Bound(ref binding) = &*binding.borrow() { return Self::check_type(binding, location); } Ok(()) } - HirType::TypeVariable(binding, kind) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { - return Self::check_type(binding, location); - } + HirType::TypeVariable(ref binding) => { + let type_var_kind = match &*binding.borrow() { + TypeBinding::Bound(binding) => { + return Self::check_type(binding, location); + } + TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + }; // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = match kind.default_type() { + let default = match type_var_kind.default_type() { Some(typ) => typ, None => return Err(MonomorphizationError::NoDefaultType { location }), }; @@ -1441,9 +1465,9 @@ impl<'interner> Monomorphizer<'interner> { fn follow_bindings(&self, bindings: &TypeBindings) -> TypeBindings { bindings .iter() - .map(|(id, (var, binding))| { + .map(|(id, (var, kind, binding))| { let binding2 = binding.follow_bindings(); - (*id, (var.clone(), binding2)) + (*id, (var.clone(), kind.clone(), binding2)) }) .collect() } @@ -1910,14 +1934,14 @@ fn unwrap_struct_type( } pub fn perform_instantiation_bindings(bindings: &TypeBindings) { - for (var, binding) in bindings.values() { + for (var, _kind, binding) in bindings.values() { var.force_bind(binding.clone()); } } pub fn undo_instantiation_bindings(bindings: TypeBindings) { - for (id, (var, _)) in bindings { - var.unbind(id); + for (id, (var, kind, _)) in bindings { + var.unbind(id, kind); } } @@ -1944,7 +1968,7 @@ pub fn perform_impl_bindings( interner.function_meta(&impl_method).typ.unwrap_forall().1.clone(); // Make each NamedGeneric in this type bindable by replacing it with a TypeVariable - // with the same internal id and binding. + // with the same internal id, binding. trait_method_type.replace_named_generics_with_type_variables(); impl_method_type.replace_named_generics_with_type_variables(); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index a95282a1ec9..6a7096c10c2 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -13,22 +13,17 @@ use petgraph::prelude::DiGraph; use petgraph::prelude::NodeIndex as PetGraphIndex; use rustc_hash::FxHashMap as HashMap; -use crate::ast::ExpressionKind; -use crate::ast::Ident; -use crate::ast::LValue; -use crate::ast::Pattern; -use crate::ast::StatementKind; -use crate::ast::UnresolvedTypeData; +use crate::ast::{ + ExpressionKind, Ident, LValue, Pattern, StatementKind, UnaryOp, UnresolvedTypeData, +}; use crate::graph::CrateId; use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::DefMaps; -use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::hir::def_map::{LocalModuleId, ModuleDefId, ModuleId}; use crate::hir::type_check::generics::TraitGenerics; use crate::hir_def::traits::NamedType; -use crate::macros_api::ModuleDefId; -use crate::macros_api::UnaryOp; use crate::usage_tracker::UnusedItem; use crate::usage_tracker::UsageTracker; use crate::QuotedType; @@ -39,7 +34,7 @@ use crate::hir_def::expr::HirIdent; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::traits::TraitImpl; use crate::hir_def::traits::{Trait, TraitConstraint}; -use crate::hir_def::types::{StructType, Type}; +use crate::hir_def::types::{Kind, StructType, Type}; use crate::hir_def::{ expr::HirExpression, function::{FuncMeta, HirFunction}, @@ -49,7 +44,7 @@ use crate::locations::LocationIndices; use crate::token::{Attributes, SecondaryAttribute}; use crate::GenericTypeVars; use crate::Generics; -use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind}; +use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId}; /// An arbitrary number to limit the recursion depth when searching for trait impls. /// This is needed to stop recursing for cases such as `impl Foo for T where T: Eq` @@ -586,7 +581,7 @@ pub enum DefinitionKind { /// Generic types in functions (T, U in `fn foo(...)` are declared as variables /// in scope in case they resolve to numeric generics later. - GenericType(TypeVariable), + NumericGeneric(TypeVariable, Box), } impl DefinitionKind { @@ -601,7 +596,7 @@ impl DefinitionKind { DefinitionKind::Function(_) => None, DefinitionKind::Global(_) => None, DefinitionKind::Local(id) => *id, - DefinitionKind::GenericType(_) => None, + DefinitionKind::NumericGeneric(_, _) => None, } } } @@ -733,7 +728,7 @@ impl NodeInterner { crate_id: unresolved_trait.crate_id, location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), generics, - self_type_typevar: TypeVariable::unbound(self.next_type_variable_id()), + self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal), methods: Vec::new(), method_ids: unresolved_trait.method_ids.clone(), associated_types, @@ -1336,6 +1331,10 @@ impl NodeInterner { Type::type_variable(self.next_type_variable_id()) } + pub fn next_type_variable_with_kind(&self, kind: Kind) -> Type { + Type::type_variable_with_kind(self, kind) + } + pub fn store_instantiation_bindings( &mut self, expr_id: ExprId, @@ -1737,7 +1736,16 @@ impl NodeInterner { // Replace each generic with a fresh type variable let substitutions = impl_generics .into_iter() - .map(|typevar| (typevar.id(), (typevar, self.next_type_variable()))) + .map(|typevar| { + let typevar_kind = typevar.kind(); + let typevar_id = typevar.id(); + let substitution = ( + typevar, + typevar_kind.clone(), + self.next_type_variable_with_kind(typevar_kind), + ); + (typevar_id, substitution) + }) .collect(); let instantiated_object_type = object_type.substitute(&substitutions); @@ -2228,11 +2236,17 @@ impl NodeInterner { let trait_generics = the_trait.generics.clone(); let self_type_var = the_trait.self_type_typevar.clone(); - bindings.insert(self_type_var.id(), (self_type_var, impl_self_type)); + bindings.insert( + self_type_var.id(), + (self_type_var.clone(), self_type_var.kind(), impl_self_type), + ); for (trait_generic, trait_impl_generic) in trait_generics.iter().zip(trait_impl_generics) { let type_var = trait_generic.type_var.clone(); - bindings.insert(type_var.id(), (type_var, trait_impl_generic.clone())); + bindings.insert( + type_var.id(), + (type_var, trait_generic.kind(), trait_impl_generic.clone()), + ); } // Now that the normal bindings are added, we still need to bind the associated types @@ -2241,7 +2255,10 @@ impl NodeInterner { for (trait_type, impl_type) in trait_associated_types.iter().zip(impl_associated_types) { let type_variable = trait_type.type_var.clone(); - bindings.insert(type_variable.id(), (type_variable, impl_type.typ.clone())); + bindings.insert( + type_variable.id(), + (type_variable, trait_type.kind(), impl_type.typ.clone()), + ); } bindings @@ -2362,22 +2379,26 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Array(_, _) => Some(Array), Type::Slice(_) => Some(Slice), Type::Integer(_, _) => Some(FieldOrInt), - Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt), - Type::TypeVariable(_, TypeVariableKind::Integer) => Some(FieldOrInt), + Type::TypeVariable(var) => { + if var.is_integer() || var.is_integer_or_field() { + Some(FieldOrInt) + } else { + None + } + } Type::Bool => Some(Bool), Type::String(_) => Some(String), Type::FmtString(_, _) => Some(FmtString), Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _, _) => Some(Function), - Type::NamedGeneric(_, _, _) => Some(Generic), + Type::NamedGeneric(_, _) => Some(Generic), Type::Quoted(quoted) => Some(Quoted(*quoted)), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), // We do not support adding methods to these types - Type::TypeVariable(_, _) - | Type::Forall(_, _) + Type::Forall(_, _) | Type::Constant(..) | Type::Error | Type::Struct(_, _) diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 66d0ca29ca6..dc363248d72 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -2,9 +2,8 @@ use chumsky::Parser; use noirc_errors::Span; use crate::{ - macros_api::SecondaryAttribute, parser::{NoirParser, ParserError, ParserErrorReason}, - token::{Attribute, Attributes, Token, TokenKind}, + token::{Attribute, Attributes, SecondaryAttribute, Token, TokenKind}, }; use super::primitives::token_kind; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index dc8b968ea7a..7b1f67a48bd 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -14,9 +14,9 @@ use crate::{ }; use crate::{ ast::{ - FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, + FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, + UnresolvedTypeData, Visibility, }, - macros_api::UnresolvedTypeData, parser::{ParserError, ParserErrorReason}, }; use crate::{ diff --git a/compiler/noirc_frontend/src/parser/parser/lambdas.rs b/compiler/noirc_frontend/src/parser/parser/lambdas.rs index 5ef0b918375..68b5724edc6 100644 --- a/compiler/noirc_frontend/src/parser/parser/lambdas.rs +++ b/compiler/noirc_frontend/src/parser/parser/lambdas.rs @@ -1,8 +1,7 @@ use chumsky::{primitive::just, Parser}; use super::{parse_type, pattern}; -use crate::ast::{Expression, ExpressionKind, Lambda, Pattern, UnresolvedType}; -use crate::macros_api::UnresolvedTypeData; +use crate::ast::{Expression, ExpressionKind, Lambda, Pattern, UnresolvedType, UnresolvedTypeData}; use crate::{ parser::{labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, NoirParser}, token::Token, diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 1c9c24f5376..4babd0f6730 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,5 +1,6 @@ -use crate::ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, TypePath, UnresolvedType}; -use crate::macros_api::ExpressionKind; +use crate::ast::{ + AsTraitPath, ExpressionKind, Ident, Path, PathKind, PathSegment, TypePath, UnresolvedType, +}; use crate::parser::{NoirParser, ParserError, ParserErrorReason}; use crate::token::{Keyword, Token}; diff --git a/compiler/noirc_frontend/src/parser/parser/primitives.rs b/compiler/noirc_frontend/src/parser/parser/primitives.rs index 7fcca89f70c..5a040f23619 100644 --- a/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -1,7 +1,8 @@ use chumsky::prelude::*; -use crate::ast::{ExpressionKind, GenericTypeArgs, Ident, PathSegment, UnaryOp}; -use crate::macros_api::{StatementKind, UnresolvedType}; +use crate::ast::{ + ExpressionKind, GenericTypeArgs, Ident, PathSegment, StatementKind, UnaryOp, UnresolvedType, +}; use crate::parser::ParserErrorReason; use crate::{ parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index b95319f6da0..78453d7f7a2 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -10,10 +10,9 @@ use super::{ }; use crate::ast::{ - Documented, Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, - TraitImplItemKind, TraitItem, UnresolvedTraitConstraint, UnresolvedType, + Documented, Expression, ItemVisibility, NoirTrait, NoirTraitImpl, Pattern, TraitBound, + TraitImplItem, TraitImplItemKind, TraitItem, UnresolvedTraitConstraint, UnresolvedType, }; -use crate::macros_api::Pattern; use crate::parser::spanned; use crate::{ parser::{ diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index a27cd1087c4..5bce82a84f9 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1586,9 +1586,7 @@ fn struct_numeric_generic_in_struct() { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::UnsupportedNumericGenericType { .. } - ), + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType(_)), )); } @@ -1641,7 +1639,6 @@ fn bool_generic_as_loop_bound() { "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 3); - assert!(matches!( errors[0].0, CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), @@ -1706,7 +1703,7 @@ fn normal_generic_as_array_length() { #[test] fn numeric_generic_as_param_type() { let src = r#" - pub fn foo(x: I) -> I { + pub fn foo(x: I) -> I { let _q: I = 5; x } @@ -1731,6 +1728,68 @@ fn numeric_generic_as_param_type() { )); } +#[test] +fn numeric_generic_as_unused_param_type() { + let src = r#" + pub fn foo(_x: I) { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_as_unused_trait_fn_param_type() { + let src = r#" + trait Foo { + fn foo(_x: I) { } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Foo is unused + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnusedItem { .. }), + )); +} + +#[test] +fn numeric_generic_as_return_type() { + let src = r#" + // std::mem::zeroed() without stdlib + trait Zeroed { + fn zeroed(self) -> T; + } + + fn foo(x: T) -> I where T: Zeroed { + x.zeroed() + } + + fn main() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + // Error from the return type + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // foo is unused + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnusedItem { .. }), + )); +} + #[test] fn numeric_generic_used_in_nested_type_fails() { let src = r#" @@ -2385,23 +2444,6 @@ fn impl_not_found_for_inner_impl() { )); } -#[test] -fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); -} - #[test] fn cannot_call_unconstrained_function_outside_of_unsafe() { let src = r#" @@ -3035,6 +3077,35 @@ fn infer_globals_to_u32_from_type_use() { assert_eq!(errors.len(), 0); } +#[test] +fn struct_array_len() { + let src = r#" + struct Array { + inner: [T; N], + } + + impl Array { + pub fn len(self) -> u32 { + N as u32 + } + } + + fn main(xs: [Field; 2]) { + let ys = Array { + inner: xs, + }; + assert(ys.len() == 2); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) + )); +} + #[test] fn non_u32_in_array_length() { let src = r#" @@ -3084,7 +3155,8 @@ fn use_numeric_generic_in_trait_method() { } fn main() { - let _ = Bar{}.foo([1,2,3]); + let bytes: [u8; 3] = [1,2,3]; + let _ = Bar{}.foo(bytes); } "#; diff --git a/compiler/noirc_frontend/src/tests/imports.rs b/compiler/noirc_frontend/src/tests/imports.rs index dfdc60e15e4..5ebc5b3bdbd 100644 --- a/compiler/noirc_frontend/src/tests/imports.rs +++ b/compiler/noirc_frontend/src/tests/imports.rs @@ -21,6 +21,23 @@ fn use_super() { assert_no_errors(src); } +#[test] +fn no_super() { + let src = "use super::some_func;"; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( + PathResolutionError::NoSuper(span), + )) = &errors[0].0 + else { + panic!("Expected a 'no super' error, got {:?}", errors[0].0); + }; + + assert_eq!(span.start(), 4); + assert_eq!(span.end(), 9); +} + #[test] fn use_super_in_path() { let src = r#" diff --git a/compiler/noirc_frontend/src/tests/metaprogramming.rs b/compiler/noirc_frontend/src/tests/metaprogramming.rs index d980cba5cfd..ec52310b3d6 100644 --- a/compiler/noirc_frontend/src/tests/metaprogramming.rs +++ b/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -2,6 +2,17 @@ use crate::hir::def_collector::dc_crate::CompilationError; use super::get_program_errors; +// Regression for #5388 +#[test] +fn comptime_let() { + let src = r#"fn main() { + comptime let my_var = 2; + assert_eq(my_var, 2); + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + #[test] fn comptime_type_in_runtime_code() { let source = "pub fn foo(_f: FunctionDefinition) {}"; diff --git a/compiler/noirc_frontend/src/tests/turbofish.rs b/compiler/noirc_frontend/src/tests/turbofish.rs index 43d536fd196..b1156b20eb0 100644 --- a/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/compiler/noirc_frontend/src/tests/turbofish.rs @@ -196,3 +196,21 @@ fn turbofish_in_struct_pattern_generic_count_mismatch() { assert_eq!(*expected, 1); assert_eq!(*found, 2); } + +#[test] +fn numeric_turbofish() { + let src = r#" + struct Reader { + } + + impl Reader { + fn read(_self: Self) {} + } + + fn main() { + let reader: Reader<1234> = Reader {}; + let _ = reader.read::<1234>(); + } + "#; + assert_no_errors(src); +} diff --git a/compiler/noirc_frontend/src/usage_tracker.rs b/compiler/noirc_frontend/src/usage_tracker.rs index 275ca1f964b..0a112c6937d 100644 --- a/compiler/noirc_frontend/src/usage_tracker.rs +++ b/compiler/noirc_frontend/src/usage_tracker.rs @@ -3,8 +3,7 @@ use std::collections::HashMap; use crate::{ ast::{Ident, ItemVisibility}, hir::def_map::ModuleId, - macros_api::StructId, - node_interner::{FuncId, GlobalId, TraitId, TypeAliasId}, + node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/docs/docs/explainers/explainer-recursion.md b/docs/docs/explainers/explainer-recursion.md index 18846176ca7..df8529ef4e0 100644 --- a/docs/docs/explainers/explainer-recursion.md +++ b/docs/docs/explainers/explainer-recursion.md @@ -111,7 +111,7 @@ He might find it more efficient to generate a proof for that setup phase separat ## What params do I need -As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - The proof to verify - The Verification Key of the circuit that generated the proof diff --git a/docs/docs/how_to/how-to-recursion.md b/docs/docs/how_to/how-to-recursion.md index 71f02fa5435..c8c4dc9f5b4 100644 --- a/docs/docs/how_to/how-to-recursion.md +++ b/docs/docs/how_to/how-to-recursion.md @@ -25,7 +25,7 @@ This guide shows you how to use recursive proofs in your NoirJS app. For the sak - You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). - You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. diff --git a/docs/docs/noir/standard_library/black_box_fns.md b/docs/docs/noir/standard_library/black_box_fns.md index d5694250f05..d6079ab182c 100644 --- a/docs/docs/noir/standard_library/black_box_fns.md +++ b/docs/docs/noir/standard_library/black_box_fns.md @@ -25,7 +25,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.md) +- [Recursive proof verification](./recursion.mdx) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/ciphers.mdx index 0103791d2e4..d2ceb63175a 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -7,7 +7,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## aes128 @@ -25,4 +25,4 @@ fn main() { ``` - \ No newline at end of file + \ No newline at end of file diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 6787c9f46a1..d46bdc0729b 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, sidebar_position: 3 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. @@ -25,7 +25,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256k1::verify_signature_slice @@ -33,7 +33,7 @@ Verifier for ECDSA Secp256k1 signatures where the message is a slice. #include_code ecdsa_secp256k1_slice noir_stdlib/src/ecdsa_secp256k1.nr rust - + ## ecdsa_secp256r1::verify_signature @@ -51,7 +51,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256r1::verify_signature @@ -59,4 +59,4 @@ Verifier for ECDSA Secp256r1 signatures where the message is a slice. #include_code ecdsa_secp256r1_slice noir_stdlib/src/ecdsa_secp256r1.nr rust - + diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx index 1ad42a5ac96..b283de693c8 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, eddsa, signatures] sidebar_position: 5 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## eddsa::eddsa_poseidon_verify @@ -23,7 +23,7 @@ use std::hash::poseidon2::Poseidon2Hasher; eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); ``` - + ## eddsa::eddsa_to_pub diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx index f1122fc37d5..e10688857a6 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, scalar multiplication] sidebar_position: 1 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. @@ -74,4 +74,4 @@ fn main() { } ``` - + diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index d2a8204bccb..c33ce34e4d1 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -8,7 +8,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## sha256 @@ -28,7 +28,7 @@ fn main() { ``` - + ## blake2s @@ -45,7 +45,7 @@ fn main() { } ``` - + ## blake3 @@ -62,7 +62,7 @@ fn main() { } ``` - + ## pedersen_hash @@ -74,7 +74,7 @@ example: #include_code pedersen-hash test_programs/execution_success/pedersen_hash/src/main.nr rust - + ## pedersen_commitment @@ -86,7 +86,7 @@ example: #include_code pedersen-commitment test_programs/execution_success/pedersen_commitment/src/main.nr rust - + ## keccak256 @@ -100,7 +100,7 @@ example: #include_code keccak256 test_programs/execution_success/keccak256/src/main.nr rust - + ## poseidon diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx index 2c9eb18cd34..286a0ac6c7d 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, schnorr, signatures] sidebar_position: 2 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## schnorr::verify_signature @@ -34,7 +34,7 @@ const signature = Array.from( ... ``` - + ## schnorr::verify_signature_slice @@ -43,4 +43,4 @@ where the message is a slice. #include_code schnorr_verify_slice noir_stdlib/src/schnorr.nr rust - + diff --git a/docs/docs/noir/standard_library/recursion.md b/docs/docs/noir/standard_library/recursion.mdx similarity index 96% rename from docs/docs/noir/standard_library/recursion.md rename to docs/docs/noir/standard_library/recursion.mdx index 7f4dcebf084..60414a2fa51 100644 --- a/docs/docs/noir/standard_library/recursion.md +++ b/docs/docs/noir/standard_library/recursion.mdx @@ -4,7 +4,7 @@ description: Learn about how to write recursive proofs in Noir. keywords: [recursion, recursive proofs, verification_key, verify_proof] --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. @@ -35,7 +35,7 @@ By incorporating this attribute directly in the circuit's definition, tooling li pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` - + ## Example usage diff --git a/docs/src/components/Notes/_blackbox.jsx b/docs/src/components/Notes/_blackbox.jsx new file mode 100644 index 00000000000..ae3c5987cb6 --- /dev/null +++ b/docs/src/components/Notes/_blackbox.jsx @@ -0,0 +1,12 @@ +import Link from '@docusaurus/Link'; + +export default function BlackBoxInfo({ to }) { + return ( +
+

+ This is a black box function. Read this section to learn more about black box functions in + Noir. +

+
+ ); +} diff --git a/docs/src/components/Notes/_blackbox.mdx b/docs/src/components/Notes/_blackbox.mdx deleted file mode 100644 index 514ca00a7e7..00000000000 --- a/docs/src/components/Notes/_blackbox.mdx +++ /dev/null @@ -1,5 +0,0 @@ -:::info - -This is a black box function. Read [this section](/docs/noir/standard_library/black_box_fns) to learn more about black box functions in Noir. - -::: diff --git a/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md index 18846176ca7..df8529ef4e0 100644 --- a/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md +++ b/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md @@ -111,7 +111,7 @@ He might find it more efficient to generate a proof for that setup phase separat ## What params do I need -As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - The proof to verify - The Verification Key of the circuit that generated the proof diff --git a/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md index 71f02fa5435..c8c4dc9f5b4 100644 --- a/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md +++ b/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md @@ -25,7 +25,7 @@ This guide shows you how to use recursive proofs in your NoirJS app. For the sak - You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). - You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md index d5694250f05..d6079ab182c 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md @@ -25,7 +25,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.md) +- [Recursive proof verification](./recursion.mdx) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx index d75e50d4b89..d6a5e1a79eb 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -7,7 +7,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## aes128 @@ -29,4 +29,4 @@ fn main() { ``` - \ No newline at end of file + \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 8520071e95f..4c22e70e8de 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, sidebar_position: 3 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. @@ -34,7 +34,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256k1::verify_signature_slice @@ -51,7 +51,7 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - + ## ecdsa_secp256r1::verify_signature @@ -78,7 +78,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256r1::verify_signature @@ -95,4 +95,4 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - + diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx index 1ad42a5ac96..ef4386052eb 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, eddsa, signatures] sidebar_position: 5 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## eddsa::eddsa_poseidon_verify @@ -23,7 +23,7 @@ use std::hash::poseidon2::Poseidon2Hasher; eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); ``` - + ## eddsa::eddsa_to_pub diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx index 0230f6a8ab9..68d033e9d60 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, scalar multiplication] sidebar_position: 1 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. @@ -95,4 +95,4 @@ fn main() { } ``` - + diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx index dadff87bb69..ddcfbb2175f 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -8,7 +8,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## sha256 @@ -36,7 +36,7 @@ fn main() { ``` - + ## blake2s @@ -57,7 +57,7 @@ fn main() { } ``` - + ## blake3 @@ -78,7 +78,7 @@ fn main() { } ``` - + ## pedersen_hash @@ -101,7 +101,7 @@ fn main(x: Field, y: Field, expected_hash: Field) { > Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - + ## pedersen_commitment @@ -125,7 +125,7 @@ fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::Embedd > Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - + ## keccak256 @@ -164,7 +164,7 @@ fn main(x: Field, result: [u8; 32]) { > Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - + ## poseidon diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx index a32138daaa6..00e7f257612 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, schnorr, signatures] sidebar_position: 2 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## schnorr::verify_signature @@ -43,7 +43,7 @@ const signature = Array.from( ... ``` - + ## schnorr::verify_signature_slice @@ -61,4 +61,4 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/schnorr.nr#L13-L20 - + diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.mdx similarity index 96% rename from docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.md rename to docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.mdx index 8cfb37fc52d..8fdb8e8f514 100644 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.mdx @@ -4,7 +4,7 @@ description: Learn about how to write recursive proofs in Noir. keywords: [recursion, recursive proofs, verification_key, verify_proof] --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. @@ -35,7 +35,7 @@ By incorporating this attribute directly in the circuit's definition, tooling li pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` - + ## Example usage diff --git a/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md index 18846176ca7..df8529ef4e0 100644 --- a/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md +++ b/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md @@ -111,7 +111,7 @@ He might find it more efficient to generate a proof for that setup phase separat ## What params do I need -As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - The proof to verify - The Verification Key of the circuit that generated the proof diff --git a/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md index 71f02fa5435..c8c4dc9f5b4 100644 --- a/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md +++ b/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md @@ -25,7 +25,7 @@ This guide shows you how to use recursive proofs in your NoirJS app. For the sak - You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). - You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md index d5694250f05..d6079ab182c 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md @@ -25,7 +25,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.md) +- [Recursive proof verification](./recursion.mdx) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx index d75e50d4b89..d6a5e1a79eb 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -7,7 +7,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## aes128 @@ -29,4 +29,4 @@ fn main() { ``` - \ No newline at end of file + \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 8520071e95f..4c22e70e8de 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, sidebar_position: 3 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. @@ -34,7 +34,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256k1::verify_signature_slice @@ -51,7 +51,7 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - + ## ecdsa_secp256r1::verify_signature @@ -78,7 +78,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256r1::verify_signature @@ -95,4 +95,4 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - + diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx index 1ad42a5ac96..b283de693c8 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, eddsa, signatures] sidebar_position: 5 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## eddsa::eddsa_poseidon_verify @@ -23,7 +23,7 @@ use std::hash::poseidon2::Poseidon2Hasher; eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); ``` - + ## eddsa::eddsa_to_pub diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx index 719549bc418..69e0265c81a 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, scalar multiplication] sidebar_position: 1 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. @@ -92,4 +92,4 @@ fn main() { } ``` - + diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx index 63a4a2afd53..797ff8cc22c 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -8,7 +8,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## sha256 @@ -36,7 +36,7 @@ fn main() { ``` - + ## blake2s @@ -57,7 +57,7 @@ fn main() { } ``` - + ## blake3 @@ -78,7 +78,7 @@ fn main() { } ``` - + ## pedersen_hash @@ -101,7 +101,7 @@ fn main(x: Field, y: Field, expected_hash: Field) { > Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - + ## pedersen_commitment @@ -125,7 +125,7 @@ fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::Embedd > Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - + ## keccak256 @@ -164,7 +164,7 @@ fn main(x: Field, result: [u8; 32]) { > Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - + ## poseidon diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx index a32138daaa6..00e7f257612 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, schnorr, signatures] sidebar_position: 2 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## schnorr::verify_signature @@ -43,7 +43,7 @@ const signature = Array.from( ... ``` - + ## schnorr::verify_signature_slice @@ -61,4 +61,4 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/schnorr.nr#L13-L20 - + diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.mdx similarity index 96% rename from docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.md rename to docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.mdx index 7f4dcebf084..60414a2fa51 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.mdx @@ -4,7 +4,7 @@ description: Learn about how to write recursive proofs in Noir. keywords: [recursion, recursive proofs, verification_key, verify_proof] --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. @@ -35,7 +35,7 @@ By incorporating this attribute directly in the circuit's definition, tooling li pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` - + ## Example usage diff --git a/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md index 18846176ca7..df8529ef4e0 100644 --- a/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md +++ b/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md @@ -111,7 +111,7 @@ He might find it more efficient to generate a proof for that setup phase separat ## What params do I need -As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - The proof to verify - The Verification Key of the circuit that generated the proof diff --git a/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md index 71f02fa5435..c8c4dc9f5b4 100644 --- a/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md +++ b/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md @@ -25,7 +25,7 @@ This guide shows you how to use recursive proofs in your NoirJS app. For the sak - You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). - You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md index d5694250f05..d6079ab182c 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md @@ -25,7 +25,7 @@ Here is a list of the current black box functions: - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.md) +- [Recursive proof verification](./recursion.mdx) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx index d75e50d4b89..d6a5e1a79eb 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -7,7 +7,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## aes128 @@ -29,4 +29,4 @@ fn main() { ``` - \ No newline at end of file + \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 8520071e95f..4c22e70e8de 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, sidebar_position: 3 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. @@ -34,7 +34,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256k1::verify_signature_slice @@ -51,7 +51,7 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - + ## ecdsa_secp256r1::verify_signature @@ -78,7 +78,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` - + ## ecdsa_secp256r1::verify_signature @@ -95,4 +95,4 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - + diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx index 1ad42a5ac96..b283de693c8 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, eddsa, signatures] sidebar_position: 5 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## eddsa::eddsa_poseidon_verify @@ -23,7 +23,7 @@ use std::hash::poseidon2::Poseidon2Hasher; eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); ``` - + ## eddsa::eddsa_to_pub diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx index 8ded020bf27..2da1e34f008 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, scalar multiplication] sidebar_position: 1 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. @@ -92,4 +92,4 @@ fn main() { } ``` - + diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx index 2581690e034..d6640d26f25 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -8,7 +8,7 @@ keywords: sidebar_position: 0 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## sha256 @@ -36,7 +36,7 @@ fn main() { ``` - + ## blake2s @@ -57,7 +57,7 @@ fn main() { } ``` - + ## blake3 @@ -78,7 +78,7 @@ fn main() { } ``` - + ## pedersen_hash @@ -101,7 +101,7 @@ fn main(x: Field, y: Field, expected_hash: Field) { > Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - + ## pedersen_commitment @@ -125,7 +125,7 @@ fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::Embedd > Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - + ## keccak256 @@ -164,7 +164,7 @@ fn main(x: Field, result: [u8; 32]) { > Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - + ## poseidon diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx index a32138daaa6..00e7f257612 100644 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -5,7 +5,7 @@ keywords: [cryptographic primitives, Noir project, schnorr, signatures] sidebar_position: 2 --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; ## schnorr::verify_signature @@ -43,7 +43,7 @@ const signature = Array.from( ... ``` - + ## schnorr::verify_signature_slice @@ -61,4 +61,4 @@ pub fn verify_signature_slice( > Source code: noir_stdlib/src/schnorr.nr#L13-L20 - + diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.mdx similarity index 96% rename from docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.md rename to docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.mdx index 7f4dcebf084..60414a2fa51 100644 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.md +++ b/docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.mdx @@ -4,7 +4,7 @@ description: Learn about how to write recursive proofs in Noir. keywords: [recursion, recursive proofs, verification_key, verify_proof] --- -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. @@ -35,7 +35,7 @@ By incorporating this attribute directly in the circuit's definition, tooling li pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` - + ## Example usage diff --git a/noir_stdlib/src/field/mod.nr b/noir_stdlib/src/field/mod.nr index 93245e18072..d5a6193db3b 100644 --- a/noir_stdlib/src/field/mod.nr +++ b/noir_stdlib/src/field/mod.nr @@ -1,5 +1,6 @@ pub mod bn254; use bn254::lt as bn254_lt; +use crate::runtime::is_unconstrained; impl Field { /// Asserts that `self` can be represented in `bit_size` bits. @@ -49,39 +50,71 @@ impl Field { pub fn to_be_bits(self: Self) -> [u1; N] {} // docs:end:to_be_bits - /// Decomposes `self` into its little endian byte decomposition as a `[u8]` slice of length `byte_size`. - /// This slice will be zero padded should not all bytes be necessary to represent `self`. + /// Decomposes `self` into its little endian byte decomposition as a `[u8;N]` array + /// This array will be zero padded should not all bytes be necessary to represent `self`. /// /// # Failures - /// Causes a constraint failure for `Field` values exceeding `2^{8*byte_size}` as the resulting slice will not - /// be able to represent the original `Field`. + /// The length N of the array must be big enough to contain all the bytes of the 'self', + /// and no more than the number of bytes required to represent the field modulus /// /// # Safety - /// Values of `byte_size` equal to or greater than the number of bytes necessary to represent the `Field` modulus - /// (e.g. 32 for the BN254 field) allow for multiple byte decompositions. This is due to how the `Field` will - /// wrap around due to overflow when verifying the decomposition. + /// The result is ensured to be the canonical decomposition of the field element // docs:start:to_le_bytes pub fn to_le_bytes(self: Self) -> [u8; N] { - self.to_le_radix(256) + // docs:end:to_le_bytes + // Compute the byte decomposition + let bytes = self.to_le_radix(256); + + if !is_unconstrained() { + // Ensure that the byte decomposition does not overflow the modulus + let p = modulus_le_bytes(); + assert(bytes.len() <= p.len()); + let mut ok = bytes.len() != p.len(); + for i in 0..N { + if !ok { + if (bytes[N - 1 - i] != p[N - 1 - i]) { + assert(bytes[N - 1 - i] < p[N - 1 - i]); + ok = true; + } + } + } + assert(ok); + } + bytes } - // docs:end:to_le_bytes - /// Decomposes `self` into its big endian byte decomposition as a `[u8]` slice of length `byte_size`. - /// This slice will be zero padded should not all bytes be necessary to represent `self`. + /// Decomposes `self` into its big endian byte decomposition as a `[u8;N]` array of length required to represent the field modulus + /// This array will be zero padded should not all bytes be necessary to represent `self`. /// /// # Failures - /// Causes a constraint failure for `Field` values exceeding `2^{8*byte_size}` as the resulting slice will not - /// be able to represent the original `Field`. + /// The length N of the array must be big enough to contain all the bytes of the 'self', + /// and no more than the number of bytes required to represent the field modulus /// /// # Safety - /// Values of `byte_size` equal to or greater than the number of bytes necessary to represent the `Field` modulus - /// (e.g. 32 for the BN254 field) allow for multiple byte decompositions. This is due to how the `Field` will - /// wrap around due to overflow when verifying the decomposition. + /// The result is ensured to be the canonical decomposition of the field element // docs:start:to_be_bytes pub fn to_be_bytes(self: Self) -> [u8; N] { - self.to_be_radix(256) + // docs:end:to_be_bytes + // Compute the byte decomposition + let bytes = self.to_be_radix(256); + + if !is_unconstrained() { + // Ensure that the byte decomposition does not overflow the modulus + let p = modulus_be_bytes(); + assert(bytes.len() <= p.len()); + let mut ok = bytes.len() != p.len(); + for i in 0..N { + if !ok { + if (bytes[i] != p[i]) { + assert(bytes[i] < p[i]); + ok = true; + } + } + } + assert(ok); + } + bytes } - // docs:end:to_be_bytes // docs:start:to_le_radix pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { @@ -130,6 +163,32 @@ impl Field { lt_fallback(self, another) } } + + /// Convert a little endian byte array to a field element. + /// If the provided byte array overflows the field modulus then the Field will silently wrap around. + pub fn from_le_bytes(bytes: [u8; N]) -> Field { + let mut v = 1; + let mut result = 0; + + for i in 0..N { + result += (bytes[i] as Field) * v; + v = v * 256; + } + result + } + + /// Convert a big endian byte array to a field element. + /// If the provided byte array overflows the field modulus then the Field will silently wrap around. + pub fn from_be_bytes(bytes: [u8; N]) -> Field { + let mut v = 1; + let mut result = 0; + + for i in 0..N { + result += (bytes[N-1-i] as Field) * v; + v = v * 256; + } + result + } } #[builtin(modulus_num_bits)] @@ -207,6 +266,7 @@ mod tests { let field = 2; let bits: [u8; 8] = field.to_be_bytes(); assert_eq(bits, [0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq(Field::from_be_bytes::<8>(bits), field); } // docs:end:to_be_bytes_example @@ -216,6 +276,7 @@ mod tests { let field = 2; let bits: [u8; 8] = field.to_le_bytes(); assert_eq(bits, [2, 0, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bits), field); } // docs:end:to_le_bytes_example @@ -225,6 +286,7 @@ mod tests { let field = 2; let bits: [u8; 8] = field.to_be_radix(256); assert_eq(bits, [0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq(Field::from_be_bytes::<8>(bits), field); } // docs:end:to_be_radix_example @@ -234,6 +296,7 @@ mod tests { let field = 2; let bits: [u8; 8] = field.to_le_radix(256); assert_eq(bits, [2, 0, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bits), field); } // docs:end:to_le_radix_example } diff --git a/noir_stdlib/src/hash/sha256.nr b/noir_stdlib/src/hash/sha256.nr index 413c26d6f6b..081f7deb0fa 100644 --- a/noir_stdlib/src/hash/sha256.nr +++ b/noir_stdlib/src/hash/sha256.nr @@ -110,7 +110,7 @@ pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { // If the block is filled, compress it. // An un-filled block is handled after this loop. - if msg_byte_ptr == BLOCK_SIZE { + if (msg_start < message_size) & (msg_byte_ptr == BLOCK_SIZE) { h = sha256_compression(msg_u8_to_u32(msg_block), h); } } @@ -335,4 +335,38 @@ mod tests { ]; assert_eq(sha256_var(input, input.len() as u64), result); } + + #[test] + fn same_msg_len_variable_padding() { + let input = [ + 29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, 119, 30, 63, 129, 143, 32, 96 + ]; + + // Prepare inputs of different lengths + let mut input_511 = [0; 511]; + let mut input_512 = [0; 512]; // Next block + let mut input_575 = [0; 575]; + let mut input_576 = [0; 576]; // Next block + for i in 0..input.len() { + input_511[i] = input[i]; + input_512[i] = input[i]; + input_575[i] = input[i]; + input_576[i] = input[i]; + } + + // Compute hashes of all inputs (with same message length) + let fixed_length_hash = super::sha256(input); + let var_full_length_hash = sha256_var(input, input.len() as u64); + let var_length_hash_511 = sha256_var(input_511, input.len() as u64); + let var_length_hash_512 = sha256_var(input_512, input.len() as u64); + let var_length_hash_575 = sha256_var(input_575, input.len() as u64); + let var_length_hash_576 = sha256_var(input_576, input.len() as u64); + + // All of the above should have produced the same hash + assert_eq(var_full_length_hash, fixed_length_hash); + assert_eq(var_length_hash_511, fixed_length_hash); + assert_eq(var_length_hash_512, fixed_length_hash); + assert_eq(var_length_hash_575, fixed_length_hash); + assert_eq(var_length_hash_576, fixed_length_hash); + } } diff --git a/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml b/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml new file mode 100644 index 00000000000..a80677c585d --- /dev/null +++ b/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sha256_var_padding_regression" +type = "bin" +authors = [""] +compiler_version = ">=0.34.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/sha256_var_padding_regression/Prover.toml b/test_programs/execution_success/sha256_var_padding_regression/Prover.toml new file mode 100644 index 00000000000..7b20e870128 --- /dev/null +++ b/test_programs/execution_success/sha256_var_padding_regression/Prover.toml @@ -0,0 +1,2 @@ +preimage = [29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, 119, 30, 63, 129, 143, 32, 96] +result = [205, 74, 73, 134, 202, 93, 199, 152, 171, 244, 133, 193, 132, 40, 42, 9, 248, 11, 99, 200, 135, 58, 220, 227, 45, 253, 183, 241, 69, 69, 80, 219] \ No newline at end of file diff --git a/test_programs/execution_success/sha256_var_padding_regression/src/main.nr b/test_programs/execution_success/sha256_var_padding_regression/src/main.nr new file mode 100644 index 00000000000..13f87a0efc5 --- /dev/null +++ b/test_programs/execution_success/sha256_var_padding_regression/src/main.nr @@ -0,0 +1,29 @@ +// Test to check sha256_var produces same results irrespective of number of padding bytes after message.length +// Ref: https://github.com/noir-lang/noir/issues/6163, https://gist.github.com/jp4g/d5953faae9eadb2909357474f7901e58 +fn main(preimage: [u8; 448], result: [u8; 32]) { + // Construct arrays of different lengths + let mut preimage_511 = [0; 511]; + let mut preimage_512 = [0; 512]; // Next block + let mut preimage_575 = [0; 575]; + let mut preimage_576 = [0; 576]; // Next block + for i in 0..preimage.len() { + preimage_511[i] = preimage[i]; + preimage_512[i] = preimage[i]; + preimage_575[i] = preimage[i]; + preimage_576[i] = preimage[i]; + } + let fixed_length_hash = std::hash::sha256::digest(preimage); + let var_full_length_hash = std::hash::sha256::sha256_var(preimage, preimage.len() as u64); + let var_length_hash_511 = std::hash::sha256::sha256_var(preimage_511, preimage.len() as u64); + let var_length_hash_512 = std::hash::sha256::sha256_var(preimage_512, preimage.len() as u64); + let var_length_hash_575 = std::hash::sha256::sha256_var(preimage_575, preimage.len() as u64); + let var_length_hash_576 = std::hash::sha256::sha256_var(preimage_576, preimage.len() as u64); + + // All of the above should have produced the same hash (result) + assert(fixed_length_hash == result); + assert(var_full_length_hash == result); + assert(var_length_hash_511 == result); + assert(var_length_hash_512 == result); + assert(var_length_hash_575 == result); + assert(var_length_hash_576 == result); +} diff --git a/test_programs/execution_success/to_be_bytes/src/main.nr b/test_programs/execution_success/to_be_bytes/src/main.nr index 809d8ad4563..062f9f763d5 100644 --- a/test_programs/execution_success/to_be_bytes/src/main.nr +++ b/test_programs/execution_success/to_be_bytes/src/main.nr @@ -8,5 +8,6 @@ fn main(x: Field) -> pub [u8; 31] { if (bytes[30] != 60) | (bytes[29] != 33) | (bytes[28] != 31) { assert(false); } + assert(Field::from_be_bytes::<31>(bytes) == x); bytes } diff --git a/test_programs/execution_success/to_le_bytes/src/main.nr b/test_programs/execution_success/to_le_bytes/src/main.nr index 4e232b025aa..867551b6dbd 100644 --- a/test_programs/execution_success/to_le_bytes/src/main.nr +++ b/test_programs/execution_success/to_le_bytes/src/main.nr @@ -2,17 +2,12 @@ fn main(x: Field, cond: bool) -> pub [u8; 31] { // The result of this byte array will be little-endian let byte_array: [u8; 31] = x.to_le_bytes(); assert(byte_array.len() == 31); - - let mut bytes = [0; 31]; - for i in 0..31 { - bytes[i] = byte_array[i]; - } - + assert(Field::from_le_bytes::<31>(byte_array) == x); if cond { // We've set x = "2040124" so we shouldn't be able to represent this as a single byte. let bad_byte_array: [u8; 1] = x.to_le_bytes(); assert_eq(bad_byte_array.len(), 1); } - bytes + byte_array } diff --git a/tooling/lsp/src/modules.rs b/tooling/lsp/src/modules.rs index f1eff3b5a7d..9f9a826d6ca 100644 --- a/tooling/lsp/src/modules.rs +++ b/tooling/lsp/src/modules.rs @@ -3,9 +3,8 @@ use std::collections::BTreeMap; use noirc_frontend::{ ast::ItemVisibility, graph::{CrateId, Dependency}, - hir::def_map::{CrateDefMap, ModuleId}, - macros_api::{ModuleDefId, NodeInterner}, - node_interner::ReferenceId, + hir::def_map::{CrateDefMap, ModuleDefId, ModuleId}, + node_interner::{NodeInterner, ReferenceId}, }; use crate::visibility::is_visible; diff --git a/tooling/lsp/src/requests/code_action.rs b/tooling/lsp/src/requests/code_action.rs index 64eccab8947..9299dc76368 100644 --- a/tooling/lsp/src/requests/code_action.rs +++ b/tooling/lsp/src/requests/code_action.rs @@ -15,7 +15,7 @@ use noirc_frontend::{ ast::{ConstructorExpression, ItemVisibility, NoirTraitImpl, Path, UseTree, Visitor}, graph::CrateId, hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}, - macros_api::NodeInterner, + node_interner::NodeInterner, }; use noirc_frontend::{ parser::{Item, ItemKind, ParsedSubModule}, diff --git a/tooling/lsp/src/requests/code_action/import_or_qualify.rs b/tooling/lsp/src/requests/code_action/import_or_qualify.rs index 0d97ccde2ed..769f801a6f1 100644 --- a/tooling/lsp/src/requests/code_action/import_or_qualify.rs +++ b/tooling/lsp/src/requests/code_action/import_or_qualify.rs @@ -2,7 +2,7 @@ use lsp_types::{Position, Range, TextEdit}; use noirc_errors::Location; use noirc_frontend::{ ast::{Ident, Path}, - macros_api::ModuleDefId, + hir::def_map::ModuleDefId, }; use crate::{ diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index 4565569e67b..42f2af3a7bf 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -89,8 +89,8 @@ fn on_code_lens_request_inner( } pub(crate) fn collect_lenses_for_package( - context: &noirc_frontend::macros_api::HirContext, - crate_id: noirc_frontend::macros_api::CrateId, + context: &noirc_frontend::hir::Context, + crate_id: noirc_frontend::graph::CrateId, workspace: &Workspace, package: &Package, file_path: Option<&std::path::PathBuf>, diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 588f5b18f1b..2882cb143bf 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -24,9 +24,9 @@ use noirc_frontend::{ UseTreeKind, Visitor, }, graph::{CrateId, Dependency}, - hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}, + hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}, hir_def::traits::Trait, - macros_api::{ModuleDefId, NodeInterner}, + node_interner::NodeInterner, node_interner::ReferenceId, parser::{Item, ItemKind, ParsedSubModule}, token::{CustomAttribute, Token, Tokens}, @@ -581,8 +581,8 @@ impl<'a> NodeFinder<'a> { Type::Tuple(types) => { self.complete_tuple_fields(types, self_prefix); } - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { + if let TypeBinding::Bound(ref typ) = &*var.borrow() { return self.complete_type_fields_and_methods( typ, prefix, @@ -1662,8 +1662,8 @@ fn get_field_type(typ: &Type, name: &str) -> Option { } } Type::Alias(alias_type, generics) => Some(alias_type.borrow().get_type(generics)), - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { + if let TypeBinding::Bound(ref typ) = &*var.borrow() { get_field_type(typ, name) } else { None @@ -1680,7 +1680,7 @@ fn get_array_element_type(typ: Type) -> Option { let typ = alias_type.borrow().get_type(&generics); get_array_element_type(typ) } - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => { + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { get_array_element_type(typ.clone()) } else { diff --git a/tooling/lsp/src/requests/completion/auto_import.rs b/tooling/lsp/src/requests/completion/auto_import.rs index 20b126a248d..e2dd582f2f3 100644 --- a/tooling/lsp/src/requests/completion/auto_import.rs +++ b/tooling/lsp/src/requests/completion/auto_import.rs @@ -1,5 +1,5 @@ use lsp_types::{Position, Range, TextEdit}; -use noirc_frontend::macros_api::ModuleDefId; +use noirc_frontend::hir::def_map::ModuleDefId; use crate::modules::{relative_module_full_path, relative_module_id_path}; diff --git a/tooling/lsp/src/requests/completion/completion_items.rs b/tooling/lsp/src/requests/completion/completion_items.rs index 809988c34a5..f281f5e3abf 100644 --- a/tooling/lsp/src/requests/completion/completion_items.rs +++ b/tooling/lsp/src/requests/completion/completion_items.rs @@ -4,10 +4,9 @@ use lsp_types::{ }; use noirc_frontend::{ ast::AttributeTarget, - hir::def_map::ModuleId, + hir::def_map::{ModuleDefId, ModuleId}, hir_def::{function::FuncMeta, stmt::HirPattern}, - macros_api::{ModuleDefId, StructId}, - node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, + node_interner::{FuncId, GlobalId, ReferenceId, StructId, TraitId, TypeAliasId}, QuotedType, Type, }; diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 2628c9b2ab6..7b1fa7352a6 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -7,11 +7,15 @@ use noirc_frontend::{ ast::Visibility, elaborator::types::try_eval_array_length_id, hir::def_map::ModuleId, - hir_def::{expr::HirArrayLiteral, stmt::HirPattern, traits::Trait}, - macros_api::{HirExpression, HirLiteral, NodeInterner, StructId}, + hir_def::{ + expr::{HirArrayLiteral, HirExpression, HirLiteral}, + stmt::HirPattern, + traits::Trait, + }, node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId, }, + node_interner::{NodeInterner, StructId}, Generics, Shared, StructType, Type, TypeAlias, TypeBinding, TypeVariable, }; @@ -526,7 +530,7 @@ impl<'a> TypeLinksGatherer<'a> { self.gather_type_links(generic); } } - Type::TypeVariable(var, _) => { + Type::TypeVariable(var) => { self.gather_type_variable_links(var); } Type::TraitAsType(trait_id, _, generics) => { @@ -539,7 +543,7 @@ impl<'a> TypeLinksGatherer<'a> { self.gather_type_links(&named_type.typ); } } - Type::NamedGeneric(var, _, _) => { + Type::NamedGeneric(var, _) => { self.gather_type_variable_links(var); } Type::Function(args, return_type, env, _) => { diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index 2eef4f6e262..e119ee0d5b6 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -15,10 +15,9 @@ use noirc_frontend::{ UnresolvedTypeData, Visitor, }, hir_def::stmt::HirPattern, - macros_api::NodeInterner, - node_interner::ReferenceId, + node_interner::{NodeInterner, ReferenceId}, parser::{Item, ParsedSubModule}, - Type, TypeBinding, TypeVariable, TypeVariableKind, + Kind, Type, TypeBinding, TypeVariable, }; use crate::{utils, LspState}; @@ -460,19 +459,14 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File parts.push(string_part("&mut ")); push_type_parts(typ, parts, files); } - Type::TypeVariable(var, TypeVariableKind::Normal) => { - push_type_variable_parts(var, parts, files); - } - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - push_type_parts(&Type::default_int_type(), parts, files); - } else { - push_type_variable_parts(binding, parts, files); - } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - parts.push(string_part("Field")); + Type::TypeVariable(binding) => { + if let TypeBinding::Unbound(_, kind) = &*binding.borrow() { + match kind { + Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), + Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), + Kind::IntegerOrField => parts.push(string_part("Field")), + Kind::Numeric(ref typ) => push_type_parts(typ, parts, files), + } } else { push_type_variable_parts(binding, parts, files); } diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index 576d026081d..597d8355468 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -18,7 +18,7 @@ use nargo_fmt::Config; use noirc_frontend::graph::CrateId; use noirc_frontend::hir::def_map::CrateDefMap; -use noirc_frontend::{graph::Dependency, macros_api::NodeInterner}; +use noirc_frontend::{graph::Dependency, node_interner::NodeInterner}; use serde::{Deserialize, Serialize}; use crate::{ diff --git a/tooling/lsp/src/requests/signature_help.rs b/tooling/lsp/src/requests/signature_help.rs index b075fea1d1e..c0d40656c19 100644 --- a/tooling/lsp/src/requests/signature_help.rs +++ b/tooling/lsp/src/requests/signature_help.rs @@ -12,8 +12,7 @@ use noirc_frontend::{ MethodCallExpression, Statement, Visitor, }, hir_def::{function::FuncMeta, stmt::HirPattern}, - macros_api::NodeInterner, - node_interner::ReferenceId, + node_interner::{NodeInterner, ReferenceId}, parser::Item, ParsedModule, Type, }; diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index 56d2e5e1ea1..14b40858bb1 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -3,14 +3,14 @@ use std::collections::BTreeMap; use noirc_frontend::{ ast::NoirTraitImpl, graph::CrateId, + hir::def_map::ModuleDefId, hir::{ def_map::{CrateDefMap, ModuleId}, type_check::generics::TraitGenerics, }, hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait}, - macros_api::{ModuleDefId, NodeInterner}, - node_interner::{FunctionModifiers, ReferenceId}, - Kind, ResolvedGeneric, Type, TypeVariableKind, + node_interner::{FunctionModifiers, NodeInterner, ReferenceId}, + Kind, ResolvedGeneric, Type, }; use crate::modules::relative_module_id_path; @@ -290,7 +290,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { self.string.push_str(&trait_.name.0.contents); self.append_trait_generics(trait_generics); } - Type::TypeVariable(typevar, _) => { + Type::TypeVariable(typevar) => { if typevar.id() == self.trait_.self_type_typevar.id() { self.string.push_str("Self"); return; @@ -323,8 +323,8 @@ impl<'a> TraitImplMethodStubGenerator<'a> { self.string.push_str("error"); } - Type::NamedGeneric(typevar, _name, _kind) => { - self.append_type(&Type::TypeVariable(typevar.clone(), TypeVariableKind::Normal)); + Type::NamedGeneric(typevar, _name) => { + self.append_type(&Type::TypeVariable(typevar.clone())); } Type::Function(args, ret, env, unconstrained) => { if *unconstrained { @@ -437,9 +437,11 @@ impl<'a> TraitImplMethodStubGenerator<'a> { } fn append_resolved_generic(&mut self, generic: &ResolvedGeneric) { - match &generic.kind { - Kind::Normal => self.string.push_str(&generic.name), - Kind::Numeric(typ) => { + match &generic.kind() { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { + self.string.push_str(&generic.name); + } + Kind::Numeric(ref typ) => { self.string.push_str("let "); self.string.push_str(&generic.name); self.string.push_str(": "); diff --git a/tooling/nargo_fmt/Cargo.toml b/tooling/nargo_fmt/Cargo.toml index 9868f259097..1e4d93b3125 100644 --- a/tooling/nargo_fmt/Cargo.toml +++ b/tooling/nargo_fmt/Cargo.toml @@ -11,6 +11,7 @@ workspace = true [dependencies] bytecount = "0.6.3" +noirc_errors.workspace = true noirc_frontend.workspace = true serde.workspace = true toml.workspace = true diff --git a/tooling/nargo_fmt/src/items.rs b/tooling/nargo_fmt/src/items.rs index 57757982e83..e68be7cdc95 100644 --- a/tooling/nargo_fmt/src/items.rs +++ b/tooling/nargo_fmt/src/items.rs @@ -1,4 +1,4 @@ -use noirc_frontend::macros_api::Span; +use noirc_errors::Span; use crate::{ utils::{comment_len, find_comment_end}, diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 873b5c87056..98b50e92476 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -1,8 +1,9 @@ +use noirc_errors::Span; use noirc_frontend::ast::{ ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, Path, PathKind, UnaryOp, UnresolvedType, }; -use noirc_frontend::{macros_api::Span, token::Token}; +use noirc_frontend::token::Token; use crate::rewrite; use crate::visitor::{ diff --git a/tooling/nargo_fmt/src/visitor/item.rs b/tooling/nargo_fmt/src/visitor/item.rs index 2feae4b390c..428f143141f 100644 --- a/tooling/nargo_fmt/src/visitor/item.rs +++ b/tooling/nargo_fmt/src/visitor/item.rs @@ -6,9 +6,8 @@ use crate::{ }, visitor::expr::{format_seq, NewlineMode}, }; -use noirc_frontend::{ - ast::{ItemVisibility, NoirFunction, TraitImplItemKind, Visibility}, - macros_api::UnresolvedTypeData, +use noirc_frontend::ast::{ + ItemVisibility, ModuleDeclaration, NoirFunction, NoirStruct, TraitImplItemKind, Visibility, }; use noirc_frontend::{ hir::resolution::errors::Span, @@ -277,11 +276,18 @@ impl super::FmtVisitor<'_> { self.push_rewrite(use_tree, span); self.last_position = span.end(); } + ItemKind::ModuleDecl(ModuleDeclaration { visibility, ident, outer_attributes }) => { + if !outer_attributes.is_empty() { + self.push_rewrite(self.slice(span).to_string(), span); + self.last_position = span.end(); + continue; + } + } + ItemKind::Struct(_) | ItemKind::Trait(_) | ItemKind::TypeAlias(_) | ItemKind::Global(..) - | ItemKind::ModuleDecl(_) | ItemKind::InnerAttribute(_) => { self.push_rewrite(self.slice(span).to_string(), span); self.last_position = span.end(); diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 8908aabd87c..7298be641d9 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -1,6 +1,6 @@ use std::iter::zip; -use noirc_frontend::macros_api::Span; +use noirc_errors::Span; use noirc_frontend::ast::{ConstrainKind, ConstrainStatement, ForRange, Statement, StatementKind};