Skip to content

Conversation

@christianparpart
Copy link
Contributor

@christianparpart christianparpart commented Jun 1, 2021

Partial fix for #11468

(some tests are still failing though. working on it).

I'll squash commits once 👍'd

@chriseth
Copy link
Contributor

chriseth commented Jun 1, 2021

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?

@cameel
Copy link
Collaborator

cameel commented Jun 1, 2021

@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:

  • There is no code actually generated for the call. E.g. because you do it in a function that is itself overriden.
  • (after Make super skip unimplemented functions #11472) You make a call via super and the unimplemented function gets skipped.

@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 (empty_modifier_body.sol). Those are probably not the only ones though.

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).

@christianparpart christianparpart force-pushed the funcdef-unimplemented-modifier branch 4 times, most recently from 2dc8dbc to 106f113 Compare June 7, 2021 12:45
@christianparpart christianparpart marked this pull request as ready for review June 7, 2021 12:45
@christianparpart christianparpart requested a review from cameel June 7, 2021 12:46
@christianparpart christianparpart force-pushed the funcdef-unimplemented-modifier branch 4 times, most recently from f000d1d to 4047f80 Compare June 7, 2021 13:20
@christianparpart christianparpart requested a review from chriseth June 7, 2021 14:09
Comment on lines +1 to +9
contract A {
modifier m() virtual { _; }
}
abstract contract B is A {
modifier m() virtual override;
}
contract C is B {
function f() m public {}
}
Copy link
Collaborator

@cameel cameel Jun 8, 2021

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 {}
}

Copy link
Collaborator

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.

@christianparpart christianparpart force-pushed the funcdef-unimplemented-modifier branch 2 times, most recently from 1d9d855 to ff886e7 Compare June 8, 2021 14:46
m_errorReporter.typeError(
9113_error,
_modifier.location(),
"Cannot call unimplemented modifier."
Copy link
Contributor

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.

@christianparpart christianparpart force-pushed the funcdef-unimplemented-modifier branch from ff886e7 to bca764a Compare June 8, 2021 15:34
Copy link
Collaborator

@cameel cameel left a 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.

Comment on lines 633 to 634
auto const* modifierContract = dynamic_cast<ContractDefinition const*>(modifierDecl->scope());
solAssert(modifierContract, "");
Copy link
Collaborator

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.

Comment on lines 644 to 647
if (m_currentContract->derivesFrom(*modifierDecl->annotation().contract) &&
!modifierDecl->isImplemented()
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (m_currentContract->derivesFrom(*modifierDecl->annotation().contract) &&
!modifierDecl->isImplemented()
)
if (
m_currentContract->derivesFrom(*modifierDecl->annotation().contract) &&
!modifierDecl->isImplemented()
)

Comment on lines 638 to 642
m_errorReporter.typeError(
9428_error,
_modifier.location(),
"Can only use modifiers defined in the current contract or in base contracts."
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
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."
);

"Can only use modifiers defined in the current contract or in base contracts."
);

if (m_currentContract->derivesFrom(*modifierDecl->annotation().contract) &&
Copy link
Collaborator

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.

Comment on lines 1 to 2
// SPDX-License-Identifier: UNLICENSED
pragma solidity >= 0.5.0;

Copy link
Collaborator

@cameel cameel Jun 8, 2021

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.

Comment on lines +1 to +9
contract A {
modifier m() virtual { _; }
}
abstract contract B is A {
modifier m() virtual override;
}
contract C is B {
function f() m public {}
}
Copy link
Collaborator

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.

@christianparpart christianparpart force-pushed the funcdef-unimplemented-modifier branch 3 times, most recently from bc74f07 to 9c698a3 Compare June 9, 2021 09:55
chriseth
chriseth previously approved these changes Jun 9, 2021
@chriseth
Copy link
Contributor

chriseth commented Jun 9, 2021

Needs test updates.

@christianparpart christianparpart force-pushed the funcdef-unimplemented-modifier branch from 9c698a3 to 8234fa1 Compare June 9, 2021 10:22
@chriseth chriseth force-pushed the funcdef-unimplemented-modifier branch 2 times, most recently from 8aaf5a1 to 8234fa1 Compare June 9, 2021 10:28
chriseth
chriseth previously approved these changes Jun 9, 2021
@chriseth chriseth enabled auto-merge June 9, 2021 10:28
@cameel
Copy link
Collaborator

cameel commented Jun 9, 2021

I have changed the first comment because now this no longer completely fixes #11468.

@chriseth
Copy link
Contributor

chriseth commented Jun 9, 2021

I think @christianparpart wanted to create a new issue for the other part.

Copy link
Collaborator

@cameel cameel left a 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.

@chriseth chriseth merged commit e5500b9 into develop Jun 9, 2021
@chriseth chriseth deleted the funcdef-unimplemented-modifier branch June 9, 2021 11:54
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.

4 participants