Merge pull request #345 from VoR0220/develop

Inline Arrays
This commit is contained in:
chriseth 2016-01-12 19:01:35 +01:00
commit 02c1aacd25
5 changed files with 294 additions and 31 deletions

View File

@ -317,7 +317,8 @@ by `msg.data`.
Can state variables be initialized in-line? Can state variables be initialized in-line?
=========================================== ===========================================
Yes, this is possible for most types (even for structs), but not for arrays. Yes, this is possible for all types (even for structs). However, for arrays it
should be noted that you must declare them as static memory arrays.
Examples:: Examples::
@ -325,6 +326,7 @@ Examples::
struct S { uint a; uint b; } struct S { uint a; uint b; }
S public x = S(1, 2); S public x = S(1, 2);
string name = "Ada"; string name = "Ada";
string[4] memory AdaArr = ["This", "is", "an", "array"];
} }
contract D { contract D {
C c = new C(); C c = new C();

View File

@ -783,10 +783,12 @@ bool TypeChecker::visit(Assignment const& _assignment)
bool TypeChecker::visit(TupleExpression const& _tuple) bool TypeChecker::visit(TupleExpression const& _tuple)
{ {
vector<ASTPointer<Expression>> const& components = _tuple.components(); vector<ASTPointer<Expression>> const& components = _tuple.components();
solAssert(!_tuple.isInlineArray(), "Tuple type not properly declared");
TypePointers types; TypePointers types;
if (_tuple.annotation().lValueRequested) if (_tuple.annotation().lValueRequested)
{ {
if (_tuple.isInlineArray())
fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
for (auto const& component: components) for (auto const& component: components)
if (component) if (component)
{ {
@ -804,6 +806,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
} }
else else
{ {
TypePointer inlineArrayType;
for (size_t i = 0; i < components.size(); ++i) for (size_t i = 0; i < components.size(); ++i)
{ {
// Outside of an lvalue-context, the only situation where a component can be empty is (x,). // Outside of an lvalue-context, the only situation where a component can be empty is (x,).
@ -813,18 +816,34 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{ {
components[i]->accept(*this); components[i]->accept(*this);
types.push_back(type(*components[i])); types.push_back(type(*components[i]));
if (_tuple.isInlineArray())
solAssert(!!types[i], "Inline array cannot have empty components");
if (i == 0 && _tuple.isInlineArray())
inlineArrayType = types[i]->mobileType();
else if (_tuple.isInlineArray() && inlineArrayType)
inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType());
} }
else else
types.push_back(TypePointer()); types.push_back(TypePointer());
} }
if (components.size() == 1) if (_tuple.isInlineArray())
_tuple.annotation().type = type(*components[0]); {
if (!inlineArrayType)
fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
_tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
}
else else
{ {
if (components.size() == 2 && !components[1]) if (components.size() == 1)
types.pop_back(); _tuple.annotation().type = type(*components[0]);
_tuple.annotation().type = make_shared<TupleType>(types); else
{
if (components.size() == 2 && !components[1])
types.pop_back();
_tuple.annotation().type = make_shared<TupleType>(types);
}
} }
} }
return false; return false;
} }

View File

@ -219,25 +219,46 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
bool ExpressionCompiler::visit(TupleExpression const& _tuple) bool ExpressionCompiler::visit(TupleExpression const& _tuple)
{ {
vector<unique_ptr<LValue>> lvalues; if (_tuple.isInlineArray())
for (auto const& component: _tuple.components()) {
if (component) ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
m_context << max(u256(32u), arrayType.memorySize());
utils().allocateMemory();
m_context << eth::Instruction::DUP1;
for (auto const& component: _tuple.components())
{ {
component->accept(*this); component->accept(*this);
if (_tuple.annotation().lValueRequested) utils().convertType(*component->annotation().type, *arrayType.baseType(), true);
{ utils().storeInMemoryDynamic(*arrayType.baseType(), true);
solAssert(!!m_currentLValue, "");
lvalues.push_back(move(m_currentLValue));
}
} }
else if (_tuple.annotation().lValueRequested)
lvalues.push_back(unique_ptr<LValue>()); m_context << eth::Instruction::POP;
if (_tuple.annotation().lValueRequested) }
else
{ {
if (_tuple.components().size() == 1) vector<unique_ptr<LValue>> lvalues;
m_currentLValue = move(lvalues[0]); for (auto const& component: _tuple.components())
else if (component)
m_currentLValue.reset(new TupleObject(m_context, move(lvalues))); {
component->accept(*this);
if (_tuple.annotation().lValueRequested)
{
solAssert(!!m_currentLValue, "");
lvalues.push_back(move(m_currentLValue));
}
}
else if (_tuple.annotation().lValueRequested)
lvalues.push_back(unique_ptr<LValue>());
if (_tuple.annotation().lValueRequested)
{
if (_tuple.components().size() == 1)
m_currentLValue = move(lvalues[0]);
else
m_currentLValue.reset(new TupleObject(m_context, move(lvalues)));
}
} }
return false; return false;
} }
@ -774,7 +795,6 @@ bool ExpressionCompiler::visit(NewExpression const&)
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
// Check whether the member is a bound function. // Check whether the member is a bound function.
ASTString const& member = _memberAccess.memberName(); ASTString const& member = _memberAccess.memberName();
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
@ -1123,6 +1143,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _literal); CompilerContext::LocationSetter locationSetter(m_context, _literal);
TypePointer type = _literal.annotation().type; TypePointer type = _literal.annotation().type;
switch (type->category()) switch (type->category())
{ {
case Type::Category::IntegerConstant: case Type::Category::IntegerConstant:

View File

@ -6107,6 +6107,119 @@ BOOST_AUTO_TEST_CASE(bound_function_to_string)
BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3))); BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3)));
} }
BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_strings)
{
char const* sourceCode = R"(
contract C {
string s = "doh";
function f() returns (string, string) {
string memory t = "ray";
string[3] memory x = [s, t, "mi"];
return (x[1], x[2]);
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x40), u256(0x80), u256(3), string("ray"), u256(2), string("mi")));
}
BOOST_AUTO_TEST_CASE(inline_array_strings_from_document)
{
char const* sourceCode = R"(
contract C {
function f(uint i) returns (string) {
string[4] memory x = ["This", "is", "an", "array"];
return (x[i]);
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("f(uint256)", u256(0)) == encodeArgs(u256(0x20), u256(4), string("This")));
BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs(u256(0x20), u256(2), string("is")));
BOOST_CHECK(callContractFunction("f(uint256)", u256(2)) == encodeArgs(u256(0x20), u256(2), string("an")));
BOOST_CHECK(callContractFunction("f(uint256)", u256(3)) == encodeArgs(u256(0x20), u256(5), string("array")));
}
BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_ints)
{
char const* sourceCode = R"(
contract C {
function f() returns (uint x, uint y) {
x = 3;
y = 6;
uint[2] memory z = [x, y];
return (z[0], z[1]);
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(3, 6));
}
BOOST_AUTO_TEST_CASE(inline_array_index_access_ints)
{
char const* sourceCode = R"(
contract C {
function f() returns (uint) {
return ([1, 2, 3, 4][2]);
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(3));
}
BOOST_AUTO_TEST_CASE(inline_array_index_access_strings)
{
char const* sourceCode = R"(
contract C {
string public tester;
function f() returns (string) {
return (["abc", "def", "g"][0]);
}
function test() {
tester = f();
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("test()") == encodeArgs());
BOOST_CHECK(callContractFunction("tester()") == encodeArgs(u256(0x20), u256(3), string("abc")));
}
BOOST_AUTO_TEST_CASE(inline_array_return)
{
char const* sourceCode = R"(
contract C {
uint8[] tester;
function f() returns (uint8[5]) {
return ([1,2,3,4,5]);
}
function test() returns (uint8, uint8, uint8, uint8, uint8) {
tester = f();
return (tester[0], tester[1], tester[2], tester[3], tester[4]);
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(1, 2, 3, 4, 5));
}
BOOST_AUTO_TEST_CASE(inline_long_string_return)
{
char const* sourceCode = R"(
contract C {
function f() returns (string) {
return (["somethingShort", "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"][1]);
}
}
)";
string strLong = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("f()") == encodeDyn(strLong));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -2769,15 +2769,98 @@ BOOST_AUTO_TEST_CASE(function_overload_array_type)
BOOST_CHECK(success(text)); BOOST_CHECK(success(text));
} }
/*BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing) BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion)
{
char const* text = R"(
contract C {
function f() returns (uint) {
uint8 x = 7;
uint16 y = 8;
uint32 z = 9;
uint32[3] memory ending = [x, y, z];
return (ending[1]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion_strings)
{ {
char const* text = R"( char const* text = R"(
contract C { contract C {
uint[] a; function f() returns (string) {
function f() returns (uint, uint) { string memory x = "Hello";
a = [1,2,3]; string memory y = "World";
return (a[3], [3,4][0]); string[2] memory z = [x, y];
} return (z[0]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(inline_array_declaration_const_int_conversion)
{
char const* text = R"(
contract C {
function f() returns (uint) {
uint8[4] memory z = [1,2,3,5];
return (z[0]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(inline_array_declaration_const_string_conversion)
{
char const* text = R"(
contract C {
function f() returns (string) {
string[2] memory z = ["Hello", "World"];
return (z[0]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type)
{
char const* text = R"(
contract C {
function f() returns (uint) {
return ([4,5,6][1]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type_strings)
{
char const* text = R"(
contract C {
function f() returns (string) {
return (["foo", "man", "choo"][1]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(inline_struct_declaration_arrays)
{
char const* text = R"(
contract C {
struct S {
uint a;
string b;
}
function f() {
S[2] memory x = [S({a: 1, b: "fish"}), S({a: 2, b: "fish"})];
}
} }
)"; )";
BOOST_CHECK(success(text)); BOOST_CHECK(success(text));
@ -2788,12 +2871,37 @@ BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array)
char const* text = R"( char const* text = R"(
contract C { contract C {
function f() { function f() {
uint[] x = [45, "foo", true]; uint[3] x = [45, 'foo', true];
} }
} }
)"; )";
BOOST_CHECK(expectError(text) == Error::Type::TypeError); BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}*/ }
BOOST_AUTO_TEST_CASE(dynamic_inline_array)
{
char const* text = R"(
contract C {
function f() {
uint8[4][4] memory dyn = [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]];
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(lvalues_as_inline_array)
{
char const* text = R"(
contract C {
function f() {
[1, 2, 3]++;
[1, 2, 3] = [4, 5, 6];
}
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()