Skip to content

Commit bde0b40

Browse files
authored
Merge pull request #1479 from ethereum/function_variable_mixin
Disallow mixin of functions and attributes under the same name
2 parents 14703ca + abc2442 commit bde0b40

9 files changed

Lines changed: 190 additions & 67 deletions

File tree

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Bugfixes:
88
* Remappings: Prefer longer context over longer prefix.
99
* Type checker, code generator: enable access to events of base contracts' names.
1010
* Imports: ``import ".dir/a"`` is not a relative path. Relative paths begin with directory ``.`` or ``..``.
11+
* Type checker, disallow inheritances of different kinds (e.g. a function and a modifier) of members of the same name
1112

1213
### 0.4.7 (2016-12-15)
1314

docs/contracts.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,13 @@ cannot be resolved.
877877
A simple rule to remember is to specify the base classes in
878878
the order from "most base-like" to "most derived".
879879

880+
Inheriting Different Kinds of Members of the Same Name
881+
======================================================
882+
883+
When the inheritance results in a contract with a function and a modifier of the same name, it is considered as an error.
884+
This error is produced also by an event and a modifier of the same name, and a function and an event of the same name.
885+
As an exception, a state variable accessor can override a public function.
886+
880887
.. index:: ! contract;abstract, ! abstract contract
881888

882889
******************

libsolidity/analysis/DeclarationContainer.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,19 @@ Declaration const* DeclarationContainer::conflictingDeclaration(
4444

4545
if (dynamic_cast<FunctionDefinition const*>(&_declaration))
4646
{
47-
// check that all other declarations with the same name are functions
47+
// check that all other declarations with the same name are functions or a public state variable
4848
for (Declaration const* declaration: declarations)
49-
if (!dynamic_cast<FunctionDefinition const*>(declaration))
49+
{
50+
if (dynamic_cast<FunctionDefinition const*>(declaration))
51+
continue;
52+
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(declaration))
53+
{
54+
if (variableDeclaration->isStateVariable() && !variableDeclaration->isConstant() && variableDeclaration->isPublic())
55+
continue;
5056
return declaration;
57+
}
58+
return declaration;
59+
}
5160
}
5261
else if (declarations.size() == 1 && declarations.front() == &_declaration)
5362
return nullptr;

libsolidity/analysis/NameAndTypeResolver.cpp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,16 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
260260
for (auto it = _declarations.begin(); it != _declarations.end(); ++it)
261261
{
262262
solAssert(*it, "");
263-
// the declaration is functionDefinition while declarations > 1
264-
FunctionDefinition const& functionDefinition = dynamic_cast<FunctionDefinition const&>(**it);
265-
FunctionType functionType(functionDefinition);
266-
for (auto parameter: functionType.parameterTypes() + functionType.returnParameterTypes())
263+
// the declaration is functionDefinition or a VariableDeclaration while declarations > 1
264+
solAssert(dynamic_cast<FunctionDefinition const*>(*it) || dynamic_cast<VariableDeclaration const*>(*it),
265+
"Found overloading involving something not a function or a variable");
266+
267+
shared_ptr<FunctionType const> functionType { (*it)->functionType(false) };
268+
if (!functionType)
269+
functionType = (*it)->functionType(true);
270+
solAssert(functionType, "failed to determine the function type of the overloaded");
271+
272+
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
267273
if (!parameter)
268274
reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context");
269275

@@ -272,8 +278,10 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
272278
uniqueFunctions.end(),
273279
[&](Declaration const* d)
274280
{
275-
FunctionType newFunctionType(dynamic_cast<FunctionDefinition const&>(*d));
276-
return functionType.hasEqualArgumentTypes(newFunctionType);
281+
shared_ptr<FunctionType const> newFunctionType { d->functionType(false) };
282+
if (!newFunctionType)
283+
newFunctionType = d->functionType(true);
284+
return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType);
277285
}
278286
))
279287
uniqueFunctions.push_back(*it);
@@ -289,7 +297,39 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
289297
for (auto const& declaration: nameAndDeclaration.second)
290298
// Import if it was declared in the base, is not the constructor and is visible in derived classes
291299
if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts())
292-
m_currentScope->registerDeclaration(*declaration);
300+
if (!m_currentScope->registerDeclaration(*declaration))
301+
{
302+
SourceLocation firstDeclarationLocation;
303+
SourceLocation secondDeclarationLocation;
304+
Declaration const* conflictingDeclaration = m_currentScope->conflictingDeclaration(*declaration);
305+
solAssert(conflictingDeclaration, "");
306+
307+
// Usual shadowing is not an error
308+
if (dynamic_cast<VariableDeclaration const*>(declaration) && dynamic_cast<VariableDeclaration const*>(conflictingDeclaration))
309+
continue;
310+
311+
// Usual shadowing is not an error
312+
if (dynamic_cast<ModifierDefinition const*>(declaration) && dynamic_cast<ModifierDefinition const*>(conflictingDeclaration))
313+
continue;
314+
315+
if (declaration->location().start < conflictingDeclaration->location().start)
316+
{
317+
firstDeclarationLocation = declaration->location();
318+
secondDeclarationLocation = conflictingDeclaration->location();
319+
}
320+
else
321+
{
322+
firstDeclarationLocation = conflictingDeclaration->location();
323+
secondDeclarationLocation = declaration->location();
324+
}
325+
326+
reportDeclarationError(
327+
secondDeclarationLocation,
328+
"Identifier already declared.",
329+
firstDeclarationLocation,
330+
"The previous declaration is here:"
331+
);
332+
}
293333
}
294334

