mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #5936 from ethereum/calldataStructsV2
ABIEncoderV2: Implement calldata structs without dynamically encoded members.
This commit is contained in:
		
						commit
						92cb6cb793
					
				| @ -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); | ||||
| 	} | ||||
| @ -2085,8 +2087,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