diff --git a/Changelog.md b/Changelog.md index 50cf3b0c6..8f8489d1d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Important Bugfixes: * Immutables: Properly perform sign extension on signed immutables. + * User Defined Value Type: Fix storage layout of user defined value types for underlying types shorter than 32 bytes. Bugfixes: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 3e6c52467..2220875b1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1108,6 +1108,8 @@ public: bool leftAligned() const override { return underlyingType().leftAligned(); } bool canBeStored() const override { return underlyingType().canBeStored(); } u256 storageSize() const override { return underlyingType().storageSize(); } + unsigned storageBytes() const override { return underlyingType().storageBytes(); } + bool isValueType() const override { solAssert(underlyingType().isValueType(), ""); @@ -1119,6 +1121,25 @@ public: return true; } + bool containsNestedMapping() const override + { + solAssert(nameable(), "Called for a non nameable type."); + solAssert(!underlyingType().containsNestedMapping(), ""); + return false; + } + + bool hasSimpleZeroValueInMemory() const override + { + solAssert(underlyingType().hasSimpleZeroValueInMemory(), ""); + return true; + } + + bool dataStoredIn(DataLocation _loc) const override + { + solAssert(!underlyingType().dataStoredIn(_loc), ""); + return false; + } + std::string toString(bool _short) const override; std::string canonicalName() const override { solAssert(false, ""); } std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); } diff --git a/test/cmdlineTests/storage_layout_user_defined/args b/test/cmdlineTests/storage_layout_user_defined/args new file mode 100644 index 000000000..f69ac4e39 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/args @@ -0,0 +1 @@ +--storage-layout diff --git a/test/cmdlineTests/storage_layout_user_defined/err b/test/cmdlineTests/storage_layout_user_defined/err new file mode 100644 index 000000000..a9b5f07b1 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/err @@ -0,0 +1,2 @@ +Warning: Source file does not specify required compiler version! +--> storage_layout_user_defined/input.sol diff --git a/test/cmdlineTests/storage_layout_user_defined/input.sol b/test/cmdlineTests/storage_layout_user_defined/input.sol new file mode 100644 index 000000000..f07020081 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/input.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL v3 +type MyInt128 is int128; +type MyInt8 is int8; +contract C { + // slot 0 + MyInt128 a; + MyInt128 b; + // slot 1 + MyInt128 c; + MyInt8 d; + MyInt8 e; + MyInt8 f; + MyInt8 g; + // slot 2 + MyInt8 h; +} diff --git a/test/cmdlineTests/storage_layout_user_defined/output b/test/cmdlineTests/storage_layout_user_defined/output new file mode 100644 index 000000000..0f6890dae --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/output @@ -0,0 +1,4 @@ + +======= storage_layout_user_defined/input.sol:C ======= +Contract Storage Layout: +{"storage":[{"astId":7,"contract":"storage_layout_user_defined/input.sol:C","label":"a","offset":0,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":10,"contract":"storage_layout_user_defined/input.sol:C","label":"b","offset":16,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":13,"contract":"storage_layout_user_defined/input.sol:C","label":"c","offset":0,"slot":"1","type":"t_userDefinedValueType(MyInt128)2"},{"astId":16,"contract":"storage_layout_user_defined/input.sol:C","label":"d","offset":16,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":19,"contract":"storage_layout_user_defined/input.sol:C","label":"e","offset":17,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":22,"contract":"storage_layout_user_defined/input.sol:C","label":"f","offset":18,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":25,"contract":"storage_layout_user_defined/input.sol:C","label":"g","offset":19,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":28,"contract":"storage_layout_user_defined/input.sol:C","label":"h","offset":20,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"}],"types":{"t_userDefinedValueType(MyInt128)2":{"encoding":"inplace","label":"MyInt128","numberOfBytes":"16"},"t_userDefinedValueType(MyInt8)4":{"encoding":"inplace","label":"MyInt8","numberOfBytes":"1"}}} diff --git a/test/libsolidity/semanticTests/userDefinedValueType/storage_layout.sol b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout.sol new file mode 100644 index 000000000..aab0b86d8 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout.sol @@ -0,0 +1,74 @@ +type MyInt8 is int8; +type MyAddress is address; +type MyInt96 is int96; + +contract C { + MyInt8 a; + MyInt8 b; + MyInt8 c; + MyAddress d; + + MyAddress e; + + MyAddress f; + MyInt96 g; + + function storage_a() pure external returns(uint slot, uint offset) { + assembly { + slot := a.slot + offset := a.offset + } + } + + function storage_b() pure external returns(uint slot, uint offset) { + assembly { + slot := b.slot + offset := b.offset + } + } + + function storage_c() pure external returns(uint slot, uint offset) { + assembly { + slot := d.slot + offset := c.offset + } + } + function storage_d() pure external returns(uint slot, uint offset) { + assembly { + slot := d.slot + offset := d.offset + } + } + + function storage_e() pure external returns(uint slot, uint offset) { + assembly { + slot := e.slot + offset := e.offset + } + } + + function storage_f() pure external returns(uint slot, uint offset) { + assembly { + slot := f.slot + offset := f.offset + } + } + + function storage_g() pure external returns(uint slot, uint offset) { + assembly { + slot := g.slot + offset := g.offset + } + } + +} +// ==== +// compileViaYul: also +// ---- +// storage_a() -> 0, 0 +// storage_b() -> 0, 1 +// storage_c() -> 0, 2 +// storage_d() -> 0, 3 +// storage_e() -> 1, 0 +// storage_f() -> 2, 0 +// storage_g() -> 2, 0x14 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/storage_layout_struct.sol b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout_struct.sol new file mode 100644 index 000000000..445cd3d00 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout_struct.sol @@ -0,0 +1,167 @@ +type MyInt64 is int64; +struct HalfSlot { + MyInt64 a; + MyInt64 b; +} + +struct RegularHalfSlot { + int64 a; + int64 b; +} + +type MyAddress is address; +type MyInt96 is int96; +struct FullSlot { + MyInt96 a; + MyAddress b; +} +struct RegularFullSlot { + int96 a; + address b; +} + +contract C { + HalfSlot public a; + RegularHalfSlot public ra; + + HalfSlot public b; + RegularHalfSlot public rb; + + HalfSlot public c; + RegularHalfSlot public rc; + + FullSlot public d; + RegularFullSlot public rd; + + function storage_a() pure external returns(uint slot, uint offset) { + assembly { + slot := a.slot + offset := a.offset + } + } + + function storage_ra() pure external returns(uint slot, uint offset) { + assembly { + slot := ra.slot + offset := ra.offset + } + } + + function storage_b() pure external returns(uint slot, uint offset) { + assembly { + slot := b.slot + offset := b.offset + } + } + + function storage_rb() pure external returns(uint slot, uint offset) { + assembly { + slot := rb.slot + offset := rb.offset + } + } + + function storage_c() pure external returns(uint slot, uint offset) { + assembly { + slot := c.slot + offset := c.offset + } + } + + function storage_rc() pure external returns(uint slot, uint offset) { + assembly { + slot := rc.slot + offset := rc.offset + } + } + + function storage_d() pure external returns(uint slot, uint offset) { + assembly { + slot := d.slot + offset := d.offset + } + } + + function storage_rd() pure external returns(uint slot, uint offset) { + assembly { + slot := rd.slot + offset := rd.offset + } + } + + + function set_a(MyInt64 _a, MyInt64 _b) external { + a.a = _a; + a.b = _b; + } + + function set_ra(int64 _a, int64 _b) external { + ra.a = _a; + ra.b = _b; + } + + function set_b(MyInt64 _a, MyInt64 _b) external { + b.a = _a; + b.b = _b; + } + + function set_rb(int64 _a, int64 _b) external { + rb.a = _a; + rb.b = _b; + } + + function set_c(MyInt64 _a, MyInt64 _b) external { + c.a = _a; + c.b = _b; + } + + function set_rc(int64 _a, int64 _b) external { + rc.a = _a; + rc.b = _b; + } + + function set_d(MyInt96 _a, MyAddress _b) external { + d.a = _a; + d.b = _b; + } + + function set_rd(int96 _a, address _b) external { + rd.a = _a; + rd.b = _b; + } + + function read_slot(uint slot) view external returns (uint value) { + assembly { + value := sload(slot) + } + } + +} + +// ==== +// compileViaYul: also +// ---- +// storage_a() -> 0, 0 +// set_a(int64,int64): 100, 200 -> +// read_slot(uint256): 0 -> 0xc80000000000000064 +// storage_ra() -> 1, 0 +// set_ra(int64,int64): 100, 200 -> +// read_slot(uint256): 1 -> 0xc80000000000000064 +// storage_b() -> 2, 0 +// set_b(int64,int64): 0, 200 -> +// read_slot(uint256): 2 -> 3689348814741910323200 +// storage_rb() -> 3, 0 +// set_rb(int64,int64): 0, 200 -> +// read_slot(uint256): 3 -> 3689348814741910323200 +// storage_c() -> 4, 0 +// set_c(int64,int64): 100, 0 -> +// read_slot(uint256): 4 -> 0x64 +// storage_rc() -> 5, 0 +// set_rc(int64,int64): 100, 0 -> +// read_slot(uint256): 5 -> 0x64 +// storage_d() -> 6, 0 +// set_d(int96,address): 39614081257132168796771975167, 1461501637330902918203684832716283019655932542975 -> +// read_slot(uint256): 6 -> -39614081257132168796771975169 +// storage_rd() -> 7, 0 +// set_rd(int96,address): 39614081257132168796771975167, 1461501637330902918203684832716283019655932542975 -> +// read_slot(uint256): 7 -> -39614081257132168796771975169