aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/frequently-asked-questions.rst4
-rw-r--r--libsolidity/analysis/TypeChecker.cpp31
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp51
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp113
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp124
5 files changed, 293 insertions, 30 deletions
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index cf037131..f87b785d 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -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();
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 669a7e4a..ab1151a1 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -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;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index b0e92b59..f0dab41a 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -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)
+ utils().convertType(*component->annotation().type, *arrayType.baseType(), true);
+ utils().storeInMemoryDynamic(*arrayType.baseType(), true);
+ }
+
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ vector<unique_ptr<LValue>> lvalues;
+ for (auto const& component: _tuple.components())
+ if (component)
{
- solAssert(!!m_currentLValue, "");
- lvalues.push_back(move(m_currentLValue));
+ 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)));
}
- 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:
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 2f627fc2..34c5dffc 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -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()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 70c9eb0d..4697e756 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -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()