@@ -18,12 +18,13 @@ use noirc_evaluator::ssa::{SsaLogging, SsaProgramArtifact};
18
18
use noirc_frontend:: debug:: build_debug_crate_file;
19
19
use noirc_frontend:: elaborator:: { FrontendOptions , UnstableFeature } ;
20
20
use noirc_frontend:: hir:: Context ;
21
- use noirc_frontend:: hir:: def_map:: { Contract , CrateDefMap } ;
21
+ use noirc_frontend:: hir:: def_map:: { CrateDefMap , ModuleDefId , ModuleId } ;
22
22
use noirc_frontend:: monomorphization:: {
23
23
errors:: MonomorphizationError , monomorphize, monomorphize_debug,
24
24
} ;
25
- use noirc_frontend:: node_interner:: FuncId ;
25
+ use noirc_frontend:: node_interner:: { FuncId , GlobalId , TypeId } ;
26
26
use noirc_frontend:: token:: SecondaryAttribute ;
27
+ use std:: collections:: HashMap ;
27
28
use std:: path:: Path ;
28
29
use tracing:: info;
29
30
@@ -428,39 +429,42 @@ pub fn compile_contract(
428
429
) -> CompilationResult < CompiledContract > {
429
430
let ( _, warnings) = check_crate ( context, crate_id, options) ?;
430
431
431
- // TODO: We probably want to error if contracts is empty
432
- let contracts = context . get_all_contracts ( & crate_id ) ;
432
+ let def_map = context . def_map ( & crate_id ) . expect ( "The local crate should be analyzed already" ) ;
433
+ let mut contracts = def_map . get_all_contracts ( ) ;
433
434
434
- let mut compiled_contracts = vec ! [ ] ;
435
- let mut errors = warnings;
436
-
437
- if contracts. len ( ) > 1 {
435
+ let Some ( ( module_id, name) ) = contracts. next ( ) else {
438
436
let err = CustomDiagnostic :: from_message (
439
- "Packages are limited to a single contract" ,
437
+ "cannot compile crate into a contract as it does not contain any contracts " ,
440
438
FileId :: default ( ) ,
441
439
) ;
442
440
return Err ( vec ! [ err] ) ;
443
- } else if contracts. is_empty ( ) {
441
+ } ;
442
+
443
+ if contracts. next ( ) . is_some ( ) {
444
444
let err = CustomDiagnostic :: from_message (
445
- "cannot compile crate into a contract as it does not contain any contracts " ,
445
+ "Packages are limited to a single contract " ,
446
446
FileId :: default ( ) ,
447
447
) ;
448
448
return Err ( vec ! [ err] ) ;
449
- } ;
449
+ }
450
+ drop ( contracts) ;
450
451
451
- for contract in contracts {
452
- match compile_contract_inner ( context, contract, options) {
453
- Ok ( contract) => compiled_contracts. push ( contract) ,
454
- Err ( mut more_errors) => errors. append ( & mut more_errors) ,
452
+ let module_id = ModuleId { krate : crate_id, local_id : module_id } ;
453
+ let contract = read_contract ( context, module_id, name) ;
454
+
455
+ let mut errors = warnings;
456
+
457
+ let compiled_contract = match compile_contract_inner ( context, contract, options) {
458
+ Ok ( contract) => contract,
459
+ Err ( mut more_errors) => {
460
+ errors. append ( & mut more_errors) ;
461
+ return Err ( errors) ;
455
462
}
456
- }
463
+ } ;
457
464
458
465
if has_errors ( & errors, options. deny_warnings ) {
459
466
Err ( errors)
460
467
} else {
461
- assert_eq ! ( compiled_contracts. len( ) , 1 ) ;
462
- let compiled_contract = compiled_contracts. remove ( 0 ) ;
463
-
464
468
if options. print_acir {
465
469
for contract_function in & compiled_contract. functions {
466
470
if let Some ( ref name) = options. show_contract_fn {
@@ -480,6 +484,52 @@ pub fn compile_contract(
480
484
}
481
485
}
482
486
487
+ /// Return a Vec of all `contract` declarations in the source code and the functions they contain
488
+ fn read_contract ( context : & Context , module_id : ModuleId , name : String ) -> Contract {
489
+ let module = context. module ( module_id) ;
490
+
491
+ let functions = module
492
+ . value_definitions ( )
493
+ . filter_map ( |id| {
494
+ id. as_function ( ) . map ( |function_id| {
495
+ let attrs = context. def_interner . function_attributes ( & function_id) ;
496
+ let is_entry_point = attrs. is_contract_entry_point ( ) ;
497
+ ContractFunctionMeta { function_id, is_entry_point }
498
+ } )
499
+ } )
500
+ . collect ( ) ;
501
+
502
+ let mut outputs = ContractOutputs { structs : HashMap :: new ( ) , globals : HashMap :: new ( ) } ;
503
+
504
+ context. def_interner . get_all_globals ( ) . iter ( ) . for_each ( |global_info| {
505
+ context. def_interner . global_attributes ( & global_info. id ) . iter ( ) . for_each ( |attr| {
506
+ if let SecondaryAttribute :: Abi ( tag) = attr {
507
+ if let Some ( tagged) = outputs. globals . get_mut ( tag) {
508
+ tagged. push ( global_info. id ) ;
509
+ } else {
510
+ outputs. globals . insert ( tag. to_string ( ) , vec ! [ global_info. id] ) ;
511
+ }
512
+ }
513
+ } ) ;
514
+ } ) ;
515
+
516
+ module. type_definitions ( ) . for_each ( |id| {
517
+ if let ModuleDefId :: TypeId ( struct_id) = id {
518
+ context. def_interner . type_attributes ( & struct_id) . iter ( ) . for_each ( |attr| {
519
+ if let SecondaryAttribute :: Abi ( tag) = attr {
520
+ if let Some ( tagged) = outputs. structs . get_mut ( tag) {
521
+ tagged. push ( struct_id) ;
522
+ } else {
523
+ outputs. structs . insert ( tag. to_string ( ) , vec ! [ struct_id] ) ;
524
+ }
525
+ }
526
+ } ) ;
527
+ }
528
+ } ) ;
529
+
530
+ Contract { name, functions, outputs }
531
+ }
532
+
483
533
/// True if there are (non-warning) errors present and we should halt compilation
484
534
fn has_errors ( errors : & [ CustomDiagnostic ] , deny_warnings : bool ) -> bool {
485
535
if deny_warnings { !errors. is_empty ( ) } else { errors. iter ( ) . any ( |error| error. is_error ( ) ) }
@@ -721,3 +771,29 @@ pub fn compile_no_check(
721
771
brillig_names,
722
772
} )
723
773
}
774
+
775
+ /// Specifies a contract function and extra metadata that
776
+ /// one can use when processing a contract function.
777
+ ///
778
+ /// One of these is whether the contract function is an entry point.
779
+ /// The caller should only type-check these functions and not attempt
780
+ /// to create a circuit for them.
781
+ struct ContractFunctionMeta {
782
+ function_id : FuncId ,
783
+ /// Indicates whether the function is an entry point
784
+ is_entry_point : bool ,
785
+ }
786
+
787
+ struct ContractOutputs {
788
+ structs : HashMap < String , Vec < TypeId > > ,
789
+ globals : HashMap < String , Vec < GlobalId > > ,
790
+ }
791
+
792
+ /// A 'contract' in Noir source code with a given name, functions and events.
793
+ /// This is not an AST node, it is just a convenient form to return for CrateDefMap::get_all_contracts.
794
+ struct Contract {
795
+ /// To keep `name` semi-unique, it is prefixed with the names of parent modules via CrateDefMap::get_module_path
796
+ name : String ,
797
+ functions : Vec < ContractFunctionMeta > ,
798
+ outputs : ContractOutputs ,
799
+ }
0 commit comments