From f95baf2cb9adfb342cf6eeb2d1d68019413c3855 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 26 Jun 2015 18:35:43 +0200 Subject: [PATCH 1/6] Delete for memory objects. --- libsolidity/ExpressionCompiler.cpp | 22 +++++++++++--- libsolidity/LValue.cpp | 13 ++------- test/libsolidity/SolidityEndToEndTest.cpp | 29 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 26 +++++++++++++++++ 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 7ddcc0318..59907b14b 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -679,10 +679,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::Struct: { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << eth::Instruction::POP; // structs always align to new slot - pair const& offsets = type.getStorageOffsetsOfMember(member); - m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second); - setLValueToStorageItem(_memberAccess); + switch (type.location()) + { + case DataLocation::Storage: + { + m_context << eth::Instruction::POP; // structs always align to new slot + pair const& offsets = type.getStorageOffsetsOfMember(member); + m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second); + setLValueToStorageItem(_memberAccess); + break; + } + case DataLocation::Memory: + { + solAssert(false, "Member access for memory structs not yet implemented."); + break; + } + default: + solAssert(false, "Illegal data location for struct."); + } break; } case Type::Category::Enum: diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index cf640e910..7eec478b3 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -69,17 +69,8 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo void StackVariable::setToZero(SourceLocation const& _location, bool) const { - unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset); - if (stackDiff > 16) - BOOST_THROW_EXCEPTION( - CompilerError() << - errinfo_sourceLocation(_location) << - errinfo_comment("Stack too deep, try removing local variables.") - ); - solAssert(stackDiff >= m_size - 1, ""); - for (unsigned i = 0; i < m_size; ++i) - m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i) - << eth::Instruction::POP; + CompilerUtils(m_context).pushZeroValue(m_dataType); + storeValue(m_dataType, _location, true); } MemoryItem::MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded): diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a01e98cf8..9451c8cf2 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4691,6 +4691,35 @@ BOOST_AUTO_TEST_CASE(memory_types_initialisation) BOOST_CHECK(callContractFunction("nestedStat()") == encodeArgs(vector(3 * 7))); } +BOOST_AUTO_TEST_CASE(memory_arrays_delete) +{ + char const* sourceCode = R"( + contract Test { + function del() returns (uint24[3][4]) { + uint24[3][4] memory x; + for (uint24 i = 0; i < x.length; i ++) + for (uint24 j = 0; j < x[i].length; j ++) + x[i][j] = i * 0x10 + j; + delete x[1]; + delete x[3][2]; + return x; + } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + + vector data(3 * 4); + for (unsigned i = 0; i < 4; i++) + for (unsigned j = 0; j < 3; j++) + { + u256 v = 0; + if (!(i == 1 || (i == 3 && j == 2))) + v = i * 0x10 + j; + data[i * 3 + j] = v; + } + BOOST_CHECK(callContractFunction("del()") == encodeArgs(data)); +} + BOOST_AUTO_TEST_CASE(memory_arrays_index_access_write) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 765593c59..f24930ba5 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1900,6 +1900,18 @@ BOOST_AUTO_TEST_CASE(storage_location_local_variables) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); } +BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array) +{ + char const* sourceCode = R"( + contract C { + function f() { + mapping(uint=>uint)[] memory x; + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) { char const* sourceCode = R"( @@ -1931,6 +1943,20 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + function f() { + var x = data; + delete x; + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) { char const* sourceCode = R"( From 97180a1305951d6451537a5f3defcbbafb690831 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 26 Jun 2015 19:14:26 +0200 Subject: [PATCH 2/6] No delete on storage pointers. --- libsolidity/Types.cpp | 29 +++++++++++++++++------------ libsolidity/Types.h | 3 +-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 15c367421..858828ba0 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -671,6 +671,23 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } +TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const +{ + if (_operator != Token::Delete) + return TypePointer(); + // delete can be used on everything except calldata references or storage pointers + // (storage references are ok) + switch (location()) + { + case DataLocation::CallData: + return TypePointer(); + case DataLocation::Memory: + return make_shared(); + case DataLocation::Storage: + return m_isPointer ? TypePointer() : make_shared(); + } +} + TypePointer ReferenceType::copyForLocationIfReference(DataLocation _location, TypePointer const& _type) { if (auto type = dynamic_cast(_type.get())) @@ -738,13 +755,6 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const } } -TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const -{ - if (_operator == Token::Delete) - return make_shared(); - return TypePointer(); -} - bool ArrayType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -962,11 +972,6 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } -TypePointer StructType::unaryOperatorResult(Token::Value _operator) const -{ - return _operator == Token::Delete ? make_shared() : TypePointer(); -} - bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 9d9aaaba7..d15e2f963 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -376,6 +376,7 @@ public: explicit ReferenceType(DataLocation _location): m_location(_location) {} DataLocation location() const { return m_location; } + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual unsigned memoryHeadSize() const override { return 32; } /// @returns a copy of this type with location (recursively) changed to @a _location, @@ -444,7 +445,6 @@ public: {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(const Type& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } @@ -545,7 +545,6 @@ public: //@todo only storage until we have non-storage structs ReferenceType(DataLocation::Storage), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override; virtual u256 getStorageSize() const override; From 1f5c86d01ab83a93671859a4e8e5ce82762f9edb Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 26 Jun 2015 21:27:53 +0200 Subject: [PATCH 3/6] Disallow memory types containing mappings. --- libsolidity/AST.cpp | 11 ++++++++++- libsolidity/Types.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 09af49c67..2b73aab5e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -535,7 +535,16 @@ void VariableDeclaration::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type.")); m_type = type->mobileType(); } - if (m_isStateVariable && getVisibility() >= Visibility::Public && !FunctionType(*this).externalType()) + solAssert(!!m_type, ""); + if (!m_isStateVariable) + { + if (m_type->dataStoredIn(DataLocation::Memory) || m_type->dataStoredIn(DataLocation::CallData)) + if (!m_type->canLiveOutsideStorage()) + BOOST_THROW_EXCEPTION(createTypeError( + "Type " + m_type->toString() + " is only valid in storage." + )); + } + else if (getVisibility() >= Visibility::Public && !FunctionType(*this).externalType()) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index d15e2f963..6b03b1ae2 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -449,6 +449,7 @@ public: virtual unsigned getCalldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } virtual u256 getStorageSize() const override; + virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } virtual unsigned getSizeOnStack() const override; virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override From 2951cbd3cbba7ebd14eb24338361f6a0393fda77 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 27 Jun 2015 00:13:53 +0200 Subject: [PATCH 4/6] Fix gcc issue. --- libsolidity/Types.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 858828ba0..a6d462a9a 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -685,6 +685,8 @@ TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const return make_shared(); case DataLocation::Storage: return m_isPointer ? TypePointer() : make_shared(); + default: + solAssert(false, ""); } } From 4cdea67cea1c03977442a6b1b6180d8a479adcb8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 29 Jun 2015 12:47:04 +0200 Subject: [PATCH 5/6] Another test for arrays in constructors. --- test/libsolidity/SolidityEndToEndTest.cpp | 41 ++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 9451c8cf2..a2dbc35e6 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4493,7 +4493,7 @@ BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) } } contract Main is Base { - function Main(bytes s, uint x) Base(x, s){}//f(s)) {} + function Main(bytes s, uint x) Base(x, f(s)) {} function f(bytes s) returns (bytes) { return s; } @@ -4517,6 +4517,45 @@ BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) ); } +BOOST_AUTO_TEST_CASE(arrays_in_constructors) +{ + char const* sourceCode = R"( + contract Base { + uint public m_x; + address[] m_s; + function Base(uint x, address[] s) { + m_x = x; + m_s = s; + } + function part(uint i) returns (address) { + return m_s[i]; + } + } + contract Main is Base { + function Main(address[] s, uint x) Base(x, f(s)) {} + function f(address[] s) returns (address[]) { + return s; + } + } + contract Creator { + function f(uint x, address[] s) returns (uint r, address ch) { + var c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); + } + } + )"; + compileAndRun(sourceCode, 0, "Creator"); + vector s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + bytes dyn1 = encodeArgs(u256(s1.size()), s1); + u256 x = 7; + bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; + BOOST_REQUIRE( + callContractFunction("f(uint256,address[])", asString(args1)) == + encodeArgs(x, s1[unsigned(x)]) + ); +} + BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) { char const* sourceCode = R"( From 6f649196987bf2732fd888bd9d652ed23f0483e7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 29 Jun 2015 13:23:02 +0200 Subject: [PATCH 6/6] Another compiler fix. --- libsolidity/Types.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index a6d462a9a..c1769d000 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -688,6 +688,7 @@ TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const default: solAssert(false, ""); } + return TypePointer(); } TypePointer ReferenceType::copyForLocationIfReference(DataLocation _location, TypePointer const& _type)