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

Allow named parameters in mapping types #13384

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open

Conversation

zemse
Copy link

@zemse zemse commented Aug 11, 2022

Closes #11407

  • Allows providing an optional name/identifier just after the key type and the value type.
  • Identifier defaults to empty string "".
  • The optional identifier if provided is set to the "name" field in JSON ABI.
  • Test cases are added for parser and ABI generator

Please let me know of any changes that are needed.

@zemse zemse dismissed stale reviews from ghost via 48df82f Aug 13, 2022
@@ -10,4 +10,6 @@ contract Error2 {
mapping (address => uint balances; // missing ) before "balances"
}
// ----
// ParserError 6635: (417-425): Expected ')' but got identifier
// ParserError 6635: (425-426): Expected ')' but got ';'
Copy link
Author

@zemse zemse Aug 13, 2022

Choose a reason for hiding this comment

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

Some existing error recovery tests change their behavior because a syntax that was illegal before (i.e. ability to place an identifier next to type in mapping) is legal now, hence the parser passes further and generates a different error.

Copy link
Member

@hrkrshnn hrkrshnn left a comment

Looks quite good! Some small comments.

We were wondering with the annotations, if it makes sense to have the function call options syntax for mappings. For example, for mapping (address user => uint balance) balanceOf, would the syntax balanceOf[{user: 0xaddress}] be allowed? It's not needed to implement it in this PR, but worth thinking about.

Also, it's missing a changelog entry.

libsolidity/ast/AST.h Outdated Show resolved Hide resolved
libsolidity/parsing/Parser.cpp Outdated Show resolved Hide resolved
libsolidity/parsing/Parser.cpp Outdated Show resolved Hide resolved
libsolidity/parsing/Parser.cpp Show resolved Hide resolved
test/libsolidity/ABIJson/mapping.sol Show resolved Hide resolved
@hrkrshnn
Copy link
Member

hrkrshnn commented Aug 17, 2022

Can you add a semantic test as well? https://github.com/ethereum/solidity/blob/develop/test/libsolidity/semanticTests/getters/mapping.sol here is an example

@zemse zemse dismissed a stale review via cf2056e Aug 17, 2022
@hrkrshnn
Copy link
Member

hrkrshnn commented Aug 17, 2022

Could you also add an example in the documentation?

@zemse
Copy link
Author

zemse commented Aug 18, 2022

Thanks for the review! I've made the changes.

function call options syntax for mappings

This might be tricky for nested mappings: allowances[{owner: 0xAddr}][{spender: 0xAddr}]. Somehow having it together could be good allowances[{owner: 0xAddr, spender: 0xAddr}], however the names are optional hence this together syntax might not work. But yeah having it makes sense for improving code readability, does not affect any ABI or bytecode. I'd prefer adding it in a separate PR unless it is needed in this PR.

@zemse zemse requested a review from hrkrshnn Aug 18, 2022
@cameel cameel requested a review from nishant-sachdeva Aug 18, 2022
Copy link
Contributor

@Marenz Marenz left a comment

already looking very good.
Additionally to the comments, I am wondering if it makes sense to make it an optional<ASTPointer<ASTString>>.
Would love to hear @cameel and @christianparpart on that topic

docs/types/mapping-types.rst Outdated Show resolved Hide resolved
libsolidity/parsing/Parser.cpp Show resolved Hide resolved
@cameel
Copy link
Member

cameel commented Aug 24, 2022

Additionally to the comments, I am wondering if it makes sense to make it an optional<ASTPointer<ASTString>>.

Depends if we prefer to treat it as a missing name or an empty name. That will show up in the AST.

But overall optional would make it clearer that this is even possible. And since names need to be unique, having to treat "" as an exception is not that great.

Also, I can't see any place in the AST where we'd have a single optional name but in a few cases we do have vectors of names and that's closer to optional than to having an empty string when the name is missing.

@zemse
Copy link
Author

zemse commented Sep 12, 2022

I have rebased over the latest develop branch to fix the conflicts.

Edit: I'm not able to request reviews from multiple people. cc: @hrkrshnn, @Marenz, @cameel, @nishant-sachdeva. (Sorry for tagging).

@zemse zemse requested review from Marenz and hrkrshnn and removed request for hrkrshnn, nishant-sachdeva and Marenz Sep 12, 2022
@hrkrshnn
Copy link
Member

hrkrshnn commented Sep 25, 2022

Will rebase now

Changelog.md Outdated Show resolved Hide resolved
docs/types/mapping-types.rst Outdated Show resolved Hide resolved
// Convert key type to memory.
keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType);

// Convert value type to storage reference.
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType);
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType, keyName, valueName);
Copy link
Member

@hrkrshnn hrkrshnn Sep 25, 2022

Choose a reason for hiding this comment

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

Nit: I would have preferred the order to be keyType, KeyName, valueType, valueName. But no strong argument on changing this.

