From 7c953f600113abcb9a31df68165b61a2c897f591 Mon Sep 17 00:00:00 2001 From: Michael Diamant Date: Tue, 8 Mar 2022 20:07:44 -0500 Subject: [PATCH] Update user guide docs to reflect addition of DynamicScratchVar (#226) --- docs/control_structures.rst | 26 ++++++++++++++-- docs/scratch.rst | 30 ++++++++++++++++--- tests/pass_by_ref_test.py | 8 ++++- ..._snippet_dynamic_scratch_var_expected.teal | 17 +++++++++++ tests/user_guide_test.py | 27 +++++++++++++++++ 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 tests/teal/user_guide_snippet_dynamic_scratch_var_expected.teal create mode 100644 tests/user_guide_test.py diff --git a/docs/control_structures.rst b/docs/control_structures.rst index 46cfb26d6..f36eaed36 100644 --- a/docs/control_structures.rst +++ b/docs/control_structures.rst @@ -296,9 +296,11 @@ Subroutines .. note:: Subroutines are only available in TEAL version 4 or higher. -A subroutine is section of code that can be called multiple times from within a program. Subroutines -are PyTeal's equivalent to functions. Subroutines can accept any number of arguments, and these -arguments must be PyTeal expressions. Additionally, a subroutine may return a single value, or no value. +A subroutine is section of code that can be called multiple times from within a program. Subroutines are PyTeal's equivalent to functions. Subroutine constraints include: + +* Subroutines accept any number of arguments. +* Subroutine argument types can be any `Expr` (PyTeal expression) or strictly `ScratchVar` (no subclasses allowed). PyTeal applies pass-by-value semantics to `Expr` and pass-by-reference to `ScratchVar`. +* Subroutines return a single value, or no value. Creating Subroutines -------------------- @@ -317,6 +319,24 @@ For example, def isEven(i): return i % Int(2) == Int(0) +PyTeal applies these parameter type annotation constraints when compiling subroutine definitions: + +* :any:`ScratchVar` parameters *require* a type annotation. +* :any:`Expr` parameters do *not* require a type annotation. PyTeal implicitly declares unannotated parameter types as :any:`Expr`. + +Here's an example illustrating `ScratchVar` parameter declaration with parameter type annotations: + +.. code-block:: python + + @Subroutine(TealType.none) + def swap(x: ScratchVar, y: ScratchVar): + z = ScratchVar(TealType.anytype) + return Seq( + z.store(x.load()), + x.store(y.load()), + y.store(z.load()), + ) + Calling Subroutines ------------------- diff --git a/docs/scratch.rst b/docs/scratch.rst index ddb123141..d08a332ea 100644 --- a/docs/scratch.rst +++ b/docs/scratch.rst @@ -5,15 +5,15 @@ Scratch Space `Scratch space `_ is a temporary place to store values for later use in your program. It is temporary because any -changes to scratch space do not persist beyond the current tranasaction. Scratch space can be used +changes to scratch space do not persist beyond the current transaction. Scratch space can be used in both Application and Signature mode. Scratch space consists of 256 scratch slots, each capable of storing one integer or byte slice. When using the :any:`ScratchVar` class to work with scratch space, a slot is automatically assigned to each variable. -Writing and Reading -~~~~~~~~~~~~~~~~~~~~~~ +ScratchVar: Writing and Reading to/from Scratch Space +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To write to scratch space, first create a :any:`ScratchVar` object and pass in the :any:`TealType` of the values that you will store there. It is possible to create a :any:`ScratchVar` that can store @@ -22,7 +22,7 @@ that no type checking takes places in this situation. It is also possible to man slot ID the compiler should assign the scratch slot to in the TEAL code. If no slot ID is specified, the compiler will assign it to any available slot. -To write or read values, use the corresponding :any:`ScratchVar.store` or :any:`ScratchVar.load` methods. +To write or read values, use the corresponding :any:`ScratchVar.store` or :any:`ScratchVar.load` methods. :any:`ScratchVar.store` *must* be invoked before invoking :any:`ScratchVar.load`. For example: @@ -34,3 +34,25 @@ For example: Assert(myvar.load() == Int(5)) ]) anotherVar = ScratchVar(TealType.bytes, 4) # assign this scratch slot to slot #4 + +DynamicScratchVar: Referencing a ScratchVar +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:any:`DynamicScratchVar` functions as a pointer to a :any:`ScratchVar` instance. + +Reference a :any:`ScratchVar` instance by invoking :any:`DynamicScratchVar.set_index`. :any:`DynamicScratchVar.set_index` *must* be invoked before using :any:`DynamicScratchVar.load` and :any:`DynamicScratchVar.store`. + +Here's an example to motivate usage. The example shows how a :any:`DynamicScratchVar` updates the *value* of a referenced :any:`ScratchVar` from 7 to 10. + +.. code-block:: python + + s = ScratchVar(TealType.uint64) + d = DynamicScratchVar(TealType.uint64) + + return Seq( + d.set_index(s), + s.store(Int(7)), + d.store(d.load() + Int(3)), + Assert(s.load() == Int(10)), + Int(1), + ) diff --git a/tests/pass_by_ref_test.py b/tests/pass_by_ref_test.py index fb9a9a079..acde89b26 100644 --- a/tests/pass_by_ref_test.py +++ b/tests/pass_by_ref_test.py @@ -193,7 +193,13 @@ def test_old(): if not OLD_CODE_ONLY: - NEW_CASES = (sub_logcat_dynamic, swapper, wilt_the_stilt, fac_by_ref, sub_mixed) + NEW_CASES = ( + sub_logcat_dynamic, + swapper, + wilt_the_stilt, + fac_by_ref, + sub_mixed, + ) def test_swapper(): compile_and_save(swapper) diff --git a/tests/teal/user_guide_snippet_dynamic_scratch_var_expected.teal b/tests/teal/user_guide_snippet_dynamic_scratch_var_expected.teal new file mode 100644 index 000000000..fe74bc0a3 --- /dev/null +++ b/tests/teal/user_guide_snippet_dynamic_scratch_var_expected.teal @@ -0,0 +1,17 @@ +#pragma version 6 +int 0 +store 1 +int 7 +store 0 +load 1 +load 1 +loads +int 3 ++ +stores +load 0 +int 10 +== +assert +int 1 +return \ No newline at end of file diff --git a/tests/user_guide_test.py b/tests/user_guide_test.py new file mode 100644 index 000000000..74c4238b7 --- /dev/null +++ b/tests/user_guide_test.py @@ -0,0 +1,27 @@ +import pytest + +from pyteal import * + +from .compile_asserts import assert_new_v_old, compile_and_save + + +def user_guide_snippet_dynamic_scratch_var() -> Expr: + """ + The user guide docs use the test to illustrate `DynamicScratchVar` usage. If the test breaks, then the user guide docs must be updated along with the test. + """ + + s = ScratchVar(TealType.uint64) + d = DynamicScratchVar(TealType.uint64) + + return Seq( + d.set_index(s), + s.store(Int(7)), + d.store(d.load() + Int(3)), + Assert(s.load() == Int(10)), + Int(1), + ) + + +@pytest.mark.parametrize("snippet", [user_guide_snippet_dynamic_scratch_var]) +def test_user_guide_snippets(snippet): + assert_new_v_old(snippet)