-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Reports an error on unimplemented modifiers in a function definition. #11471
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
Conversation
|
So your fix is to require an overriding modifier to be implemented, right? Is this the same requirement for functions? If yes, can we somehow to do it in a uniform manner? |
|
@chriseth For functions the rule seems to be that you cannot override an implemented function with an unimplemented one unless:
If you manage to override it, you can call such an unimplemented function it in two cases:
@christianparpart Looks like this breaks quite a few tests. I think that we need to allow at least cases where the modifier is used in its own (abstract) contract but only implemented in one that inherits from it ( Also, you should add some new tests, including the one from the issue. I'd also add some where you explicitly reference the unimplemented modifier via its contract name (we do not have a test for that). |
2dc8dbc to
106f113
Compare
f000d1d to
4047f80
Compare
| contract A { | ||
| modifier m() virtual { _; } | ||
| } | ||
| abstract contract B is A { | ||
| modifier m() virtual override; | ||
| } | ||
| contract C is B { | ||
| function f() m 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.
I think we could use some positive test cases too (unless they already exist somewhere):
Overriding unimplemented with unimplemented:
abstract contract A {
modifier m() virtual;
}
abstract contract B is A {
modifier m() virtual override;
}
abstract contract C is B {
modifier m() virtual override;
function f() m public {}
}Leaving a modifier somewhere in the chain unimplemented through multiple inheritance:
contract A {
modifier m() virtual { _; }
}
abstract contract B {
modifier m() virtual;
}
contract C is A, B {
modifier m() override(A, B) { _; }
function f() m public {}
}Use of an unimplemented modifier in an abstract contract:
(EDIT: Replaced A.m with m. Also, this should not be an error after all)
abstract contract A {
modifier m() virtual;
function f() m public {}
}
contract B is A {
modifier m() virtual override { _; }
}Use of an unimplemented modifier on a method that gets overridden:
(EDIT: Replaced A.m with m, changed B to abstract and removed m override from B)
abstract contract A {
modifier m() virtual;
function f() m public virtual {}
}
abstract contract B is A {
function f() public override {}
}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.
What about these? Could you add them as tests?
All but the second one are currently errors (Cannot call unimplemented modifier.). I think they should all work without errors because equivalent examples for virtual functions do pass.
1d9d855 to
ff886e7
Compare
libsolidity/analysis/TypeChecker.cpp
Outdated
| m_errorReporter.typeError( | ||
| 9113_error, | ||
| _modifier.location(), | ||
| "Cannot call unimplemented modifier." |
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 think this depends on whether the lookup is virtual or static.
ff886e7 to
bca764a
Compare
cameel
left a 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.
Most of the comments below are cosmetic issues but there's also one that's a blocker in my opinion: I think that the cases I listed in #11471 (comment) are not handled correctly. They result in errors in cases that worked before and should still work because they work for functions.
libsolidity/analysis/TypeChecker.cpp
Outdated
| auto const* modifierContract = dynamic_cast<ContractDefinition const*>(modifierDecl->scope()); | ||
| solAssert(modifierContract, ""); |
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.
This could be moved into the if below.
libsolidity/analysis/TypeChecker.cpp
Outdated
| if (m_currentContract->derivesFrom(*modifierDecl->annotation().contract) && | ||
| !modifierDecl->isImplemented() | ||
| ) |
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.
| if (m_currentContract->derivesFrom(*modifierDecl->annotation().contract) && | |
| !modifierDecl->isImplemented() | |
| ) | |
| if ( | |
| m_currentContract->derivesFrom(*modifierDecl->annotation().contract) && | |
| !modifierDecl->isImplemented() | |
| ) |
libsolidity/analysis/TypeChecker.cpp
Outdated
| m_errorReporter.typeError( | ||
| 9428_error, | ||
| _modifier.location(), | ||
| "Can only use modifiers defined in the current contract or in base contracts." | ||
| ); |
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.
| m_errorReporter.typeError( | |
| 9428_error, | |
| _modifier.location(), | |
| "Can only use modifiers defined in the current contract or in base contracts." | |
| ); | |
| m_errorReporter.typeError( | |
| 9428_error, | |
| _modifier.location(), | |
| "Can only use modifiers defined in the current contract or in base contracts." | |
| ); |
libsolidity/analysis/TypeChecker.cpp
Outdated
| "Can only use modifiers defined in the current contract or in base contracts." | ||
| ); | ||
|
|
||
| if (m_currentContract->derivesFrom(*modifierDecl->annotation().contract) && |
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.
Isn't this really equivalent to contains(m_currentContract->annotation().linearizedBaseContracts, modifierContract) used above? I think this if could be just else for the if above, with the !modifierDecl->isImplemented() check inside.
| // SPDX-License-Identifier: UNLICENSED | ||
| pragma solidity >= 0.5.0; | ||
|
|
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.
We typically don't include these in tests. They get added automatically by the test suite.
And why specifically 0.5.0? The B.m call syntax for modifiers wasn't added until 0.7.x.
| contract A { | ||
| modifier m() virtual { _; } | ||
| } | ||
| abstract contract B is A { | ||
| modifier m() virtual override; | ||
| } | ||
| contract C is B { | ||
| function f() m 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.
What about these? Could you add them as tests?
All but the second one are currently errors (Cannot call unimplemented modifier.). I think they should all work without errors because equivalent examples for virtual functions do pass.
test/libsolidity/syntaxTests/modifiers/modifier_call_unimplemented_2.sol
Outdated
Show resolved
Hide resolved
bc74f07 to
9c698a3
Compare
|
Needs test updates. |
9c698a3 to
8234fa1
Compare
8aaf5a1 to
8234fa1
Compare
|
I have changed the first comment because now this no longer completely fixes #11468. |
|
I think @christianparpart wanted to create a new issue for the other part. |
cameel
left a 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 think it would be better to still include test cases from #11471 (comment) in this PR. If the PR changes the behavior for any of them, then it would be a non-backwards compatible change.
8234fa1 to
a14ac19
Compare
Partial fix for #11468
(some tests are still failing though. working on it).
I'll squash commits once 👍'd