Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix[lang]: forbid calling __default__ #4371

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3daf88b
feat[lang]: allow module intrinsic interface call
charles-cooper Jun 1, 2024
dad60ac
add codegen test
charles-cooper Jun 1, 2024
08c652a
add export test for unimplemented _inline_ interfaces
charles-cooper Jun 1, 2024
fe1cfa4
add codegen test for inline interface export
charles-cooper Jun 1, 2024
15d0fde
fix lint
charles-cooper Jun 1, 2024
a2c0ea8
Merge branch 'master' into fix/interface-intrinsic
charles-cooper Jun 2, 2024
feac371
Merge branch 'master' into fix/interface-intrinsic
charles-cooper Jun 2, 2024
2d5f67d
add sanity check
charles-cooper Aug 8, 2024
9621397
Merge branch 'master' into fix/interface-intrinsic
charles-cooper Oct 11, 2024
8504d4d
Merge branch 'master' into fix/interface-intrinsic
charles-cooper Oct 13, 2024
15e810e
Merge branch 'master' into fix/interface-intrinsic
charles-cooper Oct 19, 2024
4986b50
fix lint
charles-cooper Oct 19, 2024
24ac428
reject empty interfaces
charles-cooper Oct 19, 2024
c0f37ac
fix a test
charles-cooper Oct 19, 2024
59e298e
fix again
charles-cooper Oct 19, 2024
e05fabf
remove init function from interfaces
charles-cooper Oct 20, 2024
7a2d36d
fix for windows tests
charles-cooper Oct 20, 2024
ac43beb
reject weird exports of value types
charles-cooper Oct 22, 2024
df217e8
add `module.__at__`
charles-cooper Nov 10, 2024
dfb3eb3
fix exports
charles-cooper Nov 10, 2024
236e11a
add invalid at exports
cyberthirst Nov 13, 2024
2b5c4ad
parametrize over interface accessor
cyberthirst Nov 13, 2024
3db8f42
add intrinsic interface instantiation test
cyberthirst Nov 13, 2024
5b91458
add intrinsic interface convert test
cyberthirst Nov 13, 2024
7ebf244
add intrinsic interfaces have different types
cyberthirst Nov 13, 2024
272d4d5
extend interface init function test
cyberthirst Nov 13, 2024
6860897
add test accesing default function in interface
cyberthirst Nov 13, 2024
73aeacc
add more interface tests
cyberthirst Nov 13, 2024
4e282b4
lint
cyberthirst Nov 13, 2024
9bac423
Merge pull request #50 from cyberthirst/fork/charles-cooper/fix/inter…
charles-cooper Nov 19, 2024
341d4b6
mark xfail
charles-cooper Nov 20, 2024
4c5e304
raise error when calling __default__
sandbubbles Nov 23, 2024
eb574c0
remove xfail mark
sandbubbles Nov 23, 2024
0903256
test staticcall fails with default
sandbubbles Nov 23, 2024
d20f8b7
forbid staticcall on __default__
sandbubbles Nov 23, 2024
17ee487
lint
sandbubbles Nov 23, 2024
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
23 changes: 23 additions & 0 deletions tests/functional/codegen/modules/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,26 @@ def __init__():
# call `c.__default__()`
env.message_call(c.address)
assert c.counter() == 6


def test_inline_interface_export(make_input_bundle, get_contract):
lib1 = """
interface IAsset:
def asset() -> address: view

implements: IAsset

@external
@view
def asset() -> address:
return self
"""
main = """
import lib1

exports: lib1.IAsset
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)

assert c.asset() == c.address
36 changes: 35 additions & 1 deletion tests/functional/codegen/modules/test_interface_imports.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import pytest


def test_import_interface_types(make_input_bundle, get_contract):
ifaces = """
interface IFoo:
Expand Down Expand Up @@ -50,16 +53,47 @@ def foo() -> bool:
# check that this typechecks both directions
a: lib1.IERC20 = IERC20(msg.sender)
b: lib2.IERC20 = IERC20(msg.sender)
c: IERC20 = lib1.IERC20(msg.sender) # allowed in call position

# return the equality so we can sanity check it
return a == b
return a == b and b == c
"""
input_bundle = make_input_bundle({"lib1.vy": lib1, "lib2.vy": lib2})
c = get_contract(main, input_bundle=input_bundle)

assert c.foo() is True


@pytest.mark.parametrize("interface_syntax", ["__at__", "__interface__"])
def test_intrinsic_interface(get_contract, make_input_bundle, interface_syntax):
lib = """
@external
@view
def foo() -> uint256:
# detect self call
if msg.sender == self:
return 4
else:
return 5
"""

main = f"""
import lib

exports: lib.__interface__

@external
@view
def bar() -> uint256:
return staticcall lib.{interface_syntax}(self).foo()
"""
input_bundle = make_input_bundle({"lib.vy": lib})
c = get_contract(main, input_bundle=input_bundle)

assert c.foo() == 5
assert c.bar() == 4


def test_import_interface_flags(make_input_bundle, get_contract):
ifaces = """
flag Foo:
Expand Down
89 changes: 89 additions & 0 deletions tests/functional/codegen/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,92 @@ def foo(s: MyStruct) -> MyStruct:
assert "b: uint256" in out
assert "struct Voter:" in out
assert "voted: bool" in out


