diff --git a/crates/fm/src/lib.rs b/crates/fm/src/lib.rs index b79a2b2c700..63a53856660 100644 --- a/crates/fm/src/lib.rs +++ b/crates/fm/src/lib.rs @@ -70,7 +70,7 @@ impl FileManager { // Unwrap as we ensure that all file_id's map to a corresponding file in the file map self.file_map.get_file(file_id).unwrap() } - fn path(&mut self, file_id: FileId) -> &Path { + pub fn path(&mut self, file_id: FileId) -> &Path { // Unwrap as we ensure that all file_ids are created by the file manager // So all file_ids will points to a corresponding path self.id_to_path.get(&file_id).unwrap().0.as_path() diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index f4b797a11c5..be60006b3a0 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -139,7 +139,7 @@ fn on_code_lens_request( // We ignore the warnings and errors produced by compilation for producing codelenses // because we can still get the test functions even if compilation fails - let _ = check_crate(&mut state.context, false); + let _ = check_crate(&mut state.context, false, false); let fm = &state.context.file_manager; let files = fm.as_simple_files(); @@ -224,7 +224,7 @@ fn on_did_save_text_document( let mut diagnostics = Vec::new(); - let file_diagnostics = match check_crate(&mut state.context, false) { + let file_diagnostics = match check_crate(&mut state.context, false, false) { Ok(warnings) => warnings, Err(errors_and_warnings) => errors_and_warnings, }; diff --git a/crates/nargo_cli/src/cli/check_cmd.rs b/crates/nargo_cli/src/cli/check_cmd.rs index 89f4cc5764f..02ac3c024c0 100644 --- a/crates/nargo_cli/src/cli/check_cmd.rs +++ b/crates/nargo_cli/src/cli/check_cmd.rs @@ -37,7 +37,7 @@ fn check_from_path( compile_options: &CompileOptions, ) -> Result<(), CliError> { let mut context = resolve_root_manifest(program_dir)?; - check_crate_and_report_errors(&mut context, compile_options.deny_warnings)?; + check_crate_and_report_errors(&mut context, compile_options.deny_warnings, compile_options.experimental_ssa)?; // XXX: We can have a --overwrite flag to determine if you want to overwrite the Prover/Verifier.toml files if let Some((parameters, return_type)) = compute_function_signature(&context) { @@ -214,7 +214,8 @@ d2 = ["", "", ""] pub(crate) fn check_crate_and_report_errors( context: &mut Context, deny_warnings: bool, + enable_slices: bool, ) -> Result<(), ReportedErrors> { - let result = check_crate(context, deny_warnings).map(|warnings| ((), warnings)); + let result = check_crate(context, deny_warnings, enable_slices).map(|warnings| ((), warnings)); super::compile_cmd::report_errors(result, context, deny_warnings) } diff --git a/crates/nargo_cli/src/cli/mod.rs b/crates/nargo_cli/src/cli/mod.rs index 4f2d0531c0a..50b88ce3bd2 100644 --- a/crates/nargo_cli/src/cli/mod.rs +++ b/crates/nargo_cli/src/cli/mod.rs @@ -132,7 +132,7 @@ mod tests { let mut context = Context::default(); create_local_crate(&mut context, &root_file, CrateType::Binary); - let result = check_crate(&mut context, false); + let result = check_crate(&mut context, false, false); let success = result.is_ok(); let errors = match result { diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index b845ac2d294..5b51efe1add 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -41,7 +41,7 @@ fn run_tests( compile_options: &CompileOptions, ) -> Result<(), CliError> { let mut context = resolve_root_manifest(program_dir)?; - check_crate_and_report_errors(&mut context, compile_options.deny_warnings)?; + check_crate_and_report_errors(&mut context, compile_options.deny_warnings, compile_options.experimental_ssa)?; let test_functions = context.get_all_test_functions_in_crate_matching(&LOCAL_CRATE, test_name); println!("Running {} test functions...", test_functions.len()); diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/Prover.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/Prover.toml index c61cd3b3a95..2e2ed310ba1 100644 --- a/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/Prover.toml +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/Prover.toml @@ -5,5 +5,4 @@ t = "10" #7128 #15309 -#16349 - +#16349 \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/src/main.nr index 9593c56524f..b9ed6b6f345 100644 --- a/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/src/main.nr +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/6_array/src/main.nr @@ -51,4 +51,3 @@ fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) { } } } - diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/slices/Nargo.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/slices/Nargo.toml new file mode 100644 index 00000000000..670888e37cd --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/slices/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.6.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/slices/Prover.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/slices/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/slices/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr new file mode 100644 index 00000000000..2a7226ed6cf --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr @@ -0,0 +1,23 @@ +use dep::std::slice; + +fn main(x : Field, y : pub Field) { + + let mut slice: [Field] = [0; 2]; + + assert(slice[0] == 0); + assert(slice[0] != 1); + slice[0] = x; + assert(slice[0] == x); + + let slice_plus_10 = slice.push_back(y); + assert(slice_plus_10[2] == 10); + assert(slice_plus_10[2] != 8); + assert(slice_plus_10.len() == 3); + + let mut new_slice: [Field] = []; + for i in 0..5 { + new_slice = new_slice.push_back(i); + } + assert(new_slice.len() == 5); +} + diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 0eb5f9e03b2..92d31824f86 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -157,6 +157,7 @@ pub fn propagate_dep( pub fn check_crate( context: &mut Context, deny_warnings: bool, + enable_slices: bool, ) -> Result { // Add the stdlib before we check the crate // TODO: This should actually be done when constructing the driver and then propagated to each dependency when added; @@ -167,6 +168,8 @@ pub fn check_crate( let std_crate = create_non_local_crate(context, path_to_std_lib_file, CrateType::Library); propagate_dep(context, std_crate, &CrateName::new(std_crate_name).unwrap()); + context.def_interner.enable_slices = enable_slices; + let mut errors = vec![]; CrateDefMap::collect_defs(LOCAL_CRATE, context, &mut errors); @@ -195,7 +198,7 @@ pub fn compile_main( is_opcode_supported: &impl Fn(&Opcode) -> bool, options: &CompileOptions, ) -> Result<(CompiledProgram, Warnings), ErrorsAndWarnings> { - let warnings = check_crate(context, options.deny_warnings)?; + let warnings = check_crate(context, options.deny_warnings, options.experimental_ssa)?; let main = match context.get_main_function(&LOCAL_CRATE) { Some(m) => m, @@ -226,7 +229,7 @@ pub fn compile_contracts( is_opcode_supported: &impl Fn(&Opcode) -> bool, options: &CompileOptions, ) -> Result<(Vec, Warnings), ErrorsAndWarnings> { - let warnings = check_crate(context, options.deny_warnings)?; + let warnings = check_crate(context, options.deny_warnings, options.experimental_ssa)?; let contracts = context.get_all_contracts(&LOCAL_CRATE); let mut compiled_contracts = vec![]; diff --git a/crates/noirc_evaluator/src/ssa/context.rs b/crates/noirc_evaluator/src/ssa/context.rs index ea9a062ec10..0fecb633db6 100644 --- a/crates/noirc_evaluator/src/ssa/context.rs +++ b/crates/noirc_evaluator/src/ssa/context.rs @@ -1212,7 +1212,7 @@ impl SsaContext { Type::Function(..) => ObjectType::Function, Type::Tuple(_) => todo!("Conversion to ObjectType is unimplemented for tuples"), Type::String(_) => todo!("Conversion to ObjectType is unimplemented for strings"), - Type::Vec(_) => todo!("Conversion to ObjectType is unimplemented for Vecs"), + Type::Slice(_) => todo!("Conversion to ObjectType is unimplemented for slices"), } } diff --git a/crates/noirc_evaluator/src/ssa/value.rs b/crates/noirc_evaluator/src/ssa/value.rs index b640369cd56..35e5f9f12de 100644 --- a/crates/noirc_evaluator/src/ssa/value.rs +++ b/crates/noirc_evaluator/src/ssa/value.rs @@ -96,7 +96,7 @@ impl Value { Type::Unit | Type::Function(..) | Type::Array(..) - | Type::Vec(..) + | Type::Slice(..) | Type::String(..) | Type::Integer(..) | Type::Bool diff --git a/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs index 2b49bae9e80..c075af19258 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs @@ -578,6 +578,9 @@ impl Context { (_, Type::Array(..)) | (Type::Array(..), _) => { unreachable!("Arrays are invalid in binary operations") } + (_, Type::Slice(..)) | (Type::Slice(..), _) => { + unreachable!("Arrays are invalid in binary operations") + } // If either side is a Field constant then, we coerce into the type // of the other operand (Type::Numeric(NumericType::NativeField), typ) @@ -732,6 +735,7 @@ impl Context { Self::convert_vars_to_values(out_vars, dfg, result_ids) } + _ => todo!("expected a black box function"), } } @@ -743,6 +747,10 @@ impl Context { assert_eq!(elements.len(), 1); (&elements[0]).into() } + Type::Slice(elements) => { + assert_eq!(elements.len(), 1); + (&elements[0]).into() + } _ => unreachable!("Expected array type"), } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs index 9104b65d16f..30fb42ac60f 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs @@ -238,10 +238,16 @@ impl DataFlowGraph { /// Gets or creates a ValueId for the given Intrinsic. pub(crate) fn import_intrinsic(&mut self, intrinsic: Intrinsic) -> ValueId { - if let Some(existing) = self.intrinsics.get(&intrinsic) { + if let Some(existing) = self.get_intrinsic(intrinsic) { return *existing; } - self.values.insert(Value::Intrinsic(intrinsic)) + let intrinsic_value_id = self.values.insert(Value::Intrinsic(intrinsic)); + self.intrinsics.insert(intrinsic, intrinsic_value_id); + intrinsic_value_id + } + + pub(crate) fn get_intrinsic(&mut self, intrinsic: Intrinsic) -> Option<&ValueId> { + self.intrinsics.get(&intrinsic) } /// Attaches results to the instruction, clearing any previous results. @@ -360,6 +366,20 @@ impl DataFlowGraph { } } + /// Returns the Type::Array associated with this ValueId if it refers to an array parameter. + /// Otherwise, this returns None. + pub(crate) fn get_array_parameter_type( + &self, + value: ValueId, + ) -> Option<(Rc, usize)> { + match &self.values[self.resolve(value)] { + Value::Param { typ: Type::Array(element_type, size), .. } => { + Some((element_type.clone(), *size)) + } + _ => None, + } + } + /// Sets the terminator instruction for the given basic block pub(crate) fn set_block_terminator( &mut self, diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs index 5a7365e6bfa..2d9da32792b 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs @@ -32,6 +32,8 @@ pub(crate) type InstructionId = Id; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum Intrinsic { Sort, + ArrayLen, + SlicePushBack, Println, ToBits(Endian), ToRadix(Endian), @@ -43,6 +45,8 @@ impl std::fmt::Display for Intrinsic { match self { Intrinsic::Println => write!(f, "println"), Intrinsic::Sort => write!(f, "arraysort"), + Intrinsic::ArrayLen => write!(f, "array_len"), + Intrinsic::SlicePushBack => write!(f, "slice_push_back"), Intrinsic::ToBits(Endian::Big) => write!(f, "to_be_bits"), Intrinsic::ToBits(Endian::Little) => write!(f, "to_le_bits"), Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"), @@ -59,6 +63,8 @@ impl Intrinsic { match name { "println" => Some(Intrinsic::Println), "arraysort" => Some(Intrinsic::Sort), + "array_len" => Some(Intrinsic::ArrayLen), + "slice_push_back" => Some(Intrinsic::SlicePushBack), "to_le_radix" => Some(Intrinsic::ToRadix(Endian::Little)), "to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)), "to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)), @@ -276,7 +282,6 @@ impl Instruction { Instruction::ArrayGet { array, index } => { let array = dfg.get_array_constant(*array); let index = dfg.get_numeric_constant(*index); - if let (Some((array, _)), Some(index)) = (array, index) { let index = index.try_to_u64().expect("Expected array index to fit in u64") as usize; @@ -290,7 +295,6 @@ 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 index = index.try_to_u64().expect("Expected array index to fit in u64") as usize; @@ -375,23 +379,55 @@ fn simplify_call(func: ValueId, arguments: &[ValueId], dfg: &mut DataFlowGraph) Value::Intrinsic(intrinsic) => *intrinsic, _ => return None, }; + let constant_args: Option> = arguments.iter().map(|value_id| dfg.get_numeric_constant(*value_id)).collect(); - let constant_args = match constant_args { - Some(constant_args) => constant_args, - Option::None => return None, - }; + match intrinsic { Intrinsic::ToBits(endian) => { - let field = constant_args[0]; - let limb_count = constant_args[1].to_u128() as u32; - SimplifiedTo(constant_to_radix(endian, field, 2, limb_count, dfg)) + if let Some(constant_args) = constant_args { + let field = constant_args[0]; + let limb_count = constant_args[1].to_u128() as u32; + SimplifiedTo(constant_to_radix(endian, field, 2, limb_count, dfg)) + } else { + None + } } Intrinsic::ToRadix(endian) => { - let field = constant_args[0]; - let radix = constant_args[1].to_u128() as u32; - let limb_count = constant_args[2].to_u128() as u32; - SimplifiedTo(constant_to_radix(endian, field, radix, limb_count, dfg)) + if let Some(constant_args) = constant_args { + let field = constant_args[0]; + let radix = constant_args[1].to_u128() as u32; + let limb_count = constant_args[2].to_u128() as u32; + SimplifiedTo(constant_to_radix(endian, field, radix, limb_count, dfg)) + } else { + None + } + } + Intrinsic::ArrayLen => { + let slice = dfg.get_array_constant(arguments[0]); + if let Some((slice, _)) = slice { + let slice_len = + dfg.make_constant(FieldElement::from(slice.len() as u128), Type::field()); + SimplifiedTo(slice_len) + } else if let Some((_, slice_len)) = dfg.get_array_parameter_type(arguments[0]) { + let slice_len = dfg.make_constant( + FieldElement::from(slice_len as u128), + Type::Numeric(NumericType::NativeField), + ); + SimplifiedTo(slice_len) + } else { + None + } + } + Intrinsic::SlicePushBack => { + let slice = dfg.get_array_constant(arguments[0]); + if let (Some((mut slice, element_type)), elem) = (slice, arguments[1]) { + slice.push_back(elem); + let new_slice = dfg.make_array(slice, element_type); + SimplifiedTo(new_slice) + } else { + None + } } Intrinsic::BlackBox(_) | Intrinsic::Println | Intrinsic::Sort => None, } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/types.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/types.rs index a9285531203..9bb90e0b96b 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/types.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/types.rs @@ -29,6 +29,9 @@ pub(crate) enum Type { /// An immutable array value with the given element type and length Array(Rc, usize), + /// An immutable slice value with a given element type + Slice(Rc), + /// A function that may be called directly Function, } @@ -74,6 +77,10 @@ impl std::fmt::Display for Type { let elements = vecmap(element.iter(), |element| element.to_string()); write!(f, "[{}; {length}]", elements.join(", ")) } + Type::Slice(element) => { + let elements = vecmap(element.iter(), |element| element.to_string()); + write!(f, "[{}]", elements.join(", ")) + } Type::Function => write!(f, "function"), } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs index 9c8b3c6ee91..f19aade8aa8 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs @@ -388,6 +388,7 @@ impl<'f> Context<'f> { then_value, else_value, ), + Type::Slice(_) => panic!("Cannot return slices from an if expression"), Type::Reference => panic!("Cannot return references from an if expression"), Type::Function => panic!("Cannot return functions from an if expression"), } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs index 2f13733a2dc..9ce34cb1e6b 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs @@ -206,16 +206,15 @@ impl<'a> FunctionContext<'a> { ast::Type::Unit => panic!("convert_non_tuple_type called on a unit type"), ast::Type::Tuple(_) => panic!("convert_non_tuple_type called on a tuple: {typ}"), ast::Type::Function(_, _) => Type::Function, + ast::Type::Slice(element) => { + let element_types = Self::convert_type(element).flatten(); + Type::Slice(Rc::new(element_types)) + } ast::Type::MutableReference(element) => { // Recursive call to panic if element is a tuple Self::convert_non_tuple_type(element); Type::Reference } - - // How should we represent Vecs? - // Are they a struct of array + length + capacity? - // Or are they just references? - ast::Type::Vec(_) => Type::Reference, } } diff --git a/crates/noirc_frontend/src/ast/mod.rs b/crates/noirc_frontend/src/ast/mod.rs index 2f11ecc1564..339dc346e64 100644 --- a/crates/noirc_frontend/src/ast/mod.rs +++ b/crates/noirc_frontend/src/ast/mod.rs @@ -39,14 +39,6 @@ pub enum UnresolvedType { /// A Named UnresolvedType can be a struct type or a type variable Named(Path, Vec), - /// A vector of some element type. - /// It is expected the length of the generics is 1 so the inner Vec is technically unnecessary, - /// but we keep them all around to verify generic count after parsing for better error messages. - /// - /// The Span here encompasses the entire type and is used to issue an error if exactly 1 - /// generic argument is not given. - Vec(Vec, Span), - /// &mut T MutableReference(Box), @@ -115,10 +107,6 @@ impl std::fmt::Display for UnresolvedType { let args = vecmap(args, ToString::to_string); write!(f, "fn({}) -> {ret}", args.join(", ")) } - Vec(args, _span) => { - let args = vecmap(args, ToString::to_string); - write!(f, "Vec<{}>", args.join(", ")) - } MutableReference(element) => write!(f, "&mut {element}"), Unit => write!(f, "()"), Error => write!(f, "error"), diff --git a/crates/noirc_frontend/src/graph/mod.rs b/crates/noirc_frontend/src/graph/mod.rs index 0e4a4c63518..e4962065471 100644 --- a/crates/noirc_frontend/src/graph/mod.rs +++ b/crates/noirc_frontend/src/graph/mod.rs @@ -11,6 +11,7 @@ use smol_str::SmolStr; /// The local crate is the crate being compiled. /// The caller should ensure that this crate has a CrateId(0). pub const LOCAL_CRATE: CrateId = CrateId(0); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CrateId(usize); @@ -47,6 +48,12 @@ pub struct CrateGraph { arena: FxHashMap, } +impl CrateGraph { + pub fn is_last_crate(&self, crate_id: CrateId) -> bool { + (self.arena.len() - 1) == crate_id.0 + } +} + /// List of characters that are not allowed in a crate name /// For example, Hyphen(-) is disallowed as it is similar to underscore(_) /// and we do not want names that differ by a hyphen diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index a05872027e5..9d17a8b4ba4 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -77,7 +77,24 @@ impl CrateDefMap { // First parse the root file. let root_file_id = context.crate_graph[crate_id].root_file_id; - let ast = parse_file(&mut context.file_manager, root_file_id, errors); + let mut ast = parse_file(&mut context.file_manager, root_file_id, errors); + + // TODO(#1850): This check should be removed once we fully move over to the new SSA pass + // Compiling with the old SSA pass will lead to duplicate method definitions between + // the `slice` and `array` modules of the stdlib. + // + // The last crate represents the stdlib crate. + // After resolving the manifest of the local crate the stdlib is added to the manifest and propagated to all crates + // thus being the last crate. + if !context.def_interner.enable_slices && context.crate_graph.is_last_crate(crate_id) { + let path_as_str = context + .file_manager + .path(root_file_id) + .to_str() + .expect("expected std path to be convertible to str"); + assert_eq!(path_as_str, "std"); + ast.module_decls.retain(|ident| ident.0.contents != "slice"); + } // Allocate a default Module for the root, giving it a ModuleId let mut modules: Arena = Arena::default(); diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 72b03689ec2..47a40d8c14d 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -325,8 +325,11 @@ impl<'a> Resolver<'a> { match typ { UnresolvedType::FieldElement(comp_time) => Type::FieldElement(comp_time), UnresolvedType::Array(size, elem) => { - let resolved_size = self.resolve_array_size(size, new_variables); let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); + if self.interner.enable_slices && size.is_none() { + return Type::Slice(elem); + } + let resolved_size = self.resolve_array_size(size, new_variables); Type::Array(Box::new(resolved_size), elem) } UnresolvedType::Expression(expr) => self.convert_expression_type(expr), @@ -348,20 +351,6 @@ impl<'a> Resolver<'a> { let ret = Box::new(self.resolve_type_inner(*ret, new_variables)); Type::Function(args, ret) } - UnresolvedType::Vec(mut args, span) => { - let arg = if args.len() != 1 { - self.push_err(ResolverError::IncorrectGenericCount { - span, - struct_type: "Vec".into(), - actual: args.len(), - expected: 1, - }); - Type::Error - } else { - self.resolve_type_inner(args.remove(0), new_variables) - }; - Type::Vec(Box::new(arg)) - } UnresolvedType::MutableReference(element) => { Type::MutableReference(Box::new(self.resolve_type_inner(*element, new_variables))) } @@ -762,6 +751,10 @@ impl<'a> Resolver<'a> { } } + Type::Slice(typ) => { + Self::find_numeric_generics_in_type(typ, found); + } + Type::Tuple(fields) => { for field in fields { Self::find_numeric_generics_in_type(field, found); @@ -784,7 +777,6 @@ impl<'a> Resolver<'a> { } } } - Type::Vec(element) => Self::find_numeric_generics_in_type(element, found), Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), } } diff --git a/crates/noirc_frontend/src/hir/type_check/expr.rs b/crates/noirc_frontend/src/hir/type_check/expr.rs index b310215a2f4..8afb4c0640e 100644 --- a/crates/noirc_frontend/src/hir/type_check/expr.rs +++ b/crates/noirc_frontend/src/hir/type_check/expr.rs @@ -42,7 +42,10 @@ impl<'interner> TypeChecker<'interner> { HirLiteral::Array(HirArrayLiteral::Standard(arr)) => { let elem_types = vecmap(&arr, |arg| self.check_expression(arg)); - let first_elem_type = elem_types.get(0).cloned().unwrap_or(Type::Error); + let first_elem_type = elem_types + .get(0) + .cloned() + .unwrap_or_else(|| self.interner.next_type_variable()); let arr_type = Type::Array( Box::new(Type::Constant(arr.len() as u64)), @@ -320,6 +323,7 @@ impl<'interner> TypeChecker<'interner> { // XXX: We can check the array bounds here also, but it may be better to constant fold first // and have ConstId instead of ExprId for constants Type::Array(_, base_type) => *base_type, + Type::Slice(base_type) => *base_type, Type::Error => Type::Error, typ => { let span = self.interner.expr_span(&index_expr.collection); diff --git a/crates/noirc_frontend/src/hir/type_check/stmt.rs b/crates/noirc_frontend/src/hir/type_check/stmt.rs index a6d7442d98a..52b6fc7dd2e 100644 --- a/crates/noirc_frontend/src/hir/type_check/stmt.rs +++ b/crates/noirc_frontend/src/hir/type_check/stmt.rs @@ -193,6 +193,7 @@ impl<'interner> TypeChecker<'interner> { let typ = match result { Type::Array(_, elem_type) => *elem_type, + Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, other => { // TODO: Need a better span here diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index adc239976c5..9441307bf28 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -21,6 +21,9 @@ pub enum Type { /// is either a type variable of some kind or a Type::Constant. Array(Box, Box), + /// Slice(E) is a slice with elements of type E. + Slice(Box), + /// A primitive integer type with the given sign, bit count, and whether it is known at compile-time. /// E.g. `u32` would be `Integer(CompTime::No(None), Unsigned, 32)` Integer(CompTime, Signedness, u32), @@ -71,11 +74,6 @@ pub enum Type { /// A functions with arguments, and a return type. Function(Vec, Box), - /// A variable-sized Vector type. - /// Unlike arrays, this type can have a dynamic size and can grow/shrink dynamically via .push, - /// .pop, and similar methods. - Vec(Box), - /// &mut T MutableReference(Box), @@ -584,6 +582,8 @@ impl Type { elem.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) } + Type::Slice(elem) => elem.contains_numeric_typevar(target_id), + Type::Tuple(fields) => { fields.iter().any(|field| field.contains_numeric_typevar(target_id)) } @@ -600,7 +600,6 @@ impl Type { } }) } - Type::Vec(element) => element.contains_numeric_typevar(target_id), Type::MutableReference(element) => element.contains_numeric_typevar(target_id), } } @@ -623,6 +622,7 @@ impl std::fmt::Display for Type { write!(f, "{comp_time}Field") } Type::Array(len, typ) => write!(f, "[{typ}; {len}]"), + Type::Slice(typ) => write!(f, "[{typ}]"), Type::Integer(comp_time, sign, num_bits) => match sign { Signedness::Signed => write!(f, "{comp_time}i{num_bits}"), Signedness::Unsigned => write!(f, "{comp_time}u{num_bits}"), @@ -668,9 +668,6 @@ impl std::fmt::Display for Type { let args = vecmap(args, ToString::to_string); write!(f, "fn({}) -> {}", args.join(", "), ret) } - Type::Vec(element) => { - write!(f, "Vec<{element}>") - } Type::MutableReference(element) => { write!(f, "&mut {element}") } @@ -941,6 +938,8 @@ impl Type { elem_a.try_unify(elem_b, span) } + (Slice(elem_a), Slice(elem_b)) => elem_a.try_unify(elem_b, span), + (Tuple(elements_a), Tuple(elements_b)) => { if elements_a.len() != elements_b.len() { Err(SpanKind::None) @@ -1004,8 +1003,6 @@ impl Type { } } - (Vec(elem_a), Vec(elem_b)) => elem_a.try_unify(elem_b, span), - (MutableReference(elem_a), MutableReference(elem_b)) => elem_a.try_unify(elem_b, span), (other_a, other_b) => { @@ -1076,6 +1073,10 @@ impl Type { elem_a.is_subtype_of(elem_b, span) } + (Slice(elem_a), Slice(elem_b)) => elem_a.is_subtype_of(elem_b, span), + + (Array(_, elem_a), Slice(elem_b)) => elem_a.is_subtype_of(elem_b, span), + (Tuple(elements_a), Tuple(elements_b)) => { if elements_a.len() != elements_b.len() { Err(SpanKind::None) @@ -1139,8 +1140,6 @@ impl Type { } } - (Vec(elem_a), Vec(elem_b)) => elem_a.is_subtype_of(elem_b, span), - // `T <: U => &mut T <: &mut U` would be unsound(*), so mutable // references are never subtypes of each other. // @@ -1227,8 +1226,8 @@ impl Type { Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), Type::Function(_, _) => unreachable!(), + Type::Slice(_) => unreachable!("slices cannot be used in the abi"), Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"), - Type::Vec(_) => unreachable!("Vecs cannot be used in the abi"), } } @@ -1310,6 +1309,10 @@ impl Type { let element = Box::new(element.substitute(type_bindings)); Type::Array(size, element) } + Type::Slice(element) => { + let element = Box::new(element.substitute(type_bindings)); + Type::Slice(element) + } Type::String(size) => { let size = Box::new(size.substitute(type_bindings)); Type::String(size) @@ -1342,7 +1345,6 @@ impl Type { let ret = Box::new(ret.substitute(type_bindings)); Type::Function(args, ret) } - Type::Vec(element) => Type::Vec(Box::new(element.substitute(type_bindings))), Type::MutableReference(element) => { Type::MutableReference(Box::new(element.substitute(type_bindings))) } @@ -1360,6 +1362,7 @@ impl Type { fn occurs(&self, target_id: TypeVariableId) -> bool { match self { Type::Array(len, elem) => len.occurs(target_id) || elem.occurs(target_id), + Type::Slice(element) => element.occurs(target_id), Type::String(len) => len.occurs(target_id), Type::Struct(_, generic_args) => generic_args.iter().any(|arg| arg.occurs(target_id)), Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), @@ -1375,7 +1378,6 @@ impl Type { Type::Function(args, ret) => { args.iter().any(|arg| arg.occurs(target_id)) || ret.occurs(target_id) } - Type::Vec(element) => element.occurs(target_id), Type::MutableReference(element) => element.occurs(target_id), Type::FieldElement(_) @@ -1399,6 +1401,7 @@ impl Type { Array(size, elem) => { Array(Box::new(size.follow_bindings()), Box::new(elem.follow_bindings())) } + Slice(elem) => Slice(Box::new(elem.follow_bindings())), String(size) => String(Box::new(size.follow_bindings())), Struct(def, args) => { let args = vecmap(args, |arg| arg.follow_bindings()); @@ -1418,7 +1421,6 @@ impl Type { let ret = Box::new(ret.follow_bindings()); Function(args, ret) } - Vec(element) => Vec(Box::new(element.follow_bindings())), MutableReference(element) => MutableReference(Box::new(element.follow_bindings())), // Expect that this function should only be called on instantiated types diff --git a/crates/noirc_frontend/src/monomorphization/ast.rs b/crates/noirc_frontend/src/monomorphization/ast.rs index 305e6635dfc..7cac2ed8e4f 100644 --- a/crates/noirc_frontend/src/monomorphization/ast.rs +++ b/crates/noirc_frontend/src/monomorphization/ast.rs @@ -209,7 +209,7 @@ pub enum Type { String(/*len:*/ u64), // String(4) = str[4] Unit, Tuple(Vec), - Vec(Box), + Slice(Box), MutableReference(Box), Function(/*args:*/ Vec, /*ret:*/ Box), } @@ -323,7 +323,7 @@ impl std::fmt::Display for Type { let args = vecmap(args, ToString::to_string); write!(f, "fn({}) -> {}", args.join(", "), ret) } - Type::Vec(element) => write!(f, "Vec<{element}>"), + Type::Slice(element) => write!(f, "[{element}"), Type::MutableReference(element) => write!(f, "&mut {element}"), } } diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 7412a4124db..d56390dbf0d 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -22,7 +22,7 @@ use crate::{ }, node_interner::{self, DefinitionKind, NodeInterner, StmtId}, token::Attribute, - CompTime, FunctionKind, TypeBinding, TypeBindings, + CompTime, FunctionKind, Type, TypeBinding, TypeBindings, }; use self::ast::{Definition, FuncId, Function, LocalId, Program}; @@ -266,7 +266,7 @@ impl<'interner> Monomorphizer<'interner> { Literal(Integer(value, typ)) } HirExpression::Literal(HirLiteral::Array(array)) => match array { - HirArrayLiteral::Standard(array) => self.standard_array(array), + HirArrayLiteral::Standard(array) => self.standard_array(expr, array), HirArrayLiteral::Repeated { repeated_element, length } => { self.repeated_array(repeated_element, length) } @@ -347,9 +347,14 @@ impl<'interner> Monomorphizer<'interner> { } } - fn standard_array(&mut self, array: Vec) -> ast::Expression { - let element_type = Self::convert_type(&self.interner.id_type(array[0])); - let contents = vecmap(array, |id| self.expr(id)); + fn standard_array( + &mut self, + array: node_interner::ExprId, + array_elements: Vec, + ) -> ast::Expression { + let element_type = + Self::convert_type(&unwrap_array_element_type(&self.interner.id_type(array))); + let contents = vecmap(array_elements, |id| self.expr(id)); Self::aos_to_soa(contents, element_type) } @@ -401,7 +406,7 @@ impl<'interner> Monomorphizer<'interner> { }, )), - ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Vec(_) => { + ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Slice(_) => { unreachable!("Nested arrays, arrays of strings, and Vecs are not supported") } } @@ -445,7 +450,7 @@ impl<'interner> Monomorphizer<'interner> { })) } - ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Vec(_) => { + ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Slice(_) => { unreachable!("Nested arrays and arrays of strings or Vecs are not supported") } } @@ -661,6 +666,11 @@ impl<'interner> Monomorphizer<'interner> { Self::aos_to_soa_type(length, element) } + HirType::Slice(element) => { + let element = Self::convert_type(element.as_ref()); + ast::Type::Slice(Box::new(element)) + } + HirType::PolymorphicInteger(_, binding) | HirType::TypeVariable(binding) | HirType::NamedGeneric(binding, _) => { @@ -697,11 +707,6 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Function(args, ret) } - HirType::Vec(element) => { - let element = Self::convert_type(element); - ast::Type::Vec(Box::new(element)) - } - HirType::MutableReference(element) => { let element = Self::convert_type(element); ast::Type::MutableReference(Box::new(element)) @@ -728,7 +733,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Tuple(vecmap(elements, |typ| Self::aos_to_soa_type(length, typ))) } - ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Vec(_) => { + ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Slice(_) => { unreachable!("Nested arrays and arrays of strings are not supported") } } @@ -763,13 +768,16 @@ impl<'interner> Monomorphizer<'interner> { ) -> Option { if let ast::Expression::Ident(ident) = func { if let Definition::Builtin(opcode) = &ident.definition { + // TODO(#1736): Move this builtin to the SSA pass if opcode == "array_len" { let typ = self.interner.id_type(arguments[0]); - let len = typ.evaluate_to_u64().unwrap(); - return Some(ast::Expression::Literal(ast::Literal::Integer( - (len as u128).into(), - ast::Type::Field, - ))); + if let Type::Array(_, _) = typ { + let len = typ.evaluate_to_u64().unwrap(); + return Some(ast::Expression::Literal(ast::Literal::Integer( + (len as u128).into(), + ast::Type::Field, + ))); + } } else if opcode == "modulus_num_bits" { return Some(ast::Expression::Literal(ast::Literal::Integer( (FieldElement::max_num_bits() as u128).into(), @@ -993,13 +1001,18 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Function(parameter_types, ret_type) => { self.create_zeroed_function(parameter_types, ret_type) } - ast::Type::Vec(_) => panic!("Cannot create a zeroed Vec value. This type is currently unimplemented and meant to be unusable outside of unconstrained functions"), + ast::Type::Slice(element_type) => { + ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { + contents: vec![], + element_type: *element_type.clone(), + })) + } ast::Type::MutableReference(element) => { use crate::UnaryOp::MutableReference; let rhs = Box::new(self.zeroed_value_of_type(element)); let result_type = typ.clone(); ast::Expression::Unary(ast::Unary { rhs, result_type, operator: MutableReference }) - }, + } } } @@ -1062,6 +1075,20 @@ fn unwrap_struct_type(typ: &HirType) -> Vec<(String, HirType)> { } } +fn unwrap_array_element_type(typ: &HirType) -> HirType { + match typ { + HirType::Array(_, elem) => *elem.clone(), + HirType::Slice(elem) => *elem.clone(), + HirType::TypeVariable(binding) => match &*binding.borrow() { + TypeBinding::Bound(binding) => unwrap_array_element_type(binding), + TypeBinding::Unbound(_) => unreachable!(), + }, + other => { + unreachable!("unwrap_array_element_type: expected an array or slice, found {:?}", other) + } + } +} + fn perform_instantiation_bindings(bindings: &TypeBindings) { for (var, binding) in bindings.values() { *var.borrow_mut() = TypeBinding::Bound(binding.clone()); diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index b2bbc0df0d0..f37daf45136 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -70,6 +70,10 @@ pub struct NodeInterner { /// Methods on primitive types defined in the stdlib. primitive_methods: HashMap<(TypeMethodKey, String), FuncId>, + + /// TODO(#1850): This is technical debt that should be removed once we fully move over + /// to the new SSA pass which does have slices enabled + pub enable_slices: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -249,6 +253,7 @@ impl Default for NodeInterner { globals: HashMap::new(), struct_methods: HashMap::new(), primitive_methods: HashMap::new(), + enable_slices: false, }; // An empty block expression is used often, we add this into the `node` on startup @@ -527,8 +532,7 @@ impl NodeInterner { } pub fn next_type_variable(&mut self) -> Type { - let binding = TypeBinding::Unbound(self.next_type_variable_id()); - Type::TypeVariable(Shared::new(binding)) + Type::type_variable(self.next_type_variable_id()) } pub fn store_instantiation_bindings( @@ -599,12 +603,12 @@ enum TypeMethodKey { /// accept only fields or integers, it is just that their names may not clash. FieldOrInt, Array, + Slice, Bool, String, Unit, Tuple, Function, - Vec, } fn get_type_method_key(typ: &Type) -> Option { @@ -613,6 +617,7 @@ fn get_type_method_key(typ: &Type) -> Option { match &typ { Type::FieldElement(_) => Some(FieldOrInt), Type::Array(_, _) => Some(Array), + Type::Slice(_) => Some(Slice), Type::Integer(_, _, _) => Some(FieldOrInt), Type::PolymorphicInteger(_, _) => Some(FieldOrInt), Type::Bool(_) => Some(Bool), @@ -620,7 +625,6 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _) => Some(Function), - Type::Vec(_) => Some(Vec), Type::MutableReference(element) => get_type_method_key(element), // We do not support adding methods to these types diff --git a/crates/noirc_frontend/src/parser/errors.rs b/crates/noirc_frontend/src/parser/errors.rs index e788893c58d..50b84235d58 100644 --- a/crates/noirc_frontend/src/parser/errors.rs +++ b/crates/noirc_frontend/src/parser/errors.rs @@ -11,8 +11,6 @@ use super::labels::ParsingRuleLabel; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParserErrorReason { - #[error("Arrays must have at least one element")] - ZeroSizedArray, #[error("Unexpected '{0}', expected a field name")] ExpectedFieldName(Token), #[error("Expected a ; separating these two statements")] diff --git a/crates/noirc_frontend/src/parser/parser.rs b/crates/noirc_frontend/src/parser/parser.rs index ec3357c56d4..2303825dd01 100644 --- a/crates/noirc_frontend/src/parser/parser.rs +++ b/crates/noirc_frontend/src/parser/parser.rs @@ -646,7 +646,6 @@ fn parse_type_inner( named_type(recursive_type_parser.clone()), array_type(recursive_type_parser.clone()), tuple_type(recursive_type_parser.clone()), - vec_type(recursive_type_parser.clone()), function_type(recursive_type_parser.clone()), mutable_reference_type(recursive_type_parser), )) @@ -706,12 +705,6 @@ fn named_type(type_parser: impl NoirParser) -> impl NoirParser) -> impl NoirParser { - keyword(Keyword::Vec) - .ignore_then(generic_type_args(type_parser)) - .map_with_span(UnresolvedType::Vec) -} - fn generic_type_args( type_parser: impl NoirParser, ) -> impl NoirParser> { @@ -1001,12 +994,7 @@ where { expression_list(expr_parser) .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .validate(|elements, span, emit| { - if elements.is_empty() { - emit(ParserError::with_reason(ParserErrorReason::ZeroSizedArray, span)); - } - ExpressionKind::array(elements) - }) + .validate(|elements, _span, _emit| ExpressionKind::array(elements)) } /// [a; N] diff --git a/crates/wasm/src/compile.rs b/crates/wasm/src/compile.rs index 79bc90cd590..762ca9fe2fd 100644 --- a/crates/wasm/src/compile.rs +++ b/crates/wasm/src/compile.rs @@ -90,7 +90,7 @@ pub fn compile(args: JsValue) -> JsValue { add_noir_lib(&mut context, dependency.as_str()); } - check_crate(&mut context, false).expect("Crate check failed"); + check_crate(&mut context, false, false).expect("Crate check failed"); if options.contracts { let compiled_contracts = compile_contracts( diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index 52c6b6c218c..9e44aa03fcc 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -1,10 +1,12 @@ +// TODO: Once we fully move to the new SSA pass this module can be removed and replaced +// by the methods in the `slice` module impl [T; N] { #[builtin(array_len)] - fn len(_array: Self) -> comptime Field {} + fn len(_self: Self) -> comptime Field {} #[builtin(arraysort)] - fn sort(_array: Self) -> Self {} + fn sort(_self: Self) -> Self {} // Sort with a custom sorting function. fn sort_via(mut a: Self, ordering: fn(T, T) -> bool) -> Self { diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr deleted file mode 100644 index 130dfdfc2a6..00000000000 --- a/noir_stdlib/src/collections/vec.nr +++ /dev/null @@ -1,24 +0,0 @@ - -// These methods are all stubs currently and aren't implemented internally yet. -// For a similar reason, no constructor for Vec is exposed yet since the type -// is still in-progress. -impl Vec { - /// Get an element from the vector at the given index. - /// Fails with a constraint error if the given index - /// points beyond the end of the vector. - #[builtin(vec_get)] - fn get(_self: Self, _index: Field) -> T { } - - /// Push a new element to the end of the vector, returning a - /// new vector with a length one greater than the - /// original unmodified vector. - #[builtin(vec_push)] - fn push(_self: Self, _elem: T) -> Self { } - - /// Pop an element from the end of the given vector, returning - /// a new vector with a length of one less than the given vector, - /// as well as the popped element. - /// Fails with a constraint error if the given vector's length is zero. - #[builtin(vec_pop)] - fn pop(_self: Self) -> (Self, T) { } -} diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index b010eb31be3..fa183f0bab6 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -1,5 +1,6 @@ mod hash; mod array; +mod slice; mod merkle; mod schnorr; mod ecdsa_secp256k1; @@ -10,7 +11,6 @@ mod sha512; mod field; mod ec; mod unsafe; -mod collections; mod compat; #[builtin(println)] diff --git a/noir_stdlib/src/slice.nr b/noir_stdlib/src/slice.nr new file mode 100644 index 00000000000..8e813cab1c6 --- /dev/null +++ b/noir_stdlib/src/slice.nr @@ -0,0 +1,78 @@ + +impl [T] { + /// Push a new element to the end of the slice, returning a + /// new slice with a length one greater than the + /// original unmodified slice. + #[builtin(slice_push_back)] + fn push_back(_self: Self, _elem: T) -> Self { } + + #[builtin(array_len)] + fn len(_self: Self) -> comptime Field {} + + #[builtin(arraysort)] + fn sort(_self: Self) -> Self {} + + // Sort with a custom sorting function. + fn sort_via(mut a: Self, ordering: fn(T, T) -> bool) -> Self { + for i in 1 .. a.len() { + for j in 0..i { + if ordering(a[i], a[j]) { + let old_a_j = a[j]; + a[j] = a[i]; + a[i] = old_a_j; + } + } + } + a + } + + // Apply a function to each element of a slice, returning a new slice + // containing the mapped elements. + fn map(self, f: fn(T) -> U) -> [U] { + let mut ret: [U] = []; + for elem in self { + ret = ret.push_back(f(elem)); + } + ret + } + + // Apply a function to each element of the slice and an accumulator value, + // returning the final accumulated value. This function is also sometimes + // called `foldl`, `fold_left`, `reduce`, or `inject`. + fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U { + for elem in self { + accumulator = f(accumulator, elem); + } + accumulator + } + + // Apply a function to each element of the slice and an accumulator value, + // returning the final accumulated value. Unlike fold, reduce uses the first + // element of the given slice as its starting accumulator value. + fn reduce(self, f: fn(T, T) -> T) -> T { + let mut accumulator = self[0]; + for i in 1 .. self.len() { + accumulator = f(accumulator, self[i]); + } + accumulator + } + + // Returns true if all elements in the array satisfy the predicate + fn all(self, predicate: fn(T) -> bool) -> bool { + let mut ret = true; + for elem in self { + ret &= predicate(elem); + } + ret + } + + // Returns true if any element in the array satisfies the predicate + fn any(self, predicate: fn(T) -> bool) -> bool { + let mut ret = false; + for elem in self { + ret |= predicate(elem); + } + ret + } +} +