Skip to content

Commit 4b32292

Browse files
sandbubblescyberthirstcharles-cooper
authored
fix[lang]!: forbid calling __default__ (#4371)
This commit forbids `extcall` and `staticcall`s to `__default__`. This was not possible before the introduction of `__interface__()` and `__at__()`; however, now that we have the possibility of casting a module to its interface, it is possible to actually call the `__default__()` function, which generates a "normal" ABI-encoded call including the method id for `__default__()`. That could cause a selector collision with a different function at target, and also leads to confusing behavior (`__default__` gets called when no selector matches). The error message recommends the user to use `raw_call()` for this use case. --------- Co-authored-by: cyberthirst <[email protected]> Co-authored-by: Charles Cooper <[email protected]>
1 parent d9444fb commit 4b32292

File tree

2 files changed

+28
-3
lines changed

2 files changed

+28
-3
lines changed

tests/functional/syntax/test_interfaces.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from vyper import compiler
44
from vyper.exceptions import (
55
ArgumentException,
6+
CallViolation,
67
FunctionDeclarationException,
78
InterfaceViolation,
89
InvalidReference,
@@ -605,7 +606,6 @@ def bar():
605606
compiler.compile_code(main, input_bundle=input_bundle)
606607

607608

608-
@pytest.mark.xfail
609609
def test_intrinsic_interfaces_default_function(make_input_bundle, get_contract):
610610
lib1 = """
611611
@external
@@ -623,6 +623,26 @@ def bar():
623623
"""
624624
input_bundle = make_input_bundle({"lib1.vy": lib1})
625625

626-
# TODO make the exception more precise once fixed
627-
with pytest.raises(Exception): # noqa: B017
626+
with pytest.raises(CallViolation):
627+
compiler.compile_code(main, input_bundle=input_bundle)
628+
629+
630+
def test_intrinsic_interfaces_default_function_staticcall(make_input_bundle, get_contract):
631+
lib1 = """
632+
@external
633+
@view
634+
def __default__() -> int128:
635+
return 43
636+
"""
637+
main = """
638+
import lib1
639+
640+
@external
641+
def bar():
642+
foo:int128 = 0
643+
foo = staticcall lib1.__at__(self).__default__()
644+
"""
645+
input_bundle = make_input_bundle({"lib1.vy": lib1})
646+
647+
with pytest.raises(CallViolation):
628648
compiler.compile_code(main, input_bundle=input_bundle)

vyper/semantics/analysis/local.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,11 @@ def visit_Call(self, node: vy_ast.Call, typ: VyperType) -> None:
775775
msg += f"must use the `{should}` keyword."
776776
hint = f"try `{should} {node.node_source_code}`"
777777
raise CallViolation(msg, hint=hint)
778+
779+
if func_type.is_fallback:
780+
msg = "`__default__` function cannot be called directly."
781+
msg += " If you mean to call the default function, use `raw_call`"
782+
raise CallViolation(msg)
778783
else:
779784
if not node.is_plain_call:
780785
kind = node.kind_str

0 commit comments

Comments
 (0)