def test_intrinsic_interface_instantiation(make_input_bundle, get_contract):
lib1 = """
@external
@view
def foo():
pass
"""
main = """
import lib1

i: lib1.__interface__

@external
def bar() -> lib1.__interface__:
self.i = lib1.__at__(self)
return self.i
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)

assert c.bar() == c.address


def test_intrinsic_interface_converts(make_input_bundle, get_contract):
lib1 = """
@external
@view
def foo():
pass
"""
main = """
import lib1

@external
def bar() -> lib1.__interface__:
return lib1.__at__(self)
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)

assert c.bar() == c.address


def test_intrinsic_interface_kws(env, make_input_bundle, get_contract):
value = 10**5
lib1 = f"""
@external
@payable
def foo(a: address):
send(a, {value})
"""
main = f"""
import lib1

exports: lib1.__interface__

@external
def bar(a: address):
extcall lib1.__at__(self).foo(a, value={value})
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)
env.set_balance(c.address, value)
original_balance = env.get_balance(env.deployer)
c.bar(env.deployer)
assert env.get_balance(env.deployer) == original_balance + value


def test_intrinsic_interface_defaults(env, make_input_bundle, get_contract):
lib1 = """
@external
@payable
def foo(i: uint256=1) -> uint256:
return i
"""
main = """
import lib1

exports: lib1.__interface__

@external
def bar() -> uint256:
return extcall lib1.__at__(self).foo()
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)
assert c.bar() == 1
34 changes: 33 additions & 1 deletion tests/functional/syntax/modules/test_deploy_visibility.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from vyper.compiler import compile_code
from vyper.exceptions import CallViolation
from vyper.exceptions import CallViolation, UnknownAttribute


def test_call_deploy_from_external(make_input_bundle):
Expand All @@ -25,3 +25,35 @@ def foo():
compile_code(main, input_bundle=input_bundle)

assert e.value.message == "Cannot call an @deploy function from an @external function!"


@pytest.mark.parametrize("interface_syntax", ["__interface__", "__at__"])
def test_module_interface_init(make_input_bundle, tmp_path, interface_syntax):
lib1 = """
#lib1.vy
k: uint256

@external
def bar():
pass

@deploy
def __init__():
self.k = 10
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})

code = f"""
import lib1

@deploy
def __init__():
lib1.{interface_syntax}(self).__init__()
"""

with pytest.raises(UnknownAttribute) as e:
compile_code(code, input_bundle=input_bundle)

# as_posix() for windows tests
lib1_path = (tmp_path / "lib1.vy").as_posix()
assert e.value.message == f"interface {lib1_path} has no member '__init__'."
106 changes: 106 additions & 0 deletions tests/functional/syntax/modules/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,28 @@ def do_xyz():
assert e.value._message == "requested `lib1.ifoo` but `lib1` does not implement `lib1.ifoo`!"


def test_no_export_unimplemented_inline_interface(make_input_bundle):
lib1 = """
interface ifoo:
def do_xyz(): nonpayable

# technically implements ifoo, but missing `implements: ifoo`

@external
def do_xyz():
pass
"""
main = """
import lib1

exports: lib1.ifoo
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
with pytest.raises(InterfaceViolation) as e:
compile_code(main, input_bundle=input_bundle)
assert e.value._message == "requested `lib1.ifoo` but `lib1` does not implement `lib1.ifoo`!"


def test_export_selector_conflict(make_input_bundle):
ifoo = """
@external
Expand Down Expand Up @@ -444,3 +466,87 @@ def __init__():
with pytest.raises(InterfaceViolation) as e:
compile_code(main, input_bundle=input_bundle)
assert e.value._message == "requested `lib1.ifoo` but `lib1` does not implement `lib1.ifoo`!"


def test_export_empty_interface(make_input_bundle, tmp_path):
lib1 = """
def an_internal_function():
pass
"""
main = """
import lib1

exports: lib1.__interface__
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
with pytest.raises(StructureException) as e:
compile_code(main, input_bundle=input_bundle)

# as_posix() for windows
lib1_path = (tmp_path / "lib1.vy").as_posix()
assert e.value._message == f"lib1 (located at `{lib1_path}`) has no external functions!"


def test_invalid_export(make_input_bundle):
lib1 = """
@external
def foo():
pass
"""
main = """
import lib1
a: address

exports: lib1.__interface__(self.a).foo
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})

with pytest.raises(StructureException) as e:
compile_code(main, input_bundle=input_bundle)

assert e.value._message == "invalid export of a value"
assert e.value._hint == "exports should look like <module>.<function | interface>"

main = """
interface Foo:
def foo(): nonpayable

exports: Foo
"""
with pytest.raises(StructureException) as e:
compile_code(main)

assert e.value._message == "invalid export"
assert e.value._hint == "exports should look like <module>.<function | interface>"


@pytest.mark.parametrize("exports_item", ["__at__", "__at__(self)", "__at__(self).__interface__"])
def test_invalid_at_exports(get_contract, make_input_bundle, exports_item):
lib = """
@external
@view
def foo() -> uint256:
return 5
"""

main = f"""
import lib

exports: lib.{exports_item}

@external
@view
def bar() -> uint256:
return staticcall lib.__at__(self).foo()
"""
input_bundle = make_input_bundle({"lib.vy": lib})

with pytest.raises(Exception) as e:
compile_code(main, input_bundle=input_bundle)

if exports_item == "__at__":
assert "not a function or interface" in str(e.value)
if exports_item == "__at__(self)":
assert "invalid exports" in str(e.value)
if exports_item == "__at__(self).__interface__":
assert "has no member '__interface__'" in str(e.value)
Loading
Loading