Repository: vyperlang/vyper

Issue:
2-clause if statement must have a zerovalent body error
### Version Information

* vyper Version: 0.1.0b5
* OS: win
* Python Version (python --version): 3.7.0
* Environment (output of `pip freeze`):

```
attrdict==2.0.0
base58==1.0.2
certifi==2018.11.29
chardet==3.0.4
codecov==2.0.15
coverage==4.5.2
cytoolz==0.9.0.1
ecdsa==0.13
eth-abi==1.3.0
eth-account==0.3.0
eth-hash==0.2.0
eth-keyfile==0.5.1
eth-keys==0.2.0b3
eth-rlp==0.1.2
eth-typing==2.0.0
eth-utils==1.4.0
hexbytes==0.1.0
idna==2.7
numpy==1.15.4
pandas==0.23.4
parsimonious==0.8.1
pycryptodome==3.7.2
python-dateutil==2.7.5
pytz==2018.7
requests==2.20.1
rlp==1.0.3
setuptools-scm==3.1.0
six==1.11.0
toolz==0.9.0
tronapi==3.0.2
urllib3==1.24.1
vyper==0.1.0b5
```

### What's your issue about?

Script returns cryptic "zerovalent" error when there is a function call after an if statement. Adding a pass statement after the function call makes it go away. Likewise, replacing the function call with the body of the function call makes it go away.

Stripped down example:
```
@private
def foo():
    pass

@public
def start():
    if True:
        self.foo()
        # pass  # uncomment to successfully compile
```

Error output:
```
Error compiling:  error_reproduce.vy
Exception: 2-clause if statement must have a zerovalent body: [seq,
  /* Internal Call: foo */
  [seq_unchecked,
    [add, pc, 6],
    [goto, priv_3264763256],
    jumpdest,
    0]]
```

Compiled using `vyper error_reproduce.vy`



Discussion:
Starting on this.

Resolving patch:
diff --git a/vyper/parser/self_call.py b/vyper/parser/self_call.py
--- a/vyper/parser/self_call.py
+++ b/vyper/parser/self_call.py
@@ -171,8 +171,21 @@ def call_self_private(stmt_expr, context, sig):
                     typ=None, annotation='dynamic unpacker', pos=getpos(stmt_expr))
                 pop_return_values.append(o)
 
+    call_body = (
+        ['seq_unchecked'] +
+        pre_init +
+        push_local_vars +
+        push_args +
+        jump_to_func +
+        pop_return_values +
+        pop_local_vars +
+        [returner]
+    )
+    # If we have no return, we need to pop off
+    pop_returner_call_body = ['pop', call_body] if sig.output_type is None else call_body
+
     o = LLLnode.from_list(
-        ['seq_unchecked'] + pre_init + push_local_vars + push_args + jump_to_func + pop_return_values + pop_local_vars + [returner],
+        pop_returner_call_body,
         typ=sig.output_type, location='memory', pos=getpos(stmt_expr), annotation='Internal Call: %s' % method_name,
         add_gas_estimate=sig.gas
     )


Test patch:
diff --git a/tests/parser/features/decorators/test_private.py b/tests/parser/features/decorators/test_private.py
--- a/tests/parser/features/decorators/test_private.py
+++ b/tests/parser/features/decorators/test_private.py
@@ -503,3 +503,26 @@ def test(z: int128) -> bool:
     assert c.test(-1) is True
     assert c.test(0) is True
     assert c.test(1) is False
+
+
+def test_private_call_expr(get_contract):
+    code = """
+test: public(bool)
+
+
+@private
+def foo():
+    self.test = True
+
+
+@public
+def start():
+    if True:
+        self.foo()
+    """
+
+    c = get_contract(code)
+
+    assert c.test() is False
+    c.start(transact={})
+    assert c.test() is True