Repository: vyperlang/vyper

Issue:
VIP: Only bools should be used as logical values.
## Preamable
```
VIP: 1000
Title: Only bools should be used as logical values.
Author: Ryuya Nakamura (@nrryuya)
Type: Standard Track
Status: Draft
Created: 18-8-2018
```

## Simple Summary
Only bools should be used as a logical value for simplicity and security.

## Abstract
Now values whose type is not `bool` can used in `assert` and `if`.
(The code below can be compiled in [vyper.online](https://vyper.online/))

![image](https://user-images.githubusercontent.com/22876645/44295095-f9aa6a00-a2dc-11e8-9672-0e051da75002.png)


However, only `bool` values should be used as a logical value.

## Motivation
In Python, syntaxes like `if some_object:` are used to check the existence of `some_object`. However, in Vyper we can code like `if n != 0:` (e.g. n is `int128`) because every types has default value other than `None`. I think the latter is easier to understand and better for auditability.

## Specification
Compiler allow only bools to be used as a logical value.

## Backwards Compatibility
The codes in which values whose type is not `bool` is used as logical value will not be compiled anymore.

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)



Discussion:
This VIP is closely related to #999 but I divided them because they might make discussion of different pros and cons.

Resolving patch:
diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py
--- a/vyper/parser/stmt.py
+++ b/vyper/parser/stmt.py
@@ -153,6 +153,13 @@ def assign(self):
         self.context.set_in_assignment(False)
         return o
 
+    def is_bool_expr(self, test_expr):
+        if not isinstance(test_expr.typ, BaseType):
+            return False
+        if not test_expr.typ.typ == 'bool':
+            return False
+        return True
+
     def parse_if(self):
         from .parser import (
             parse_body,
@@ -167,8 +174,13 @@ def parse_if(self):
 
         block_scope_id = id(self.stmt)
         self.context.start_blockscope(block_scope_id)
+        test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
+
+        if not self.is_bool_expr(test_expr):
+            raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test)
+
         o = LLLnode.from_list(
-            ['if', Expr.parse_value_expr(self.stmt.test, self.context), parse_body(self.stmt.body, self.context)] + add_on,
+            ['if', test_expr, parse_body(self.stmt.body, self.context)] + add_on,
             typ=None, pos=getpos(self.stmt)
         )
         self.context.end_blockscope(block_scope_id)
@@ -246,6 +258,9 @@ def call(self):
             raise StructureException("Unsupported operator: %r" % ast.dump(self.stmt), self.stmt)
 
     def parse_assert(self):
+        test_expr = Expr.parse_value_expr(self.stmt.test, self.context)
+        if not self.is_bool_expr(test_expr):
+            raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test)
         if self.stmt.msg:
             if len(self.stmt.msg.s.strip()) == 0:
                 raise StructureException('Empty reason string not allowed.', self.stmt)
@@ -260,10 +275,10 @@ def parse_assert(self):
                     ['mstore', sig_placeholder, method_id],
                     ['mstore', arg_placeholder, 32],
                     placeholder_bytes,
-                    ['assert_reason', Expr.parse_value_expr(self.stmt.test, self.context), int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32)]]
+                    ['assert_reason', test_expr, int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32)]]
             return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
         else:
-            return LLLnode.from_list(['assert', Expr.parse_value_expr(self.stmt.test, self.context)], typ=None, pos=getpos(self.stmt))
+            return LLLnode.from_list(['assert', test_expr], typ=None, pos=getpos(self.stmt))
 
     def parse_for(self):
         from .parser import (


Test patch:
diff --git a/tests/parser/features/test_conditionals.py b/tests/parser/features/test_conditionals.py
--- a/tests/parser/features/test_conditionals.py
+++ b/tests/parser/features/test_conditionals.py
@@ -5,7 +5,7 @@ def foo(i: bool) -> int128:
     if i:
         return 5
     else:
-        assert 2
+        assert 2 != 0
         return 7
     return 11
     """
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
@@ -66,6 +66,12 @@ def foo() -> bool:
 def foo() -> bool:
     b: uint256
     return not b
+    """,
+    """
+@public
+def test(a: address) -> bool:
+    assert(a)
+    return True
     """
 ]