From 8530645ad895a2051bc6c11ec36e7818da59e480 Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Wed, 20 May 2026 14:59:04 +0400 Subject: [PATCH 1/2] fix[venom]: honor folded bool kwargs --- .../builtins/codegen/test_abi_decode.py | 14 ++++++++++ tests/functional/syntax/test_print.py | 20 ++++++++++++++ .../compiler/venom/test_builtin_kwargs.py | 26 +++++++++++++++++++ vyper/codegen_venom/builtins/abi.py | 4 ++- vyper/codegen_venom/builtins/misc.py | 5 ++-- 5 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 tests/unit/compiler/venom/test_builtin_kwargs.py diff --git a/tests/functional/builtins/codegen/test_abi_decode.py b/tests/functional/builtins/codegen/test_abi_decode.py index aba3e6820a..ca326a0d54 100644 --- a/tests/functional/builtins/codegen/test_abi_decode.py +++ b/tests/functional/builtins/codegen/test_abi_decode.py @@ -113,6 +113,20 @@ def foo(x: Bytes[{input_len}]) -> {output_typ}: assert c.foo(encoded) == expected +def test_abi_decode_folded_unwrap_tuple_kwarg(get_contract): + contract = """ +UNWRAP: constant(bool) = False + +@external +def foo(x: Bytes[64]) -> Bytes[3]: + return abi_decode(x, Bytes[3], unwrap_tuple=UNWRAP) + """ + c = get_contract(contract) + + encoded = abi.encode("bytes", b"vyx") + assert c.foo(encoded) == b"vyx" + + @pytest.mark.parametrize( "arg,expected,input_len,output_typ1,output_typ2,abi_typ", [ diff --git a/tests/functional/syntax/test_print.py b/tests/functional/syntax/test_print.py index 54632c5148..48fbf8ac1b 100644 --- a/tests/functional/syntax/test_print.py +++ b/tests/functional/syntax/test_print.py @@ -1,6 +1,8 @@ import pytest from vyper import compiler +from vyper.compiler.settings import Settings +from vyper.utils import method_id_int valid_list = [ """ @@ -79,3 +81,21 @@ def foo(): @pytest.mark.parametrize("good_code", valid_list) def test_print_syntax(good_code): assert compiler.compile_code(good_code) is not None + + +def test_print_folded_hardhat_compat_kwarg(): + code = """ +HARDHAT_COMPAT: constant(bool) = True + +@external +def foo(x: uint256): + print(x, hardhat_compat=HARDHAT_COMPAT) + """ + + out = compiler.compile_code( + code, output_formats=["ir_runtime"], settings=Settings(experimental_codegen=True) + ) + ir_runtime = str(out["ir_runtime"]) + + assert f"0x{method_id_int('log(uint256)'):08x}" in ir_runtime + assert f"0x{method_id_int('log(string,bytes)'):08x}" not in ir_runtime diff --git a/tests/unit/compiler/venom/test_builtin_kwargs.py b/tests/unit/compiler/venom/test_builtin_kwargs.py new file mode 100644 index 0000000000..cdf5e703bc --- /dev/null +++ b/tests/unit/compiler/venom/test_builtin_kwargs.py @@ -0,0 +1,26 @@ +import pytest + +from vyper import ast as vy_ast +from vyper.codegen_venom.builtins.abi import _get_bool_kwarg as get_abi_bool_kwarg +from vyper.codegen_venom.builtins.misc import _get_bool_kwarg as get_misc_bool_kwarg +from vyper.exceptions import CompilerPanic + + +def _call_node(source): + return vy_ast.parse_to_ast(source).body[0].value + + +@pytest.mark.parametrize("get_bool_kwarg", [get_abi_bool_kwarg, get_misc_bool_kwarg]) +def test_bool_kwarg_uses_reduced_value(get_bool_kwarg): + call_node = _call_node("foo(flag=FLAG)") + call_node.keywords[0].value._set_folded_value(vy_ast.NameConstant(value=False)) + + assert get_bool_kwarg(call_node, "flag", True) is False + + +@pytest.mark.parametrize("get_bool_kwarg", [get_abi_bool_kwarg, get_misc_bool_kwarg]) +def test_bool_kwarg_rejects_unreduced_value(get_bool_kwarg): + call_node = _call_node("foo(flag=FLAG)") + + with pytest.raises(CompilerPanic, match="unfoldable boolean kwarg: flag"): + get_bool_kwarg(call_node, "flag", True) diff --git a/vyper/codegen_venom/builtins/abi.py b/vyper/codegen_venom/builtins/abi.py index 16e8711b2f..ba1cb1a8f1 100644 --- a/vyper/codegen_venom/builtins/abi.py +++ b/vyper/codegen_venom/builtins/abi.py @@ -15,6 +15,7 @@ from vyper.codegen_venom.abi import abi_decode_to_buf, abi_encode_to_buf from vyper.codegen_venom.buffer import Buffer, Ptr from vyper.codegen_venom.value import VyperValue +from vyper.exceptions import CompilerPanic from vyper.semantics.data_locations import DataLocation from vyper.semantics.types import BytesT, TupleT from vyper.utils import fourbytes_to_int @@ -37,13 +38,14 @@ def _get_bool_kwarg(node: vy_ast.Call, kwarg_name: str, default: bool) -> bool: kw_node = _get_kwarg_value(node, kwarg_name) if kw_node is None: return default + kw_node = kw_node.reduced() # The value should be a NameConstant (True/False) if isinstance(kw_node, vy_ast.NameConstant): return kw_node.value # Could also be an Int with constant value if isinstance(kw_node, vy_ast.Int): return bool(kw_node.value) - return default + raise CompilerPanic(f"unfoldable boolean kwarg: {kwarg_name}") def _parse_method_id(method_id_node: vy_ast.VyperNode) -> Optional[int]: diff --git a/vyper/codegen_venom/builtins/misc.py b/vyper/codegen_venom/builtins/misc.py index 09f7f91fa8..09721a425d 100644 --- a/vyper/codegen_venom/builtins/misc.py +++ b/vyper/codegen_venom/builtins/misc.py @@ -19,7 +19,7 @@ from vyper.codegen_venom.abi.abi_encoder import abi_encode_to_buf from vyper.codegen_venom.constants import BLOCKHASH_LOOKBACK_LIMIT, ECRECOVER_PRECOMPILE from vyper.evm.opcodes import version_check -from vyper.exceptions import EvmVersionException +from vyper.exceptions import CompilerPanic, EvmVersionException from vyper.semantics.types import BytesT, DecimalT, StringT, TupleT from vyper.utils import DECIMAL_DIVISOR, method_id_int from vyper.venom.basicblock import IRLiteral, IROperand, IRVariable @@ -389,11 +389,12 @@ def _get_bool_kwarg(node: vy_ast.Call, kwarg_name: str, default: bool) -> bool: kw_node = _get_kwarg_value(node, kwarg_name) if kw_node is None: return default + kw_node = kw_node.reduced() if isinstance(kw_node, vy_ast.NameConstant): return kw_node.value if isinstance(kw_node, vy_ast.Int): return bool(kw_node.value) - return default + raise CompilerPanic(f"unfoldable boolean kwarg: {kwarg_name}") def _create_tuple_in_memory( From 6e25d4505ca3b551559f43406d156c206e5a37ff Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Thu, 21 May 2026 13:00:34 +0400 Subject: [PATCH 2/2] fix: attach folded kwarg panic node --- vyper/codegen_venom/builtins/abi.py | 2 +- vyper/codegen_venom/builtins/misc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vyper/codegen_venom/builtins/abi.py b/vyper/codegen_venom/builtins/abi.py index ba1cb1a8f1..a7496c5093 100644 --- a/vyper/codegen_venom/builtins/abi.py +++ b/vyper/codegen_venom/builtins/abi.py @@ -45,7 +45,7 @@ def _get_bool_kwarg(node: vy_ast.Call, kwarg_name: str, default: bool) -> bool: # Could also be an Int with constant value if isinstance(kw_node, vy_ast.Int): return bool(kw_node.value) - raise CompilerPanic(f"unfoldable boolean kwarg: {kwarg_name}") + raise CompilerPanic(f"unfoldable boolean kwarg: {kwarg_name}", kw_node) def _parse_method_id(method_id_node: vy_ast.VyperNode) -> Optional[int]: diff --git a/vyper/codegen_venom/builtins/misc.py b/vyper/codegen_venom/builtins/misc.py index 09721a425d..55c7468867 100644 --- a/vyper/codegen_venom/builtins/misc.py +++ b/vyper/codegen_venom/builtins/misc.py @@ -394,7 +394,7 @@ def _get_bool_kwarg(node: vy_ast.Call, kwarg_name: str, default: bool) -> bool: return kw_node.value if isinstance(kw_node, vy_ast.Int): return bool(kw_node.value) - raise CompilerPanic(f"unfoldable boolean kwarg: {kwarg_name}") + raise CompilerPanic(f"unfoldable boolean kwarg: {kwarg_name}", kw_node) def _create_tuple_in_memory(