Skip to content

Commit

Permalink
support empty tuples
Browse files Browse the repository at this point in the history
  • Loading branch information
achidlow committed Nov 7, 2024
1 parent ec424ef commit 67457e9
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 14 deletions.
4 changes: 2 additions & 2 deletions examples/sizes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
transaction/Transaction 1072 897 - | 654 512 -
tuple_support/NestedTuples 1259 900 897 | 784 510 509
tuple_support/TupleComparisons 124 67 - | 81 35 -
tuple_support/TupleSupport 667 405 - | 375 175 -
tuple_support/TupleSupport 674 405 - | 378 175 -
typed_abi_call/Greeter 5040 3977 3968 | 2727 1832 1829
typed_abi_call/Logger 1405 1144 1141 | 822 603 602
typed_abi_call_txn/Caller 580 514 - | 306 263 -
Expand All @@ -130,4 +130,4 @@
unssa/UnSSA 432 368 - | 241 204 -
voting/VotingRoundApp 1593 1483 - | 734 649 -
with_reentrancy/WithReentrancy 255 242 - | 132 122 -
Total 69200 53576 53517 | 32843 21764 21720
Total 69207 53576 53517 | 32846 21764 21720
2 changes: 0 additions & 2 deletions src/puya/awst/wtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,6 @@ def __hash__(self) -> int:

@types.validator
def _types_validator(self, _attribute: object, types: tuple[WType, ...]) -> None:
if not types:
raise CodeError("empty tuples are not supported", self.source_location)
if void_wtype in types:
raise CodeError("tuple should not contain void types", self.source_location)

