diff --git a/compiler/compiler/src/compiler.rs b/compiler/compiler/src/compiler.rs index befe13fd1b..32dddf32a3 100644 --- a/compiler/compiler/src/compiler.rs +++ b/compiler/compiler/src/compiler.rs @@ -242,6 +242,23 @@ impl<'a> Compiler<'a> { Ok(()) } + /// Runs the destructuring pass. + pub fn destructuring_pass(&mut self, symbol_table: &SymbolTable) -> Result<()> { + self.ast = Destructurer::do_pass(( + std::mem::take(&mut self.ast), + symbol_table, + &self.type_table, + &self.node_builder, + &self.assigner, + ))?; + + if self.compiler_options.output.destructured_ast { + self.write_ast_to_json("destructured_ast.json")?; + } + + Ok(()) + } + /// Runs the function inlining pass. pub fn function_inlining_pass(&mut self, call_graph: &CallGraph) -> Result<()> { let ast = FunctionInliner::do_pass(( @@ -295,6 +312,8 @@ impl<'a> Compiler<'a> { self.flattening_pass(&st)?; + self.destructuring_pass(&st)?; + self.function_inlining_pass(&call_graph)?; self.dead_code_elimination_pass()?; diff --git a/compiler/compiler/src/options.rs b/compiler/compiler/src/options.rs index 174cfd7bf9..0e25f739d2 100644 --- a/compiler/compiler/src/options.rs +++ b/compiler/compiler/src/options.rs @@ -52,6 +52,8 @@ pub struct OutputOptions { pub ssa_ast: bool, /// If enabled writes the AST after flattening. pub flattened_ast: bool, + /// If enabled writes the AST after destructuring. + pub destructured_ast: bool, /// If enabled writes the AST after inlining. pub inlined_ast: bool, /// If enabled writes the AST after dead code elimination. diff --git a/compiler/compiler/tests/compile.rs b/compiler/compiler/tests/compile.rs index 5ce8896936..526a6a85c8 100644 --- a/compiler/compiler/tests/compile.rs +++ b/compiler/compiler/tests/compile.rs @@ -66,6 +66,7 @@ struct CompileOutput { pub unrolled_ast: String, pub ssa_ast: String, pub flattened_ast: String, + pub destructured_ast: String, pub inlined_ast: String, pub dce_ast: String, pub bytecode: String, @@ -95,6 +96,7 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result Result Result Result Result Result (String, String, String, String, String, String) { +pub fn hash_asts() -> (String, String, String, String, String, String, String) { let initial_ast = hash_file("/tmp/output/test.initial_ast.json"); let unrolled_ast = hash_file("/tmp/output/test.unrolled_ast.json"); let ssa_ast = hash_file("/tmp/output/test.ssa_ast.json"); let flattened_ast = hash_file("/tmp/output/test.flattened_ast.json"); + let destructured_ast = hash_file("/tmp/output/test.destructured_ast.json"); let inlined_ast = hash_file("/tmp/output/test.inlined_ast.json"); let dce_ast = hash_file("/tmp/output/test.dce_ast.json"); - (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) + (initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast) } pub fn hash_symbol_tables() -> (String, String, String) { @@ -236,6 +237,8 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result { type AdditionalOutput = Vec; diff --git a/compiler/passes/src/lib.rs b/compiler/passes/src/lib.rs index 41d218727c..23a35a251f 100644 --- a/compiler/passes/src/lib.rs +++ b/compiler/passes/src/lib.rs @@ -26,6 +26,9 @@ pub use common::*; pub mod dead_code_elimination; pub use dead_code_elimination::*; +pub mod destructuring; +pub use destructuring::*; + pub mod flattening; pub use flattening::*; diff --git a/compiler/passes/src/type_checking/checker.rs b/compiler/passes/src/type_checking/checker.rs index 18260776c5..55cdf2ecfa 100644 --- a/compiler/passes/src/type_checking/checker.rs +++ b/compiler/passes/src/type_checking/checker.rs @@ -1060,28 +1060,28 @@ impl<'a> TypeChecker<'a> { /// Emits an error if the type or its constituent types is not valid. pub(crate) fn assert_type_is_valid(&self, type_: &Type, span: Span) -> bool { - let mut is_defined = true; + let mut is_valid = true; match type_ { // String types are temporarily disabled. Type::String => { - is_defined = false; + is_valid = false; self.emit_err(TypeCheckerError::strings_are_not_supported(span)); } // Check that the named composite type has been defined. Type::Identifier(identifier) if self.symbol_table.borrow().lookup_struct(identifier.name).is_none() => { - is_defined = false; + is_valid = false; self.emit_err(TypeCheckerError::undefined_type(identifier.name, span)); } // Check that the constituent types of the tuple are valid. Type::Tuple(tuple_type) => { for type_ in tuple_type.elements().iter() { - is_defined &= self.assert_type_is_valid(type_, span) + is_valid &= self.assert_type_is_valid(type_, span) } } // Check that the constituent types of mapping are valid. Type::Mapping(mapping_type) => { - is_defined &= self.assert_type_is_valid(&mapping_type.key, span); - is_defined &= self.assert_type_is_valid(&mapping_type.value, span); + is_valid &= self.assert_type_is_valid(&mapping_type.key, span); + is_valid &= self.assert_type_is_valid(&mapping_type.value, span); } // Check that the array element types are valid. Type::Array(array_type) => { @@ -1093,11 +1093,15 @@ impl<'a> TypeChecker<'a> { self.emit_err(TypeCheckerError::array_too_large(length, Testnet3::MAX_ARRAY_ELEMENTS, span)) } } - is_defined &= self.assert_type_is_valid(array_type.element_type(), span) + // Check that the array element type is not a tuple. + if let Type::Tuple(_) = array_type.element_type() { + self.emit_err(TypeCheckerError::array_element_cannot_be_tuple(span)) + } + is_valid &= self.assert_type_is_valid(array_type.element_type(), span) } _ => {} // Do nothing. } - is_defined + is_valid } /// Emits an error if the type is not a mapping. diff --git a/errors/src/errors/type_checker/type_checker_error.rs b/errors/src/errors/type_checker/type_checker_error.rs index c013b597d7..c66e4338bd 100644 --- a/errors/src/errors/type_checker/type_checker_error.rs +++ b/errors/src/errors/type_checker/type_checker_error.rs @@ -698,4 +698,11 @@ create_messages!( msg: format!("An array cannot have more than {max} elements, found one with {size} elements"), help: None, } + + @formatted + array_element_cannot_be_tuple { + args: (), + msg: format!("An array cannot have a tuple as an element type"), + help: None, + } ); diff --git a/leo/cli/commands/build.rs b/leo/cli/commands/build.rs index abbace59f0..4c5775f30e 100644 --- a/leo/cli/commands/build.rs +++ b/leo/cli/commands/build.rs @@ -53,6 +53,7 @@ impl From for CompilerOptions { unrolled_ast: options.enable_unrolled_ast_snapshot, ssa_ast: options.enable_ssa_ast_snapshot, flattened_ast: options.enable_flattened_ast_snapshot, + destructured_ast: options.enable_destructured_ast_snapshot, inlined_ast: options.enable_inlined_ast_snapshot, dce_ast: options.enable_dce_ast_snapshot, }, @@ -63,6 +64,7 @@ impl From for CompilerOptions { out_options.output.unrolled_ast = true; out_options.output.ssa_ast = true; out_options.output.flattened_ast = true; + out_options.output.destructured_ast = true; out_options.output.inlined_ast = true; out_options.output.dce_ast = true; } diff --git a/leo/cli/commands/mod.rs b/leo/cli/commands/mod.rs index b6ae036af7..463f06782f 100644 --- a/leo/cli/commands/mod.rs +++ b/leo/cli/commands/mod.rs @@ -145,6 +145,8 @@ pub struct BuildOptions { pub enable_ssa_ast_snapshot: bool, #[clap(long, help = "Writes AST snapshot of the flattened AST.")] pub enable_flattened_ast_snapshot: bool, + #[clap(long, help = "Writes AST snapshot of the destructured AST.")] + pub enable_destructured_ast_snapshot: bool, #[clap(long, help = "Writes AST snapshot of the inlined AST.")] pub enable_inlined_ast_snapshot: bool, #[clap(long, help = "Writes AST snapshot of the dead code eliminated (DCE) AST.")] diff --git a/tests/expectations/compiler/statements/mutate.out b/tests/expectations/compiler/statements/mutate.out index 43313b8c2a..048cd820ba 100644 --- a/tests/expectations/compiler/statements/mutate.out +++ b/tests/expectations/compiler/statements/mutate.out @@ -7,9 +7,9 @@ outputs: unrolled_symbol_table: 583ed0adba552a2abfd5927ecebd5c20fa943380c8d0b45e00a33e7c03de3300 initial_ast: 34a1afc5b8e850d3ca1fffbabcfb5ea6bacea945b20628868784f8a5b4140854 unrolled_ast: 34a1afc5b8e850d3ca1fffbabcfb5ea6bacea945b20628868784f8a5b4140854 - ssa_ast: df3ca19301a9f38667d034bfdb7ae892292ffa822eaa491e72cf75730c6355be - flattened_ast: ede7aeb18282c9c9b48b3b60212fea5fb8886321d4a0742e96b2b640129b0463 - inlined_ast: ede7aeb18282c9c9b48b3b60212fea5fb8886321d4a0742e96b2b640129b0463 - dce_ast: a53f58149602de59ef43a6987aac4e8fbec357995ca89e3d5093aedce9af73f4 + ssa_ast: bb6b1c14a80f6c148484d7e330bac0d6591163a2a98d0787f530ae5d28dcaed3 + flattened_ast: 4752b34e62f67e177c91688f03fe3f1f827420b09e586d5daccfbe19beb5ab18 + inlined_ast: 4752b34e62f67e177c91688f03fe3f1f827420b09e586d5daccfbe19beb5ab18 + dce_ast: 4d25901f577f93b7a8ab66c82774d538f648eb0d7675c3d4513e558f8a19c967 bytecode: 4f4c5c377fed78feede8ee754c9f838f449f8d00cf771b2bb65884e876f90b7e warnings: "" diff --git a/tests/test-framework/benches/leo_compiler.rs b/tests/test-framework/benches/leo_compiler.rs index 6fe06a33ab..f82163c868 100644 --- a/tests/test-framework/benches/leo_compiler.rs +++ b/tests/test-framework/benches/leo_compiler.rs @@ -41,6 +41,8 @@ enum BenchMode { Ssa, /// Benchmarks flattening. Flatten, + /// Benchmarks destructuring. + Destructure, /// Benchmarks function inlining. Inline, /// Benchmarks dead code elimination. @@ -98,6 +100,7 @@ fn new_compiler(handler: &Handler) -> Compiler<'_> { unrolled_ast: false, ssa_ast: false, flattened_ast: false, + destructured_ast: false, inlined_ast: false, dce_ast: false, }, @@ -125,6 +128,7 @@ impl Sample { BenchMode::Unroll => self.bench_loop_unroller(c), BenchMode::Ssa => self.bench_ssa(c), BenchMode::Flatten => self.bench_flattener(c), + BenchMode::Destructure => self.bench_destructurer(c), BenchMode::Inline => self.bench_inline(c), BenchMode::Dce => self.bench_dce(c), BenchMode::Codegen => self.bench_codegen(c), @@ -229,6 +233,22 @@ impl Sample { }); } + fn bench_destructurer(&self, c: &mut Criterion) { + self.bencher_after_parse(c, "destructurer pass", |mut compiler| { + let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table"); + let (symbol_table, _struct_graph, _call_graph) = + compiler.type_checker_pass(symbol_table).expect("failed to run type check pass"); + let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass"); + compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass"); + compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass"); + let start = Instant::now(); + let out = compiler.destructuring_pass(&symbol_table); + let time = start.elapsed(); + out.expect("failed to run destructurer pass"); + time + }); + } + fn bench_inline(&self, c: &mut Criterion) { self.bencher_after_parse(c, "inliner pass", |mut compiler| { let symbol_table = compiler.symbol_table_pass().expect("failed to generate symbol table"); @@ -237,6 +257,7 @@ impl Sample { let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass"); compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass"); compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass"); + compiler.destructuring_pass(&symbol_table).expect("failed to run destructurer pass"); let start = Instant::now(); let out = compiler.function_inlining_pass(&call_graph); let time = start.elapsed(); @@ -253,6 +274,7 @@ impl Sample { let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass"); compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass"); compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass"); + compiler.destructuring_pass(&symbol_table).expect("failed to run destructurer pass"); compiler.function_inlining_pass(&call_graph).expect("failed to run inliner pass"); let start = Instant::now(); let out = compiler.dead_code_elimination_pass(); @@ -270,6 +292,7 @@ impl Sample { let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass"); compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass"); compiler.flattening_pass(&symbol_table).expect("failed to run flattener pass"); + compiler.destructuring_pass(&symbol_table).expect("failed to run destructurer pass"); compiler.function_inlining_pass(&call_graph).expect("failed to run inliner pass"); compiler.dead_code_elimination_pass().expect("failed to run dce pass"); let start = Instant::now(); @@ -291,6 +314,7 @@ impl Sample { let symbol_table = compiler.loop_unrolling_pass(symbol_table).expect("failed to run loop unrolling pass"); compiler.static_single_assignment_pass(&symbol_table).expect("failed to run ssa pass"); compiler.flattening_pass(&symbol_table).expect("failed to run flattening pass"); + compiler.destructuring_pass(&symbol_table).expect("failed to run destructuring pass"); compiler.function_inlining_pass(&call_graph).expect("failed to run function inlining pass"); compiler.dead_code_elimination_pass().expect("failed to run dce pass"); compiler @@ -315,6 +339,7 @@ bench!(bench_type, BenchMode::Type); bench!(bench_unroll, BenchMode::Unroll); bench!(bench_ssa, BenchMode::Ssa); bench!(bench_flatten, BenchMode::Flatten); +bench!(bench_destructure, BenchMode::Destructure); bench!(bench_inline, BenchMode::Inline); bench!(bench_dce, BenchMode::Dce); bench!(bench_codegen, BenchMode::Codegen); @@ -330,6 +355,7 @@ criterion_group!( bench_unroll, bench_ssa, bench_flatten, + bench_destructure, bench_inline, bench_dce, bench_codegen,