Skip to content

Conversation

@achidlow
Copy link
Contributor

Safety checks to ensure explicit user asserts and error conditions are preserved through optimization passes and not inadvertently eliminated or modified.

@achidlow achidlow requested a review from Copilot November 18, 2025 09:09
@engineering-ci
Copy link

engineering-ci bot commented Nov 18, 2025

Name Status O0 bytes O1 bytes O2 bytes O0 ops O1 ops O2 ops
box_storage 🔺 - +4🔺 +4 - +4🔺 +4
global_state 🔺 - +2🔺 +2 - +2🔺 +2
voting 🔺 - +2🔺 +2 - +2🔺 +2
abi_routing/Reference 🔺 - +2🔺 +2 - +2🔺 +2
arc4_dynamic_arrays 🔺 - +6🔺 +4🔺 - +6🔺 +4🔺
arc4_types/Arc4AddressContract 🔺 - +3🔺 +3 - +2🔺 +2
arc4_types/Arc4ArraysContract 🔺 - +2🔺 +2 - +2🔺 +2
arc4_types/Arc4BoolEvalContract 🔺 - +3🔺 +3 - +2🔺 +2
arc4_types/Arc4BoolTypeContract 🔺 - +3🔺 +3 - +2🔺 +2
arc4_types/Arc4DynamicBytesContract 🔺 - +2🔺 +2 - +2🔺 +2
arc4_types/Arc4MutableParamsContract 🔺 - +2🔺 - - +2🔺 -
arc4_types/Arc4MutationContract 🔺 - +2🔺 +2 - +2🔺 +2
arc4_types/Arc4NumericTypesContract 🔺 - +3🔺 +3 - +2🔺 +2
arc4_types/Arc4RefTypesContract 🔺 - +5🔺 +5 - +2🔺 +2
arc4_types/Arc4StringTypesContract 🔺 - +3🔺 +3 - +2🔺 +2
arc4_types/MutableParams2 🔺 - +2🔺 - - +2🔺 -
arc_56 🔺 - +2🔺 +2 - +2🔺 +2
array/Contract 🔺 - +14🔺 +14 - +14🔺 +14
array/ImmutableArrayInitContract 🔺 - +4🔺 +4 - +4🔺 +4
avm_12/Contract 🔺 - +2🔺 +2 - +2🔺 +2
boolean_binary_ops 🔺 - +4🔺 +4 - +2🔺 +2
compile/HelloFactory 🔺 - +14🔺 +14 - +14🔺 +14
enumeration 🔺 - +2🔺 +2 - +2🔺 +2
fixed_bytes_ops/CheckABIApp 🔺 - +2🔺 +2 - +2🔺 +2
group_side_effects/AppExpectingEffects 🔺 - +2🔺 +2 - +2🔺 +2
inner_transactions/FieldTupleContract 🔺 - +4🔺 +4 - +4🔺 +4
inner_transactions/ItxnNamedTuple 🔺 - +6🔺 +6 - +6🔺 +6
inner_transactions_assignment 🔺 - +4🔺 +4 - +4🔺 +4
intrinsics/ImmediateVariants 🔺 - +3🔺 +3 - +2🔺 +2
iteration/URangeIterationTest 🔺 - +4🔺 +4 - +2🔺 +2
literals 🔺 - +2🔺 +2 - +2🔺 +2
logic_signature/dont_use_this 🔺 - +3🔺 +3 - +2🔺 +2
logic_signature/pre_approved_sale 🔺 - +2🔺 +2 - +2🔺 +2
named_tuples 🔺 - +2🔺 +2 - +2🔺 +2
regression_tests/Issue434 🔺 - +3🔺 +3 - +2🔺 +2
reinterpret_cast 🔺 - +5🔺 +5 - +3🔺 +3
scratch_slots/MyContract 🔺 - +2🔺 +2 - +3🔺 +3
scratch_slots/MyContract2 🔺 - +2🔺 +2 - +3🔺 +3
state_proxies 🔺 - +6🔺 +6 - +6🔺 +6
string_ops 🔺 - +2🔺 +2 - +2🔺 +2
template_variables 🔺 - +4🔺 +4 - +4🔺 +4
too_many_permutations 🔺 - +3🔺 +3 - +2🔺 +2
transaction 🔺 - +2🔺 +2 - +2🔺 +2
typed_abi_call/Greeter 🔺 - +48🔺 +48 - +32🔺 +32
unssa 🔺 - +2🔺 +2 - +2🔺 +2
Total 🔺 - +201🔺 +195🔺 - +169🔺 +163🔺

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements safety checks to ensure explicit user asserts and error conditions are preserved through optimization passes and not inadvertently eliminated or modified during compilation.

