mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9843 from ethereum/deleteStructSol2Yul
[Sol->Yul] Implementing delete struct
This commit is contained in:
commit
f8d5c4db36
@ -33,8 +33,11 @@ string MultiUseYulFunctionCollector::requestedFunctions()
|
|||||||
{
|
{
|
||||||
string result;
|
string result;
|
||||||
for (auto const& f: m_requestedFunctions)
|
for (auto const& f: m_requestedFunctions)
|
||||||
|
{
|
||||||
|
solAssert(f.second != "<<STUB<<", "");
|
||||||
// std::map guarantees ascending order when iterating through its keys.
|
// std::map guarantees ascending order when iterating through its keys.
|
||||||
result += f.second;
|
result += f.second;
|
||||||
|
}
|
||||||
m_requestedFunctions.clear();
|
m_requestedFunctions.clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -43,9 +46,10 @@ string MultiUseYulFunctionCollector::createFunction(string const& _name, functio
|
|||||||
{
|
{
|
||||||
if (!m_requestedFunctions.count(_name))
|
if (!m_requestedFunctions.count(_name))
|
||||||
{
|
{
|
||||||
|
m_requestedFunctions[_name] = "<<STUB<<";
|
||||||
string fun = _creator();
|
string fun = _creator();
|
||||||
solAssert(!fun.empty(), "");
|
solAssert(!fun.empty(), "");
|
||||||
solAssert(fun.find("function " + _name) != string::npos, "Function not properly named.");
|
solAssert(fun.find("function " + _name + "(") != string::npos, "Function not properly named.");
|
||||||
m_requestedFunctions[_name] = std::move(fun);
|
m_requestedFunctions[_name] = std::move(fun);
|
||||||
}
|
}
|
||||||
return _name;
|
return _name;
|
||||||
|
@ -851,7 +851,6 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
|
|||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
|
||||||
solUnimplementedAssert(_type.baseType()->storageSize() == 1, "");
|
|
||||||
|
|
||||||
string functionName = "resize_array_" + _type.identifier();
|
string functionName = "resize_array_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
@ -1147,6 +1146,45 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
|
||||||
|
{
|
||||||
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
|
|
||||||
|
string functionName = "clear_struct_storage_" + _type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
|
MemberList::MemberMap structMembers = _type.nativeMembers(nullptr);
|
||||||
|
vector<map<string, string>> memberSetValues;
|
||||||
|
|
||||||
|
for (auto const& member: structMembers)
|
||||||
|
{
|
||||||
|
auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name);
|
||||||
|
|
||||||
|
memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"(
|
||||||
|
<setZero>(add(slot, <memberSlotDiff>), <memberStorageOffset>)
|
||||||
|
)")
|
||||||
|
("setZero", storageSetToZeroFunction(*member.type))
|
||||||
|
("memberSlotDiff", memberSlotDiff.str())
|
||||||
|
("memberStorageOffset", to_string(memberStorageOffset))
|
||||||
|
.render()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot) {
|
||||||
|
<#member>
|
||||||
|
<clearMember>
|
||||||
|
</member>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("allocStruct", allocateMemoryStructFunction(_type))
|
||||||
|
("storageSize", _type.storageSize().str())
|
||||||
|
("member", memberSetValues)
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
||||||
@ -2346,27 +2384,32 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
auto const& toStructType = dynamic_cast<StructType const &>(_to);
|
auto const& toStructType = dynamic_cast<StructType const &>(_to);
|
||||||
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
|
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
|
||||||
|
|
||||||
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
|
if (fromStructType.location() == toStructType.location() && toStructType.isPointer())
|
||||||
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
|
body = "converted := value";
|
||||||
solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, "");
|
|
||||||
|
|
||||||
if (fromStructType.location() == DataLocation::CallData)
|
|
||||||
{
|
|
||||||
body = Whiskers(R"(
|
|
||||||
converted := <abiDecode>(value, calldatasize())
|
|
||||||
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
|
|
||||||
{&toStructType}
|
|
||||||
)).render();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(fromStructType.location() == DataLocation::Storage, "");
|
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
|
||||||
|
solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, "");
|
||||||
|
|
||||||
body = Whiskers(R"(
|
if (fromStructType.location() == DataLocation::CallData)
|
||||||
converted := <readFromStorage>(value)
|
{
|
||||||
)")
|
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
|
||||||
("readFromStorage", readFromStorage(toStructType, 0, true))
|
body = Whiskers(R"(
|
||||||
.render();
|
converted := <abiDecode>(value, calldatasize())
|
||||||
|
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
|
||||||
|
{&toStructType}
|
||||||
|
)).render();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(fromStructType.location() == DataLocation::Storage, "");
|
||||||
|
|
||||||
|
body = Whiskers(R"(
|
||||||
|
converted := <readFromStorage>(value)
|
||||||
|
)")
|
||||||
|
("readFromStorage", readFromStorage(toStructType, 0, true))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -2809,11 +2852,24 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
|
|||||||
else if (_type.category() == Type::Category::Array)
|
else if (_type.category() == Type::Category::Array)
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot, offset) {
|
function <functionName>(slot, offset) {
|
||||||
|
if iszero(eq(offset, 0)) { <panic>() }
|
||||||
<clearArray>(slot)
|
<clearArray>(slot)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
|
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
|
||||||
|
("panic", panicFunction())
|
||||||
|
.render();
|
||||||
|
else if (_type.category() == Type::Category::Struct)
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot, offset) {
|
||||||
|
if iszero(eq(offset, 0)) { <panic>() }
|
||||||
|
<clearStruct>(slot)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type)))
|
||||||
|
("panic", panicFunction())
|
||||||
.render();
|
.render();
|
||||||
else
|
else
|
||||||
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
|
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
|
||||||
|
@ -431,6 +431,10 @@ private:
|
|||||||
/// signature: (slot, offset)
|
/// signature: (slot, offset)
|
||||||
std::string partialClearStorageSlotFunction();
|
std::string partialClearStorageSlotFunction();
|
||||||
|
|
||||||
|
/// @returns the name of a function that will clear the given storage struct
|
||||||
|
/// signature: (slot) ->
|
||||||
|
std::string clearStorageStructFunction(StructType const& _type);
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings m_revertStrings;
|
RevertStrings m_revertStrings;
|
||||||
MultiUseYulFunctionCollector& m_functionCollector;
|
MultiUseYulFunctionCollector& m_functionCollector;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Error (1834): Unimplemented feature error: setToZero for type t_struct$_S_$4_storage not yet implemented! in FILENAME REMOVED
|
Error (1834): Unimplemented feature error: Byte Arrays not yet implemented! in FILENAME REMOVED
|
||||||
--> yul_unimplemented/input.sol:9:9:
|
--> yul_unimplemented/input.sol:6:9:
|
||||||
|
|
|
|
||||||
9 | delete str;
|
6 | delete a;
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.0;
|
pragma solidity >=0.0;
|
||||||
contract test {
|
contract test {
|
||||||
struct S {
|
bytes a;
|
||||||
uint x;
|
function f() public {
|
||||||
}
|
delete a;
|
||||||
S str;
|
|
||||||
constructor() {
|
|
||||||
delete str;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,5 +16,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access #
|
// f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access #
|
||||||
|
@ -31,6 +31,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// l() -> 0
|
// l() -> 0
|
||||||
// f(uint256): 42 ->
|
// f(uint256): 42 ->
|
||||||
|
@ -18,6 +18,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> true
|
// f() -> true
|
||||||
// a() -> 7
|
// a() -> 7
|
||||||
|
@ -21,5 +21,7 @@ contract C {
|
|||||||
return (s.f(), h(s));
|
return (s.f(), h(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// g() -> 7, 7
|
// g() -> 7, 7
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint16 v;
|
||||||
|
S[] x;
|
||||||
|
}
|
||||||
|
uint8[77] padding;
|
||||||
|
S s;
|
||||||
|
constructor() {
|
||||||
|
s.v = 21;
|
||||||
|
s.x.push(); s.x.push(); s.x.push();
|
||||||
|
s.x[0].v = 101; s.x[1].v = 102; s.x[2].v = 103;
|
||||||
|
}
|
||||||
|
function f() public returns (uint256 a, uint256 b, uint256 c, uint256 d) {
|
||||||
|
S storage sptr1 = s.x[0];
|
||||||
|
S storage sptr2 = s.x[1];
|
||||||
|
S storage sptr3 = s.x[2];
|
||||||
|
uint256 slot1; uint256 slot2; uint256 slot3;
|
||||||
|
assembly { slot1 := sptr1.slot slot2 := sptr2.slot slot3 := sptr3.slot }
|
||||||
|
delete s;
|
||||||
|
assembly { a := sload(s.slot) b := sload(slot1) c := sload(slot2) d := sload(slot3) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0, 0, 0, 0
|
@ -15,5 +15,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 1
|
// f() -> 1
|
||||||
|
@ -16,5 +16,7 @@ contract test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// deleteMember() -> 0
|
// deleteMember() -> 0
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint256 x;
|
||||||
|
uint128 y;
|
||||||
|
uint32 z;
|
||||||
|
}
|
||||||
|
uint8 b = 23;
|
||||||
|
S s;
|
||||||
|
uint8 a = 17;
|
||||||
|
function f() public {
|
||||||
|
s.x = 42; s.y = 42; s.y = 42;
|
||||||
|
delete s;
|
||||||
|
assert(s.x == 0);
|
||||||
|
assert(s.y == 0);
|
||||||
|
assert(s.z == 0);
|
||||||
|
assert(b == 23);
|
||||||
|
assert(a == 17);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() ->
|
@ -0,0 +1,47 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint128 a;
|
||||||
|
uint256[] x;
|
||||||
|
uint240 b;
|
||||||
|
}
|
||||||
|
uint8 b = 23;
|
||||||
|
S s;
|
||||||
|
uint8 a = 17;
|
||||||
|
function f() public {
|
||||||
|
delete s;
|
||||||
|
s.x.push(42); s.x.push(42); s.x.push(42);
|
||||||
|
delete s;
|
||||||
|
assert(s.x.length == 0);
|
||||||
|
uint256[] storage x = s.x;
|
||||||
|
assembly { sstore(x.slot, 3) }
|
||||||
|
assert(s.x[0] == 0);
|
||||||
|
assert(s.x[1] == 0);
|
||||||
|
assert(s.x[2] == 0);
|
||||||
|
assert(b == 23);
|
||||||
|
assert(a == 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public {
|
||||||
|
delete s;
|
||||||
|
s.x.push(42); s.x.push(42); s.x.push(42);
|
||||||
|
s.a = 1; s.b = 2;
|
||||||
|
delete s.x;
|
||||||
|
assert(s.x.length == 0);
|
||||||
|
uint256[] storage x = s.x;
|
||||||
|
assembly { sstore(x.slot, 3) }
|
||||||
|
assert(s.x[0] == 0);
|
||||||
|
assert(s.x[1] == 0);
|
||||||
|
assert(s.x[2] == 0);
|
||||||
|
assert(b == 23);
|
||||||
|
assert(a == 17);
|
||||||
|
assert(s.a == 1);
|
||||||
|
assert(s.b == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() ->
|
||||||
|
// g() ->
|
@ -14,5 +14,7 @@ contract test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// deleteIt() -> 0
|
// deleteIt() -> 0
|
||||||
|
@ -18,6 +18,8 @@ contract test {
|
|||||||
inner.recursive[0].z = inner.recursive[1].z + 1;
|
inner.recursive[0].z = inner.recursive[1].z + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// check() -> false
|
// check() -> false
|
||||||
// set() ->
|
// set() ->
|
||||||
|
@ -22,5 +22,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> true
|
// f() -> true
|
||||||
|
Loading…
Reference in New Issue
Block a user