Repository: vyperlang/vyper

Issue:
empty(Bytes[1024]) causes vyper.exceptions.CompilerPanic: Unsupported location: None
### Version Information

* vyper Version (output of `vyper --version`): 0.2.3+commit.006968f
* OS: Osx

I am trying to mimic solidity's `new bytes(0)`

and looks like `empty(Bytes[1024])` causes this exception:

vyper.exceptions.CompilerPanic: Unsupported location: None Please create an issue.vyper.exceptions.CompilerPanic: Unsupported location: None Please create an issue.

using `b''` works great, I wonder, is there a significant gas diff between using `b''` and `empty(Bytes[1024])`?


Discussion:
I tried this and it seemed to work just fine, can you give the full source code of a Minimal Working Example (MWE) that is giving you problems?

Example:
```python
@external 
def foo(): 
    b: Bytes[1024] = empty(Bytes[1024]) 
```

Also, yes `b''` and `empty(Bytes[1024])` should work interchangeably.
your code works fine, I see the problem, in my code, I passed `empty(Bytes[1024])` as a function argument

```py
from vyper.interfaces import ERC20

interface UniswapV2Pair:
    def swap(amount_0_out: uint256, amount_1_out: uint256, to: address, data: Bytes[1024]): nonpayable

@external
def foo():
    UniswapV2Pair(ZERO_ADDRESS).swap(1, 1, ZERO_ADDRESS, empty(Bytes[1024]))
```
Yeah, in that situation you would use `b""`. If that works for you, that's great!

They should be interchangeable even then though, in fact `empty(Bytes[1024])` is somewhat more efficient as it leverages empty calldata instead of creating new memory. I was talking with @iamdefinitelyahuman and it seems like this may still be a bug.

Also, glad to see you're able to work with UniSwap V2!
Yes, this looks like a bug.  It should be possible to use `empty` without an assignment.

Resolving patch:
diff --git a/vyper/parser/parser_utils.py b/vyper/parser/parser_utils.py
--- a/vyper/parser/parser_utils.py
+++ b/vyper/parser/parser_utils.py
@@ -443,7 +443,6 @@ def pack_arguments(signature, args, context, stmt_expr, is_external_call):
     pos = getpos(stmt_expr)
     setters = []
     staticarray_offset = 0
-    needpos = False
 
     maxlen = sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32
     if is_external_call:
@@ -458,6 +457,10 @@ def pack_arguments(signature, args, context, stmt_expr, is_external_call):
     if len(signature.args) != len(args):
         return
 
+    # check for dynamic-length types
+    dynamic_remaining = len([i for i in signature.args if isinstance(i.typ, ByteArrayLike)])
+    needpos = bool(dynamic_remaining)
+
     for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])):
         if isinstance(typ, BaseType):
             setters.append(
@@ -471,26 +474,34 @@ def pack_arguments(signature, args, context, stmt_expr, is_external_call):
             )
 
         elif isinstance(typ, ByteArrayLike):
+            dynamic_remaining -= 1
             setters.append(["mstore", placeholder + staticarray_offset + i * 32, "_poz"])
             arg_copy = LLLnode.from_list("_s", typ=arg.typ, location=arg.location)
             target = LLLnode.from_list(["add", placeholder, "_poz"], typ=typ, location="memory",)
-            setters.append(
-                [
-                    "with",
-                    "_s",
-                    arg,
+            pos_setter = "pass"
+
+            # if `arg.value` is None, this is a call to `empty()`
+            # if `arg.typ.maxlen` is 0, this is a literal "" or b""
+            if arg.value is None or arg.typ.maxlen == 0:
+                if dynamic_remaining:
+                    # only adjust the dynamic pointer if this is not the last dynamic type
+                    pos_setter = ["set", "_poz", ["add", "_poz", 64]]
+                setters.append(["seq", mzero(target, 64), pos_setter])
+            else:
+                if dynamic_remaining:
+                    pos_setter = [
+                        "set",
+                        "_poz",
+                        ["add", 32, ["ceil32", ["add", "_poz", get_length(arg_copy)]]],
+                    ]
+                setters.append(
                     [
-                        "seq",
-                        make_byte_array_copier(target, arg_copy, pos),
-                        [
-                            "set",
-                            "_poz",
-                            ["add", 32, ["ceil32", ["add", "_poz", get_length(arg_copy)]]],
-                        ],
-                    ],
-                ]
-            )
-            needpos = True
+                        "with",
+                        "_s",
+                        arg,
+                        ["seq", make_byte_array_copier(target, arg_copy, pos), pos_setter],
+                    ]
+                )
 
         elif isinstance(typ, (StructType, ListType)):
             if has_dynamic_data(typ):


Test patch:
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
@@ -538,3 +538,51 @@ def baz():
 )
 def test_clear_typecheck(contract, get_contract, assert_compile_failed):
     assert_compile_failed(lambda: get_contract(contract), TypeMismatch)
+
+
+@pytest.mark.parametrize(
+    "a,b,expected",
+    [
+        ("empty(Bytes[65])", "b'hello'", [b"hello", b""]),
+        ("b'hello'", "empty(Bytes[33])", [b"", b"hello"]),
+        (
+            "empty(Bytes[65])",
+            "b'thirty three bytes long baby!!!!!'",
+            [b"thirty three bytes long baby!!!!!", b""],
+        ),
+        (
+            "b'thirty three bytes long baby!!!aathirty three bytes long baby!!!a'",
+            "b'thirty three bytes long baby!!!aa'",
+            [
+                b"thirty three bytes long baby!!!aa",
+                b"thirty three bytes long baby!!!aathirty three bytes long baby!!!a",
+            ],
+        ),
+    ],
+)
+def test_empty_as_func_arg(get_contract, a, b, expected):
+    code_a = """
+@view
+@external
+def foo(
+    a: uint256, b: Bytes[65], c: uint256, d: Bytes[33]
+) -> (uint256, Bytes[33], Bytes[65], uint256):
+    return a, d, b, c
+    """
+
+    code_b = f"""
+interface Foo:
+    def foo(
+        a: uint256, b: Bytes[65], c: uint256, d: Bytes[33]
+    ) -> (uint256, Bytes[33], Bytes[65], uint256): view
+
+@view
+@external
+def bar(a: address) -> (uint256, Bytes[33], Bytes[65], uint256):
+    return Foo(a).foo(12, {a}, 42, {b})
+    """
+
+    c1 = get_contract(code_a)
+    c2 = get_contract(code_b)
+
+    assert c2.bar(c1.address) == [12] + expected + [42]