Key changes:

  • Introduces explicit tracking of user-written assert/err operations via an explicit flag
  • Adds validation at MIR and TEAL optimization stages to prevent removal of explicit checks
  • Removes inline comment handling from TEAL operations in favor of a dedicated teal_comment property

Reviewed Changes

Copilot reviewed 115 out of 2294 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/puya/ir/models.py Added Assert operation class and explicit flag to Fail operation for tracking user-defined checks
src/puya/mir/models.py Added Assert operation and explicit flag to Err for MIR-level explicit check tracking
src/puya/teal/models.py Added Assert and updated Err with explicit flag; refactored comment handling to use teal_comment property
src/puya/mir/main.py Added validation to ensure explicit checks are not removed during global stack allocation
src/puya/teal/main.py Added validation to ensure explicit checks are preserved through TEAL optimization
src/puya/teal/optimize/peephole.py Updated peephole optimizer to only optimize non-explicit asserts
src/puya/teal/optimize/constant_block.py Simplified constant handling by removing TEAL alias resolution and comment logic
src/puya/teal/optimize/combine_pushes.py Refactored to use comments field on combined push operations
src/puya/teal/builder.py Updated to create Assert operations and handle constant comments during TEAL generation
test_cases/**/out_* Updated test output files to reflect removal of inline comments (e.g., // 1) from constant operations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@engineering-ci
Copy link

engineering-ci bot commented Nov 18, 2025

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/puya
   __main__.py36683%45, 52–53, 71–72, 78
   arc32.py89496%74, 125–130
   arc56.py81495%285–287, 297
   artifact_sorter.py57296%32, 89
   compile.py210797%98–99, 173–174, 182, 343, 401
   context.py52198%39
   errors.py41198%59
   log.py2657074%82, 92–93, 98–105, 120–128, 150, 173–174, 186–187, 190–191, 229–231, 234–236, 238, 251–267, 296, 302, 312–317, 320, 388–389, 426–428, 441–455, 479
   main.py41783%31, 50–55
   parse.py83495%31, 40, 50, 56
   program_refs.py28293%45, 56
   utils.py2261494%37, 196, 201–204, 222–224, 258, 265, 288, 290, 309, 348
src/puya/awst
   function_traverser.py325399%66, 76, 407
   nodes.py12866295%112, 116–119, 159, 163–166, 461, 526, 686, 755, 781, 839, 859, 898, 943, 972–973, 995, 999, 1067, 1088, 1119–1120, 1147, 1150–1152, 1170, 1204, 1207, 1245, 1250, 1377, 1403, 1436, 1468, 1494, 1499, 1746, 1890, 1909, 1975, 2013, 2022, 2241, 2290, 2308, 2312, 2430, 2577, 2582, 2587, 2595, 2600, 2605, 2642–2643, 2653
   serialize.py761284%21–25, 49, 94–100, 117, 121
   to_code_visitor.py4691397%149, 260, 297, 343, 380, 477–480, 660, 688, 692, 704–705
   txn_fields.py101199%53
   wtypes.py3761097%233, 254, 314, 366, 379, 390, 395–396, 437, 466
src/puya/awst/validation
   base_invoker.py47491%55, 62, 72–76
   immutable.py33294%29, 37
   inner_transactions.py196199%168
   labels.py30873%25–27, 32, 36–41
src/puya/ir
   _arc4_default_args.py1101586%63–64, 79, 92, 94, 96, 98, 100, 102, 108, 115, 136–137, 232, 239
   contract_metadata.py229797%56, 206–210, 308–314
   arc4_router.py259598%169, 233, 335, 353, 519
   arc4_types.py1621988%26–27, 46, 50, 54, 58, 62, 70, 96, 103, 110, 117, 129, 135, 139, 151, 211, 215, 230
   avm_ops.py324199%46
   avm_ops_models.py37295%15, 23
   context.py113695%79, 91, 98, 101, 103, 112
   encodings.py204697%230, 233, 239, 257, 294, 298
   main.py2241494%117, 126–132, 142–148, 174, 342–343, 355, 363
   models.py7962896%114, 218, 225, 420, 442, 503, 521, 525, 641–642, 766–767, 772, 778–782, 795, 852, 882, 938, 999, 1044, 1129, 1173, 1188, 1200, 1227, 1295–1296
   mutating_register_context.py87397%48–49, 51
   op_utils.py163299%212–219
   ssa.py151299%52–53
   types.py2512490%55, 58, 61, 74, 79–81, 85–87, 127, 141, 180, 247, 250, 261, 270, 274, 277, 295, 301, 399–403
   visitor.py185597%184, 285, 306, 312, 358
   visitor_mutator.py223797%89–92, 94, 193, 234
src/puya/ir/builder
   _utils.py39295%50, 52
   assignment.py119794%63, 79, 190, 192–193, 220, 265
   blocks.py134795%55, 92–96, 158, 166, 231
   bytes.py701480%17–49, 138
   callsub.py1091091%35–36, 57–60, 73, 118–122
   dynamic_array.py167597%81–82, 135–136, 172
   encoding_validation.py71199%121
   flow_control.py104298%53, 57
   iteration.py239598%103–104, 120, 152, 230
   itxn.py5564991%153–154, 156, 170, 208–209, 233–234, 593, 612–630, 661, 750, 774, 782, 789, 796, 800, 836, 848, 852, 866, 878, 884, 888, 908, 972, 982, 986, 990, 1006, 1018, 1046, 1052, 1056, 1060, 1064, 1082, 1093, 1104
   main.py7374993%165, 284, 296–315, 325–327, 394, 418, 442, 466–467, 491, 517, 559, 564–565, 576–577, 720, 745–746, 778, 813, 894, 959, 1058–1062, 1145, 1213, 1216, 1369, 1395, 1423–1424, 1575, 1595, 1650–1652
   sequence.py136696%191–196, 217–220, 235–236
   storage.py211598%122, 340–344, 356
src/puya/ir/builder/aggregates
   arc4_codecs.py3172592%58, 94, 147–148, 203, 271, 322, 335, 351, 394, 412, 428–430, 444–446, 520, 537, 539, 556–557, 572–573, 602
   main.py88397%76, 102, 116
   sequence.py156199%144
src/puya/ir/destructure
   coalesce_locals.py1442185%198, 209, 213–224, 230–231, 246–251
   critical_edges.py32197%24
   parcopy.py72199%80
src/puya/ir/optimize
   _call_graph.py32197%42
   add_box_extract_replace.py223498%195–196, 200, 408
   assignments.py114496%48, 168, 184–185
   compiled_reference.py101595%56, 87, 168–173
   constant_propagation.py71199%90
   control_op_simplification.py77199%170
   inlining.py2441195%34–43, 47, 54, 417, 421, 425, 435, 439
   inner_txn.py36294%49–50
   intrinsic_simplification.py8094295%268, 514, 545, 556–557, 625–626, 647, 752, 833, 864–893, 1244–1246, 1325, 1351, 1353, 1426, 1440, 1471, 1477, 1479, 1481, 1486, 1488, 1490, 1494, 1568
   repeated_loads_elimination.py142299%157, 173
src/puya/ir/validation
   _base.py30197%28
   compile_reference_validator.py20290%24, 30
   min_avm_version_validator.py15473%16–20
   op_run_mode_validator.py19195%29
   slot_reservation.py18194%20
src/puya/mir
   aligned_writer.py63297%21, 61
   builder.py2072389%129–131, 159–161, 180, 183, 186, 189, 192, 195, 198, 254, 261, 401, 404, 407, 410, 413, 416, 419, 422
   main.py77199%37
   models.py499499%120, 255, 321, 727
   output.py61198%20
   visitor.py86199%175
src/puya/mir/stack_allocation
   f_stack.py95397%58–67
   l_stack.py117199%71
   peephole.py41393%38, 40, 43
   x_stack.py203399%33, 331–335
src/puya/teal
   builder.py200299%74, 114
   models.py470199%461
   stack_manipulations.py31197%42
src/puya/teal/optimize
   constant_block.py93496%43, 126, 163, 180
   constant_stack_shuffling.py94990%54–55, 77–78, 94–100
   main.py176498%141, 200, 227–228
   peephole.py137299%170, 295
   repeated_rotations.py51590%16, 52–55
   repeated_rotations_search.py90693%35, 41–42, 58, 68–69
src/puya/ussemble
   assemble.py201399%253, 273, 317
   models.py26196%16
   op_spec_models.py22195%20
src/puyapy
   __main__.py43686%155, 160–165, 204, 213
   client_gen.py1101190%59–60, 78–82, 86, 200, 207–208, 228
   compile.py911386%41, 57, 68, 72, 132–133, 141–144, 153–156
   find_sources.py97397%46, 105, 124
   interpreter_data.py18289%32–33
   models.py103496%76, 88–90
   parse.py2473287%114–119, 178, 188, 196, 208–209, 225–227, 238–243, 251–256, 260, 420–423, 427–428, 441–443, 448
   template.py32875%10–11, 18–19, 27–28, 34, 37
src/puyapy/awst_build
   arc4_client.py1042774%46–50, 58, 70, 76, 80, 97, 109, 115–116, 122, 125, 128, 134, 137, 140–145, 148, 151, 154, 157, 160, 163, 166, 169, 172
   arc4_client_gen.py134894%30, 89–90, 100, 102, 117–118, 188
   arc4_decorators.py2435179%46, 91–92, 99–101, 112–116, 124–126, 147–149, 152, 177, 189, 197, 211, 213, 243–244, 261–262, 277–278, 285, 289–295, 301, 309, 328, 336, 340, 350–354, 370, 372, 375–376, 385, 388–389, 391
   arc4_utils.py122497%172, 179, 193, 221
   base_mypy_visitor.py1603876%77–83, 103, 112–125, 139, 141, 143, 156, 161, 166, 170, 174, 200, 204, 208, 217, 222, 226, 230, 235, 255, 260, 265, 270, 275, 280, 285, 290, 295, 300, 305, 310, 315
   context.py2644981%57, 60, 70–71, 89–90, 130, 204, 209, 215–219, 226, 235, 237, 240–242, 244, 251, 253, 264–265, 270–272, 275, 290, 302–303, 315, 329, 332–344, 362, 380, 405
   contract.py3313689%125, 177, 206–210, 250, 252, 256, 260, 268, 280, 282, 304–311, 368, 371, 383, 391, 394, 397, 400, 403, 406, 409, 412, 415, 418, 511–515, 564–568, 642–646, 704, 726
   intrinsic_models.py49198%55
   main.py42198%41
   module.py4456685%135, 160, 166–187, 206–207, 214, 223–224, 231–235, 257, 286, 306–307, 318, 340–343, 353–355, 361, 378–381, 394, 428, 457–458, 481–482, 540–541, 569, 580, 583, 589, 595, 605, 611, 614, 626, 629, 652, 672, 677, 681, 690–694, 786, 794, 796
   pytypes.py6366390%87, 97–99, 104, 111, 116–118, 122–124, 151–152, 192, 210–216, 239, 263, 305, 343–345, 374, 441, 450, 469, 487, 491, 606–607, 691–693, 707–708, 775–776, 880, 891–892, 930–931, 978–979, 984, 1053–1054, 1077–1078, 1229–1230, 1258, 1286, 1322–1324, 1364, 1374–1375
   subroutine.py6574993%162, 169, 189–190, 287, 313–314, 332, 349, 374, 382, 393, 420–423, 535–536, 548, 551, 554, 557, 560, 608, 741, 752, 754, 763, 781, 784, 816, 822, 883, 891–894, 900, 951, 956, 959–965, 1056, 1076, 1232, 1272, 1298, 1314–1315, 1324, 1328
   utils.py1862984%28, 45–49, 67, 102–103, 105, 149–150, 201, 209, 214, 227–231, 236–239, 248, 252, 260, 294, 308–311
src/puyapy/awst_build/eb
   _base.py1271687%52, 57–59, 64, 71, 80–82, 143, 154, 184, 189, 199, 210, 224–226
   _bytes_backed.py48296%30–31
   _expect.py1221786%25, 36, 85–88, 107, 160–161, 219–222, 232–235
   _literals.py1563478%44, 65, 77, 96, 125, 142, 156–160, 164, 168–174, 184–198, 203
   _type_registry.py41393%270–271, 284
   utils.py62395%36–38, 108
   biguint.py101694%56, 100, 139, 155–156, 158
   binary_bool_op.py105397%153, 161, 171
   bool.py55984%38–42, 58, 69, 82, 96
   bytes.py1731790%102–103, 130–131, 136–137, 143–144, 147, 155, 198, 233, 271, 292–293, 313–314
   compiled.py70987%85–89, 126–130, 153
   conditional_literal.py1333474%97, 101, 161, 165–168, 177–179, 202–205, 214, 218, 222–225, 240–252, 261–262, 273–276
   contracts.py77890%55, 61, 63, 73, 99, 109, 111, 116
   dict.py27581%24, 32–34, 38
   ensure_budget.py31197%46
   fixed_bytes.py2741296%110–111, 142, 250, 335, 412, 464, 516, 522, 586–587, 596
   interface.py91298%317–319
   intrinsics.py119893%50–51, 64, 86, 93, 106, 113, 205
   log.py43491%45–46, 51, 60
   logicsig.py15193%25
   none.py27485%17, 27–28, 37
   size_of.py25388%31–33
   string.py1441391%71, 115–116, 135, 140, 183, 190, 194, 206, 280–282, 302
   subroutine.py801680%46, 50–53, 68, 71–78, 93, 101–102, 104–107, 112
   template_variables.py37295%29, 57
   tuple.py3381296%87, 94, 135, 154, 257, 350–351, 470, 546, 557–558, 621
   uint64.py108595%57, 118–119, 167–168
   uint64_enums.py40295%40, 45
   unsigned_builtins.py1552286%73, 80, 104, 128, 132, 136, 140, 148, 152, 156, 160, 164, 174, 178, 184, 195, 201, 207, 246, 278, 290, 302
src/puyapy/awst_build/eb/arc4
   _base.py106595%184–187, 198, 228–229
   _utils.py123596%44, 103, 134, 197, 201
   abi_call.py3522194%123, 129, 150, 220, 233–234, 317, 329, 411, 435, 468, 487–488, 507, 541, 586, 622, 696, 781–782, 799
   address.py73297%56, 115
   bool.py60198%42
   dynamic_array.py122993%109–110, 128, 152, 206, 221–222, 225–228
   dynamic_bytes.py68396%98–100
   emit.py53296%40–43
   string.py100793%54–55, 105, 126, 131–134
   struct.py82199%54
   tuple.py921287%51–53, 91–94, 97–98, 137–140, 145, 170
   ufixed.py74297%51, 110
   uint.py96298%101–106
src/puyapy/awst_build/eb/reference_types
   account.py82199%179
   application.py45198%39
   asset.py65198%47
src/puyapy/awst_build/eb/storage
   _common.py69396%106, 119–120
   _storage.py1082081%58, 66, 70, 74, 78, 82, 86, 90, 94, 104, 108, 112, 116, 122, 133, 139, 145, 157–159
   _value_proxy.py55493%42, 50, 54, 91
   box.py113199%174
   box_map.py164299%199, 265
   box_ref.py1111785%76–80, 89–93, 103, 111, 127, 135, 148, 153–162
   global_state.py127695%103–104, 113–114, 163–164
   local_state.py1371192%98–99, 103, 150, 154, 158, 168, 172, 196, 253, 277
src/puyapy/awst_build/eb/transaction
   base.py39295%22, 42
   group.py51198%47
   inner.py48296%90–91
   inner_params.py77594%63, 73, 77, 137, 139
   itxn_args.py60198%72
src/puyapy/lsp
   __main__.py17170%1–44
   analyse.py26819129%58–70, 76–183, 186–197, 201–216, 226–246, 249, 252–254, 265–281, 288–332, 349–373, 381, 385–386, 395–399, 404–411, 434–449, 461, 465, 467, 473, 482–487, 491
   log.py44440%1–89
   server.py1338238%62–70, 74–79, 86–136, 153–155, 158–162, 165–172, 179–197, 200, 203–208, 218–220
src/puyapy/validation
   arc4_copy.py130298%35, 39
TOTAL30021202193% 

Tests Skipped Failures Errors Time
1202 3 💤 0 ❌ 0 🔥 11m 0s ⏱️

@achidlow achidlow force-pushed the user-assert-tracking-rebase branch from bad993b to 7e0b283 Compare November 18, 2025 09:17
 - use explicit `assert` op models from IR down in order to track explicit asserts vs generated asserts
 - similarly, track explicit vs generated `err` ops
 - require the use of `AssertExpression` node in AWST, both puyapy and puya-ts already do this
 - add explicit flag to `AssertExpression` for use by ARC-4 router code generator
 - with the explicit `assert` and `err` op models, simplify the handling of TEAL op comment/error formatting
@achidlow achidlow force-pushed the user-assert-tracking-rebase branch from 7e0b283 to bf6321b Compare November 20, 2025 01:11
@achidlow achidlow merged commit fa8139a into main Nov 20, 2025
31 checks passed
@achidlow achidlow deleted the user-assert-tracking-rebase branch November 20, 2025 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants