From bf8af89bba16641e275e1ad5a06f239d9005f913 Mon Sep 17 00:00:00 2001 From: mingchuan Date: Fri, 24 May 2019 16:40:15 +0800 Subject: [PATCH] Fix assignment of struct containing array of mappings --- Changelog.md | 1 + docs/types/reference-types.rst | 3 +- libsolidity/ast/Types.cpp | 3 +- libsolidity/codegen/LValue.cpp | 7 +-- .../conversion/assignment_ignore_mapping.sol | 48 +++++++++++++++++++ ...tructs_with_mapping_array_struct_array.sol | 12 +++++ 6 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 test/libsolidity/semanticTests/structs/conversion/assignment_ignore_mapping.sol create mode 100644 test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mapping_array_struct_array.sol diff --git a/Changelog.md b/Changelog.md index e79287e48..0a5089127 100644 --- a/Changelog.md +++ b/Changelog.md @@ -25,6 +25,7 @@ Bugfixes: * SMTChecker: Fix internal error in fixed point operations. * SMTChecker: Fix internal error in assignment to unsupported type. * SMTChecker: Fix internal error in branching when inlining function calls that modify local variables. + * Code Generator: Fix assertion failure when assign structs containing array of mapping. diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 6a4f6e64c..0333f4aa4 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -371,7 +371,8 @@ shown in the following example: campaignID = numCampaigns++; // campaignID is return variable // Creates new struct in memory and copies it to storage. // We leave out the mapping type, because it is not valid in memory. - // If structs are copied (even from storage to storage), mapping types + // If structs are copied (even from storage to storage), + // types that are not valid outside of storage (ex. mappings and array of mappings) // are always omitted, because they cannot be enumerated. campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e58fa1547..6877ecbd6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2068,7 +2068,8 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const { TypePointer type = variable->annotation().type; solAssert(type, ""); - // Skip all mapping members if we are not in storage. + // If we are not in storage, skip all members that cannot live outside of storage, + // ex. mappings and array of mappings if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) continue; members.emplace_back( diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index a49963f69..4413d5ee4 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -288,7 +288,8 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc { solAssert( _sourceType.category() == m_dataType->category(), - "Wrong type conversation for assignment."); + "Wrong type conversation for assignment." + ); if (m_dataType->category() == Type::Category::Array) { m_context << Instruction::POP; // remove byte offset @@ -313,9 +314,9 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); for (auto const& member: structType.members(nullptr)) { - // assign each member that is not a mapping + // assign each member that can live outside of storage TypePointer const& memberType = member.type; - if (memberType->category() == Type::Category::Mapping) + if (!memberType->canLiveOutsideStorage()) continue; TypePointer sourceMemberType = sourceType.memberType(member.name); if (sourceType.location() == DataLocation::Storage) diff --git a/test/libsolidity/semanticTests/structs/conversion/assignment_ignore_mapping.sol b/test/libsolidity/semanticTests/structs/conversion/assignment_ignore_mapping.sol new file mode 100644 index 000000000..d63c7a784 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/conversion/assignment_ignore_mapping.sol @@ -0,0 +1,48 @@ +contract Test { + struct A { + mapping(uint=>uint) m; + } + struct B { + mapping(uint=>uint) m; + uint x; + } + struct C { + mapping(uint=>uint)[] ma; + } + struct D { + A[] a; + } + A storageA; + B storageB; + C storageC; + D storageD; + constructor() public { + storageA.m[1] = 2; + storageB.m[3] = 4; + storageB.x = 5; + storageC.ma.length = 6; + storageD.a.length = 7; + } + function run() public returns (uint, uint, uint, uint, uint, uint) { + A memory memoryA = A(); + B memory memoryB = B(42); + C memory memoryC = C(); + D memory memoryD1 = D(new A[](999)); + D memory memoryD2 = storageD; + storageA = memoryA; + storageB = memoryB; + storageC = memoryC; + // the following line does not compile because unimplemented + // storageD = memoryD1; + return ( + storageA.m[1], + storageB.x, + memoryB.x, + storageC.ma.length, + memoryD1.a.length, + memoryD2.a.length + ); + } +} +// ---- +// run() -> 2, 42, 42, 6, 999, 7 diff --git a/test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mapping_array_struct_array.sol b/test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mapping_array_struct_array.sol new file mode 100644 index 000000000..64d62e08c --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mapping_array_struct_array.sol @@ -0,0 +1,12 @@ +contract Test { + struct S1 { uint8 a; mapping(uint => uint)[] b1; uint8 c; } + struct S2 { uint8 a; S1[] b2; uint8 c; } + S2 s2; + function f() public { + S2 memory x = s2; + x.b2.length; + x.b2[1].b1[2][3]; + } +} +// ---- +// TypeError: (208-218): Member "b1" is not available in struct Test.S1 memory outside of storage.