Skip to content

Commit b7b7e2f

Browse files
committed
FunctionDefinition.resolveVirtual(): Skip unimplemented functions when lookup happens via super
1 parent 8d10d3c commit b7b7e2f

11 files changed

+144
-4
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Compiler Features:
1616

1717
Bugfixes:
1818
* AST: Do not output value of Yul literal if it is not a valid UTF-8 string.
19+
* Code Generator: Fix internal error when super would have to skip an unimplemented function in the virtual resolution order.
1920
* SMTChecker: Fix internal error on struct constructor with fixed bytes member initialized with string literal.
2021
* Source Locations: Properly set source location of scoped blocks.
2122
* Standard JSON: Properly allow the ``inliner`` setting under ``settings.optimizer.details``.

libsolidity/ast/AST.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
397397
) const
398398
{
399399
solAssert(!isConstructor(), "");
400+
400401
// If we are not doing super-lookup and the function is not virtual, we can stop here.
401402
if (_searchStart == nullptr && !virtualSemantics())
402403
return *this;
@@ -407,19 +408,30 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
407408

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

411+
bool foundSearchStart = (_searchStart == nullptr);
410412
for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts)
411413
{
412-
if (_searchStart != nullptr && c != _searchStart)
414+
if (!foundSearchStart && c != _searchStart)
413415
continue;
414-
_searchStart = nullptr;
416+
else
417+
foundSearchStart = true;
418+
415419
for (FunctionDefinition const* function: c->definedFunctions())
416420
if (
417421
function->name() == name() &&
418422
!function->isConstructor() &&
423+
// Lookup via super should skip an unimplemented function because there must be
424+
// another, implemented one behind it (otherwise we would fail at analysis stage).
425+
// For ordinary virtual lookup, on the other hand, the only case in which we might
426+
// encounter an unimplemented function is when there is no implementation at all
427+
// and that only happens if the code passed analysis due to the call never actually
428+
// happening (i.e. it's inside a function that gets overridden).
429+
(function->isImplemented() || _searchStart == nullptr) &&
419430
FunctionType(*function).asExternallyCallableFunction(false)->hasEqualParameterTypes(*functionType)
420431
)
421432
return *function;
422433
}
434+
423435
solAssert(false, "Virtual function " + name() + " not found.");
424436
return *this; // not reached
425437
}

test/libsolidity/semanticTests/functionCall/inheritance/call_unimplemented_base.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ abstract contract I
22
{
33
function a() internal view virtual returns(uint256);
44
}
5-
abstract contract V is I
5+
abstract contract J is I
6+
{
7+
function a() internal view virtual override returns(uint256);
8+
}
9+
abstract contract V is J
610
{
711
function b() public view returns(uint256) { return a(); }
812
}
913
contract C is V
1014
{
11-
function a() internal view override returns (uint256) { return 42;}
15+
function a() internal view override returns (uint256) { return 42; }
1216
}
1317
// ====
1418
// compileToEwasm: also
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
contract A {
2+
function f() public virtual returns (uint) {
3+
return 42;
4+
}
5+
}
6+
7+
abstract contract I {
8+
function f() external virtual returns (uint);
9+
}
10+
11+
contract B is A, I {
12+
function f() override(A, I) public returns (uint) {
13+
// I.f() is before A.f() in the C3 linearized order
14+
// but it has no implementation.
15+
return super.f();
16+
}
17+
}
18+
// ====
19+
// compileToEwasm: also
20+
// compileViaYul: also
21+
// ----
22+
// f() -> 42
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
contract A {
2+
function f() public virtual returns (uint) {
3+
return 42;
4+
}
5+
}
6+
7+
interface I {
8+
function f() external returns (uint);
9+
}
10+
11+
contract B is A, I {
12+
function f() override(A, I) public returns (uint) {
13+
// I.f() is before A.f() in the C3 linearized order
14+
// but it has no implementation.
15+
return super.f();
16+
}
17+
}
18+
// ====
19+
// compileToEwasm: also
20+
// compileViaYul: also
21+
// ----
22+
// f() -> 42
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
abstract contract I {
2+
function a() internal view virtual returns(uint256);
3+
}
4+
5+
abstract contract C is I {
6+
function f() public view returns(uint256) {
7+
return I.a();
8+
}
9+
}
10+
11+
abstract contract D is I {
12+
function f() public view returns(uint256) {
13+
return super.a();
14+
}
15+
}
16+
// ----
17+
// TypeError 7501: (172-177): Cannot call unimplemented base function.
18+
// TypeError 9582: (278-285): Member "a" not found or not visible after argument-dependent lookup in type(contract super D).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
contract A {
2+
function f() public virtual {}
3+
}
4+
abstract contract B {
5+
function f() public virtual;
6+
}
7+
contract C is A, B {
8+
function f() public virtual override(A, B) {
9+
B.f(); // Should not skip over to A.f() just because B.f() has no implementation.
10+
}
11+
}
12+
// ----
13+
// TypeError 7501: (185-190): Cannot call unimplemented base function.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
contract A {
2+
function f() public virtual {}
3+
}
4+
abstract contract B {
5+
function f() public virtual;
6+
}
7+
contract C is A, B {
8+
function f() public override(A, B) {
9+
super.f(); // super should skip the unimplemented B.f() and call A.f() instead.
10+
}
11+
}
12+
// ----
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
contract A {
2+
function f() public virtual {}
3+
}
4+
abstract contract B {
5+
function f() public virtual;
6+
}
7+
contract C is A, B {
8+
function f() public override(A, B) {
9+
// This is fine. The unimplemented B.f() is not used.
10+
}
11+
}
12+
// ----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
contract A {
2+
function f() public virtual {}
3+
}
4+
abstract contract B {
5+
function f() public virtual;
6+
}
7+
abstract contract C is A, B {
8+
function g() public {
9+
f(); // Would call B.f() if we did not require an override in C.
10+
}
11+
}
12+
// ----
13+
// TypeError 6480: (107-243): Derived contract must override function "f". Two or more base classes define function with same name and parameter types.

0 commit comments

Comments
 (0)