Skip to content
Open
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
24 changes: 21 additions & 3 deletions tests/functional/syntax/test_abi_decode.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from vyper import compiler
from vyper.exceptions import TypeMismatch
from vyper.exceptions import CompilerPanic, TypeMismatch, UnfoldableNode

fail_list = [
(
Expand Down Expand Up @@ -31,13 +31,31 @@ def test_abi_decode_fail(bad_code, exc):
compiler.compile_code(bad_code)


valid_list = ["""
valid_list = [
"""
@external
def foo(x: Bytes[32]) -> uint256:
return _abi_decode(x, uint256)
"""]
""",
"""
@external
def foo(x: Bytes[32]) -> uint256:
return _abi_decode(x, uint256, unwrap_tuple=(0 < 1))
""",
]


@pytest.mark.parametrize("good_code", valid_list)
def test_abi_decode_success(good_code):
assert compiler.compile_code(good_code) is not None


# UnfoldableNode on legacy, CompilerPanic in venom

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the compiler panic from?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CompilerPanic: unfoldable boolean kwarg: unwrap_tuple
From _get_bool_kwarg in misc.py

I tried to make it raise an UnfoldableNode as well, but actually the proper fix is to fix #5153
The front-end checks that this is a Constant, so we should never be in a situation were we need to raise in the first place

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we fix the venom pipeline too so that it doesn't panic? what is the point of "fixing" the panic in one pipeline but not the other?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR fixes the panic on both backends for cases like:

_abi_decode(x, uint256, unwrap_tuple=(0 < 1))

This xfail test is instead due to #5153
I did not want to burden this PR with some codegen changes just to change the type of VyperInternalException which is reported

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude-authored explanation of why this happens here (and not on abi_encode):

Semantic analysis passes cleanly, and the kwarg is only consumed at codegen:

  • Legacy codegen: @process_inputs → process_kwarg (vyper/builtins/_signatures.py:37) calls kwarg_node.get_folded_value().value → UnfoldableNode (same underlying failure, just later).
  • Venom codegen: vyper/codegen_venom/builtins/abi.py:36-48's _get_bool_kwarg uses reduced() (which returns self when there's no folded value, no exception), then does an isinstance(kw_node, vy_ast.NameConstant | vy_ast.Int) check. empty(bool) is a Call, falls through, and hits the explicit raise CompilerPanic("unfoldable boolean kwarg: …") at line 48.

@pytest.mark.xfail(raises=(UnfoldableNode, CompilerPanic))
def test_abi_decode_unwrap_tuple_foldable_expr():
code = """
@external
def f(x: Bytes[32]) -> uint256:
return abi_decode(x, uint256, unwrap_tuple=empty(bool))
"""
assert compiler.compile_code(code) is not None
17 changes: 16 additions & 1 deletion tests/functional/syntax/test_abi_encode.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from vyper import compiler
from vyper.exceptions import InvalidLiteral, TypeMismatch
from vyper.exceptions import InvalidLiteral, TypeMismatch, UnfoldableNode

fail_list = [
(
Expand Down Expand Up @@ -119,9 +119,24 @@ def foo(x: Bytes[1]) -> Bytes[68]:
def foo() -> Bytes[224]:
return _abi_encode(BAR)
""",
"""
@external
def foo(x: Bytes[1]) -> Bytes[96]:
return _abi_encode(x, ensure_tuple=(0 < 1))
""",
]


@pytest.mark.parametrize("good_code", valid_list)
def test_abi_encode_success(good_code):
assert compiler.compile_code(good_code) is not None


@pytest.mark.xfail(raises=UnfoldableNode)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include CompilerPanic in the encode xfail

When the CI matrix runs the experimental pipeline (.github/workflows/test.yml includes --experimental-codegen), this new test reaches venom's _get_bool_kwarg path for empty(bool), which raises CompilerPanic for an unreduced boolean kwarg; the adjacent decode test handles that with raises=(UnfoldableNode, CompilerPanic), but this xfail only permits UnfoldableNode. As a result, the newly added encode regression test fails in every experimental-codegen job instead of being reported as xfailed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is empirically wrong, the venom tests pass

def test_abi_encode_ensure_tuple_foldable_expr():
code = """
@external
def f(x: uint256) -> Bytes[64]:
return abi_encode(x, ensure_tuple=empty(bool))
"""
assert compiler.compile_code(code) is not None
26 changes: 17 additions & 9 deletions vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,20 @@


class FoldedFunctionT(BuiltinFunctionT):
# Base class for nodes which should always be folded
"""
Base class for nodes which should always be folded
"""

_modifiability = Modifiability.CONSTANT


class TypenameFoldedFunctionT(FoldedFunctionT):
# Base class for builtin functions that:
# (1) take a typename as the only argument; and
# (2) should always be folded.
"""
Base class for builtin functions that:
(1) take a typename as the only argument; and
(2) should always be folded.
"""

_inputs = [("typename", TYPE_T.any())]

def fetch_call_return(self, node):
Expand Down Expand Up @@ -2243,11 +2248,14 @@ def infer_kwarg_types(self, node):

def fetch_call_return(self, node):
self._validate_arg_types(node)
ensure_tuple = next(
(arg.value.value for arg in node.keywords if arg.arg == "ensure_tuple"), True
)
kwargs = {kw.arg: kw.value for kw in node.keywords}

if "ensure_tuple" in kwargs:
ensure_tuple = kwargs["ensure_tuple"].get_folded_value().value
else:
ensure_tuple = True

assert isinstance(ensure_tuple, bool)
has_method_id = "method_id" in [arg.arg for arg in node.keywords]

# figure out the output type by converting
# the types to ABI_Types and calling size_bound API
Expand All @@ -2264,7 +2272,7 @@ def fetch_call_return(self, node):

maxlen = arg_abi_t.size_bound()

if has_method_id:
if "method_id" in kwargs:
# the output includes 4 bytes for the method_id.
maxlen += 4

Expand Down
Loading