mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9602 from ethereum/structMemToStorageSol2Yul
[Sol->Yul] Implementing various copying of structs
This commit is contained in:
commit
3a48be546d
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
* Allow function definitions outside of contracts, behaving much like internal library functions.
|
* Allow function definitions outside of contracts, behaving much like internal library functions.
|
||||||
|
* Code generator: Implementing copying structs from calldata to storage.
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* SMTChecker: Add underflow and overflow as verification conditions in the CHC engine.
|
* SMTChecker: Add underflow and overflow as verification conditions in the CHC engine.
|
||||||
|
@ -357,38 +357,47 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
"Struct assignment with conversion."
|
"Struct assignment with conversion."
|
||||||
);
|
);
|
||||||
solAssert(!structType.containsNestedMapping(), "");
|
solAssert(!structType.containsNestedMapping(), "");
|
||||||
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
|
if (sourceType.location() == DataLocation::CallData)
|
||||||
for (auto const& member: structType.members(nullptr))
|
|
||||||
{
|
{
|
||||||
// assign each member that can live outside of storage
|
solAssert(sourceType.sizeOnStack() == 1, "");
|
||||||
TypePointer const& memberType = member.type;
|
solAssert(structType.sizeOnStack() == 1, "");
|
||||||
solAssert(memberType->nameable(), "");
|
m_context << Instruction::DUP2 << Instruction::DUP2;
|
||||||
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(sourceType, structType, 0), 2, 0);
|
||||||
if (sourceType.location() == DataLocation::Storage)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto const& member: structType.members(nullptr))
|
||||||
{
|
{
|
||||||
// stack layout: source_ref target_ref
|
// assign each member that can live outside of storage
|
||||||
pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
|
TypePointer const& memberType = member.type;
|
||||||
m_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
solAssert(memberType->nameable(), "");
|
||||||
|
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
||||||
|
if (sourceType.location() == DataLocation::Storage)
|
||||||
|
{
|
||||||
|
// stack layout: source_ref target_ref
|
||||||
|
pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
|
||||||
|
m_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||||
|
m_context << u256(offsets.second);
|
||||||
|
// stack: source_ref target_ref source_member_ref source_member_off
|
||||||
|
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||||
|
// stack: source_ref target_ref source_value...
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(sourceType.location() == DataLocation::Memory, "");
|
||||||
|
// stack layout: source_ref target_ref
|
||||||
|
m_context << sourceType.memoryOffsetOfMember(member.name);
|
||||||
|
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||||
|
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||||
|
// stack layout: source_ref target_ref source_value...
|
||||||
|
}
|
||||||
|
unsigned stackSize = sourceMemberType->sizeOnStack();
|
||||||
|
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
||||||
|
m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD;
|
||||||
m_context << u256(offsets.second);
|
m_context << u256(offsets.second);
|
||||||
// stack: source_ref target_ref source_member_ref source_member_off
|
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
||||||
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
||||||
// stack: source_ref target_ref source_value...
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
solAssert(sourceType.location() == DataLocation::Memory, "");
|
|
||||||
// stack layout: source_ref target_ref
|
|
||||||
m_context << sourceType.memoryOffsetOfMember(member.name);
|
|
||||||
m_context << Instruction::DUP3 << Instruction::ADD;
|
|
||||||
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
|
||||||
// stack layout: source_ref target_ref source_value...
|
|
||||||
}
|
|
||||||
unsigned stackSize = sourceMemberType->sizeOnStack();
|
|
||||||
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
|
||||||
m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD;
|
|
||||||
m_context << u256(offsets.second);
|
|
||||||
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
|
||||||
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
|
||||||
}
|
}
|
||||||
// stack layout: source_ref target_ref
|
// stack layout: source_ref target_ref
|
||||||
solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size.");
|
solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size.");
|
||||||
|
@ -964,7 +964,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
|
|||||||
("dataAreaFunction", arrayDataAreaFunction(_type))
|
("dataAreaFunction", arrayDataAreaFunction(_type))
|
||||||
("isByteArray", _type.isByteArray())
|
("isByteArray", _type.isByteArray())
|
||||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||||
("storeValue", updateStorageValueFunction(*_type.baseType()))
|
("storeValue", updateStorageValueFunction(*_type.baseType(), *_type.baseType()))
|
||||||
("maxArrayLength", (u256(1) << 64).str())
|
("maxArrayLength", (u256(1) << 64).str())
|
||||||
("shl", shiftLeftFunctionDynamic())
|
("shl", shiftLeftFunctionDynamic())
|
||||||
("shr", shiftRightFunction(248))
|
("shr", shiftRightFunction(248))
|
||||||
@ -994,7 +994,7 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
|
|||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("fetchLength", arrayLengthFunction(_type))
|
("fetchLength", arrayLengthFunction(_type))
|
||||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||||
("storeValue", updateStorageValueFunction(*_type.baseType()))
|
("storeValue", updateStorageValueFunction(*_type.baseType(), *_type.baseType()))
|
||||||
("maxArrayLength", (u256(1) << 64).str())
|
("maxArrayLength", (u256(1) << 64).str())
|
||||||
("zeroValueFunction", zeroValueFunction(*_type.baseType()))
|
("zeroValueFunction", zeroValueFunction(*_type.baseType()))
|
||||||
.render();
|
.render();
|
||||||
@ -1404,15 +1404,35 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
|
|||||||
|
|
||||||
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
|
if (_type.isValueType())
|
||||||
|
return readFromStorageValueType(_type, _offset, _splitFunctionTypes);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(_offset == 0, "");
|
||||||
|
return readFromStorageReferenceType(_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
|
||||||
|
{
|
||||||
|
solAssert(_type.isValueType(), "");
|
||||||
|
return readFromStorageValueTypeDynamic(_type, _splitFunctionTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||||
|
{
|
||||||
|
solAssert(_type.isValueType(), "");
|
||||||
|
|
||||||
if (_type.category() == Type::Category::Function)
|
if (_type.category() == Type::Category::Function)
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
string functionName =
|
string functionName =
|
||||||
"read_from_storage_" +
|
"read_from_storage_" +
|
||||||
string(_splitFunctionTypes ? "split_" : "") +
|
string(_splitFunctionTypes ? "split_" : "") +
|
||||||
"offset_" +
|
"offset_" +
|
||||||
to_string(_offset) +
|
to_string(_offset) +
|
||||||
"_" +
|
"_" +
|
||||||
_type.identifier();
|
_type.identifier();
|
||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
solAssert(_type.sizeOnStack() == 1, "");
|
solAssert(_type.sizeOnStack() == 1, "");
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
@ -1420,18 +1440,19 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
|
|||||||
value := <extract>(sload(slot))
|
value := <extract>(sload(slot))
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("extract", extractFromStorageValue(_type, _offset, false))
|
("extract", extractFromStorageValue(_type, _offset, false))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
string YulUtilFunctions::readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes)
|
||||||
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
|
|
||||||
{
|
{
|
||||||
|
solAssert(_type.isValueType(), "");
|
||||||
if (_type.category() == Type::Category::Function)
|
if (_type.category() == Type::Category::Function)
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"read_from_storage_dynamic" +
|
"read_from_storage_value_type_dynamic" +
|
||||||
string(_splitFunctionTypes ? "split_" : "") +
|
string(_splitFunctionTypes ? "split_" : "") +
|
||||||
"_" +
|
"_" +
|
||||||
_type.identifier();
|
_type.identifier();
|
||||||
@ -1447,6 +1468,55 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu
|
|||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_type.category() == Type::Category::Struct, "");
|
||||||
|
|
||||||
|
string functionName = "read_from_storage_reference_type_" + _type.identifier();
|
||||||
|
|
||||||
|
auto const& structType = dynamic_cast<StructType const&>(_type);
|
||||||
|
solAssert(structType.location() == DataLocation::Memory, "");
|
||||||
|
MemberList::MemberMap structMembers = structType.nativeMembers(nullptr);
|
||||||
|
vector<map<string, string>> memberSetValues(structMembers.size());
|
||||||
|
for (size_t i = 0; i < structMembers.size(); ++i)
|
||||||
|
{
|
||||||
|
auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name);
|
||||||
|
|
||||||
|
memberSetValues[i]["setMember"] = Whiskers(R"(
|
||||||
|
{
|
||||||
|
let <memberValues> := <readFromStorage>(add(slot, <memberSlotDiff>)<?hasOffset>, <memberStorageOffset></hasOffset>)
|
||||||
|
<writeToMemory>(add(value, <memberMemoryOffset>), <memberValues>)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("memberValues", suffixedVariableNameList("memberValue_", 0, structMembers[i].type->stackItems().size()))
|
||||||
|
("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str())
|
||||||
|
("memberSlotDiff", memberSlotDiff.str())
|
||||||
|
("memberStorageOffset", to_string(memberStorageOffset))
|
||||||
|
("readFromStorage",
|
||||||
|
structMembers[i].type->isValueType() ?
|
||||||
|
readFromStorageDynamic(*structMembers[i].type, true) :
|
||||||
|
readFromStorage(*structMembers[i].type, memberStorageOffset, true)
|
||||||
|
)
|
||||||
|
("writeToMemory", writeToMemoryFunction(*structMembers[i].type))
|
||||||
|
("hasOffset", structMembers[i].type->isValueType())
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot) -> value {
|
||||||
|
value := <allocStruct>()
|
||||||
|
<#member>
|
||||||
|
<setMember>
|
||||||
|
</member>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("allocStruct", allocateMemoryStructFunction(structType))
|
||||||
|
("member", memberSetValues)
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::readFromMemory(Type const& _type)
|
string YulUtilFunctions::readFromMemory(Type const& _type)
|
||||||
{
|
{
|
||||||
@ -1458,18 +1528,25 @@ string YulUtilFunctions::readFromCalldata(Type const& _type)
|
|||||||
return readFromMemoryOrCalldata(_type, true);
|
return readFromMemoryOrCalldata(_type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::optional<unsigned> const& _offset)
|
string YulUtilFunctions::updateStorageValueFunction(
|
||||||
|
Type const& _fromType,
|
||||||
|
Type const& _toType,
|
||||||
|
std::optional<unsigned> const& _offset
|
||||||
|
)
|
||||||
{
|
{
|
||||||
string const functionName =
|
string const functionName =
|
||||||
"update_storage_value_" +
|
"update_storage_value_" +
|
||||||
(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
|
(_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") +
|
||||||
_type.identifier();
|
_fromType.identifier() +
|
||||||
|
"_to_" +
|
||||||
|
_toType.identifier();
|
||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&] {
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
if (_type.isValueType())
|
if (_toType.isValueType())
|
||||||
{
|
{
|
||||||
solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size.");
|
solAssert(_fromType.isImplicitlyConvertibleTo(_toType), "");
|
||||||
solAssert(_type.storageBytes() > 0, "Invalid storage bytes size.");
|
solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size.");
|
||||||
|
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot, <offset>value) {
|
function <functionName>(slot, <offset>value) {
|
||||||
@ -1480,19 +1557,83 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::opti
|
|||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("update",
|
("update",
|
||||||
_offset.has_value() ?
|
_offset.has_value() ?
|
||||||
updateByteSliceFunction(_type.storageBytes(), *_offset) :
|
updateByteSliceFunction(_toType.storageBytes(), *_offset) :
|
||||||
updateByteSliceFunctionDynamic(_type.storageBytes())
|
updateByteSliceFunctionDynamic(_toType.storageBytes())
|
||||||
)
|
)
|
||||||
("offset", _offset.has_value() ? "" : "offset, ")
|
("offset", _offset.has_value() ? "" : "offset, ")
|
||||||
("prepare", prepareStoreFunction(_type))
|
("prepare", prepareStoreFunction(_toType))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_type.category() == Type::Category::Array)
|
auto const* toReferenceType = dynamic_cast<ReferenceType const*>(&_toType);
|
||||||
solUnimplementedAssert(false, "");
|
auto const* fromReferenceType = dynamic_cast<ReferenceType const*>(&_toType);
|
||||||
else if (_type.category() == Type::Category::Struct)
|
solAssert(fromReferenceType && toReferenceType, "");
|
||||||
|
solAssert(*toReferenceType->copyForLocation(
|
||||||
|
fromReferenceType->location(),
|
||||||
|
fromReferenceType->isPointer()
|
||||||
|
).get() == *fromReferenceType, "");
|
||||||
|
|
||||||
|
if (_toType.category() == Type::Category::Array)
|
||||||
solUnimplementedAssert(false, "");
|
solUnimplementedAssert(false, "");
|
||||||
|
else if (_toType.category() == Type::Category::Struct)
|
||||||
|
{
|
||||||
|
solAssert(_fromType.category() == Type::Category::Struct, "");
|
||||||
|
auto const& fromStructType = dynamic_cast<StructType const&>(_fromType);
|
||||||
|
auto const& toStructType = dynamic_cast<StructType const&>(_toType);
|
||||||
|
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
|
||||||
|
solAssert(fromStructType.location() != DataLocation::Storage, "");
|
||||||
|
solUnimplementedAssert(_offset.has_value() && _offset.value() == 0, "");
|
||||||
|
|
||||||
|
Whiskers templ(R"(
|
||||||
|
function <functionName>(slot, value) {
|
||||||
|
<#member>
|
||||||
|
{
|
||||||
|
<updateMemberCall>
|
||||||
|
}
|
||||||
|
</member>
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", functionName);
|
||||||
|
|
||||||
|
MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr);
|
||||||
|
|
||||||
|
vector<map<string, string>> memberParams(structMembers.size());
|
||||||
|
for (size_t i = 0; i < structMembers.size(); ++i)
|
||||||
|
{
|
||||||
|
solAssert(structMembers[i].type->memoryHeadSize() == 32, "");
|
||||||
|
bool fromCalldata = fromStructType.location() == DataLocation::CallData;
|
||||||
|
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name);
|
||||||
|
memberParams[i]["updateMemberCall"] = Whiskers(R"(
|
||||||
|
let <memberValues> := <loadFromMemoryOrCalldata>(add(value, <memberOffset>))
|
||||||
|
<updateMember>(add(slot, <memberStorageSlotDiff>), <?hasOffset><memberStorageOffset>,</hasOffset> <memberValues>)
|
||||||
|
)")
|
||||||
|
("memberValues", suffixedVariableNameList(
|
||||||
|
"memberValue_",
|
||||||
|
0,
|
||||||
|
structMembers[i].type->stackItems().size()
|
||||||
|
))
|
||||||
|
("hasOffset", structMembers[i].type->isValueType())
|
||||||
|
(
|
||||||
|
"updateMember",
|
||||||
|
structMembers[i].type->isValueType() ?
|
||||||
|
updateStorageValueFunction(*structMembers[i].type, *structMembers[i].type) :
|
||||||
|
updateStorageValueFunction(*structMembers[i].type, *structMembers[i].type, offset)
|
||||||
|
)
|
||||||
|
("memberStorageSlotDiff", slotDiff.str())
|
||||||
|
("memberStorageOffset", to_string(offset))
|
||||||
|
("memberOffset",
|
||||||
|
fromCalldata ?
|
||||||
|
to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) :
|
||||||
|
fromStructType.memoryOffsetOfMember(structMembers[i].name).str()
|
||||||
|
)
|
||||||
|
("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
templ("member", memberParams);
|
||||||
|
|
||||||
|
return templ.render();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "Invalid non-value type for assignment.");
|
solAssert(false, "Invalid non-value type for assignment.");
|
||||||
}
|
}
|
||||||
@ -2052,13 +2193,27 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
|
|
||||||
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
|
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
|
||||||
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
|
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
|
||||||
solUnimplementedAssert(fromStructType.location() == DataLocation::CallData, "");
|
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
|
||||||
|
{
|
||||||
|
solAssert(fromStructType.location() == DataLocation::Storage, "");
|
||||||
|
|
||||||
|
body = Whiskers(R"(
|
||||||
|
converted := <readFromStorage>(value)
|
||||||
|
)")
|
||||||
|
("readFromStorage", readFromStorage(toStructType, 0, true))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
body = Whiskers(R"(
|
|
||||||
converted := <abiDecode>(value, calldatasize())
|
|
||||||
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
|
|
||||||
{&toStructType}
|
|
||||||
)).render();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::FixedBytes:
|
case Type::Category::FixedBytes:
|
||||||
@ -2490,7 +2645,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
|
|||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("store", updateStorageValueFunction(_type))
|
("store", updateStorageValueFunction(_type, _type))
|
||||||
("zeroValue", zeroValueFunction(_type))
|
("zeroValue", zeroValueFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
else if (_type.category() == Type::Category::Array)
|
else if (_type.category() == Type::Category::Array)
|
||||||
|
@ -221,8 +221,7 @@ public:
|
|||||||
/// @param _keyType the type of the value provided
|
/// @param _keyType the type of the value provided
|
||||||
std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType);
|
std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType);
|
||||||
|
|
||||||
/// @returns a function that reads a value type from storage.
|
/// @returns a function that reads a type from storage.
|
||||||
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
|
||||||
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
||||||
/// single variable.
|
/// single variable.
|
||||||
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||||
@ -248,7 +247,11 @@ public:
|
|||||||
/// the specified slot and offset. If offset is not given, it is expected as
|
/// the specified slot and offset. If offset is not given, it is expected as
|
||||||
/// runtime parameter.
|
/// runtime parameter.
|
||||||
/// signature: (slot, [offset,] value)
|
/// signature: (slot, [offset,] value)
|
||||||
std::string updateStorageValueFunction(Type const& _type, std::optional<unsigned> const& _offset = std::optional<unsigned>());
|
std::string updateStorageValueFunction(
|
||||||
|
Type const& _fromType,
|
||||||
|
Type const& _toType,
|
||||||
|
std::optional<unsigned> const& _offset = std::optional<unsigned>()
|
||||||
|
);
|
||||||
|
|
||||||
/// Returns the name of a function that will write the given value to
|
/// Returns the name of a function that will write the given value to
|
||||||
/// the specified address.
|
/// the specified address.
|
||||||
@ -401,6 +404,16 @@ private:
|
|||||||
|
|
||||||
std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata);
|
std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata);
|
||||||
|
|
||||||
|
/// @returns a function that reads a value type from storage.
|
||||||
|
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
||||||
|
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
||||||
|
/// single variable.
|
||||||
|
std::string readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||||
|
std::string readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes);
|
||||||
|
|
||||||
|
/// @returns a function that reads a reference type from storage to memory (performing a deep copy).
|
||||||
|
std::string readFromStorageReferenceType(Type const& _type);
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings m_revertStrings;
|
RevertStrings m_revertStrings;
|
||||||
MultiUseYulFunctionCollector& m_functionCollector;
|
MultiUseYulFunctionCollector& m_functionCollector;
|
||||||
|
@ -314,9 +314,9 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
|||||||
|
|
||||||
writeToLValue(*m_currentLValue, value);
|
writeToLValue(*m_currentLValue, value);
|
||||||
|
|
||||||
m_currentLValue.reset();
|
if (m_currentLValue->type.category() != Type::Category::Struct && *_assignment.annotation().type != *TypeProvider::emptyTuple())
|
||||||
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
|
|
||||||
define(_assignment, value);
|
define(_assignment, value);
|
||||||
|
m_currentLValue.reset();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2506,7 +2506,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
offset = std::get<unsigned>(_storage.offset);
|
offset = std::get<unsigned>(_storage.offset);
|
||||||
|
|
||||||
m_code <<
|
m_code <<
|
||||||
m_utils.updateStorageValueFunction(_lvalue.type, offset) <<
|
m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offset) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
_storage.slot <<
|
_storage.slot <<
|
||||||
(
|
(
|
||||||
|
@ -4,15 +4,16 @@ contract C {
|
|||||||
struct S {
|
struct S {
|
||||||
uint256 a;
|
uint256 a;
|
||||||
uint256 b;
|
uint256 b;
|
||||||
|
bytes2 c;
|
||||||
}
|
}
|
||||||
|
|
||||||
function f(S calldata s) external pure returns (uint256, uint256) {
|
function f(S calldata s) external pure returns (uint256, uint256, byte) {
|
||||||
S memory m = s;
|
S memory m = s;
|
||||||
return (m.a, m.b);
|
return (m.a, m.b, m.c[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====
|
// ====
|
||||||
// compileViaYul: also
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f((uint256,uint256)): 42, 23 -> 42, 23
|
// f((uint256,uint256, bytes2)): 42, 23, "ab" -> 42, 23, "b"
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint256 a;
|
||||||
|
uint64 b;
|
||||||
|
bytes2 c;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[153] r;
|
||||||
|
S s;
|
||||||
|
|
||||||
|
function f(uint32 a, S calldata c, uint256 b) external returns (uint256, uint256, byte) {
|
||||||
|
s = c;
|
||||||
|
return (s.a, s.b, s.c[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint32, (uint256, uint64, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b"
|
@ -3,6 +3,7 @@ contract c {
|
|||||||
uint256 a;
|
uint256 a;
|
||||||
uint256 b;
|
uint256 b;
|
||||||
}
|
}
|
||||||
|
uint[75] r;
|
||||||
Struct data1;
|
Struct data1;
|
||||||
Struct data2;
|
Struct data2;
|
||||||
|
|
||||||
@ -15,5 +16,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> true
|
// test() -> true
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
uint128 b;
|
||||||
|
uint256 c;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct X {
|
||||||
|
uint256 a;
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[79] r;
|
||||||
|
X x;
|
||||||
|
|
||||||
|
function f() external returns (uint32, uint128, uint256) {
|
||||||
|
X memory m = X(12, S(42, 23, 34));
|
||||||
|
x = m;
|
||||||
|
return (x.s.a, x.s.b, x.s.c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 42, 23, 34
|
@ -0,0 +1,33 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
uint128 b;
|
||||||
|
uint256 c;
|
||||||
|
function() internal returns (uint32) f;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct X {
|
||||||
|
uint256 a;
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[79] r;
|
||||||
|
X x;
|
||||||
|
|
||||||
|
function f() external returns (uint32, uint128, uint256, uint32, uint32) {
|
||||||
|
X memory m = X(12, S(42, 23, 34, g));
|
||||||
|
x = m;
|
||||||
|
return (x.s.a, x.s.b, x.s.c, x.s.f(), m.s.f());
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() internal returns (uint32) {
|
||||||
|
return x.s.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: false
|
||||||
|
// ----
|
||||||
|
// f() -> 42, 23, 34, 42, 42
|
@ -10,5 +10,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// s() -> 1, true
|
// s() -> 1, true
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
uint128 b;
|
||||||
|
uint256 c;
|
||||||
|
}
|
||||||
|
struct X {
|
||||||
|
uint32 a;
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[79] arr;
|
||||||
|
X x = X(12, S(42, 23, 34));
|
||||||
|
|
||||||
|
function f() external returns (uint32, uint128, uint256) {
|
||||||
|
X memory m = x;
|
||||||
|
return (m.s.a, m.s.b, m.s.c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 42, 23, 34
|
@ -0,0 +1,32 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint32 a;
|
||||||
|
uint128 b;
|
||||||
|
uint256 c;
|
||||||
|
function() internal returns (uint32) f;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct X {
|
||||||
|
uint256 a;
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[79] arr;
|
||||||
|
X x = X(12, S(42, 23, 34, g));
|
||||||
|
|
||||||
|
function f() external returns (uint32, uint128, uint256, uint32, uint32) {
|
||||||
|
X memory m = x;
|
||||||
|
return (m.s.a, m.s.b, m.s.c, m.s.f(), x.s.f());
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() internal returns (uint32) {
|
||||||
|
return x.s.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: false
|
||||||
|
// ----
|
||||||
|
// f() -> 42, 23, 34, 42, 42
|
Loading…
Reference in New Issue
Block a user