Repository: vyperlang/vyper

Issue:
VIP: start and length args in `slice` should be uint256
## Simple Summary
The `start` and `length` args in the builtin function `slice` should be expected as `uint256`.


## Motivation
We do not use negative values for indexing, so a negative start index or length already won't work. There is no need for `int128` here.

## Specification
Change the function signature to:

```python
slice(b: bytes, start: uint256, length: uint256) → bytes
```

## Backwards Compatibility
This is a breaking change.

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)



Discussion:

Resolving patch:
diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py
--- a/vyper/functions/functions.py
+++ b/vyper/functions/functions.py
@@ -225,15 +225,14 @@ def build_LLL(self, expr, context):
 class Slice:
 
     _id = "slice"
-    _inputs = [("b", ("bytes", "bytes32", "string")), ("start", "int128"), ("length", "int128")]
+    _inputs = [("b", ("bytes", "bytes32", "string")), ("start", "uint256"), ("length", "uint256")]
     _return_type = None
 
     def fetch_call_return(self, node):
         validate_call_args(node, 3)
 
-        # TODO
-        if isinstance(node.args[1], vy_ast.Int) and node.args[1].value < 0:
-            raise ArgumentException("Start must be a positive integer", node.args[1])
+        for arg in node.args[1:]:
+            validate_expected_type(arg, Uint256Definition())
         if isinstance(node.args[2], vy_ast.Int) and node.args[2].value < 1:
             raise ArgumentException("Length cannot be less than 1", node.args[2])
 
@@ -245,9 +244,6 @@ def fetch_call_return(self, node):
         except VyperException:
             return_type = BytesArrayDefinition()
 
-        for arg in node.args[1:]:
-            validate_expected_type(arg, Int128Definition())
-
         if isinstance(node.args[2], vy_ast.Int):
             return_type.set_length(node.args[2].value)
         else:


Test patch:
diff --git a/tests/parser/features/test_internal_call.py b/tests/parser/features/test_internal_call.py
--- a/tests/parser/features/test_internal_call.py
+++ b/tests/parser/features/test_internal_call.py
@@ -86,12 +86,12 @@ def _catty(x: bytes[5], y: bytes[5]) -> bytes[10]:
     return concat(x, y)
 
 @private
-def _slicey1(x: bytes[10], y: int128) -> bytes[10]:
+def _slicey1(x: bytes[10], y: uint256) -> bytes[10]:
     return slice(x, 0, y)
 
 @private
-def _slicey2(y: int128, x: bytes[10]) -> bytes[10]:
-    return slice(x, 0,y)
+def _slicey2(y: uint256, x: bytes[10]) -> bytes[10]:
+    return slice(x, 0, y)
 
 @public
 def returnten() -> int128:
@@ -152,7 +152,7 @@ def _underscore() -> bytes[1]:
     return b"_"
 
 @private
-def _hardtest(x: bytes[100], y: int128, z: int128, a: bytes[100], b: int128, c: int128) -> bytes[201]:  # noqa: E501
+def _hardtest(x: bytes[100], y: uint256, z: uint256, a: bytes[100], b: uint256, c: uint256) -> bytes[201]:  # noqa: E501
     return concat(slice(x, y, z), self._underscore(), slice(a, b, c))
 
 @public
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
@@ -26,12 +26,13 @@ def bar(inp1: bytes[10]) -> int128:
 
 
 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 = """
 @public
 def slice_tower_test(inp1: bytes[50]) -> bytes[50]:
     inp: bytes[50] = inp1
     for i in range(1, 11):
-        inp = slice(inp, 1, 30 - i * 2)
+        inp = slice(inp, 1, convert(30 - i * 2, uint256))
     return inp
     """
     c = get_contract_with_gas_estimation(test_slice2)
@@ -73,7 +74,7 @@ def bar(inp1: bytes[50]) -> int128:
 def test_test_slice4(get_contract_with_gas_estimation, assert_tx_failed):
     test_slice4 = """
 @public
-def foo(inp: bytes[10], start: int128, _len: int128) -> bytes[10]:
+def foo(inp: bytes[10], start: uint256, _len: uint256) -> bytes[10]:
     return slice(inp, start, _len)
     """
 
