Skip to content

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

Merged
charles-cooper merged 35 commits intovyperlang:masterfrom
charles-cooper:fix/interface-intrinsic
Nov 25, 2024
Merged

feat[lang]: add module.__at__() to cast to interface#4090
charles-cooper merged 35 commits intovyperlang:masterfrom
charles-cooper:fix/interface-intrinsic

Conversation

@charles-cooper
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

should address #3943, right?

@cyberthirst cyberthirst added this to the v0.4.1 milestone Aug 5, 2024
@cyberthirst
Copy link
Copy Markdown
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
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member Author

should address #3943, right?

yes. updated the PR description

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

'ContractFunctionT' may not be defined if module [vyper.semantics.types.function](1) is imported before module [vyper.semantics.types.module](2), as the [definition](3) of ContractFunctionT occurs after the cyclic [import](4) of vyper.semantics.types.module. 'ContractFunctionT' may not be defined if module [vyper.semantics.types.function](1) is imported before module [vyper.semantics.types.module](2), as the [definition](3) of ContractFunctionT occurs after the cyclic [import](5) of vyper.semantics.types.module.
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

'MemberFunctionT' may not be defined if module [vyper.semantics.types.function](1) is imported before module [vyper.semantics.types.module](2), as the [definition](3) of MemberFunctionT occurs after the cyclic [import](4) of vyper.semantics.types.module. 'MemberFunctionT' may not be defined if module [vyper.semantics.types.function](1) is imported before module [vyper.semantics.types.module](2), as the [definition](3) of MemberFunctionT occurs after the cyclic [import](5) of vyper.semantics.types.module.
@codecov
Copy link
Copy Markdown

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
Copy Markdown
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
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

5 participants