diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index cbdca8524..32d96369c 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2330,10 +2330,6 @@ string YulUtilFunctions::updateStorageValueFunction( { solAssert(_toType.category() == Type::Category::Struct, ""); - solUnimplementedAssert( - fromReferenceType->location() != DataLocation::Storage, - "Copying from storage to storage is not yet implemented." - ); auto const& fromStructType = dynamic_cast(_fromType); auto const& toStructType = dynamic_cast(_toType); solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); @@ -2341,6 +2337,7 @@ string YulUtilFunctions::updateStorageValueFunction( Whiskers templ(R"( function (slot, value) { + if eq(slot, value) { leave } <#member> { @@ -2348,6 +2345,7 @@ string YulUtilFunctions::updateStorageValueFunction( } )"); + templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage)); templ("functionName", functionName); MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); @@ -2356,67 +2354,81 @@ string YulUtilFunctions::updateStorageValueFunction( vector> memberParams(structMembers.size()); for (size_t i = 0; i < structMembers.size(); ++i) { - solAssert(structMembers[i].type->memoryHeadSize() == 32, ""); - bool fromCalldata = fromStructType.location() == DataLocation::CallData; + Type const& memberType = *structMembers[i].type; + solAssert(memberType.memoryHeadSize() == 32, ""); auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); Whiskers t(R"( let memberSlot := add(slot, ) + let memberSrcPtr := add(value, ) - - let := (value, add(value, )) - - let := add(value, ) - + let := + + (value, memberSrcPtr) + + memberSrcPtr + - let := () - (memberSlot, , ) - - (memberSlot, ) + := () - - let memberMemoryOffset := add(value, ) - let := (memberMemoryOffset) - (memberSlot, , ) + + + let := (memberSrcPtr) + + + + let := + + (memberSrcPtr) + + memberSrcPtr + + + + (memberSlot, ) )"); + bool fromCalldata = fromStructType.location() == DataLocation::CallData; t("fromCalldata", fromCalldata); + bool fromMemory = fromStructType.location() == DataLocation::Memory; + t("fromMemory", fromMemory); + bool fromStorage = fromStructType.location() == DataLocation::Storage; + t("fromStorage", fromStorage); + t("isValueType", memberType.isValueType()); + t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size())); + + t("memberStorageSlotDiff", slotDiff.str()); if (fromCalldata) { - t("memberCalldataOffset", suffixedVariableNameList( - "memberCalldataOffset_", - 0, - structMembers[i].type->stackItems().size() - )); - t("dynamicallyEncodedMember", structMembers[i].type->isDynamicallyEncoded()); - if (structMembers[i].type->isDynamicallyEncoded()) - t("accessCalldataTail", accessCalldataTailFunction(*structMembers[i].type)); + t("memberOffset", to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name))); + t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded()); + if (memberType.isDynamicallyEncoded()) + t("accessCalldataTail", accessCalldataTailFunction(memberType)); + if (memberType.isValueType()) + t("read", readFromCalldata(memberType)); } - t("isValueType", structMembers[i].type->isValueType()); - t("memberValues", suffixedVariableNameList( - "memberValue_", - 0, - structMembers[i].type->stackItems().size() + else if (fromMemory) + { + t("memberOffset", fromStructType.memoryOffsetOfMember(structMembers[i].name).str()); + t("read", readFromMemory(memberType)); + } + else if (fromStorage) + { + auto [srcSlotOffset, srcOffset] = fromStructType.storageOffsetsOfMember(structMembers[i].name); + t("memberOffset", formatNumber(srcSlotOffset)); + if (memberType.isValueType()) + t("read", readFromStorageValueType(memberType, srcOffset, false)); + else + solAssert(srcOffset == 0, ""); + + } + t("updateStorageValue", updateStorageValueFunction( + memberType, + *toStructMembers[i].type, + memberType.isValueType() ? optional{offset} : std::nullopt )); - t("hasOffset", structMembers[i].type->isValueType()); - t( - "updateMember", - structMembers[i].type->isValueType() ? - updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type) : - updateStorageValueFunction(*structMembers[i].type, *toStructMembers[i].type, offset) - ); - t("memberStorageSlotDiff", slotDiff.str()); - t("memberStorageOffset", to_string(offset)); - t( - "memberOffset", - fromCalldata ? - to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) : - fromStructType.memoryOffsetOfMember(structMembers[i].name).str() - ); - if (!fromCalldata || structMembers[i].type->isValueType()) - t("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata)); memberParams[i]["updateMemberCall"] = t.render(); } templ("member", memberParams); diff --git a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol index ca6dffb9d..4b456ee6c 100644 --- a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol +++ b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol @@ -32,5 +32,7 @@ contract test { } } +// ==== +// compileViaYul: also // ---- // assign() -> 2, 2, 3, 3 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol index c6e25f9d5..2ab4a8f36 100644 --- a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -20,6 +20,8 @@ contract c { return data1.data[i]; } } +// ==== +// compileViaYul: also // ---- // storage: empty // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true diff --git a/test/libsolidity/semanticTests/structs/struct_copy.sol b/test/libsolidity/semanticTests/structs/struct_copy.sol index 2849c16bc..7c9ef9751 100644 --- a/test/libsolidity/semanticTests/structs/struct_copy.sol +++ b/test/libsolidity/semanticTests/structs/struct_copy.sol @@ -34,6 +34,8 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // set(uint256): 7 -> true // retrieve(uint256): 7 -> 1, 3, 4, 2 diff --git a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol index 59907b476..cd3bf04da 100644 --- a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol +++ b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol @@ -23,6 +23,8 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // x() -> 0, 0 // y() -> 0, 0