Repository: vyperlang/vyper

Issue:
raise UNREACHABLE: "vyper.exceptions.CompilerPanic: None is not allowed as IRnode value"
### Version Information

* vyper Version (output of `vyper --version`): 0.3.6+commit.4a2124d
* OS: win10 home, version 21H2
* Python Version (output of `python --version`): 3.10.6

### What's your issue about?
Tried to compile a simple contract for training purposes using the command line in windows 10:
`python -m vyper errors.vy --verbose`

Face with the "vyper.exceptions.CompilerPanic" compile error such that:

```
Error compiling: errors.vy
Traceback (most recent call last):
  File "C:\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Python310\lib\site-packages\vyper\__main__.py", line 13, in <module>
    vyper_compile._parse_cli_args()
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 56, in _parse_cli_args
    return _parse_args(sys.argv[1:])
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 150, in _parse_args
    compiled = compile_files(
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 288, in compile_files
    compiler_data = vyper.compile_codes(
  File "C:\Python310\lib\site-packages\vyper\evm\opcodes.py", line 224, in _wrapper
    return fn(*args, **kwargs)
  File "C:\Python310\lib\site-packages\vyper\compiler\__init__.py", line 137, in compile_codes
    exc_handler(contract_name, exc)
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 184, in exc_handler
    raise exception
  File "C:\Python310\lib\site-packages\vyper\compiler\__init__.py", line 134, in compile_codes
    out[contract_name][output_format] = OUTPUT_FORMATS[output_format](compiler_data)
  File "C:\Python310\lib\site-packages\vyper\compiler\output.py", line 243, in build_bytecode_output
    return f"0x{compiler_data.bytecode.hex()}"
  File "C:\Python310\lib\functools.py", line 981, in __get__
    val = self.func(instance)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 145, in bytecode
    return generate_bytecode(self.assembly, is_runtime=False)
  File "C:\Python310\lib\functools.py", line 981, in __get__
    val = self.func(instance)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 137, in assembly
    return generate_assembly(self.ir_nodes, self.no_optimize)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 122, in ir_nodes
    ir, ir_runtime, sigs = self._ir_output
  File "C:\Python310\lib\functools.py", line 981, in __get__
    val = self.func(instance)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 118, in _ir_output
    return generate_ir_nodes(self.global_ctx, self.no_optimize)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 271, in generate_ir_nodes
    ir_nodes, ir_runtime, function_sigs = module.generate_ir_for_module(global_ctx)
  File "C:\Python310\lib\site-packages\vyper\codegen\module.py", line 209, in generate_ir_for_module
    runtime, internal_functions = _runtime_ir(runtime_functions, all_sigs, global_ctx)
  File "C:\Python310\lib\site-packages\vyper\codegen\module.py", line 145, in _runtime_ir
    func_ir = generate_ir_for_function(func_ast, all_sigs, global_ctx, skip_nonpayable_check)
  File "C:\Python310\lib\site-packages\vyper\codegen\function_definitions\common.py", line 62, in generate_ir_for_function
    o = generate_ir_for_external_function(code, sig, context, skip_nonpayable_check)
  File "C:\Python310\lib\site-packages\vyper\codegen\function_definitions\external_function.py", line 186, in generate_ir_for_external_function
    body += [parse_body(code.body, context, ensure_terminated=True)]
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 424, in parse_body
    ir = parse_stmt(stmt, context)
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 398, in parse_stmt
    return Stmt(stmt, context).ir_node
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 40, in __init__
    self.ir_node = fn()
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 226, in parse_Raise
    return self._assert_reason(None, self.stmt.exc)
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 163, in _assert_reason
    return IRnode.from_list(
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 538, in from_list
    [cls.from_list(o, source_pos=source_pos) for o in obj[1:]],
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 538, in <listcomp>
    [cls.from_list(o, source_pos=source_pos) for o in obj[1:]],
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 523, in from_list
    return cls(
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 144, in __init__
    _check(self.value is not None, "None is not allowed as IRnode value")
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 142, in _check
    raise CompilerPanic(str(err))
vyper.exceptions.CompilerPanic: None is not allowed as IRnode value

This is an unhandled internal compiler error. Please create an issue on Github to notify the developers.
https://github.com/vyperlang/vyper/issues/new?template=bug.md
```

Source code is as follows:

