Repository: vyperlang/vyper

Issue:
Fix deleting a value from a map.
### What's your issue about?

It seems that `del` keyword doesn't work for a map.
```
E           vyper.exceptions.StructureException: line 14: Unsupported statement type
E               del self.big_storage[key]
E           ----^
```

Example test case:
```
def test_map_delete(get_contract):
    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(code)

    assert c.get("test") == b""
    assert c.set("test", "value")
    assert c.get("test") == b"value"
    c.delete("test")
    assert c.get("test") == b""
```

This will be done



### How can it be fixed?

Fixed by using `SSTORE 0x0`on the sha32'ed key value.

#### Cute Animal Picture
![](http://2.bp.blogspot.com/-l8i1ohnuksM/T8ZZ8Yl4L5I/AAAAAAAAMVA/X7IXIWmxZ4s/s640/cute-baby-animals-011.jpg)


Discussion:
I think this is actually good syntax for those coming from Python :+1:

Resolving patch:
diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py
--- a/vyper/parser/stmt.py
+++ b/vyper/parser/stmt.py
@@ -55,6 +55,7 @@ def __init__(self, stmt, context):
             ast.Break: self.parse_break,
             ast.Continue: self.parse_continue,
             ast.Return: self.parse_return,
+            ast.Delete: self.parse_delete
         }
         stmt_type = self.stmt.__class__
         if stmt_type in self.stmt_table:
@@ -62,7 +63,7 @@ def __init__(self, stmt, context):
         elif isinstance(stmt, ast.Name) and stmt.id == "throw":
             self.lll_node = LLLnode.from_list(['assert', 0], typ=None, pos=getpos(stmt))
         else:
-            raise StructureException("Unsupported statement type", stmt)
+            raise StructureException("Unsupported statement type: %s" % type(stmt), stmt)
 
     def expr(self):
         return Stmt(self.stmt.value, self.context).lll_node
@@ -492,6 +493,18 @@ def increment_dynamic_offset(dynamic_spot):
         else:
             raise TypeMismatchException("Can only return base type!", self.stmt)
 
+    def parse_delete(self):
+        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 LLLnode.from_list(['seq', ['sstore', target_lll, 0]], typ=None)
+
+        raise StructureException("Deleting type not supported.", 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.
             raise_exception = False


Test patch:
diff --git a/tests/parser/exceptions/test_structure_exception.py b/tests/parser/exceptions/test_structure_exception.py
--- a/tests/parser/exceptions/test_structure_exception.py
+++ b/tests/parser/exceptions/test_structure_exception.py
@@ -165,6 +165,18 @@ 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
new file mode 100644
--- /dev/null
+++ b/tests/parser/features/test_map_delete.py
@@ -0,0 +1,52 @@
+
+
+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("test") == b'\x00' * 32
+    c.set("test", "value")
+    assert c.get("test")[:5] == b"value"
+    c.delete("test")
+    assert c.get("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("test1", "test2") == b'\x00' * 32
+    c.set("test1", "test2", "value")
+    assert c.get("test1", "test2")[:5] == b"value"
+    c.delete("test1", "test2")
+    assert c.get("test1", "test2") == b'\x00' * 32