mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
ABIEncoderV2: Implement calldata structs without dynamically encoded members.
This commit is contained in:
parent
94607011dc
commit
0e4912a203
@ -9,6 +9,7 @@ Bugfixes:
|
||||
|
||||
|
||||
Language Features:
|
||||
* Allow calldata structs without dynamically encoded members with ABIEncoderV2.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
@ -371,10 +371,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
|
||||
if (
|
||||
!m_scope->isInterface() &&
|
||||
baseType->category() == Type::Category::Struct &&
|
||||
baseType->dataStoredIn(DataLocation::CallData)
|
||||
)
|
||||
m_errorReporter.typeError(var->location(), "Calldata structs are not yet supported.");
|
||||
if (auto const* structType = dynamic_cast<StructType const*>(baseType.get()))
|
||||
if (structType->isDynamicallyEncoded())
|
||||
m_errorReporter.typeError(var->location(), "Dynamically encoded calldata structs are not yet supported.");
|
||||
|
||||
checkArgumentAndReturnParameter(*var);
|
||||
var->accept(*this);
|
||||
}
|
||||
@ -2071,8 +2073,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
exprType->toString() + " (expected " + funType->selfType()->toString() + ")."
|
||||
);
|
||||
|
||||
if (exprType->category() == Type::Category::Struct)
|
||||
annotation.isLValue = true;
|
||||
if (auto const* structType = dynamic_cast<StructType const*>(exprType.get()))
|
||||
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
|
||||
else if (exprType->category() == Type::Category::Array)
|
||||
{
|
||||
auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
|
||||
|
@ -2053,6 +2053,24 @@ unsigned StructType::calldataEncodedSize(bool) const
|
||||
return size;
|
||||
}
|
||||
|
||||
unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
|
||||
{
|
||||
unsigned offset = 0;
|
||||
for (auto const& member: members(nullptr))
|
||||
{
|
||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||
if (member.name == _member)
|
||||
return offset;
|
||||
{
|
||||
// Struct members are always padded.
|
||||
unsigned memberSize = member.type->calldataEncodedSize(true);
|
||||
solAssert(memberSize != 0, "");
|
||||
offset += memberSize;
|
||||
}
|
||||
}
|
||||
solAssert(false, "Struct member not found.");
|
||||
}
|
||||
|
||||
bool StructType::isDynamicallyEncoded() const
|
||||
{
|
||||
solAssert(!recursive(), "");
|
||||
|
@ -852,6 +852,7 @@ public:
|
||||
|
||||
std::pair<u256, unsigned> const& storageOffsetsOfMember(std::string const& _name) const;
|
||||
u256 memoryOffsetOfMember(std::string const& _name) const;
|
||||
unsigned calldataOffsetOfMember(std::string const& _name) const;
|
||||
|
||||
StructDefinition const& structDefinition() const { return m_struct; }
|
||||
|
||||
|
@ -1246,7 +1246,16 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo
|
||||
return abiDecodingFunctionArray(*arrayType, _fromMemory);
|
||||
}
|
||||
else if (auto const* structType = dynamic_cast<StructType const*>(decodingType.get()))
|
||||
return abiDecodingFunctionStruct(*structType, _fromMemory);
|
||||
{
|
||||
if (structType->dataStoredIn(DataLocation::CallData))
|
||||
{
|
||||
solAssert(!_fromMemory, "");
|
||||
solUnimplementedAssert(!structType->isDynamicallyEncoded(), "Dynamically encoded calldata structs are not yet implemented.");
|
||||
return abiDecodingFunctionCalldataStruct(*structType);
|
||||
}
|
||||
else
|
||||
return abiDecodingFunctionStruct(*structType, _fromMemory);
|
||||
}
|
||||
else if (auto const* functionType = dynamic_cast<FunctionType const*>(decodingType.get()))
|
||||
return abiDecodingFunctionFunctionType(*functionType, _fromMemory, _forUseOnStack);
|
||||
else
|
||||
@ -1423,15 +1432,37 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(_type.calldataEncodedSize(true) != 0, "");
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier();
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers w{R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> value {
|
||||
if slt(sub(end, offset), <minimumSize>) { revert(0, 0) }
|
||||
value := offset
|
||||
}
|
||||
)"};
|
||||
w("functionName", functionName);
|
||||
w("readableTypeName", _type.toString(true));
|
||||
w("minimumSize", to_string(_type.calldataEncodedSize(true)));
|
||||
return w.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory)
|
||||
{
|
||||
solAssert(!_type.dataStoredIn(DataLocation::CallData), "");
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
|
||||
solUnimplementedAssert(!_type.dataStoredIn(DataLocation::CallData), "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
// <readableTypeName>
|
||||
|
@ -222,6 +222,8 @@ private:
|
||||
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type);
|
||||
/// Part of @a abiDecodingFunction for byte array types.
|
||||
std::string abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for calldata struct types.
|
||||
std::string abiDecodingFunctionCalldataStruct(StructType const& _type);
|
||||
/// Part of @a abiDecodingFunction for struct types.
|
||||
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for array types.
|
||||
|
@ -918,8 +918,7 @@ void CompilerUtils::convertType(
|
||||
auto& targetType = dynamic_cast<StructType const&>(_targetType);
|
||||
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
|
||||
solAssert(
|
||||
targetType.location() != DataLocation::CallData &&
|
||||
typeOnStack.location() != DataLocation::CallData
|
||||
targetType.location() != DataLocation::CallData
|
||||
, "");
|
||||
switch (targetType.location())
|
||||
{
|
||||
@ -933,9 +932,9 @@ void CompilerUtils::convertType(
|
||||
break;
|
||||
case DataLocation::Memory:
|
||||
// Copy the array to a free position in memory, unless it is already in memory.
|
||||
if (typeOnStack.location() != DataLocation::Memory)
|
||||
switch (typeOnStack.location())
|
||||
{
|
||||
solAssert(typeOnStack.location() == DataLocation::Storage, "");
|
||||
case DataLocation::Storage:
|
||||
// stack: <source ref>
|
||||
m_context << typeOnStack.memorySize();
|
||||
allocateMemory();
|
||||
@ -955,6 +954,19 @@ void CompilerUtils::convertType(
|
||||
storeInMemoryDynamic(*targetMemberType, true);
|
||||
}
|
||||
m_context << Instruction::POP << Instruction::POP;
|
||||
break;
|
||||
case DataLocation::CallData:
|
||||
{
|
||||
solUnimplementedAssert(!typeOnStack.isDynamicallyEncoded(), "");
|
||||
m_context << Instruction::DUP1;
|
||||
m_context << Instruction::CALLDATASIZE;
|
||||
m_context << Instruction::SUB;
|
||||
abiDecode({targetType.shared_from_this()}, false);
|
||||
break;
|
||||
}
|
||||
case DataLocation::Memory:
|
||||
// nothing to do
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DataLocation::CallData:
|
||||
|
@ -1380,6 +1380,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
|
||||
break;
|
||||
}
|
||||
case DataLocation::CallData:
|
||||
{
|
||||
solUnimplementedAssert(!type.isDynamicallyEncoded(), "");
|
||||
m_context << type.calldataOffsetOfMember(member) << Instruction::ADD;
|
||||
// For non-value types the calldata offset is returned directly.
|
||||
if (_memberAccess.annotation().type->isValueType())
|
||||
{
|
||||
solAssert(_memberAccess.annotation().type->calldataEncodedSize(false) > 0, "");
|
||||
CompilerUtils(m_context).loadFromMemoryDynamic(*_memberAccess.annotation().type, true, true, false);
|
||||
}
|
||||
else
|
||||
solAssert(
|
||||
_memberAccess.annotation().type->category() == Type::Category::Array ||
|
||||
_memberAccess.annotation().type->category() == Type::Category::Struct,
|
||||
""
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "Illegal data location for struct.");
|
||||
}
|
||||
|
@ -8013,6 +8013,248 @@ BOOST_AUTO_TEST_CASE(struct_named_constructor)
|
||||
ABI_CHECK(callContractFunction("s()"), encodeArgs(u256(1), true));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256 b; }
|
||||
function f(S calldata s) external pure returns (uint256 a, uint256 b) {
|
||||
a = s.a;
|
||||
b = s.b;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256))", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct_and_ints)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256 b; }
|
||||
function f(uint256 a, S calldata s, uint256 b) external pure returns (uint256, uint256, uint256, uint256) {
|
||||
return (a, s.a, s.b, b);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f(uint256,(uint256,uint256),uint256)", encodeArgs(u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(1), u256(2), u256(3), u256(4)));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_structs)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S1 { uint256 a; uint256 b; }
|
||||
struct S2 { uint256 a; }
|
||||
function f(S1 calldata s1, S2 calldata s2, S1 calldata s3)
|
||||
external pure returns (uint256 a, uint256 b, uint256 c, uint256 d, uint256 e) {
|
||||
a = s1.a;
|
||||
b = s1.b;
|
||||
c = s2.a;
|
||||
d = s3.a;
|
||||
e = s3.b;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256),(uint256),(uint256,uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct_array_member)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256[2] b; uint256 c; }
|
||||
function f(S calldata s) external pure returns (uint256 a, uint256 b0, uint256 b1, uint256 c) {
|
||||
a = s.a;
|
||||
b0 = s.b[0];
|
||||
b1 = s.b[1];
|
||||
c = s.c;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256[2],uint256))", encodeArgs(u256(42), u256(1), u256(2), u256(23))), encodeArgs(u256(42), u256(1), u256(2), u256(23)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_array_of_struct)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256 b; }
|
||||
function f(S[] calldata s) external pure returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) {
|
||||
l = s.length;
|
||||
a = s[0].a;
|
||||
b = s[0].b;
|
||||
c = s[1].a;
|
||||
d = s[1].b;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256)[])", encodeArgs(u256(0x20), u256(2), u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(2), u256(1), u256(2), u256(3), u256(4)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_array_of_struct_to_memory)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256 b; }
|
||||
function f(S[] calldata s) external pure returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) {
|
||||
S[] memory m = s;
|
||||
l = m.length;
|
||||
a = m[0].a;
|
||||
b = m[0].b;
|
||||
c = m[1].a;
|
||||
d = m[1].b;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256)[])", encodeArgs(u256(0x20), u256(2), u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(2), u256(1), u256(2), u256(3), u256(4)));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct_to_memory)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256 b; }
|
||||
function f(S calldata s) external pure returns (uint256, uint256) {
|
||||
S memory m = s;
|
||||
return (m.a, m.b);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256))", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nested_calldata_struct)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S1 { uint256 a; uint256 b; }
|
||||
struct S2 { uint256 a; uint256 b; S1 s; uint256 c; }
|
||||
function f(S2 calldata s) external pure returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) {
|
||||
return (s.a, s.b, s.s.a, s.s.b, s.c);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256,(uint256,uint256),uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nested_calldata_struct_to_memory)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S1 { uint256 a; uint256 b; }
|
||||
struct S2 { uint256 a; uint256 b; S1 s; uint256 c; }
|
||||
function f(S2 calldata s) external pure returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) {
|
||||
S2 memory m = s;
|
||||
return (m.a, m.b, m.s.a, m.s.b, m.c);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256,(uint256,uint256),uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct_short)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint256 a; uint256 b; }
|
||||
function f(S calldata) external pure returns (uint256) {
|
||||
return msg.data.length;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
// double check that the valid case goes through
|
||||
ABI_CHECK(callContractFunction("f((uint256,uint256))", u256(1), u256(2)), encodeArgs(0x44));
|
||||
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(63,0)), encodeArgs());
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(33,0)), encodeArgs());
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(32,0)), encodeArgs());
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes(31,0)), encodeArgs());
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes()), encodeArgs());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct_cleaning)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { uint8 a; bytes1 b; }
|
||||
function f(S calldata s) external pure returns (uint256 a, bytes32 b) {
|
||||
uint8 tmp1 = s.a;
|
||||
bytes1 tmp2 = s.b;
|
||||
assembly {
|
||||
a := tmp1
|
||||
b := tmp2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
// double check that the valid case goes through
|
||||
ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x12), bytes{0x34} + bytes(31,0)), encodeArgs(0x12, bytes{0x34} + bytes(31,0)));
|
||||
ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x1234), bytes{0x56, 0x78} + bytes(30,0)), encodeArgs(0x34, bytes{0x56} + bytes(31,0)));
|
||||
ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(-1), u256(-1)), encodeArgs(0xFF, bytes{0xFF} + bytes(31,0)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calldata_struct_function_type)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { function (uint) external returns (uint) fn; }
|
||||
function f(S calldata s) external returns (uint256) {
|
||||
return s.fn(42);
|
||||
}
|
||||
function g(uint256 a) external returns (uint256) {
|
||||
return a * 3;
|
||||
}
|
||||
function h(uint256 a) external returns (uint256) {
|
||||
return 23;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
|
||||
bytes fn_C_g = m_contractAddress.asBytes() + FixedHash<4>(dev::keccak256("g(uint256)")).asBytes() + bytes(8,0);
|
||||
bytes fn_C_h = m_contractAddress.asBytes() + FixedHash<4>(dev::keccak256("h(uint256)")).asBytes() + bytes(8,0);
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_g), encodeArgs(42 * 3));
|
||||
ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(literal_strings)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
|
@ -15,7 +15,3 @@ contract B is A {
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// TypeError: (102-112): Calldata structs are not yet supported.
|
||||
// TypeError: (146-156): Calldata structs are not yet supported.
|
||||
// TypeError: (198-208): Calldata structs are not yet supported.
|
||||
// TypeError: (250-260): Calldata structs are not yet supported.
|
||||
|
@ -6,5 +6,3 @@ contract Test {
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// TypeError: (89-101): Calldata structs are not yet supported.
|
||||
// TypeError: (131-145): Calldata structs are not yet supported.
|
||||
|
@ -5,4 +5,3 @@ contract Test {
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// TypeError: (89-99): Calldata structs are not yet supported.
|
||||
|
@ -0,0 +1,10 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract Test {
|
||||
struct S { int[3] a; }
|
||||
function f(S calldata s, int[3] calldata a) external {
|
||||
s.a = a;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// TypeError: (144-147): Expression has to be an lvalue.
|
8
test/libsolidity/syntaxTests/structs/calldata_assign.sol
Normal file
8
test/libsolidity/syntaxTests/structs/calldata_assign.sol
Normal file
@ -0,0 +1,8 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract Test {
|
||||
struct S { int a; }
|
||||
function f(S calldata s) external { s.a = 4; }
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// TypeError: (114-117): Expression has to be an lvalue.
|
@ -0,0 +1,9 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { function (uint) external returns (uint) fn; }
|
||||
function f(S calldata s) external returns (uint256 a) {
|
||||
return s.fn(42);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
12
test/libsolidity/syntaxTests/structs/memory_to_calldata.sol
Normal file
12
test/libsolidity/syntaxTests/structs/memory_to_calldata.sol
Normal file
@ -0,0 +1,12 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract Test {
|
||||
struct S { int a; }
|
||||
function f(S calldata s) external { s = S(2); }
|
||||
function g(S calldata s) external { S memory m; s = m; }
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// TypeError: (114-115): Expression has to be an lvalue.
|
||||
// TypeError: (118-122): Type struct Test.S memory is not implicitly convertible to expected type struct Test.S calldata.
|
||||
// TypeError: (178-179): Expression has to be an lvalue.
|
||||
// TypeError: (182-183): Type struct Test.S memory is not implicitly convertible to expected type struct Test.S calldata.
|
Loading…
Reference in New Issue
Block a user