-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Fix ICE when calling unimplemented base/super function #7987
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
|
I see this is in the 0.6.0 project.. but it doesn't need to be, nothing here is breaking. So if this is blocking feel free to move it to 0.6.1 |
libsolidity/analysis/TypeChecker.cpp
Outdated
| { | ||
| if ( | ||
| !funcDef->isImplemented() && | ||
| contains(resolveDirectBaseContracts(*m_scope), funcDef->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.
Why is it important whether it is a direct base or an indirect base?
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's a good point. I had a wrong assumption here.
| } | ||
| // ---- | ||
| // TypeError: (110-117): Member "f" not found or not visible after argument-dependent lookup in contract super b. | ||
| // TypeError: (110-119): Call to unimplemented function "f". |
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 don't think this should be an error in general. At this point, we cannot tell whether there is a super function or not.
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.
Really? I mean, the check is very specifically checking for this situation? Can you think of a scenario where this message is wrong?
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.
super performs virtual function lookup. This means the lookup is performed differently depending on the final derived contract that is being compiled. In the following example, super.f() should compile for D.
contract A { function f() public pure virtual; }
contract B is A { function f() public pure override virtual { super.f(); } }
contract C is A { function f() public pure override virtual { } }
contract D is B, C { function f() public pure virtual override(B, C) { super.f(); } }
I think we need to discuss what to do if B is to be compiled on its own. I think @ekpyron is of the opinion to remove super altogether.
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 does the super in contract B refer to then? (that's where it would throw an error with 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.
I suppose I can either exclude super from the check, or I set a tag in the annotation and only trigger errors when the final contract was processed... though that could get messy
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.
Are you sure about your example? I think this doesn't even work with the current develop version
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.
Please only fix the non-super case. I said that super.f() should compile. It is a bug that it currently does not compile, but it is a different bug that #7314
| } | ||
| // ---- | ||
| // TypeError: (118-125): Member "f" not found or not visible after argument-dependent lookup in contract super b. | ||
| // TypeError: (118-127): Call to unimplemented function "f". |
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.
Same here.
|
Please do not change the behaviour of |
|
I was doing that in response to
|
|
updated to exclude super |
libsolidity/analysis/TypeChecker.cpp
Outdated
| { | ||
| if ( | ||
| !funcDef->isImplemented() && | ||
| contains(m_scope->annotation().linearizedBaseContracts, funcDef->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.
I don't think this is how we should do it. It does not catch things like
function() internal pure x = a.t;
x();
|
Updated. The check happens now as soon as we access the member. |
bshastry
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.
Some minor comments.
Changelog.md
Outdated
|
|
||
|
|
||
| Bugfixes: | ||
| * Fix compiler error when calling the base function from the derived |
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.
White space instead of tab?
libsolidity/analysis/TypeChecker.cpp
Outdated
| struct SetScopeFunc | ||
| { | ||
| SetScopeFunc(TypeChecker& _checker, FunctionDefinition const* _func) | ||
| :m_checker( _checker) |
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_checker( _checker) | |
| : m_checker(_checker) |
| function f() public override { a.f(); } | ||
| } | ||
| // ---- | ||
| // TypeError: (110-113): Referencing unimplemented function "f". |
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.
Referencing unimplemented function "a.f" would be a nicer error message though.
|
This (valid?) test case throws a different ICE? Perhaps unrelated but documenting it here in any case. throws this |
| if ( | ||
| !funcDef->isImplemented() && | ||
| contains(m_scope->annotation().linearizedBaseContracts, funcDef->annotation().contract) && | ||
| m_scopeFunction->name() == funcDef->name() && |
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.
Is the assumption that the names are the same valid? Please see the test input that I added
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.
One more related comment.
libsolidity/analysis/TypeChecker.cpp
Outdated
| ) | ||
| m_errorReporter.typeError( | ||
| _memberAccess.location(), | ||
| "Referencing unimplemented function \"" + funcDef->name() + "\"." |
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.
| "Referencing unimplemented function \"" + funcDef->name() + "\"." | |
| "Referencing unimplemented function \"" + funcDef->name() + "\" in contract \"" + tt->actualType()->canonicalName() + "\"." |
If you accept this change, test expectations need to be updated.
|
Good finds! Not sure about that extra test case you found. We certainly shouldn't get an ICE, but I want to hear what @chriseth has to say before changing my code |
| { | ||
| if (tt->actualType()->category() == Type::Category::Enum) | ||
| annotation.isPure = true; | ||
| else if (tt->actualType()->category() == Type::Category::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.
This again does not catch function() x = A.f. I think that information has to be added to the type. We should discuss if we want that.
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.
Ah, sorry, that's wrong - there is a test about exactly that :)
|
|
||
| ContractDefinition const* m_scope = nullptr; | ||
|
|
||
| /// Pointer to the function we're currently in |
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.
Why is it important that we are currently inside that function?
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 stems from a misunderstanding from my side. The issue description says
This is a rather specific case - you have to call the non-implemented base function and override the function at the same time.
I thought this means it only happens when you call it from within the function that overrides which is what I am detecting here.
@bshastry newly found test case shows that the problem is more general than this.
|
I think we have to discuss that at a more basic level. Do we want |
|
I would say |
|
Part of my reasoning would be that And for that we'd need to have a specific type for |
|
Ah, yes: for reference: access to selectors via contract names in general is tracked as #3506 and staged for 0.6.2 as well. |
|
@Marenz We now merged #8105 , which introduces a new kind of So: do you want to continue with this based on that, or should I take over this PR as well? |
|
As discussed with @Marenz in chat: I'm taking this over now. |
|
Closing this in favour of #7987 |
fixes #7314
I need to use the
resolveDirectBaseContractsfunction from the override checker. I am currently not sure what the best way to do that is. I could pull it out into a different or extra file.. I could move my check into the override checker.. or something else?