Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Compiler Features:

Bugfixes:
* AST: Do not output value of Yul literal if it is not a valid UTF-8 string.
* Code Generator: Fix internal error when super would have to skip an unimplemented function in the virtual resolution order.
* Control Flow Graph: Take internal calls to functions that always revert into account for reporting unused or unassigned variables.
* SMTChecker: Fix internal error on struct constructor with fixed bytes member initialized with string literal.
* SMTChecker: Fix internal error on external calls from the constructor.
Expand Down
12 changes: 10 additions & 2 deletions libsolidity/ast/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
) const
{
solAssert(!isConstructor(), "");

// If we are not doing super-lookup and the function is not virtual, we can stop here.
if (_searchStart == nullptr && !virtualSemantics())
return *this;
Expand All @@ -407,19 +408,26 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(

FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false);

bool foundSearchStart = (_searchStart == nullptr);
for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts)
{
if (_searchStart != nullptr && c != _searchStart)
if (!foundSearchStart && c != _searchStart)
continue;
_searchStart = nullptr;
else
foundSearchStart = true;

for (FunctionDefinition const* function: c->definedFunctions())
if (
function->name() == name() &&
!function->isConstructor() &&
// With super lookup analysis guarantees that there is an implemented function in the chain.
// With virtual lookup there are valid cases where returning an unimplemented one is fine.
(function->isImplemented() || _searchStart == nullptr) &&
FunctionType(*function).asExternallyCallableFunction(false)->hasEqualParameterTypes(*functionType)
)
return *function;
}

solAssert(false, "Virtual function " + name() + " not found.");
return *this; // not reached
}
Expand Down
6 changes: 5 additions & 1 deletion libsolidity/codegen/CompilerContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,11 @@ FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition cons
solAssert(m_mostDerivedContract, "No most derived contract set.");
ContractDefinition const* super = _base.superContract(mostDerivedContract());
solAssert(super, "Super contract not available.");
return _function.resolveVirtual(mostDerivedContract(), super);

FunctionDefinition const& resolvedFunction = _function.resolveVirtual(mostDerivedContract(), super);
solAssert(resolvedFunction.isImplemented(), "");

return resolvedFunction;
}

ContractDefinition const& CompilerContext::mostDerivedContract() const
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/codegen/ContractCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)

bool ContractCompiler::visit(FunctionDefinition const& _function)
{
solAssert(_function.isImplemented(), "");

CompilerContext::LocationSetter locationSetter(m_context, _function);

m_context.startFunction(_function);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ abstract contract I
{
function a() internal view virtual returns(uint256);
}
abstract contract V is I
abstract contract J is I
{
function a() internal view virtual override returns(uint256);
}
abstract contract V is J
{
function b() public view returns(uint256) { return a(); }
}
contract C is V
{
function a() internal view override returns (uint256) { return 42;}
function a() internal view override returns (uint256) { return 42; }
}
// ====
// compileToEwasm: also
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
contract A {
function f() public virtual returns (uint) {
return 42;
}
}

abstract contract I {
function f() external virtual returns (uint);
}

contract B is A, I {
function f() override(A, I) public returns (uint) {
// I.f() is before A.f() in the C3 linearized order
// but it has no implementation.
return super.f();
}
}
// ====
// compileToEwasm: also
// compileViaYul: also
// ----
// f() -> 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
contract A {
function f() public virtual returns (uint) {
return 42;
}
}

interface I {
function f() external returns (uint);
}

contract B is A, I {
function f() override(A, I) public returns (uint) {
// I.f() is before A.f() in the C3 linearized order
// but it has no implementation.
return super.f();
}
}
// ====
// compileToEwasm: also
// compileViaYul: also
// ----
// f() -> 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
abstract contract I {
function a() internal view virtual returns(uint256);
}

abstract contract C is I {
function f() public view returns(uint256) {
return I.a();
}
}

abstract contract D is I {
function f() public view returns(uint256) {
return super.a();
}
}
// ----
// TypeError 7501: (172-177): Cannot call unimplemented base function.
// TypeError 9582: (278-285): Member "a" not found or not visible after argument-dependent lookup in type(contract super D).
Comment on lines +1 to +18
Copy link
Collaborator Author

@cameel cameel Jun 1, 2021

Choose a reason for hiding this comment

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

This test ensures that we'll not hit the final assert in resolveVirtual() when super skips all the unimplemented functions and there are no implemented ones to use.
The error is misleading though. Should just say that the function is not implemented.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
contract A {
function f() public virtual {}
}
abstract contract B {
function f() public virtual;
}
contract C is A, B {
function f() public virtual override(A, B) {
B.f(); // Should not skip over to A.f() just because B.f() has no implementation.
}
}
// ----
// TypeError 7501: (185-190): Cannot call unimplemented base function.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract A {
function f() public virtual {}
}
abstract contract B {
function f() public virtual;
}
contract C is A, B {
function f() public override(A, B) {
super.f(); // super should skip the unimplemented B.f() and call A.f() instead.
}
}
// ----
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract A {
function f() public virtual {}
}
abstract contract B {
function f() public virtual;
}
contract C is A, B {
function f() public override(A, B) {
// This is fine. The unimplemented B.f() is not used.
}
}
// ----
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
contract A {
function f() public virtual {}
}
abstract contract B {
function f() public virtual;
}
abstract contract C is A, B {
function g() public {
f(); // Would call B.f() if we did not require an override in C.
}
}
// ----
// TypeError 6480: (107-243): Derived contract must override function "f". Two or more base classes define function with same name and parameter types.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract A {
function f() public virtual {}
}
abstract contract B is A {
function f() public virtual override;
}
contract C is B {
function f() public virtual override {}
}
// ----
// TypeError 4593: (81-118): Overriding an implemented function with an unimplemented function is not allowed.
Comment on lines +1 to +11
Copy link
Collaborator Author

@cameel cameel Jun 1, 2021

Choose a reason for hiding this comment

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

This shows that the same problem does not exist without multiple inheritance because we just do not allow unimplemented functions between implemented ones in the resolution chain.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
contract C {
modifier m() { _; }
}
contract D is C {
function f() super.m public {
}
}
// ----
// DeclarationError 7920: (74-81): Identifier not found or not unique.
Comment on lines +1 to +9
Copy link
Collaborator Author

@cameel cameel Jun 1, 2021

Choose a reason for hiding this comment

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

It's also not possible with modifiers because they do not have super (though I found a different problem: #11468).