Repository: vyperlang/vyper

Issue:
`not` operation can be applied to values other than bools
## Simple Summary
`not` operation should be applied to only bools for simplicity and security.

## Abstract
Now `not` operation can be applied to not only `bool` but also other types like `int128`, `uint256`, `address`, `bytes`.
(The code below can be compiled in [vyper.online](https://vyper.online/))

![image](https://user-images.githubusercontent.com/22876645/44295085-d97aab00-a2dc-11e8-8bc6-4cf87664438d.png)


However, `not` operation should be applied to only `bool`.

## Motivation
I think "bool operations can only be applied to bools" is a simple design. In addition, it can prevent unexpected behavior. For example, when developers(like me) might make a mistake and code like `not n in [1, 2, 3]` but she means `not(n in [1, 2, 3])` actually, the first code is compiled and have different semantics.

## Specification
Compiler doesn't allow `not` operation used for the variables whose type is other than `bool`.

## Backwards Compatibility
The codes in which `not` is used for the variables whose type is other than `bool` will not be compiled anymore.


Discussion:
Looks like a bug. So does the result is basically a check of zero-ness?
I support disallowing that.
Agree this seems like a bug. I would support reducing scope from a VIP to a normal issue.
> I would support reducing scope from a VIP to a normal issue.

I see. I modified the title.
Starting on this.

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -29,11 +29,6 @@
         'pytest-cov',
         'eth-tester[py-evm]==0.1.0b31',
         'web3==4.4.1',
-        'py-evm==0.2.0a30',
-        # 'py-evm==0.2.0a26',
-        # 'py-evm==0.2.0a18',
-        # 'py-evm==0.2.0-alpha.26',
-        # 'eth-tester==0.1.0-beta.29',
     ],
     scripts=[
         'bin/vyper',
diff --git a/vyper/optimizer.py b/vyper/optimizer.py
--- a/vyper/optimizer.py
+++ b/vyper/optimizer.py
@@ -93,6 +93,9 @@ def optimize(node):
             return LLLnode(argz[0].value, [], node.typ, node.location, node.pos, node.annotation, add_gas_estimate=node.add_gas_estimate)
         else:
             raise Exception("Clamp always fails")
+    # [eq, x, 0] is the same as [iszero, x].
+    elif node.value == 'eq' and int_at(argz, 1) and argz[1].value == 0:
+        return LLLnode('iszero', [argz[0]], node.typ, node.location, node.pos, node.annotation, add_gas_estimate=node.add_gas_estimate)
     # Turns out this is actually not such a good optimization after all
     elif node.value == "with" and int_at(argz, 1) and not search_for_set(argz[2], argz[0].value) and False:
         o = replace_with_value(argz[2], argz[0].value, argz[1].value)
diff --git a/vyper/parser/expr.py b/vyper/parser/expr.py
--- a/vyper/parser/expr.py
+++ b/vyper/parser/expr.py
@@ -578,9 +578,10 @@ def boolean_operations(self):
     def unary_operations(self):
         operand = Expr.parse_value_expr(self.expr.operand, self.context)
         if isinstance(self.expr.op, ast.Not):
-            # Note that in the case of bool, num, address, decimal, uint256 AND bytes32,
-            # a zero entry represents false, all others represent true
-            return LLLnode.from_list(["iszero", operand], typ='bool', pos=getpos(self.expr))
+            if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool':
+                return LLLnode.from_list(["iszero", operand], typ='bool', pos=getpos(self.expr))
+            else:
+                raise TypeMismatchException("Only bool is supported for not operation, %r supplied." % operand.typ, self.expr)
         elif isinstance(self.expr.op, ast.USub):
             if not is_numeric_type(operand.typ):
                 raise TypeMismatchException("Unsupported type for negation: %r" % operand.typ, operand)


Test patch:
diff --git a/tests/parser/integration/test_escrow.py b/tests/parser/integration/test_escrow.py
--- a/tests/parser/integration/test_escrow.py
+++ b/tests/parser/integration/test_escrow.py
@@ -9,7 +9,7 @@ def test_arbitration_code(w3, get_contract_with_gas_estimation, assert_tx_failed
 
 @public
 def setup(_seller: address, _arbitrator: address):
-    if not self.buyer:
+    if self.buyer == ZERO_ADDRESS:
         self.buyer = msg.sender
         self.seller = _seller
         self.arbitrator = _arbitrator
@@ -43,7 +43,7 @@ def test_arbitration_code_with_init(w3, assert_tx_failed, get_contract_with_gas_
 @public
 @payable
 def __init__(_seller: address, _arbitrator: address):
-    if not self.buyer:
+    if self.buyer == ZERO_ADDRESS:
         self.buyer = msg.sender
         self.seller = _seller
         self.arbitrator = _arbitrator
diff --git a/tests/parser/syntax/test_bool.py b/tests/parser/syntax/test_bool.py
--- a/tests/parser/syntax/test_bool.py
+++ b/tests/parser/syntax/test_bool.py
@@ -43,6 +43,29 @@ def foo() -> bool:
 def foo() -> bool:
     a: address
     return a == 1
+    """,
+    """
+@public
+def foo(a: address) -> bool:
+    return not a
+    """,
+    """
+@public
+def foo() -> bool:
+    b: int128
+    return not b
+    """,
+    """
+@public
+def foo() -> bool:
+    b: uint256
+    return not b
+    """,
+    """
+@public
+def foo() -> bool:
+    b: uint256
+    return not b
     """
 ]
 
@@ -106,6 +129,11 @@ def foo() -> bool:
 @public
 def foo() -> bool:
     return 1 <= 1
+    """,
+    """
+@public
+def foo2(a: address) -> bool:
+    return a != ZERO_ADDRESS
     """
 ]