diff --git a/tests/parser/syntax/test_block.py b/tests/parser/syntax/test_block.py
--- a/tests/parser/syntax/test_block.py
+++ b/tests/parser/syntax/test_block.py
@@ -28,11 +28,14 @@ def foo() -> decimal:
     y: int128 = block.timestamp + 50
     return x / y
     """,
-    """
+    (
+        """
 @public
 def foo():
-    x: bytes[10] = slice(b"cow", 0, block.timestamp)
+    x: bytes[10] = slice(b"cow", -1, block.timestamp)
     """,
+        InvalidType,
+    ),
     """
 @public
 def foo():
@@ -67,7 +70,7 @@ def add_record():
     """
 @public
 def foo(inp: bytes[10]) -> bytes[3]:
-    return slice(inp, block.timestamp, 3)
+    return slice(inp, block.timestamp, 6)
     """,
     (
         """
diff --git a/tests/parser/syntax/test_chainid.py b/tests/parser/syntax/test_chainid.py
--- a/tests/parser/syntax/test_chainid.py
+++ b/tests/parser/syntax/test_chainid.py
@@ -38,11 +38,6 @@ def foo() -> decimal:
     """,
     """
 @public
-def foo():
-    x: bytes[10] = slice("cow", 0, chain.id)
-    """,
-    """
-@public
 def foo():
     x: int128 = 7
     y: int128 = min(x, chain.id)
@@ -61,11 +56,14 @@ def add_record():
 def add_record():
     self.a[chain.id] = chain.id + 20
     """,
-    """
+    (
+        """
 @public
 def foo(inp: bytes[10]) -> bytes[3]:
-    return slice(inp, chain.id, 3)
+    return slice(inp, chain.id, -3)
     """,
+        InvalidType,
+    ),
 ]
 
 
diff --git a/tests/parser/types/test_bytes_literal.py b/tests/parser/types/test_bytes_literal.py
--- a/tests/parser/types/test_bytes_literal.py
+++ b/tests/parser/types/test_bytes_literal.py
@@ -43,7 +43,7 @@ def test_bytes_literal_splicing_fuzz(get_contract_with_gas_estimation):
 moo: bytes[100]
 
 @public
-def foo(s: int128, L: int128) -> bytes[100]:
+def foo(s: uint256, L: uint256) -> bytes[100]:
         x: int128 = 27
         r: bytes[100] = slice(b"{("c" * i)}", s, L)
         y: int128 = 37
@@ -52,7 +52,7 @@ def foo(s: int128, L: int128) -> bytes[100]:
         return b"3434346667777"
 
 @public
-def bar(s: int128, L: int128) -> bytes[100]:
+def bar(s: uint256, L: uint256) -> bytes[100]:
         self.moo = b"{("c" * i)}"
         x: int128 = 27
         r: bytes[100] = slice(self.moo, s, L)
@@ -62,7 +62,7 @@ def bar(s: int128, L: int128) -> bytes[100]:
         return b"3434346667777"
 
 @public
-def baz(s: int128, L: int128) -> bytes[100]:
+def baz(s: uint256, L: uint256) -> bytes[100]:
         x: int128 = 27
         self.moo = slice(b"{("c" * i)}", s, L)
         y: int128 = 37
diff --git a/tests/parser/types/test_string.py b/tests/parser/types/test_string.py
--- a/tests/parser/types/test_string.py
+++ b/tests/parser/types/test_string.py
@@ -59,7 +59,7 @@ def get(k: string[34]) -> int128:
 def test_string_slice(get_contract_with_gas_estimation, assert_tx_failed):
     test_slice4 = """
 @public
-def foo(inp: string[10], start: int128, _len: int128) -> string[10]:
+def foo(inp: string[10], start: uint256, _len: uint256) -> string[10]:
     return slice(inp, start, _len)
     """