From c61e7320f33042b3f08ffcb2295cb5f9c95935d8 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 5 Apr 2023 09:24:42 -0500 Subject: [PATCH 1/4] Add Vec keyword --- crates/noirc_frontend/src/ast/mod.rs | 5 +++++ crates/noirc_frontend/src/lexer/token.rs | 3 +++ crates/noirc_frontend/src/parser/parser.rs | 11 +++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/noirc_frontend/src/ast/mod.rs b/crates/noirc_frontend/src/ast/mod.rs index 2e6592e3c87..1e1c2834261 100644 --- a/crates/noirc_frontend/src/ast/mod.rs +++ b/crates/noirc_frontend/src/ast/mod.rs @@ -35,6 +35,11 @@ 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. + Vec(Vec), + // Note: Tuples have no visibility, instead each of their elements may have one. Tuple(Vec), diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 84939641bd7..852b90451a3 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -439,6 +439,7 @@ pub enum Keyword { Struct, Unconstrained, Use, + Vec, While, } @@ -471,6 +472,7 @@ impl fmt::Display for Keyword { Keyword::Struct => write!(f, "struct"), Keyword::Unconstrained => write!(f, "unconstrained"), Keyword::Use => write!(f, "use"), + Keyword::Vec => write!(f, "Vec"), Keyword::While => write!(f, "while"), } } @@ -506,6 +508,7 @@ impl Keyword { "struct" => Keyword::Struct, "unconstrained" => Keyword::Unconstrained, "use" => Keyword::Use, + "vec" => Keyword::Vec, "while" => Keyword::While, "true" => return Some(Token::Bool(true)), diff --git a/crates/noirc_frontend/src/parser/parser.rs b/crates/noirc_frontend/src/parser/parser.rs index 463fbee248e..f782f1a04a8 100644 --- a/crates/noirc_frontend/src/parser/parser.rs +++ b/crates/noirc_frontend/src/parser/parser.rs @@ -537,11 +537,12 @@ fn parse_type_inner( choice(( field_type(), int_type(), + bool_type(), + string_type(), named_type(recursive_type_parser.clone()), array_type(recursive_type_parser.clone()), tuple_type(recursive_type_parser.clone()), - bool_type(), - string_type(), + vec_type(recursive_type_parser.clone()), function_type(recursive_type_parser), )) } @@ -593,6 +594,12 @@ fn named_type(type_parser: impl NoirParser) -> impl NoirParser) -> impl NoirParser { + keyword(Keyword::Vec) + .ignore_then(generic_type_args(type_parser)) + .map(UnresolvedType::Vec) +} + fn generic_type_args( type_parser: impl NoirParser, ) -> impl NoirParser> { From 90eb27cfc9fcfb36b992f7a08e9d762778dbeb03 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 5 Apr 2023 14:10:26 -0500 Subject: [PATCH 2/4] Add Vec to frontend --- crates/noirc_evaluator/src/ssa/context.rs | 1 + crates/noirc_evaluator/src/ssa/value.rs | 1 + crates/noirc_frontend/src/ast/mod.rs | 9 ++++++++- .../src/hir/resolution/errors.rs | 11 +++-------- .../src/hir/resolution/resolver.rs | 19 +++++++++++++++++-- crates/noirc_frontend/src/hir_def/types.rs | 17 +++++++++++++++++ crates/noirc_frontend/src/lexer/token.rs | 2 +- .../src/monomorphization/ast.rs | 2 ++ .../src/monomorphization/mod.rs | 16 +++++++++++----- crates/noirc_frontend/src/node_interner.rs | 2 ++ crates/noirc_frontend/src/parser/parser.rs | 2 +- noir_stdlib/src/collections.nr | 1 + noir_stdlib/src/collections/vec.nr | 14 ++++++++++++++ noir_stdlib/src/lib.nr | 1 + 14 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 noir_stdlib/src/collections.nr create mode 100644 noir_stdlib/src/collections/vec.nr diff --git a/crates/noirc_evaluator/src/ssa/context.rs b/crates/noirc_evaluator/src/ssa/context.rs index da528e21d05..0154b36debc 100644 --- a/crates/noirc_evaluator/src/ssa/context.rs +++ b/crates/noirc_evaluator/src/ssa/context.rs @@ -1205,6 +1205,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"), } } diff --git a/crates/noirc_evaluator/src/ssa/value.rs b/crates/noirc_evaluator/src/ssa/value.rs index 30c07ccd567..915effe480b 100644 --- a/crates/noirc_evaluator/src/ssa/value.rs +++ b/crates/noirc_evaluator/src/ssa/value.rs @@ -96,6 +96,7 @@ impl Value { Type::Unit | Type::Function(..) | Type::Array(..) + | Type::Vec(..) | Type::String(..) | Type::Integer(..) | Type::Bool diff --git a/crates/noirc_frontend/src/ast/mod.rs b/crates/noirc_frontend/src/ast/mod.rs index 1e1c2834261..6bd5c148d66 100644 --- a/crates/noirc_frontend/src/ast/mod.rs +++ b/crates/noirc_frontend/src/ast/mod.rs @@ -38,7 +38,10 @@ pub enum UnresolvedType { /// 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. - Vec(Vec), + /// + /// 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), // Note: Tuples have no visibility, instead each of their elements may have one. Tuple(Vec), @@ -105,6 +108,10 @@ 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(", ")) + } Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), diff --git a/crates/noirc_frontend/src/hir/resolution/errors.rs b/crates/noirc_frontend/src/hir/resolution/errors.rs index 3ce7d9d0249..9406474a226 100644 --- a/crates/noirc_frontend/src/hir/resolution/errors.rs +++ b/crates/noirc_frontend/src/hir/resolution/errors.rs @@ -2,7 +2,7 @@ pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::{parser::ParserError, Ident, Shared, StructType, Type}; +use crate::{parser::ParserError, Ident, Type}; use super::import::PathResolutionError; @@ -53,12 +53,7 @@ pub enum ResolverError { #[error("Cannot apply generics on Self type")] GenericsOnSelfType { span: Span }, #[error("Incorrect amount of arguments to generic type constructor")] - IncorrectGenericCount { - span: Span, - struct_type: Shared, - actual: usize, - expected: usize, - }, + IncorrectGenericCount { span: Span, struct_type: String, actual: usize, expected: usize }, #[error("{0}")] ParserError(ParserError), #[error("Function is not defined in a contract yet sets its contract visibility")] @@ -238,7 +233,7 @@ impl From for Diagnostic { let actual_plural = if actual == 1 { "is" } else { "are" }; Diagnostic::simple_error( - format!("The struct type {} has {expected} generic{expected_plural} but {actual} {actual_plural} given here", struct_type.borrow()), + format!("The struct type {struct_type} has {expected} generic{expected_plural} but {actual} {actual_plural} given here"), "Incorrect number of generic arguments".into(), span, ) diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index d01952f7c49..e0e282e0e02 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -347,6 +347,20 @@ 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)) + } } } @@ -390,13 +404,13 @@ impl<'a> Resolver<'a> { if args.len() != expected_generic_count { self.push_err(ResolverError::IncorrectGenericCount { span, - struct_type: struct_type.clone(), + struct_type: struct_type.borrow().to_string(), actual: args.len(), expected: expected_generic_count, }); // Fix the generic count so we can continue typechecking - args.resize_with(expected_generic_count, || self.interner.next_type_variable()) + args.resize_with(expected_generic_count, || Type::Error) } Type::Struct(struct_type, args) @@ -751,6 +765,7 @@ impl<'a> Resolver<'a> { } } } + Type::Vec(element) => Self::find_numeric_generics_in_type(element, found), } } diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index f22ebb621d7..ff113f83c51 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -70,6 +70,11 @@ 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), + /// A type generic over the given type variables. /// Storing both the TypeVariableId and TypeVariable isn't necessary /// but it makes handling them both easier. The TypeVariableId should @@ -589,6 +594,7 @@ impl Type { } }) } + Type::Vec(element) => element.contains_numeric_typevar(target_id), } } } @@ -645,6 +651,9 @@ 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) + } } } } @@ -975,6 +984,8 @@ impl Type { } } + (Vec(elem_a), Vec(elem_b)) => elem_a.try_unify(elem_b, span), + (other_a, other_b) => { if other_a == other_b { Ok(()) @@ -1106,6 +1117,8 @@ impl Type { } } + (Vec(elem_a), Vec(elem_b)) => elem_a.is_subtype_of(elem_b, span), + (other_a, other_b) => { if other_a == other_b { Ok(()) @@ -1176,6 +1189,7 @@ impl Type { Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), Type::Function(_, _) => unreachable!(), + Type::Vec(_) => unreachable!("Vecs cannot be used in the abi"), } } @@ -1289,6 +1303,7 @@ 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::FieldElement(_) | Type::Integer(_, _, _) @@ -1318,6 +1333,7 @@ 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::FieldElement(_) | Type::Integer(_, _, _) @@ -1359,6 +1375,7 @@ impl Type { let ret = Box::new(ret.follow_bindings()); Function(args, ret) } + Vec(element) => Vec(Box::new(element.follow_bindings())), // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 852b90451a3..73ff6bfdedb 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -508,7 +508,7 @@ impl Keyword { "struct" => Keyword::Struct, "unconstrained" => Keyword::Unconstrained, "use" => Keyword::Use, - "vec" => Keyword::Vec, + "Vec" => Keyword::Vec, "while" => Keyword::While, "true" => return Some(Token::Bool(true)), diff --git a/crates/noirc_frontend/src/monomorphization/ast.rs b/crates/noirc_frontend/src/monomorphization/ast.rs index 7920676aa7d..938a937405c 100644 --- a/crates/noirc_frontend/src/monomorphization/ast.rs +++ b/crates/noirc_frontend/src/monomorphization/ast.rs @@ -202,6 +202,7 @@ pub enum Type { String(/*len:*/ u64), // String(4) = str[4] Unit, Tuple(Vec), + Vec(Box), Function(/*args:*/ Vec, /*ret:*/ Box), } @@ -301,6 +302,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}>"), } } } diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 26009c0227e..bfce292d2eb 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -382,8 +382,8 @@ impl<'interner> Monomorphizer<'interner> { }, )), - ast::Type::Array(_, _) | ast::Type::String(_) => { - unreachable!("Nested arrays and arrays of strings are not supported") + ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Vec(_) => { + unreachable!("Nested arrays, arrays of strings, and Vecs are not supported") } } } @@ -425,8 +425,8 @@ impl<'interner> Monomorphizer<'interner> { })) } - ast::Type::Array(_, _) | ast::Type::String(_) => { - unreachable!("Nested arrays and arrays of strings are not supported") + ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Vec(_) => { + unreachable!("Nested arrays and arrays of strings or Vecs are not supported") } } } @@ -663,6 +663,11 @@ 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::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } @@ -683,7 +688,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::Array(_, _) | ast::Type::String(_) | ast::Type::Vec(_) => { unreachable!("Nested arrays and arrays of strings are not supported") } } @@ -941,6 +946,7 @@ 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"), } } diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 417c7bedc66..4479fe91135 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -654,6 +654,7 @@ enum TypeMethodKey { Unit, Tuple, Function, + Vec, } fn get_type_method_key(typ: &Type) -> Option { @@ -669,6 +670,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _) => Some(Function), + Type::Vec(_) => Some(Vec), // We do not support adding methods to these types Type::TypeVariable(_) diff --git a/crates/noirc_frontend/src/parser/parser.rs b/crates/noirc_frontend/src/parser/parser.rs index f782f1a04a8..62824023daf 100644 --- a/crates/noirc_frontend/src/parser/parser.rs +++ b/crates/noirc_frontend/src/parser/parser.rs @@ -597,7 +597,7 @@ fn named_type(type_parser: impl NoirParser) -> impl NoirParser) -> impl NoirParser { keyword(Keyword::Vec) .ignore_then(generic_type_args(type_parser)) - .map(UnresolvedType::Vec) + .map_with_span(UnresolvedType::Vec) } fn generic_type_args( diff --git a/noir_stdlib/src/collections.nr b/noir_stdlib/src/collections.nr new file mode 100644 index 00000000000..e06c662e658 --- /dev/null +++ b/noir_stdlib/src/collections.nr @@ -0,0 +1 @@ +mod vec; diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr new file mode 100644 index 00000000000..39116c20d44 --- /dev/null +++ b/noir_stdlib/src/collections/vec.nr @@ -0,0 +1,14 @@ + +// 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 { + #[builtin(vec_get)] + fn get(_self: Self, _index: Field) -> T { } + + #[builtin(vec_push)] + fn push(_self: Self, _elem: T) -> Self { } + + #[builtin(vec_pop)] + fn pop(_self: Self) -> (Self, T) { } +} diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 16383c2c704..f0af06b97ba 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -9,6 +9,7 @@ mod sha512; mod field; mod ec; mod unsafe; +mod collections; #[builtin(println)] fn println(_input : T) {} From 827131cb4536052a48d6c365fac6609e541e72c0 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 6 Apr 2023 14:14:33 -0500 Subject: [PATCH 3/4] Add doc comments --- noir_stdlib/src/collections/vec.nr | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index 39116c20d44..8eab4a22c65 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -3,12 +3,22 @@ // 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 is + /// less than the length 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) { } } From aad166306d135eae02238dd259ba2d0a300828de Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 6 Apr 2023 14:35:35 -0500 Subject: [PATCH 4/4] Update noir_stdlib/src/collections/vec.nr Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- noir_stdlib/src/collections/vec.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index 8eab4a22c65..130dfdfc2a6 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -4,8 +4,8 @@ // 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 is - /// less than the length of the vector. + /// 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 { }