libsolidity/ast/AST.h Outdated Show resolved Hide resolved
docs/types/mapping-types.rst Outdated Show resolved Hide resolved
@zemse
Copy link
Author

zemse commented Nov 20, 2022

Summary of the changes done:

  1. Removed the commit ee0fee7 that disallowed param for mapping value types.
  2. Added more tests in 4aedd77.
  3. Rebased over upstream/develop branch. (For comparison of changes before & after the force push, I've pushed backup branch)

}
// ----
// DeclarationError 5609: (45-84): Conflicting parameter name "owner" in mapping.
// DeclarationError 1809: (20-85): Conflicting parameter name "owner" in recursive mapping.
Copy link
Collaborator

@nikola-matic nikola-matic Nov 23, 2022

Choose a reason for hiding this comment

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

Why does this one have a different error code, i.e. 1809 vs 5609? Ah OK, it's a different error in case the nested type is a mapping, so you're differentiating between conflicting key -> key and key -> value names. The error message is the same though, which is perfectly fine, but in that case, the errors should have the same code.

Copy link
Author

@zemse zemse Nov 23, 2022

Choose a reason for hiding this comment

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

Since both these cases serve the same purpose, it might make sense to just have the same error and code in both cases.

so you're differentiating between conflicting key -> key and key -> value names

I did that because the error logic was at different places in the code and I tend to write more descriptive error msg if possible. But here I agree the solidity dev that ends up on this error doesn't benefit much.

@@ -0,0 +1,6 @@
contract test {
struct Person {
mapping(uint phone => mapping(uint call => uint time) callTimes) friends;
Copy link
Collaborator

@nikola-matic nikola-matic Nov 23, 2022

Choose a reason for hiding this comment

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

Adding another test case with an additional level of nesting would be nice, especially if it hits the conflicting parameter error between the root and leaf mapping, e.g.

mapping(uint phone => mapping(uint call => mapping(uint phone => something) leafMapping) callTimes) friends;

Copy link
Author

@zemse zemse Nov 24, 2022

Choose a reason for hiding this comment

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

There was a typo in the logic and this test case caught that. I've updated the fix in commit 7438248.

m_errorReporter.declarationError(
1809_error,
_mapping.location(),
"Conflicting parameter name \"" + keyName + "\" in recursive mapping."
Copy link
Collaborator

@nikola-matic nikola-matic Nov 23, 2022

Choose a reason for hiding this comment

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

Oh yeah, one thing that kinda irks me is this being called a recursive mapping. Nested would be a more appropriate term here.

Copy link
Author

@zemse zemse Nov 24, 2022

Choose a reason for hiding this comment

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

I've just combined the error into single one, removing the "recursive" word itself. So now it looks like: Conflicting parameter name "nameSame" in mapping.. Lmk if we have to change this into something better.

@nikola-matic
Copy link
Collaborator

nikola-matic commented Nov 24, 2022

I've pushed an empty commit, squash and pushed in the hopes of restarting the build successfully, but unfortunately we seem to have CI problems, which will hopefully be resolved soon. In fact, as soon as that happens, I'll restart the build to see whether everything passes, and then I'll resume reviewing.

ASTString childKeyName = childMappingType->keyName();
if (keyName == childKeyName)
{
m_errorReporter.declarationError(
Copy link
Collaborator

@nikola-matic nikola-matic Nov 24, 2022

Choose a reason for hiding this comment

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

The chk_errorcodes step is failing because you can't throw an error with the same code twice, i.e. this one, and the one on line 310; instead you'll have to use a boolean to indicate whether an error needs to be thrown, and then check at the end of the loop iteration and report error if true.

Copy link
Author

@zemse zemse Nov 25, 2022

Choose a reason for hiding this comment

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

I've just pushed a change for this.

Co-authored-by: Nikola Matić <nikola.matic@ethereum.org>
if (keyName == currentValueName)
{
isError = true;
break;
}
Copy link
Collaborator

@nikola-matic nikola-matic Nov 25, 2022

Choose a reason for hiding this comment

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

Suggested change
if (keyName == currentValueName)
{
isError = true;
break;
}
if (keyName == currentValueName)
isError = true;

libsolidity/analysis/DeclarationTypeChecker.cpp Outdated Show resolved Hide resolved
@@ -0,0 +1,7 @@
contract test {
struct Person {
mapping(uint nameSame => mapping(uint name1 => mapping(uint nameSame => uint name2) name3) name4) name5;
Copy link
Collaborator

@nikola-matic nikola-matic Nov 25, 2022

Choose a reason for hiding this comment

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

Another three-level nested mapping test, but this time valid, please :)

Copy link
Author

@zemse zemse Nov 26, 2022

Choose a reason for hiding this comment

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

I've added that, please review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

Allow specifying parameter name in public mappings
7 participants