Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add new Vec type to frontend #1103

Merged
merged 4 commits into from
Apr 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
14 changes: 14 additions & 0 deletions noir_stdlib/src/collections/vec.nr
Original file line number Diff line number Diff line change
@@ -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<T> Vec<T> {
#[builtin(vec_get)]
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
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) { }
}
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) {}