Skip to content

Commit fd68cb1

Browse files
authored
Merge pull request #12579 from ethereum/fix-memory-copy-bug
[Sol2Yul] Fixed an ICE on struct member copy
2 parents ef8911a + 0fe5811 commit fd68cb1

File tree

8 files changed

+262
-2
lines changed

8 files changed

+262
-2
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Bugfixes:
1818
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the parent contract contains immutable variables.
1919
* IR Generator: Add missing cleanup during the conversion of fixed bytes types to smaller fixed bytes types.
2020
* IR Generator: Add missing cleanup for indexed event arguments of value type.
21+
* IR Generator: Fix internal error when copying reference types in calldata and storage to struct or array members in memory.
2122
* IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions.
2223
* Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
2324
* TypeChecker: Fix ICE when a constant variable declaration forward references a struct.

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2985,8 +2985,11 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
29852985
{
29862986
solAssert(_lvalue.type.sizeOnStack() == 1);
29872987
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
2988-
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory));
2989-
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
2988+
solAssert(valueReferenceType);
2989+
if (valueReferenceType->dataStoredIn(DataLocation::Memory))
2990+
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
2991+
else
2992+
appendCode() << "mstore(" + _memory.address + ", " + m_utils.conversionFunction(_value.type(), _lvalue.type) + "(" + _value.commaSeparatedList() + "))\n";
29902993
}
29912994
},
29922995
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Example from https://github.com/ethereum/solidity/issues/12558
2+
pragma abicoder v2;
3+
contract C {
4+
function f(uint[] calldata a) external returns (uint[][] memory) {
5+
uint[][] memory m = new uint[][](2);
6+
m[0] = a;
7+
8+
return m;
9+
}
10+
}
11+
contract Test {
12+
C immutable c = new C();
13+
14+
function test() external returns (bool) {
15+
uint[] memory arr = new uint[](4);
16+
17+
arr[0] = 13;
18+
arr[1] = 14;
19+
arr[2] = 15;
20+
arr[3] = 16;
21+
22+
uint[][] memory ret = c.f(arr);
23+
assert(ret.length == 2);
24+
assert(ret[0].length == 4);
25+
assert(ret[0][0] == 13);
26+
assert(ret[0][1] == 14);
27+
assert(ret[0][2] == 15);
28+
assert(ret[0][3] == 16);
29+
assert(ret[1].length == 0);
30+
31+
return true;
32+
}
33+
}
34+
// ====
35+
// EVMVersion: >homestead
36+
// compileViaYul: also
37+
// ----
38+
// test() -> true
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
pragma abicoder v2;
2+
// Example from https://github.com/ethereum/solidity/issues/12558
3+
struct S {
4+
uint x;
5+
}
6+
7+
contract C {
8+
S sStorage;
9+
constructor() {
10+
sStorage.x = 13;
11+
}
12+
13+
function f() external returns (S[] memory) {
14+
S[] memory sMemory = new S[](1);
15+
16+
sMemory[0] = sStorage;
17+
18+
return sMemory;
19+
}
20+
}
21+
// ====
22+
// compileViaYul: also
23+
// ----
24+
// f() -> 0x20, 1, 13
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
pragma abicoder v2;
2+
3+
struct S { uint value; }
4+
5+
contract Test {
6+
S[][] a;
7+
S[] b;
8+
9+
constructor() {
10+
a.push();
11+
a[0].push(S(1));
12+
a[0].push(S(2));
13+
a[0].push(S(3));
14+
15+
b.push(S(4));
16+
b.push(S(5));
17+
b.push(S(6));
18+
b.push(S(7));
19+
}
20+
21+
function test1() external returns (bool) {
22+
a.push();
23+
a[1] = b;
24+
25+
assert(a.length == 2);
26+
assert(a[0].length == 3);
27+
assert(a[1].length == 4);
28+
assert(a[1][0].value == 4);
29+
assert(a[1][1].value == 5);
30+
assert(a[1][2].value == 6);
31+
assert(a[1][3].value == 7);
32+
33+
return true;
34+
}
35+
36+
function test2() external returns (bool) {
37+
S[][] memory temp = new S[][](2);
38+
39+
temp = a;
40+
41+
assert(temp.length == 2);
42+
assert(temp[0].length == 3);
43+
assert(temp[1].length == 4);
44+
assert(temp[1][0].value == 4);
45+
assert(temp[1][1].value == 5);
46+
assert(temp[1][2].value == 6);
47+
assert(temp[1][3].value == 7);
48+
49+
return true;
50+
}
51+
52+
function test3() external returns (bool) {
53+
S[][] memory temp = new S[][](2);
54+
55+
temp[0] = a[0];
56+
temp[1] = a[1];
57+
58+
assert(temp.length == 2);
59+
assert(temp[0].length == 3);
60+
assert(temp[1].length == 4);
61+
assert(temp[1][0].value == 4);
62+
assert(temp[1][1].value == 5);
63+
assert(temp[1][2].value == 6);
64+
assert(temp[1][3].value == 7);
65+
66+
return true;
67+
}
68+
69+
function test4() external returns (bool) {
70+
S[][] memory temp = new S[][](2);
71+
72+
temp[0] = a[0];
73+
temp[1] = b;
74+
75+
assert(temp.length == 2);
76+
assert(temp[0].length == 3);
77+
assert(temp[1].length == 4);
78+
assert(temp[1][0].value == 4);
79+
assert(temp[1][1].value == 5);
80+
assert(temp[1][2].value == 6);
81+
assert(temp[1][3].value == 7);
82+
83+
return true;
84+
}
85+
}
86+
// ====
87+
// EVMVersion: >homestead
88+
// compileViaYul: also
89+
// ----
90+
// test1() -> true
91+
// gas irOptimized: 150618
92+
// gas legacy: 150266
93+
// gas legacyOptimized: 149875
94+
// test2() -> true
95+
// test3() -> true
96+
// test4() -> true
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
pragma abicoder v2;
2+
struct S {
3+
function () external[] functions;
4+
}
5+
6+
contract C {
7+
function f(function () external[] calldata functions) external returns (S memory) {
8+
S memory s;
9+
s.functions = functions;
10+
return s;
11+
}
12+
}
13+
14+
contract Test {
15+
C immutable c = new C();
16+
17+
function test() external returns (bool) {
18+
function() external[] memory functions = new function() external[](3);
19+
20+
functions[0] = this.random1;
21+
functions[1] = this.random2;
22+
functions[2] = this.random3;
23+
24+
S memory ret = c.f(functions);
25+
26+
assert(ret.functions.length == 3);
27+
assert(ret.functions[0] == this.random1);
28+
assert(ret.functions[1] == this.random2);
29+
assert(ret.functions[2] == this.random3);
30+
31+
return true;
32+
}
33+
function random1() external {
34+
}
35+
function random2() external {
36+
}
37+
function random3() external {
38+
}
39+
}
40+
// ====
41+
// EVMVersion: >homestead
42+
// compileViaYul: also
43+
// ----
44+
// test() -> true
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
pragma abicoder v2;
2+
3+
struct St0 {
4+
bytes el0;
5+
}
6+
contract C {
7+
function f() external returns (St0 memory) {
8+
St0 memory x;
9+
x.el0 = msg.data;
10+
return x;
11+
}
12+
13+
function g() external returns (St0 memory) {
14+
bytes memory temp = msg.data;
15+
St0 memory x;
16+
x.el0 = temp;
17+
return x;
18+
}
19+
20+
function hashes() external returns (bytes4, bytes4) {
21+
return (this.f.selector, this.g.selector);
22+
}
23+
24+
function large(uint256, uint256, uint256, uint256) external returns (St0 memory) {
25+
St0 memory x;
26+
x.el0 = msg.data;
27+
return x;
28+
}
29+
30+
function another_large(uint256, uint256, uint256, uint256) external returns (St0 memory) {
31+
bytes memory temp = msg.data;
32+
St0 memory x;
33+
x.el0 = temp;
34+
return x;
35+
}
36+
37+
}
38+
// ====
39+
// compileViaYul: also
40+
// ----
41+
// f() -> 0x20, 0x20, 4, 0x26121ff000000000000000000000000000000000000000000000000000000000
42+
// g() -> 0x20, 0x20, 4, 0xe2179b8e00000000000000000000000000000000000000000000000000000000
43+
// hashes() -> 0x26121ff000000000000000000000000000000000000000000000000000000000, 0xe2179b8e00000000000000000000000000000000000000000000000000000000
44+
// large(uint256,uint256,uint256,uint256): 1, 2, 3, 4 -> 0x20, 0x20, 0x84, 0xe02492f800000000000000000000000000000000000000000000000000000000, 0x100000000000000000000000000000000000000000000000000000000, 0x200000000000000000000000000000000000000000000000000000000, 0x300000000000000000000000000000000000000000000000000000000, 0x400000000000000000000000000000000000000000000000000000000
45+
// another_large(uint256,uint256,uint256,uint256): 1, 2, 3, 4 -> 0x20, 0x20, 0x84, 0x2a46f85a00000000000000000000000000000000000000000000000000000000, 0x100000000000000000000000000000000000000000000000000000000, 0x200000000000000000000000000000000000000000000000000000000, 0x300000000000000000000000000000000000000000000000000000000, 0x400000000000000000000000000000000000000000000000000000000
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Example from https://github.com/ethereum/solidity/issues/12558
2+
pragma abicoder v2;
3+
contract C {
4+
function() external[1][] s0;
5+
constructor(function() external[1][] memory i0)
6+
{
7+
i0[0] = s0[1];
8+
}
9+
}

0 commit comments

Comments
 (0)