Skip to content

Commit cac39b5

Browse files
committed
More strict override check for data locations.
1 parent a7157a2 commit cac39b5

9 files changed

Lines changed: 66 additions & 20 deletions

File tree

Changelog.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
### 0.8.14 (unreleased)
22

3-
Important Bugfixes:
4-
* Type Checker: Disallow data location change between ``memory`` and ``calldata`` when overriding, except for external functions.
5-
63
Language Features:
74

85

libsolidity/analysis/ContractLevelChecker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace
3939
{
4040

4141
template <class T, class B>
42-
bool hasEqualParameters(T const& _a, B const& _b)
42+
bool hasEqualExternalCallableParameters(T const& _a, B const& _b)
4343
{
4444
return FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes(
4545
*FunctionType(_b).asExternallyCallableFunction(false)
@@ -204,7 +204,7 @@ void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const
204204
SecondarySourceLocation ssl;
205205

206206
for (size_t j = i + 1; j < overloads.size(); ++j)
207-
if (hasEqualParameters(*overloads[i], *overloads[j]))
207+
if (hasEqualExternalCallableParameters(*overloads[i], *overloads[j]))
208208
{
209209
solAssert(
210210
(

libsolidity/analysis/OverrideChecker.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ Token OverrideProxy::functionKind() const
313313
}, m_item);
314314
}
315315

316-
FunctionType const* OverrideProxy::functionType() const
316+
FunctionType const* OverrideProxy::externalFunctionType() const
317317
{
318318
return std::visit(GenericVisitor{
319319
[&](FunctionDefinition const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); },
@@ -322,6 +322,15 @@ FunctionType const* OverrideProxy::functionType() const
322322
}, m_item);
323323
}
324324

325+
FunctionType const* OverrideProxy::specificFunctionType() const
326+
{
327+
return std::visit(GenericVisitor{
328+
[&](FunctionDefinition const* _item) { return TypeProvider::function(*_item); },
329+
[&](VariableDeclaration const*) -> FunctionType const* { solAssert(false, "Requested specifig function type of variable."); return nullptr; },
330+
[&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested specific function type of modifier."); return nullptr; }
331+
}, m_item);
332+
}
333+
325334
ModifierType const* OverrideProxy::modifierType() const
326335
{
327336
return std::visit(GenericVisitor{
@@ -413,7 +422,7 @@ OverrideProxy::OverrideComparator const& OverrideProxy::overrideComparator() con
413422
[&](FunctionDefinition const* _function)
414423
{
415424
vector<string> paramTypes;
416-
for (Type const* t: functionType()->parameterTypes())
425+
for (Type const* t: externalFunctionType()->parameterTypes())
417426
paramTypes.emplace_back(t->richIdentifier());
418427
return OverrideComparator{
419428
_function->name(),
@@ -424,7 +433,7 @@ OverrideProxy::OverrideComparator const& OverrideProxy::overrideComparator() con
424433
[&](VariableDeclaration const* _var)
425434
{
426435
vector<string> paramTypes;
427-
for (Type const* t: functionType()->parameterTypes())
436+
for (Type const* t: externalFunctionType()->parameterTypes())
428437
paramTypes.emplace_back(t->richIdentifier());
429438
return OverrideComparator{
430439
_var->name(),
@@ -589,21 +598,54 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
589598

590599
if (_super.isFunction())
591600
{
592-
FunctionType const* functionType = _overriding.functionType();
593-
FunctionType const* superType = _super.functionType();
601+
FunctionType const* functionType = _overriding.externalFunctionType();
602+
FunctionType const* superType = _super.externalFunctionType();
594603

604+
bool returnTypesDifferAlready = false;
595605
if (_overriding.functionKind() != Token::Fallback)
596606
{
597607
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
598608

599609
if (!functionType->hasEqualReturnTypes(*superType))
610+
{
611+
returnTypesDifferAlready = true;
600612
overrideError(
601613
_overriding,
602614
_super,
603615
4822_error,
604616
"Overriding " + _overriding.astNodeName() + " return types differ.",
605617
"Overridden " + _overriding.astNodeName() + " is here:"
606618
);
619+
}
620+
}
621+
622+
// The override proxy considers calldata and memory the same data location.
623+
// Here we do a more specific check:
624+
// Data locations of parameters and return variables have to match
625+
// unless we have a public function overriding an external one.
626+
if (
627+
_overriding.isFunction() &&
628+
!returnTypesDifferAlready &&
629+
_super.visibility() != Visibility::External &&
630+
_overriding.functionKind() != Token::Fallback
631+
)
632+
{
633+
if (!_overriding.specificFunctionType()->hasEqualParameterTypes(*_super.specificFunctionType()))
634+
overrideError(
635+
_overriding,
636+
_super,
637+
7723_error,
638+
"Data locations of parameters have to be the same when overriding, but they differ.",
639+
"Overridden " + _overriding.astNodeName() + " is here:"
640+
);
641+
if (!_overriding.specificFunctionType()->hasEqualReturnTypes(*_super.specificFunctionType()))
642+
overrideError(
643+
_overriding,
644+
_super,
645+
1443_error,
646+
"Data locations of return variables have to be the same when overriding, but they differ.",
647+
"Overridden " + _overriding.astNodeName() + " is here:"
648+
);
607649
}
608650

609651
// Stricter mutability is always okay except when super is Payable

libsolidity/analysis/OverrideChecker.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ class OverrideProxy
8484
/// @returns receive / fallback / function (only the latter for modifiers and variables);
8585
langutil::Token functionKind() const;
8686

87-
FunctionType const* functionType() const;
87+
/// @returns the externally callable function type
88+
FunctionType const* externalFunctionType() const;
89+
/// @returns the (unmodified) function type
90+
FunctionType const* specificFunctionType() const;
8891
ModifierType const* modifierType() const;
8992

9093
Declaration const* declaration() const;
@@ -101,6 +104,7 @@ class OverrideProxy
101104

102105
/**
103106
* Struct to help comparing override items about whether they override each other.
107+
* Compares functions based on their "externally callable" type.
104108
* Does not produce a total order.
105109
*/
106110
struct OverrideComparator

libsolidity/ast/AST.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
480480
solAssert(isOrdinary(), "");
481481
solAssert(!libraryFunction(), "");
482482

483-
FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false);
483+
// We actually do not want the externally callable function here.
484+
// This is just to add an assertion since the comparison used to be less strict.
485+
FunctionType const* externalFunctionType = TypeProvider::function(*this)->asExternallyCallableFunction(false);
484486

485487
bool foundSearchStart = (_searchStart == nullptr);
486488
for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts)
@@ -495,9 +497,12 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
495497
// With super lookup analysis guarantees that there is an implemented function in the chain.
496498
// With virtual lookup there are valid cases where returning an unimplemented one is fine.
497499
(function->isImplemented() || _searchStart == nullptr) &&
498-
FunctionType(*function).asExternallyCallableFunction(false)->hasEqualParameterTypes(*functionType)
500+
FunctionType(*function).asExternallyCallableFunction(false)->hasEqualParameterTypes(*externalFunctionType)
499501
)
502+
{
503+
solAssert(FunctionType(*function).hasEqualParameterTypes(*TypeProvider::function(*this)));
500504
return *function;
505+
}
501506
}
502507

503508
solAssert(false, "Virtual function " + name() + " not found.");

test/libsolidity/semanticTests/inheritance/dataLocation/external_public_calldata.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ contract B is A {
1414
// ====
1515
// compileViaYul: also
1616
// ----
17-
// f(uint256[]): 0x20, 1, 9 -> 23
18-
// g(uint256[]): 0x20, 1, 9 -> 23
17+
// f(uint256[]): 0x20, 1, 9 -> 9
18+
// g(uint256[]): 0x20, 1, 9 -> 9

test/libsolidity/syntaxTests/inheritance/dataLocation/memory_calldata_internal.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ contract B is A {
1313
}
1414
}
1515
// ----
16-
// TypeError 7792: (279-287): Function has override specified but does not override anything.
17-
// TypeError 3656: (214-336): Contract "B" should be marked as abstract.
16+
// TypeError 7723: (236-334): Data locations of parameters have to be the same when overriding, but they differ.

test/libsolidity/syntaxTests/inheritance/dataLocation/memory_calldata_public.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ contract B is A {
1313
}
1414
}
1515
// ----
16-
// TypeError 7792: (275-283): Function has override specified but does not override anything.
17-
// TypeError 3656: (212-332): Contract "B" should be marked as abstract.
16+
// TypeError 7723: (234-330): Data locations of parameters have to be the same when overriding, but they differ.

tools/solidityUpgrade/Upgrade060.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ void OverridingFunction::endVisit(ContractDefinition const& _contract)
7272
{
7373
auto& super = (*begin);
7474
auto functionType = FunctionType(*function).asExternallyCallableFunction(false);
75-
auto superType = super.functionType()->asExternallyCallableFunction(false);
75+
auto superType = super.externalFunctionType();
7676

7777
if (functionType && functionType->hasEqualParameterTypes(*superType))
7878
{

0 commit comments

Comments
 (0)