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]: support flags from imported interfaces #4253

Merged
merged 20 commits into from
Oct 15, 2024

Conversation

tserg
Copy link
Collaborator

@tserg tserg commented Sep 22, 2024

What I did

Resolves #4121

How I did it

How to verify it

Commit message

this commit allows flag types to be imported from `.vyi` interface
files.

Description for the changelog

Cute Animal Picture

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

Copy link

codecov bot commented Sep 22, 2024

Codecov Report

Attention: Patch coverage is 56.52174% with 10 lines in your changes missing coverage. Please review.

Project coverage is 51.30%. Comparing base (6bcdb00) to head (482f306).

Files with missing lines Patch % Lines
vyper/semantics/types/module.py 56.52% 8 Missing and 2 partials ⚠️

❗ There is a different number of reports uploaded between BASE (6bcdb00) and HEAD (482f306). Click for more details.

HEAD has 117 uploads less than BASE
Flag BASE (6bcdb00) HEAD (482f306)
140 23
Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4253       +/-   ##
===========================================
- Coverage   91.40%   51.30%   -40.10%     
===========================================
  Files         112      112               
  Lines       15919    15925        +6     
  Branches     2694     2698        +4     
===========================================
- Hits        14551     8171     -6380     
- Misses        934     7135     +6201     
- Partials      434      619      +185     

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

constants = [
(node.target.id, node.target._metadata["varinfo"])
for node in module_t.variable_decls
if node.target._metadata["varinfo"].modifiability is Modifiability.CONSTANT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just check node.is_constant

) -> None:
validate_unique_method_ids(list(functions.values()))

members = functions | events | structs
constants = constants if constants else {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constants or {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh we should do this for all the other things too, since in most cases we end up passing an empty dict for most of these parameters

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean to set the default for these params to None? should I do it in this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and yes!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking that since _from_lists is already passing a dictionary (either empty or with values) for all the params, and it is the only caller of the ctor for InterfaceT, maybe we can require a dict instead for all the same params in the ctor for InterfaceT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the changes in 69e5b3e to show what I meant in the previous comment.

Copy link
Member

@charles-cooper charles-cooper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks! can we also add flags into scope? #4121

@tserg tserg marked this pull request as ready for review October 2, 2024 08:48
"""

contract = """
import ifaces
Copy link
Collaborator

@pcaversaccio pcaversaccio Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a question here (without having read the full changelog). If you define a constant in an interface, this constant has the visibility public by default I think (see #4178) and thus if you do implements you must create a getter in the main contract to be compliant? It just confuses me how we handle this properly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider that, but as it is at the moment, constants in an interface are not external by default`. Do we want the behaviour to be public by default?

I can see why that makes sense for consistency but IMO it would be better to not treat constants the same way. The mode of declaring a constant as public is different from that for a function, and also we would not expect the getter of a constant to be part of the interface if it was not explicitly declared as public.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's exactly why I raise this point to highlight the inconsistency here. And I agree with you that it should not be handled the same way, but given the current semantics, it could be interpreted like this. Also, that's why I think having optional external visibility in interfaces is not appropriate really; explicitness is better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that is a good point. the idea here is that constants are like types, they can be imported for use in your contract, but they shouldn't be allowed to be marked external

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we maybe make this more clear via some syntax sugar? An interface, in the context of smart contract programming, signals something "external", and given that now the visibility modifier is even only optional, we should make the scope clear for constant values to avoid any ambiguity.

Copy link
Collaborator

@cyberthirst cyberthirst Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That feels a little hacky.

How about we skip this for 0.4.1 and introduce type aliases in the next release?

something in this spirit:

NUM_ITEMS: alias(uint256) = 5
arr: alias(DynArray[uint256, NUM_ITEMS])

def foo(a: arr):
    pass

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the past, Charles suggested that aliases might also help with this (a bit ugly 😉 ) interface syntax
#4090 (comment)

Copy link
Member

@charles-cooper charles-cooper Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i mean i think if you can use it in type declarations, you should be able to use it like any other constant. e.g.

for i: uint256 in range(MyInterface.NUM_ITEMS):
    ...

Copy link
Member

@charles-cooper charles-cooper Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on offline discussion I think we should keep the changes for flags here, roll back the changes for constants here and punt to future work / a separate PR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm

structs: list = []

return cls._from_lists(node.name, node, functions, events, structs)
return cls._from_lists(node.name, node, functions)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner now, thanks

isinstance(prev, ContractFunctionT)
and isinstance(item, VarInfo)
and item.decl_node.is_public
):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, why is this needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, a public constant would result in a duplicate because it would be in constants and its public getter would be in functions.

return TYPE_T(self._helper.get_member(attr, node))
# get an event, struct or constant from this interface
type_member = self._helper.get_member(attr, node)
if isinstance(type_member, (EventT, FlagT, StructT)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe

Suggested change
if isinstance(type_member, (EventT, FlagT, StructT)):
if isinstance(type_member, VyperType):

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or UserType might be better

@tserg tserg changed the title feat[ux]: support constants from imported interface feat[ux]: support flags from imported interface Oct 12, 2024
@@ -21,7 +21,7 @@
from vyper.semantics.types.base import TYPE_T, VyperType, is_type_t
from vyper.semantics.types.function import ContractFunctionT
from vyper.semantics.types.primitives import AddressT
from vyper.semantics.types.user import EventT, StructT, _UserType
from vyper.semantics.types.user import EventT, FlagT, StructT, _UserType

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
vyper.semantics.types.user
begins an import cycle.
@charles-cooper charles-cooper changed the title feat[ux]: support flags from imported interface feat[ux]: support flags from imported interfaces Oct 15, 2024
@charles-cooper charles-cooper changed the title feat[ux]: support flags from imported interfaces feat[lang]: support flags from imported interfaces Oct 15, 2024
@charles-cooper charles-cooper enabled auto-merge (squash) October 15, 2024 01:33
Copy link
Member

@charles-cooper charles-cooper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!

@charles-cooper charles-cooper merged commit 990a6fa into vyperlang:master Oct 15, 2024
158 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

flag Can't Be Imported
4 participants