Merge pull request #10415 from ethereum/arrayClearingStorageSol2Yul

[Sol->Yul] Fixing copying from storage to storage.
This commit is contained in:
chriseth 2020-12-01 09:43:47 +01:00 committed by GitHub
commit 388fcddd23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 98 additions and 18 deletions

View File

@ -1000,10 +1000,9 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
});
}
std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
{
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
if (_type.isByteArray())
@ -1019,8 +1018,10 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
let oldLen := <fetchLength>(array)
// Store new length
sstore(array, newLen)
<?isDynamic>
// Store new length
sstore(array, newLen)
</isDynamic>
// Size was reduced, clear end of array
if lt(newLen, oldLen) {
@ -1041,6 +1042,7 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
("functionName", functionName)
("panic", panicFunction())
("fetchLength", arrayLengthFunction(_type))
("isDynamic", _type.isDynamicallySized())
("convertToSize", arrayConvertLengthToSize(_type))
("dataPosition", arrayDataAreaFunction(_type))
("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
@ -1425,7 +1427,7 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type)
)")
("functionName", functionName)
("dynamic", _type.isDynamicallySized())
("resizeArray", _type.isDynamicallySized() ? resizeDynamicArrayFunction(_type) : "")
("resizeArray", _type.isDynamicallySized() ? resizeArrayFunction(_type) : "")
(
"clearRange",
clearStorageRangeFunction(
@ -1497,6 +1499,9 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
if (!_toType.isDynamicallySized())
solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), "");
if (_fromType.isByteArray())
return copyByteArrayToStorageFunction(_fromType, _toType);
if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
@ -1508,9 +1513,8 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
<?fromStorage> if eq(slot, value) { leave } </fromStorage>
let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
<?isToDynamic>
<resizeArray>(slot, length)
</isToDynamic>
<resizeArray>(slot, length)
let srcPtr := <srcDataLocation>(value)
@ -1564,7 +1568,6 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory);
templ("fromMemory", fromMemory);
templ("fromCalldata", fromCalldata);
templ("isToDynamic", _toType.isDynamicallySized());
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
if (fromCalldata)
{
@ -1573,8 +1576,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
if (_fromType.baseType()->isDynamicallyEncoded())
templ("accessCalldataTail", accessCalldataTailFunction(*_fromType.baseType()));
}
if (_toType.isDynamicallySized())
templ("resizeArray", resizeDynamicArrayFunction(_toType));
templ("resizeArray", resizeArrayFunction(_toType));
templ("arrayLength",arrayLengthFunction(_fromType));
templ("isValueType", _fromType.baseType()->isValueType());
templ("dstDataLocation", arrayDataAreaFunction(_toType));
@ -1693,6 +1695,8 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), "");
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
solUnimplementedAssert(_fromType.storageStride() == _toType.storageStride(), "");
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
@ -1701,9 +1705,7 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
let length := <arrayLength>(src)
// Make sure array length is sane
if gt(length, 0xffffffffffffffff) { <panic>() }
<?isToDynamic>
<resizeArray>(dst, length)
</isToDynamic>
<resizeArray>(dst, length)
let srcPtr := <srcDataLocation>(src)
@ -1723,9 +1725,7 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
if (_fromType.dataStoredIn(DataLocation::Storage))
solAssert(!_fromType.isValueType(), "");
templ("functionName", functionName);
templ("isToDynamic", _toType.isDynamicallySized());
if (_toType.isDynamicallySized())
templ("resizeArray", resizeDynamicArrayFunction(_toType));
templ("resizeArray", resizeArrayFunction(_toType));
templ("arrayLength",arrayLengthFunction(_fromType));
templ("panic", panicFunction());
templ("srcDataLocation", arrayDataAreaFunction(_fromType));

View File

@ -171,8 +171,9 @@ public:
std::string arrayLengthFunction(ArrayType const& _type);
/// @returns the name of a function that resizes a storage array
/// for statically sized arrays, it will just clean-up elements of array starting from newLen until the end
/// signature: (array, newLen)
std::string resizeDynamicArrayFunction(ArrayType const& _type);
std::string resizeArrayFunction(ArrayType const& _type);
/// @returns the name of a function that reduces the size of a storage array by one element
/// signature: (array)

View File

@ -0,0 +1,19 @@
contract c {
uint64[] data1;
uint256[] data2;
function test() public returns (uint256 x, uint256 y) {
data2.push(11);
data1.push(0);
data1.push(1);
data1.push(2);
data1.push(3);
data1.push(4);
data2 = data1;
assert(data1[0] == data2[0]);
x = data2.length;
y = data2[4];
}
}
// ----
// test() -> 5, 4

View File

@ -0,0 +1,22 @@
contract c {
uint256[] data1;
uint256[] data2;
function test() public returns (uint256 x, uint256 y) {
data2.push(11);
data1.push(0);
data1.push(1);
data1.push(2);
data1.push(3);
data1.push(4);
data2 = data1;
assert(data1[0] == data2[0]);
x = data2.length;
y = data2[4];
}
}
// ====
// compileViaYul: also
// ----
// test() -> 5, 4

View File

@ -13,5 +13,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test() -> 8, 0

View File

@ -0,0 +1,9 @@
contract C {
byte[32] data1;
bytes2[10] data2;
function f() external {
data1 = data2;
}
}
// ----
// TypeError 7407: (99-104): Type bytes2[10] storage ref is not implicitly convertible to expected type bytes1[32] storage ref.

View File

@ -0,0 +1,9 @@
contract C {
uint64[32] data1;
uint256[10] data2;
function f() external {
data1 = data2;
}
}
// ----
// TypeError 7407: (102-107): Type uint256[10] storage ref is not implicitly convertible to expected type uint64[32] storage ref.

View File

@ -0,0 +1,9 @@
contract C {
uint256[10] data1;
uint256[] data2;
function f() external {
data1 = data2;
}
}
// ----
// TypeError 7407: (101-106): Type uint256[] storage ref is not implicitly convertible to expected type uint256[10] storage ref.

View File

@ -0,0 +1,9 @@
contract C {
uint256[10] data1;
uint256[18] data2;
function f() external {
data1 = data2;
}
}
// ----
// TypeError 7407: (103-108): Type uint256[18] storage ref is not implicitly convertible to expected type uint256[10] storage ref.