Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Compiler Features:

Bugfixes:
* Code Generator: Fix ICE on parenthesized custom error construction in require statement.
* Code Generator: Fix ICE when assigning a calldata struct containing a member of external function type to a storage struct.
* Commandline Interface: Report proper error instead of ICE on non-hex mixed-case address value given via `--libraries`.


Expand Down
24 changes: 14 additions & 10 deletions libsolidity/codegen/YulUtilFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3792,23 +3792,27 @@ std::string YulUtilFunctions::copyStructToStorageFunction(StructType const& _fro
{
Type const& memberType = *structMembers[i].type;
solAssert(memberType.memoryHeadSize() == 32, "");

// Value types cannot be dynamically encoded
if (_from.location() == DataLocation::CallData)
solAssert(!memberType.isDynamicallyEncoded() || !memberType.isValueType());

auto const&[slotDiff, offset] = _to.storageOffsetsOfMember(structMembers[i].name);

Whiskers t(R"(
let memberSlot := add(slot, <memberStorageSlotDiff>)
let memberSrcPtr := add(value, <memberOffset>)

<?fromCalldata>
let <memberValues> :=
<?dynamicallyEncodedMember>
<accessCalldataTail>(value, memberSrcPtr)
<!dynamicallyEncodedMember>
memberSrcPtr
</dynamicallyEncodedMember>

<?isValueType>
<memberValues> := <read>(<memberValues>)
</isValueType>
<?dynamicallyEncodedMember>
Comment thread
nikola-matic marked this conversation as resolved.
let <memberValues> := <accessCalldataTail>(value, memberSrcPtr)
<!dynamicallyEncodedMember>
<?isValueType>
let <memberValues> := <read>(memberSrcPtr)
<!isValueType>
let <memberValues> := memberSrcPtr
</isValueType>
</dynamicallyEncodedMember>
Comment on lines +3807 to +3815

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a silent assumption that value types are never dynamically encoded. I think that's true now, but such types are technically possible in the encoding - it would be a case where you have an offset in the head and the value is fixed-size, but still stored in the tail.

No need to handle that, but please at least explicitly assert that dynamicallyEncodedMember && isValueType is impossible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right.
Though before, in that case, it would default to the one read method for value types, possibly generating a miscompilation, I guess.
Added an assertion in the form: p -> q (= !p v q), with p: dynamically encoded, q: not value type.

</fromCalldata>

<?fromMemory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ contract c {
// ----
// test((uint16,uint16,uint16[3],uint16[])): 0x20, 2, 3, 0, 0, 4, 0xC0, 4, 0, 0, 5, 0, 0 -> 2, 3, 4, 5
// gas irOptimized: 137153
// gas legacy: 142414
// gas legacy: 142398
// gas legacyOptimized: 137975
// gas ssaCFGOptimized: 136989

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a few more tests:

  1. struct with external function ptr + uint64 (packing)
  2. struct with external function ptr + uint256[] array (dynamic)
  3. array of structs with external function ptr, i.e.
contract C {
  S[] storageStruct;
  ....
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma abicoder v2;
struct S {
function(uint) external fn_uint;
}

contract C {
S storageStruct;

function test(S calldata calldataStruct) public returns (bool) {
storageStruct = calldataStruct;

assert(storageStruct.fn_uint.address == address(bytes20("01234567890123456789")));
assert(storageStruct.fn_uint.selector == bytes4("abcd"));
return true;
}
}
// ----
// test((function)): "01234567890123456789abcd" -> true
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma abicoder v2;
struct S {
function(uint) external fn_uint;
}

contract C {
S[] storageStruct;

function test(S[] calldata calldataStruct) public returns (bool) {
storageStruct = calldataStruct;

assert(storageStruct.length == 2);
assert(storageStruct[0].fn_uint.address == address(bytes20("01234567890123456789")));
assert(storageStruct[0].fn_uint.selector == bytes4("abcd"));
assert(storageStruct[1].fn_uint.address == address(bytes20("98765432109876543210")));
assert(storageStruct[1].fn_uint.selector == bytes4("efgh"));
return true;
}
}
// ====
// compileViaYul: true
// ----
// test((function)[]): 0x20, 2, "01234567890123456789abcd", "98765432109876543210efgh" -> true
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pragma abicoder v2;
struct S {
function(uint) external fn_uint;
uint256[] arr;
}

contract C {
S storageStruct;

function test(S calldata calldataStruct) public returns (bool) {
storageStruct = calldataStruct;

assert(storageStruct.fn_uint.address == address(bytes20("01234567890123456789")));
assert(storageStruct.fn_uint.selector == bytes4("abcd"));
assert(storageStruct.arr.length == 3);
assert(storageStruct.arr[0] == 11);
assert(storageStruct.arr[1] == 22);
assert(storageStruct.arr[2] == 33);
return true;
}
}
// ----
// test((function,uint256[])): 0x20, "01234567890123456789abcd", 0x40, 3, 11, 22, 33 -> true
// gas irOptimized: 134866
// gas legacy: 137443
// gas legacyOptimized: 135246
// gas ssaCFGOptimized: 134897
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma abicoder v2;
struct S {
function(uint) external fn_uint;
}

contract C {
S storageStruct;

function test(S calldata calldataStruct) public returns (bool) {
storageStruct = calldataStruct;
return true;
}
}
// ----
// test((function)): "01234567890123456789abcd" -> true
// test((function)): 0x3031323334353637383930313233343536373839616263640000000000000001 -> FAILURE
// test((function)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma abicoder v2;
struct S {
function(uint) external fn_uint;
uint64 packed;
}

contract C {
S storageStruct;

function test(S calldata calldataStruct) public returns (bool) {
storageStruct = calldataStruct;

assert(storageStruct.fn_uint.address == address(bytes20("01234567890123456789")));
assert(storageStruct.fn_uint.selector == bytes4("abcd"));
assert(storageStruct.packed == 42);
return true;
}
}
// ----
// test((function,uint64)): "01234567890123456789abcd", 42 -> true
2 changes: 1 addition & 1 deletion test/libsolidity/semanticTests/structs/copy_to_mapping.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ contract C {
// gas ssaCFGOptimized: 122611
// from_calldata((bytes,uint16[],uint16)): 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -> 0x20, 0x60, 0xa0, 21, 3, 0x666f6f0000000000000000000000000000000000000000000000000000000000, 2, 13, 14
// gas irOptimized: 114824
// gas legacy: 118207
// gas legacy: 118199
// gas legacyOptimized: 115327
// gas ssaCFGOptimized: 114700
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ contract C {
// ----
// f(uint32,(uint128,uint256[][2],uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 77, 1, 2, 88
// gas irOptimized: 202902
// gas legacy: 207376
// gas legacy: 207360
// gas legacyOptimized: 203583
// gas ssaCFGOptimized: 202897
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract C {
// s() -> 0, 0, 0x00, 0
// f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 ->
// gas irOptimized: 44237
// gas legacy: 47154
// gas legacy: 47122
// gas legacyOptimized: 44982
// s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15
// g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3
Expand Down