Hi, very interesting question, the answer is yes you could do that, but I wouldnt consider it safe since you would have to be very careful not to accidentally overlap storages.
The storage layout for MyStruct
version 1 would be, ignoring the astId
s:
"members": [
{
"astId": 486,
"contract": "",
"label": "val1",
"offset": 0,
"slot": "0",
"type": "t_uint16"
},
{
"astId": 488,
"contract": "",
"label": "val2",
"offset": 2,
"slot": "0",
"type": "t_uint16"
},
{
"astId": 490,
"contract": "",
"label": "val3",
"offset": 4,
"slot": "0",
"type": "t_uint64"
},
{
"astId": 492,
"contract": "",
"label": "name",
"offset": 0,
"slot": "1",
"type": "t_string_storage"
}
],
And for the second version:
"members": [
{
"astId": 577,
"contract": "",
"label": "val1",
"offset": 0,
"slot": "0",
"type": "t_uint16"
},
{
"astId": 579,
"contract": "",
"label": "val2",
"offset": 2,
"slot": "0",
"type": "t_uint16"
},
{
"astId": 581,
"contract": "",
"label": "val3",
"offset": 4,
"slot": "0",
"type": "t_uint64"
},
{
"astId": 583,
"contract": "",
"label": "val4",
"offset": 12,
"slot": "0",
"type": "t_uint160"
},
{
"astId": 585,
"contract": "",
"label": "name",
"offset": 0,
"slot": "1",
"type": "t_string_storage"
}
],
As you can see all uint values will belong to the same slot on different offsets, so in theory it can be possible to use this gap in structs when upgrading contracts.
Note: For this you would need to manually implement the upgrades since the OZ tool for upgrades wont allow this.