-
-
Notifications
You must be signed in to change notification settings - Fork 802
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
Conversation
Codecov ReportAttention: Patch coverage is
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. |
vyper/semantics/types/module.py
Outdated
constants = [ | ||
(node.target.id, node.target._metadata["varinfo"]) | ||
for node in module_t.variable_decls | ||
if node.target._metadata["varinfo"].modifiability is Modifiability.CONSTANT |
There was a problem hiding this comment.
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
vyper/semantics/types/module.py
Outdated
) -> None: | ||
validate_unique_method_ids(list(functions.values())) | ||
|
||
members = functions | events | structs | ||
constants = constants if constants else {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constants or {}
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and yes!
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this 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
…/interface_const
""" | ||
|
||
contract = """ | ||
import ifaces |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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):
...
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
much cleaner now, thanks
vyper/semantics/types/module.py
Outdated
isinstance(prev, ContractFunctionT) | ||
and isinstance(item, VarInfo) | ||
and item.decl_node.is_public | ||
): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
vyper/semantics/types/module.py
Outdated
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)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe
if isinstance(type_member, (EventT, FlagT, StructT)): | |
if isinstance(type_member, VyperType): |
There was a problem hiding this comment.
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
…into feat/interface_const
…/interface_const
…/interface_const
@@ -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
vyper.semantics.types.user
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you!
What I did
Resolves #4121
How I did it
How to verify it
Commit message
Description for the changelog
Cute Animal Picture