diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst index 387f22915b..30bc41bf44 100644 --- a/docs/built-in-functions.rst +++ b/docs/built-in-functions.rst @@ -56,6 +56,20 @@ Rounds a decimal up to the nearest integer. Converts a variable/ literal from one type to another. +**clear** +------------------------- +:: + + def clear(a): + """ + :param a: variable to reset to its default value + :type a: all types + + :output c: either decimal, int128, uint256 or bytes32 + """ + +Clears a variable's contents to the default value of its type. + **as_wei_value** ------------------------- :: diff --git a/docs/index.rst b/docs/index.rst index 447cc4b57e..522b638db9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -55,6 +55,7 @@ Following the principles and goals, Vyper **does not** provide the following fea Compatibility-breaking Changelog ******************************** +8 **2018.12.12**: Disallow use of `None`, disallow use of `del`, implemented `clear()` built-in function. * **2018.11.19**: Change mapping syntax to use map(). (`VIP1026 `_) * **2018.10.02**: Change the convert style to use types instead of string. (`VIP1026 `_) * **2018.09.24**: Add support for custom constants. diff --git a/tests/parser/features/test_map_delete.py b/tests/parser/features/test_map_delete.py deleted file mode 100644 index a531616ed8..0000000000 --- a/tests/parser/features/test_map_delete.py +++ /dev/null @@ -1,85 +0,0 @@ - - -def test_map_delete(get_contract_with_gas_estimation): - code = """ -big_storage: map(bytes32, bytes32) - -@public -def set(key: bytes32, value: bytes32): - self.big_storage[key] = value - -@public -def get(key: bytes32) -> bytes32: - return self.big_storage[key] - -@public -def delete(key: bytes32): - del self.big_storage[key] - """ - - c = get_contract_with_gas_estimation(code) - - assert c.get(b"test") == b'\x00' * 32 - c.set(b"test", b"value", transact={}) - assert c.get(b"test")[:5] == b"value" - c.delete(b"test", transact={}) - assert c.get(b"test") == b'\x00' * 32 - - -def test_map_delete_nested(get_contract_with_gas_estimation): - code = """ -big_storage: map(bytes32, map(bytes32, bytes32)) - -@public -def set(key1: bytes32, key2: bytes32, value: bytes32): - self.big_storage[key1][key2] = value - -@public -def get(key1: bytes32, key2: bytes32) -> bytes32: - return self.big_storage[key1][key2] - -@public -def delete(key1: bytes32, key2: bytes32): - del self.big_storage[key1][key2] - """ - - c = get_contract_with_gas_estimation(code) - - assert c.get(b"test1", b"test2") == b'\x00' * 32 - c.set(b"test1", b"test2", b"value", transact={}) - assert c.get(b"test1", b"test2")[:5] == b"value" - c.delete(b"test1", b"test2", transact={}) - assert c.get(b"test1", b"test2") == b'\x00' * 32 - - -def test_map_delete_struct(get_contract_with_gas_estimation): - code = """ -struct X: - a: int128 - b: int128 - -structmap: map(int128, X) - -@public -def set(): - self.structmap[123] = X({ - a: 333, - b: 444 - }) - -@public -def get() -> (int128, int128): - return self.structmap[123].a, self.structmap[123].b - -@public -def delete(): - self.structmap[123] = None - """ - - c = get_contract_with_gas_estimation(code) - - assert c.get() == [0, 0] - c.set(transact={}) - assert c.get() == [333, 444] - c.delete(transact={}) - assert c.get() == [0, 0] diff --git a/tests/parser/functions/test_clear.py b/tests/parser/functions/test_clear.py new file mode 100644 index 0000000000..0306b764a9 --- /dev/null +++ b/tests/parser/functions/test_clear.py @@ -0,0 +1,412 @@ + +def test_clear_basic_type(get_contract_with_gas_estimation): + contracts = [ + """ +foobar: int128 + +@public +def foo(): + self.foobar = 1 + bar: int128 = 1 + + clear(self.foobar) + clear(bar) + + assert self.foobar == 0 + assert bar == 0 + """, + """ +foobar: uint256 + +@public +def foo(): + self.foobar = 1 + bar: uint256 = 1 + + clear(self.foobar) + clear(bar) + + assert self.foobar == 0 + assert bar == 0 + """, + """ +foobar: bool + +@public +def foo(): + self.foobar = True + bar: bool = True + + clear(self.foobar) + clear(bar) + + assert self.foobar == False + assert bar == False + """, + """ +foobar: decimal + +@public +def foo(): + self.foobar = 1.0 + bar: decimal = 1.0 + + clear(self.foobar) + clear(bar) + + assert self.foobar == 0.0 + assert bar == 0.0 + """, + """ +foobar: bytes32 + +@public +def foo(): + self.foobar = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + bar: bytes32 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + clear(self.foobar) + clear(bar) + + assert self.foobar == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert bar == 0x0000000000000000000000000000000000000000000000000000000000000000 + """, + """ +foobar: address + +@public +def foo(): + self.foobar = msg.sender + bar: address = msg.sender + + clear(self.foobar) + clear(bar) + + assert self.foobar == ZERO_ADDRESS + assert bar == ZERO_ADDRESS + """ + ] + + for contract in contracts: + c = get_contract_with_gas_estimation(contract) + c.foo() + + +def test_clear_basic_type_lists(get_contract_with_gas_estimation): + contracts = [ + """ +foobar: int128[3] + +@public +def foo(): + self.foobar = [1, 2, 3] + bar: int128[3] = [1, 2, 3] + + clear(self.foobar) + clear(bar) + + assert self.foobar[0] == 0 + assert self.foobar[1] == 0 + assert self.foobar[2] == 0 + assert bar[0] == 0 + assert bar[1] == 0 + assert bar[2] == 0 + """, + """ +foobar: uint256[3] + +@public +def foo(): + self.foobar = [1, 2, 3] + bar: uint256[3] = [1, 2, 3] + + clear(self.foobar) + clear(bar) + + assert self.foobar[0] == 0 + assert self.foobar[1] == 0 + assert self.foobar[2] == 0 + assert bar[0] == 0 + assert bar[1] == 0 + assert bar[2] == 0 + """, + """ +foobar: bool[3] + +@public +def foo(): + self.foobar = [True, True, True] + bar: bool[3] = [True, True, True] + + clear(self.foobar) + clear(bar) + + assert self.foobar[0] == False + assert self.foobar[1] == False + assert self.foobar[2] == False + assert bar[0] == False + assert bar[1] == False + assert bar[2] == False + """, + """ +foobar: decimal[3] + +@public +def foo(): + self.foobar = [1.0, 2.0, 3.0] + bar: decimal[3] = [1.0, 2.0, 3.0] + + clear(self.foobar) + clear(bar) + + assert self.foobar[0] == 0.0 + assert self.foobar[1] == 0.0 + assert self.foobar[2] == 0.0 + assert bar[0] == 0.0 + assert bar[1] == 0.0 + assert bar[2] == 0.0 + """, + """ +foobar: bytes32[3] + +@public +def foo(): + self.foobar = [ + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000, + 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ] + bar: bytes32[3] = [ + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000, + 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ] + + clear(self.foobar) + clear(bar) + + assert self.foobar[0] == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert self.foobar[1] == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert self.foobar[2] == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert bar[0] == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert bar[1] == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert bar[2] == 0x0000000000000000000000000000000000000000000000000000000000000000 + """, + """ +foobar: address[3] + +@public +def foo(): + self.foobar = [msg.sender, msg.sender, msg.sender] + bar: address[3] = [msg.sender, msg.sender, msg.sender] + + clear(self.foobar) + clear(bar) + + assert self.foobar[0] == ZERO_ADDRESS + assert self.foobar[1] == ZERO_ADDRESS + assert self.foobar[2] == ZERO_ADDRESS + assert bar[0] == ZERO_ADDRESS + assert bar[1] == ZERO_ADDRESS + assert bar[2] == ZERO_ADDRESS + """ + ] + + for contract in contracts: + c = get_contract_with_gas_estimation(contract) + c.foo() + + +def test_clear_literals(assert_compile_failed, get_contract_with_gas_estimation): + contracts = [ + """ +@public +def foo(): + clear(1) + """, + """ +@public +def foo(): + clear(True) + """, + """ +@public +def foo(): + clear(1.0) + """, + """ +@public +def foo(): + clear(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + """, + """ +@public +def foo(): + clear(0xF5D4020dCA6a62bB1efFcC9212AAF3c9819E30D7) + """ + ] + + for contract in contracts: + assert_compile_failed( + lambda: get_contract_with_gas_estimation(contract), + Exception + ) + + +def test_clear_bytes(get_contract_with_gas_estimation): + code = """ +foobar: bytes[5] + +@public +def foo() -> (bytes[5], bytes[5]): + self.foobar = 'Hello' + bar: bytes[5] = 'World' + + clear(self.foobar) + clear(bar) + + return (self.foobar, bar) + """ + + c = get_contract_with_gas_estimation(code) + a, b = c.foo() + assert a == b'' + assert b == b'' + + +def test_clear_struct(get_contract_with_gas_estimation): + code = """ +struct FOOBAR: + a: int128 + b: uint256 + c: bool + d: decimal + e: bytes32 + f: address + +foobar: FOOBAR + +@public +def foo(): + self.foobar = FOOBAR({ + a: 1, + b: 2, + c: True, + d: 3.0, + e: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + f: msg.sender + }) + bar: FOOBAR = FOOBAR({ + a: 1, + b: 2, + c: True, + d: 3.0, + e: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + f: msg.sender + }) + + clear(self.foobar) + clear(bar) + + assert self.foobar.a == 0 + assert self.foobar.b == 0 + assert self.foobar.c == False + assert self.foobar.d == 0.0 + assert self.foobar.e == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert self.foobar.f == ZERO_ADDRESS + + assert bar.a == 0 + assert bar.b == 0 + assert bar.c == False + assert bar.d == 0.0 + assert bar.e == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert bar.f == ZERO_ADDRESS + """ + + c = get_contract_with_gas_estimation(code) + c.foo() + + +def test_map_clear(get_contract_with_gas_estimation): + code = """ +big_storage: map(bytes32, bytes32) + +@public +def set(key: bytes32, value: bytes32): + self.big_storage[key] = value + +@public +def get(key: bytes32) -> bytes32: + return self.big_storage[key] + +@public +def delete(key: bytes32): + clear(self.big_storage[key]) + """ + + c = get_contract_with_gas_estimation(code) + + assert c.get(b"test") == b'\x00' * 32 + c.set(b"test", b"value", transact={}) + assert c.get(b"test")[:5] == b"value" + c.delete(b"test", transact={}) + assert c.get(b"test") == b'\x00' * 32 + + +def test_map_clear_nested(get_contract_with_gas_estimation): + code = """ +big_storage: map(bytes32, map(bytes32, bytes32)) + +@public +def set(key1: bytes32, key2: bytes32, value: bytes32): + self.big_storage[key1][key2] = value + +@public +def get(key1: bytes32, key2: bytes32) -> bytes32: + return self.big_storage[key1][key2] + +@public +def delete(key1: bytes32, key2: bytes32): + clear(self.big_storage[key1][key2]) + """ + + c = get_contract_with_gas_estimation(code) + + assert c.get(b"test1", b"test2") == b'\x00' * 32 + c.set(b"test1", b"test2", b"value", transact={}) + assert c.get(b"test1", b"test2")[:5] == b"value" + c.delete(b"test1", b"test2", transact={}) + assert c.get(b"test1", b"test2") == b'\x00' * 32 + + +def test_map_clear_struct(get_contract_with_gas_estimation): + code = """ +struct X: + a: int128 + b: int128 + +structmap: map(int128, X) + +@public +def set(): + self.structmap[123] = X({ + a: 333, + b: 444 + }) + +@public +def get() -> (int128, int128): + return self.structmap[123].a, self.structmap[123].b + +@public +def delete(): + clear(self.structmap[123]) + """ + + c = get_contract_with_gas_estimation(code) + + assert c.get() == [0, 0] + c.set(transact={}) + assert c.get() == [333, 444] + c.delete(transact={}) + assert c.get() == [0, 0] diff --git a/tests/parser/globals/test_setters.py b/tests/parser/globals/test_setters.py index a1d573c2e3..9939fa3eef 100644 --- a/tests/parser/globals/test_setters.py +++ b/tests/parser/globals/test_setters.py @@ -30,12 +30,12 @@ def gop() -> int128: # Following a standard naming scheme; nothing to do with th @public def hoo() -> int128: - self.dog = None + clear(self.dog) return(self.dog[0] + self.dog[1] * 10 + self.dog[2] * 100) @public def hop() -> int128: - self.bar[1] = None + clear(self.bar[1]) return self.bar[0][0] + self.bar[0][1] * 10 + self.bar[0][2] * 100 + \ self.bar[1][0] * 1000 + self.bar[1][1] * 10000 + self.bar[1][2] * 100000 @@ -43,7 +43,7 @@ def hop() -> int128: def joo() -> int128: goo: int128[3] goo = [1, 2, 3] - goo = None + clear(goo) return(goo[0] + goo[1] * 10 + goo[2] * 100) @public @@ -51,7 +51,7 @@ def jop() -> int128: gar: int128[3][3] gar[0] = [1, 2, 3] gar[1] = [4, 5, 6] - gar[1] = None + clear(gar[1]) return gar[0][0] + gar[0][1] * 10 + gar[0][2] * 100 + \ gar[1][0] * 1000 + gar[1][1] * 10000 + gar[1][2] * 100000 @@ -187,8 +187,8 @@ def fop() -> int128: @public def foq() -> int128: popp: Mom = Mom({a: [C({c: 1}), C({c: 2}), C({c: 3})], b: 4}) - popp.a[0] = None - popp.a[2] = None + clear(popp.a[0]) + clear(popp.a[2]) return popp.a[0].c + popp.a[1].c * 10 + popp.a[2].c * 100 + popp.b * 1000 """ diff --git a/tests/parser/integration/test_crowdfund.py b/tests/parser/integration/test_crowdfund.py index cba6f721f3..1bd10cb3af 100644 --- a/tests/parser/integration/test_crowdfund.py +++ b/tests/parser/integration/test_crowdfund.py @@ -150,7 +150,7 @@ def refund(): self.refundIndex = self.nextFunderIndex return send(self.funders[i].sender, self.funders[i].value) - self.funders[i] = None + clear(self.funders[i]) self.refundIndex = ind + 30 """ diff --git a/tests/parser/syntax/test_no_none.py b/tests/parser/syntax/test_no_none.py new file mode 100644 index 0000000000..6d72d8c11a --- /dev/null +++ b/tests/parser/syntax/test_no_none.py @@ -0,0 +1,221 @@ +from vyper.exceptions import ( + InvalidLiteralException +) + + +def test_no_none_assign(assert_compile_failed, get_contract_with_gas_estimation): + contracts = [ + """ +@public +def foo(): + bar: int128 + bar = None + """, + """ +@public +def foo(): + bar: uint256 + bar = None + """, + """ +@public +def foo(): + bar: bool + bar = None + """, + """ +@public +def foo(): + bar: decimal + bar = None + """, + """ +@public +def foo(): + bar: bytes32 + bar = None + """, + """ +@public +def foo(): + bar: address + bar = None + """, + """ +@public +def foo(): + bar: int128 = None + """, + """ +@public +def foo(): + bar: uint256 = None + """, + """ +@public +def foo(): + bar: bool = None + """, + """ +@public +def foo(): + bar: decimal = None + """, + """ +@public +def foo(): + bar: bytes32 = None + """, + """ +@public +def foo(): + bar: address = None + """ + ] + + for contract in contracts: + assert_compile_failed( + lambda: get_contract_with_gas_estimation(contract), + InvalidLiteralException + ) + + +def test_no_is_none(assert_compile_failed, get_contract_with_gas_estimation): + contracts = [ + """ +@public +def foo(): + bar: int128 + assert bar is None + """, + """ +@public +def foo(): + bar: uint256 + assert bar is None + """, + """ +@public +def foo(): + bar: bool + assert bar is None + """, + """ +@public +def foo(): + bar: decimal + assert bar is None + """, + """ +@public +def foo(): + bar: bytes32 + assert bar is None + """, + """ +@public +def foo(): + bar: address + assert bar is None + """ + ] + + for contract in contracts: + assert_compile_failed( + lambda: get_contract_with_gas_estimation(contract), + InvalidLiteralException + ) + + +def test_no_eq_none(assert_compile_failed, get_contract_with_gas_estimation): + contracts = [ + """ +@public +def foo(): + bar: int128 + assert bar == None + """, + """ +@public +def foo(): + bar: uint256 + assert bar == None + """, + """ +@public +def foo(): + bar: bool + assert bar == None + """, + """ +@public +def foo(): + bar: decimal + assert bar == None + """, + """ +@public +def foo(): + bar: bytes32 + assert bar == None + """, + """ +@public +def foo(): + bar: address + assert bar == None + """ + ] + + for contract in contracts: + assert_compile_failed( + lambda: get_contract_with_gas_estimation(contract), + InvalidLiteralException + ) + + +def test_struct_none(assert_compile_failed, get_contract_with_gas_estimation): + contracts = [ + """ +struct Mom: + a: uint256 + b: int128 + +@public +def foo(): + mom: Mom = Mom({a: None, b: 0}) + """, + """ +struct Mom: + a: uint256 + b: int128 + +@public +def foo(): + mom: Mom = Mom({a: 0, b: None}) + """, + """ +struct Mom: + a: uint256 + b: int128 + +@public +def foo(): + mom: Mom = Mom({b: None, a: 0}) + """, + """ +struct Mom: + a: uint256 + b: int128 + +@public +def foo(): + mom: Mom = Mom({a: None, b: None}) + """ + ] + + for contract in contracts: + assert_compile_failed( + lambda: get_contract_with_gas_estimation(contract), + InvalidLiteralException + ) diff --git a/tests/parser/syntax/test_structs.py b/tests/parser/syntax/test_structs.py index 4cb7ffc5cf..133b7074de 100644 --- a/tests/parser/syntax/test_structs.py +++ b/tests/parser/syntax/test_structs.py @@ -255,7 +255,7 @@ def foo(): nom: Nom @public def foo(): - self.nom = None + clear(self.nom) """, """ struct C: @@ -439,7 +439,7 @@ def foo(): nom: C[3] @public def foo(): - self.mom = Mom({a: None, b: 5}) + self.mom = Mom({a: self.nom, b: 5}) """, """ struct C: diff --git a/tests/parser/types/test_bytes.py b/tests/parser/types/test_bytes.py index 5fd4794101..bc1546ec97 100644 --- a/tests/parser/types/test_bytes.py +++ b/tests/parser/types/test_bytes.py @@ -102,13 +102,13 @@ def test_test_bytes4(get_contract_with_gas_estimation): @public def foo(inp: bytes[60]) -> bytes[60]: self.a = inp - self.a = None + self.a = "" return self.a @public def bar(inp: bytes[60]) -> bytes[60]: b: bytes[60] = inp - b = None + b = "" return b """ diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py index 2a0116bcaa..5e92e3d689 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -5,6 +5,7 @@ InvalidLiteralException, StructureException, TypeMismatchException, + ParserException, ) from .signature import ( signature, @@ -723,6 +724,10 @@ def _can_compare_with_uint256(operand): return LLLnode.from_list(['with', '_l', left, ['with', '_r', right, o]], typ=otyp, pos=getpos(expr)) +def _clear(): + raise ParserException("This function should never be called! `clear()` is currently handled differently than other functions as it self modifies its input argument statement. Please see `_clear()` in `stmt.py`") + + dispatch_table = { 'floor': floor, 'ceil': ceil, @@ -755,6 +760,7 @@ def _can_compare_with_uint256(operand): } stmt_dispatch_table = { + 'clear': _clear, 'send': send, 'selfdestruct': selfdestruct, 'raw_call': raw_call, diff --git a/vyper/parser/expr.py b/vyper/parser/expr.py index e9b6aae5b4..6ce3ec828b 100644 --- a/vyper/parser/expr.py +++ b/vyper/parser/expr.py @@ -511,6 +511,9 @@ def compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.comparators[0], self.context) + if isinstance(right.typ, NullType): + raise InvalidLiteralException('Comparison to None is not allowed, compare against a default value.', self.expr) + if isinstance(left.typ, ByteArrayType) and isinstance(right.typ, ByteArrayType): if left.typ.maxlen != right.typ.maxlen: raise TypeMismatchException('Can only compare bytes of the same length', self.expr) diff --git a/vyper/parser/parser_utils.py b/vyper/parser/parser_utils.py index 2e2c169642..1310547646 100644 --- a/vyper/parser/parser_utils.py +++ b/vyper/parser/parser_utils.py @@ -418,6 +418,9 @@ def make_setter(left, right, location, pos): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatchException("Setter type mismatch: left side is %r, right side is %r" % (left.typ, right.typ), pos) if isinstance(left.typ, StructType): + for k in right.args: + if k.value is None: + raise InvalidLiteralException('Setting struct value to None is not allowed, use a default value.', pos) for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException("Keys don't match for structs, missing %s" % k, pos) diff --git a/vyper/parser/pre_parser.py b/vyper/parser/pre_parser.py index 431a1e486f..0a35c83a2a 100644 --- a/vyper/parser/pre_parser.py +++ b/vyper/parser/pre_parser.py @@ -10,7 +10,9 @@ tokenize, untokenize, ) -from vyper.exceptions import StructureException +from vyper.exceptions import ( + StructureException, +) from vyper import __version__ diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py index dbcaae154d..87af428944 100644 --- a/vyper/parser/stmt.py +++ b/vyper/parser/stmt.py @@ -31,7 +31,7 @@ ListType, TupleType, StructType, - NullType + NullType, ) from vyper.types import ( get_size_of_type, @@ -165,14 +165,21 @@ def ann_assign(self): o = LLLnode.from_list('pass', typ=None, pos=pos) if self.stmt.value is not None: sub = Expr(self.stmt.value, self.context).lll_node + + # Disallow assignment to None + if isinstance(sub.typ, NullType): + raise InvalidLiteralException('Assignment to None is not allowed, use a default value or built-in `clear()`.', self.stmt) + # If bytes[32] to bytes32 assignment rewrite sub as bytes32. if isinstance(sub.typ, ByteArrayType) and sub.typ.maxlen == 32 and isinstance(typ, BaseType) and typ.typ == 'bytes32': bytez, bytez_length = string_to_bytes(self.stmt.value.s) sub = LLLnode(bytes_to_int(bytez), typ=BaseType('bytes32'), pos=getpos(self.stmt)) + self._check_valid_assign(sub) self._check_same_variable_assign(sub) variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt)) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) + # o.pos = getpos(self.stmt) # TODO: Should this be here like in assign()? self.context.set_in_assignment(False) return o @@ -183,6 +190,10 @@ def assign(self): self.context.set_in_assignment(True) sub = Expr(self.stmt.value, self.context).lll_node + # Disallow assignment to None + if isinstance(sub.typ, NullType): + raise InvalidLiteralException('Assignment to None is not allowed, use a default value or built-in `clear()`.', self.stmt) + # Determine if it's an RLPList assignment. if isinstance(self.stmt.value, ast.Call) and getattr(self.stmt.value.func, 'id', '') is 'RLPList': pos = self.context.new_variable(self.stmt.targets[0].id, sub.typ) @@ -236,6 +247,23 @@ def parse_if(self): self.context.end_blockscope(block_scope_id) return o + def _clear(self): + # Create zero node + none = ast.NameConstant(None) + none.lineno = self.stmt.lineno + none.col_offset = self.stmt.col_offset + zero = Expr(none, self.context).lll_node + + # Get target variable + target = self.get_target(self.stmt.args[0]) + + # Generate LLL node to set to zero + o = make_setter(target, zero, target.location, pos=getpos(self.stmt)) + o.pos = getpos(self.stmt) + self.context.set_in_assignment(False) + + return o + def call(self): from .parser import ( pack_logging_data, @@ -244,7 +272,10 @@ def call(self): if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: - return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) + if self.stmt.func.id == 'clear': + return self._clear() + else: + return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in dispatch_table: raise StructureException("Function {} can not be called without being used.".format(self.stmt.func.id), self.stmt) else: @@ -718,19 +749,7 @@ def increment_dynamic_offset(dynamic_spot): raise TypeMismatchException("Can only return base type!", self.stmt) def parse_delete(self): - from .parser import ( - make_setter, - ) - if len(self.stmt.targets) != 1: - raise StructureException("Can delete one variable at a time", self.stmt) - target = self.stmt.targets[0] - target_lll = Expr(self.stmt.targets[0], self.context).lll_node - - if isinstance(target, ast.Subscript): - if target_lll.location == "storage": - return make_setter(target_lll, LLLnode.from_list(None, typ=NullType()), "storage", pos=getpos(self.stmt)) - - raise StructureException("Deleting type not supported.", self.stmt) + raise StructureException("Deleting is not supported, use built-in `clear()` function.", self.stmt) def get_target(self, target): if isinstance(target, ast.Subscript) and self.context.in_for_loop: # Check if we are doing assignment of an iteration loop. diff --git a/vyper/utils.py b/vyper/utils.py index 91d48ec262..62999fdd74 100644 --- a/vyper/utils.py +++ b/vyper/utils.py @@ -148,7 +148,7 @@ def in_bounds(cls, type_str, value): 'pass', 'def', 'push', 'dup', 'swap', 'send', 'call', 'selfdestruct', 'assert', 'stop', 'throw', 'raise', 'init', '_init_', '___init___', '____init____', - 'true', 'false', 'self', 'this', 'continue', + 'true', 'false', 'self', 'this', 'continue', 'none', 'clear', 'ether', 'wei', 'finney', 'szabo', 'shannon', 'lovelace', 'ada', 'babbage', 'gwei', 'kwei', 'mwei', 'twei', 'pwei', 'contract', 'units', 'zero_address', 'empty_bytes32' 'max_int128', 'min_int128', 'max_decimal', 'min_decimal', 'max_uint256', # constants