From 2a2643115204c7c82ad974542522b2da3e5c0262 Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Wed, 28 Nov 2018 11:42:18 -0700 Subject: [PATCH 1/8] Disallow assignment to None Fixed test name Basic implementation of reset() functionality Catch use of None at pre-parser stage Reset from both memory and storage Removed unnecessary usage of None in other tests and added more testing for usage of None Reworked approach to implementing reset function, updated tests Refactored, finished testing suite Make the linter happy Disallow use of None at a syntax level, implement build-in reset() function --- tests/parser/features/test_map_delete.py | 2 +- tests/parser/functions/test_reset.py | 297 +++++++++++++++++++++ tests/parser/globals/test_setters.py | 12 +- tests/parser/integration/test_crowdfund.py | 2 +- tests/parser/syntax/test_maps.py | 5 +- tests/parser/syntax/test_no_none.py | 97 +++++++ tests/parser/types/test_bytes.py | 4 +- vyper/functions/functions.py | 6 + vyper/parser/pre_parser.py | 8 +- vyper/parser/stmt.py | 21 +- 10 files changed, 440 insertions(+), 14 deletions(-) create mode 100644 tests/parser/functions/test_reset.py create mode 100644 tests/parser/syntax/test_no_none.py diff --git a/tests/parser/features/test_map_delete.py b/tests/parser/features/test_map_delete.py index 8ec7dc50be..c720c011be 100644 --- a/tests/parser/features/test_map_delete.py +++ b/tests/parser/features/test_map_delete.py @@ -73,7 +73,7 @@ def get() -> (int128, int128): @public def delete(): - self.structmap[123] = None + del self.structmap[123] """ c = get_contract_with_gas_estimation(code) diff --git a/tests/parser/functions/test_reset.py b/tests/parser/functions/test_reset.py new file mode 100644 index 0000000000..8f89f049bb --- /dev/null +++ b/tests/parser/functions/test_reset.py @@ -0,0 +1,297 @@ + +def test_reset_basic_type(get_contract_with_gas_estimation): + contracts = [ + """ +foobar: int128 + +@public +def foo(): + self.foobar = 1 + bar: int128 = 1 + + reset(self.foobar) + reset(bar) + + assert self.foobar == 0 + assert bar == 0 + """, + """ +foobar: uint256 + +@public +def foo(): + self.foobar = 1 + bar: uint256 = 1 + + reset(self.foobar) + reset(bar) + + assert self.foobar == 0 + assert bar == 0 + """, + """ +foobar: bool + +@public +def foo(): + self.foobar = True + bar: bool = True + + reset(self.foobar) + reset(bar) + + assert self.foobar == False + assert bar == False + """, + """ +foobar: decimal + +@public +def foo(): + self.foobar = 1.0 + bar: decimal = 1.0 + + reset(self.foobar) + reset(bar) + + assert self.foobar == 0.0 + assert bar == 0.0 + """, + """ +foobar: bytes32 + +@public +def foo(): + self.foobar = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + bar: bytes32 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + reset(self.foobar) + reset(bar) + + assert self.foobar == 0x0000000000000000000000000000000000000000000000000000000000000000 + assert bar == 0x0000000000000000000000000000000000000000000000000000000000000000 + """, + """ +foobar: address + +@public +def foo(): + self.foobar = msg.sender + bar: address = msg.sender + + reset(self.foobar) + reset(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_reset_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] + + reset(self.foobar) + reset(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] + + reset(self.foobar) + reset(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] + + reset(self.foobar) + reset(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] + + reset(self.foobar) + reset(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 + ] + + reset(self.foobar) + reset(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] + + reset(self.foobar) + reset(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_reset_bytes(get_contract_with_gas_estimation): + code = """ +foobar: bytes[5] + +@public +def foo() -> (bytes[5], bytes[5]): + self.foobar = 'Hello' + bar: bytes[5] = 'World' + + reset(self.foobar) + reset(bar) + + return (self.foobar, bar) + """ + + c = get_contract_with_gas_estimation(code) + a, b = c.foo() + assert a == b'' + assert b == b'' + + +def test_reset_struct(get_contract_with_gas_estimation): + code = """ +foobar: { + a: int128, + b: uint256, + c: bool, + d: decimal, + e: bytes32, + f: address +} + +@public +def foo(): + self.foobar = { + a: 1, + b: 2, + c: True, + d: 3.0, + e: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + f: msg.sender + } + bar: { + a: int128, + b: uint256, + c: bool, + d: decimal, + e: bytes32, + f: address + } = { + a: 1, + b: 2, + c: True, + d: 3.0, + e: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + f: msg.sender + } + + reset(self.foobar) + reset(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() diff --git a/tests/parser/globals/test_setters.py b/tests/parser/globals/test_setters.py index 4fc2b96a3d..7a05b2bce9 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 + reset(self.dog) return(self.dog[0] + self.dog[1] * 10 + self.dog[2] * 100) @public def hop() -> int128: - self.bar[1] = None + reset(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 + reset(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 + reset(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 @@ -175,8 +175,8 @@ def fop() -> int128: @public def foq() -> int128: popp: {a: {c: int128}[3], b:int128} = {a: [{c: 1}, {c: 2}, {c: 3}], b: 4} - popp.a[0] = None - popp.a[2] = None + reset(popp.a[0]) + reset(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 9c3533f3b4..84ee2a8506 100644 --- a/tests/parser/integration/test_crowdfund.py +++ b/tests/parser/integration/test_crowdfund.py @@ -144,7 +144,7 @@ def refund(): self.refundIndex = self.nextFunderIndex return send(self.funders[i].sender, self.funders[i].value) - self.funders[i] = None + self.funders[i] = {sender: ZERO_ADDRESS, value: 0} self.refundIndex = ind + 30 """ diff --git a/tests/parser/syntax/test_maps.py b/tests/parser/syntax/test_maps.py index 052f3c8264..6f68e8d0c3 100644 --- a/tests/parser/syntax/test_maps.py +++ b/tests/parser/syntax/test_maps.py @@ -73,7 +73,7 @@ def foo(): nom: {a: {c: int128}[int128], b: int128} @public def foo(): - self.nom = None + reset(self.nom) """, """ nom: {a: {c: int128}[int128], b: int128} @@ -172,7 +172,8 @@ def foo(): nom: {c: int128}[3] @public def foo(): - self.mom = {a: None, b: 5} + empty: {c: int128}[3] + self.mom = {a: empty, b: 5} """, """ mom: {a: {c: int128}[3], b: int128} diff --git a/tests/parser/syntax/test_no_none.py b/tests/parser/syntax/test_no_none.py new file mode 100644 index 0000000000..47117a0e05 --- /dev/null +++ b/tests/parser/syntax/test_no_none.py @@ -0,0 +1,97 @@ +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 + """ + ] + + 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 == 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 + ) diff --git a/tests/parser/types/test_bytes.py b/tests/parser/types/test_bytes.py index 6bd87442fe..653662d967 100644 --- a/tests/parser/types/test_bytes.py +++ b/tests/parser/types/test_bytes.py @@ -128,13 +128,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 fef125e31f..c428af2b54 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -5,6 +5,7 @@ InvalidLiteralException, StructureException, TypeMismatchException, + ParserException, ) from .signature import ( signature, @@ -682,6 +683,10 @@ def _can_compare_with_uint256(operand): return LLLnode.from_list(['with', '_l', left, ['with', '_r', right, o]], typ=otyp, pos=getpos(expr)) +def _reset(): + raise ParserException("This function should never be called! `reset()` is currently handled differently than other functions as it self modifies its input argument statement. Please see `_reset()` in `stmt.py`") + + dispatch_table = { 'floor': floor, 'ceil': ceil, @@ -714,6 +719,7 @@ def _can_compare_with_uint256(operand): } stmt_dispatch_table = { + 'reset': _reset, 'send': send, 'selfdestruct': selfdestruct, 'raw_call': raw_call, diff --git a/vyper/parser/pre_parser.py b/vyper/parser/pre_parser.py index 938df4ffd7..fba7f24018 100644 --- a/vyper/parser/pre_parser.py +++ b/vyper/parser/pre_parser.py @@ -9,7 +9,10 @@ tokenize, untokenize, ) -from vyper.exceptions import StructureException +from vyper.exceptions import ( + StructureException, + InvalidLiteralException, +) from vyper import __version__ @@ -49,6 +52,9 @@ def pre_parse(code): # Prevent semi-colon line statements. elif (token.type, token.string) == (OP, ";"): raise StructureException("Semi-colon statements not allowed.", token.start) + # Prevent use of None literal + elif (token.type, token.string) == (NAME, "None"): + raise InvalidLiteralException('None is not allowed as a literal, use a default value or built-in `reset()`.', token.start) result.append(token) except TokenError as e: diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py index f17c260e28..667ebd79a3 100644 --- a/vyper/parser/stmt.py +++ b/vyper/parser/stmt.py @@ -168,6 +168,7 @@ def ann_assign(self): 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 @@ -227,6 +228,21 @@ def parse_if(self): self.context.end_blockscope(block_scope_id) return o + def _reset(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 + + # Generate LLL node to set to zero + target = self.get_target(self.stmt.args[0]) + 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, @@ -235,7 +251,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 == 'reset': + return self._reset() + 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: From 6753ce6aae60faa08d53e69c9406ecfa96000253 Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Fri, 30 Nov 2018 09:10:53 -0700 Subject: [PATCH 2/8] Renamed reset() to clear(), added description to built-in functions section of the docs, added more tests --- docs/built-in-functions.rst | 14 +++ .../{test_reset.py => test_clear.py} | 100 ++++++++++++------ tests/parser/globals/test_setters.py | 12 +-- tests/parser/syntax/test_maps.py | 2 +- vyper/functions/functions.py | 6 +- vyper/parser/pre_parser.py | 2 +- vyper/parser/stmt.py | 10 +- 7 files changed, 99 insertions(+), 47 deletions(-) rename tests/parser/functions/{test_reset.py => test_clear.py} (80%) diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst index 80e634503a..e9f2e65362 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/tests/parser/functions/test_reset.py b/tests/parser/functions/test_clear.py similarity index 80% rename from tests/parser/functions/test_reset.py rename to tests/parser/functions/test_clear.py index 8f89f049bb..c27f90228b 100644 --- a/tests/parser/functions/test_reset.py +++ b/tests/parser/functions/test_clear.py @@ -1,5 +1,5 @@ -def test_reset_basic_type(get_contract_with_gas_estimation): +def test_clear_basic_type(get_contract_with_gas_estimation): contracts = [ """ foobar: int128 @@ -9,8 +9,8 @@ def foo(): self.foobar = 1 bar: int128 = 1 - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar == 0 assert bar == 0 @@ -23,8 +23,8 @@ def foo(): self.foobar = 1 bar: uint256 = 1 - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar == 0 assert bar == 0 @@ -37,8 +37,8 @@ def foo(): self.foobar = True bar: bool = True - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar == False assert bar == False @@ -51,8 +51,8 @@ def foo(): self.foobar = 1.0 bar: decimal = 1.0 - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar == 0.0 assert bar == 0.0 @@ -65,8 +65,8 @@ def foo(): self.foobar = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF bar: bytes32 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar == 0x0000000000000000000000000000000000000000000000000000000000000000 assert bar == 0x0000000000000000000000000000000000000000000000000000000000000000 @@ -79,8 +79,8 @@ def foo(): self.foobar = msg.sender bar: address = msg.sender - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar == ZERO_ADDRESS assert bar == ZERO_ADDRESS @@ -92,7 +92,7 @@ def foo(): c.foo() -def test_reset_basic_type_lists(get_contract_with_gas_estimation): +def test_clear_basic_type_lists(get_contract_with_gas_estimation): contracts = [ """ foobar: int128[3] @@ -102,8 +102,8 @@ def foo(): self.foobar = [1, 2, 3] bar: int128[3] = [1, 2, 3] - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar[0] == 0 assert self.foobar[1] == 0 @@ -120,8 +120,8 @@ def foo(): self.foobar = [1, 2, 3] bar: uint256[3] = [1, 2, 3] - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar[0] == 0 assert self.foobar[1] == 0 @@ -138,8 +138,8 @@ def foo(): self.foobar = [True, True, True] bar: bool[3] = [True, True, True] - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar[0] == False assert self.foobar[1] == False @@ -156,8 +156,8 @@ def foo(): self.foobar = [1.0, 2.0, 3.0] bar: decimal[3] = [1.0, 2.0, 3.0] - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar[0] == 0.0 assert self.foobar[1] == 0.0 @@ -182,8 +182,8 @@ def foo(): 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ] - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar[0] == 0x0000000000000000000000000000000000000000000000000000000000000000 assert self.foobar[1] == 0x0000000000000000000000000000000000000000000000000000000000000000 @@ -200,8 +200,8 @@ def foo(): self.foobar = [msg.sender, msg.sender, msg.sender] bar: address[3] = [msg.sender, msg.sender, msg.sender] - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar[0] == ZERO_ADDRESS assert self.foobar[1] == ZERO_ADDRESS @@ -217,7 +217,43 @@ def foo(): c.foo() -def test_reset_bytes(get_contract_with_gas_estimation): +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] @@ -226,8 +262,8 @@ def foo() -> (bytes[5], bytes[5]): self.foobar = 'Hello' bar: bytes[5] = 'World' - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) return (self.foobar, bar) """ @@ -238,7 +274,7 @@ def foo() -> (bytes[5], bytes[5]): assert b == b'' -def test_reset_struct(get_contract_with_gas_estimation): +def test_clear_struct(get_contract_with_gas_estimation): code = """ foobar: { a: int128, @@ -275,8 +311,8 @@ def foo(): f: msg.sender } - reset(self.foobar) - reset(bar) + clear(self.foobar) + clear(bar) assert self.foobar.a == 0 assert self.foobar.b == 0 diff --git a/tests/parser/globals/test_setters.py b/tests/parser/globals/test_setters.py index 7a05b2bce9..fee8929683 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: - reset(self.dog) + clear(self.dog) return(self.dog[0] + self.dog[1] * 10 + self.dog[2] * 100) @public def hop() -> int128: - reset(self.bar[1]) + 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] - reset(goo) + 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] - reset(gar[1]) + 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 @@ -175,8 +175,8 @@ def fop() -> int128: @public def foq() -> int128: popp: {a: {c: int128}[3], b:int128} = {a: [{c: 1}, {c: 2}, {c: 3}], b: 4} - reset(popp.a[0]) - reset(popp.a[2]) + 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/syntax/test_maps.py b/tests/parser/syntax/test_maps.py index 6f68e8d0c3..e1642ded18 100644 --- a/tests/parser/syntax/test_maps.py +++ b/tests/parser/syntax/test_maps.py @@ -73,7 +73,7 @@ def foo(): nom: {a: {c: int128}[int128], b: int128} @public def foo(): - reset(self.nom) + clear(self.nom) """, """ nom: {a: {c: int128}[int128], b: int128} diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py index c428af2b54..18782fdef0 100644 --- a/vyper/functions/functions.py +++ b/vyper/functions/functions.py @@ -683,8 +683,8 @@ def _can_compare_with_uint256(operand): return LLLnode.from_list(['with', '_l', left, ['with', '_r', right, o]], typ=otyp, pos=getpos(expr)) -def _reset(): - raise ParserException("This function should never be called! `reset()` is currently handled differently than other functions as it self modifies its input argument statement. Please see `_reset()` in `stmt.py`") +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 = { @@ -719,7 +719,7 @@ def _reset(): } stmt_dispatch_table = { - 'reset': _reset, + 'clear': _clear, 'send': send, 'selfdestruct': selfdestruct, 'raw_call': raw_call, diff --git a/vyper/parser/pre_parser.py b/vyper/parser/pre_parser.py index fba7f24018..71eb766bdd 100644 --- a/vyper/parser/pre_parser.py +++ b/vyper/parser/pre_parser.py @@ -54,7 +54,7 @@ def pre_parse(code): raise StructureException("Semi-colon statements not allowed.", token.start) # Prevent use of None literal elif (token.type, token.string) == (NAME, "None"): - raise InvalidLiteralException('None is not allowed as a literal, use a default value or built-in `reset()`.', token.start) + raise InvalidLiteralException('None is not allowed as a literal, use a default value or built-in `clear()`.', token.start) result.append(token) except TokenError as e: diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py index 667ebd79a3..c34cf157e0 100644 --- a/vyper/parser/stmt.py +++ b/vyper/parser/stmt.py @@ -228,15 +228,17 @@ def parse_if(self): self.context.end_blockscope(block_scope_id) return o - def _reset(self): + 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 - # Generate LLL node to set to zero + # 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) @@ -251,8 +253,8 @@ def call(self): ) if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: - if self.stmt.func.id == 'reset': - return self._reset() + 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: From 34abab030ef944b603e4b06099b9a0abadc74b85 Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Mon, 3 Dec 2018 08:46:43 -0700 Subject: [PATCH 3/8] Remove usage of del function, refactor tests --- .../exceptions/test_structure_exception.py | 12 --- tests/parser/features/test_map_delete.py | 85 ------------------- tests/parser/functions/test_clear.py | 85 +++++++++++++++++++ vyper/parser/stmt.py | 15 +--- 4 files changed, 86 insertions(+), 111 deletions(-) delete mode 100644 tests/parser/features/test_map_delete.py diff --git a/tests/parser/exceptions/test_structure_exception.py b/tests/parser/exceptions/test_structure_exception.py index 26f9f8f097..d9e36d7f77 100644 --- a/tests/parser/exceptions/test_structure_exception.py +++ b/tests/parser/exceptions/test_structure_exception.py @@ -165,18 +165,6 @@ def foo() -> int128: q:int128 = 111 def foo() -> int128: return self.q - """, - """ -b: bytes32[int128] -@public -def foo(): - del self.b[0], self.b[1] - """, - """ -@public -def foo(): - b: int128 - del b """ ] diff --git a/tests/parser/features/test_map_delete.py b/tests/parser/features/test_map_delete.py deleted file mode 100644 index c720c011be..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: 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: bytes32[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 = """ -structmap: { - a: int128, - b: int128 -}[int128] - - -@public -def set(): - self.structmap[123] = { - a: 333, - b: 444 - } - -@public -def get() -> (int128, int128): - return self.structmap[123].a, self.structmap[123].b - -@public -def delete(): - del 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/functions/test_clear.py b/tests/parser/functions/test_clear.py index c27f90228b..6dc668f476 100644 --- a/tests/parser/functions/test_clear.py +++ b/tests/parser/functions/test_clear.py @@ -331,3 +331,88 @@ def foo(): c = get_contract_with_gas_estimation(code) c.foo() + + +def test_map_clear(get_contract_with_gas_estimation): + code = """ +big_storage: 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: bytes32[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 = """ +structmap: { + a: int128, + b: int128 +}[int128] + + +@public +def set(): + self.structmap[123] = { + 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/vyper/parser/stmt.py b/vyper/parser/stmt.py index c34cf157e0..06f54b9b4b 100644 --- a/vyper/parser/stmt.py +++ b/vyper/parser/stmt.py @@ -28,7 +28,6 @@ ListType, TupleType, StructType, - NullType ) from vyper.types import ( get_size_of_type, @@ -704,19 +703,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. From e453b01c662e86ddbf20efeda624cd3afa2fd1ff Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Tue, 4 Dec 2018 10:33:30 -0700 Subject: [PATCH 4/8] Make None a resevered word, throw errors in statements evaluation and expression evaluation rather than in pre-parser, more tests --- tests/parser/syntax/test_no_none.py | 30 +++++++++++++++++++++++++++++ vyper/parser/expr.py | 3 +++ vyper/parser/pre_parser.py | 3 --- vyper/parser/stmt.py | 5 +++++ vyper/utils.py | 2 +- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/tests/parser/syntax/test_no_none.py b/tests/parser/syntax/test_no_none.py index 47117a0e05..fee6447a22 100644 --- a/tests/parser/syntax/test_no_none.py +++ b/tests/parser/syntax/test_no_none.py @@ -40,6 +40,36 @@ def foo(): 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 """ ] diff --git a/vyper/parser/expr.py b/vyper/parser/expr.py index 052aa64311..912cc7eb4d 100644 --- a/vyper/parser/expr.py +++ b/vyper/parser/expr.py @@ -509,6 +509,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/pre_parser.py b/vyper/parser/pre_parser.py index 71eb766bdd..5d95fd9ebe 100644 --- a/vyper/parser/pre_parser.py +++ b/vyper/parser/pre_parser.py @@ -52,9 +52,6 @@ def pre_parse(code): # Prevent semi-colon line statements. elif (token.type, token.string) == (OP, ";"): raise StructureException("Semi-colon statements not allowed.", token.start) - # Prevent use of None literal - elif (token.type, token.string) == (NAME, "None"): - raise InvalidLiteralException('None is not allowed as a literal, use a default value or built-in `clear()`.', token.start) result.append(token) except TokenError as e: diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py index c69717d52e..627577579b 100644 --- a/vyper/parser/stmt.py +++ b/vyper/parser/stmt.py @@ -28,6 +28,7 @@ ListType, TupleType, StructType, + NullType, ) from vyper.types import ( get_size_of_type, @@ -159,6 +160,8 @@ 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 + 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) @@ -177,6 +180,8 @@ def assign(self): raise StructureException("Assignment statement must have one target", self.stmt) self.context.set_in_assignment(True) sub = Expr(self.stmt.value, self.context).lll_node + 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) diff --git a/vyper/utils.py b/vyper/utils.py index 37205f96ea..ffd14afad3 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', '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 From e8b4a4fcc2ca42f3998ba8ced9f8e721666cf220 Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Tue, 4 Dec 2018 14:30:41 -0700 Subject: [PATCH 5/8] Make the linter happy --- vyper/parser/pre_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/vyper/parser/pre_parser.py b/vyper/parser/pre_parser.py index 5d95fd9ebe..b11a314d46 100644 --- a/vyper/parser/pre_parser.py +++ b/vyper/parser/pre_parser.py @@ -11,7 +11,6 @@ ) from vyper.exceptions import ( StructureException, - InvalidLiteralException, ) from vyper import __version__ From 7e76e8947961bd4b3df44a98a38c261e1e7485b0 Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Wed, 5 Dec 2018 23:27:00 -0700 Subject: [PATCH 6/8] Update clear() tests to use new struct syntax --- tests/parser/functions/test_clear.py | 41 ++++++++++++---------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/tests/parser/functions/test_clear.py b/tests/parser/functions/test_clear.py index d27b0facfe..0306b764a9 100644 --- a/tests/parser/functions/test_clear.py +++ b/tests/parser/functions/test_clear.py @@ -276,40 +276,34 @@ def foo() -> (bytes[5], bytes[5]): def test_clear_struct(get_contract_with_gas_estimation): code = """ -foobar: { - a: int128, - b: uint256, - c: bool, - d: decimal, - e: bytes32, +struct FOOBAR: + a: int128 + b: uint256 + c: bool + d: decimal + e: bytes32 f: address -} + +foobar: FOOBAR @public def foo(): - self.foobar = { + self.foobar = FOOBAR({ a: 1, b: 2, c: True, d: 3.0, e: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, f: msg.sender - } - bar: { - a: int128, - b: uint256, - c: bool, - d: decimal, - e: bytes32, - f: address - } = { + }) + bar: FOOBAR = FOOBAR({ a: 1, b: 2, c: True, d: 3.0, e: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, f: msg.sender - } + }) clear(self.foobar) clear(bar) @@ -387,17 +381,18 @@ def delete(key1: bytes32, key2: bytes32): def test_map_clear_struct(get_contract_with_gas_estimation): code = """ -structmap: map(int128, { - a: int128, +struct X: + a: int128 b: int128 -}) + +structmap: map(int128, X) @public def set(): - self.structmap[123] = { + self.structmap[123] = X({ a: 333, b: 444 - } + }) @public def get() -> (int128, int128): From d974624d4a6a08f133ba0f39a2e44aa659d1799c Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Tue, 11 Dec 2018 18:11:01 -0700 Subject: [PATCH 7/8] Fixed upstream test cases using None, disallow using None in struct assignments, added aditional tests --- tests/parser/integration/test_crowdfund.py | 2 +- tests/parser/syntax/test_no_none.py | 94 ++++++++++++++++++++++ tests/parser/syntax/test_structs.py | 4 +- vyper/parser/parser_utils.py | 3 + 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/tests/parser/integration/test_crowdfund.py b/tests/parser/integration/test_crowdfund.py index f49f448dd3..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] = {sender: ZERO_ADDRESS, value: 0} + 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 index fee6447a22..6d72d8c11a 100644 --- a/tests/parser/syntax/test_no_none.py +++ b/tests/parser/syntax/test_no_none.py @@ -84,6 +84,53 @@ 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 @@ -125,3 +172,50 @@ def foo(): 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/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) From e72fae4f2659e7ed2505830745cf496bdf6ec71f Mon Sep 17 00:00:00 2001 From: Jacob Rockland Date: Wed, 12 Dec 2018 10:16:42 -0700 Subject: [PATCH 8/8] Made clear a reserved word and added a entry to the changelog --- docs/index.rst | 1 + vyper/utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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/vyper/utils.py b/vyper/utils.py index eeae7f72eb..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', 'none', + '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