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?
===========================================
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::
@ -325,6 +326,7 @@ Examples::
struct S { uint a; uint b; }
S public x = S(1, 2);
string name = "Ada";
string[4] memory AdaArr = ["This", "is", "an", "array"];
}
contract D {
C c = new C();

View File

@ -783,10 +783,12 @@ bool TypeChecker::visit(Assignment const& _assignment)
bool TypeChecker::visit(TupleExpression const& _tuple)
{
vector<ASTPointer<Expression>> const& components = _tuple.components();
solAssert(!_tuple.isInlineArray(), "Tuple type not properly declared");
TypePointers types;
if (_tuple.annotation().lValueRequested)
{
if (_tuple.isInlineArray())
fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
for (auto const& component: components)
if (component)
{
@ -804,6 +806,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
}
else
{
TypePointer inlineArrayType;
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,).
@ -813,18 +816,34 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
components[i]->accept(*this);
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
types.push_back(TypePointer());
}
if (components.size() == 1)
_tuple.annotation().type = type(*components[0]);
if (_tuple.isInlineArray())
{
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
{
if (components.size() == 2 && !components[1])
types.pop_back();
_tuple.annotation().type = make_shared<TupleType>(types);
if (components.size() == 1)
_tuple.annotation().type = type(*components[0]);
else
{
if (components.size() == 2 && !components[1])
types.pop_back();
_tuple.annotation().type = make_shared<TupleType>(types);
}
}
}
return false;
}

View File

@ -219,25 +219,46 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
bool ExpressionCompiler::visit(TupleExpression const& _tuple)
{
vector<unique_ptr<LValue>> lvalues;
for (auto const& component: _tuple.components())
if (component)
if (_tuple.isInlineArray())
{
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);
if (_tuple.annotation().lValueRequested)
{
solAssert(!!m_currentLValue, "");
lvalues.push_back(move(m_currentLValue));
}
utils().convertType(*component->annotation().type, *arrayType.baseType(), true);
utils().storeInMemoryDynamic(*arrayType.baseType(), true);
}
else if (_tuple.annotation().lValueRequested)
lvalues.push_back(unique_ptr<LValue>());
if (_tuple.annotation().lValueRequested)
m_context << eth::Instruction::POP;
}
else
{
if (_tuple.components().size() == 1)
m_currentLValue = move(lvalues[0]);
else
m_currentLValue.reset(new TupleObject(m_context, move(lvalues)));
vector<unique_ptr<LValue>> lvalues;
for (auto const& component: _tuple.components())
if (component)
{
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;
}
@ -774,7 +795,6 @@ bool ExpressionCompiler::visit(NewExpression const&)
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
// Check whether the member is a bound function.
ASTString const& member = _memberAccess.memberName();
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);
TypePointer type = _literal.annotation().type;
switch (type->category())
{
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_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()
}

View File

@ -2769,15 +2769,98 @@ BOOST_AUTO_TEST_CASE(function_overload_array_type)
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"(
contract C {
uint[] a;
function f() returns (uint, uint) {
a = [1,2,3];
return (a[3], [3,4][0]);
}
function f() returns (string) {
string memory x = "Hello";
string memory y = "World";
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));
@ -2788,12 +2871,37 @@ BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array)
char const* text = R"(
contract C {
function f() {
uint[] x = [45, "foo", true];
uint[3] x = [45, 'foo', true];
}
}
)";
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()