Expand Down
2 changes: 2 additions & 0 deletions src/puya/ir/builder/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def assign_targets(
targets: list[Register],
assignment_location: SourceLocation | None,
) -> None:
if not (source.types or targets):
return
for target in targets:
context.ssa.write_variable(target.name, context.block_builder.active_block, target)
context.block_builder.add(
Expand Down
5 changes: 5 additions & 0 deletions test_cases/tuple_support/out/TupleSupport.ssa.ir
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ contract test_cases.tuple_support.tuple_support.TupleSupport:
(assert tmp%29#0)
let tmp%30#0: bool = (== x.1#0 0x)
(assert tmp%30#0)
test_cases.tuple_support.tuple_support.test_empty()
let tmp%31#0: uint64 = (+ a#0 b#0)
return tmp%31#0

Expand Down Expand Up @@ -286,6 +287,10 @@ contract test_cases.tuple_support.tuple_support.TupleSupport:
let tmp%0#0: bool = (== tup.0#0 1u)
(assert tmp%0#0)
return

subroutine test_cases.tuple_support.tuple_support.test_empty() -> void:
block@0: // L178
return

program clear-state:
subroutine test_cases.tuple_support.tuple_support.TupleSupport.clear_state_program() -> uint64:
Expand Down
12 changes: 12 additions & 0 deletions test_cases/tuple_support/out/module.awst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ contract TupleSupport
x: tuple<uint64,bytes> = (0u, hex<"">)
assert(x[0] == 0u)
assert(x[1] == hex<"">)
test_cases.tuple_support.tuple_support.test_empty()
return a + b
}

Expand Down Expand Up @@ -112,6 +113,7 @@ contract TupleSupport
x: tuple<uint64,bytes> = (0u, hex<"">)
assert(x[0] == 0u)
assert(x[1] == hex<"">)
test_cases.tuple_support.tuple_support.test_empty()
return a + b
}
}
Expand Down Expand Up @@ -219,6 +221,16 @@ subroutine slicing(values: tuple<uint64,uint64,uint64,uint64,uint64,uint64,uint6
assert(&&(&&(one_to_three[0] == SINGLE_EVAL(id=7, source=one_to_three[:])[0], one_to_three[1] == SINGLE_EVAL(id=7)[1]), one_to_three[2] == SINGLE_EVAL(id=7)[2]))
}

subroutine test_empty(): void
{
empty: tuple<> = ()
empty2: tuple<> = empty
(): tuple<> = empty
(): tuple<> = ()
assert(true)
assert(true)
}

contract TupleComparisons
{
method_resolution_order: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ main_after_if_else@12:
dup
==
assert
// tuple_support/tuple_support.py:64
// test_empty()
callsub test_empty
// tuple_support/tuple_support.py:11
// (a, b) = (UInt64(1), UInt64(2))
intc_0 // 1
Expand Down Expand Up @@ -774,3 +777,12 @@ single_tuple:
==
assert
retsub


// test_cases.tuple_support.tuple_support.test_empty() -> void:
test_empty:
// tuple_support/tuple_support.py:178-179
// @subroutine
// def test_empty() -> None:
proto 0 0
retsub
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ contract test_cases.tuple_support.tuple_support.TupleSupport:
(assert tmp%29#0)
let tmp%30#0: bool = (== 0x 0x)
(assert tmp%30#0)
test_cases.tuple_support.tuple_support.test_empty()
let tmp%31#0: uint64 = (+ 1u 2u)
return tmp%31#0

Expand Down Expand Up @@ -265,6 +266,10 @@ contract test_cases.tuple_support.tuple_support.TupleSupport:
let tmp%0#0: bool = (== 1u 1u)
(assert tmp%0#0)
return

subroutine test_cases.tuple_support.tuple_support.test_empty() -> void:
block@0: // L178
return

program clear-state:
subroutine test_cases.tuple_support.tuple_support.TupleSupport.clear_state_program() -> uint64:
Expand Down
18 changes: 18 additions & 0 deletions test_cases/tuple_support/puya.log
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,10 @@ debug: Sealing block@0: // L82
debug: Terminated block@0: // L82
debug: Sealing block@0: // L76
debug: Terminated block@0: // L76
debug: Sealing block@0: // L178
tuple_support/tuple_support.py:184:5 warning: assertion is always true, ignoring
tuple_support/tuple_support.py:185:5 warning: assertion is always true, ignoring
debug: Terminated block@0: // L178
debug: Sealing block@0: // L1
debug: Terminated block@0: // L1
debug: Sealing block@1: // call __init___L1
Expand Down Expand Up @@ -1108,6 +1112,20 @@ debug: Optimizer: Remove Empty Blocks
debug: Optimizer: Remove Unreachable Blocks
debug: Optimizer: Repeated Expression Elimination
debug: Optimizer: Remove Calls To No Op Subroutines
debug: Optimizing subroutine test_cases.tuple_support.tuple_support.test_empty
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: Inner Txn Field Replacer
debug: Optimizer: Replace Compiled References
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: Optimizer: Remove Calls To No Op Subroutines
debug: Optimizing subroutine test_cases.tuple_support.tuple_support.TupleSupport.clear_state_program
debug: Splitting parallel copies prior to optimization
debug: Optimizer: Constant Replacer
Expand Down
12 changes: 11 additions & 1 deletion test_cases/tuple_support/tuple_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def approval_program(self) -> UInt64:
x = tuple[UInt64, Bytes]((UInt64(), Bytes()))
assert x[0] == 0
assert x[1] == b""

test_empty()
return a + b

def clear_state_program(self) -> UInt64:
Expand Down Expand Up @@ -173,3 +173,13 @@ def slicing(values: tuple[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64
assert one_to_three[-2:-1][0] == one_to_three[1]

assert one_to_three == one_to_three[:]


@subroutine
def test_empty() -> None:
empty = ()
empty2 = empty
() = empty
() = ()
assert not empty
assert not empty2
13 changes: 9 additions & 4 deletions tests/test_expected_output/literals.test
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,20 @@ def too_long() -> arc4.DynamicBytes:
return arc4.DynamicBytes(b"a" * 4096) ## E: encoded bytes exceed max length


## case: test_tuple
## case: test_tuple1
import typing
from algopy import *

@subroutine
def wrong_type() -> None:
a = tuple(arc4.DynamicBytes(b"as")) ## E: unhandled argument type

## case: test_tuple2
import typing
from algopy import *

@subroutine
def empty_tuple() -> None:
a: tuple[typing.Never, ...] = tuple() ## E: empty tuples are not supported

class MyContract(ARC4Contract):
@arc4.abimethod
def empty_tuple(self) -> None:
a: tuple[typing.Never, ...] = tuple()
2 changes: 1 addition & 1 deletion tests/test_expected_output/module.test
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ NotAllowed = collections.namedtuple("NotAllowed", ["x", "y"]) ## E: Unsupported
import typing
from algopy import *

class MyTuple(typing.NamedTuple): ## E: empty tuples are not supported
class MyTuple(typing.NamedTuple):
pass

@subroutine
Expand Down
4 changes: 0 additions & 4 deletions tests/test_expected_output/tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,6 @@ def test_tuple6() -> None:
UInt64(2),
) # type:ignore[assignment]

@subroutine
def test_tuple7() -> None:
tup = () ## E: empty tuples are not supported

@subroutine
def test_tuple8() -> None:
a, b = UInt64(), Bytes()
Expand Down

0 comments on commit 67457e9

Please sign in to comment.