diff --git a/compiler/passes/src/processing_async/ast.rs b/compiler/passes/src/processing_async/ast.rs index 292e32fb25f..fbf93ba7383 100644 --- a/compiler/passes/src/processing_async/ast.rs +++ b/compiler/passes/src/processing_async/ast.rs @@ -17,22 +17,29 @@ use super::ProcessingAsyncVisitor; use crate::{CompilerState, Replacer}; use indexmap::{IndexMap, IndexSet}; +use itertools::Itertools; use leo_ast::{ AstReconstructor, AstVisitor, AsyncExpression, Block, CallExpression, + Composite, Expression, Function, Identifier, Input, IterationStatement, Location, + Member, + MemberAccess, + Mode, Node, Path, ProgramVisitor, Statement, + StructExpression, + StructVariableInitializer, TupleAccess, TupleExpression, TupleType, @@ -77,6 +84,166 @@ impl AstVisitor for SymbolAccessCollector<'_> { impl ProgramVisitor for SymbolAccessCollector<'_> {} +/// Bundle inputs into structs when they exceed max_inputs. +/// Returns: (new_inputs, new_arguments, synthetic_structs, replacements_for_bundled) +/// where replacements_for_bundled maps (Symbol, Option) to the path to access the field in the bundled struct. +fn bundle_inputs_into_structs( + inputs: Vec, + arguments: Vec, + input_metadata: Vec<(Symbol, Option)>, + max_inputs: usize, + function_name: Symbol, + node_builder: &mut leo_ast::NodeBuilder, + assigner: &mut crate::Assigner, +) -> (Vec, Vec, Vec, IndexMap<(Symbol, Option), Expression>) { + if inputs.len() <= max_inputs { + return (inputs, arguments, vec![], IndexMap::new()); + } + + let mut new_inputs = Vec::new(); + let mut new_arguments = Vec::new(); + let mut synthetic_structs = Vec::new(); + let mut replacements = IndexMap::new(); + + let total_inputs = inputs.len(); + let bundle_capacity = max_inputs; // 16 is max + let num_bundles = (total_inputs - max_inputs + bundle_capacity - 2) / (bundle_capacity - 1); + let unbundled_count = max_inputs - num_bundles; + + // Keep the first unbundled_count inputs as-is + for (input, arg) in inputs.iter().zip(arguments.iter()).take(unbundled_count) { + new_inputs.push(input.clone()); + new_arguments.push(arg.clone()); + } + + // Bundle remaining inputs into struct(s) + let remaining_inputs: Vec<_> = inputs.into_iter().skip(unbundled_count).collect(); + let remaining_arguments: Vec<_> = arguments.into_iter().skip(unbundled_count).collect(); + let remaining_metadata: Vec<_> = input_metadata.into_iter().skip(unbundled_count).collect(); + + for ((chunk_inputs, chunk_args), chunk_metadata) in remaining_inputs + .chunks(bundle_capacity) + .zip(remaining_arguments.chunks(bundle_capacity)) + .zip(remaining_metadata.chunks(bundle_capacity)) + { + // Generate a unique struct name + let sanitized_fn_name = Symbol::intern(&function_name.to_string().replace('$', "_")); + let struct_name = assigner.unique_symbol(sanitized_fn_name, "_bundle"); + + let struct_identifier = Identifier { + name: struct_name, + span: Span::default(), + id: node_builder.next_id(), + }; + + // Create structt members from inputs + let members: Vec = chunk_inputs + .iter() + .enumerate() + .map(|(i, input)| Member { + mode: Mode::None, + identifier: Identifier { + name: Symbol::intern(&format!("field_{}", i)), + span: Span::default(), + id: node_builder.next_id(), + }, + type_: input.type_.clone(), + span: Span::default(), + id: node_builder.next_id(), + }) + .collect(); + + // Create the synthetic struct definition + let composite = Composite { + identifier: struct_identifier, + const_parameters: vec![], + members: members.clone(), + external: None, + is_record: false, + span: Span::default(), + id: node_builder.next_id(), + }; + + synthetic_structs.push(composite); + + // Create an input parameter for this bundled struct + // Note: program should be None for local structs - the program context comes from scope during type checking + let composite_type = Type::Composite(leo_ast::CompositeType { + path: Path::from(struct_identifier).into_absolute(), + const_arguments: vec![], + program: None, + }); + + let param_name = assigner.unique_symbol(struct_name, "_param"); + + let bundle_input = Input { + identifier: Identifier { + name: param_name, + span: Span::default(), + id: node_builder.next_id(), + }, + mode: Mode::None, + type_: composite_type, + span: Span::default(), + id: node_builder.next_id(), + }; + + new_inputs.push(bundle_input); + + // Create a struct initialization expression as the argument + let struct_members: Vec = chunk_inputs + .iter() + .zip(chunk_args.iter()) + .enumerate() + .map(|(i, (_, arg))| StructVariableInitializer { + identifier: Identifier { + name: Symbol::intern(&format!("field_{}", i)), + span: Span::default(), + id: node_builder.next_id(), + }, + expression: Some(arg.clone()), + span: Span::default(), + id: node_builder.next_id(), + }) + .collect(); + + let struct_init = StructExpression { + path: Path::from(struct_identifier).into_absolute(), + const_arguments: vec![], + members: struct_members, + span: Span::default(), + id: node_builder.next_id(), + }; + + new_arguments.push(struct_init.into()); + + // Track replacements, the original input names should now be accessed via struct.field_N + for (i, (original_symbol, original_index)) in chunk_metadata.iter().enumerate() { + // Create a member access expression: param_name.field_i + let member_access = MemberAccess { + inner: Path::from(Identifier { + name: param_name, + span: Span::default(), + id: node_builder.next_id(), + }) + .into(), + name: Identifier { + name: Symbol::intern(&format!("field_{}", i)), + span: Span::default(), + id: node_builder.next_id(), + }, + span: Span::default(), + id: node_builder.next_id(), + } + .into(); + + replacements.insert((*original_symbol, *original_index), member_access); + } + } + + (new_inputs, new_arguments, synthetic_structs, replacements) +} + impl AstReconstructor for ProcessingAsyncVisitor<'_> { type AdditionalInput = (); type AdditionalOutput = (); @@ -245,7 +412,7 @@ impl AstReconstructor for ProcessingAsyncVisitor<'_> { }; // Step 3: Resolve symbol accesses into inputs and call arguments - let (inputs, arguments): (Vec<_>, Vec<_>) = access_collector + let inputs_and_args_with_metadata: Vec<_> = access_collector .symbol_accesses .iter() .filter_map(|(path, index)| { @@ -265,12 +432,42 @@ impl AstReconstructor for ProcessingAsyncVisitor<'_> { // All other variables become parameters to the async function being built. let var = self.state.symbol_table.lookup_local(local_var_name)?; - Some(make_inputs_and_arguments(self, local_var_name, &var.type_, *index)) + let inputs_and_args = make_inputs_and_arguments(self, local_var_name, &var.type_, *index); + + // For each (Input, Expression) pair, attach metadata (symbol, index) + Some( + inputs_and_args + .into_iter() + .map(|(input, arg)| (input, arg, (local_var_name, *index))) + .collect::>() + ) }) .flatten() - .unzip(); + .collect(); + + // Separate into parallel vectors + let (inputs, arguments, input_metadata): (Vec<_>, Vec<_>, Vec<_>) = + inputs_and_args_with_metadata.into_iter() + .map(|(input, arg, metadata)| (input, arg, metadata)) + .multiunzip(); - // Step 4: Replacement logic used to patch the async block + // Step 4: Bundle inputs if necessary + let (final_inputs, final_arguments, synthetic_structs, bundle_replacements) = bundle_inputs_into_structs( + inputs, + arguments, + input_metadata, + self.max_inputs, + finalize_fn_name, + &mut self.state.node_builder, + &mut self.state.assigner, + ); + + // If there are bundle replacements, merge them into the main replacements map + for (key, expr) in bundle_replacements { + replacements.insert(key, expr); + } + + // Step 5: Reconstruct the block with replaced references let replace_expr = |expr: &Expression| -> Expression { match expr { Expression::Path(path) => { @@ -292,17 +489,13 @@ impl AstReconstructor for ProcessingAsyncVisitor<'_> { } }; - // Step 5: Reconstruct the block with replaced references let mut replacer = Replacer::new(replace_expr, true /* refresh IDs */, self.state); let new_block = replacer.reconstruct_block(input.block.clone()).0; - // Ensure we're not trying to capture too many variables - if inputs.len() > self.max_inputs { - self.state.handler.emit_err(leo_errors::StaticAnalyzerError::async_block_capturing_too_many_vars( - inputs.len(), - self.max_inputs, - input.span, - )); + // Register synthetic structs + for composite in synthetic_structs { + let struct_name = composite.name(); + self.synthetic_structs.push((struct_name, composite)); } // Step 6: Define the new async function @@ -311,7 +504,7 @@ impl AstReconstructor for ProcessingAsyncVisitor<'_> { variant: Variant::AsyncFunction, identifier: make_identifier(self, finalize_fn_name), const_parameters: vec![], - input: inputs, + input: final_inputs.clone(), output: vec![], // `async function`s can't have returns output_type: Type::Unit, // Always the case for `async function`s block: new_block, @@ -333,7 +526,7 @@ impl AstReconstructor for ProcessingAsyncVisitor<'_> { self.state.node_builder.next_id(), ), const_arguments: vec![], - arguments, + arguments: final_arguments, program: Some(self.current_program), span: input.span, id: self.state.node_builder.next_id(), diff --git a/compiler/passes/src/processing_async/mod.rs b/compiler/passes/src/processing_async/mod.rs index eaea9c93692..a9d67474e2d 100644 --- a/compiler/passes/src/processing_async/mod.rs +++ b/compiler/passes/src/processing_async/mod.rs @@ -76,6 +76,7 @@ impl Pass for ProcessingAsync { current_program: Symbol::intern(""), current_function: Symbol::intern(""), new_async_functions: Vec::new(), + synthetic_structs: Vec::new(), modified: false, }; ast.ast = visitor.reconstruct_program(ast.ast); diff --git a/compiler/passes/src/processing_async/program.rs b/compiler/passes/src/processing_async/program.rs index 0c27914216e..5e0e6d62c44 100644 --- a/compiler/passes/src/processing_async/program.rs +++ b/compiler/passes/src/processing_async/program.rs @@ -49,9 +49,15 @@ impl ProgramReconstructor for ProcessingAsyncVisitor<'_> { // Now append all newly created `async` functions. This ensures transition functions still show up before all other functions. reconstructed_functions.append(&mut self.new_async_functions); + // Reconstruct existing structs and append synthetic structs created for bundling captured variables + let mut all_structs: Vec<_> = + input.structs.into_iter().map(|(id, def)| (id, self.reconstruct_struct(def))).collect(); + + all_structs.append(&mut self.synthetic_structs); + ProgramScope { program_id: input.program_id, - structs: input.structs.into_iter().map(|(id, def)| (id, self.reconstruct_struct(def))).collect(), + structs: all_structs, mappings: input.mappings.into_iter().map(|(id, mapping)| (id, self.reconstruct_mapping(mapping))).collect(), functions: reconstructed_functions, constructor: input.constructor, diff --git a/compiler/passes/src/processing_async/visitor.rs b/compiler/passes/src/processing_async/visitor.rs index 11828cc053b..2b56749282c 100644 --- a/compiler/passes/src/processing_async/visitor.rs +++ b/compiler/passes/src/processing_async/visitor.rs @@ -16,7 +16,7 @@ use crate::CompilerState; -use leo_ast::{Function, NodeID}; +use leo_ast::{Composite, Function, NodeID}; use leo_span::Symbol; pub struct ProcessingAsyncVisitor<'a> { @@ -30,6 +30,8 @@ pub struct ProcessingAsyncVisitor<'a> { pub current_function: Symbol, /// A map of reconstructed functions in the current program scope. pub new_async_functions: Vec<(Symbol, Function)>, + /// Synthetic structs created to bundle captured variables when an async block captures more than MAX_INPUTS. + pub synthetic_structs: Vec<(Symbol, Composite)>, /// Indicates whether this pass actually processed any async blocks. pub modified: bool, } diff --git a/tests/expectations/compiler/async_blocks/capture_complex_types_bundled.out b/tests/expectations/compiler/async_blocks/capture_complex_types_bundled.out new file mode 100644 index 00000000000..423586d9b8f --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_complex_types_bundled.out @@ -0,0 +1,75 @@ +program test.aleo; + +struct Position: + x as u32; + y as u32; + +struct Entity: + pos as Position; + health as u32; + +struct main_0_bundle1: + field_0 as u32; + field_1 as u32; + field_2 as u32; + field_3 as u32; + field_4 as u32; + field_5 as u32; + field_6 as u32; + field_7 as u32; + +function main: + cast 10u32 20u32 into r0 as Position; + cast r0 100u32 into r1 as Entity; + cast 30u32 40u32 into r2 as Position; + cast r2 80u32 into r3 as Entity; + cast 1u32 2u32 3u32 into r4 as [u32; 3u32]; + cast 4u32 5u32 6u32 into r5 as [u32; 3u32]; + cast 7u32 8u32 9u32 into r6 as [u32; 3u32]; + cast 10u32 11u32 12u32 13u32 14u32 15u32 16u32 17u32 into r7 as main_0_bundle1; + async main r1 r3 r4 r5 r6 0u32 1u32 2u32 3u32 4u32 5u32 6u32 7u32 8u32 9u32 r7 into r8; + output r8 as test.aleo/main.future; + +finalize main: + input r0 as Entity.public; + input r1 as Entity.public; + input r2 as [u32; 3u32].public; + input r3 as [u32; 3u32].public; + input r4 as [u32; 3u32].public; + input r5 as u32.public; + input r6 as u32.public; + input r7 as u32.public; + input r8 as u32.public; + input r9 as u32.public; + input r10 as u32.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as main_0_bundle1.public; + assert.eq r0.pos.x 10u32; + assert.eq r0.pos.y 20u32; + assert.eq r0.health 100u32; + assert.eq r1.pos.x 30u32; + assert.eq r1.health 80u32; + assert.eq r2[0u32] 1u32; + assert.eq r3[1u32] 5u32; + assert.eq r4[2u32] 9u32; + add r5 r6 into r16; + add r16 r7 into r17; + add r17 r8 into r18; + add r18 r9 into r19; + add r19 r10 into r20; + add r20 r11 into r21; + add r21 r12 into r22; + add r22 r13 into r23; + add r23 r14 into r24; + add r24 r15.field_0 into r25; + add r25 r15.field_1 into r26; + add r26 r15.field_2 into r27; + add r27 r15.field_3 into r28; + add r28 r15.field_4 into r29; + add r29 r15.field_5 into r30; + add r30 r15.field_6 into r31; + add r31 r15.field_7 into r32; + assert.eq r32 153u32; diff --git a/tests/expectations/compiler/async_blocks/capture_multiple_async_bundled.out b/tests/expectations/compiler/async_blocks/capture_multiple_async_bundled.out new file mode 100644 index 00000000000..2ce0a089e11 --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_multiple_async_bundled.out @@ -0,0 +1,91 @@ +program test.aleo; + +struct first_0_bundle1: + field_0 as u32; + field_1 as u32; + +struct second_3_bundle4: + field_0 as u32; + field_1 as u32; + field_2 as u32; + +function first: + cast 15u32 16u32 into r0 as first_0_bundle1; + async first 0u32 1u32 2u32 3u32 4u32 5u32 6u32 7u32 8u32 9u32 10u32 11u32 12u32 13u32 14u32 r0 into r1; + output r1 as test.aleo/first.future; + +finalize first: + input r0 as u32.public; + input r1 as u32.public; + input r2 as u32.public; + input r3 as u32.public; + input r4 as u32.public; + input r5 as u32.public; + input r6 as u32.public; + input r7 as u32.public; + input r8 as u32.public; + input r9 as u32.public; + input r10 as u32.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as first_0_bundle1.public; + add r0 r1 into r16; + add r16 r2 into r17; + add r17 r3 into r18; + add r18 r4 into r19; + add r19 r5 into r20; + add r20 r6 into r21; + add r21 r7 into r22; + add r22 r8 into r23; + add r23 r9 into r24; + add r24 r10 into r25; + add r25 r11 into r26; + add r26 r12 into r27; + add r27 r13 into r28; + add r28 r14 into r29; + add r29 r15.field_0 into r30; + add r30 r15.field_1 into r31; + assert.eq r31 136u32; + +function second: + cast 35u32 36u32 37u32 into r0 as second_3_bundle4; + async second 20u32 21u32 22u32 23u32 24u32 25u32 26u32 27u32 28u32 29u32 30u32 31u32 32u32 33u32 34u32 r0 into r1; + output r1 as test.aleo/second.future; + +finalize second: + input r0 as u32.public; + input r1 as u32.public; + input r2 as u32.public; + input r3 as u32.public; + input r4 as u32.public; + input r5 as u32.public; + input r6 as u32.public; + input r7 as u32.public; + input r8 as u32.public; + input r9 as u32.public; + input r10 as u32.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as second_3_bundle4.public; + add r0 r1 into r16; + add r16 r2 into r17; + add r17 r3 into r18; + add r18 r4 into r19; + add r19 r5 into r20; + add r20 r6 into r21; + add r21 r7 into r22; + add r22 r8 into r23; + add r23 r9 into r24; + add r24 r10 into r25; + add r25 r11 into r26; + add r26 r12 into r27; + add r27 r13 into r28; + add r28 r14 into r29; + add r29 r15.field_0 into r30; + add r30 r15.field_1 into r31; + add r31 r15.field_2 into r32; + assert.eq r32 513u32; diff --git a/tests/expectations/compiler/async_blocks/capture_stress_test_bundled.out b/tests/expectations/compiler/async_blocks/capture_stress_test_bundled.out new file mode 100644 index 00000000000..76b41566290 --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_stress_test_bundled.out @@ -0,0 +1,200 @@ +program test.aleo; + +struct Point: + x as u32; + y as u32; + +struct Color: + r as u8; + g as u8; + b as u8; + +struct Entity: + id as u32; + health as u32; + active as boolean; + +struct main_0_bundle1: + field_0 as Color; + field_1 as Color; + field_2 as Entity; + field_3 as Entity; + field_4 as Entity; + field_5 as Entity; + field_6 as Entity; + field_7 as u32; + field_8 as u32; + field_9 as u32; + field_10 as u32; + field_11 as u32; + field_12 as u32; + field_13 as u32; + field_14 as u32; + field_15 as u32; + +struct main_0_bundle3: + field_0 as u32; + field_1 as [u32; 3u32]; + field_2 as [u32; 3u32]; + field_3 as [u32; 3u32]; + field_4 as u32; + field_5 as u32; + field_6 as u32; + field_7 as u32; + field_8 as u32; + field_9 as u32; + field_10 as u32; + field_11 as u32; + field_12 as u32; + field_13 as u32; + field_14 as u32; + field_15 as u32; + +struct main_0_bundle5: + field_0 as u32; + field_1 as u32; + field_2 as u32; + field_3 as u32; + field_4 as u32; + field_5 as u32; + field_6 as u32; + field_7 as u32; + +function main: + cast 0u32 10u32 into r0 as Point; + cast 1u32 11u32 into r1 as Point; + cast 2u32 12u32 into r2 as Point; + cast 3u32 13u32 into r3 as Point; + cast 4u32 14u32 into r4 as Point; + cast 5u32 15u32 into r5 as Point; + cast 6u32 16u32 into r6 as Point; + cast 7u32 17u32 into r7 as Point; + cast 8u32 18u32 into r8 as Point; + cast 9u32 19u32 into r9 as Point; + cast 255u8 0u8 0u8 into r10 as Color; + cast 0u8 255u8 0u8 into r11 as Color; + cast 0u8 0u8 255u8 into r12 as Color; + cast 128u8 128u8 0u8 into r13 as Color; + cast 0u8 128u8 128u8 into r14 as Color; + cast 100u32 100u32 true into r15 as Entity; + cast 101u32 80u32 true into r16 as Entity; + cast 102u32 60u32 false into r17 as Entity; + cast 103u32 40u32 true into r18 as Entity; + cast 104u32 20u32 false into r19 as Entity; + cast 10u32 11u32 12u32 into r20 as [u32; 3u32]; + cast 13u32 14u32 15u32 into r21 as [u32; 3u32]; + cast 16u32 17u32 18u32 into r22 as [u32; 3u32]; + cast r13 r14 r15 r16 r17 r18 r19 0u32 1u32 2u32 3u32 4u32 5u32 6u32 7u32 8u32 into r23 as main_0_bundle1; + cast 9u32 r20 r21 r22 0u32 1u32 2u32 3u32 4u32 5u32 6u32 7u32 8u32 9u32 10u32 11u32 into r24 as main_0_bundle3; + cast 12u32 13u32 14u32 15u32 16u32 17u32 18u32 19u32 into r25 as main_0_bundle5; + async main r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r23 r24 r25 into r26; + output r26 as test.aleo/main.future; + +finalize main: + input r0 as Point.public; + input r1 as Point.public; + input r2 as Point.public; + input r3 as Point.public; + input r4 as Point.public; + input r5 as Point.public; + input r6 as Point.public; + input r7 as Point.public; + input r8 as Point.public; + input r9 as Point.public; + input r10 as Color.public; + input r11 as Color.public; + input r12 as Color.public; + input r13 as main_0_bundle1.public; + input r14 as main_0_bundle3.public; + input r15 as main_0_bundle5.public; + add r0.x r1.x into r16; + add r16 r2.x into r17; + add r17 r3.x into r18; + add r18 r4.x into r19; + add r19 r5.x into r20; + add r20 r6.x into r21; + add r21 r7.x into r22; + add r22 r8.x into r23; + add r23 r9.x into r24; + add r0.y r1.y into r25; + add r25 r2.y into r26; + add r26 r3.y into r27; + add r27 r4.y into r28; + add r28 r5.y into r29; + add r29 r6.y into r30; + add r30 r7.y into r31; + add r31 r8.y into r32; + add r32 r9.y into r33; + cast r10.r into r34 as u32; + cast r11.r into r35 as u32; + add r34 r35 into r36; + cast r12.r into r37 as u32; + add r36 r37 into r38; + cast r13.field_0.r into r39 as u32; + add r38 r39 into r40; + cast r13.field_1.r into r41 as u32; + add r40 r41 into r42; + cast r10.g into r43 as u32; + cast r11.g into r44 as u32; + add r43 r44 into r45; + cast r12.g into r46 as u32; + add r45 r46 into r47; + cast r13.field_0.g into r48 as u32; + add r47 r48 into r49; + cast r13.field_1.g into r50 as u32; + add r49 r50 into r51; + add r13.field_2.health r13.field_3.health into r52; + add r52 r13.field_4.health into r53; + add r53 r13.field_5.health into r54; + add r54 r13.field_6.health into r55; + ternary r13.field_2.active 1u32 0u32 into r56; + ternary r13.field_3.active 1u32 0u32 into r57; + add r56 r57 into r58; + ternary r13.field_4.active 1u32 0u32 into r59; + add r58 r59 into r60; + ternary r13.field_5.active 1u32 0u32 into r61; + add r60 r61 into r62; + ternary r13.field_6.active 1u32 0u32 into r63; + add r62 r63 into r64; + add r13.field_7 r13.field_8 into r65; + add r65 r13.field_9 into r66; + add r66 r13.field_10 into r67; + add r67 r13.field_11 into r68; + add r68 r13.field_12 into r69; + add r69 r13.field_13 into r70; + add r70 r13.field_14 into r71; + add r71 r13.field_15 into r72; + add r72 r14.field_0 into r73; + add r14.field_1[0u32] r14.field_1[1u32] into r74; + add r74 r14.field_2[0u32] into r75; + add r75 r14.field_2[1u32] into r76; + add r76 r14.field_3[0u32] into r77; + add r77 r14.field_3[1u32] into r78; + add r14.field_4 r14.field_5 into r79; + add r79 r14.field_6 into r80; + add r80 r14.field_7 into r81; + add r81 r14.field_8 into r82; + add r82 r14.field_9 into r83; + add r83 r14.field_10 into r84; + add r84 r14.field_11 into r85; + add r85 r14.field_12 into r86; + add r86 r14.field_13 into r87; + add r87 r14.field_14 into r88; + add r88 r14.field_15 into r89; + add r89 r15.field_0 into r90; + add r90 r15.field_1 into r91; + add r91 r15.field_2 into r92; + add r92 r15.field_3 into r93; + add r93 r15.field_4 into r94; + add r94 r15.field_5 into r95; + add r95 r15.field_6 into r96; + add r96 r15.field_7 into r97; + assert.eq r24 45u32; + assert.eq r33 145u32; + assert.eq r42 511u32; + assert.eq r51 511u32; + assert.eq r55 300u32; + assert.eq r64 3u32; + assert.eq r73 45u32; + assert.eq r78 81u32; + assert.eq r97 190u32; diff --git a/tests/expectations/compiler/async_blocks/capture_struct_fields_bundled.out b/tests/expectations/compiler/async_blocks/capture_struct_fields_bundled.out new file mode 100644 index 00000000000..4a7cec6f427 --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_struct_fields_bundled.out @@ -0,0 +1,88 @@ +program test.aleo; + +struct Data: + amount as u32; + count as u32; + flag as boolean; + +struct main_0_bundle1: + field_0 as u32; + field_1 as u32; + +function main: + cast 1u32 10u32 true into r0 as Data; + cast 2u32 20u32 false into r1 as Data; + cast 3u32 30u32 true into r2 as Data; + cast 4u32 40u32 false into r3 as Data; + cast 5u32 50u32 true into r4 as Data; + cast 6u32 60u32 false into r5 as Data; + cast 7u32 70u32 true into r6 as Data; + cast 8u32 80u32 false into r7 as Data; + cast 9u32 90u32 true into r8 as Data; + cast 10u32 100u32 false into r9 as Data; + cast 11u32 110u32 true into r10 as Data; + cast 4u32 5u32 into r11 as main_0_bundle1; + async main r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 0u32 1u32 2u32 3u32 r11 into r12; + output r12 as test.aleo/main.future; + +finalize main: + input r0 as Data.public; + input r1 as Data.public; + input r2 as Data.public; + input r3 as Data.public; + input r4 as Data.public; + input r5 as Data.public; + input r6 as Data.public; + input r7 as Data.public; + input r8 as Data.public; + input r9 as Data.public; + input r10 as Data.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as main_0_bundle1.public; + add r0.amount r1.amount into r16; + add r16 r2.amount into r17; + add r17 r3.amount into r18; + add r18 r4.amount into r19; + add r19 r5.amount into r20; + add r20 r6.amount into r21; + add r21 r7.amount into r22; + add r22 r8.amount into r23; + add r23 r9.amount into r24; + add r24 r10.amount into r25; + add r0.count r1.count into r26; + add r26 r2.count into r27; + add r27 r3.count into r28; + add r28 r4.count into r29; + add r29 r5.count into r30; + add r30 r6.count into r31; + add r31 r7.count into r32; + add r32 r8.count into r33; + add r33 r9.count into r34; + add r34 r10.count into r35; + not r1.flag into r36; + and r0.flag r36 into r37; + and r37 r2.flag into r38; + not r3.flag into r39; + and r38 r39 into r40; + and r40 r4.flag into r41; + not r5.flag into r42; + and r41 r42 into r43; + and r43 r6.flag into r44; + not r7.flag into r45; + and r44 r45 into r46; + and r46 r8.flag into r47; + not r9.flag into r48; + and r47 r48 into r49; + and r49 r10.flag into r50; + add r11 r12 into r51; + add r51 r13 into r52; + add r52 r14 into r53; + add r53 r15.field_0 into r54; + add r54 r15.field_1 into r55; + assert.eq r25 66u32; + assert.eq r35 660u32; + assert.eq r50 true; + assert.eq r55 15u32; diff --git a/tests/expectations/compiler/async_blocks/capture_tuples_bundled.out b/tests/expectations/compiler/async_blocks/capture_tuples_bundled.out new file mode 100644 index 00000000000..16daf638db5 --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_tuples_bundled.out @@ -0,0 +1,61 @@ +program test.aleo; + +struct main_0_bundle1: + field_0 as u32; + field_1 as u32; + field_2 as u32; + field_3 as u32; + field_4 as u32; + field_5 as u32; + field_6 as u32; + field_7 as u32; + field_8 as u32; + field_9 as u32; + +function main: + cast 16u32 17u32 18u32 19u32 20u32 21u32 22u32 23u32 100u32 200u32 into r0 as main_0_bundle1; + async main 1u32 2u32 3u32 4u32 5u32 6u32 7u32 8u32 9u32 10u32 11u32 12u32 13u32 14u32 15u32 r0 into r1; + output r1 as test.aleo/main.future; + +finalize main: + input r0 as u32.public; + input r1 as u32.public; + input r2 as u32.public; + input r3 as u32.public; + input r4 as u32.public; + input r5 as u32.public; + input r6 as u32.public; + input r7 as u32.public; + input r8 as u32.public; + input r9 as u32.public; + input r10 as u32.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as main_0_bundle1.public; + add r0 r1 into r16; + add r16 r2 into r17; + add r17 r3 into r18; + add r18 r4 into r19; + add r19 r5 into r20; + add r20 r6 into r21; + add r21 r7 into r22; + add r22 r8 into r23; + add r23 r9 into r24; + add r24 r10 into r25; + add r25 r11 into r26; + add r26 r12 into r27; + add r27 r13 into r28; + add r28 r14 into r29; + add r29 r15.field_0 into r30; + add r30 r15.field_1 into r31; + add r31 r15.field_2 into r32; + add r32 r15.field_3 into r33; + add r33 r15.field_4 into r34; + add r34 r15.field_5 into r35; + add r35 r15.field_6 into r36; + add r36 r15.field_7 into r37; + add r37 r15.field_8 into r38; + add r38 r15.field_9 into r39; + assert.eq r39 576u32; diff --git a/tests/expectations/compiler/async_blocks/capture_with_operations_bundled.out b/tests/expectations/compiler/async_blocks/capture_with_operations_bundled.out new file mode 100644 index 00000000000..2b7e3cccdd1 --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_with_operations_bundled.out @@ -0,0 +1,62 @@ +program test.aleo; + +struct main_0_bundle1: + field_0 as u32; + field_1 as u32; + field_2 as u32; + field_3 as u32; + field_4 as u32; + field_5 as u32; + field_6 as u32; + field_7 as u32; + field_8 as u32; + +function main: + cast 7u32 8u32 9u32 1u32 2u32 3u32 4u32 5u32 6u32 into r0 as main_0_bundle1; + async main 10u32 20u32 30u32 40u32 50u32 60u32 70u32 80u32 90u32 100u32 2u32 3u32 4u32 5u32 6u32 r0 into r1; + output r1 as test.aleo/main.future; + +finalize main: + input r0 as u32.public; + input r1 as u32.public; + input r2 as u32.public; + input r3 as u32.public; + input r4 as u32.public; + input r5 as u32.public; + input r6 as u32.public; + input r7 as u32.public; + input r8 as u32.public; + input r9 as u32.public; + input r10 as u32.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as main_0_bundle1.public; + add r0 r1 into r16; + add r16 r2 into r17; + add r17 r3 into r18; + add r18 r4 into r19; + add r19 r5 into r20; + add r20 r6 into r21; + add r21 r7 into r22; + add r22 r8 into r23; + add r23 r9 into r24; + mul r10 r11 into r25; + mul r25 r12 into r26; + mul r26 r13 into r27; + div r24 r14 into r28; + sub r27 r15.field_0 into r29; + add r28 r29 into r30; + add r30 r15.field_1 into r31; + add r31 r15.field_2 into r32; + add r15.field_3 r15.field_4 into r33; + add r33 r15.field_5 into r34; + add r34 r15.field_6 into r35; + add r35 r15.field_7 into r36; + add r36 r15.field_8 into r37; + assert.eq r24 550u32; + assert.eq r27 120u32; + assert.eq r37 21u32; + gt r32 0u32 into r38; + assert.eq r38 true; diff --git a/tests/expectations/compiler/async_blocks/capture_with_user_structs.out b/tests/expectations/compiler/async_blocks/capture_with_user_structs.out new file mode 100644 index 00000000000..0725619c808 --- /dev/null +++ b/tests/expectations/compiler/async_blocks/capture_with_user_structs.out @@ -0,0 +1,74 @@ +program test.aleo; + +struct Point: + x as u32; + y as u32; + +struct Color: + r as u8; + g as u8; + b as u8; + +struct Entity: + id as u32; + active as boolean; + +struct main_0_bundle1: + field_0 as u32; + field_1 as u32; + field_2 as u32; + field_3 as u32; + field_4 as u32; + field_5 as u32; + field_6 as u32; + +function main: + cast 10u32 20u32 into r0 as Point; + cast 30u32 40u32 into r1 as Point; + cast 255u8 0u8 0u8 into r2 as Color; + cast 1u32 true into r3 as Entity; + cast 11u32 12u32 13u32 14u32 15u32 16u32 17u32 into r4 as main_0_bundle1; + async main r0 r1 r2 r3 0u32 1u32 2u32 3u32 4u32 5u32 6u32 7u32 8u32 9u32 10u32 r4 into r5; + output r5 as test.aleo/main.future; + +finalize main: + input r0 as Point.public; + input r1 as Point.public; + input r2 as Color.public; + input r3 as Entity.public; + input r4 as u32.public; + input r5 as u32.public; + input r6 as u32.public; + input r7 as u32.public; + input r8 as u32.public; + input r9 as u32.public; + input r10 as u32.public; + input r11 as u32.public; + input r12 as u32.public; + input r13 as u32.public; + input r14 as u32.public; + input r15 as main_0_bundle1.public; + assert.eq r0.x 10u32; + assert.eq r0.y 20u32; + assert.eq r1.x 30u32; + assert.eq r1.y 40u32; + assert.eq r2.r 255u8; + assert.eq r3.active true; + add r4 r5 into r16; + add r16 r6 into r17; + add r17 r7 into r18; + add r18 r8 into r19; + add r19 r9 into r20; + add r20 r10 into r21; + add r21 r11 into r22; + add r22 r12 into r23; + add r23 r13 into r24; + add r24 r14 into r25; + add r25 r15.field_0 into r26; + add r26 r15.field_1 into r27; + add r27 r15.field_2 into r28; + add r28 r15.field_3 into r29; + add r29 r15.field_4 into r30; + add r30 r15.field_5 into r31; + add r31 r15.field_6 into r32; + assert.eq r32 153u32; diff --git a/tests/expectations/compiler/async_blocks/too_many_captures_fail.out b/tests/expectations/compiler/async_blocks/too_many_captures_fail.out deleted file mode 100644 index 0f8541bf804..00000000000 --- a/tests/expectations/compiler/async_blocks/too_many_captures_fail.out +++ /dev/null @@ -1,47 +0,0 @@ -Error [ESAZ0374011]: An `async` block cannot capture more than 16 variables, found one attempting to capture 20 variables. - --> compiler-test:25:17 - | - 25 | let f = async { - | ^^^^^^^ - 26 | assert(a0 == 0); - | ^^^^^^^^^^^^^^^^ - 27 | assert(a1 == 0); - | ^^^^^^^^^^^^^^^^ - 28 | assert(a2 == 0); - | ^^^^^^^^^^^^^^^^ - 29 | assert(a3 == 0); - | ^^^^^^^^^^^^^^^^ - 30 | assert(a4 == 0); - | ^^^^^^^^^^^^^^^^ - 31 | assert(a5 == 0); - | ^^^^^^^^^^^^^^^^ - 32 | assert(a6 == 0); - | ^^^^^^^^^^^^^^^^ - 33 | assert(a7 == 0); - | ^^^^^^^^^^^^^^^^ - 34 | assert(a8 == 0); - | ^^^^^^^^^^^^^^^^ - 35 | assert(a9 == 0); - | ^^^^^^^^^^^^^^^^ - 36 | assert(b0 == 0); - | ^^^^^^^^^^^^^^^^ - 37 | assert(b1 == 0); - | ^^^^^^^^^^^^^^^^ - 38 | assert(b2 == 0); - | ^^^^^^^^^^^^^^^^ - 39 | assert(b3 == 0); - | ^^^^^^^^^^^^^^^^ - 40 | assert(b4 == 0); - | ^^^^^^^^^^^^^^^^ - 41 | assert(b5 == 0); - | ^^^^^^^^^^^^^^^^ - 42 | assert(b6 == 0); - | ^^^^^^^^^^^^^^^^ - 43 | assert(b7 == 0); - | ^^^^^^^^^^^^^^^^ - 44 | assert(b8 == 0); - | ^^^^^^^^^^^^^^^^ - 45 | assert(b9 == 0); - | ^^^^^^^^^^^^^^^^ - 46 | }; - | ^ diff --git a/tests/tests/compiler/async_blocks/capture_complex_types_bundled.leo b/tests/tests/compiler/async_blocks/capture_complex_types_bundled.leo new file mode 100644 index 00000000000..5c8338378db --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_complex_types_bundled.leo @@ -0,0 +1,71 @@ +program test.aleo { + struct Position { + x: u32, + y: u32, + } + + struct Entity { + pos: Position, + health: u32, + } + + // Test capturing complex types (nested structs, arrays) with bundling + async transition main() -> Future { + let entity1 = Entity { + pos: Position { x: 10u32, y: 20u32 }, + health: 100u32, + }; + + let entity2 = Entity { + pos: Position { x: 30u32, y: 40u32 }, + health: 80u32, + }; + + let arr1 = [1u32, 2u32, 3u32]; + let arr2 = [4u32, 5u32, 6u32]; + let arr3 = [7u32, 8u32, 9u32]; + + // Additional scalars to push over 16 captures + let s0 = 0u32; + let s1 = 1u32; + let s2 = 2u32; + let s3 = 3u32; + let s4 = 4u32; + let s5 = 5u32; + let s6 = 6u32; + let s7 = 7u32; + let s8 = 8u32; + let s9 = 9u32; + let s10 = 10u32; + let s11 = 11u32; + let s12 = 12u32; + let s13 = 13u32; + let s14 = 14u32; + let s15 = 15u32; + let s16 = 16u32; + let s17 = 17u32; + + // Total captures: 2 entities + 3 arrays + 18 scalars = 23 captures + // Should create 1 bundle with 7 fields + let f = async { + // Use nested struct fields + assert_eq(entity1.pos.x, 10u32); + assert_eq(entity1.pos.y, 20u32); + assert_eq(entity1.health, 100u32); + assert_eq(entity2.pos.x, 30u32); + assert_eq(entity2.health, 80u32); + + // Use arrays + assert_eq(arr1[0u32], 1u32); + assert_eq(arr2[1u32], 5u32); + assert_eq(arr3[2u32], 9u32); + + // Use scalars + let scalar_sum = s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17; + assert_eq(scalar_sum, 153u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/capture_multiple_async_bundled.leo b/tests/tests/compiler/async_blocks/capture_multiple_async_bundled.leo new file mode 100644 index 00000000000..9356c27415a --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_multiple_async_bundled.leo @@ -0,0 +1,63 @@ +program test.aleo { + // Test multiple async transition functions with bundling + // Ensures synthetic struct names don't collide across different functions + async transition first() -> Future { + // First set of variables for first async block + let a0 = 0u32; + let a1 = 1u32; + let a2 = 2u32; + let a3 = 3u32; + let a4 = 4u32; + let a5 = 5u32; + let a6 = 6u32; + let a7 = 7u32; + let a8 = 8u32; + let a9 = 9u32; + let a10 = 10u32; + let a11 = 11u32; + let a12 = 12u32; + let a13 = 13u32; + let a14 = 14u32; + let a15 = 15u32; + let a16 = 16u32; + + // Async block with 17 captures + let f = async { + let sum1 = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + + a10 + a11 + a12 + a13 + a14 + a15 + a16; + assert_eq(sum1, 136u32); + }; + return f; + } + + async transition second() -> Future { + // Second set of variables for second async transition + let b0 = 20u32; + let b1 = 21u32; + let b2 = 22u32; + let b3 = 23u32; + let b4 = 24u32; + let b5 = 25u32; + let b6 = 26u32; + let b7 = 27u32; + let b8 = 28u32; + let b9 = 29u32; + let b10 = 30u32; + let b11 = 31u32; + let b12 = 32u32; + let b13 = 33u32; + let b14 = 34u32; + let b15 = 35u32; + let b16 = 36u32; + let b17 = 37u32; + + // Second async block with 18 captures + let f = async { + let sum2 = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9 + + b10 + b11 + b12 + b13 + b14 + b15 + b16 + b17; + assert_eq(sum2, 513u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/capture_stress_test_bundled.leo b/tests/tests/compiler/async_blocks/capture_stress_test_bundled.leo new file mode 100644 index 00000000000..5ff5aa24880 --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_stress_test_bundled.leo @@ -0,0 +1,122 @@ +program test.aleo { + struct Point { + x: u32, + y: u32, + } + + struct Color { + r: u8, + g: u8, + b: u8, + } + + struct Entity { + id: u32, + health: u32, + active: bool, + } + + // Stresss test with a mx of structs, tuples, arrays, and scalars with 50+ captures + async transition main() -> Future { + // 10 Point structs + let p0 = Point { x: 0u32, y: 10u32 }; + let p1 = Point { x: 1u32, y: 11u32 }; + let p2 = Point { x: 2u32, y: 12u32 }; + let p3 = Point { x: 3u32, y: 13u32 }; + let p4 = Point { x: 4u32, y: 14u32 }; + let p5 = Point { x: 5u32, y: 15u32 }; + let p6 = Point { x: 6u32, y: 16u32 }; + let p7 = Point { x: 7u32, y: 17u32 }; + let p8 = Point { x: 8u32, y: 18u32 }; + let p9 = Point { x: 9u32, y: 19u32 }; + + // 5 Color structs + let c0 = Color { r: 255u8, g: 0u8, b: 0u8 }; + let c1 = Color { r: 0u8, g: 255u8, b: 0u8 }; + let c2 = Color { r: 0u8, g: 0u8, b: 255u8 }; + let c3 = Color { r: 128u8, g: 128u8, b: 0u8 }; + let c4 = Color { r: 0u8, g: 128u8, b: 128u8 }; + + // 5 Entity structs + let e0 = Entity { id: 100u32, health: 100u32, active: true }; + let e1 = Entity { id: 101u32, health: 80u32, active: true }; + let e2 = Entity { id: 102u32, health: 60u32, active: false }; + let e3 = Entity { id: 103u32, health: 40u32, active: true }; + let e4 = Entity { id: 104u32, health: 20u32, active: false }; + + // 5 tuples + let t0 = (0u32, 1u32); + let t1 = (2u32, 3u32); + let t2 = (4u32, 5u32); + let t3 = (6u32, 7u32); + let t4 = (8u32, 9u32); + + // 3 arrays + let arr0 = [10u32, 11u32, 12u32]; + let arr1 = [13u32, 14u32, 15u32]; + let arr2 = [16u32, 17u32, 18u32]; + + // 20 scalar u32 variables + let s0 = 0u32; + let s1 = 1u32; + let s2 = 2u32; + let s3 = 3u32; + let s4 = 4u32; + let s5 = 5u32; + let s6 = 6u32; + let s7 = 7u32; + let s8 = 8u32; + let s9 = 9u32; + let s10 = 10u32; + let s11 = 11u32; + let s12 = 12u32; + let s13 = 13u32; + let s14 = 14u32; + let s15 = 15u32; + let s16 = 16u32; + let s17 = 17u32; + let s18 = 18u32; + let s19 = 19u32; + + // Total: 10 Points + 5 Colors + 5 Entities + 5 Tuples + 3 Arrays + 20 Scalars = 48 captures + // This will require multiple bundle structs + let f = async { + // Use Point structs + let sum_x = p0.x + p1.x + p2.x + p3.x + p4.x + p5.x + p6.x + p7.x + p8.x + p9.x; + let sum_y = p0.y + p1.y + p2.y + p3.y + p4.y + p5.y + p6.y + p7.y + p8.y + p9.y; + + // Use Color structs + let sum_r = (c0.r as u32) + (c1.r as u32) + (c2.r as u32) + (c3.r as u32) + (c4.r as u32); + let sum_g = (c0.g as u32) + (c1.g as u32) + (c2.g as u32) + (c3.g as u32) + (c4.g as u32); + + // Use Entity structs + let sum_health = e0.health + e1.health + e2.health + e3.health + e4.health; + let active_count = (e0.active ? 1u32 : 0u32) + (e1.active ? 1u32 : 0u32) + + (e2.active ? 1u32 : 0u32) + (e3.active ? 1u32 : 0u32) + + (e4.active ? 1u32 : 0u32); + + // Use tuples + let sum_tuples = t0.0 + t0.1 + t1.0 + t1.1 + t2.0 + t2.1 + t3.0 + t3.1 + t4.0 + t4.1; + + // Use arrays + let sum_arrays = arr0[0u32] + arr0[1u32] + arr1[0u32] + arr1[1u32] + arr2[0u32] + arr2[1u32]; + + // Use scalars + let sum_scalars = s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17 + s18 + s19; + + // Verify all values + assert_eq(sum_x, 45u32); + assert_eq(sum_y, 145u32); + assert_eq(sum_r, 511u32); + assert_eq(sum_g, 511u32); + assert_eq(sum_health, 300u32); + assert_eq(active_count, 3u32); + assert_eq(sum_tuples, 45u32); + assert_eq(sum_arrays, 81u32); + assert_eq(sum_scalars, 190u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/capture_struct_fields_bundled.leo b/tests/tests/compiler/async_blocks/capture_struct_fields_bundled.leo new file mode 100644 index 00000000000..1ab81552670 --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_struct_fields_bundled.leo @@ -0,0 +1,49 @@ +program test.aleo { + struct Data { + amount: u32, + count: u32, + flag: bool, + } + + // Test capturing individual struct fields (similar to tuple fields) + // Each field access counts as a separate capture + async transition main() -> Future { + let d1 = Data { amount: 1u32, count: 10u32, flag: true }; + let d2 = Data { amount: 2u32, count: 20u32, flag: false }; + let d3 = Data { amount: 3u32, count: 30u32, flag: true }; + let d4 = Data { amount: 4u32, count: 40u32, flag: false }; + let d5 = Data { amount: 5u32, count: 50u32, flag: true }; + let d6 = Data { amount: 6u32, count: 60u32, flag: false }; + let d7 = Data { amount: 7u32, count: 70u32, flag: true }; + let d8 = Data { amount: 8u32, count: 80u32, flag: false }; + let d9 = Data { amount: 9u32, count: 90u32, flag: true }; + let d10 = Data { amount: 10u32, count: 100u32, flag: false }; + let d11 = Data { amount: 11u32, count: 110u32, flag: true }; + + let s0 = 0u32; + let s1 = 1u32; + let s2 = 2u32; + let s3 = 3u32; + let s4 = 4u32; + let s5 = 5u32; + + // Total: 11 structs + 6 scalars = 17 captures + // Should trigger bundling of 1 variable + let f = async { + let sum_amounts = d1.amount + d2.amount + d3.amount + d4.amount + d5.amount + d6.amount + + d7.amount + d8.amount + d9.amount + d10.amount + d11.amount; + let sum_counts = d1.count + d2.count + d3.count + d4.count + d5.count + d6.count + + d7.count + d8.count + d9.count + d10.count + d11.count; + let all_flags = d1.flag && !d2.flag && d3.flag && !d4.flag && d5.flag && !d6.flag + && d7.flag && !d8.flag && d9.flag && !d10.flag && d11.flag; + let scalar_sum = s0 + s1 + s2 + s3 + s4 + s5; + + assert_eq(sum_amounts, 66u32); + assert_eq(sum_counts, 660u32); + assert(all_flags); + assert_eq(scalar_sum, 15u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/capture_tuples_bundled.leo b/tests/tests/compiler/async_blocks/capture_tuples_bundled.leo new file mode 100644 index 00000000000..8eca2fd67b9 --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_tuples_bundled.leo @@ -0,0 +1,28 @@ +program test.aleo { + // Test capturing tuple fields that require bundling + // Tuple fields count as individual captures + async transition main() -> Future { + let tuple1 = (1u32, 2u32, 3u32, 4u32); + let tuple2 = (5u32, 6u32, 7u32, 8u32); + let tuple3 = (9u32, 10u32, 11u32, 12u32); + let tuple4 = (13u32, 14u32, 15u32, 16u32); + let tuple5 = (17u32, 18u32, 19u32, 20u32); + let tuple6 = (21u32, 22u32, 23u32); + let a = 100u32; + let b = 200u32; + + // Captures: 4+4+4+4+4+3 tuple fields + 2 scalars = 25 total + // Should create 1 bundle with 9 fields + let f = async { + let sum = tuple1.0 + tuple1.1 + tuple1.2 + tuple1.3 + + tuple2.0 + tuple2.1 + tuple2.2 + tuple2.3 + + tuple3.0 + tuple3.1 + tuple3.2 + tuple3.3 + + tuple4.0 + tuple4.1 + tuple4.2 + tuple4.3 + + tuple5.0 + tuple5.1 + tuple5.2 + tuple5.3 + + tuple6.0 + tuple6.1 + tuple6.2; + assert_eq(sum + a + b, 576u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/capture_with_operations_bundled.leo b/tests/tests/compiler/async_blocks/capture_with_operations_bundled.leo new file mode 100644 index 00000000000..82374b173dc --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_with_operations_bundled.leo @@ -0,0 +1,43 @@ +program test.aleo { + // Test that bundled variables can be used in complex expressions + async transition main() -> Future { + let a0 = 10u32; + let a1 = 20u32; + let a2 = 30u32; + let a3 = 40u32; + let a4 = 50u32; + let a5 = 60u32; + let a6 = 70u32; + let a7 = 80u32; + let a8 = 90u32; + let a9 = 100u32; + let b0 = 2u32; + let b1 = 3u32; + let b2 = 4u32; + let b3 = 5u32; + let b4 = 6u32; + let b5 = 7u32; + let b6 = 8u32; + let b7 = 9u32; + let c0 = 1u32; + let c1 = 2u32; + let c2 = 3u32; + let c3 = 4u32; + let c4 = 5u32; + let c5 = 6u32; + + // 24 captures total - tests operations with bundled variables + let f = async { + let sum = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; + let product = b0 * b1 * b2 * b3; + let mixed = (sum / b4) + (product - b5) + b6 + b7; + let extra = c0 + c1 + c2 + c3 + c4 + c5; + assert_eq(sum, 550u32); + assert_eq(product, 120u32); + assert_eq(extra, 21u32); + assert(mixed > 0u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/capture_with_user_structs.leo b/tests/tests/compiler/async_blocks/capture_with_user_structs.leo new file mode 100644 index 00000000000..d14282b5e49 --- /dev/null +++ b/tests/tests/compiler/async_blocks/capture_with_user_structs.leo @@ -0,0 +1,63 @@ +program test.aleo { + struct Point { + x: u32, + y: u32, + } + + struct Color { + r: u8, + g: u8, + b: u8, + } + + struct Entity { + id: u32, + active: bool, + } + + // Test capturing user-defined structs along with many scalar variables + // This ensures synthetic bundle structs don't conflict with user structs + async transition main() -> Future { + let p1 = Point { x: 10u32, y: 20u32 }; + let p2 = Point { x: 30u32, y: 40u32 }; + let c1 = Color { r: 255u8, g: 0u8, b: 0u8 }; + let e1 = Entity { id: 1u32, active: true }; + + // Add scalar variables to exceed 16 captures + let a0 = 0u32; + let a1 = 1u32; + let a2 = 2u32; + let a3 = 3u32; + let a4 = 4u32; + let a5 = 5u32; + let a6 = 6u32; + let a7 = 7u32; + let a8 = 8u32; + let a9 = 9u32; + let b0 = 10u32; + let b1 = 11u32; + let b2 = 12u32; + let b3 = 13u32; + let b4 = 14u32; + let b5 = 15u32; + let b6 = 16u32; + let b7 = 17u32; + + // Total: 4 structs + 18 scalars = 22 captures + // Should create 1 bundle struct + let f = async { + assert_eq(p1.x, 10u32); + assert_eq(p1.y, 20u32); + assert_eq(p2.x, 30u32); + assert_eq(p2.y, 40u32); + assert_eq(c1.r, 255u8); + assert(e1.active); + + let sum = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + + b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7; + assert_eq(sum, 153u32); + }; + return f; + } +} + diff --git a/tests/tests/compiler/async_blocks/too_many_captures_fail.leo b/tests/tests/compiler/async_blocks/too_many_captures_fail.leo deleted file mode 100644 index 11427a118e0..00000000000 --- a/tests/tests/compiler/async_blocks/too_many_captures_fail.leo +++ /dev/null @@ -1,49 +0,0 @@ -program test.aleo { - async transition main(x: u32) -> Future { - let a0 = 0u32; - let a1 = 0u32; - let a2 = 0u32; - let a3 = 0u32; - let a4 = 0u32; - let a5 = 0u32; - let a6 = 0u32; - let a7 = 0u32; - let a8 = 0u32; - let a9 = 0u32; - let b0 = 0u32; - let b1 = 0u32; - let b2 = 0u32; - let b3 = 0u32; - let b4 = 0u32; - let b5 = 0u32; - let b6 = 0u32; - let b7 = 0u32; - let b8 = 0u32; - let b9 = 0u32; - - // More captures than allowed in the VM - let f = async { - assert(a0 == 0); - assert(a1 == 0); - assert(a2 == 0); - assert(a3 == 0); - assert(a4 == 0); - assert(a5 == 0); - assert(a6 == 0); - assert(a7 == 0); - assert(a8 == 0); - assert(a9 == 0); - assert(b0 == 0); - assert(b1 == 0); - assert(b2 == 0); - assert(b3 == 0); - assert(b4 == 0); - assert(b5 == 0); - assert(b6 == 0); - assert(b7 == 0); - assert(b8 == 0); - assert(b9 == 0); - }; - return f; - } -}