Skip to content

Commit

Permalink
Merge pull request #1479 from charles-cooper/array_index
Browse files Browse the repository at this point in the history
Allow array index to be uint256
  • Loading branch information
jacqueswww authored Jun 21, 2019
2 parents 0cf2a29 + 8618aa2 commit 34ad5a5
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
55 changes: 55 additions & 0 deletions tests/parser/types/test_lists.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from vyper.exceptions import (
ArrayIndexException,
)


def test_list_tester_code(get_contract_with_gas_estimation):
list_tester_code = """
z: int128[3]
Expand Down Expand Up @@ -199,3 +204,53 @@ def test_multi4() -> uint256[2][2][2][2]:
[[[1, 0], [0, 4]], [[0, 0], [0, 0]]],
[[[444, 0], [0, 0]], [[1, 0], [0, 222]]],
]


def test_uint256_accessor(get_contract_with_gas_estimation, assert_tx_failed):
code = """
@public
def bounds_check_uint256(ix: uint256) -> uint256:
xs: uint256[3] = [1,2,3]
return xs[ix]
"""
c = get_contract_with_gas_estimation(code)
assert c.bounds_check_uint256(0) == 1
assert c.bounds_check_uint256(2) == 3
assert_tx_failed(lambda: c.bounds_check_uint256(3))


def test_int128_accessor(get_contract_with_gas_estimation, assert_tx_failed):
code = """
@public
def bounds_check_int128(ix: int128) -> uint256:
xs: uint256[3] = [1,2,3]
return xs[ix]
"""
c = get_contract_with_gas_estimation(code)
assert c.bounds_check_int128(0) == 1
assert c.bounds_check_int128(2) == 3
assert_tx_failed(lambda: c.bounds_check_int128(3))
assert_tx_failed(lambda: c.bounds_check_int128(-1))


def test_compile_time_bounds_check(get_contract_with_gas_estimation, assert_compile_failed):
code = """
@public
def fail() -> uint256:
xs: uint256[3] = [1,2,3]
return xs[3]
"""
assert_compile_failed(
lambda: get_contract_with_gas_estimation(code),
ArrayIndexException
)
code = """
@public
def fail() -> uint256:
xs: uint256[3] = [1,2,3]
return xs[-1]
"""
assert_compile_failed(
lambda: get_contract_with_gas_estimation(code),
ArrayIndexException
)
4 changes: 4 additions & 0 deletions vyper/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class SyntaxException(ParserException):
pass


class ArrayIndexException(ParserException):
pass


class CompilerPanic(Exception):

def __init__(self, message):
Expand Down
22 changes: 19 additions & 3 deletions vyper/parser/parser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from vyper import ast
from vyper.exceptions import (
ArrayIndexException,
InvalidLiteralException,
StructureException,
TypeMismatchException,
Expand Down Expand Up @@ -333,9 +334,24 @@ def add_variable_offset(parent, key, pos):
elif isinstance(typ, ListType):

subtype = typ.subtype
sub = [
'uclamplt', base_type_conversion(key, key.typ, BaseType('int128'), pos=pos), typ.count
]
k = unwrap_location(key)
if not is_base_type(key.typ, ('int128', 'uint256')):
raise TypeMismatchException('Invalid type for array index: %r' % key.typ, pos)

if key.typ.is_literal: # note: BaseType always has is_literal attr
# perform the check at compile time and elide the runtime check.
if key.value < 0 or key.value >= typ.count:
raise ArrayIndexException(
'Array index determined to be out of bounds. '
'Index is %r but array size is %r' % (key.value, typ.count),
pos)
sub = k
else:
# this works, even for int128. for int128, since two's-complement
# is used, if the index is negative, (unsigned) LT will interpret
# it as a very large number, larger than any practical value for
# an array index, and the clamp will throw an error.
sub = ['uclamplt', k, typ.count]

if location == 'storage':
return LLLnode.from_list(['add', ['sha3_32', parent], sub],
Expand Down

0 comments on commit 34ad5a5

Please sign in to comment.