Merge pull request #7817 from ethereum/bail-on-shadowing-state-vars

Report error on shadowing state variables
This commit is contained in:
chriseth 2019-12-03 21:22:39 +01:00 committed by GitHub
commit 2d42da3b7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 31 additions and 91 deletions

View File

@ -22,6 +22,7 @@ Breaking changes:
* Source mappings: Add "modifier depth" as a fifth field in the source mappings. * Source mappings: Add "modifier depth" as a fifth field in the source mappings.
* AST: Inline assembly is exported as structured JSON instead of plain string. * AST: Inline assembly is exported as structured JSON instead of plain string.
* General: ``private`` cannot be used together with ``virtual``. * General: ``private`` cannot be used together with ``virtual``.
* Inheritance: State variable shadowing is now disallowed.
Language Features: Language Features:
* Allow global enums and structs. * Allow global enums and structs.

View File

@ -38,6 +38,10 @@ This section lists purely syntactic changes that do not affect the behavior of e
If the name contains a dot, its prefix up to the dot may not conflict with any declaration outside the inline If the name contains a dot, its prefix up to the dot may not conflict with any declaration outside the inline
assembly block. assembly block.
* State variable shadowing is now disallowed. A derived contract can only
declare a state variable ``x``, if there is no visible state variable with
the same name in any of its bases.
Semantic Only Changes Semantic Only Changes
===================== =====================

View File

@ -19,6 +19,10 @@ is compiled into the created contract. This means that all internal calls
to functions of base contracts also just use internal function calls to functions of base contracts also just use internal function calls
(``super.f(..)`` will use JUMP and not a message call). (``super.f(..)`` will use JUMP and not a message call).
State variable shadowing is considered as an error. A derived contract can
only declare a state variable ``x``, if there is no visible state variable
with the same name in any of its bases.
The general inheritance system is very similar to The general inheritance system is very similar to
`Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_, `Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_,
especially concerning multiple inheritance, but there are also especially concerning multiple inheritance, but there are also

View File

@ -345,10 +345,6 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
Declaration const* conflictingDeclaration = m_currentScope->conflictingDeclaration(*declaration); Declaration const* conflictingDeclaration = m_currentScope->conflictingDeclaration(*declaration);
solAssert(conflictingDeclaration, ""); solAssert(conflictingDeclaration, "");
// Usual shadowing is not an error
if (dynamic_cast<VariableDeclaration const*>(declaration) && dynamic_cast<VariableDeclaration const*>(conflictingDeclaration))
continue;
// Usual shadowing is not an error // Usual shadowing is not an error
if (dynamic_cast<ModifierDefinition const*>(declaration) && dynamic_cast<ModifierDefinition const*>(conflictingDeclaration)) if (dynamic_cast<ModifierDefinition const*>(declaration) && dynamic_cast<ModifierDefinition const*>(conflictingDeclaration))
continue; continue;

View File

@ -5972,38 +5972,6 @@ BOOST_AUTO_TEST_CASE(invalid_enum_as_external_arg)
ABI_CHECK(callContractFunction("test()"), encodeArgs()); ABI_CHECK(callContractFunction("test()"), encodeArgs());
} }
BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes)
{
// bug #1798
char const* sourceCode = R"(
contract init {
function isOk() public virtual returns (bool) { return false; }
bool public ok = false;
}
contract fix {
function isOk() public virtual returns (bool) { return true; }
bool public ok = true;
}
contract init_fix is init, fix {
function checkOk() public returns (bool) { return ok; }
function isOk() public override (init, fix) returns (bool) { return super.isOk(); }
}
contract fix_init is fix, init {
function checkOk() public returns (bool) { return ok; }
function isOk() public override (init, fix) returns (bool) { return super.isOk(); }
}
)";
compileAndRun(sourceCode, 0, "init_fix");
ABI_CHECK(callContractFunction("isOk()"), encodeArgs(true));
ABI_CHECK(callContractFunction("ok()"), encodeArgs(true));
compileAndRun(sourceCode, 0, "fix_init");
ABI_CHECK(callContractFunction("isOk()"), encodeArgs(false));
ABI_CHECK(callContractFunction("ok()"), encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct) BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
@ -8052,42 +8020,6 @@ BOOST_AUTO_TEST_CASE(inherited_constant_state_var)
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7)));
} }
BOOST_AUTO_TEST_CASE(multiple_inherited_state_vars)
{
char const* sourceCode = R"(
contract A {
uint x = 7;
}
contract B {
uint x = 9;
}
contract C is A, B {
function a() public returns (uint) {
return A.x;
}
function b() public returns (uint) {
return B.x;
}
function a_set(uint _x) public returns (uint) {
A.x = _x;
return 1;
}
function b_set(uint _x) public returns (uint) {
B.x = _x;
return 1;
}
}
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(7)));
ABI_CHECK(callContractFunction("b()"), encodeArgs(u256(9)));
ABI_CHECK(callContractFunction("a_set(uint256)", u256(1)), encodeArgs(u256(1)));
ABI_CHECK(callContractFunction("b_set(uint256)", u256(3)), encodeArgs(u256(1)));
ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1)));
ABI_CHECK(callContractFunction("b()"), encodeArgs(u256(3)));
}
BOOST_AUTO_TEST_CASE(constant_string_literal) BOOST_AUTO_TEST_CASE(constant_string_literal)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(

View File

@ -2,12 +2,10 @@ pragma experimental SMTChecker;
contract Base1 { contract Base1 {
uint x; uint x;
uint private t;
} }
contract Base2 is Base1 { contract Base2 is Base1 {
uint z; uint z;
uint private t;
} }
contract C is Base2 { contract C is Base2 {

View File

@ -9,3 +9,4 @@ contract X is A {
function test2() internal override(A) returns (uint256) {} function test2() internal override(A) returns (uint256) {}
} }
// ---- // ----
// DeclarationError: (171-198): Identifier already declared.

View File

@ -11,4 +11,5 @@ abstract contract X is A, B {
function test() internal override returns (uint256) {} function test() internal override returns (uint256) {}
} }
// ---- // ----
// DeclarationError: (257-284): Identifier already declared.
// TypeError: (226-343): Derived contract must override function "foo". Function with the same name and parameter types defined in two or more base classes. // TypeError: (226-343): Derived contract must override function "foo". Function with the same name and parameter types defined in two or more base classes.

View File

@ -1,5 +1,4 @@
abstract contract A { abstract contract A {
int public testvar;
function foo() internal virtual returns (uint256); function foo() internal virtual returns (uint256);
function test(uint8 _a) internal virtual returns (uint256) {} function test(uint8 _a) internal virtual returns (uint256) {}
} }
@ -18,4 +17,3 @@ contract X is A, B, C, D {
function test() internal override returns (uint256) {} function test() internal override returns (uint256) {}
function foo() internal override(A, B, C, D) returns (uint256) {} function foo() internal override(A, B, C, D) returns (uint256) {}
} }
// ----

