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(frontend): aztec syntactic sugar (feature flagged) #2403

Merged
merged 23 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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/nargo_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ default = ["plonk_bn254"]
# The plonk backend can only use bn254, so we do not specify the field
plonk_bn254 = ["acvm-backend-barretenberg/native"]
plonk_bn254_wasm = ["acvm-backend-barretenberg/wasm"]
aztec = []
3 changes: 3 additions & 0 deletions crates/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ pub fn check_crate(
// You can add any crate type to the crate graph
// but you cannot depend on Binaries
let std_crate = context.crate_graph.add_stdlib(root_file_id);

// Maybe this is a nice solution eventually
// let aztec_crate = context.crate_graph.add_stdlib(root_file_id);
jfecher marked this conversation as resolved.
Show resolved Hide resolved
propagate_dep(context, std_crate, &std_crate_name.parse().unwrap());

let mut errors = vec![];
Expand Down
5 changes: 4 additions & 1 deletion crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ impl<'a> FunctionContext<'a> {
let slice_contents = self.codegen_array(elements, typ[1].clone());
Tree::Branch(vec![slice_length.into(), slice_contents])
}
_ => unreachable!("ICE: array literal type must be an array or a slice, but got {}", array.typ),
_ => unreachable!(
"ICE: array literal type must be an array or a slice, but got {}",
array.typ
),
}
}
ast::Literal::Integer(value, typ) => {
Expand Down
1 change: 1 addition & 0 deletions crates/noirc_frontend/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl From<FunctionDefinition> for NoirFunction {
Some(Attribute::Test) => FunctionKind::Normal,
Some(Attribute::Oracle(_)) => FunctionKind::Oracle,
Some(Attribute::Deprecated(_)) | None => FunctionKind::Normal,
Some(Attribute::Custom(_)) => FunctionKind::Normal,
};

NoirFunction { def: fd, kind }
Expand Down
202 changes: 202 additions & 0 deletions crates/noirc_frontend/src/hir/def_map/aztec_helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use noirc_errors::Span;

use crate::{
token::Attribute, CallExpression, Distinctness, Expression, ExpressionKind, FunctionReturnType,
Ident, LetStatement, MethodCallExpression, NoirFunction, ParsedModule, Pattern, Statement,
UnresolvedType, Visibility,
};

pub(crate) fn aztec_contracts_macros(ast: &mut ParsedModule) {
// Usage -> mut ast -> AztecLib.transform(&mut ast)

// TODO: rm
// loop over all of the functions and print their attributes, to check what attributes look like and if they
// can be added at will
// NOTE: only one attribute can be applied per function at the moment
jfecher marked this conversation as resolved.
Show resolved Hide resolved

// This should cover all functions in the ast
for func in ast.functions.iter_mut() {
transform_function(func);
}
for submodule in ast.submodules.iter_mut() {
for func in submodule.contents.functions.iter_mut() {
transform_function(func);
}
}
}

fn transform_function(func: &mut NoirFunction) {
if let Some(Attribute::Custom(custom_attribute)) = func.def.attribute.as_ref() {
// TODO: this can just become the one function!!!
// match based on the custom attribute
match custom_attribute.as_str() {
"aztec(private)" => {
// Edit the ast to inject the private context into the function
// Create the context using the current params
let create_context = create_context("PrivateContext", &func.def.parameters);
// Insert the context creation as the first action
func.def.body.0.insert(0, create_context);

// Add the inputs to the params
let private_input = create_inputs("PrivateContextInputs");
func.def.parameters.insert(0, private_input);

// Push the finish method call to the end of the function
let finish_def = create_context_finish();
// dbg!(&finish_def);
func.def.body.0.push(finish_def);

let return_type = create_return_type("PrivateCircuitPublicInputs");
func.def.return_type = return_type;
func.def.return_visibility = Visibility::Public;
func.def.return_distinctness = Distinctness::Distinct;
}
"aztec(public)" => {
let create_context = create_context("PublicContext", &func.def.parameters);
// Insert the context creation as the first action
func.def.body.0.insert(0, create_context);

// Add the inputs to the params
let private_input = create_inputs("PublicContextInputs");
func.def.parameters.insert(0, private_input);

// Push the finish method call to the end of the function
let finish_def = create_context_finish();
// dbg!(&finish_def);
func.def.body.0.push(finish_def);

let return_type = create_return_type("PublicCircuitPublicInputs");
func.def.return_type = return_type;
func.def.return_visibility = Visibility::Public;
// func.def.return_distinctness = Distinctness::Distinct;
}
_ => return,
}
dbg!(&func);
}
}

/// Helper function that returns what the private context would look like in the ast
/// This should make it available to be consumed within aztec private annotated functions.
pub(crate) fn create_inputs(ty: &str) -> (Pattern, UnresolvedType, Visibility) {
let context_ident = Ident::new("inputs".to_string(), Span::default());
let context_patt = Pattern::Identifier(context_ident);
let context_type_ident = Ident::new(ty.to_string(), Span::default());
let context_type = UnresolvedType::Named(crate::Path::from_ident(context_type_ident), vec![]);
let visibility = Visibility::Private;

(context_patt, context_type, visibility)
}

pub(crate) fn create_context(
ty: &str, // type
params: &Vec<(Pattern, UnresolvedType, Visibility)>,
) -> Statement {
let hash_args = create_hash_args(params);

let context_ident = Ident::new("context".to_string(), Span::default());
let context_patt = Pattern::Identifier(context_ident);
let context_mut = Pattern::Mutable(Box::new(context_patt.clone()), Span::default());
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let context_type_ident = Ident::new(ty.to_string(), Span::default());
let mut context_path = crate::Path::from_ident(context_type_ident);
let context_type = UnresolvedType::Named(context_path.clone(), vec![]);

// Create the new context
context_path.segments.push(Ident::new("new".to_string(), Span::default()));

let inputs_expression = Expression::new(
ExpressionKind::Variable(crate::Path::from_ident(Ident::new(
"inputs".to_string(),
Span::default(),
))),
Span::default(),
);
let hash_call = Expression::new(ExpressionKind::Call(Box::new(hash_args)), Span::default());
let new_context_args = vec![inputs_expression, hash_call];

// Call the init of the context
let expression = Expression::new(
ExpressionKind::Call(Box::new(CallExpression {
func: Box::new(Expression::new(
ExpressionKind::Variable(context_path),
Span::default(),
)),
arguments: new_context_args,
})),
Span::default(),
);

let let_expression = LetStatement { pattern: context_mut, r#type: context_type, expression };
Statement::Let(let_expression)
}

/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be
/// appended into the args hash object
fn create_hash_args(params: &Vec<(Pattern, UnresolvedType, Visibility)>) -> CallExpression {
let mut hash_path = crate::Path::from_ident(Ident::new("abi".to_string(), Span::default()));
hash_path.segments.push(Ident::new("hash_args".to_string(), Span::default()));

let param_expressions = params
.iter()
.map(|param| {
let param_pattern = &param.0;
match param_pattern {
Pattern::Identifier(ident) => {
// Converts each type to a Field Element before hashing
let variable = Expression::new(
ExpressionKind::Variable(crate::Path::from_ident(ident.clone())),
Span::default(),
);
let cast_expression = ExpressionKind::Cast(Box::new(crate::CastExpression {
lhs: variable,
r#type: UnresolvedType::FieldElement,
}));

Expression::new(cast_expression, Span::default())
}
_ => todo!(),
}
})
.collect::<Vec<Expression>>();

let args_array = Expression::new(
ExpressionKind::Literal(crate::Literal::Array(crate::ArrayLiteral::Standard(
param_expressions.clone(),
))),
Span::default(),
);

let call_args = vec![args_array];

let call_expression = CallExpression {
func: Box::new(Expression::new(ExpressionKind::Variable(hash_path), Span::default())),
arguments: call_args,
};
return call_expression;
}

pub(crate) fn create_return_type(ty: &str) -> FunctionReturnType {
let return_ident_base = Ident::new("abi".to_string(), Span::default());
let return_ident = Ident::new(ty.to_string(), Span::default());
let mut return_path = crate::Path::from_ident(return_ident_base);
return_path.segments.push(return_ident);

let ty = UnresolvedType::Named(return_path, vec![]);
FunctionReturnType::Ty(ty, Span::default())
}

pub(crate) fn create_context_finish() -> Statement {
let context_ident = Ident::new("context".to_string(), Span::default());
let method_call_expression = MethodCallExpression {
object: Expression::new(
ExpressionKind::Variable(crate::Path::from_ident(context_ident)),
Span::default(),
),
method_name: Ident::new("finish".to_string(), Span::default()),
arguments: vec![],
};
let method_call = ExpressionKind::MethodCall(Box::new(method_call_expression));

let expression = Expression::new(method_call, Span::default());
Statement::Expression(expression)
}
13 changes: 13 additions & 0 deletions crates/noirc_frontend/src/hir/def_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ pub use module_data::*;
mod namespace;
pub use namespace::*;

#[cfg(feature = "aztec")]
mod aztec_helper;
#[cfg(feature = "aztec")]
use aztec_helper::aztec_contracts_macros;

/// The name that is used for a non-contract program's entry-point function.
pub const MAIN_FUNCTION: &str = "main";

Expand Down Expand Up @@ -83,8 +88,16 @@ impl CrateDefMap {

// First parse the root file.
let root_file_id = context.crate_graph[crate_id].root_file_id;

jfecher marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(not(feature = "aztec"))]
let ast = parse_file(&mut context.file_manager, root_file_id, errors);

#[cfg(feature = "aztec")]
{
let mut ast = parse_file(&mut context.file_manager, root_file_id, errors);
aztec_contracts_macros(&mut ast);
}
jfecher marked this conversation as resolved.
Show resolved Hide resolved

// Allocate a default Module for the root, giving it a ModuleId
let mut modules: Arena<ModuleData> = Arena::default();
let location = Location::new(Default::default(), root_file_id);
Expand Down
12 changes: 12 additions & 0 deletions crates/noirc_frontend/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,18 @@ fn deprecated_attribute_with_note() {
assert_eq!(token.token(), &Token::Attribute(Attribute::Deprecated("hello".to_string().into())));
}

#[test]
fn custom_attribute() {
let input = r#"#[custom(hello)]"#;
let mut lexer = Lexer::new(input);

let token = lexer.next().unwrap().unwrap();
assert_eq!(
Maddiaa0 marked this conversation as resolved.
Show resolved Hide resolved
token.token(),
&Token::Attribute(Attribute::Custom("custom(hello)".to_string().into()))
);
}

#[test]
fn test_custom_gate_syntax() {
let input = "#[foreign(sha256)]#[foreign(blake2s)]#[builtin(sum)]";
Expand Down
8 changes: 6 additions & 2 deletions crates/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ pub enum Attribute {
Oracle(String),
Deprecated(Option<String>),
Test,
Custom(String),
}

impl fmt::Display for Attribute {
Expand All @@ -337,6 +338,7 @@ impl fmt::Display for Attribute {
Attribute::Test => write!(f, "#[test]"),
Attribute::Deprecated(None) => write!(f, "#[deprecated]"),
Attribute::Deprecated(Some(ref note)) => write!(f, r#"#[deprecated("{note}")]"#),
Attribute::Custom(ref k) => write!(f, "#[{k}]"),
}
}
}
Expand Down Expand Up @@ -390,8 +392,9 @@ impl Attribute {
Attribute::Deprecated(name.trim_matches('"').to_string().into())
}
["test"] => Attribute::Test,
_ => {
return Err(LexerErrorKind::MalformedFuncAttribute { span, found: word.to_owned() })
tokens => {
tokens.iter().try_for_each(|token| validate(token))?;
Attribute::Custom(word.to_owned())
}
};

Expand Down Expand Up @@ -429,6 +432,7 @@ impl AsRef<str> for Attribute {
Attribute::Oracle(string) => string,
Attribute::Deprecated(Some(string)) => string,
Attribute::Test | Attribute::Deprecated(None) => "",
Attribute::Custom(string) => string,
}
}
}
Expand Down