Repository: vyperlang/vyper

Issue:
Cannot use `slice` on `bytes32` in calldata
### Version Information

* vyper Version (output of `vyper --version`): 0.2.7
* Python Version (output of `python --version`): ubuntu

### What's your issue about?
Attempting to use `slice` on a calldata `bytes32` variable raises `CompilerPanic`:

```python
  File "vyper/parser/expr.py", line 884, in parse_Call
    return DISPATCH_TABLE[function_name].build_LLL(self.expr, self.context)
  File "vyper/functions/signatures.py", line 174, in decorator_fn
    return wrapped_fn(self, node, subs, kwsubs, context)
  File "vyper/functions/functions.py", line 279, in build_LLL
    copier = make_byte_slice_copier(
  File "vyper/parser/parser_utils.py", line 178, in make_byte_slice_copier
    raise CompilerPanic(f"Unsupported location: {source.location}")
CompilerPanic: Unsupported location: calldata
```

The issue is not present when slicing calldata `Bytes` or `String`.

### How can it be fixed?
Should be fairly straightforward to add support for calldata in `functions/functions.py`



Discussion:

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
@@ -174,6 +174,11 @@ def make_byte_slice_copier(destination, source, length, max_length, pos=None):
         loader = ["mload", ["add", "_pos", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]]]
     elif source.location == "storage":
         loader = ["sload", ["add", "_pos", ["mload", MemoryPositions.FREE_LOOP_INDEX]]]
+    elif source.location == "calldata":
+        loader = [
+            "calldataload",
+            ["add", "_pos", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]],
+        ]
     else:
         raise CompilerPanic(f"Unsupported location: {source.location}")
     # Where to paste it?


Test patch:
diff --git a/tests/parser/functions/test_slice.py b/tests/parser/functions/test_slice.py
--- a/tests/parser/functions/test_slice.py
+++ b/tests/parser/functions/test_slice.py
@@ -1,3 +1,6 @@
+import pytest
+
+
 def test_test_slice(get_contract_with_gas_estimation):
     test_slice = """
 
@@ -22,25 +25,20 @@ def bar(inp1: Bytes[10]) -> int128:
 
     assert c.bar(b"badminton") == 35
 
-    print("Passed slice test")
-
 
 def test_test_slice2(get_contract_with_gas_estimation):
-    # TODO once parser is refactored so that `i` is `uint256`, remove call to `convert`
     test_slice2 = """
 @external
 def slice_tower_test(inp1: Bytes[50]) -> Bytes[50]:
     inp: Bytes[50] = inp1
     for i in range(1, 11):
-        inp = slice(inp, 1, convert(30 - i * 2, uint256))
+        inp = slice(inp, 1, 30 - i * 2)
     return inp
     """
     c = get_contract_with_gas_estimation(test_slice2)
     x = c.slice_tower_test(b"abcdefghijklmnopqrstuvwxyz1234")
     assert x == b"klmnopqrst", x
 
-    print("Passed advanced slice test")
-
 
 def test_test_slice3(get_contract_with_gas_estimation):
     test_slice3 = """
@@ -68,8 +66,6 @@ def bar(inp1: Bytes[50]) -> int128:
 
     assert c.bar(b"badminton") == 35
 
-    print("Passed storage slice test")
-
 
 def test_test_slice4(get_contract_with_gas_estimation, assert_tx_failed):
     test_slice4 = """
@@ -91,8 +87,6 @@ def foo(inp: Bytes[10], start: uint256, _len: uint256) -> Bytes[10]:
     assert_tx_failed(lambda: c.foo(b"badminton", 9, 1))
     assert_tx_failed(lambda: c.foo(b"badminton", 10, 0))
 
-    print("Passed slice edge case test")
-
 
 def test_slice_at_end(get_contract):
     code = """
@@ -105,3 +99,124 @@ def ret10_slice() -> Bytes[10]:
 
     c = get_contract(code)
     assert c.ret10_slice() == b"A"
+
+
+code_bytes32 = [
+    """
+foo: bytes32
+
+@external
+def __init__():
+    self.foo = 0x0001020304050607080910111213141516171819202122232425262728293031
+
+@external
+def bar() -> Bytes[5]:
+    return slice(self.foo, 3, 5)
+    """,
+    """
+foo: bytes32
+
+@external
+def __init__():
+    self.foo = 0x0001020304050607080910111213141516171819202122232425262728293031
+
+@external
+def bar() -> Bytes[32]:
+    a: uint256 = 3
+    b: uint256 = 5
+    return slice(self.foo, a, b)
+    """,
+    """
+@external
+def bar() -> Bytes[5]:
+    foo: bytes32 = 0x0001020304050607080910111213141516171819202122232425262728293031
+    return slice(foo, 3, 5)
+    """,
+    """
+@external
+def bar() -> Bytes[32]:
+    b: uint256 = 5
+    foo: bytes32 = 0x0001020304050607080910111213141516171819202122232425262728293031
+    a: uint256 = 3
+    return slice(foo, a, b)
+    """,
+]
+
+
+@pytest.mark.parametrize("code", code_bytes32)
+def test_slice_bytes32(get_contract, code):
+
+    c = get_contract(code)
+    assert c.bar().hex() == "0304050607"
+
+
+code_bytes32_calldata = [
+    """
+@external
+def bar(foo: bytes32) -> Bytes[32]:
+    return slice(foo, 3, 5)
+    """,
+    """
+@external
+def bar(foo: bytes32) -> Bytes[32]:
+    b: uint256 = 5
+    a: uint256 = 3
+    return slice(foo, a, b)
+    """,
+]
+
+
+@pytest.mark.parametrize("code", code_bytes32_calldata)
+def test_slice_bytes32_calldata(get_contract, code):
+
+    c = get_contract(code)
+    assert (
+        c.bar("0x0001020304050607080910111213141516171819202122232425262728293031").hex()
+        == "0304050607"
+    )
+
+
+code_bytes32_calldata_extended = [
+    (
+        """
+@external
+def bar(a: uint256, foo: bytes32, b: uint256) -> Bytes[32]:
+    return slice(foo, 3, 5)
+    """,
+        "0304050607",
+    ),
+    (
+        """
+@external
+def bar(a: uint256, foo: bytes32, b: uint256) -> Bytes[32]:
+    return slice(foo, a, b)
+    """,
+        "0304050607",
+    ),
+    (
+        """
+@external
+def bar(a: uint256, foo: bytes32, b: uint256) -> Bytes[32]:
+    return slice(foo, 31, b-4)
+    """,
+        "31",
+    ),
+    (
+        """
+@external
+def bar(a: uint256, foo: bytes32, b: uint256) -> Bytes[32]:
+    return slice(foo, 0, a+b)
+    """,
+        "0001020304050607",
+    ),
+]
+
+
+@pytest.mark.parametrize("code,result", code_bytes32_calldata_extended)
+def test_slice_bytes32_calldata_extended(get_contract, code, result):
+
+    c = get_contract(code)
+    assert (
+        c.bar(3, "0x0001020304050607080910111213141516171819202122232425262728293031", 5).hex()
+        == result
+    )