Repository: vyperlang/vyper

Issue:
Compile time evaluation of several builtins wrongfully allows `decimal` arguments.
### Version Information

* vyper Version (output of `vyper --version`): 0.3.9+commit.5c2892b2
* OS: OSX
* Python Version (output of `python --version`): 3.11.3

### What's your issue about?

The arguments of the following builtins are not well type-checked when they all are constant and the builtin is to be folded: (Should be `Int` AST nodes while the functions expect `Num` AST nodes):

- `uint256_addmod`
- `uint256_mulmod`
- `shift` (deprecated)
- `BitwiseAnd`, `BitwiseOr`, `BitwiseXor`, `BitwiseNot` (all deprecated)

For example, trying to compile the following contract will make the compiler panic:

```Vyper
@external
def foo():
    ds:uint256 = uint256_addmod(1.1,2.2,2)
```
```Bash
Error compiling: tests/customs/code.vy
Traceback (most recent call last):
  File "/Users/trocher/Documents/thesis/vyper/venv/bin/vyper", line 33, in <module>
    sys.exit(load_entry_point('vyper==0.3.9', 'console_scripts', 'vyper')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 58, in _parse_cli_args
    return _parse_args(sys.argv[1:])
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 156, in _parse_args
    compiled = compile_files(
               ^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 294, in compile_files
    compiler_data = vyper.compile_codes(
                    ^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/evm/opcodes.py", line 230, in _wrapper
    return fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 142, in compile_codes
    exc_handler(contract_name, exc)
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 191, in exc_handler
    raise exception
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 139, in compile_codes
    out[contract_name][output_format] = OUTPUT_FORMATS[output_format](compiler_data)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/output.py", line 82, in build_ir_output
    return compiler_data.ir_nodes
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 126, in ir_nodes
    ir, ir_runtime = self._ir_output
                     ^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/functools.py", line 1001, in __get__
    val = self.func(instance)
          ^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 122, in _ir_output
    return generate_ir_nodes(self.global_ctx, self.no_optimize)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 259, in generate_ir_nodes
    ir_nodes, ir_runtime = module.generate_ir_for_module(global_ctx)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/module.py", line 149, in generate_ir_for_module
    runtime = _runtime_ir(runtime_functions, global_ctx)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/module.py", line 106, in _runtime_ir
    func_ir = generate_ir_for_function(func_ast, global_ctx, skip_nonpayable_check)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/function_definitions/common.py", line 114, in generate_ir_for_function
    o = generate_ir_for_external_function(code, func_t, context, skip_nonpayable_check)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/function_definitions/external_function.py", line 187, in generate_ir_for_external_function
    body += [parse_body(code.body, context, ensure_terminated=True)]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 433, in parse_body
    ir = parse_stmt(stmt, context)
         ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 407, in parse_stmt
    return Stmt(stmt, context).ir_node
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 41, in __init__
    self.ir_node = fn()
                   ^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 68, in parse_AnnAssign
    rhs = Expr(self.stmt.value, self.context).ir_node
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/expr.py", line 80, in __init__
    self.ir_node = fn()
                   ^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/expr.py", line 89, in parse_Int
    return IRnode.from_list(self.expr.n, typ=typ)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/ir_node.py", line 532, in from_list
    return cls(
           ^^^^
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/ir_node.py", line 319, in __init__
    raise CompilerPanic(f"Invalid value for IR AST node: {self.value}")
vyper.exceptions.CompilerPanic: Invalid value for IR AST node: 1.3

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
```

### How can it be fixed?

Replacing `vy_ast.Num` by `vy_ast.Int` in the `evaluate` method of the classes of the builtins should be enough.


Discussion:

Resolving patch:
diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py
--- a/vyper/builtins/functions.py
+++ b/vyper/builtins/functions.py
@@ -1370,7 +1370,7 @@ def evaluate(self, node):
 
         validate_call_args(node, 2)
         for arg in node.args:
-            if not isinstance(arg, vy_ast.Num):
+            if not isinstance(arg, vy_ast.Int):
                 raise UnfoldableNode
             if arg.value < 0 or arg.value >= 2**256:
                 raise InvalidLiteral("Value out of range for uint256", arg)
@@ -1396,7 +1396,7 @@ def evaluate(self, node):
 
         validate_call_args(node, 2)
         for arg in node.args:
-            if not isinstance(arg, vy_ast.Num):
+            if not isinstance(arg, vy_ast.Int):
                 raise UnfoldableNode
             if arg.value < 0 or arg.value >= 2**256:
                 raise InvalidLiteral("Value out of range for uint256", arg)
@@ -1422,7 +1422,7 @@ def evaluate(self, node):
 
         validate_call_args(node, 2)
         for arg in node.args:
-            if not isinstance(arg, vy_ast.Num):
+            if not isinstance(arg, vy_ast.Int):
                 raise UnfoldableNode
             if arg.value < 0 or arg.value >= 2**256:
                 raise InvalidLiteral("Value out of range for uint256", arg)
@@ -1447,7 +1447,7 @@ def evaluate(self, node):
             self.__class__._warned = True
 
         validate_call_args(node, 1)
-        if not isinstance(node.args[0], vy_ast.Num):
+        if not isinstance(node.args[0], vy_ast.Int):
             raise UnfoldableNode
 
         value = node.args[0].value
@@ -1474,7 +1474,7 @@ def evaluate(self, node):
             self.__class__._warned = True
 
         validate_call_args(node, 2)
-        if [i for i in node.args if not isinstance(i, vy_ast.Num)]:
+        if [i for i in node.args if not isinstance(i, vy_ast.Int)]:
             raise UnfoldableNode
         value, shift = [i.value for i in node.args]
         if value < 0 or value >= 2**256:
@@ -1522,10 +1522,10 @@ class _AddMulMod(BuiltinFunction):
 
     def evaluate(self, node):
         validate_call_args(node, 3)
-        if isinstance(node.args[2], vy_ast.Num) and node.args[2].value == 0:
+        if isinstance(node.args[2], vy_ast.Int) and node.args[2].value == 0:
             raise ZeroDivisionException("Modulo by 0", node.args[2])
         for arg in node.args:
-            if not isinstance(arg, vy_ast.Num):
+            if not isinstance(arg, vy_ast.Int):
                 raise UnfoldableNode
             if arg.value < 0 or arg.value >= 2**256:
                 raise InvalidLiteral("Value out of range for uint256", arg)


Test patch:
diff --git a/tests/parser/syntax/test_addmulmod.py b/tests/parser/syntax/test_addmulmod.py
new file mode 100644
--- /dev/null
+++ b/tests/parser/syntax/test_addmulmod.py
@@ -0,0 +1,27 @@
+import pytest
+
+from vyper.exceptions import InvalidType
+
+fail_list = [
+    (  # bad AST nodes given as arguments
+        """
+@external
+def foo() -> uint256:
+    return uint256_addmod(1.1, 1.2, 3.0)
+    """,
+        InvalidType,
+    ),
+    (  # bad AST nodes given as arguments
+        """
+@external
+def foo() -> uint256:
+    return uint256_mulmod(1.1, 1.2, 3.0)
+    """,
+        InvalidType,
+    ),
+]
+
+
+@pytest.mark.parametrize("code,exc", fail_list)
+def test_add_mod_fail(assert_compile_failed, get_contract, code, exc):
+    assert_compile_failed(lambda: get_contract(code), exc)