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

feat[lang]: add module.__at__() to cast to interface #4090

Merged

Conversation

charles-cooper
Copy link
Member

@charles-cooper charles-cooper commented Jun 1, 2024

What I did

fixes #3943, #3959

How I did it

How to verify it

Commit message

add `module.__at__`, a new `MemberFunctionT`, which allows the user to
cast addresses to a module's interface.

additionally, fix a bug where interfaces defined inline could not
be exported. this is simultaneously fixed as a related bug because
previously, interfaces could come up in export analysis as `InterfaceT`
or `TYPE_T` depending on their provenance. this commit fixes the bug by
making them `TYPE_T` in both imported and inlined provenance.

this also allows `module.__interface__` to be used in export position
by adding it to `ModuleT`'s members. note this has an unwanted side
effect of allowing `module.__interface__` in call position; in other
words, `module.__interface__(<address>)` has the same behavior as
`module.__at__(<address>)` when use as an expression. this can be
addressed in a later refactor.

refactor:
- wrap interfaces in `TYPE_T`
- streamline an `isinstance(t, (VyperType, TYPE_T))` check. TYPE_T` now
  inherits from `VyperType`, so it doesn't need to be listed separately

Description for the changelog

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

allow `module.__interface__` to be used in call position by adding it to
the module membership data structure.

additionally, fix a bug where interfaces defined inline could not be
exported. this is simultaneously fixed as a related bug because
previously, interfaces could come up in export analysis as `InterfaceT`
or `TYPE_T` depending on their provenance. this commit fixes the bug by
making them `TYPE_T` in both imported and inlined provenance.

refactor:
- wrap interfaces in TYPE_T
- streamline an `isinstance(t, (VyperType, TYPE_T))` check.
  `TYPE_T` now inherits from `VyperType`, so it doesn't need to be
  listed separately
there was a test for unimplemented `.vyi` interfaces, this commit adds a
test for unimplemented inline interface for completeness
@charles-cooper charles-cooper marked this pull request as ready for review June 1, 2024 13:45
@cyberthirst
Copy link
Collaborator

we allow for exporting a module with no external functions - i think we should raise in such case:

# main.vy
import lib1

def f(x: Bytes[32*5]) -> uint256:
     k: uint256 = lib1.foo()
     return k


exports: lib1.__interface__

# lib1.vy
@internal
def foo() -> uint256:
    return 1

@cyberthirst
Copy link
Collaborator

import lib1

uses: lib1

@deploy
def __init__():
    lib1.__interface__(self).__init__()


exports: lib1.__interface__

#lib1.vy
k: uint256

@external
def bar():
    pass

@deploy
def __init__():
    self.k = 10
AttributeError: 'NoneType' object has no attribute 'module_t'

@cyberthirst
Copy link
Collaborator

__interface__ can be used in "weird positions:

main.vy
import lib1

#uses: lib1

@deploy
def __init__():
    log lib1.__interface__.Foo(1)
    s: lib1.Structt = lib1.__interface__.Structt(i=1)


exports: lib1.__interface__

#lib1.vy
event Foo:
    i: uint256

struct Structt:
    i: uint256

k: uint256

@external
def bar():
    pass

@deploy
def __init__():
    self.k = 10

@cyberthirst
Copy link
Collaborator

should address #3943, right?

@cyberthirst cyberthirst added this to the v0.4.1 milestone Aug 5, 2024
@cyberthirst
Copy link
Collaborator

with -f annotated_ast i get:

AttributeError: 'FunctionDef' object has no attribute 'node_id'
# main.vy
import lib1

@external
def foo() -> uint256:
    return staticcall lib1.__interface__(self).d()
# lib1.vy
d: public(uint256)

@fubuloubu
Copy link
Member

fubuloubu commented Sep 14, 2024

would it be too much to ask for implicit cast of mod to mod.__interface__? For either v: Mod in argument (convert to Interface to use like v.method(...)) or extcall Mod(v).method(...)

.__interface__ is a little awkward to specify

@charles-cooper
Copy link
Member Author

with -f annotated_ast i get:

AttributeError: 'FunctionDef' object has no attribute 'node_id'
# main.vy
import lib1

@external
def foo() -> uint256:
    return staticcall lib1.__interface__(self).d()
# lib1.vy
d: public(uint256)

on latest (9621397) we get:

vyper.exceptions.UnknownAttribute: tmp/lib1.vy has no member 'd'.

  contract "tmp/main.vy:6", function "foo", line 6:22 
       5 def foo() -> uint256:
  ---> 6     return staticcall lib1.__interface__(self).d()
  -----------------------------^
       7

@charles-cooper
Copy link
Member Author

would it be too much to ask for implicit cast of mod to mod.__interface__? For either v: Mod in argument (convert to Interface to use like v.method(...)) or extcall Mod(v).method(...)

.__interface__ is a little awkward to specify

yes but it's a bit hairy. it will feel ambiguous once we add deploy Mod(...) syntax

@charles-cooper
Copy link
Member Author

we allow for exporting a module with no external functions - i think we should raise in such case:

# main.vy
import lib1

def f(x: Bytes[32*5]) -> uint256:
     k: uint256 = lib1.foo()
     return k


exports: lib1.__interface__

# lib1.vy
@internal
def foo() -> uint256:
    return 1

24ac428

@charles-cooper
Copy link
Member Author

should address #3943, right?

yes. updated the PR description

@@ -19,7 +19,7 @@
)
from vyper.semantics.data_locations import DataLocation
from vyper.semantics.types.base import TYPE_T, VyperType, is_type_t
from vyper.semantics.types.function import ContractFunctionT
from vyper.semantics.types.function import ContractFunctionT, MemberFunctionT

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'ContractFunctionT' may not be defined if module
vyper.semantics.types.function
is imported before module
vyper.semantics.types.module
, as the
definition
of ContractFunctionT occurs after the cyclic
import
of vyper.semantics.types.module.
'ContractFunctionT' may not be defined if module
vyper.semantics.types.function
is imported before module
vyper.semantics.types.module
, as the
definition
of ContractFunctionT occurs after the cyclic
import
of vyper.semantics.types.module.
@@ -19,7 +19,7 @@
)
from vyper.semantics.data_locations import DataLocation
from vyper.semantics.types.base import TYPE_T, VyperType, is_type_t
from vyper.semantics.types.function import ContractFunctionT
from vyper.semantics.types.function import ContractFunctionT, MemberFunctionT

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'MemberFunctionT' may not be defined if module
vyper.semantics.types.function
is imported before module
vyper.semantics.types.module
, as the
definition
of MemberFunctionT occurs after the cyclic
import
of vyper.semantics.types.module.
'MemberFunctionT' may not be defined if module
vyper.semantics.types.function
is imported before module
vyper.semantics.types.module
, as the
definition
of MemberFunctionT occurs after the cyclic
import
of vyper.semantics.types.module.
Copy link

codecov bot commented Nov 19, 2024

Codecov Report

Attention: Patch coverage is 32.50000% with 27 lines in your changes missing coverage. Please review.

Project coverage is 47.70%. Comparing base (b3ea663) to head (9bac423).
Report is 10 commits behind head on master.

Files with missing lines Patch % Lines
vyper/semantics/analysis/module.py 10.00% 9 Missing ⚠️
vyper/codegen/expr.py 20.00% 8 Missing ⚠️
vyper/semantics/types/module.py 54.54% 5 Missing ⚠️
vyper/compiler/output.py 0.00% 1 Missing and 1 partial ⚠️
vyper/semantics/analysis/utils.py 0.00% 0 Missing and 1 partial ⚠️
vyper/semantics/types/base.py 66.66% 1 Missing ⚠️
vyper/semantics/types/function.py 0.00% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (b3ea663) and HEAD (9bac423). Click for more details.

HEAD has 137 uploads less than BASE
Flag BASE (b3ea663) HEAD (9bac423)
138 1
Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4090       +/-   ##
===========================================
- Coverage   91.40%   47.70%   -43.71%     
===========================================
  Files         112      112               
  Lines       15927    16028      +101     
  Branches     2694     2699        +5     
===========================================
- Hits        14558     7646     -6912     
- Misses        935     7749     +6814     
- Partials      434      633      +199     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

@cyberthirst
Copy link
Collaborator

# lib1.vy
def doo():
    pass

# main.vy
import lib1

i: public(lib1.__interface__)

@external
def boo():
    pass

exports: self.i
Error compiling: tests/custom/test.vy
AttributeError: 'NoneType' object has no attribute '_metadata'

During handling of the above exception, another exception occurred:

vyper.exceptions.CompilerPanic: unhandled exception 'NoneType' object has no attribute '_metadata'

  contract "tests/custom/test.vy:9", line 9:0 
       8
  ---> 9 exports: self.i
  -------^


This is an unhandled internal compiler error. Please create an issue on Github to notify the developers!
https://github.com/vyperlang/vyper/issues/new?template=bug.md

@charles-cooper charles-cooper changed the title feat[lang]: allow module intrinsic interface call feat[lang]: add module.__at__ to cast to interface Nov 25, 2024
@charles-cooper charles-cooper changed the title feat[lang]: add module.__at__ to cast to interface feat[lang]: add module.__at__() to cast to interface Nov 25, 2024
@charles-cooper charles-cooper enabled auto-merge (squash) November 25, 2024 16:59
@charles-cooper charles-cooper merged commit f249c93 into vyperlang:master Nov 25, 2024
154 of 155 checks passed
@charles-cooper charles-cooper deleted the fix/interface-intrinsic branch November 25, 2024 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release - must release blocker
Projects
None yet
Development

Successfully merging this pull request may close these issues.

imported types cannot be used in call position
4 participants