Skip to content

Commit

Permalink
feat: Add new Vec type to frontend (#1103)
Browse files Browse the repository at this point in the history
* Add Vec keyword

* Add Vec to frontend

* Add doc comments

* Update noir_stdlib/src/collections/vec.nr

Co-authored-by: Tom French <[email protected]>

---------

Co-authored-by: Tom French <[email protected]>
  • Loading branch information
jfecher and TomAFrench authored Apr 6, 2023
1 parent 1fb2756 commit e125157
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 17 deletions.
1 change: 1 addition & 0 deletions crates/noirc_evaluator/src/ssa/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/noirc_evaluator/src/ssa/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl Value {
Type::Unit
| Type::Function(..)
| Type::Array(..)
| Type::Vec(..)
| Type::String(..)
| Type::Integer(..)
| Type::Bool
Expand Down
12 changes: 12 additions & 0 deletions crates/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ pub enum UnresolvedType {
/// A Named UnresolvedType can be a struct type or a type variable
Named(Path, Vec<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.
///
/// The Span here encompasses the entire type and is used to issue an error if exactly 1
/// generic argument is not given.
Vec(Vec<UnresolvedType>, Span),

// Note: Tuples have no visibility, instead each of their elements may have one.
Tuple(Vec<UnresolvedType>),

Expand Down Expand Up @@ -100,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"),
Expand Down
11 changes: 3 additions & 8 deletions crates/noirc_frontend/src/hir/resolution/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<StructType>,
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")]
Expand Down Expand Up @@ -238,7 +233,7 @@ impl From<ResolverError> 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,
)
Expand Down
19 changes: 17 additions & 2 deletions crates/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -751,6 +765,7 @@ impl<'a> Resolver<'a> {
}
}
}
Type::Vec(element) => Self::find_numeric_generics_in_type(element, found),
}
}

Expand Down
17 changes: 17 additions & 0 deletions crates/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ pub enum Type {
/// A functions with arguments, and a return type.
Function(Vec<Type>, Box<Type>),

/// 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<Type>),

/// 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
Expand Down Expand Up @@ -589,6 +594,7 @@ impl Type {
}
})
}
Type::Vec(element) => element.contains_numeric_typevar(target_id),
}
}
}
Expand Down Expand Up @@ -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)
}
}
}
}
Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -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"),
}
}

Expand Down Expand Up @@ -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(_, _, _)
Expand Down Expand Up @@ -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(_, _, _)
Expand Down Expand Up @@ -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!(),
Expand Down
3 changes: 3 additions & 0 deletions crates/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ pub enum Keyword {
Struct,
Unconstrained,
Use,
Vec,
While,
}

Expand Down Expand Up @@ -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"),
}
}
Expand Down Expand Up @@ -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)),
Expand Down
2 changes: 2 additions & 0 deletions crates/noirc_frontend/src/monomorphization/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ pub enum Type {
String(/*len:*/ u64), // String(4) = str[4]
Unit,
Tuple(Vec<Type>),
Vec(Box<Type>),
Function(/*args:*/ Vec<Type>, /*ret:*/ Box<Type>),
}

Expand Down Expand Up @@ -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}>"),
}
}
}
16 changes: 11 additions & 5 deletions crates/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Expand Down Expand Up @@ -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")
}
}
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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")
}
}
Expand Down Expand Up @@ -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"),
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ enum TypeMethodKey {
Unit,
Tuple,
Function,
Vec,
}

fn get_type_method_key(typ: &Type) -> Option<TypeMethodKey> {
Expand All @@ -669,6 +670,7 @@ fn get_type_method_key(typ: &Type) -> Option<TypeMethodKey> {
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(_)
Expand Down
11 changes: 9 additions & 2 deletions crates/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
))
}
Expand Down Expand Up @@ -593,6 +594,12 @@ fn named_type(type_parser: impl NoirParser<UnresolvedType>) -> impl NoirParser<U
.map(|(path, args)| UnresolvedType::Named(path, args))
}

fn vec_type(type_parser: impl NoirParser<UnresolvedType>) -> impl NoirParser<UnresolvedType> {
keyword(Keyword::Vec)
.ignore_then(generic_type_args(type_parser))
.map_with_span(UnresolvedType::Vec)
}

fn generic_type_args(
type_parser: impl NoirParser<UnresolvedType>,
) -> impl NoirParser<Vec<UnresolvedType>> {
Expand Down
1 change: 1 addition & 0 deletions noir_stdlib/src/collections.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod vec;
24 changes: 24 additions & 0 deletions noir_stdlib/src/collections/vec.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

// 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<T> Vec<T> {
/// 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) { }
}
1 change: 1 addition & 0 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod sha512;
mod field;
mod ec;
mod unsafe;
mod collections;

#[builtin(println)]
fn println<T>(_input : T) {}

0 comments on commit e125157

Please sign in to comment.