Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions tests/compiler/test_source_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ def test_pos_map_offsets():
)


def test_error_map():
code = """
foo: uint256

@external
def update_foo():
self.foo += 1
"""
error_map = compile_code(code, ["source_map"])["source_map"]["error_map"]
assert "safeadd" in list(error_map.values())
assert "fallback function" in list(error_map.values())


def test_compress_source_map():
code = """
@external
Expand Down
11 changes: 7 additions & 4 deletions vyper/codegen/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ def safe_add(x, y):
# TODO push down into optimizer rules.
ok = ["ge", res, x]

ret = IRnode.from_list(["seq", ["assert", ok], res])
check = IRnode.from_list(["assert", ok], error_msg="safeadd")
ret = IRnode.from_list(["seq", check, res])
return b1.resolve(ret)


Expand Down Expand Up @@ -184,7 +185,8 @@ def safe_sub(x, y):
# TODO push down into optimizer rules.
ok = ["le", res, x]

ret = IRnode.from_list(["seq", ["assert", ok], res])
check = IRnode.from_list(["assert", ok], error_msg="safesub")
ret = IRnode.from_list(["seq", check, res])
return b1.resolve(ret)


Expand Down Expand Up @@ -250,7 +252,8 @@ def safe_mul(x, y):
# (if bits == 256, clamp_basetype is a no-op)
res = clamp_basetype(res)

res = IRnode.from_list(["seq", ["assert", ok], res], typ=res.typ)
check = IRnode.from_list(["assert", ok], error_msg="safediv")
res = IRnode.from_list(["seq", check, res], typ=res.typ)

return b1.resolve(res)

Expand Down Expand Up @@ -308,7 +311,7 @@ def safe_div(x, y):
# TODO maybe use safe_mul
res = clamp_basetype(res)

check = ["assert", ok]
check = IRnode.from_list(["assert", ok], error_msg="safemul")
return IRnode.from_list(b1.resolve(["seq", check, res]))


Expand Down
9 changes: 5 additions & 4 deletions vyper/codegen/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ def clamp_basetype(ir_node):
else: # pragma: nocover
raise CompilerPanic(f"{t} passed to clamp_basetype")

return IRnode.from_list(ret, typ=ir_node.typ)
return IRnode.from_list(ret, typ=ir_node.typ, error_msg=f"validate {t}")


def int_clamp(ir_node, bits, signed=False):
Expand Down Expand Up @@ -1024,15 +1024,16 @@ def promote_signed_int(x, bits):
# general clamp function for all ops and numbers
def clamp(op, arg, bound):
with IRnode.from_list(arg).cache_when_complex("clamp_arg") as (b1, arg):
assertion = ["assert", [op, arg, bound]]
ret = ["seq", assertion, arg]
check = IRnode.from_list(["assert", [op, arg, bound]], error_msg=f"clamp {op} {bound}")
ret = ["seq", check, arg]
return IRnode.from_list(b1.resolve(ret), typ=arg.typ)


def clamp_nonzero(arg):
# TODO: use clamp("ne", arg, 0) once optimizer rules can handle it
with IRnode.from_list(arg).cache_when_complex("should_nonzero") as (b1, arg):
ret = ["seq", ["assert", arg], arg]
check = IRnode.from_list(["assert", arg], error_msg="clamp_nonzero")
ret = ["seq", check, arg]
return IRnode.from_list(b1.resolve(ret), typ=arg.typ)


Expand Down
8 changes: 8 additions & 0 deletions vyper/codegen/ir_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def __init__(
location: Optional[AddrSpace] = None,
source_pos: Optional[Tuple[int, int]] = None,
annotation: Optional[str] = None,
error_msg: Optional[str] = None,
mutable: bool = True,
add_gas_estimate: int = 0,
encoding: Encoding = Encoding.VYPER,
Expand All @@ -129,6 +130,7 @@ def __init__(
self.typ = typ
self.location = location
self.source_pos = source_pos
self.error_msg = error_msg
self.annotation = annotation
self.mutable = mutable
self.add_gas_estimate = add_gas_estimate
Expand Down Expand Up @@ -494,6 +496,7 @@ def from_list(
location: Optional[AddrSpace] = None,
source_pos: Optional[Tuple[int, int]] = None,
annotation: Optional[str] = None,
error_msg: Optional[str] = None,
mutable: bool = True,
add_gas_estimate: int = 0,
encoding: Encoding = Encoding.VYPER,
Expand All @@ -512,6 +515,8 @@ def from_list(
obj.location = location
if obj.encoding is None:
obj.encoding = encoding
if obj.error_msg is None:
obj.error_msg = error_msg

return obj
elif not isinstance(obj, list):
Expand All @@ -523,7 +528,9 @@ def from_list(
annotation=annotation,
mutable=mutable,
add_gas_estimate=add_gas_estimate,
source_pos=source_pos,
encoding=encoding,
error_msg=error_msg,
)
else:
return cls(
Expand All @@ -536,4 +543,5 @@ def from_list(
source_pos=source_pos,
add_gas_estimate=add_gas_estimate,
encoding=encoding,
error_msg=error_msg,
)
4 changes: 3 additions & 1 deletion vyper/codegen/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ def _runtime_ir(runtime_functions, all_sigs, global_ctx):
default_function, all_sigs, global_ctx, skip_nonpayable_check
)
else:
fallback_ir = IRnode.from_list(["revert", 0, 0], annotation="Default function")
fallback_ir = IRnode.from_list(
["revert", 0, 0], annotation="Default function", error_msg="fallback function"
)

# ensure the external jumptable section gets closed out
# (for basic block hygiene and also for zksync interpreter)
Expand Down
10 changes: 6 additions & 4 deletions vyper/codegen/stmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ def parse_Call(self):

def _assert_reason(self, test_expr, msg):
if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE":
return IRnode.from_list(["assert_unreachable", test_expr])
return IRnode.from_list(
["assert_unreachable", test_expr], error_msg="assert unreachable"
)

# set constant so that revert reason str is well behaved
try:
Expand Down Expand Up @@ -209,21 +211,21 @@ def _get_last(ir):
else:
ir_node = revert_seq

return IRnode.from_list(ir_node)
return IRnode.from_list(ir_node, error_msg="user revert with reason")

def parse_Assert(self):
test_expr = Expr.parse_value_expr(self.stmt.test, self.context)

if self.stmt.msg:
return self._assert_reason(test_expr, self.stmt.msg)
else:
return IRnode.from_list(["assert", test_expr])
return IRnode.from_list(["assert", test_expr], error_msg="user assert")

def parse_Raise(self):
if self.stmt.exc:
return self._assert_reason(None, self.stmt.exc)
else:
return IRnode.from_list(["revert", 0, 0])
return IRnode.from_list(["revert", 0, 0], error_msg="user raise")

def _check_valid_range_constant(self, arg_ast_node):
with self.context.range_scope():
Expand Down
13 changes: 11 additions & 2 deletions vyper/ir/compile_ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,10 @@ class Instruction(str):
def __new__(cls, sstr, *args, **kwargs):
return super().__new__(cls, sstr)

def __init__(self, sstr, source_pos=None):
def __init__(self, sstr, source_pos=None, error_msg=None):
self.error_msg = error_msg
self.pc_debugger = False

if source_pos is not None:
self.lineno, self.col_offset, self.end_lineno, self.end_col_offset = source_pos
else:
Expand All @@ -174,8 +176,9 @@ def apply_line_numbers(func):
def apply_line_no_wrapper(*args, **kwargs):
code = args[0]
ret = func(*args, **kwargs)

new_ret = [
Instruction(i, code.source_pos)
Instruction(i, code.source_pos, code.error_msg)
if isinstance(i, str) and not isinstance(i, Instruction)
else i
for i in ret
Expand Down Expand Up @@ -726,7 +729,12 @@ def note_line_num(line_number_map, item, pos):
offsets = (item.lineno, item.col_offset, item.end_lineno, item.end_col_offset)
else:
offsets = None

line_number_map["pc_pos_map"][pos] = offsets

if item.error_msg is not None:
line_number_map["error_map"][pos] = item.error_msg

added_line_breakpoint = note_breakpoint(line_number_map, item, pos)
return added_line_breakpoint

Expand Down Expand Up @@ -938,6 +946,7 @@ def assembly_to_evm(assembly, start_pos=0, insert_vyper_signature=False):
"pc_breakpoints": set(),
"pc_jump_map": {0: "-"},
"pc_pos_map": {},
"error_map": {},
}

posmap = {}
Expand Down
2 changes: 2 additions & 0 deletions vyper/ir/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ def _optimize(node: IRnode, parent: Optional[IRnode]) -> Tuple[bool, IRnode]:
typ = node.typ
location = node.location
source_pos = node.source_pos
error_msg = node.error_msg
annotation = node.annotation
add_gas_estimate = node.add_gas_estimate

Expand All @@ -445,6 +446,7 @@ def finalize(val, args):
typ=typ,
location=location,
source_pos=source_pos,
error_msg=error_msg,
annotation=annotation,
add_gas_estimate=add_gas_estimate,
)
Expand Down