11use acvm:: FieldElement ;
2+ use iter_extended:: vecmap;
23use noirc_errors:: Span ;
34
45use crate :: graph:: CrateId ;
56use crate :: hir:: def_collector:: errors:: DefCollectorErrorKind ;
7+ use crate :: hir_def:: expr:: { HirExpression , HirLiteral } ;
8+ use crate :: hir_def:: stmt:: HirStatement ;
9+ use crate :: node_interner:: { NodeInterner , StructId } ;
610use crate :: token:: SecondaryAttribute ;
711use crate :: {
812 hir:: Context , BlockExpression , CallExpression , CastExpression , Distinctness , Expression ,
@@ -11,9 +15,14 @@ use crate::{
1115 ParsedModule , Path , PathKind , Pattern , Statement , UnresolvedType , UnresolvedTypeData ,
1216 Visibility ,
1317} ;
14- use crate :: { PrefixExpression , UnaryOp } ;
18+ use crate :: {
19+ FunctionDefinition , NoirStruct , PrefixExpression , Shared , Signedness , StructType , Type ,
20+ TypeBinding , TypeImpl , TypeVariableKind , UnaryOp ,
21+ } ;
1522use fm:: FileId ;
1623
24+ use super :: ModuleDefId ;
25+
1726//
1827// Helper macros for creating noir ast nodes
1928//
@@ -163,7 +172,7 @@ pub(crate) fn transform(
163172 for submodule in ast. submodules . iter_mut ( ) . filter ( |submodule| submodule. is_contract ) {
164173 let storage_defined = check_for_storage_definition ( & submodule. contents ) ;
165174
166- if transform_module ( & mut submodule. contents . functions , storage_defined) {
175+ if transform_module ( & mut submodule. contents , storage_defined) {
167176 match check_for_aztec_dependency ( crate_id, context) {
168177 Ok ( ( ) ) => include_relevant_imports ( & mut submodule. contents ) ,
169178 Err ( file_id) => {
@@ -175,6 +184,15 @@ pub(crate) fn transform(
175184 Ok ( ast)
176185}
177186
187+ //
188+ // Transform Hir Nodes for Aztec
189+ //
190+
191+ /// Completes the Hir with data gathered from type resolution
192+ pub ( crate ) fn transform_hir ( crate_id : & CrateId , context : & mut Context ) {
193+ transform_events ( crate_id, context) ;
194+ }
195+
178196/// Includes an import to the aztec library if it has not been included yet
179197fn include_relevant_imports ( ast : & mut ParsedModule ) {
180198 // Create the aztec import path using the assumed chained_dep! macro
@@ -206,34 +224,45 @@ fn check_for_storage_definition(module: &ParsedModule) -> bool {
206224 module. types . iter ( ) . any ( |function| function. name . 0 . contents == "Storage" )
207225}
208226
209- /// Determines if the function is annotated with `aztec(private)` or `aztec(public)`
210- /// If it is, it calls the `transform` function which will perform the required transformations.
211- /// Returns true if an annotated function is found, false otherwise
212- fn transform_module ( functions : & mut [ NoirFunction ] , storage_defined : bool ) -> bool {
213- let mut has_annotated_functions = false ;
214- for func in functions. iter_mut ( ) {
227+ /// Checks if an attribute is a custom attribute with a specific name
228+ fn is_custom_attribute ( attr : & SecondaryAttribute , attribute_name : & str ) -> bool {
229+ if let SecondaryAttribute :: Custom ( custom_attr) = attr {
230+ custom_attr. as_str ( ) == attribute_name
231+ } else {
232+ false
233+ }
234+ }
235+
236+ /// Determines if ast nodes are annotated with aztec attributes.
237+ /// For annotated functions it calls the `transform` function which will perform the required transformations.
238+ /// Returns true if an annotated node is found, false otherwise
239+ fn transform_module ( module : & mut ParsedModule , storage_defined : bool ) -> bool {
240+ let mut has_transformed_module = false ;
241+
242+ for structure in module. types . iter_mut ( ) {
243+ if structure. attributes . iter ( ) . any ( |attr| matches ! ( attr, SecondaryAttribute :: Event ) ) {
244+ module. impls . push ( generate_selector_impl ( structure) ) ;
245+ has_transformed_module = true ;
246+ }
247+ }
248+
249+ for func in module. functions . iter_mut ( ) {
215250 for secondary_attribute in func. def . attributes . secondary . clone ( ) {
216- if let SecondaryAttribute :: Custom ( custom_attribute) = secondary_attribute {
217- match custom_attribute. as_str ( ) {
218- "aztec(private)" => {
219- transform_function ( "Private" , func, storage_defined) ;
220- has_annotated_functions = true ;
221- }
222- "aztec(public)" => {
223- transform_function ( "Public" , func, storage_defined) ;
224- has_annotated_functions = true ;
225- }
226- _ => continue ,
227- }
251+ if is_custom_attribute ( & secondary_attribute, "aztec(private)" ) {
252+ transform_function ( "Private" , func, storage_defined) ;
253+ has_transformed_module = true ;
254+ } else if is_custom_attribute ( & secondary_attribute, "aztec(public)" ) {
255+ transform_function ( "Public" , func, storage_defined) ;
256+ has_transformed_module = true ;
228257 }
229258 }
230259 // Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract
231260 if storage_defined && func. def . is_unconstrained {
232261 transform_unconstrained ( func) ;
233- has_annotated_functions = true ;
262+ has_transformed_module = true ;
234263 }
235264 }
236- has_annotated_functions
265+ has_transformed_module
237266}
238267
239268/// If it does, it will insert the following things:
@@ -293,6 +322,141 @@ fn transform_unconstrained(func: &mut NoirFunction) {
293322 func. def . body . 0 . insert ( 0 , abstract_storage ( "Unconstrained" , true ) ) ;
294323}
295324
325+ fn collect_crate_structs ( crate_id : & CrateId , context : & Context ) -> Vec < StructId > {
326+ context
327+ . def_map ( crate_id)
328+ . expect ( "ICE: Missing crate in def_map" )
329+ . modules ( )
330+ . iter ( )
331+ . flat_map ( |( _, module) | {
332+ module. type_definitions ( ) . filter_map ( |typ| {
333+ if let ModuleDefId :: TypeId ( struct_id) = typ {
334+ Some ( struct_id)
335+ } else {
336+ None
337+ }
338+ } )
339+ } )
340+ . collect ( )
341+ }
342+
343+ /// Substitutes the signature literal that was introduced in the selector method previously with the actual signature.
344+ fn transform_event ( struct_id : StructId , interner : & mut NodeInterner ) {
345+ let selector_id =
346+ interner. lookup_method ( struct_id, "selector" ) . expect ( "Selector method not found" ) ;
347+ let selector_function = interner. function ( & selector_id) ;
348+
349+ let compute_selector_statement = interner. statement (
350+ selector_function
351+ . block ( interner)
352+ . statements ( )
353+ . first ( )
354+ . expect ( "Compute selector statement not found" ) ,
355+ ) ;
356+
357+ let compute_selector_expression = match compute_selector_statement {
358+ HirStatement :: Expression ( expression_id) => match interner. expression ( & expression_id) {
359+ HirExpression :: Call ( hir_call_expression) => Some ( hir_call_expression) ,
360+ _ => None ,
361+ } ,
362+ _ => None ,
363+ }
364+ . expect ( "Compute selector statement is not a call expression" ) ;
365+
366+ let first_arg_id = compute_selector_expression
367+ . arguments
368+ . first ( )
369+ . expect ( "Missing argument for compute selector" ) ;
370+
371+ match interner. expression ( first_arg_id) {
372+ HirExpression :: Literal ( HirLiteral :: Str ( signature) )
373+ if signature == SIGNATURE_PLACEHOLDER =>
374+ {
375+ let selector_literal_id = first_arg_id;
376+ let compute_selector_call_id = compute_selector_expression. func ;
377+
378+ let structure = interner. get_struct ( struct_id) ;
379+ let signature = event_signature ( & structure. borrow ( ) ) ;
380+ interner. update_expression ( * selector_literal_id, |expr| {
381+ * expr = HirExpression :: Literal ( HirLiteral :: Str ( signature. clone ( ) ) ) ;
382+ } ) ;
383+
384+ // Also update the type! It might have a different length now than the placeholder.
385+ interner. push_expr_type (
386+ selector_literal_id,
387+ Type :: String ( Box :: new ( Type :: Constant ( signature. len ( ) as u64 ) ) ) ,
388+ ) ;
389+ interner. push_expr_type (
390+ & compute_selector_call_id,
391+ Type :: Function (
392+ vec ! [ Type :: String ( Box :: new( Type :: TypeVariable (
393+ Shared :: new( TypeBinding :: Bound ( Type :: Constant ( signature. len( ) as u64 ) ) ) ,
394+ TypeVariableKind :: Normal ,
395+ ) ) ) ] ,
396+ Box :: new ( Type :: FieldElement ) ,
397+ Box :: new ( Type :: Unit ) ,
398+ ) ,
399+ ) ;
400+ }
401+ _ => unreachable ! ( "Signature placeholder literal does not match" ) ,
402+ }
403+ }
404+
405+ fn transform_events ( crate_id : & CrateId , context : & mut Context ) {
406+ for struct_id in collect_crate_structs ( crate_id, context) {
407+ let attributes = context. def_interner . struct_attributes ( & struct_id) ;
408+ if attributes. iter ( ) . any ( |attr| matches ! ( attr, SecondaryAttribute :: Event ) ) {
409+ transform_event ( struct_id, & mut context. def_interner ) ;
410+ }
411+ }
412+ }
413+
414+ const SIGNATURE_PLACEHOLDER : & str = "SIGNATURE_PLACEHOLDER" ;
415+
416+ /// Generates the impl for an event selector
417+ ///
418+ /// Inserts the following code:
419+ /// ```noir
420+ /// impl SomeStruct {
421+ /// fn selector() -> Field {
422+ /// aztec::oracle::compute_selector::compute_selector("SIGNATURE_PLACEHOLDER")
423+ /// }
424+ /// }
425+ /// ```
426+ ///
427+ /// This allows developers to emit events without having to write the signature of the event every time they emit it.
428+ /// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder.
429+ /// It'll get resolved after by transforming the HIR.
430+ fn generate_selector_impl ( structure : & mut NoirStruct ) -> TypeImpl {
431+ let struct_type = make_type ( UnresolvedTypeData :: Named ( path ( structure. name . clone ( ) ) , vec ! [ ] ) ) ;
432+
433+ let selector_fun_body = BlockExpression ( vec ! [ Statement :: Expression ( call(
434+ variable_path( chained_path!( "aztec" , "oracle" , "compute_selector" , "compute_selector" ) ) ,
435+ vec![ expression( ExpressionKind :: Literal ( Literal :: Str ( SIGNATURE_PLACEHOLDER . to_string( ) ) ) ) ] ,
436+ ) ) ] ) ;
437+
438+ let mut selector_fn_def = FunctionDefinition :: normal (
439+ & ident ( "selector" ) ,
440+ & vec ! [ ] ,
441+ & [ ] ,
442+ & selector_fun_body,
443+ & [ ] ,
444+ & FunctionReturnType :: Ty ( make_type ( UnresolvedTypeData :: FieldElement ) ) ,
445+ ) ;
446+
447+ selector_fn_def. is_public = true ;
448+
449+ // Seems to be necessary on contract modules
450+ selector_fn_def. return_visibility = Visibility :: Public ;
451+
452+ TypeImpl {
453+ object_type : struct_type,
454+ type_span : structure. span ,
455+ generics : vec ! [ ] ,
456+ methods : vec ! [ NoirFunction :: normal( selector_fn_def) ] ,
457+ }
458+ }
459+
296460/// Helper function that returns what the private context would look like in the ast
297461/// This should make it available to be consumed within aztec private annotated functions.
298462///
@@ -537,9 +701,9 @@ fn make_return_push_array(push_value: Expression) -> Statement {
537701/// `context.return_values.push_array({push_value}.serialize())`
538702fn make_struct_return_type ( expression : Expression ) -> Statement {
539703 let serialized_call = method_call (
540- expression. clone ( ) , // variable
541- "serialize" , // method name
542- vec ! [ ] , // args
704+ expression, // variable
705+ "serialize" , // method name
706+ vec ! [ ] , // args
543707 ) ;
544708 make_return_push_array ( serialized_call)
545709}
@@ -561,7 +725,7 @@ fn make_array_return_type(expression: Expression) -> Statement {
561725 vec ! [ inner_cast_expression] ,
562726 ) ) ;
563727
564- create_loop_over ( expression. clone ( ) , vec ! [ assignment] )
728+ create_loop_over ( expression, vec ! [ assignment] )
565729}
566730
567731/// Castable return type
@@ -572,7 +736,7 @@ fn make_array_return_type(expression: Expression) -> Statement {
572736/// ```
573737fn make_castable_return_type ( expression : Expression ) -> Statement {
574738 // Cast these types to a field before pushing
575- let cast_expression = cast ( expression. clone ( ) , UnresolvedTypeData :: FieldElement ) ;
739+ let cast_expression = cast ( expression, UnresolvedTypeData :: FieldElement ) ;
576740 make_return_push ( cast_expression)
577741}
578742
@@ -657,9 +821,9 @@ fn create_loop_over(var: Expression, loop_body: Vec<Statement>) -> Statement {
657821
658822 // `array.len()`
659823 let end_range_expression = method_call (
660- var. clone ( ) , // variable
661- "len" , // method name
662- vec ! [ ] , // args
824+ var, // variable
825+ "len" , // method name
826+ vec ! [ ] , // args
663827 ) ;
664828
665829 // What will be looped over
@@ -721,3 +885,37 @@ fn add_cast_to_hasher(identifier: &Ident) -> Statement {
721885 vec ! [ cast_operation] , // args
722886 ) )
723887}
888+
889+ /// Computes the aztec signature for a resolved type.
890+ fn signature_of_type ( typ : & Type ) -> String {
891+ match typ {
892+ Type :: Integer ( Signedness :: Signed , bit_size) => format ! ( "i{}" , bit_size) ,
893+ Type :: Integer ( Signedness :: Unsigned , bit_size) => format ! ( "u{}" , bit_size) ,
894+ Type :: FieldElement => "Field" . to_owned ( ) ,
895+ Type :: Bool => "bool" . to_owned ( ) ,
896+ Type :: Array ( len, typ) => {
897+ if let Type :: Constant ( len) = * * len {
898+ format ! ( "[{};{len}]" , signature_of_type( typ) )
899+ } else {
900+ unimplemented ! ( "Cannot generate signature for array with length type {:?}" , typ)
901+ }
902+ }
903+ Type :: Struct ( def, args) => {
904+ let fields = def. borrow ( ) . get_fields ( args) ;
905+ let fields = vecmap ( fields, |( _, typ) | signature_of_type ( & typ) ) ;
906+ format ! ( "({})" , fields. join( "," ) )
907+ }
908+ Type :: Tuple ( types) => {
909+ let fields = vecmap ( types, signature_of_type) ;
910+ format ! ( "({})" , fields. join( "," ) )
911+ }
912+ _ => unimplemented ! ( "Cannot generate signature for type {:?}" , typ) ,
913+ }
914+ }
915+
916+ /// Computes the signature for a resolved event type.
917+ /// It has the form 'EventName(Field,(Field),[u8;2])'
918+ fn event_signature ( event : & StructType ) -> String {
919+ let fields = vecmap ( event. get_fields ( & [ ] ) , |( _, typ) | signature_of_type ( & typ) ) ;
920+ format ! ( "{}({})" , event. name. 0 . contents, fields. join( "," ) )
921+ }
0 commit comments