Repository: vyperlang/vyper

Issue:
Cannot use empty for an array inside an event log
### What's your issue about?

The following code:

```python
event Foo:
    a: bytes32[2]

@external
def test():
    log Foo(empty(bytes32[2]))
```

Produces the following error:

```python
File "/home/computer/code/python/vyper/vyper/parser/events.py", line 208, in pack_args_by_32
    for arg2 in arg.elements:
AttributeError: 'LLLnode' object has no attribute 'elements'
```

The issue happens with any array type.

### How can it be fixed?
I think that this code should compile, so we need to look at what's happening in parser that prevents it.



Discussion:

Resolving patch:
diff --git a/vyper/parser/events.py b/vyper/parser/events.py
--- a/vyper/parser/events.py
+++ b/vyper/parser/events.py
@@ -1,5 +1,5 @@
 from vyper import ast as vy_ast
-from vyper.exceptions import TypeMismatch
+from vyper.exceptions import StructureException, TypeMismatch
 from vyper.parser.expr import Expr
 from vyper.parser.lll_node import LLLnode
 from vyper.parser.parser_utils import (
@@ -7,6 +7,7 @@
     getpos,
     make_byte_array_copier,
     make_setter,
+    mzero,
     unwrap_location,
     zero_pad,
 )
@@ -157,6 +158,13 @@ def pack_args_by_32(
         )
         holder.append(increment_counter)
     elif isinstance(typ, ListType):
+
+        if isinstance(arg, vy_ast.Call) and arg.func.get("id") == "empty":
+            # special case for `empty()` with a static-sized array
+            holder.append(mzero(placeholder, get_size_of_type(typ) * 32))
+            maxlen += (get_size_of_type(typ) - 1) * 32
+            return holder, maxlen
+
         maxlen += (typ.count - 1) * 32
         typ = typ.subtype
 
@@ -225,7 +233,7 @@ def pack_logging_data(expected_data, args, context, pos):
     prealloacted = {}
     for idx, (arg, _expected_arg) in enumerate(zip(args, expected_data)):
 
-        if isinstance(arg, (vy_ast.Str, vy_ast.Call)):
+        if isinstance(arg, (vy_ast.Str, vy_ast.Call)) and arg.get("func.id") != "empty":
             expr = Expr(arg, context)
             source_lll = expr.lll_node
             typ = source_lll.typ
@@ -293,6 +301,11 @@ def pack_logging_data(expected_data, args, context, pos):
     for i, (arg, data) in enumerate(zip(args, expected_data)):
         typ = data.typ
         if isinstance(typ, ByteArrayLike):
+            if isinstance(arg, vy_ast.Call) and arg.func.get("id") == "empty":
+                # TODO add support for this
+                raise StructureException(
+                    "Cannot use `empty` on Bytes or String types within an event log", arg
+                )
             pack_args_by_32(
                 holder=holder,
                 maxlen=maxlen,


Test patch:
diff --git a/tests/parser/exceptions/test_structure_exception.py b/tests/parser/exceptions/test_structure_exception.py
--- a/tests/parser/exceptions/test_structure_exception.py
+++ b/tests/parser/exceptions/test_structure_exception.py
@@ -67,6 +67,14 @@ def double_nonreentrant():
     """
 x: String <= 33
     """,
+    """
+event Foo:
+    a: Bytes[4]
+
+@external
+def foo():
+    log Foo(empty(Bytes[4]))
+    """,
 ]
 
 
diff --git a/tests/parser/functions/test_empty.py b/tests/parser/functions/test_empty.py
--- a/tests/parser/functions/test_empty.py
+++ b/tests/parser/functions/test_empty.py
@@ -586,3 +586,33 @@ def bar(a: address) -> (uint256, Bytes[33], Bytes[65], uint256):
     c2 = get_contract(code_b)
 
     assert c2.bar(c1.address) == [12] + expected + [42]
+
+
+def test_empty_array_in_event_logging(get_contract, get_logs):
+    code = """
+event MyLog:
+    arg1: Bytes[64]
+    arg2: int128[2][3]
+    arg3: int128
+    arg4: Bytes[64]
+    arg5: uint256[3]
+
+@external
+def foo():
+    log MyLog(
+        b'hellohellohellohellohellohellohellohellohello',
+        empty(int128[2][3]),
+        314159,
+        b'helphelphelphelphelphelphelphelphelphelphelp',
+        empty(uint256[3])
+    )
+    """
+
+    c = get_contract(code)
+    log = get_logs(c.foo(transact={}), c, "MyLog")[0]
+
+    assert log.args.arg1 == b"hello" * 9
+    assert log.args.arg2 == [[0, 0], [0, 0], [0, 0]]
+    assert log.args.arg3 == 314159
+    assert log.args.arg4 == b"help" * 11
+    assert log.args.arg5 == [0, 0, 0]