@@ -1354,3 +1354,137 @@ fn test_nested_macro_invocation_with_arg_scoping() {
13541354 // Expected bytecode: IDENTITY(__NOOP) should produce nothing (since __NOOP generates no bytecode)
13551355 assert ! ( main_bytecode. is_empty( ) ) ;
13561356}
1357+
1358+ #[ test]
1359+ fn test_nested_macro_invocation_with_label_identical_params ( ) {
1360+ // Tests nested macro invocations as arguments with labels between invocations.
1361+ // Ensures the compiler correctly resolves nested macro calls when labels
1362+ // change bytecode offsets, even with identical parameter names across macros.
1363+ let source = r#"
1364+ #define macro M3(x) = takes(0) returns(0) {
1365+ <x>
1366+ }
1367+
1368+ #define macro M2(y) = takes(0) returns(0) {
1369+ M3(<y>)
1370+ }
1371+
1372+ #define macro MAIN() = takes(0) returns(0) {
1373+ M2(M3(0x01))
1374+ lbl:
1375+ M2(M3(0x02))
1376+ }
1377+ "# ;
1378+
1379+ // Lex + Parse
1380+ let flattened_source = FullFileSource { source, file : None , spans : vec ! [ ] } ;
1381+ let lexer = Lexer :: new ( flattened_source) ;
1382+ let tokens = lexer. into_iter ( ) . map ( |x| x. unwrap ( ) ) . collect :: < Vec < Token > > ( ) ;
1383+ let mut parser = Parser :: new ( tokens, None ) ;
1384+ let mut contract = parser. parse ( ) . unwrap ( ) ;
1385+ contract. derive_storage_pointers ( ) ;
1386+
1387+ let evm_version = EVMVersion :: default ( ) ;
1388+
1389+ // This should compile successfully without stack overflow
1390+ let main_bytecode = Codegen :: generate_main_bytecode ( & evm_version, & contract, None , false ) . unwrap ( ) ;
1391+
1392+ // Expected bytecode:
1393+ // M2(M3(0x01)) -> M3(M3(0x01)) -> M3(0x01) -> 0x01 = PUSH1 0x01
1394+ // lbl: -> JUMPDEST
1395+ // M2(M3(0x02)) -> M3(M3(0x02)) -> M3(0x02) -> 0x02 = PUSH1 0x02
1396+ // Total: 6001 5b 6002
1397+ let expected_bytecode = "60015b6002" ;
1398+ assert_eq ! ( main_bytecode. to_lowercase( ) , expected_bytecode. to_lowercase( ) ) ;
1399+ }
1400+
1401+ #[ test]
1402+ fn test_nested_macro_invocation_with_label_different_params ( ) {
1403+ // Tests nested macro invocations as arguments with labels and different parameter names.
1404+ // Verifies argument resolution works correctly across macro boundaries when parameter
1405+ // names differ and labels are present.
1406+ let source = r#"
1407+ #define macro M3(arg) = takes(0) returns(0) {
1408+ <arg>
1409+ }
1410+
1411+ #define macro M2(y) = takes(0) returns(0) {
1412+ M3(<y>)
1413+ }
1414+
1415+ #define macro MAIN() = takes(0) returns(0) {
1416+ M2(M3(0x01))
1417+ lbl:
1418+ M2(M3(0x02))
1419+ }
1420+ "# ;
1421+
1422+ // Lex + Parse
1423+ let flattened_source = FullFileSource { source, file : None , spans : vec ! [ ] } ;
1424+ let lexer = Lexer :: new ( flattened_source) ;
1425+ let tokens = lexer. into_iter ( ) . map ( |x| x. unwrap ( ) ) . collect :: < Vec < Token > > ( ) ;
1426+ let mut parser = Parser :: new ( tokens, None ) ;
1427+ let mut contract = parser. parse ( ) . unwrap ( ) ;
1428+ contract. derive_storage_pointers ( ) ;
1429+
1430+ let evm_version = EVMVersion :: default ( ) ;
1431+
1432+ // This should compile successfully without "Missing Argument Definition" error
1433+ let main_bytecode = Codegen :: generate_main_bytecode ( & evm_version, & contract, None , false ) . unwrap ( ) ;
1434+
1435+ // Expected bytecode: same as above
1436+ // M2(M3(0x01)) -> M3(M3(0x01)) -> M3(0x01) -> 0x01 = PUSH1 0x01
1437+ // lbl: -> JUMPDEST
1438+ // M2(M3(0x02)) -> M3(M3(0x02)) -> M3(0x02) -> 0x02 = PUSH1 0x02
1439+ // Total: 6001 5b 6002
1440+ let expected_bytecode = "60015b6002" ;
1441+ assert_eq ! ( main_bytecode. to_lowercase( ) , expected_bytecode. to_lowercase( ) ) ;
1442+ }
1443+
1444+ #[ test]
1445+ fn test_nested_argcall_in_macrocall_with_label ( ) {
1446+ // Tests recursive argument resolution in nested macro calls with labels.
1447+ // Verifies that when a MacroCall contains an ArgCall (e.g., M3(<arg>)), the compiler
1448+ // correctly resolves nested arguments without infinite recursion or stack overflow.
1449+ let source = r#"
1450+ #define macro MAIN() = {
1451+ M1(__NOOP)
1452+ }
1453+
1454+ #define macro M1(arg) = {
1455+ M2(M3(<arg>))
1456+ lbl:
1457+ M2(M3(<arg>))
1458+ }
1459+
1460+ #define macro M2(arg) = {
1461+ <arg>
1462+ }
1463+
1464+ #define macro M3(arg) = {
1465+ <arg>
1466+ }
1467+ "# ;
1468+
1469+ // Lex + Parse
1470+ let flattened_source = FullFileSource { source, file : None , spans : vec ! [ ] } ;
1471+ let lexer = Lexer :: new ( flattened_source) ;
1472+ let tokens = lexer. into_iter ( ) . map ( |x| x. unwrap ( ) ) . collect :: < Vec < Token > > ( ) ;
1473+ let mut parser = Parser :: new ( tokens, None ) ;
1474+ let mut contract = parser. parse ( ) . unwrap ( ) ;
1475+ contract. derive_storage_pointers ( ) ;
1476+
1477+ let evm_version = EVMVersion :: default ( ) ;
1478+
1479+ // This should compile successfully without stack overflow
1480+ let main_bytecode = Codegen :: generate_main_bytecode ( & evm_version, & contract, None , false ) . unwrap ( ) ;
1481+
1482+ // Expected bytecode:
1483+ // M1(__NOOP) expands to:
1484+ // M2(M3(__NOOP)) -> M2(__NOOP) -> __NOOP (no output)
1485+ // lbl: -> JUMPDEST (5b)
1486+ // M2(M3(__NOOP)) -> M2(__NOOP) -> __NOOP (no output)
1487+ // Total: just the JUMPDEST
1488+ let expected_bytecode = "5b" ;
1489+ assert_eq ! ( main_bytecode. to_lowercase( ) , expected_bytecode. to_lowercase( ) ) ;
1490+ }
0 commit comments