diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 2b5bffd46..b90494168 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -170,6 +170,11 @@ public: /// signature: (dataOffset, length, dataEnd) -> decodedArray std::string abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory); + /// Internal decoding function that is also used by some copying routines. + /// @returns the name of a function that decodes structs. + /// signature: (dataStart, dataEnd) -> decodedStruct + std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory); + private: /// Part of @a abiEncodingFunction for array target type and given calldata array. /// Uses calldatacopy and does not perform cleanup or validation and can therefore only @@ -245,8 +250,6 @@ private: std::string abiDecodingFunctionCalldataStruct(StructType const& _type); /// Part of @a abiDecodingFunction for array types. std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack); - /// Part of @a abiDecodingFunction for struct types. - std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory); /// @returns the name of a function that retrieves an element from calldata. std::string calldataAccessFunction(Type const& _type); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 202f95499..791e8b5b9 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2992,14 +2992,16 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, ""); if (fromStructType.location() == DataLocation::CallData) - { - solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), ""); body = Whiskers(R"( converted := (value, calldatasize()) - )")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder( - {&toStructType} - )).render(); - } + )") + ( + "abiDecode", + ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).abiDecodingFunctionStruct( + toStructType, + false + ) + ).render(); else { solAssert(fromStructType.location() == DataLocation::Storage, ""); diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_nested_structs.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_nested_structs.sol new file mode 100644 index 000000000..ee9527236 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_nested_structs.sol @@ -0,0 +1,49 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + + struct S1 { + uint128 u; + S s; + } + + struct S2 { + S[2] array; + } + + function f1(S1 calldata c) internal returns(S1 calldata) { + return c; + } + + function f(S1 calldata c, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + S1 memory m = f1(c); + assert(m.s.a[0][0] == c.s.a[0][0]); + assert(m.s.a[1][1] == c.s.a[1][1]); + return (p, m.s.p1, m.s.a[0][0], m.s.a[1][1], m.s.p2); + } + + function g(S2 calldata c) external returns(uint128, uint256, uint256, uint32) { + S2 memory m = c; + assert(m.array[0].a[0][0] == c.array[0].a[0][0]); + assert(m.array[0].a[1][1] == c.array[0].a[1][1]); + return (m.array[1].p1, m.array[1].a[0][0], m.array[1].a[1][1], m.array[1].p2); + } + + function h(S1 calldata c, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + S memory m = c.s; + assert(m.a[0][0] == c.s.a[0][0]); + assert(m.a[1][1] == c.s.a[1][1]); + return (p, m.p1, m.a[0][0], m.a[1][1], m.p2); + } +} +// ==== +// compileViaYul: true +// ---- +// f((uint128, (uint128, uint256[][2], uint32)), uint32): 0x40, 44, 11, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 44, 22, 1, 2, 33 +// g(((uint128, uint256[][2], uint32)[2])): 0x20, 0x20, 0x40, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 22, 1, 2, 33 +// h((uint128, (uint128, uint256[][2], uint32)), uint32): 0x40, 44, 11, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 44, 22, 1, 2, 33 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_argument_of_lib_function.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_argument_of_lib_function.sol new file mode 100644 index 000000000..c44c5cd2a --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_argument_of_lib_function.sol @@ -0,0 +1,29 @@ +pragma experimental ABIEncoderV2; + +struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; +} +struct S1 { + uint128 u; + S s; +} + +library L { + function f(S1 memory m, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + return (p, m.s.p1, m.s.a[0][0], m.s.a[1][1], m.s.p2); + } +} + +contract C { + + function f(S1 calldata c, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + return L.f(c, p); + } +} +// ==== +// compileViaYul: true +// ---- +// library: L +// f((uint128, (uint128, uint256[][2], uint32)), uint32): 0x40, 44, 11, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 44, 22, 1, 2, 33 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_memory_argument.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_memory_argument.sol new file mode 100644 index 000000000..71b55abf4 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_memory_argument.sol @@ -0,0 +1,23 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + + function g(uint32 p1, S memory s) internal returns(uint32, uint128, uint256, uint256, uint32) { + s.p1++; + s.a[0][1]++; + return (p1, s.p1, s.a[0][0], s.a[1][1], s.p2); + } + + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + return g(p1, c); + } +} +// ==== +// compileViaYul: true +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 78, 1, 2, 88 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory_tuple_assignment.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory_tuple_assignment.sol new file mode 100644 index 000000000..9bf1d1f48 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory_tuple_assignment.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + S memory m; + uint32 p2; + (p2, m) = (p1, c); + m.p1++; + m.a[0][1]++; + return (p2, m.p1, m.a[0][0], m.a[1][1], m.p2); + } +} +// ==== +// compileViaYul: true +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 78, 1, 2, 88 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_memory.sol new file mode 100644 index 000000000..9ad5bed9c --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_memory.sol @@ -0,0 +1,23 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + S memory s = c; + assert(s.a[0][0] == c.a[0][0]); + assert(s.a[1][1] == c.a[1][1]); + s.p1++; + assert(s.p1 != c.p1); + s.a[0][1]++; + assert(s.a[0][1] != c.a[0][1]); + return (p1, s.p1, s.a[0][0], s.a[1][1], s.p2); + } +} +// ==== +// compileViaYul: true +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 78, 1, 2, 88