295335
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)

libsolidity/analysis/TypeChecker.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,8 +1500,23 @@ bool TypeChecker::visit(Identifier const& _identifier)
15001500
if (!annotation.referencedDeclaration)
15011501
{
15021502
if (!annotation.argumentTypes)
1503-
fatalTypeError(_identifier.location(), "Unable to determine overloaded type.");
1504-
if (annotation.overloadedDeclarations.empty())
1503+
{
1504+
// The identifier should be a public state variable shadowing other functions
1505+
vector<Declaration const*> candidates;
1506+
1507+
for (Declaration const* declaration: annotation.overloadedDeclarations)
1508+
{
1509+
if (VariableDeclaration const* variableDeclaration = dynamic_cast<decltype(variableDeclaration)>(declaration))
1510+
candidates.push_back(declaration);
1511+
}
1512+
if (candidates.empty())
1513+
fatalTypeError(_identifier.location(), "No matching declaration found after variable lookup.");
1514+
else if (candidates.size() == 1)
1515+
annotation.referencedDeclaration = candidates.front();
1516+
else
1517+
fatalTypeError(_identifier.location(), "No unique declaration found after variable lookup.");
1518+
}
1519+
else if (annotation.overloadedDeclarations.empty())
15051520
fatalTypeError(_identifier.location(), "No candidates for overload resolution found.");
15061521
else if (annotation.overloadedDeclarations.size() == 1)
15071522
annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin();

libsolidity/ast/AST.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,45 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
274274
return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
275275
}
276276

277+
shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const
278+
{
279+
if (_internal)
280+
{
281+
switch (visibility())
282+
{
283+
case Declaration::Visibility::Default:
284+
solAssert(false, "visibility() should not return Default");
285+
case Declaration::Visibility::Private:
286+
case Declaration::Visibility::Internal:
287+
case Declaration::Visibility::Public:
288+
return make_shared<FunctionType>(*this, _internal);
289+
case Declaration::Visibility::External:
290+
return {};
291+
default:
292+
solAssert(false, "visibility() should not return a Visibility");
293+
}
294+
}
295+
else
296+
{
297+
switch (visibility())
298+
{
299+
case Declaration::Visibility::Default:
300+
solAssert(false, "visibility() should not return Default");
301+
case Declaration::Visibility::Private:
302+
case Declaration::Visibility::Internal:
303+
return {};
304+
case Declaration::Visibility::Public:
305+
case Declaration::Visibility::External:
306+
return make_shared<FunctionType>(*this, _internal);
307+
default:
308+
solAssert(false, "visibility() should not return a Visibility");
309+
}
310+
}
311+
312+
// To make the compiler happy
313+
return {};
314+
}
315+
277316
TypePointer FunctionDefinition::type() const
278317
{
279318
return make_shared<FunctionType>(*this);
@@ -308,6 +347,14 @@ TypePointer EventDefinition::type() const
308347
return make_shared<FunctionType>(*this);
309348
}
310349

350+
std::shared_ptr<FunctionType> EventDefinition::functionType(bool _internal) const
351+
{
352+
if (_internal)
353+
return make_shared<FunctionType>(*this);
354+
else
355+
return {};
356+
}
357+
311358
EventDefinitionAnnotation& EventDefinition::annotation() const
312359
{
313360
if (!m_annotation)
@@ -365,6 +412,28 @@ TypePointer VariableDeclaration::type() const
365412
return annotation().type;
366413
}
367414