```
# @version ^0.3.6

nums: public(uint256[3])

@external
def __init__():
    self.nums[0]=1
    self.nums[1]=2
    self.nums[2]=3

@external
@pure
def ifElse(i: uint256) -> uint256:
    if i<10:
        return 0
    elif i<20:
        return 1
    else:
        return 2

@external
@view
def forLoop() -> (uint256, uint256, uint256, uint256):
    x: uint256=0
    for i in [1,2,3]:
        x+=i
    y: uint256=0
    for i in self.nums:
        y+=i
    
    z: uint256=0
    for i in range(10):
        z+=1
    
    w: uint256=0
    for i in range(1,10): # 10 is not included in the loop, i=1,2,3,....,8,9
        w=i
    return (x,y,z,w)

@external
@pure
def continueAndBreak() -> (uint256):
    x: uint256=0
    for i in [1,2,3,4,5]:
        if i<3:
            continue
        if i==4:
            break
        x=i
    return x

@external
def blank():
    pass # is used when you want to leave the function empty, to be filled later at the next iteration of code development
```

### How can it be fixed?


Discussion:
@gokcanakalin I can't reproduce this -- the source code you included compiles successfully with `0.3.6+commit.4a2124d0` on my machine

In the traceback you included it says the error is generated at a `raise` statement - do you have some other code you were trying to compile which has a `raise` statement?
> @gokcanakalin I can't reproduce this -- the source code you included compiles successfully with `0.3.6+commit.4a2124d0` on my machine
> 
> In the traceback you included it says the error is generated at a `raise` statement - do you have some other code you were trying to compile which has a `raise` statement?

I have tried again with a simpler contract:

```
# @version ^0.3.6

@external
def unreachable():
    raise UNREACHABLE
```
Here is the result for the cmd:

`python -m vyper test.vy --verbose`

```
Error compiling: test.vy
Traceback (most recent call last):
  File "C:\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Python310\lib\site-packages\vyper\__main__.py", line 13, in <module>
    vyper_compile._parse_cli_args()
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 56, in _parse_cli_args
    return _parse_args(sys.argv[1:])
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 150, in _parse_args
    compiled = compile_files(
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 288, in compile_files
    compiler_data = vyper.compile_codes(
  File "C:\Python310\lib\site-packages\vyper\evm\opcodes.py", line 224, in _wrapper
    return fn(*args, **kwargs)
  File "C:\Python310\lib\site-packages\vyper\compiler\__init__.py", line 137, in compile_codes
    exc_handler(contract_name, exc)
  File "C:\Python310\lib\site-packages\vyper\cli\vyper_compile.py", line 184, in exc_handler
    raise exception
  File "C:\Python310\lib\site-packages\vyper\compiler\__init__.py", line 134, in compile_codes
    out[contract_name][output_format] = OUTPUT_FORMATS[output_format](compiler_data)
  File "C:\Python310\lib\site-packages\vyper\compiler\output.py", line 243, in build_bytecode_output
    return f"0x{compiler_data.bytecode.hex()}"
  File "C:\Python310\lib\functools.py", line 981, in __get__
    val = self.func(instance)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 145, in bytecode
    return generate_bytecode(self.assembly, is_runtime=False)
  File "C:\Python310\lib\functools.py", line 981, in __get__
    val = self.func(instance)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 137, in assembly
    return generate_assembly(self.ir_nodes, self.no_optimize)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 122, in ir_nodes
    ir, ir_runtime, sigs = self._ir_output
  File "C:\Python310\lib\functools.py", line 981, in __get__
    val = self.func(instance)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 118, in _ir_output
    return generate_ir_nodes(self.global_ctx, self.no_optimize)
  File "C:\Python310\lib\site-packages\vyper\compiler\phases.py", line 271, in generate_ir_nodes
    ir_nodes, ir_runtime, function_sigs = module.generate_ir_for_module(global_ctx)
  File "C:\Python310\lib\site-packages\vyper\codegen\module.py", line 209, in generate_ir_for_module
    runtime, internal_functions = _runtime_ir(runtime_functions, all_sigs, global_ctx)
  File "C:\Python310\lib\site-packages\vyper\codegen\module.py", line 145, in _runtime_ir
    func_ir = generate_ir_for_function(func_ast, all_sigs, global_ctx, skip_nonpayable_check)
  File "C:\Python310\lib\site-packages\vyper\codegen\function_definitions\common.py", line 62, in generate_ir_for_function
    o = generate_ir_for_external_function(code, sig, context, skip_nonpayable_check)
  File "C:\Python310\lib\site-packages\vyper\codegen\function_definitions\external_function.py", line 186, in generate_ir_for_external_function
    body += [parse_body(code.body, context, ensure_terminated=True)]
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 424, in parse_body
    ir = parse_stmt(stmt, context)
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 398, in parse_stmt
    return Stmt(stmt, context).ir_node
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 40, in __init__
    self.ir_node = fn()
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 226, in parse_Raise
    return self._assert_reason(None, self.stmt.exc)
  File "C:\Python310\lib\site-packages\vyper\codegen\stmt.py", line 163, in _assert_reason
    return IRnode.from_list(
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 538, in from_list
    [cls.from_list(o, source_pos=source_pos) for o in obj[1:]],
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 538, in <listcomp>
    [cls.from_list(o, source_pos=source_pos) for o in obj[1:]],
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 523, in from_list
    return cls(
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 144, in __init__
    _check(self.value is not None, "None is not allowed as IRnode value")
  File "C:\Python310\lib\site-packages\vyper\codegen\ir_node.py", line 142, in _check
    raise CompilerPanic(str(err))
vyper.exceptions.CompilerPanic: None is not allowed as IRnode value

This is an unhandled internal compiler error. Please create an issue on Github to notify the developers.
https://github.com/vyperlang/vyper/issues/new?template=bug.md
```

