mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #1264 from ethereum/988
State variable under contract's name
This commit is contained in:
commit
e00a4b47c0
@ -16,7 +16,8 @@ Bugfixes:
|
|||||||
* Type checker: fixed crash related to invalid literal numbers.
|
* Type checker: fixed crash related to invalid literal numbers.
|
||||||
* Type Checker: ``super.x`` does not look up ``x`` in the current contract.
|
* Type Checker: ``super.x`` does not look up ``x`` in the current contract.
|
||||||
* Code generator: expect zero stack increase after ``super`` as an expression.
|
* Code generator: expect zero stack increase after ``super`` as an expression.
|
||||||
* Code Generator: fixed an internal compiler error for ``L.Foo`` for ``enum Foo`` defined in library ``L``.
|
* Code Generator: fix an internal compiler error for ``L.Foo`` for ``enum Foo`` defined in library ``L``.
|
||||||
|
* Code generator: allow inheritance of ``enum`` definitions.
|
||||||
* Inline assembly: support the ``address`` opcode.
|
* Inline assembly: support the ``address`` opcode.
|
||||||
* Inline assembly: fix parsing of assignment after a label.
|
* Inline assembly: fix parsing of assignment after a label.
|
||||||
* Inline assembly: external variables of unsupported type (such as ``this``, ``super``, etc.)
|
* Inline assembly: external variables of unsupported type (such as ``this``, ``super``, etc.)
|
||||||
|
@ -1386,6 +1386,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
}
|
}
|
||||||
else if (exprType->category() == Type::Category::FixedBytes)
|
else if (exprType->category() == Type::Category::FixedBytes)
|
||||||
annotation.isLValue = false;
|
annotation.isLValue = false;
|
||||||
|
else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType.get()))
|
||||||
|
{
|
||||||
|
if (ContractType const* contractType = dynamic_cast<decltype(contractType)>(typeType->actualType().get()))
|
||||||
|
annotation.isLValue = annotation.referencedDeclaration->isLValue();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -890,6 +890,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
else if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
|
appendVariable(*variable, static_cast<Expression const&>(_memberAccess));
|
||||||
else
|
else
|
||||||
_memberAccess.expression().accept(*this);
|
_memberAccess.expression().accept(*this);
|
||||||
}
|
}
|
||||||
@ -1203,15 +1205,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
|
m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
|
||||||
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
{
|
appendVariable(*variable, static_cast<Expression const&>(_identifier));
|
||||||
if (!variable->isConstant())
|
|
||||||
setLValueFromDeclaration(*declaration, _identifier);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
variable->value()->accept(*this);
|
|
||||||
utils().convertType(*variable->value()->annotation().type, *variable->annotation().type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
if (contract->isLibrary())
|
if (contract->isLibrary())
|
||||||
@ -1646,6 +1640,17 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
|
|||||||
utils().storeInMemoryDynamic(_expectedType);
|
utils().storeInMemoryDynamic(_expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression)
|
||||||
|
{
|
||||||
|
if (!_variable.isConstant())
|
||||||
|
setLValueFromDeclaration(_variable, _expression);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_variable.value()->accept(*this);
|
||||||
|
utils().convertType(*_variable.value()->annotation().type, *_variable.annotation().type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
|
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
|
||||||
{
|
{
|
||||||
if (m_context.isLocalVariable(&_declaration))
|
if (m_context.isLocalVariable(&_declaration))
|
||||||
|
@ -103,6 +103,8 @@ private:
|
|||||||
/// expected to be on the stack and is updated by this call.
|
/// expected to be on the stack and is updated by this call.
|
||||||
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
|
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
|
||||||
|
|
||||||
|
/// Appends code for a variable that might be a constant or not
|
||||||
|
void appendVariable(VariableDeclaration const& _variable, Expression const& _expression);
|
||||||
/// Sets the current LValue to a new one (of the appropriate type) from the given declaration.
|
/// Sets the current LValue to a new one (of the appropriate type) from the given declaration.
|
||||||
/// Also retrieves the value if it was not requested by @a _expression.
|
/// Also retrieves the value if it was not requested by @a _expression.
|
||||||
void setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression);
|
void setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression);
|
||||||
|
@ -5666,6 +5666,120 @@ BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable)
|
|||||||
BOOST_CHECK(callContractFunction("ticketPrice()") == encodeArgs(u256(555)));
|
BOOST_CHECK(callContractFunction("ticketPrice()") == encodeArgs(u256(555)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(state_variable_under_contract_name)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract Scope {
|
||||||
|
uint stateVar = 42;
|
||||||
|
|
||||||
|
function getStateVar() constant returns (uint stateVar) {
|
||||||
|
stateVar = Scope.stateVar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(text);
|
||||||
|
BOOST_CHECK(callContractFunction("getStateVar()") == encodeArgs(u256(42)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(state_variable_local_variable_mixture)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract A {
|
||||||
|
uint x = 1;
|
||||||
|
uint y = 2;
|
||||||
|
function a() returns (uint x) {
|
||||||
|
x = A.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inherited_function) {
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract A { function f() internal returns (uint) { return 1; } }
|
||||||
|
contract B is A {
|
||||||
|
function f() internal returns (uint) { return 2; }
|
||||||
|
function g() returns (uint) {
|
||||||
|
return A.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "B");
|
||||||
|
BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inherited_function_from_a_library) {
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
library A { function f() internal returns (uint) { return 1; } }
|
||||||
|
contract B {
|
||||||
|
function f() internal returns (uint) { return 2; }
|
||||||
|
function g() returns (uint) {
|
||||||
|
return A.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "B");
|
||||||
|
BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inherited_constant_state_var)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract A {
|
||||||
|
uint constant x = 7;
|
||||||
|
}
|
||||||
|
contract B is A {
|
||||||
|
function f() returns (uint) {
|
||||||
|
return A.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "B");
|
||||||
|
BOOST_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() returns (uint) {
|
||||||
|
return A.x;
|
||||||
|
}
|
||||||
|
function b() returns (uint) {
|
||||||
|
return B.x;
|
||||||
|
}
|
||||||
|
function a_set(uint _x) returns (uint) {
|
||||||
|
A.x = _x;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
function b_set(uint _x) returns (uint) {
|
||||||
|
B.x = _x;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("b()") == encodeArgs(u256(9)));
|
||||||
|
BOOST_CHECK(callContractFunction("a_set(uint256)", u256(1)) == encodeArgs(u256(1)));
|
||||||
|
BOOST_CHECK(callContractFunction("b_set(uint256)", u256(3)) == encodeArgs(u256(1)));
|
||||||
|
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1)));
|
||||||
|
BOOST_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"(
|
||||||
|
@ -1036,6 +1036,19 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
|
|||||||
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
|
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(missing_state_variable)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract Scope {
|
||||||
|
function getStateVar() constant returns (uint stateVar) {
|
||||||
|
stateVar = Scope.stateVar; // should fail.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor)
|
BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor)
|
||||||
{
|
{
|
||||||
// test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126
|
// test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126
|
||||||
|
Loading…
Reference in New Issue
Block a user