View File

@ -20,6 +20,7 @@ abstract contract X is A, B, C, D {
function foo() internal override(A, C, B, B, B, D ,D) virtual returns (uint256); function foo() internal override(A, C, B, B, B, D ,D) virtual returns (uint256);
} }
// ---- // ----
// DeclarationError: (529-556): Identifier already declared.
// TypeError: (599-600): Duplicate contract "D" found in override list of "test". // TypeError: (599-600): Duplicate contract "D" found in override list of "test".
// TypeError: (672-673): Duplicate contract "B" found in override list of "foo". // TypeError: (672-673): Duplicate contract "B" found in override list of "foo".
// TypeError: (675-676): Duplicate contract "B" found in override list of "foo". // TypeError: (675-676): Duplicate contract "B" found in override list of "foo".

View File

@ -1,5 +1,4 @@
abstract contract A { abstract contract A {
int public testvar;
function foo() internal virtual returns (uint256); function foo() internal virtual returns (uint256);
function test(uint8 _a) virtual internal returns (uint256); function test(uint8 _a) virtual internal returns (uint256);
} }
@ -20,5 +19,5 @@ abstract contract X is A, B, C, D {
function foo() internal override(A, C) virtual returns (uint256); function foo() internal override(A, C) virtual returns (uint256);
} }
// ---- // ----
// TypeError: (584-601): Invalid contract specified in override list: C. // TypeError: (563-580): Invalid contract specified in override list: C.
// TypeError: (654-668): Function needs to specify overridden contracts B and D. // TypeError: (633-647): Function needs to specify overridden contracts B and D.

View File

@ -1,7 +0,0 @@
abstract contract A {
int public testvar;
}
abstract contract X is A {
int public override testvar;
}
// ----

View File

@ -1,5 +1,4 @@
abstract contract A { abstract contract A {
int public testvar;
function foo() internal virtual returns (uint256); function foo() internal virtual returns (uint256);
} }
abstract contract B { abstract contract B {
@ -11,4 +10,4 @@ abstract contract X is A, B {
function test() internal override virtual returns (uint256); function test() internal override virtual returns (uint256);
} }
// ---- // ----
// TypeError: (224-347): Derived contract must override function "foo". Function with the same name and parameter types defined in two or more base classes. // TypeError: (203-326): Derived contract must override function "foo". Function with the same name and parameter types defined in two or more base classes.

View File

@ -1,5 +1,4 @@
abstract contract A { abstract contract A {
int public testvar;
function foo() internal virtual returns (uint256); function foo() internal virtual returns (uint256);
function test(uint8 _a) internal virtual returns (uint256); function test(uint8 _a) internal virtual returns (uint256);
} }
@ -22,5 +21,5 @@ abstract contract X is A, B, C, D {
function foo() internal override(MyStruct, ENUM, A, B, C, D) virtual returns (uint256); function foo() internal override(MyStruct, ENUM, A, B, C, D) virtual returns (uint256);
} }
// ---- // ----
// TypeError: (653-661): Expected contract but got struct X.MyStruct. // TypeError: (632-640): Expected contract but got struct X.MyStruct.
// TypeError: (663-667): Expected contract but got enum X.ENUM. // TypeError: (642-646): Expected contract but got enum X.ENUM.

View File

@ -0,0 +1,8 @@
contract A {
uint i;
}
contract B is A {
uint i;
}
// ----
// DeclarationError: (43-49): Identifier already declared.

View File

@ -0,0 +1,6 @@
contract A {
uint private i;
}
contract B is A {
uint i;
}