Resolving patch:
diff --git a/vyper/codegen/stmt.py b/vyper/codegen/stmt.py
--- a/vyper/codegen/stmt.py
+++ b/vyper/codegen/stmt.py
@@ -159,10 +159,16 @@ def parse_Call(self):
             return external_call.ir_for_external_call(self.stmt, self.context)
 
     def _assert_reason(self, test_expr, msg):
+        # from parse_Raise: None passed as the assert condition
+        is_raise = test_expr is None
+
         if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE":
-            return IRnode.from_list(
-                ["assert_unreachable", test_expr], error_msg="assert unreachable"
-            )
+            if is_raise:
+                return IRnode.from_list(["invalid"], error_msg="raise unreachable")
+            else:
+                return IRnode.from_list(
+                    ["assert_unreachable", test_expr], error_msg="assert unreachable"
+                )
 
         # set constant so that revert reason str is well behaved
         try:
@@ -205,12 +211,10 @@ def _get_last(ir):
             ["mstore", buf - 32, 0x20],
             ["revert", buf - 36, ["add", 4 + 32 + 32, ["ceil32", _runtime_length]]],
         ]
-
-        if test_expr is not None:
-            ir_node = ["if", ["iszero", test_expr], revert_seq]
-        else:
+        if is_raise:
             ir_node = revert_seq
-
+        else:
+            ir_node = ["if", ["iszero", test_expr], revert_seq]
         return IRnode.from_list(ir_node, error_msg="user revert with reason")
 
     def parse_Assert(self):
diff --git a/vyper/ir/compile_ir.py b/vyper/ir/compile_ir.py
--- a/vyper/ir/compile_ir.py
+++ b/vyper/ir/compile_ir.py
@@ -537,7 +537,7 @@ def _height_of(witharg):
                 o.append("POP")
         return o
     # Seq without popping.
-    # Assure (if false, invalid opcode)
+    # unreachable keyword produces INVALID opcode
     elif code.value == "assert_unreachable":
         o = _compile_to_assembly(code.args[0], withargs, existing_labels, break_dest, height)
         end_symbol = mksymbol("reachable")


Test patch:
diff --git a/tests/parser/features/test_assert_unreachable.py b/tests/parser/features/test_assert_unreachable.py
--- a/tests/parser/features/test_assert_unreachable.py
+++ b/tests/parser/features/test_assert_unreachable.py
@@ -1,4 +1,4 @@
-def test_assure_refund(w3, get_contract):
+def test_unreachable_refund(w3, get_contract):
     code = """
 @external
 def foo():
@@ -51,6 +51,17 @@ def foo(val: int128) -> int128:
 
     assert c.foo(33) == -123
 
-    assert_tx_failed(lambda: c.foo(1), exc_text="Invalid opcode 0xfe")
     assert_tx_failed(lambda: c.foo(1), exc_text="Invalid opcode 0xfe")
     assert_tx_failed(lambda: c.foo(-1), exc_text="Invalid opcode 0xfe")
+
+
+def test_raise_unreachable(w3, get_contract, assert_tx_failed):
+    code = """
+@external
+def foo():
+    raise UNREACHABLE
+    """
+
+    c = get_contract(code)
+
+    assert_tx_failed(lambda: c.foo(), exc_text="Invalid opcode 0xfe")