415+
shared_ptr<FunctionType> VariableDeclaration::functionType(bool _internal) const
416+
{
417+
if (_internal)
418+
return {};
419+
switch (visibility())
420+
{
421+
case Declaration::Visibility::Default:
422+
solAssert(false, "visibility() should not return Default");
423+
case Declaration::Visibility::Private:
424+
case Declaration::Visibility::Internal:
425+
return {};
426+
case Declaration::Visibility::Public:
427+
case Declaration::Visibility::External:
428+
return make_shared<FunctionType>(*this);
429+
default:
430+
solAssert(false, "visibility() should not return a Visibility");
431+
}
432+
433+
// To make the compiler happy
434+
return {};
435+
}
436+
368437
VariableDeclarationAnnotation& VariableDeclaration::annotation() const
369438
{
370439
if (!m_annotation)

libsolidity/ast/AST.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ class Declaration: public ASTNode
171171
/// This can only be called once types of variable declarations have already been resolved.
172172
virtual TypePointer type() const = 0;
173173

174+
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
175+
/// @returns null when it is not accessible as a function.
176+
virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const { return {}; }
177+
174178
protected:
175179
virtual Visibility defaultVisibility() const { return Visibility::Public; }
176180

@@ -581,6 +585,10 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public
581585

582586
virtual TypePointer type() const override;
583587

588+
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
589+
/// @returns null when it is not accessible as a function.
590+
virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override;
591+
584592
virtual FunctionDefinitionAnnotation& annotation() const override;
585593

586594
private:
@@ -643,6 +651,10 @@ class VariableDeclaration: public Declaration
643651

644652
virtual TypePointer type() const override;
645653

654+
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
655+
/// @returns null when it is not accessible as a function.
656+
virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override;
657+
646658
virtual VariableDeclarationAnnotation& annotation() const override;
647659

648660
protected:
@@ -740,6 +752,7 @@ class EventDefinition: public CallableDeclaration, public Documented
740752
bool isAnonymous() const { return m_anonymous; }
741753

742754
virtual TypePointer type() const override;
755+
virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override;
743756

744757
virtual EventDefinitionAnnotation& annotation() const override;
745758

test/libsolidity/SolidityEndToEndTest.cpp

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4869,60 +4869,6 @@ BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes)
48694869
BOOST_CHECK(callContractFunction("ok()") == encodeArgs(false));
48704870
}
48714871

4872-
BOOST_AUTO_TEST_CASE(proper_overwriting_accessor_by_function)
4873-
{
4874-
// bug #1798
4875-
char const* sourceCode = R"(
4876-
contract attribute {
4877-
bool ok = false;
4878-
}
4879-
contract func {
4880-
function ok() returns (bool) { return true; }
4881-
}
4882-
4883-
contract attr_func is attribute, func {
4884-
function checkOk() returns (bool) { return ok(); }
4885-
}
4886-
contract func_attr is func, attribute {
4887-
function checkOk() returns (bool) { return ok; }
4888-
}
4889-
)";
4890-
compileAndRun(sourceCode, 0, "attr_func");
4891-
BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true));
4892-
compileAndRun(sourceCode, 0, "func_attr");
4893-
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(false));
4894-
}
4895-
4896-
4897-
BOOST_AUTO_TEST_CASE(overwriting_inheritance)
4898-
{
4899-
// bug #1798
4900-
char const* sourceCode = R"(
4901-
contract A {
4902-
function ok() returns (uint) { return 1; }
4903-
}
4904-
contract B {
4905-
function ok() returns (uint) { return 2; }
4906-
}
4907-
contract C {
4908-
uint ok = 6;
4909-
}
4910-
contract AB is A, B {
4911-
function ok() returns (uint) { return 4; }
4912-
}
4913-
contract reversedE is C, AB {
4914-
function checkOk() returns (uint) { return ok(); }
4915-
}
4916-
contract E is AB, C {
4917-
function checkOk() returns (uint) { return ok; }
4918-
}
4919-
)";
4920-
compileAndRun(sourceCode, 0, "reversedE");
4921-
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(4));
4922-
compileAndRun(sourceCode, 0, "E");
4923-
BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6));
4924-
}
4925-
49264872
BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct)
49274873
{
49284874
char const* sourceCode = R"(

test/libsolidity/SolidityNameAndTypeResolution.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,9 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function)
10561056
contract A { modifier mod(uint a) { _; } }
10571057
contract B is A { function mod(uint a) { } }
10581058
)";
1059-
CHECK_ERROR(text, TypeError, "");
1059+
// Error: Identifier already declared.
1060+
// Error: Override changes modifier to function.
1061+
CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "");
10601062
}
10611063

10621064
BOOST_AUTO_TEST_CASE(function_overrides_modifier)
@@ -1065,7 +1067,9 @@ BOOST_AUTO_TEST_CASE(function_overrides_modifier)
10651067
contract A { function mod(uint a) { } }
10661068
contract B is A { modifier mod(uint a) { _; } }
10671069
)";
1068-
CHECK_ERROR(text, TypeError, "");
1070+
// Error: Identifier already declared.
1071+
// Error: Override changes function to modifier.
1072+
CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "");
10691073
}
10701074

10711075
BOOST_AUTO_TEST_CASE(modifier_returns_value)
@@ -4304,6 +4308,25 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
43044308
CHECK_ERROR(text, TypeError, "");
43054309
}
43064310

4311+
BOOST_AUTO_TEST_CASE(function_variable_mixin)
4312+
{
4313+
// bug #1798 (cpp-ethereum), related to #1286 (solidity)
4314+
char const* text = R"(
4315+
contract attribute {
4316+
bool ok = false;
4317+
}
4318+
contract func {
4319+
function ok() returns (bool) { return true; }
4320+
}
4321+
4322+
contract attr_func is attribute, func {
4323+
function checkOk() returns (bool) { return ok(); }
4324+
}
4325+
)";
4326+
CHECK_ERROR(text, DeclarationError, "");
4327+
}
4328+
4329+
43074330
BOOST_AUTO_TEST_CASE(payable_constant_conflict)
43084331
{
43094332
char const* text = R"(

0 commit comments

Comments
 (0)