diff --git a/tests/functional/codegen/types/numbers/test_exponents.py b/tests/functional/codegen/types/numbers/test_exponents.py index 702cbcb1dd..28dba59edc 100644 --- a/tests/functional/codegen/types/numbers/test_exponents.py +++ b/tests/functional/codegen/types/numbers/test_exponents.py @@ -173,3 +173,17 @@ def foo(b: int128) -> int128: c.foo(max_power) with tx_failed(): c.foo(max_power + 1) + + +valid_list = [ + """ +@external +def foo() -> uint256: + return (10**18)**2 + """ +] + + +@pytest.mark.parametrize("good_code", valid_list) +def test_exponent_success(good_code): + assert compile_code(good_code) is not None diff --git a/tests/unit/compiler/test_source_map.py b/tests/unit/compiler/test_source_map.py index d99b546403..ae1999a26e 100644 --- a/tests/unit/compiler/test_source_map.py +++ b/tests/unit/compiler/test_source_map.py @@ -97,8 +97,44 @@ def update_foo(): self.foo += 1 """ error_map = compile_code(code, output_formats=["source_map"])["source_map"]["error_map"] - assert "safeadd" in list(error_map.values()) - assert "fallback function" in list(error_map.values()) + assert "safeadd" in error_map.values() + assert "fallback function" in error_map.values() + + +def test_error_map_with_user_error(): + code = """ +@external +def foo(): + raise "some error" + """ + error_map = compile_code(code, output_formats=["source_map"])["source_map"]["error_map"] + assert "user revert with reason" in error_map.values() + + +def test_error_map_with_user_error2(): + code = """ +@external +def foo(i: uint256): + a: DynArray[uint256, 10] = [1] + a[i % 10] = 2 + """ + error_map = compile_code(code, output_formats=["source_map"])["source_map"]["error_map"] + assert "safemod" in error_map.values() + + +def test_error_map_not_overriding_errors(): + code = """ +@external +def foo(i: uint256): + raise self.bar(5%i) + +@pure +def bar(i: uint256) -> String[32]: + return "foo foo" + """ + error_map = compile_code(code, output_formats=["source_map"])["source_map"]["error_map"] + assert "user revert with reason" in error_map.values() + assert "safemod" in error_map.values() def test_compress_source_map(): diff --git a/vyper/codegen/ir_node.py b/vyper/codegen/ir_node.py index ff721fafcb..81ec47f10f 100644 --- a/vyper/codegen/ir_node.py +++ b/vyper/codegen/ir_node.py @@ -378,13 +378,18 @@ def is_complex_ir(self): and self.value.lower() not in do_not_cache ) - # set an error message and push down into all children. - # useful for overriding an error message generated by a helper - # function with a more specific error message. + # set an error message and push down to its children that don't have error_msg set def set_error_msg(self, error_msg: str) -> None: + if self.error_msg is not None: + raise CompilerPanic(f"{self.value} already has error message {self.error_msg}") + self._set_error_msg(error_msg) + + def _set_error_msg(self, error_msg: str) -> None: + if self.error_msg is not None: + return self.error_msg = error_msg for arg in self.args: - arg.set_error_msg(error_msg) + arg._set_error_msg(error_msg) # get the unique symbols contained in this node, which provides # sanity check invariants for the optimizer. @@ -627,7 +632,7 @@ def from_list( else: return cls( obj[0], - [cls.from_list(o, ast_source=ast_source) for o in obj[1:]], + [cls.from_list(o, ast_source=ast_source, error_msg=error_msg) for o in obj[1:]], typ, location=location, annotation=annotation, diff --git a/vyper/semantics/analysis/utils.py b/vyper/semantics/analysis/utils.py index a31ce7acc1..8727f3750d 100644 --- a/vyper/semantics/analysis/utils.py +++ b/vyper/semantics/analysis/utils.py @@ -41,7 +41,7 @@ def _validate_op(node, types_list, validation_fn_name): try: _validate_fn(node) ret.append(type_) - except InvalidOperation as e: + except (InvalidOperation, OverflowException) as e: err_list.append(e) if ret: diff --git a/vyper/semantics/types/primitives.py b/vyper/semantics/types/primitives.py index 5c0362e662..dcc4fe8c8e 100644 --- a/vyper/semantics/types/primitives.py +++ b/vyper/semantics/types/primitives.py @@ -173,11 +173,11 @@ def _get_lr(): if isinstance(left, vy_ast.Int): if left.value >= 2**value_bits: raise OverflowException( - "Base is too large, calculation will always overflow", left + f"Base is too large for {self}, calculation will always overflow", left ) elif left.value < -(2**value_bits): raise OverflowException( - "Base is too small, calculation will always underflow", left + f"Base is too small for {self}, calculation will always underflow", left ) elif isinstance(right, vy_ast.Int): if right.value < 0: