diff --git a/examples/sizes.txt b/examples/sizes.txt index b2415f1f74..9b5a21b779 100644 --- a/examples/sizes.txt +++ b/examples/sizes.txt @@ -64,6 +64,7 @@ stubs/BigUInt 172 112 112 stubs/Bytes 1769 258 258 stubs/Uint64 371 8 8 template_variables/TemplateVariables 168 155 155 +throwaway/Throwaway 45 31 31 too_many_permutations 108 107 107 transaction/Transaction 893 849 849 tuple_support/TupleSupport 442 294 294 diff --git a/src/puya/awst/nodes.py b/src/puya/awst/nodes.py index 21e2552bb0..c9fb779da0 100644 --- a/src/puya/awst/nodes.py +++ b/src/puya/awst/nodes.py @@ -1032,7 +1032,7 @@ class AssignmentStatement(Statement): value: Expression = attrs.field(validator=[lvalue_expr_validator]) def __attrs_post_init__(self) -> None: - if self.value.wtype != self.target.wtype: + if not wtypes.is_valid_assignment(self.target.wtype, self.value.wtype): raise CodeError( f"Assignment target type {self.target.wtype}" f" differs from expression value type {self.value.wtype}", @@ -1063,7 +1063,7 @@ def __init__(self, value: Expression, target: Lvalue, source_location: SourceLoc "Tuple unpacking in assignment expressions is not supported", target.source_location, ) - if value.wtype != target.wtype: + if not wtypes.is_valid_assignment(target.wtype, value.wtype): raise CodeError( f"Assignment target type {target.wtype}" f" differs from expression value type {value.wtype}", diff --git a/src/puya/awst/wtypes.py b/src/puya/awst/wtypes.py index f3fabc1112..0df4325761 100644 --- a/src/puya/awst/wtypes.py +++ b/src/puya/awst/wtypes.py @@ -70,6 +70,11 @@ def is_valid_utf8_literal(value: object) -> typing.TypeGuard[str]: lvalue=False, ) +throwaway_type: typing.Final = WType( + name="throwaway", + stub_name="typing.Any", +) + bool_wtype: typing.Final = WType( name="bool", stub_name="bool", @@ -526,3 +531,18 @@ def arc4_to_avm_equivalent_wtype(arc4_wtype: WType) -> WType: return bool_wtype raise InternalError(f"Invalid arc4_wtype: {arc4_wtype}") + + +def is_valid_assignment(target: WType, source: WType) -> bool: + if target == throwaway_type: + return True + if ( + isinstance(target, WTuple) + and isinstance(source, WTuple) + and len(target.types) == len(source.types) + ): + return all( + is_valid_assignment(target_item, source_item) + for target_item, source_item in zip(target.types, source.types, strict=True) + ) + return target == source diff --git a/src/puya/awst_build/eb/throwaway.py b/src/puya/awst_build/eb/throwaway.py new file mode 100644 index 0000000000..24736c72cb --- /dev/null +++ b/src/puya/awst_build/eb/throwaway.py @@ -0,0 +1,68 @@ +import typing +from collections.abc import Sequence + +import mypy.nodes + +from puya.awst import wtypes +from puya.awst.nodes import Expression, Literal, Lvalue, VarExpression +from puya.awst_build.eb.base import ExpressionBuilder +from puya.errors import CodeError, InternalError +from puya.parse import SourceLocation + + +class ThrowawayExpressionBuilder(ExpressionBuilder): + wtype = wtypes.throwaway_type + + def __init__(self, expr: Expression): + super().__init__(expr.source_location) + if not isinstance(expr, VarExpression): + raise InternalError("Expected a VarExpression", expr.source_location) + self._expr = expr + + def lvalue(self) -> Lvalue: + return self._expr + + def rvalue(self) -> typing.Never: + self._raise_error(self.source_location) + + def delete(self, location: SourceLocation) -> typing.Never: + self._raise_error(location) + + def index(self, index: ExpressionBuilder | Literal, location: SourceLocation) -> typing.Never: + self._raise_error(location) + + def call( + self, + args: Sequence[ExpressionBuilder | Literal], + arg_kinds: list[mypy.nodes.ArgKind], + arg_names: list[str | None], + location: SourceLocation, + original_expr: mypy.nodes.CallExpr, + ) -> typing.Never: + self._raise_error(location) + + def member_access(self, name: str, location: SourceLocation) -> typing.Never: + self._raise_error(location) + + def iterate(self) -> typing.Never: + self._raise_error(self.source_location) + + def bool_eval(self, location: SourceLocation, *, negate: bool = False) -> typing.Never: + self._raise_error(location) + + def unary_plus(self, location: SourceLocation) -> typing.Never: + self._raise_error(location) + + def unary_minus(self, location: SourceLocation) -> typing.Never: + self._raise_error(location) + + def bitwise_invert(self, location: SourceLocation) -> typing.Never: + self._raise_error(location) + + def contains( + self, item: ExpressionBuilder | Literal, location: SourceLocation + ) -> typing.Never: + self._raise_error(location) + + def _raise_error(self, location: SourceLocation) -> typing.Never: + raise CodeError("'_' variables can only be assigned to", location) diff --git a/src/puya/awst_build/eb/type_registry.py b/src/puya/awst_build/eb/type_registry.py index b0c381f1d2..c91fa8499e 100644 --- a/src/puya/awst_build/eb/type_registry.py +++ b/src/puya/awst_build/eb/type_registry.py @@ -18,6 +18,7 @@ named_int_constants, struct, template_variables, + throwaway, transaction, tuple as tuple_, uint64, @@ -133,6 +134,7 @@ wtypes.WGroupTransaction: transaction.GroupTransactionExpressionBuilder, wtypes.WInnerTransaction: transaction.InnerTransactionExpressionBuilder, wtypes.WInnerTransactionFields: transaction.InnerTxnParamsExpressionBuilder, + wtypes.throwaway_type: throwaway.ThrowawayExpressionBuilder, } @@ -149,5 +151,8 @@ def var_expression(expr: Expression) -> ExpressionBuilder: try: builder = WTYPE_TO_BUILDER[expr.wtype] except KeyError: - builder = WTYPE_TO_BUILDER[type(expr.wtype)] + try: + builder = WTYPE_TO_BUILDER[type(expr.wtype)] + except KeyError as ex: + raise InternalError(f"Unhandled wtype: {expr.wtype}", expr.source_location) from ex return builder(expr) diff --git a/src/puya/awst_build/subroutine.py b/src/puya/awst_build/subroutine.py index d303c05d00..5b124d229f 100644 --- a/src/puya/awst_build/subroutine.py +++ b/src/puya/awst_build/subroutine.py @@ -45,7 +45,6 @@ VarExpression, WhileLoop, ) -from puya.awst.wtypes import WType from puya.awst_build import constants, intrinsic_data from puya.awst_build.base_mypy_visitor import BaseMyPyVisitor from puya.awst_build.context import ASTConversionModuleContext @@ -120,6 +119,7 @@ def __init__( func_loc = self._location(func_def) self.contract_method_info = contract_method_info self._is_bool_context = False + self._is_lvalue_context = False self.func_def = func_def self._precondition( func_def.abstract_status == mypy.nodes.NOT_ABSTRACT, @@ -161,7 +161,11 @@ def __init__( func_loc, ) # TODO: this should be more than just type? - self._symtable = dict[str, WType]() + # mypy has special behaviour to treat '_' as Any, so predefine the '_' symbol as + # a type that can only be assigned to + self._symtable = { + "_": wtypes.throwaway_type, + } args = list[SubroutineArgument]() for arg, arg_type in zip(mypy_args, mypy_arg_types, strict=True): if arg.kind.is_star(): @@ -266,6 +270,18 @@ def _set_bool_context(self, *, is_bool_context: bool) -> Iterator[None]: _enter_bool_context = partialmethod(_set_bool_context, is_bool_context=True) _leave_bool_context = partialmethod(_set_bool_context, is_bool_context=False) + @contextlib.contextmanager + def _set_lvalue_context(self, *, is_lvalue_context: bool) -> Iterator[None]: + was_lvalue_context = self._is_lvalue_context + self._is_lvalue_context = is_lvalue_context + try: + yield + finally: + self._is_lvalue_context = was_lvalue_context + + _enter_lvalue_context = partialmethod(_set_lvalue_context, is_lvalue_context=True) + _leave_lvalue_context = partialmethod(_set_lvalue_context, is_lvalue_context=False) + def visit_expression_stmt(self, stmt: mypy.nodes.ExpressionStmt) -> ExpressionStatement: self._precondition( stmt.line == stmt.expr.line @@ -391,9 +407,10 @@ def _handle_state_proxy_assignment( ] def resolve_lvalue(self, lvalue: mypy.nodes.Expression) -> Lvalue: - builder_or_literal = lvalue.accept(self) - builder = require_expression_builder(builder_or_literal) - return builder.lvalue() + with self._enter_lvalue_context(): + builder_or_literal = lvalue.accept(self) + builder = require_expression_builder(builder_or_literal) + return builder.lvalue() def empty_statement(self, _tmt: mypy.nodes.Statement) -> None: return None @@ -700,9 +717,6 @@ def _visit_ref_expr_maybe_aliased( " as a singular lvalue in an assignment statement", expr_loc, ) - if var_name == "_": - # TODO: ignore "_" - raise CodeError("_ is not currently supported as a variable name", expr_loc) local_type = lazy_setdefault( self._symtable, key=var_name, @@ -1146,11 +1160,14 @@ def visit_bytes_expr(self, expr: mypy.nodes.BytesExpr) -> Literal: def visit_tuple_expr(self, mypy_expr: mypy.nodes.TupleExpr) -> ExpressionBuilder: items = [ - require_expression_builder( - mypy_item.accept(self), - msg="Python literals (other than True/False) are not valid as tuple elements", - ).rvalue() - for mypy_item in mypy_expr.items + eb.lvalue() if self._is_lvalue_context else eb.rvalue() + for eb in ( + require_expression_builder( + mypy_item.accept(self), + msg="Python literals (other than True/False) are not valid as tuple elements", + ) + for mypy_item in mypy_expr.items + ) ] wtype = wtypes.WTuple.from_types(i.wtype for i in items) tuple_expr = TupleExpression( diff --git a/test_cases/throwaway/contract.py b/test_cases/throwaway/contract.py new file mode 100644 index 0000000000..c3180d0886 --- /dev/null +++ b/test_cases/throwaway/contract.py @@ -0,0 +1,20 @@ +from puyapy import Account, Bytes, Contract, Global, Txn, UInt64, subroutine + + +class Throwaway(Contract): + def approval_program(self) -> bool: + tup = get_tuple() + args, sender, _ = tup + _, _, approval = tup + assert sender == Global.creator_address + assert args == 0 + assert approval + return True + + def clear_state_program(self) -> bool: + return True + + +@subroutine +def get_tuple() -> tuple[UInt64, Account, Bytes]: + return Txn.num_app_args, Txn.sender, Txn.approval_program diff --git a/test_cases/throwaway/out/Throwaway.approval.mir b/test_cases/throwaway/out/Throwaway.approval.mir new file mode 100644 index 0000000000..a6d1858bbd --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.approval.mir @@ -0,0 +1,48 @@ +// Op // Op Description Stack (out) X stack Source code Source line + +#pragma version 10 + +// test_cases.throwaway.contract.Throwaway.approval_program() -> uint64: +main_block@0: + callsub get_tuple // {get_tuple}.0,{get_tuple}.1,{get_tuple}.2 get_tuple() throwaway/contract.py:6 + cover 2 // store tup.2#0 to l-stack (no copy) tup.2#0,{get_tuple}.0,{get_tuple}.1 tup = get_tuple() throwaway/contract.py:6 + // virtual: store tup.1#0 to l-stack (no copy) tup.2#0,tup.1#0,{get_tuple}.0 tup = get_tuple() throwaway/contract.py:6 + // virtual: store tup.0#0 to l-stack (no copy) tup.2#0,tup.0#0,tup.1#0 tup = get_tuple() throwaway/contract.py:6 + global CreatorAddress // tup.2#0,tup.0#0,tup.1#0,{global} Global.creator_address throwaway/contract.py:9 + // virtual: store tmp%0#0 to l-stack (no copy) tup.2#0,tup.0#0,tup.1#0,tmp%0#0 Global.creator_address throwaway/contract.py:9 + // virtual: load tup.1#0 from l-stack (no copy) tup.2#0,tup.0#0,tmp%0#0,tup.1#0 sender == Global.creator_address throwaway/contract.py:9 + // virtual: load tmp%0#0 from l-stack (no copy) tup.2#0,tup.0#0,tup.1#0,tmp%0#0 sender == Global.creator_address throwaway/contract.py:9 + == // tup.2#0,tup.0#0,{==} sender == Global.creator_address throwaway/contract.py:9 + // virtual: store tmp%1#0 to l-stack (no copy) tup.2#0,tup.0#0,tmp%1#0 sender == Global.creator_address throwaway/contract.py:9 + // virtual: load tmp%1#0 from l-stack (no copy) tup.2#0,tup.0#0,tmp%1#0 assert sender == Global.creator_address throwaway/contract.py:9 + assert // tup.2#0,tup.0#0 assert sender == Global.creator_address throwaway/contract.py:9 + // virtual: load tup.0#0 from l-stack (no copy) tup.2#0,tup.0#0 args == 0 throwaway/contract.py:10 + ! // tup.2#0,{!} args == 0 throwaway/contract.py:10 + // virtual: store tmp%2#0 to l-stack (no copy) tup.2#0,tmp%2#0 args == 0 throwaway/contract.py:10 + // virtual: load tmp%2#0 from l-stack (no copy) tup.2#0,tmp%2#0 assert args == 0 throwaway/contract.py:10 + assert // tup.2#0 assert args == 0 throwaway/contract.py:10 + // virtual: load tup.2#0 from l-stack (no copy) tup.2#0 approval throwaway/contract.py:11 + len // {len} approval throwaway/contract.py:11 + // virtual: store tmp%3#0 to l-stack (no copy) tmp%3#0 approval throwaway/contract.py:11 + // virtual: load tmp%3#0 from l-stack (no copy) tmp%3#0 assert approval throwaway/contract.py:11 + assert // assert approval throwaway/contract.py:11 + int 1 // 1 True throwaway/contract.py:12 + return // return True throwaway/contract.py:12 + + +// test_cases.throwaway.contract.get_tuple() -> uint64, bytes, bytes: +get_tuple: + proto 0 3 // @subroutine\ndef get_tuple() -> tuple[UInt64, Account, Bytes]: throwaway/contract.py:18-19 + +get_tuple_block@0: + txn NumAppArgs // {txn} Txn.num_app_args throwaway/contract.py:20 + // virtual: store tmp%0#0 to l-stack (no copy) tmp%0#0 Txn.num_app_args throwaway/contract.py:20 + txn Sender // tmp%0#0,{txn} Txn.sender throwaway/contract.py:20 + // virtual: store tmp%1#0 to l-stack (no copy) tmp%0#0,tmp%1#0 Txn.sender throwaway/contract.py:20 + txn ApprovalProgram // tmp%0#0,tmp%1#0,{txn} Txn.approval_program throwaway/contract.py:20 + // virtual: store tmp%2#0 to l-stack (no copy) tmp%0#0,tmp%1#0,tmp%2#0 Txn.approval_program throwaway/contract.py:20 + uncover 2 // load tmp%0#0 from l-stack (no copy) tmp%1#0,tmp%2#0,tmp%0#0 return Txn.num_app_args, Txn.sender, Txn.approval_program throwaway/contract.py:20 + uncover 2 // load tmp%1#0 from l-stack (no copy) tmp%2#0,tmp%0#0,tmp%1#0 return Txn.num_app_args, Txn.sender, Txn.approval_program throwaway/contract.py:20 + uncover 2 // load tmp%2#0 from l-stack (no copy) tmp%0#0,tmp%1#0,tmp%2#0 return Txn.num_app_args, Txn.sender, Txn.approval_program throwaway/contract.py:20 + retsub // tmp%0#0,tmp%1#0,tmp%2#0 return Txn.num_app_args, Txn.sender, Txn.approval_program throwaway/contract.py:20 + diff --git a/test_cases/throwaway/out/Throwaway.approval.teal b/test_cases/throwaway/out/Throwaway.approval.teal new file mode 100644 index 0000000000..5f48ef13f9 --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.approval.teal @@ -0,0 +1,38 @@ +#pragma version 10 + +test_cases.throwaway.contract.Throwaway.approval_program: + // throwaway/contract.py:6 + // tup = get_tuple() + callsub get_tuple + cover 2 + // throwaway/contract.py:9 + // assert sender == Global.creator_address + global CreatorAddress + == + assert + // throwaway/contract.py:10 + // assert args == 0 + ! + assert + // throwaway/contract.py:11 + // assert approval + len + assert + // throwaway/contract.py:12 + // return True + int 1 + return + + +// test_cases.throwaway.contract.get_tuple() -> uint64, bytes, bytes: +get_tuple: + // throwaway/contract.py:18-19 + // @subroutine + // def get_tuple() -> tuple[UInt64, Account, Bytes]: + proto 0 3 + // throwaway/contract.py:20 + // return Txn.num_app_args, Txn.sender, Txn.approval_program + txn NumAppArgs + txn Sender + txn ApprovalProgram + retsub diff --git a/test_cases/throwaway/out/Throwaway.clear.mir b/test_cases/throwaway/out/Throwaway.clear.mir new file mode 100644 index 0000000000..cb4f92853d --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.clear.mir @@ -0,0 +1,9 @@ +// Op // Stack (out) Source code Source line + +#pragma version 10 + +// test_cases.throwaway.contract.Throwaway.clear_state_program() -> uint64: +main_block@0: + int 1 // 1 True throwaway/contract.py:15 + return // return True throwaway/contract.py:15 + diff --git a/test_cases/throwaway/out/Throwaway.clear.teal b/test_cases/throwaway/out/Throwaway.clear.teal new file mode 100644 index 0000000000..5b1642d8ee --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.clear.teal @@ -0,0 +1,7 @@ +#pragma version 10 + +test_cases.throwaway.contract.Throwaway.clear_state_program: + // throwaway/contract.py:15 + // return True + int 1 + return diff --git a/test_cases/throwaway/out/Throwaway.destructured.ir b/test_cases/throwaway/out/Throwaway.destructured.ir new file mode 100644 index 0000000000..de5570cac9 --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.destructured.ir @@ -0,0 +1,25 @@ +contract test_cases.throwaway.contract.Throwaway: + program approval: + subroutine test_cases.throwaway.contract.Throwaway.approval_program() -> uint64: + block@0: // L5 + let (tup.0#0: uint64, tup.1#0: bytes, tup.2#0: bytes) = test_cases.throwaway.contract.get_tuple() + let tmp%0#0: bytes = (global CreatorAddress) + let tmp%1#0: uint64 = (== tup.1#0 tmp%0#0) + (assert tmp%1#0) + let tmp%2#0: uint64 = (! tup.0#0) + (assert tmp%2#0) + let tmp%3#0: uint64 = (len tup.2#0) + (assert tmp%3#0) + return 1u + + subroutine test_cases.throwaway.contract.get_tuple() -> : + block@0: // L18 + let tmp%0#0: uint64 = (txn NumAppArgs) + let tmp%1#0: bytes = (txn Sender) + let tmp%2#0: bytes = (txn ApprovalProgram) + return tmp%0#0 tmp%1#0 tmp%2#0 + + program clear-state: + subroutine test_cases.throwaway.contract.Throwaway.clear_state_program() -> uint64: + block@0: // L14 + return 1u \ No newline at end of file diff --git a/test_cases/throwaway/out/Throwaway.ssa.ir b/test_cases/throwaway/out/Throwaway.ssa.ir new file mode 100644 index 0000000000..86287e9f3b --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.ssa.ir @@ -0,0 +1,31 @@ +contract test_cases.throwaway.contract.Throwaway: + program approval: + subroutine test_cases.throwaway.contract.Throwaway.approval_program() -> uint64: + block@0: // L5 + let (tup.0#0: uint64, tup.1#0: bytes, tup.2#0: bytes) = test_cases.throwaway.contract.get_tuple() + let args#0: uint64 = tup.0#0 + let sender#0: bytes = tup.1#0 + let _#0: bytes = tup.2#0 + let _#1: uint64 = tup.0#0 + let _#2: bytes = tup.1#0 + let approval#0: bytes = tup.2#0 + let tmp%0#0: bytes = (global CreatorAddress) + let tmp%1#0: uint64 = (== sender#0 tmp%0#0) + (assert tmp%1#0) + let tmp%2#0: uint64 = (== args#0 0u) + (assert tmp%2#0) + let tmp%3#0: uint64 = (len approval#0) + (assert tmp%3#0) + return 1u + + subroutine test_cases.throwaway.contract.get_tuple() -> : + block@0: // L18 + let tmp%0#0: uint64 = (txn NumAppArgs) + let tmp%1#0: bytes = (txn Sender) + let tmp%2#0: bytes = (txn ApprovalProgram) + return tmp%0#0 tmp%1#0 tmp%2#0 + + program clear-state: + subroutine test_cases.throwaway.contract.Throwaway.clear_state_program() -> uint64: + block@0: // L14 + return 1u \ No newline at end of file diff --git a/test_cases/throwaway/out/Throwaway.ssa.opt_pass_1.ir b/test_cases/throwaway/out/Throwaway.ssa.opt_pass_1.ir new file mode 100644 index 0000000000..de5570cac9 --- /dev/null +++ b/test_cases/throwaway/out/Throwaway.ssa.opt_pass_1.ir @@ -0,0 +1,25 @@ +contract test_cases.throwaway.contract.Throwaway: + program approval: + subroutine test_cases.throwaway.contract.Throwaway.approval_program() -> uint64: + block@0: // L5 + let (tup.0#0: uint64, tup.1#0: bytes, tup.2#0: bytes) = test_cases.throwaway.contract.get_tuple() + let tmp%0#0: bytes = (global CreatorAddress) + let tmp%1#0: uint64 = (== tup.1#0 tmp%0#0) + (assert tmp%1#0) + let tmp%2#0: uint64 = (! tup.0#0) + (assert tmp%2#0) + let tmp%3#0: uint64 = (len tup.2#0) + (assert tmp%3#0) + return 1u + + subroutine test_cases.throwaway.contract.get_tuple() -> : + block@0: // L18 + let tmp%0#0: uint64 = (txn NumAppArgs) + let tmp%1#0: bytes = (txn Sender) + let tmp%2#0: bytes = (txn ApprovalProgram) + return tmp%0#0 tmp%1#0 tmp%2#0 + + program clear-state: + subroutine test_cases.throwaway.contract.Throwaway.clear_state_program() -> uint64: + block@0: // L14 + return 1u \ No newline at end of file diff --git a/test_cases/throwaway/out/contract.awst b/test_cases/throwaway/out/contract.awst new file mode 100644 index 0000000000..f87b5b4a62 --- /dev/null +++ b/test_cases/throwaway/out/contract.awst @@ -0,0 +1,23 @@ +contract Throwaway +{ + approval_program(): bool + { + tup: tuple[puyapy.UInt64, puyapy.Account, puyapy.Bytes] = test_cases.throwaway.contract::get_tuple() + (args, sender, _): tuple[puyapy.UInt64, puyapy.Account, typing.Any] = tup + (_, _, approval): tuple[typing.Any, typing.Any, puyapy.Bytes] = tup + assert(sender == global()) + assert(args == 0u) + assert(reinterpret_cast(len(approval))) + return true + } + + clear_state_program(): bool + { + return true + } +} + +subroutine get_tuple(): tuple[puyapy.UInt64, puyapy.Account, puyapy.Bytes] +{ + return (txn(), txn(), txn()) +} \ No newline at end of file diff --git a/test_cases/throwaway/out_O2/Throwaway.approval.teal b/test_cases/throwaway/out_O2/Throwaway.approval.teal new file mode 100644 index 0000000000..98beb61844 --- /dev/null +++ b/test_cases/throwaway/out_O2/Throwaway.approval.teal @@ -0,0 +1,23 @@ +#pragma version 10 + +test_cases.throwaway.contract.Throwaway.approval_program: + callsub get_tuple + cover 2 + global CreatorAddress + == + assert + ! + assert + len + assert + int 1 + return + + +// test_cases.throwaway.contract.get_tuple() -> uint64, bytes, bytes: +get_tuple: + proto 0 3 + txn NumAppArgs + txn Sender + txn ApprovalProgram + retsub diff --git a/test_cases/throwaway/out_O2/Throwaway.clear.teal b/test_cases/throwaway/out_O2/Throwaway.clear.teal new file mode 100644 index 0000000000..59c6821eb8 --- /dev/null +++ b/test_cases/throwaway/out_O2/Throwaway.clear.teal @@ -0,0 +1,5 @@ +#pragma version 10 + +test_cases.throwaway.contract.Throwaway.clear_state_program: + int 1 + return diff --git a/test_cases/throwaway/out_O2/Throwaway.destructured.ir b/test_cases/throwaway/out_O2/Throwaway.destructured.ir new file mode 100644 index 0000000000..de5570cac9 --- /dev/null +++ b/test_cases/throwaway/out_O2/Throwaway.destructured.ir @@ -0,0 +1,25 @@ +contract test_cases.throwaway.contract.Throwaway: + program approval: + subroutine test_cases.throwaway.contract.Throwaway.approval_program() -> uint64: + block@0: // L5 + let (tup.0#0: uint64, tup.1#0: bytes, tup.2#0: bytes) = test_cases.throwaway.contract.get_tuple() + let tmp%0#0: bytes = (global CreatorAddress) + let tmp%1#0: uint64 = (== tup.1#0 tmp%0#0) + (assert tmp%1#0) + let tmp%2#0: uint64 = (! tup.0#0) + (assert tmp%2#0) + let tmp%3#0: uint64 = (len tup.2#0) + (assert tmp%3#0) + return 1u + + subroutine test_cases.throwaway.contract.get_tuple() -> : + block@0: // L18 + let tmp%0#0: uint64 = (txn NumAppArgs) + let tmp%1#0: bytes = (txn Sender) + let tmp%2#0: bytes = (txn ApprovalProgram) + return tmp%0#0 tmp%1#0 tmp%2#0 + + program clear-state: + subroutine test_cases.throwaway.contract.Throwaway.clear_state_program() -> uint64: + block@0: // L14 + return 1u \ No newline at end of file diff --git a/test_cases/throwaway/out_unoptimized/Throwaway.approval.teal b/test_cases/throwaway/out_unoptimized/Throwaway.approval.teal new file mode 100644 index 0000000000..36db640ab0 --- /dev/null +++ b/test_cases/throwaway/out_unoptimized/Throwaway.approval.teal @@ -0,0 +1,50 @@ +#pragma version 10 + +test_cases.throwaway.contract.Throwaway.approval_program: + // throwaway/contract.py:6 + // tup = get_tuple() + callsub get_tuple + cover 2 + swap + // throwaway/contract.py:7 + // args, sender, _ = tup + cover 2 + // throwaway/contract.py:8 + // _, _, approval = tup + swap + cover 2 + // throwaway/contract.py:9 + // assert sender == Global.creator_address + global CreatorAddress + == + assert + // throwaway/contract.py:10 + // assert args == 0 + int 0 + == + assert + // throwaway/contract.py:11 + // assert approval + len + assert + // throwaway/contract.py:12 + // return True + int 1 + return + + +// test_cases.throwaway.contract.get_tuple() -> uint64, bytes, bytes: +get_tuple: + // throwaway/contract.py:18-19 + // @subroutine + // def get_tuple() -> tuple[UInt64, Account, Bytes]: + proto 0 3 + // throwaway/contract.py:20 + // return Txn.num_app_args, Txn.sender, Txn.approval_program + txn NumAppArgs + txn Sender + txn ApprovalProgram + uncover 2 + uncover 2 + uncover 2 + retsub diff --git a/test_cases/throwaway/out_unoptimized/Throwaway.clear.teal b/test_cases/throwaway/out_unoptimized/Throwaway.clear.teal new file mode 100644 index 0000000000..5b1642d8ee --- /dev/null +++ b/test_cases/throwaway/out_unoptimized/Throwaway.clear.teal @@ -0,0 +1,7 @@ +#pragma version 10 + +test_cases.throwaway.contract.Throwaway.clear_state_program: + // throwaway/contract.py:15 + // return True + int 1 + return diff --git a/test_cases/throwaway/out_unoptimized/Throwaway.destructured.ir b/test_cases/throwaway/out_unoptimized/Throwaway.destructured.ir new file mode 100644 index 0000000000..b3ac096253 --- /dev/null +++ b/test_cases/throwaway/out_unoptimized/Throwaway.destructured.ir @@ -0,0 +1,28 @@ +contract test_cases.throwaway.contract.Throwaway: + program approval: + subroutine test_cases.throwaway.contract.Throwaway.approval_program() -> uint64: + block@0: // L5 + let (tup.0#0: uint64, tup.1#0: bytes, tup.2#0: bytes) = test_cases.throwaway.contract.get_tuple() + let args#0: uint64 = tup.0#0 + let sender#0: bytes = tup.1#0 + let approval#0: bytes = tup.2#0 + let tmp%0#0: bytes = (global CreatorAddress) + let tmp%1#0: uint64 = (== sender#0 tmp%0#0) + (assert tmp%1#0) + let tmp%2#0: uint64 = (== args#0 0u) + (assert tmp%2#0) + let tmp%3#0: uint64 = (len approval#0) + (assert tmp%3#0) + return 1u + + subroutine test_cases.throwaway.contract.get_tuple() -> : + block@0: // L18 + let tmp%0#0: uint64 = (txn NumAppArgs) + let tmp%1#0: bytes = (txn Sender) + let tmp%2#0: bytes = (txn ApprovalProgram) + return tmp%0#0 tmp%1#0 tmp%2#0 + + program clear-state: + subroutine test_cases.throwaway.contract.Throwaway.clear_state_program() -> uint64: + block@0: // L14 + return 1u \ No newline at end of file diff --git a/test_cases/throwaway/puya.log b/test_cases/throwaway/puya.log new file mode 100644 index 0000000000..cad1313a33 --- /dev/null +++ b/test_cases/throwaway/puya.log @@ -0,0 +1,405 @@ +debug: PuyaOptions(paths=['throwaway'], output_teal=True, output_arc32=True, output_awst=True, output_ssa_ir=True, output_optimization_ir=True, output_destructured_ir=True, output_memory_ir=True, debug_level=1, optimization_level=1, log_level=, target_avm_version=10, locals_coalescing_strategy=) +debug: Sealing block@0: // L11 +debug: Terminated block@0: // L11 +debug: Looking for 'required_budget_with_buffer' in an unsealed block creating an incomplete Phi: block@1: // while_top_L19 +debug: Created Phi assignment: let required_budget_with_buffer#1: uint64 = undefined while trying to resolve 'required_budget_with_buffer' in block@1: // while_top_L19 +debug: Terminated block@1: // while_top_L19 +debug: Sealing block@None: // while_body_L19 +debug: Looking for 'fee_source' in an unsealed block creating an incomplete Phi: block@1: // while_top_L19 +debug: Created Phi assignment: let fee_source#1: uint64 = undefined while trying to resolve 'fee_source' in block@1: // while_top_L19 +debug: Terminated block@2: // while_body_L19 +debug: Sealing block@None: // switch_case_default_L25 +debug: Sealing block@None: // switch_case_0_L27 +debug: Sealing block@None: // switch_case_1_L29 +debug: Terminated block@3: // switch_case_0_L27 +debug: Terminated block@4: // switch_case_1_L29 +debug: Terminated block@5: // switch_case_default_L25 +debug: Sealing block@6: // switch_case_next_L25 +debug: Terminated block@6: // switch_case_next_L25 +debug: Sealing block@1: // while_top_L19 +debug: Added required_budget_with_buffer#0 to Phi node: let required_budget_with_buffer#1: uint64 = φ(required_budget_with_buffer#0 <- block@0) in block@0: // L11 +debug: Created Phi assignment: let required_budget_with_buffer#2: uint64 = undefined while trying to resolve 'required_budget_with_buffer' in block@6: // switch_case_next_L25 +debug: Added required_budget_with_buffer#1 to Phi node: let required_budget_with_buffer#2: uint64 = φ(required_budget_with_buffer#1 <- block@3) in block@3: // switch_case_0_L27 +debug: Added required_budget_with_buffer#1 to Phi node: let required_budget_with_buffer#2: uint64 = φ(required_budget_with_buffer#1 <- block@3, required_budget_with_buffer#1 <- block@4) in block@4: // switch_case_1_L29 +debug: Added required_budget_with_buffer#1 to Phi node: let required_budget_with_buffer#2: uint64 = φ(required_budget_with_buffer#1 <- block@3, required_budget_with_buffer#1 <- block@4, required_budget_with_buffer#1 <- block@5) in block@5: // switch_case_default_L25 +debug: Replacing trivial Phi node: let required_budget_with_buffer#2: uint64 = φ(required_budget_with_buffer#1 <- block@3, required_budget_with_buffer#1 <- block@4, required_budget_with_buffer#1 <- block@5) (required_budget_with_buffer#2) with required_budget_with_buffer#1 +debug: Deleting Phi assignment: let required_budget_with_buffer#2: uint64 = φ(required_budget_with_buffer#1 <- block@3, required_budget_with_buffer#1 <- block@4, required_budget_with_buffer#1 <- block@5) +debug: Replaced trivial Phi node: let required_budget_with_buffer#2: uint64 = φ(required_budget_with_buffer#1 <- block@3, required_budget_with_buffer#1 <- block@4, required_budget_with_buffer#1 <- block@5) (required_budget_with_buffer#2) with required_budget_with_buffer#1 in current definition for 1 blocks +debug: Added required_budget_with_buffer#1 to Phi node: let required_budget_with_buffer#1: uint64 = φ(required_budget_with_buffer#0 <- block@0, required_budget_with_buffer#1 <- block@6) in block@6: // switch_case_next_L25 +debug: Replacing trivial Phi node: let required_budget_with_buffer#1: uint64 = φ(required_budget_with_buffer#0 <- block@0, required_budget_with_buffer#1 <- block@6) (required_budget_with_buffer#1) with required_budget_with_buffer#0 +debug: Deleting Phi assignment: let required_budget_with_buffer#1: uint64 = φ(required_budget_with_buffer#0 <- block@0, required_budget_with_buffer#1 <- block@6) +debug: Replaced trivial Phi node: let required_budget_with_buffer#1: uint64 = φ(required_budget_with_buffer#0 <- block@0, required_budget_with_buffer#1 <- block@6) (required_budget_with_buffer#1) with required_budget_with_buffer#0 in current definition for 6 blocks +debug: Added fee_source#0 to Phi node: let fee_source#1: uint64 = φ(fee_source#0 <- block@0) in block@0: // L11 +debug: Created Phi assignment: let fee_source#2: uint64 = undefined while trying to resolve 'fee_source' in block@6: // switch_case_next_L25 +debug: Added fee_source#1 to Phi node: let fee_source#2: uint64 = φ(fee_source#1 <- block@3) in block@3: // switch_case_0_L27 +debug: Added fee_source#1 to Phi node: let fee_source#2: uint64 = φ(fee_source#1 <- block@3, fee_source#1 <- block@4) in block@4: // switch_case_1_L29 +debug: Added fee_source#1 to Phi node: let fee_source#2: uint64 = φ(fee_source#1 <- block@3, fee_source#1 <- block@4, fee_source#1 <- block@5) in block@5: // switch_case_default_L25 +debug: Replacing trivial Phi node: let fee_source#2: uint64 = φ(fee_source#1 <- block@3, fee_source#1 <- block@4, fee_source#1 <- block@5) (fee_source#2) with fee_source#1 +debug: Deleting Phi assignment: let fee_source#2: uint64 = φ(fee_source#1 <- block@3, fee_source#1 <- block@4, fee_source#1 <- block@5) +debug: Replaced trivial Phi node: let fee_source#2: uint64 = φ(fee_source#1 <- block@3, fee_source#1 <- block@4, fee_source#1 <- block@5) (fee_source#2) with fee_source#1 in current definition for 1 blocks +debug: Added fee_source#1 to Phi node: let fee_source#1: uint64 = φ(fee_source#0 <- block@0, fee_source#1 <- block@6) in block@6: // switch_case_next_L25 +debug: Replacing trivial Phi node: let fee_source#1: uint64 = φ(fee_source#0 <- block@0, fee_source#1 <- block@6) (fee_source#1) with fee_source#0 +debug: Deleting Phi assignment: let fee_source#1: uint64 = φ(fee_source#0 <- block@0, fee_source#1 <- block@6) +debug: Replaced trivial Phi node: let fee_source#1: uint64 = φ(fee_source#0 <- block@0, fee_source#1 <- block@6) (fee_source#1) with fee_source#0 in current definition for 6 blocks +debug: Sealing block@None: // after_while_L19 +debug: Terminated block@7: // after_while_L19 +debug: Sealing block@0: // L20 +debug: Terminated block@0: // L20 +debug: Sealing block@0: // L39 +debug: Terminated block@0: // L39 +debug: Sealing block@0: // L57 +debug: Terminated block@0: // L57 +debug: Sealing block@0: // L84 +debug: Terminated block@0: // L84 +debug: Sealing block@None: // if_body_L106 +debug: Sealing block@None: // else_body_L106 +debug: Terminated block@1: // if_body_L106 +debug: Sealing block@2: // after_if_else_L106 +debug: Created Phi assignment: let array_length#1: uint64 = undefined while trying to resolve 'array_length' in block@2: // after_if_else_L106 +debug: Added array_length#0 to Phi node: let array_length#1: uint64 = φ(array_length#0 <- block@0) in block@0: // L84 +debug: Added array_length#0 to Phi node: let array_length#1: uint64 = φ(array_length#0 <- block@0, array_length#0 <- block@1) in block@1: // if_body_L106 +debug: Replacing trivial Phi node: let array_length#1: uint64 = φ(array_length#0 <- block@0, array_length#0 <- block@1) (array_length#1) with array_length#0 +debug: Deleting Phi assignment: let array_length#1: uint64 = φ(array_length#0 <- block@0, array_length#0 <- block@1) +debug: Replaced trivial Phi node: let array_length#1: uint64 = φ(array_length#0 <- block@0, array_length#0 <- block@1) (array_length#1) with array_length#0 in current definition for 1 blocks +debug: Created Phi assignment: let is_packed#1: uint64 = undefined while trying to resolve 'is_packed' in block@2: // after_if_else_L106 +debug: Added is_packed#0 to Phi node: let is_packed#1: uint64 = φ(is_packed#0 <- block@0) in block@0: // L84 +debug: Added is_packed#0 to Phi node: let is_packed#1: uint64 = φ(is_packed#0 <- block@0, is_packed#0 <- block@1) in block@1: // if_body_L106 +debug: Replacing trivial Phi node: let is_packed#1: uint64 = φ(is_packed#0 <- block@0, is_packed#0 <- block@1) (is_packed#1) with is_packed#0 +debug: Deleting Phi assignment: let is_packed#1: uint64 = φ(is_packed#0 <- block@0, is_packed#0 <- block@1) +debug: Replaced trivial Phi node: let is_packed#1: uint64 = φ(is_packed#0 <- block@0, is_packed#0 <- block@1) (is_packed#1) with is_packed#0 in current definition for 1 blocks +debug: Terminated block@2: // after_if_else_L106 +debug: Sealing block@None: // ternary_true_L110 +debug: Sealing block@None: // ternary_false_L110 +debug: Terminated block@3: // ternary_true_L110 +debug: Terminated block@4: // ternary_false_L110 +debug: Sealing block@5: // ternary_merge_L110 +debug: Created Phi assignment: let ternary_result%6#2: uint64 = undefined while trying to resolve 'ternary_result%6' in block@5: // ternary_merge_L110 +debug: Added ternary_result%6#0 to Phi node: let ternary_result%6#2: uint64 = φ(ternary_result%6#0 <- block@3) in block@3: // ternary_true_L110 +debug: Added ternary_result%6#1 to Phi node: let ternary_result%6#2: uint64 = φ(ternary_result%6#0 <- block@3, ternary_result%6#1 <- block@4) in block@4: // ternary_false_L110 +debug: Created Phi assignment: let new_items_count#1: uint64 = undefined while trying to resolve 'new_items_count' in block@5: // ternary_merge_L110 +debug: Created Phi assignment: let new_items_count#2: uint64 = undefined while trying to resolve 'new_items_count' in block@2: // after_if_else_L106 +debug: Added new_items_count#0 to Phi node: let new_items_count#2: uint64 = φ(new_items_count#0 <- block@0) in block@0: // L84 +debug: Added new_items_count#0 to Phi node: let new_items_count#2: uint64 = φ(new_items_count#0 <- block@0, new_items_count#0 <- block@1) in block@1: // if_body_L106 +debug: Replacing trivial Phi node: let new_items_count#2: uint64 = φ(new_items_count#0 <- block@0, new_items_count#0 <- block@1) (new_items_count#2) with new_items_count#0 +debug: Deleting Phi assignment: let new_items_count#2: uint64 = φ(new_items_count#0 <- block@0, new_items_count#0 <- block@1) +debug: Replaced trivial Phi node: let new_items_count#2: uint64 = φ(new_items_count#0 <- block@0, new_items_count#0 <- block@1) (new_items_count#2) with new_items_count#0 in current definition for 1 blocks +debug: Added new_items_count#0 to Phi node: let new_items_count#1: uint64 = φ(new_items_count#0 <- block@3) in block@3: // ternary_true_L110 +debug: Added new_items_count#0 to Phi node: let new_items_count#1: uint64 = φ(new_items_count#0 <- block@3, new_items_count#0 <- block@4) in block@4: // ternary_false_L110 +debug: Replacing trivial Phi node: let new_items_count#1: uint64 = φ(new_items_count#0 <- block@3, new_items_count#0 <- block@4) (new_items_count#1) with new_items_count#0 +debug: Deleting Phi assignment: let new_items_count#1: uint64 = φ(new_items_count#0 <- block@3, new_items_count#0 <- block@4) +debug: Replaced trivial Phi node: let new_items_count#1: uint64 = φ(new_items_count#0 <- block@3, new_items_count#0 <- block@4) (new_items_count#1) with new_items_count#0 in current definition for 1 blocks +debug: Terminated block@5: // ternary_merge_L110 +debug: Looking for 'range_item%7' in an unsealed block creating an incomplete Phi: block@6: // for_header_L110 +debug: Created Phi assignment: let range_item%7#1: uint64 = undefined while trying to resolve 'range_item%7' in block@6: // for_header_L110 +debug: Terminated block@6: // for_header_L110 +debug: Looking for 'range_item%7' in an unsealed block creating an incomplete Phi: block@7: // for_body_L110 +debug: Created Phi assignment: let range_item%7#2: uint64 = undefined while trying to resolve 'range_item%7' in block@7: // for_body_L110 +debug: Looking for 'result' in an unsealed block creating an incomplete Phi: block@7: // for_body_L110 +debug: Created Phi assignment: let result#2: bytes = undefined while trying to resolve 'result' in block@7: // for_body_L110 +debug: Looking for 'write_offset' in an unsealed block creating an incomplete Phi: block@7: // for_body_L110 +debug: Created Phi assignment: let write_offset#1: uint64 = undefined while trying to resolve 'write_offset' in block@7: // for_body_L110 +debug: Looking for 'new_items_bytes' in an unsealed block creating an incomplete Phi: block@7: // for_body_L110 +debug: Created Phi assignment: let new_items_bytes#1: bytes = undefined while trying to resolve 'new_items_bytes' in block@7: // for_body_L110 +debug: Terminated block@7: // for_body_L110 +debug: Sealing block@8: // for_footer_L110 +debug: Terminated block@8: // for_footer_L110 +debug: Sealing block@9: // for_increment_L110 +debug: Terminated block@9: // for_increment_L110 +debug: Sealing block@6: // for_header_L110 +debug: Added range_item%7#0 to Phi node: let range_item%7#1: uint64 = φ(range_item%7#0 <- block@5) in block@5: // ternary_merge_L110 +debug: Added range_item%7#3 to Phi node: let range_item%7#1: uint64 = φ(range_item%7#0 <- block@5, range_item%7#3 <- block@9) in block@9: // for_increment_L110 +debug: Sealing block@7: // for_body_L110 +debug: Added range_item%7#1 to Phi node: let range_item%7#2: uint64 = φ(range_item%7#1 <- block@6) in block@6: // for_header_L110 +debug: Replacing trivial Phi node: let range_item%7#2: uint64 = φ(range_item%7#1 <- block@6) (range_item%7#2) with range_item%7#1 +debug: Deleting Phi assignment: let range_item%7#2: uint64 = φ(range_item%7#1 <- block@6) +debug: Replaced trivial Phi node: let range_item%7#2: uint64 = φ(range_item%7#1 <- block@6) (range_item%7#2) with range_item%7#1 in current definition for 2 blocks +debug: Created Phi assignment: let result#4: bytes = undefined while trying to resolve 'result' in block@6: // for_header_L110 +debug: Created Phi assignment: let result#5: bytes = undefined while trying to resolve 'result' in block@5: // ternary_merge_L110 +debug: Created Phi assignment: let result#6: bytes = undefined while trying to resolve 'result' in block@2: // after_if_else_L106 +debug: Added result#0 to Phi node: let result#6: bytes = φ(result#0 <- block@0) in block@0: // L84 +debug: Added result#1 to Phi node: let result#6: bytes = φ(result#0 <- block@0, result#1 <- block@1) in block@1: // if_body_L106 +debug: Added result#6 to Phi node: let result#5: bytes = φ(result#6 <- block@3) in block@3: // ternary_true_L110 +debug: Added result#6 to Phi node: let result#5: bytes = φ(result#6 <- block@3, result#6 <- block@4) in block@4: // ternary_false_L110 +debug: Replacing trivial Phi node: let result#5: bytes = φ(result#6 <- block@3, result#6 <- block@4) (result#5) with result#6 +debug: Deleting Phi assignment: let result#5: bytes = φ(result#6 <- block@3, result#6 <- block@4) +debug: Replaced trivial Phi node: let result#5: bytes = φ(result#6 <- block@3, result#6 <- block@4) (result#5) with result#6 in current definition for 1 blocks +debug: Added result#6 to Phi node: let result#4: bytes = φ(result#6 <- block@5) in block@5: // ternary_merge_L110 +debug: Added result#3 to Phi node: let result#4: bytes = φ(result#6 <- block@5, result#3 <- block@9) in block@9: // for_increment_L110 +debug: Added result#4 to Phi node: let result#2: bytes = φ(result#4 <- block@6) in block@6: // for_header_L110 +debug: Replacing trivial Phi node: let result#2: bytes = φ(result#4 <- block@6) (result#2) with result#4 +debug: Deleting Phi assignment: let result#2: bytes = φ(result#4 <- block@6) +debug: Replaced trivial Phi node: let result#2: bytes = φ(result#4 <- block@6) (result#2) with result#4 in current definition for 0 blocks +debug: Created Phi assignment: let write_offset#3: uint64 = undefined while trying to resolve 'write_offset' in block@6: // for_header_L110 +debug: Created Phi assignment: let write_offset#4: uint64 = undefined while trying to resolve 'write_offset' in block@5: // ternary_merge_L110 +debug: Added write_offset#0 to Phi node: let write_offset#4: uint64 = φ(write_offset#0 <- block@3) in block@3: // ternary_true_L110 +debug: Added write_offset#0 to Phi node: let write_offset#4: uint64 = φ(write_offset#0 <- block@3, write_offset#0 <- block@4) in block@4: // ternary_false_L110 +debug: Replacing trivial Phi node: let write_offset#4: uint64 = φ(write_offset#0 <- block@3, write_offset#0 <- block@4) (write_offset#4) with write_offset#0 +debug: Deleting Phi assignment: let write_offset#4: uint64 = φ(write_offset#0 <- block@3, write_offset#0 <- block@4) +debug: Replaced trivial Phi node: let write_offset#4: uint64 = φ(write_offset#0 <- block@3, write_offset#0 <- block@4) (write_offset#4) with write_offset#0 in current definition for 1 blocks +debug: Added write_offset#0 to Phi node: let write_offset#3: uint64 = φ(write_offset#0 <- block@5) in block@5: // ternary_merge_L110 +debug: Added write_offset#2 to Phi node: let write_offset#3: uint64 = φ(write_offset#0 <- block@5, write_offset#2 <- block@9) in block@9: // for_increment_L110 +debug: Added write_offset#3 to Phi node: let write_offset#1: uint64 = φ(write_offset#3 <- block@6) in block@6: // for_header_L110 +debug: Replacing trivial Phi node: let write_offset#1: uint64 = φ(write_offset#3 <- block@6) (write_offset#1) with write_offset#3 +debug: Deleting Phi assignment: let write_offset#1: uint64 = φ(write_offset#3 <- block@6) +debug: Replaced trivial Phi node: let write_offset#1: uint64 = φ(write_offset#3 <- block@6) (write_offset#1) with write_offset#3 in current definition for 0 blocks +debug: Created Phi assignment: let new_items_bytes#2: bytes = undefined while trying to resolve 'new_items_bytes' in block@6: // for_header_L110 +debug: Created Phi assignment: let new_items_bytes#3: bytes = undefined while trying to resolve 'new_items_bytes' in block@5: // ternary_merge_L110 +debug: Created Phi assignment: let new_items_bytes#4: bytes = undefined while trying to resolve 'new_items_bytes' in block@2: // after_if_else_L106 +debug: Added new_items_bytes#0 to Phi node: let new_items_bytes#4: bytes = φ(new_items_bytes#0 <- block@0) in block@0: // L84 +debug: Added new_items_bytes#0 to Phi node: let new_items_bytes#4: bytes = φ(new_items_bytes#0 <- block@0, new_items_bytes#0 <- block@1) in block@1: // if_body_L106 +debug: Replacing trivial Phi node: let new_items_bytes#4: bytes = φ(new_items_bytes#0 <- block@0, new_items_bytes#0 <- block@1) (new_items_bytes#4) with new_items_bytes#0 +debug: Deleting Phi assignment: let new_items_bytes#4: bytes = φ(new_items_bytes#0 <- block@0, new_items_bytes#0 <- block@1) +debug: Replaced trivial Phi node: let new_items_bytes#4: bytes = φ(new_items_bytes#0 <- block@0, new_items_bytes#0 <- block@1) (new_items_bytes#4) with new_items_bytes#0 in current definition for 1 blocks +debug: Added new_items_bytes#0 to Phi node: let new_items_bytes#3: bytes = φ(new_items_bytes#0 <- block@3) in block@3: // ternary_true_L110 +debug: Added new_items_bytes#0 to Phi node: let new_items_bytes#3: bytes = φ(new_items_bytes#0 <- block@3, new_items_bytes#0 <- block@4) in block@4: // ternary_false_L110 +debug: Replacing trivial Phi node: let new_items_bytes#3: bytes = φ(new_items_bytes#0 <- block@3, new_items_bytes#0 <- block@4) (new_items_bytes#3) with new_items_bytes#0 +debug: Deleting Phi assignment: let new_items_bytes#3: bytes = φ(new_items_bytes#0 <- block@3, new_items_bytes#0 <- block@4) +debug: Replaced trivial Phi node: let new_items_bytes#3: bytes = φ(new_items_bytes#0 <- block@3, new_items_bytes#0 <- block@4) (new_items_bytes#3) with new_items_bytes#0 in current definition for 1 blocks +debug: Added new_items_bytes#0 to Phi node: let new_items_bytes#2: bytes = φ(new_items_bytes#0 <- block@5) in block@5: // ternary_merge_L110 +debug: Added new_items_bytes#1 to Phi node: let new_items_bytes#2: bytes = φ(new_items_bytes#0 <- block@5, new_items_bytes#1 <- block@9) in block@9: // for_increment_L110 +debug: Added new_items_bytes#2 to Phi node: let new_items_bytes#1: bytes = φ(new_items_bytes#2 <- block@6) in block@6: // for_header_L110 +debug: Replacing trivial Phi node: let new_items_bytes#1: bytes = φ(new_items_bytes#2 <- block@6) (new_items_bytes#1) with new_items_bytes#2 +debug: Deleting Phi assignment: let new_items_bytes#1: bytes = φ(new_items_bytes#2 <- block@6) +debug: Replacing trivial Phi node: let new_items_bytes#2: bytes = φ(new_items_bytes#0 <- block@5, new_items_bytes#2 <- block@9) (new_items_bytes#2) with new_items_bytes#0 +debug: Deleting Phi assignment: let new_items_bytes#2: bytes = φ(new_items_bytes#0 <- block@5, new_items_bytes#2 <- block@9) +debug: Replaced trivial Phi node: let new_items_bytes#1: bytes = φ(new_items_bytes#2 <- block@6) (new_items_bytes#1) with new_items_bytes#2 in current definition for 3 blocks +debug: Replaced trivial Phi node: let new_items_bytes#2: bytes = φ(new_items_bytes#0 <- block@5, new_items_bytes#2 <- block@9) (new_items_bytes#2) with new_items_bytes#0 in current definition for 4 blocks +debug: Sealing block@None: // after_for_L110 +debug: Terminated block@10: // after_for_L110 +debug: Sealing block@0: // L117 +debug: Terminated block@0: // L117 +debug: Sealing block@0: // L139 +debug: Terminated block@0: // L139 +debug: Sealing block@0: // L166 +debug: Terminated block@0: // L166 +debug: Sealing block@0: // L195 +debug: Terminated block@0: // L195 +debug: Sealing block@None: // if_body_L210 +debug: Sealing block@None: // else_body_L210 +debug: Terminated block@1: // if_body_L210 +debug: Terminated block@2: // else_body_L210 +debug: Sealing block@3: // after_if_else_L210 +debug: Created Phi assignment: let length#1: uint64 = undefined while trying to resolve 'length' in block@3: // after_if_else_L210 +debug: Added length#0 to Phi node: let length#1: uint64 = φ(length#0 <- block@1) in block@1: // if_body_L210 +debug: Added length#0 to Phi node: let length#1: uint64 = φ(length#0 <- block@1, length#0 <- block@2) in block@2: // else_body_L210 +debug: Replacing trivial Phi node: let length#1: uint64 = φ(length#0 <- block@1, length#0 <- block@2) (length#1) with length#0 +debug: Deleting Phi assignment: let length#1: uint64 = φ(length#0 <- block@1, length#0 <- block@2) +debug: Replaced trivial Phi node: let length#1: uint64 = φ(length#0 <- block@1, length#0 <- block@2) (length#1) with length#0 in current definition for 1 blocks +debug: Created Phi assignment: let start_at_index#1: uint64 = undefined while trying to resolve 'start_at_index' in block@3: // after_if_else_L210 +debug: Added start_at_index#0 to Phi node: let start_at_index#1: uint64 = φ(start_at_index#0 <- block@1) in block@1: // if_body_L210 +debug: Added start_at_index#0 to Phi node: let start_at_index#1: uint64 = φ(start_at_index#0 <- block@1, start_at_index#0 <- block@2) in block@2: // else_body_L210 +debug: Replacing trivial Phi node: let start_at_index#1: uint64 = φ(start_at_index#0 <- block@1, start_at_index#0 <- block@2) (start_at_index#1) with start_at_index#0 +debug: Deleting Phi assignment: let start_at_index#1: uint64 = φ(start_at_index#0 <- block@1, start_at_index#0 <- block@2) +debug: Replaced trivial Phi node: let start_at_index#1: uint64 = φ(start_at_index#0 <- block@1, start_at_index#0 <- block@2) (start_at_index#1) with start_at_index#0 in current definition for 1 blocks +debug: Terminated block@3: // after_if_else_L210 +debug: Looking for 'range_item%1' in an unsealed block creating an incomplete Phi: block@4: // for_header_L215 +debug: Created Phi assignment: let range_item%1#1: uint64 = undefined while trying to resolve 'range_item%1' in block@4: // for_header_L215 +debug: Terminated block@4: // for_header_L215 +debug: Looking for 'range_item%1' in an unsealed block creating an incomplete Phi: block@5: // for_body_L215 +debug: Created Phi assignment: let range_item%1#2: uint64 = undefined while trying to resolve 'range_item%1' in block@5: // for_body_L215 +debug: Looking for 'tail_cursor' in an unsealed block creating an incomplete Phi: block@5: // for_body_L215 +debug: Created Phi assignment: let tail_cursor#2: uint64 = undefined while trying to resolve 'tail_cursor' in block@5: // for_body_L215 +debug: Looking for 'array_data' in an unsealed block creating an incomplete Phi: block@5: // for_body_L215 +debug: Created Phi assignment: let array_data#1: bytes = undefined while trying to resolve 'array_data' in block@5: // for_body_L215 +debug: Looking for 'header_cursor' in an unsealed block creating an incomplete Phi: block@5: // for_body_L215 +debug: Created Phi assignment: let header_cursor#1: uint64 = undefined while trying to resolve 'header_cursor' in block@5: // for_body_L215 +debug: Terminated block@5: // for_body_L215 +debug: Sealing block@6: // for_footer_L215 +debug: Terminated block@6: // for_footer_L215 +debug: Sealing block@7: // for_increment_L215 +debug: Terminated block@7: // for_increment_L215 +debug: Sealing block@4: // for_header_L215 +debug: Added range_item%1#0 to Phi node: let range_item%1#1: uint64 = φ(range_item%1#0 <- block@3) in block@3: // after_if_else_L210 +debug: Added range_item%1#3 to Phi node: let range_item%1#1: uint64 = φ(range_item%1#0 <- block@3, range_item%1#3 <- block@7) in block@7: // for_increment_L215 +debug: Sealing block@5: // for_body_L215 +debug: Added range_item%1#1 to Phi node: let range_item%1#2: uint64 = φ(range_item%1#1 <- block@4) in block@4: // for_header_L215 +debug: Replacing trivial Phi node: let range_item%1#2: uint64 = φ(range_item%1#1 <- block@4) (range_item%1#2) with range_item%1#1 +debug: Deleting Phi assignment: let range_item%1#2: uint64 = φ(range_item%1#1 <- block@4) +debug: Replaced trivial Phi node: let range_item%1#2: uint64 = φ(range_item%1#1 <- block@4) (range_item%1#2) with range_item%1#1 in current definition for 2 blocks +debug: Created Phi assignment: let tail_cursor#4: uint64 = undefined while trying to resolve 'tail_cursor' in block@4: // for_header_L215 +debug: Created Phi assignment: let tail_cursor#5: uint64 = undefined while trying to resolve 'tail_cursor' in block@3: // after_if_else_L210 +debug: Added tail_cursor#0 to Phi node: let tail_cursor#5: uint64 = φ(tail_cursor#0 <- block@1) in block@1: // if_body_L210 +debug: Added tail_cursor#1 to Phi node: let tail_cursor#5: uint64 = φ(tail_cursor#0 <- block@1, tail_cursor#1 <- block@2) in block@2: // else_body_L210 +debug: Added tail_cursor#5 to Phi node: let tail_cursor#4: uint64 = φ(tail_cursor#5 <- block@3) in block@3: // after_if_else_L210 +debug: Added tail_cursor#3 to Phi node: let tail_cursor#4: uint64 = φ(tail_cursor#5 <- block@3, tail_cursor#3 <- block@7) in block@7: // for_increment_L215 +debug: Added tail_cursor#4 to Phi node: let tail_cursor#2: uint64 = φ(tail_cursor#4 <- block@4) in block@4: // for_header_L215 +debug: Replacing trivial Phi node: let tail_cursor#2: uint64 = φ(tail_cursor#4 <- block@4) (tail_cursor#2) with tail_cursor#4 +debug: Deleting Phi assignment: let tail_cursor#2: uint64 = φ(tail_cursor#4 <- block@4) +debug: Replaced trivial Phi node: let tail_cursor#2: uint64 = φ(tail_cursor#4 <- block@4) (tail_cursor#2) with tail_cursor#4 in current definition for 0 blocks +debug: Created Phi assignment: let array_data#3: bytes = undefined while trying to resolve 'array_data' in block@4: // for_header_L215 +debug: Created Phi assignment: let array_data#4: bytes = undefined while trying to resolve 'array_data' in block@3: // after_if_else_L210 +debug: Added array_data#0 to Phi node: let array_data#4: bytes = φ(array_data#0 <- block@1) in block@1: // if_body_L210 +debug: Added array_data#0 to Phi node: let array_data#4: bytes = φ(array_data#0 <- block@1, array_data#0 <- block@2) in block@2: // else_body_L210 +debug: Replacing trivial Phi node: let array_data#4: bytes = φ(array_data#0 <- block@1, array_data#0 <- block@2) (array_data#4) with array_data#0 +debug: Deleting Phi assignment: let array_data#4: bytes = φ(array_data#0 <- block@1, array_data#0 <- block@2) +debug: Replaced trivial Phi node: let array_data#4: bytes = φ(array_data#0 <- block@1, array_data#0 <- block@2) (array_data#4) with array_data#0 in current definition for 1 blocks +debug: Added array_data#0 to Phi node: let array_data#3: bytes = φ(array_data#0 <- block@3) in block@3: // after_if_else_L210 +debug: Added array_data#2 to Phi node: let array_data#3: bytes = φ(array_data#0 <- block@3, array_data#2 <- block@7) in block@7: // for_increment_L215 +debug: Added array_data#3 to Phi node: let array_data#1: bytes = φ(array_data#3 <- block@4) in block@4: // for_header_L215 +debug: Replacing trivial Phi node: let array_data#1: bytes = φ(array_data#3 <- block@4) (array_data#1) with array_data#3 +debug: Deleting Phi assignment: let array_data#1: bytes = φ(array_data#3 <- block@4) +debug: Replaced trivial Phi node: let array_data#1: bytes = φ(array_data#3 <- block@4) (array_data#1) with array_data#3 in current definition for 0 blocks +debug: Created Phi assignment: let header_cursor#3: uint64 = undefined while trying to resolve 'header_cursor' in block@4: // for_header_L215 +debug: Created Phi assignment: let header_cursor#4: uint64 = undefined while trying to resolve 'header_cursor' in block@3: // after_if_else_L210 +debug: Added header_cursor#0 to Phi node: let header_cursor#4: uint64 = φ(header_cursor#0 <- block@1) in block@1: // if_body_L210 +debug: Added header_cursor#0 to Phi node: let header_cursor#4: uint64 = φ(header_cursor#0 <- block@1, header_cursor#0 <- block@2) in block@2: // else_body_L210 +debug: Replacing trivial Phi node: let header_cursor#4: uint64 = φ(header_cursor#0 <- block@1, header_cursor#0 <- block@2) (header_cursor#4) with header_cursor#0 +debug: Deleting Phi assignment: let header_cursor#4: uint64 = φ(header_cursor#0 <- block@1, header_cursor#0 <- block@2) +debug: Replaced trivial Phi node: let header_cursor#4: uint64 = φ(header_cursor#0 <- block@1, header_cursor#0 <- block@2) (header_cursor#4) with header_cursor#0 in current definition for 1 blocks +debug: Added header_cursor#0 to Phi node: let header_cursor#3: uint64 = φ(header_cursor#0 <- block@3) in block@3: // after_if_else_L210 +debug: Added header_cursor#2 to Phi node: let header_cursor#3: uint64 = φ(header_cursor#0 <- block@3, header_cursor#2 <- block@7) in block@7: // for_increment_L215 +debug: Added header_cursor#3 to Phi node: let header_cursor#1: uint64 = φ(header_cursor#3 <- block@4) in block@4: // for_header_L215 +debug: Replacing trivial Phi node: let header_cursor#1: uint64 = φ(header_cursor#3 <- block@4) (header_cursor#1) with header_cursor#3 +debug: Deleting Phi assignment: let header_cursor#1: uint64 = φ(header_cursor#3 <- block@4) +debug: Replaced trivial Phi node: let header_cursor#1: uint64 = φ(header_cursor#3 <- block@4) (header_cursor#1) with header_cursor#3 in current definition for 0 blocks +debug: Sealing block@None: // after_for_L215 +debug: Terminated block@8: // after_for_L215 +debug: Sealing block@0: // L4 +debug: Terminated block@0: // L4 +debug: Looking for 'start' in an unsealed block creating an incomplete Phi: block@1: // while_top_L11 +debug: Created Phi assignment: let start#1: uint64 = undefined while trying to resolve 'start' in block@1: // while_top_L11 +debug: Looking for 'item' in an unsealed block creating an incomplete Phi: block@1: // while_top_L11 +debug: Created Phi assignment: let item#1: bytes = undefined while trying to resolve 'item' in block@1: // while_top_L11 +debug: Looking for 'sequence' in an unsealed block creating an incomplete Phi: block@1: // while_top_L11 +debug: Created Phi assignment: let sequence#1: bytes = undefined while trying to resolve 'sequence' in block@1: // while_top_L11 +debug: Terminated block@1: // while_top_L11 +debug: Sealing block@None: // while_body_L11 +debug: Terminated block@2: // while_body_L11 +debug: Sealing block@None: // if_body_L12 +debug: Sealing block@None: // else_body_L12 +debug: Terminated block@3: // if_body_L12 +debug: Sealing block@4: // after_if_else_L12 +debug: Terminated block@4: // after_if_else_L12 +debug: Sealing block@1: // while_top_L11 +debug: Added start#0 to Phi node: let start#1: uint64 = φ(start#0 <- block@0) in block@0: // L4 +debug: Added start#2 to Phi node: let start#1: uint64 = φ(start#0 <- block@0, start#2 <- block@4) in block@4: // after_if_else_L12 +debug: Added item#0 to Phi node: let item#1: bytes = φ(item#0 <- block@0) in block@0: // L4 +debug: Added item#1 to Phi node: let item#1: bytes = φ(item#0 <- block@0, item#1 <- block@4) in block@4: // after_if_else_L12 +debug: Replacing trivial Phi node: let item#1: bytes = φ(item#0 <- block@0, item#1 <- block@4) (item#1) with item#0 +debug: Deleting Phi assignment: let item#1: bytes = φ(item#0 <- block@0, item#1 <- block@4) +debug: Replaced trivial Phi node: let item#1: bytes = φ(item#0 <- block@0, item#1 <- block@4) (item#1) with item#0 in current definition for 3 blocks +debug: Added sequence#0 to Phi node: let sequence#1: bytes = φ(sequence#0 <- block@0) in block@0: // L4 +debug: Added sequence#1 to Phi node: let sequence#1: bytes = φ(sequence#0 <- block@0, sequence#1 <- block@4) in block@4: // after_if_else_L12 +debug: Replacing trivial Phi node: let sequence#1: bytes = φ(sequence#0 <- block@0, sequence#1 <- block@4) (sequence#1) with sequence#0 +debug: Deleting Phi assignment: let sequence#1: bytes = φ(sequence#0 <- block@0, sequence#1 <- block@4) +debug: Replaced trivial Phi node: let sequence#1: bytes = φ(sequence#0 <- block@0, sequence#1 <- block@4) (sequence#1) with sequence#0 in current definition for 3 blocks +debug: Sealing block@None: // after_while_L11 +debug: Terminated block@5: // after_while_L11 +debug: Sealing block@0: // L18 +debug: Terminated block@0: // L18 +debug: Sealing block@0: // L5 +debug: Terminated block@0: // L5 +debug: Sealing block@0: // L14 +debug: Terminated block@0: // L14 +debug: Output IR to throwaway/out/Throwaway.ssa.ir +info: Optimizing test_cases.throwaway.contract.Throwaway at level 1 +debug: Begin optimization pass 1/100 +debug: Optimizing subroutine test_cases.throwaway.contract.Throwaway.approval_program +debug: Splitting parallel copies prior to optimization +debug: Optimizer: Constant Replacer +debug: Optimizer: Copy Propagation +debug: Found equivalence set: tup.0#0, args#0, _#1 +debug: Replacing {args#0, _#1} with tup.0#0 made 1 modifications +debug: Found equivalence set: tup.1#0, sender#0, _#2 +debug: Replacing {sender#0, _#2} with tup.1#0 made 1 modifications +debug: Found equivalence set: tup.2#0, _#0, approval#0 +debug: Replacing {_#0, approval#0} with tup.2#0 made 1 modifications +debug: Optimizer: Intrinsic Simplifier +debug: Simplified (== tup.0#0 0u) to (! tup.0#0) +debug: Optimizer: Remove Unused Variables +debug: Optimizer: Simplify Control Ops +debug: Optimizer: Remove Linear Jump +debug: Optimizer: Remove Empty Blocks +debug: Optimizer: Remove Unreachable Blocks +debug: Optimizer: Repeated Expression Elimination +debug: Optimizing subroutine test_cases.throwaway.contract.get_tuple +debug: Splitting parallel copies prior to optimization +debug: Optimizer: Constant Replacer +debug: Optimizer: Copy Propagation +debug: Optimizer: Intrinsic Simplifier +debug: Optimizer: Remove Unused Variables +debug: Optimizer: Simplify Control Ops +debug: Optimizer: Remove Linear Jump +debug: Optimizer: Remove Empty Blocks +debug: Optimizer: Remove Unreachable Blocks +debug: Optimizer: Repeated Expression Elimination +debug: Optimizing subroutine test_cases.throwaway.contract.Throwaway.clear_state_program +debug: Splitting parallel copies prior to optimization +debug: Optimizer: Constant Replacer +debug: Optimizer: Copy Propagation +debug: Optimizer: Intrinsic Simplifier +debug: Optimizer: Remove Unused Variables +debug: Optimizer: Simplify Control Ops +debug: Optimizer: Remove Linear Jump +debug: Optimizer: Remove Empty Blocks +debug: Optimizer: Remove Unreachable Blocks +debug: Optimizer: Repeated Expression Elimination +debug: Output IR to throwaway/out/Throwaway.ssa.opt_pass_1.ir +debug: Begin optimization pass 2/100 +debug: Optimizing subroutine test_cases.throwaway.contract.Throwaway.approval_program +debug: Optimizer: Constant Replacer +debug: Optimizer: Copy Propagation +debug: Optimizer: Intrinsic Simplifier +debug: Optimizer: Remove Unused Variables +debug: Optimizer: Simplify Control Ops +debug: Optimizer: Remove Linear Jump +debug: Optimizer: Remove Empty Blocks +debug: Optimizer: Remove Unreachable Blocks +debug: Optimizer: Repeated Expression Elimination +debug: Optimizing subroutine test_cases.throwaway.contract.get_tuple +debug: Optimizer: Constant Replacer +debug: Optimizer: Copy Propagation +debug: Optimizer: Intrinsic Simplifier +debug: Optimizer: Remove Unused Variables +debug: Optimizer: Simplify Control Ops +debug: Optimizer: Remove Linear Jump +debug: Optimizer: Remove Empty Blocks +debug: Optimizer: Remove Unreachable Blocks +debug: Optimizer: Repeated Expression Elimination +debug: Optimizing subroutine test_cases.throwaway.contract.Throwaway.clear_state_program +debug: Optimizer: Constant Replacer +debug: Optimizer: Copy Propagation +debug: Optimizer: Intrinsic Simplifier +debug: Optimizer: Remove Unused Variables +debug: Optimizer: Simplify Control Ops +debug: Optimizer: Remove Linear Jump +debug: Optimizer: Remove Empty Blocks +debug: Optimizer: Remove Unreachable Blocks +debug: Optimizer: Repeated Expression Elimination +debug: No optimizations performed in pass 2, ending loop +debug: Removing Phis from test_cases.throwaway.contract.Throwaway.approval_program +debug: Removing Phis from test_cases.throwaway.contract.get_tuple +debug: Removing Phis from test_cases.throwaway.contract.Throwaway.clear_state_program +debug: Coalescing local variables in test_cases.throwaway.contract.Throwaway.approval_program using strategy RootOperandGrouping +debug: Coalescing resulted in 0 replacement/s +debug: Coalescing local variables in test_cases.throwaway.contract.get_tuple using strategy RootOperandGrouping +debug: Coalescing resulted in 0 replacement/s +debug: Coalescing local variables in test_cases.throwaway.contract.Throwaway.clear_state_program using strategy RootOperandGrouping +debug: Coalescing resulted in 0 replacement/s +debug: Sequentializing parallel copies in test_cases.throwaway.contract.Throwaway.approval_program +debug: Sequentializing parallel copies in test_cases.throwaway.contract.get_tuple +debug: Sequentializing parallel copies in test_cases.throwaway.contract.Throwaway.clear_state_program +debug: Performing post-SSA optimizations +debug: Output IR to throwaway/out/Throwaway.destructured.ir +debug: Inserted main_block@0.ops[9]: 'store tmp%1#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[11]: 'load tmp%1#0' with 'load tmp%1#0 from l-stack (no copy)' +debug: Inserted main_block@0.ops[15]: 'store tmp%2#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[17]: 'load tmp%2#0' with 'load tmp%2#0 from l-stack (no copy)' +debug: Inserted main_block@0.ops[21]: 'store tmp%3#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[23]: 'load tmp%3#0' with 'load tmp%3#0 from l-stack (no copy)' +debug: Inserted main_block@0.ops[5]: 'store tmp%0#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[8]: 'load tmp%0#0' with 'load tmp%0#0 from l-stack (no copy)' +debug: Inserted main_block@0.ops[2]: 'store tup.1#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[8]: 'load tup.1#0' with 'load tup.1#0 from l-stack (no copy)' +debug: Inserted main_block@0.ops[4]: 'store tup.0#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[16]: 'load tup.0#0' with 'load tup.0#0 from l-stack (no copy)' +debug: Inserted main_block@0.ops[1]: 'store tup.2#0 to l-stack (copy)' +debug: Replaced main_block@0.ops[23]: 'load tup.2#0' with 'load tup.2#0 from l-stack (no copy)' +debug: Inserted get_tuple_block@0.ops[5]: 'store tmp%2#0 to l-stack (copy)' +debug: Replaced get_tuple_block@0.ops[9]: 'load tmp%2#0' with 'load tmp%2#0 from l-stack (no copy)' +debug: Inserted get_tuple_block@0.ops[3]: 'store tmp%1#0 to l-stack (copy)' +debug: Replaced get_tuple_block@0.ops[9]: 'load tmp%1#0' with 'load tmp%1#0 from l-stack (no copy)' +debug: Inserted get_tuple_block@0.ops[1]: 'store tmp%0#0 to l-stack (copy)' +debug: Replaced get_tuple_block@0.ops[9]: 'load tmp%0#0' with 'load tmp%0#0 from l-stack (no copy)' +info: Writing throwaway/out/Throwaway.approval.teal +info: Writing throwaway/out/Throwaway.clear.teal \ No newline at end of file diff --git a/tests/test_expected_output/expected_errors.test b/tests/test_expected_output/expected_errors.test index 802507f54a..cf760a778d 100644 --- a/tests/test_expected_output/expected_errors.test +++ b/tests/test_expected_output/expected_errors.test @@ -61,4 +61,22 @@ class ContractWithUndefinedVariable(Contract): return undefined def clear_state_program(self) -> bool: - return True \ No newline at end of file + return True + +## case: test_throwaway_var + +from puyapy import Account, Txn, UInt64, subroutine + +@subroutine +def get_tuple() -> tuple[Account, UInt64]: + return Txn.sender, Txn.num_app_args + +@subroutine +def read_() -> None: + sender, _ = get_tuple() + assert _ ## E: '_' variables can only be assigned to + +@subroutine +def assign_() -> None: + sender, _ = get_tuple() + bad_assign = _ ## E: '_' variables can only be assigned to