We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Compiler error when function returns tuple containing structs
In Ethereum Python Community Discord group, fubuloubu asked me to create this issue.

### Version Information

* vyper: 0.2.12
* OS: OSX
* Python: 3.9.6

### What's your issue about?

When a function in a contract is returning a tuple of two structs, It will not compile. Here is the error: 

`AttributeError: 'StructType' object has no attribute 'typ'`

Here is sample code if you want to replicate the error:
```
struct Animal:
    location: address
    fur: uint256

struct Human:
    location: address
    height: uint256

@external
def thisDoesNotWork(_animal: Animal, _human: Human) -> (Animal, Human):
    animal: Animal = _animal
    human: Human = _human

    # do stuff, edit the structs
    animal.fur += 1
    human.height += 1

    return animal, human
```

### How can it be fixed?

fubuloubu says it's a compiler error. I don't know exactly how to fix it.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `vyper/old_codegen/return_.py`
Content:
```
1 from vyper import ast as vy_ast
2 from vyper.old_codegen.lll_node import LLLnode
3 from vyper.old_codegen.parser_utils import getpos, make_setter
4 from vyper.old_codegen.types import (
5     BaseType,
6     ListType,
7     TupleType,
8     get_size_of_type,
9 )
10 from vyper.old_codegen.types.check import check_assign
11 from vyper.utils import MemoryPositions
12 
13 from .abi import abi_encode, abi_type_of, ensure_tuple
14 
15 
16 # Generate return code for stmt
17 def make_return_stmt(stmt, context, begin_pos, _size, loop_memory_position=None):
18     # TODO check this out
19     from vyper.old_codegen.function_definitions.utils import (
20         get_nonreentrant_lock,
21     )
22 
23     func_type = stmt.get_ancestor(vy_ast.FunctionDef)._metadata["type"]
24     _, nonreentrant_post = get_nonreentrant_lock(func_type, context.global_ctx)
25 
26     if context.is_internal:
27         if loop_memory_position is None:
28             loop_memory_position = context.new_internal_variable(BaseType("uint256"))
29 
30         # Make label for stack push loop.
31         label_id = "_".join([str(x) for x in (context.method_id, stmt.lineno, stmt.col_offset)])
32         exit_label = f"make_return_loop_exit_{label_id}"
33         start_label = f"make_return_loop_start_{label_id}"
34 
35         # Push prepared data onto the stack,
36         # in reverse order so it can be popped of in order.
37         if isinstance(begin_pos, int) and isinstance(_size, int):
38             # static values, unroll the mloads instead.
39             mloads = [["mload", pos] for pos in range(begin_pos, _size, 32)]
40         else:
41             mloads = [
42                 "seq_unchecked",
43                 ["mstore", loop_memory_position, _size],
44                 ["label", start_label],
45                 [  # maybe exit loop / break.
46                     "if",
47                     ["le", ["mload", loop_memory_position], 0],
48                     ["goto", exit_label],
49                 ],
50                 [  # push onto stack
51                     "mload",
52                     ["add", begin_pos, ["sub", ["mload", loop_memory_position], 32]],
53                 ],
54                 [  # decrement i by 32.
55                     "mstore",
56                     loop_memory_position,
57                     ["sub", ["mload", loop_memory_position], 32],
58                 ],
59                 ["goto", start_label],
60                 ["label", exit_label],
61             ]
62 
63         # if we are in a for loop, we have to exit prior to returning
64         exit_repeater = ["exit_repeater"] if context.forvars else []
65 
66         return (
67             ["seq_unchecked"]
68             + exit_repeater
69             + mloads
70             + nonreentrant_post
71             + [["jump", ["mload", context.callback_ptr]]]
72         )
73     else:
74         return ["seq_unchecked"] + nonreentrant_post + [["return", begin_pos, _size]]
75 
76 
77 # Generate code for returning a tuple or struct.
78 def gen_tuple_return(stmt, context, sub):
79     abi_typ = abi_type_of(context.return_type)
80     # according to the ABI, return types are ALWAYS tuples even if
81     # only one element is being returned.
82     # https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector-and-argument-encoding
83     # "and the return values v_1, ..., v_k of f are encoded as
84     #
85     #    enc((v_1, ..., v_k))
86     #    i.e. the values are combined into a tuple and encoded.
87     # "
88     # therefore, wrap it in a tuple if it's not already a tuple.
89     # (big difference between returning `(bytes,)` and `bytes`.
90     abi_typ = ensure_tuple(abi_typ)
91     abi_bytes_needed = abi_typ.static_size() + abi_typ.dynamic_size_bound()
92     dst = context.memory_allocator.expand_memory(abi_bytes_needed)
93     return_buffer = LLLnode(
94         dst, location="memory", annotation="return_buffer", typ=context.return_type
95     )
96 
97     check_assign(return_buffer, sub, pos=getpos(stmt))
98 
99     if sub.value == "multi":
100 
101         if isinstance(context.return_type, TupleType) and not abi_typ.dynamic_size_bound():
102             # for tuples where every value is of the same type and a fixed length,
103             # we can simplify the encoding by treating it as though it were an array
104             base_types = set()
105             for typ in context.return_type.members:
106                 while isinstance(typ, ListType):
107                     typ = typ.subtype
108                 base_types.add(typ.typ)
109 
110             if len(base_types) == 1:
111                 new_sub = LLLnode.from_list(
112                     context.new_internal_variable(context.return_type),
113                     typ=context.return_type,
114                     location="memory",
115                 )
116                 setter = make_setter(new_sub, sub, "memory", pos=getpos(stmt))
117                 return LLLnode.from_list(
118                     [
119                         "seq",
120                         setter,
121                         make_return_stmt(
122                             stmt, context, new_sub, get_size_of_type(context.return_type) * 32,
123                         ),
124                     ],
125                     typ=None,
126                     pos=getpos(stmt),
127                 )
128 
129         # in case of multi we can't create a variable to store location of the return expression
130         # as multi can have data from multiple location like store, calldata etc
131         encode_out = abi_encode(return_buffer, sub, pos=getpos(stmt), returns=True)
132         load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
133         os = [
134             "seq",
135             ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
136             make_return_stmt(stmt, context, return_buffer, load_return_len),
137         ]
138         return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)
139 
140     # for tuple return types where a function is called inside the tuple, we
141     # process the calls prior to encoding the return data
142     if sub.value == "seq_unchecked" and sub.args[-1].value == "multi":
143         encode_out = abi_encode(return_buffer, sub.args[-1], pos=getpos(stmt), returns=True)
144         load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
145         os = (
146             ["seq"]
147             + sub.args[:-1]
148             + [
149                 ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
150                 make_return_stmt(stmt, context, return_buffer, load_return_len),
151             ]
152         )
153         return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)
154 
155     # for all othe cases we are creating a stack variable named sub_loc to store the  location
156     # of the return expression. This is done so that the return expression does not get evaluated
157     # abi-encode uses a function named o_list which evaluate the expression multiple times
158     sub_loc = LLLnode("sub_loc", typ=sub.typ, location=sub.location)
159     encode_out = abi_encode(return_buffer, sub_loc, pos=getpos(stmt), returns=True)
160     load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE]
161     os = [
162         "with",
163         "sub_loc",
164         sub,
165         [
166             "seq",
167             ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out],
168             make_return_stmt(stmt, context, return_buffer, load_return_len),
169         ],
170     ]
171     return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)
172
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/vyper/old_codegen/return_.py b/vyper/old_codegen/return_.py
--- a/vyper/old_codegen/return_.py
+++ b/vyper/old_codegen/return_.py
@@ -1,12 +1,7 @@
 from vyper import ast as vy_ast
 from vyper.old_codegen.lll_node import LLLnode
 from vyper.old_codegen.parser_utils import getpos, make_setter
-from vyper.old_codegen.types import (
-    BaseType,
-    ListType,
-    TupleType,
-    get_size_of_type,
-)
+from vyper.old_codegen.types import BaseType, TupleType, get_size_of_type
 from vyper.old_codegen.types.check import check_assign
 from vyper.utils import MemoryPositions
 
@@ -100,31 +95,26 @@
 
         if isinstance(context.return_type, TupleType) and not abi_typ.dynamic_size_bound():
             # for tuples where every value is of the same type and a fixed length,
-            # we can simplify the encoding by treating it as though it were an array
-            base_types = set()
-            for typ in context.return_type.members:
-                while isinstance(typ, ListType):
-                    typ = typ.subtype
-                base_types.add(typ.typ)
-
-            if len(base_types) == 1:
-                new_sub = LLLnode.from_list(
-                    context.new_internal_variable(context.return_type),
-                    typ=context.return_type,
-                    location="memory",
-                )
-                setter = make_setter(new_sub, sub, "memory", pos=getpos(stmt))
-                return LLLnode.from_list(
-                    [
-                        "seq",
-                        setter,
-                        make_return_stmt(
-                            stmt, context, new_sub, get_size_of_type(context.return_type) * 32,
-                        ),
-                    ],
-                    typ=None,
-                    pos=getpos(stmt),
-                )
+            # we can simplify the encoding by using make_setter, since
+            # our memory encoding happens to be identical to the ABI
+            # encoding.
+            new_sub = LLLnode.from_list(
+                context.new_internal_variable(context.return_type),
+                typ=context.return_type,
+                location="memory",
+            )
+            setter = make_setter(new_sub, sub, "memory", pos=getpos(stmt))
+            return LLLnode.from_list(
+                [
+                    "seq",
+                    setter,
+                    make_return_stmt(
+                        stmt, context, new_sub, get_size_of_type(context.return_type) * 32,
+                    ),
+                ],
+                typ=None,
+                pos=getpos(stmt),
+            )
 
         # in case of multi we can't create a variable to store location of the return expression
         # as multi can have data from multiple location like store, calldata etc

{"golden_diff": "diff --git a/vyper/old_codegen/return_.py b/vyper/old_codegen/return_.py\n--- a/vyper/old_codegen/return_.py\n+++ b/vyper/old_codegen/return_.py\n@@ -1,12 +1,7 @@\n from vyper import ast as vy_ast\n from vyper.old_codegen.lll_node import LLLnode\n from vyper.old_codegen.parser_utils import getpos, make_setter\n-from vyper.old_codegen.types import (\n-    BaseType,\n-    ListType,\n-    TupleType,\n-    get_size_of_type,\n-)\n+from vyper.old_codegen.types import BaseType, TupleType, get_size_of_type\n from vyper.old_codegen.types.check import check_assign\n from vyper.utils import MemoryPositions\n \n@@ -100,31 +95,26 @@\n \n         if isinstance(context.return_type, TupleType) and not abi_typ.dynamic_size_bound():\n             # for tuples where every value is of the same type and a fixed length,\n-            # we can simplify the encoding by treating it as though it were an array\n-            base_types = set()\n-            for typ in context.return_type.members:\n-                while isinstance(typ, ListType):\n-                    typ = typ.subtype\n-                base_types.add(typ.typ)\n-\n-            if len(base_types) == 1:\n-                new_sub = LLLnode.from_list(\n-                    context.new_internal_variable(context.return_type),\n-                    typ=context.return_type,\n-                    location=\"memory\",\n-                )\n-                setter = make_setter(new_sub, sub, \"memory\", pos=getpos(stmt))\n-                return LLLnode.from_list(\n-                    [\n-                        \"seq\",\n-                        setter,\n-                        make_return_stmt(\n-                            stmt, context, new_sub, get_size_of_type(context.return_type) * 32,\n-                        ),\n-                    ],\n-                    typ=None,\n-                    pos=getpos(stmt),\n-                )\n+            # we can simplify the encoding by using make_setter, since\n+            # our memory encoding happens to be identical to the ABI\n+            # encoding.\n+            new_sub = LLLnode.from_list(\n+                context.new_internal_variable(context.return_type),\n+                typ=context.return_type,\n+                location=\"memory\",\n+            )\n+            setter = make_setter(new_sub, sub, \"memory\", pos=getpos(stmt))\n+            return LLLnode.from_list(\n+                [\n+                    \"seq\",\n+                    setter,\n+                    make_return_stmt(\n+                        stmt, context, new_sub, get_size_of_type(context.return_type) * 32,\n+                    ),\n+                ],\n+                typ=None,\n+                pos=getpos(stmt),\n+            )\n \n         # in case of multi we can't create a variable to store location of the return expression\n         # as multi can have data from multiple location like store, calldata etc\n", "issue": "Compiler error when function returns tuple containing structs\nIn Ethereum Python Community Discord group, fubuloubu asked me to create this issue.\r\n\r\n### Version Information\r\n\r\n* vyper: 0.2.12\r\n* OS: OSX\r\n* Python: 3.9.6\r\n\r\n### What's your issue about?\r\n\r\nWhen a function in a contract is returning a tuple of two structs, It will not compile. Here is the error: \r\n\r\n`AttributeError: 'StructType' object has no attribute 'typ'`\r\n\r\nHere is sample code if you want to replicate the error:\r\n```\r\nstruct Animal:\r\n    location: address\r\n    fur: uint256\r\n\r\nstruct Human:\r\n    location: address\r\n    height: uint256\r\n\r\n@external\r\ndef thisDoesNotWork(_animal: Animal, _human: Human) -> (Animal, Human):\r\n    animal: Animal = _animal\r\n    human: Human = _human\r\n\r\n    # do stuff, edit the structs\r\n    animal.fur += 1\r\n    human.height += 1\r\n\r\n    return animal, human\r\n```\r\n\r\n### How can it be fixed?\r\n\r\nfubuloubu says it's a compiler error. I don't know exactly how to fix it.\r\n\n", "before_files": [{"content": "from vyper import ast as vy_ast\nfrom vyper.old_codegen.lll_node import LLLnode\nfrom vyper.old_codegen.parser_utils import getpos, make_setter\nfrom vyper.old_codegen.types import (\n    BaseType,\n    ListType,\n    TupleType,\n    get_size_of_type,\n)\nfrom vyper.old_codegen.types.check import check_assign\nfrom vyper.utils import MemoryPositions\n\nfrom .abi import abi_encode, abi_type_of, ensure_tuple\n\n\n# Generate return code for stmt\ndef make_return_stmt(stmt, context, begin_pos, _size, loop_memory_position=None):\n    # TODO check this out\n    from vyper.old_codegen.function_definitions.utils import (\n        get_nonreentrant_lock,\n    )\n\n    func_type = stmt.get_ancestor(vy_ast.FunctionDef)._metadata[\"type\"]\n    _, nonreentrant_post = get_nonreentrant_lock(func_type, context.global_ctx)\n\n    if context.is_internal:\n        if loop_memory_position is None:\n            loop_memory_position = context.new_internal_variable(BaseType(\"uint256\"))\n\n        # Make label for stack push loop.\n        label_id = \"_\".join([str(x) for x in (context.method_id, stmt.lineno, stmt.col_offset)])\n        exit_label = f\"make_return_loop_exit_{label_id}\"\n        start_label = f\"make_return_loop_start_{label_id}\"\n\n        # Push prepared data onto the stack,\n        # in reverse order so it can be popped of in order.\n        if isinstance(begin_pos, int) and isinstance(_size, int):\n            # static values, unroll the mloads instead.\n            mloads = [[\"mload\", pos] for pos in range(begin_pos, _size, 32)]\n        else:\n            mloads = [\n                \"seq_unchecked\",\n                [\"mstore\", loop_memory_position, _size],\n                [\"label\", start_label],\n                [  # maybe exit loop / break.\n                    \"if\",\n                    [\"le\", [\"mload\", loop_memory_position], 0],\n                    [\"goto\", exit_label],\n                ],\n                [  # push onto stack\n                    \"mload\",\n                    [\"add\", begin_pos, [\"sub\", [\"mload\", loop_memory_position], 32]],\n                ],\n                [  # decrement i by 32.\n                    \"mstore\",\n                    loop_memory_position,\n                    [\"sub\", [\"mload\", loop_memory_position], 32],\n                ],\n                [\"goto\", start_label],\n                [\"label\", exit_label],\n            ]\n\n        # if we are in a for loop, we have to exit prior to returning\n        exit_repeater = [\"exit_repeater\"] if context.forvars else []\n\n        return (\n            [\"seq_unchecked\"]\n            + exit_repeater\n            + mloads\n            + nonreentrant_post\n            + [[\"jump\", [\"mload\", context.callback_ptr]]]\n        )\n    else:\n        return [\"seq_unchecked\"] + nonreentrant_post + [[\"return\", begin_pos, _size]]\n\n\n# Generate code for returning a tuple or struct.\ndef gen_tuple_return(stmt, context, sub):\n    abi_typ = abi_type_of(context.return_type)\n    # according to the ABI, return types are ALWAYS tuples even if\n    # only one element is being returned.\n    # https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector-and-argument-encoding\n    # \"and the return values v_1, ..., v_k of f are encoded as\n    #\n    #    enc((v_1, ..., v_k))\n    #    i.e. the values are combined into a tuple and encoded.\n    # \"\n    # therefore, wrap it in a tuple if it's not already a tuple.\n    # (big difference between returning `(bytes,)` and `bytes`.\n    abi_typ = ensure_tuple(abi_typ)\n    abi_bytes_needed = abi_typ.static_size() + abi_typ.dynamic_size_bound()\n    dst = context.memory_allocator.expand_memory(abi_bytes_needed)\n    return_buffer = LLLnode(\n        dst, location=\"memory\", annotation=\"return_buffer\", typ=context.return_type\n    )\n\n    check_assign(return_buffer, sub, pos=getpos(stmt))\n\n    if sub.value == \"multi\":\n\n        if isinstance(context.return_type, TupleType) and not abi_typ.dynamic_size_bound():\n            # for tuples where every value is of the same type and a fixed length,\n            # we can simplify the encoding by treating it as though it were an array\n            base_types = set()\n            for typ in context.return_type.members:\n                while isinstance(typ, ListType):\n                    typ = typ.subtype\n                base_types.add(typ.typ)\n\n            if len(base_types) == 1:\n                new_sub = LLLnode.from_list(\n                    context.new_internal_variable(context.return_type),\n                    typ=context.return_type,\n                    location=\"memory\",\n                )\n                setter = make_setter(new_sub, sub, \"memory\", pos=getpos(stmt))\n                return LLLnode.from_list(\n                    [\n                        \"seq\",\n                        setter,\n                        make_return_stmt(\n                            stmt, context, new_sub, get_size_of_type(context.return_type) * 32,\n                        ),\n                    ],\n                    typ=None,\n                    pos=getpos(stmt),\n                )\n\n        # in case of multi we can't create a variable to store location of the return expression\n        # as multi can have data from multiple location like store, calldata etc\n        encode_out = abi_encode(return_buffer, sub, pos=getpos(stmt), returns=True)\n        load_return_len = [\"mload\", MemoryPositions.FREE_VAR_SPACE]\n        os = [\n            \"seq\",\n            [\"mstore\", MemoryPositions.FREE_VAR_SPACE, encode_out],\n            make_return_stmt(stmt, context, return_buffer, load_return_len),\n        ]\n        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)\n\n    # for tuple return types where a function is called inside the tuple, we\n    # process the calls prior to encoding the return data\n    if sub.value == \"seq_unchecked\" and sub.args[-1].value == \"multi\":\n        encode_out = abi_encode(return_buffer, sub.args[-1], pos=getpos(stmt), returns=True)\n        load_return_len = [\"mload\", MemoryPositions.FREE_VAR_SPACE]\n        os = (\n            [\"seq\"]\n            + sub.args[:-1]\n            + [\n                [\"mstore\", MemoryPositions.FREE_VAR_SPACE, encode_out],\n                make_return_stmt(stmt, context, return_buffer, load_return_len),\n            ]\n        )\n        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)\n\n    # for all othe cases we are creating a stack variable named sub_loc to store the  location\n    # of the return expression. This is done so that the return expression does not get evaluated\n    # abi-encode uses a function named o_list which evaluate the expression multiple times\n    sub_loc = LLLnode(\"sub_loc\", typ=sub.typ, location=sub.location)\n    encode_out = abi_encode(return_buffer, sub_loc, pos=getpos(stmt), returns=True)\n    load_return_len = [\"mload\", MemoryPositions.FREE_VAR_SPACE]\n    os = [\n        \"with\",\n        \"sub_loc\",\n        sub,\n        [\n            \"seq\",\n            [\"mstore\", MemoryPositions.FREE_VAR_SPACE, encode_out],\n            make_return_stmt(stmt, context, return_buffer, load_return_len),\n        ],\n    ]\n    return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)\n", "path": "vyper/old_codegen/return_.py"}], "after_files": [{"content": "from vyper import ast as vy_ast\nfrom vyper.old_codegen.lll_node import LLLnode\nfrom vyper.old_codegen.parser_utils import getpos, make_setter\nfrom vyper.old_codegen.types import BaseType, TupleType, get_size_of_type\nfrom vyper.old_codegen.types.check import check_assign\nfrom vyper.utils import MemoryPositions\n\nfrom .abi import abi_encode, abi_type_of, ensure_tuple\n\n\n# Generate return code for stmt\ndef make_return_stmt(stmt, context, begin_pos, _size, loop_memory_position=None):\n    # TODO check this out\n    from vyper.old_codegen.function_definitions.utils import (\n        get_nonreentrant_lock,\n    )\n\n    func_type = stmt.get_ancestor(vy_ast.FunctionDef)._metadata[\"type\"]\n    _, nonreentrant_post = get_nonreentrant_lock(func_type, context.global_ctx)\n\n    if context.is_internal:\n        if loop_memory_position is None:\n            loop_memory_position = context.new_internal_variable(BaseType(\"uint256\"))\n\n        # Make label for stack push loop.\n        label_id = \"_\".join([str(x) for x in (context.method_id, stmt.lineno, stmt.col_offset)])\n        exit_label = f\"make_return_loop_exit_{label_id}\"\n        start_label = f\"make_return_loop_start_{label_id}\"\n\n        # Push prepared data onto the stack,\n        # in reverse order so it can be popped of in order.\n        if isinstance(begin_pos, int) and isinstance(_size, int):\n            # static values, unroll the mloads instead.\n            mloads = [[\"mload\", pos] for pos in range(begin_pos, _size, 32)]\n        else:\n            mloads = [\n                \"seq_unchecked\",\n                [\"mstore\", loop_memory_position, _size],\n                [\"label\", start_label],\n                [  # maybe exit loop / break.\n                    \"if\",\n                    [\"le\", [\"mload\", loop_memory_position], 0],\n                    [\"goto\", exit_label],\n                ],\n                [  # push onto stack\n                    \"mload\",\n                    [\"add\", begin_pos, [\"sub\", [\"mload\", loop_memory_position], 32]],\n                ],\n                [  # decrement i by 32.\n                    \"mstore\",\n                    loop_memory_position,\n                    [\"sub\", [\"mload\", loop_memory_position], 32],\n                ],\n                [\"goto\", start_label],\n                [\"label\", exit_label],\n            ]\n\n        # if we are in a for loop, we have to exit prior to returning\n        exit_repeater = [\"exit_repeater\"] if context.forvars else []\n\n        return (\n            [\"seq_unchecked\"]\n            + exit_repeater\n            + mloads\n            + nonreentrant_post\n            + [[\"jump\", [\"mload\", context.callback_ptr]]]\n        )\n    else:\n        return [\"seq_unchecked\"] + nonreentrant_post + [[\"return\", begin_pos, _size]]\n\n\n# Generate code for returning a tuple or struct.\ndef gen_tuple_return(stmt, context, sub):\n    abi_typ = abi_type_of(context.return_type)\n    # according to the ABI, return types are ALWAYS tuples even if\n    # only one element is being returned.\n    # https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector-and-argument-encoding\n    # \"and the return values v_1, ..., v_k of f are encoded as\n    #\n    #    enc((v_1, ..., v_k))\n    #    i.e. the values are combined into a tuple and encoded.\n    # \"\n    # therefore, wrap it in a tuple if it's not already a tuple.\n    # (big difference between returning `(bytes,)` and `bytes`.\n    abi_typ = ensure_tuple(abi_typ)\n    abi_bytes_needed = abi_typ.static_size() + abi_typ.dynamic_size_bound()\n    dst = context.memory_allocator.expand_memory(abi_bytes_needed)\n    return_buffer = LLLnode(\n        dst, location=\"memory\", annotation=\"return_buffer\", typ=context.return_type\n    )\n\n    check_assign(return_buffer, sub, pos=getpos(stmt))\n\n    if sub.value == \"multi\":\n\n        if isinstance(context.return_type, TupleType) and not abi_typ.dynamic_size_bound():\n            # for tuples where every value is of the same type and a fixed length,\n            # we can simplify the encoding by using make_setter, since\n            # our memory encoding happens to be identical to the ABI\n            # encoding.\n            new_sub = LLLnode.from_list(\n                context.new_internal_variable(context.return_type),\n                typ=context.return_type,\n                location=\"memory\",\n            )\n            setter = make_setter(new_sub, sub, \"memory\", pos=getpos(stmt))\n            return LLLnode.from_list(\n                [\n                    \"seq\",\n                    setter,\n                    make_return_stmt(\n                        stmt, context, new_sub, get_size_of_type(context.return_type) * 32,\n                    ),\n                ],\n                typ=None,\n                pos=getpos(stmt),\n            )\n\n        # in case of multi we can't create a variable to store location of the return expression\n        # as multi can have data from multiple location like store, calldata etc\n        encode_out = abi_encode(return_buffer, sub, pos=getpos(stmt), returns=True)\n        load_return_len = [\"mload\", MemoryPositions.FREE_VAR_SPACE]\n        os = [\n            \"seq\",\n            [\"mstore\", MemoryPositions.FREE_VAR_SPACE, encode_out],\n            make_return_stmt(stmt, context, return_buffer, load_return_len),\n        ]\n        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)\n\n    # for tuple return types where a function is called inside the tuple, we\n    # process the calls prior to encoding the return data\n    if sub.value == \"seq_unchecked\" and sub.args[-1].value == \"multi\":\n        encode_out = abi_encode(return_buffer, sub.args[-1], pos=getpos(stmt), returns=True)\n        load_return_len = [\"mload\", MemoryPositions.FREE_VAR_SPACE]\n        os = (\n            [\"seq\"]\n            + sub.args[:-1]\n            + [\n                [\"mstore\", MemoryPositions.FREE_VAR_SPACE, encode_out],\n                make_return_stmt(stmt, context, return_buffer, load_return_len),\n            ]\n        )\n        return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)\n\n    # for all othe cases we are creating a stack variable named sub_loc to store the  location\n    # of the return expression. This is done so that the return expression does not get evaluated\n    # abi-encode uses a function named o_list which evaluate the expression multiple times\n    sub_loc = LLLnode(\"sub_loc\", typ=sub.typ, location=sub.location)\n    encode_out = abi_encode(return_buffer, sub_loc, pos=getpos(stmt), returns=True)\n    load_return_len = [\"mload\", MemoryPositions.FREE_VAR_SPACE]\n    os = [\n        \"with\",\n        \"sub_loc\",\n        sub,\n        [\n            \"seq\",\n            [\"mstore\", MemoryPositions.FREE_VAR_SPACE, encode_out],\n            make_return_stmt(stmt, context, return_buffer, load_return_len),\n        ],\n    ]\n    return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)\n", "path": "vyper/old_codegen/return_.py"}]}