mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12628 from nishant-sachdeva/adding_functiontype_string_and_allow_string_concat_operations
Added support for FunctionType::Kind::StringConcat
This commit is contained in:
commit
1210c3e60f
@ -17,6 +17,7 @@ Bugfixes:
|
|||||||
* Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots.
|
* Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots.
|
||||||
* Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``.
|
* Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``.
|
||||||
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
|
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
|
||||||
|
* General: ``string.concat`` now properly takes strings as arguments and returns ``string memory``. It was accidentally introduced as a copy of ``bytes.concat`` before.
|
||||||
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the derived contract contains immutable variables.
|
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the derived contract contains immutable variables.
|
||||||
* IR Generator: Add missing cleanup during the conversion of fixed bytes types to smaller fixed bytes types.
|
* IR Generator: Add missing cleanup during the conversion of fixed bytes types to smaller fixed bytes types.
|
||||||
* IR Generator: Add missing cleanup for indexed event arguments of value type.
|
* IR Generator: Add missing cleanup for indexed event arguments of value type.
|
||||||
|
@ -86,6 +86,8 @@ Global Variables
|
|||||||
to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)``
|
to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)``
|
||||||
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of
|
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of
|
||||||
arguments to one byte array<bytes-concat>`
|
arguments to one byte array<bytes-concat>`
|
||||||
|
- ``string.concat(...) returns (string memory)``: :ref:`Concatenates variable number of
|
||||||
|
arguments to one string array<string-concat>`
|
||||||
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
|
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
|
||||||
- ``block.chainid`` (``uint``): current chain id
|
- ``block.chainid`` (``uint``): current chain id
|
||||||
- ``block.coinbase`` (``address payable``): current block miner's address
|
- ``block.coinbase`` (``address payable``): current block miner's address
|
||||||
|
@ -150,7 +150,7 @@ length or index access.
|
|||||||
Solidity does not have string manipulation functions, but there are
|
Solidity does not have string manipulation functions, but there are
|
||||||
third-party string libraries. You can also compare two strings by their keccak256-hash using
|
third-party string libraries. You can also compare two strings by their keccak256-hash using
|
||||||
``keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))`` and
|
``keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))`` and
|
||||||
concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``.
|
concatenate two strings using ``string.concat(s1, s2)``.
|
||||||
|
|
||||||
You should use ``bytes`` over ``bytes1[]`` because it is cheaper,
|
You should use ``bytes`` over ``bytes1[]`` because it is cheaper,
|
||||||
since using ``bytes1[]`` in ``memory`` adds 31 padding bytes between the elements. Note that in ``storage``, the
|
since using ``bytes1[]`` in ``memory`` adds 31 padding bytes between the elements. Note that in ``storage``, the
|
||||||
@ -165,31 +165,40 @@ always use one of the value types ``bytes1`` to ``bytes32`` because they are muc
|
|||||||
that you are accessing the low-level bytes of the UTF-8 representation,
|
that you are accessing the low-level bytes of the UTF-8 representation,
|
||||||
and not the individual characters.
|
and not the individual characters.
|
||||||
|
|
||||||
.. index:: ! bytes-concat
|
.. index:: ! bytes-concat, ! string-concat
|
||||||
|
|
||||||
.. _bytes-concat:
|
.. _bytes-concat:
|
||||||
|
.. _string-concat:
|
||||||
|
|
||||||
``bytes.concat`` function
|
The functions ``bytes.concat`` and ``string.concat``
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
You can concatenate a variable number of ``bytes`` or ``bytes1 ... bytes32`` using ``bytes.concat``.
|
You can concatenate an arbitrary number of ``string`` values using ``string.concat``.
|
||||||
|
The function returns a single ``string memory`` array that contains the contents of the arguments without padding.
|
||||||
|
If you want to use parameters of other types that are not implicitly convertible to ``string``, you need to convert them to ``string`` first.
|
||||||
|
|
||||||
|
Analogously, the ``bytes.concat`` function can concatenate an arbitrary number of ``bytes`` or ``bytes1 ... bytes32`` values.
|
||||||
The function returns a single ``bytes memory`` array that contains the contents of the arguments without padding.
|
The function returns a single ``bytes memory`` array that contains the contents of the arguments without padding.
|
||||||
If you want to use string parameters or other types, you need to convert them to ``bytes`` or ``bytes1``/.../``bytes32`` first.
|
If you want to use string parameters or other types that are not implicitly convertible to ``bytes``, you need to convert them to ``bytes`` or ``bytes1``/.../``bytes32`` first.
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: solidity
|
.. code-block:: solidity
|
||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.8.4;
|
pragma solidity ^0.8.12;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
bytes s = "Storage";
|
string s = "Storage";
|
||||||
function f(bytes calldata c, string memory m, bytes16 b) public view {
|
function f(bytes calldata bc, string memory sm, bytes16 b) public view {
|
||||||
bytes memory a = bytes.concat(s, c, c[:2], "Literal", bytes(m), b);
|
string memory concat_string = string.concat(s, string(bc), "Literal", sm);
|
||||||
assert((s.length + c.length + 2 + 7 + bytes(m).length + 16) == a.length);
|
assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concat_string).length);
|
||||||
|
|
||||||
|
bytes memory concat_bytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
|
||||||
|
assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concat_bytes.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
If you call ``bytes.concat`` without arguments it will return an empty ``bytes`` array.
|
If you call ``string.concat`` or ``bytes.concat`` without arguments they return an empty array.
|
||||||
|
|
||||||
.. index:: ! array;allocating, new
|
.. index:: ! array;allocating, new
|
||||||
|
|
||||||
|
@ -154,6 +154,14 @@ Members of bytes
|
|||||||
|
|
||||||
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of bytes and bytes1, ..., bytes32 arguments to one byte array<bytes-concat>`
|
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of bytes and bytes1, ..., bytes32 arguments to one byte array<bytes-concat>`
|
||||||
|
|
||||||
|
.. index:: string members
|
||||||
|
|
||||||
|
Members of string
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
- ``string.concat(...) returns (string memory)``: :ref:`Concatenates variable number of string arguments to one string array<string-concat>`
|
||||||
|
|
||||||
|
|
||||||
.. index:: assert, revert, require
|
.. index:: assert, revert, require
|
||||||
|
|
||||||
Error Handling
|
Error Handling
|
||||||
|
@ -2207,14 +2207,42 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TypeChecker::typeCheckStringConcatFunction(
|
||||||
|
FunctionCall const& _functionCall,
|
||||||
|
FunctionType const* _functionType
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(_functionType);
|
||||||
|
solAssert(_functionType->kind() == FunctionType::Kind::StringConcat);
|
||||||
|
solAssert(_functionCall.names().empty());
|
||||||
|
|
||||||
|
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
|
||||||
|
|
||||||
|
for (shared_ptr<Expression const> const& argument: _functionCall.arguments())
|
||||||
|
{
|
||||||
|
Type const* argumentType = type(*argument);
|
||||||
|
bool notConvertibleToString = !argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory());
|
||||||
|
|
||||||
|
if (notConvertibleToString)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
9977_error,
|
||||||
|
argument->location(),
|
||||||
|
"Invalid type for argument in the string.concat function call. "
|
||||||
|
"string type is required, but " +
|
||||||
|
argumentType->identifier() + " provided."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TypeChecker::typeCheckBytesConcatFunction(
|
void TypeChecker::typeCheckBytesConcatFunction(
|
||||||
FunctionCall const& _functionCall,
|
FunctionCall const& _functionCall,
|
||||||
FunctionType const* _functionType
|
FunctionType const* _functionType
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(_functionType, "");
|
solAssert(_functionType);
|
||||||
solAssert(_functionType->kind() == FunctionType::Kind::BytesConcat, "");
|
solAssert(_functionType->kind() == FunctionType::Kind::BytesConcat);
|
||||||
solAssert(_functionCall.names().empty(), "");
|
solAssert(_functionCall.names().empty());
|
||||||
|
|
||||||
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
|
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
|
||||||
|
|
||||||
@ -2651,6 +2679,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
returnTypes = functionType->returnParameterTypes();
|
returnTypes = functionType->returnParameterTypes();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::StringConcat:
|
||||||
|
{
|
||||||
|
typeCheckStringConcatFunction(_functionCall, functionType);
|
||||||
|
returnTypes = functionType->returnParameterTypes();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case FunctionType::Kind::Wrap:
|
case FunctionType::Kind::Wrap:
|
||||||
case FunctionType::Kind::Unwrap:
|
case FunctionType::Kind::Unwrap:
|
||||||
{
|
{
|
||||||
|
@ -113,6 +113,12 @@ private:
|
|||||||
/// Performs checks specific to the ABI encode functions of type ABIEncodeCall
|
/// Performs checks specific to the ABI encode functions of type ABIEncodeCall
|
||||||
void typeCheckABIEncodeCallFunction(FunctionCall const& _functionCall);
|
void typeCheckABIEncodeCallFunction(FunctionCall const& _functionCall);
|
||||||
|
|
||||||
|
/// Performs general checks and checks specific to string concat function call
|
||||||
|
void typeCheckStringConcatFunction(
|
||||||
|
FunctionCall const& _functionCall,
|
||||||
|
FunctionType const* _functionType
|
||||||
|
);
|
||||||
|
|
||||||
/// Performs general checks and checks specific to bytes concat function call
|
/// Performs general checks and checks specific to bytes concat function call
|
||||||
void typeCheckBytesConcatFunction(
|
void typeCheckBytesConcatFunction(
|
||||||
FunctionCall const& _functionCall,
|
FunctionCall const& _functionCall,
|
||||||
|
@ -2928,6 +2928,7 @@ string FunctionType::richIdentifier() const
|
|||||||
case Kind::ArrayPush: id += "arraypush"; break;
|
case Kind::ArrayPush: id += "arraypush"; break;
|
||||||
case Kind::ArrayPop: id += "arraypop"; break;
|
case Kind::ArrayPop: id += "arraypop"; break;
|
||||||
case Kind::BytesConcat: id += "bytesconcat"; break;
|
case Kind::BytesConcat: id += "bytesconcat"; break;
|
||||||
|
case Kind::StringConcat: id += "stringconcat"; break;
|
||||||
case Kind::ObjectCreation: id += "objectcreation"; break;
|
case Kind::ObjectCreation: id += "objectcreation"; break;
|
||||||
case Kind::Assert: id += "assert"; break;
|
case Kind::Assert: id += "assert"; break;
|
||||||
case Kind::Require: id += "require"; break;
|
case Kind::Require: id += "require"; break;
|
||||||
@ -3817,15 +3818,14 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
|||||||
)
|
)
|
||||||
members.emplace_back("concat", TypeProvider::function(
|
members.emplace_back("concat", TypeProvider::function(
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
TypePointers{TypeProvider::bytesMemory()},
|
TypePointers{arrayType->isString() ? TypeProvider::stringMemory() : TypeProvider::bytesMemory()},
|
||||||
strings{},
|
strings{},
|
||||||
strings{string()},
|
strings{string{}},
|
||||||
FunctionType::Kind::BytesConcat,
|
arrayType->isString() ? FunctionType::Kind::StringConcat : FunctionType::Kind::BytesConcat,
|
||||||
StateMutability::Pure,
|
StateMutability::Pure,
|
||||||
nullptr,
|
nullptr,
|
||||||
FunctionType::Options::withArbitraryParameters()
|
FunctionType::Options::withArbitraryParameters()
|
||||||
));
|
));
|
||||||
|
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1228,6 +1228,7 @@ public:
|
|||||||
ArrayPush, ///< .push() to a dynamically sized array in storage
|
ArrayPush, ///< .push() to a dynamically sized array in storage
|
||||||
ArrayPop, ///< .pop() from a dynamically sized array in storage
|
ArrayPop, ///< .pop() from a dynamically sized array in storage
|
||||||
BytesConcat, ///< .concat() on bytes (type type)
|
BytesConcat, ///< .concat() on bytes (type type)
|
||||||
|
StringConcat, ///< .concat() on string (type type)
|
||||||
ObjectCreation, ///< array creation using new
|
ObjectCreation, ///< array creation using new
|
||||||
Assert, ///< assert()
|
Assert, ///< assert()
|
||||||
Require, ///< require()
|
Require, ///< require()
|
||||||
|
@ -1101,6 +1101,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
ArrayUtils(m_context).popStorageArrayElement(*arrayType);
|
ArrayUtils(m_context).popStorageArrayElement(*arrayType);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::StringConcat:
|
||||||
case FunctionType::Kind::BytesConcat:
|
case FunctionType::Kind::BytesConcat:
|
||||||
{
|
{
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
@ -1121,10 +1122,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(!dynamic_cast<RationalNumberType const*>(argument->annotation().type), "");
|
solAssert(!dynamic_cast<RationalNumberType const*>(argument->annotation().type), "");
|
||||||
|
if (function.kind() == FunctionType::Kind::StringConcat)
|
||||||
|
{
|
||||||
|
solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()), "");
|
||||||
|
targetTypes.emplace_back(TypeProvider::stringMemory());
|
||||||
|
}
|
||||||
|
else if (function.kind() == FunctionType::Kind::BytesConcat)
|
||||||
|
{
|
||||||
solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
|
solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
|
||||||
targetTypes.emplace_back(TypeProvider::bytesMemory());
|
targetTypes.emplace_back(TypeProvider::bytesMemory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
// stack: <arg1> <arg2> ... <argn> <free mem>
|
// stack: <arg1> <arg2> ... <argn> <free mem>
|
||||||
m_context << u256(32) << Instruction::ADD;
|
m_context << u256(32) << Instruction::ADD;
|
||||||
|
@ -2475,18 +2475,26 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::bytesConcatFunction(vector<Type const*> const& _argumentTypes)
|
string YulUtilFunctions::bytesOrStringConcatFunction(
|
||||||
|
vector<Type const*> const& _argumentTypes,
|
||||||
|
FunctionType::Kind _functionTypeKind
|
||||||
|
)
|
||||||
{
|
{
|
||||||
string functionName = "bytes_concat";
|
solAssert(_functionTypeKind == FunctionType::Kind::BytesConcat || _functionTypeKind == FunctionType::Kind::StringConcat);
|
||||||
|
std::string functionName = (_functionTypeKind == FunctionType::Kind::StringConcat) ? "string_concat" : "bytes_concat";
|
||||||
size_t totalParams = 0;
|
size_t totalParams = 0;
|
||||||
vector<Type const*> targetTypes;
|
vector<Type const*> targetTypes;
|
||||||
|
|
||||||
for (Type const* argumentType: _argumentTypes)
|
for (Type const* argumentType: _argumentTypes)
|
||||||
{
|
{
|
||||||
|
if (_functionTypeKind == FunctionType::Kind::StringConcat)
|
||||||
|
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()));
|
||||||
|
else if (_functionTypeKind == FunctionType::Kind::BytesConcat)
|
||||||
solAssert(
|
solAssert(
|
||||||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
|
argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
|
||||||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32)),
|
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32))
|
||||||
""
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (argumentType->category() == Type::Category::FixedBytes)
|
if (argumentType->category() == Type::Category::FixedBytes)
|
||||||
targetTypes.emplace_back(argumentType);
|
targetTypes.emplace_back(argumentType);
|
||||||
else if (
|
else if (
|
||||||
@ -2496,15 +2504,16 @@ string YulUtilFunctions::bytesConcatFunction(vector<Type const*> const& _argumen
|
|||||||
targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size())));
|
targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size())));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(!dynamic_cast<RationalNumberType const*>(argumentType), "");
|
solAssert(!dynamic_cast<RationalNumberType const*>(argumentType));
|
||||||
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
|
targetTypes.emplace_back(
|
||||||
targetTypes.emplace_back(TypeProvider::bytesMemory());
|
_functionTypeKind == FunctionType::Kind::StringConcat ?
|
||||||
|
TypeProvider::stringMemory() :
|
||||||
|
TypeProvider::bytesMemory()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
totalParams += argumentType->sizeOnStack();
|
totalParams += argumentType->sizeOnStack();
|
||||||
functionName += "_" + argumentType->identifier();
|
functionName += "_" + argumentType->identifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>(<parameters>) -> outPtr {
|
function <functionName>(<parameters>) -> outPtr {
|
||||||
|
@ -312,9 +312,13 @@ public:
|
|||||||
/// of the storage array into it.
|
/// of the storage array into it.
|
||||||
std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to);
|
std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to);
|
||||||
|
|
||||||
/// @returns the name of a function that does concatenation of variadic number of bytes
|
/// @returns the name of a function that does concatenation of variadic number of
|
||||||
/// or fixed bytes
|
/// bytes if @a functionTypeKind is FunctionType::Kind::BytesConcat,
|
||||||
std::string bytesConcatFunction(std::vector<Type const*> const& _argumentTypes);
|
/// or of strings, if @a functionTypeKind is FunctionType::Kind::StringConcat.
|
||||||
|
std::string bytesOrStringConcatFunction(
|
||||||
|
std::vector<Type const*> const& _argumentTypes,
|
||||||
|
FunctionType::Kind _functionTypeKind
|
||||||
|
);
|
||||||
|
|
||||||
/// @returns the name of a function that performs index access for mappings.
|
/// @returns the name of a function that performs index access for mappings.
|
||||||
/// @param _mappingType the type of the mapping
|
/// @param _mappingType the type of the mapping
|
||||||
|
@ -1389,6 +1389,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::StringConcat:
|
||||||
case FunctionType::Kind::BytesConcat:
|
case FunctionType::Kind::BytesConcat:
|
||||||
{
|
{
|
||||||
TypePointers argumentTypes;
|
TypePointers argumentTypes;
|
||||||
@ -1399,11 +1400,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
argumentVars += IRVariable(*argument).stackSlots();
|
argumentVars += IRVariable(*argument).stackSlots();
|
||||||
}
|
}
|
||||||
define(IRVariable(_functionCall)) <<
|
define(IRVariable(_functionCall)) <<
|
||||||
m_utils.bytesConcatFunction(argumentTypes) <<
|
m_utils.bytesOrStringConcatFunction(argumentTypes, functionType->kind()) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
joinHumanReadable(argumentVars) <<
|
joinHumanReadable(argumentVars) <<
|
||||||
")\n";
|
")\n";
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FunctionType::Kind::MetaType:
|
case FunctionType::Kind::MetaType:
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
function f(string memory a, string memory b) public returns (string memory) {
|
||||||
|
return string.concat(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(string,string): 0x40, 0x80, 32, "abcdabcdabcdabcdabcdabcdabcdabcd", 5, "bcdef" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
|
||||||
|
// f(string,string): 0x40, 0xa0, 64, "abcdabcdabcdabcdabcdabcdabcdabcd", "abcdabcdabcdabcdabcdabcdabcdabcd", 5, "bcdef" -> 0x20, 0x45, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
|
||||||
|
// f(string,string): 0x40, 0x80, 3, "abc", 3, "def" -> 0x20, 6, "abcdef"
|
||||||
|
// f(string,string): 0x40, 0xa0, 34, "abcdabcdabcdabcdabcdabcdabcdabcd", "ab", 30, "cdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x40, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636461626364616263646162636461626364616263646162636461626364
|
||||||
|
// f(string,string): 0x40, 0xa0, 34, "abcdabcdabcdabcdabcdabcdabcdabcd", "ab", 34, "cdabcdabcdabcdabcdabcdabcdabcdab", "cd" -> 0x20, 0x44, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636461626364616263646162636461626364616263646162636461626364, 44048183293808120317390542201052832727062033572611867748297851798484192067584
|
||||||
|
// f(string,string): 0x40, 0x80, 3, "abc", 30, "dabcdabcdabcdabcdabcdabcdabcda" -> 0x20, 0x21, 0x6162636461626364616263646162636461626364616263646162636461626364, 43874346312576839672212443538448152585028080127215369968075725190498334277632
|
@ -0,0 +1,37 @@
|
|||||||
|
contract C{
|
||||||
|
string s = "bcdef";
|
||||||
|
|
||||||
|
function f(string memory a) public returns (string memory) {
|
||||||
|
return string.concat(a, "bcdef");
|
||||||
|
}
|
||||||
|
function g(string calldata a) public returns (string memory) {
|
||||||
|
return string.concat(a, "abcdefghabcdefghabcdefghabcdefghab");
|
||||||
|
}
|
||||||
|
function h(string calldata a) public returns (string memory) {
|
||||||
|
return string.concat(a, s);
|
||||||
|
}
|
||||||
|
function j(string calldata a) public returns (string memory) {
|
||||||
|
string storage ref = s;
|
||||||
|
return string.concat(a, ref, s);
|
||||||
|
}
|
||||||
|
function k(string calldata a, bytes memory b) public returns (string memory) {
|
||||||
|
return string.concat(a, string(b));
|
||||||
|
}
|
||||||
|
function slice(string calldata a) public returns (string memory) {
|
||||||
|
require(bytes(a).length > 2, "");
|
||||||
|
return string.concat(a[:2], a[2:]);
|
||||||
|
}
|
||||||
|
function strParam(bytes calldata a) public returns (string memory) {
|
||||||
|
return string.concat(string(a), "bcdef");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
|
||||||
|
// g(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x42, 0x6162636461626364616263646162636461626364616263646162636461626364, 0x6162636465666768616263646566676861626364656667686162636465666768, 44047497324925121336511606693520958599579173549109180625971642598225011015680
|
||||||
|
// h(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
|
||||||
|
// j(string): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x2a, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928944786876717917111204727192787026596791669343131645116682757734400
|
||||||
|
// k(string,bytes): 0x40, 0x80, 32, "abcdabcdabcdabcdabcdabcdabcdabcd", 5, "bcdef" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
|
||||||
|
// slice(string): 0x20, 4, "abcd" -> 0x20, 4, "abcd"
|
||||||
|
// strParam(bytes): 0x20, 32, "abcdabcdabcdabcdabcdabcdabcdabcd" -> 0x20, 0x25, 0x6162636461626364616263646162636461626364616263646162636461626364, 44502269928904312298000709931354278973409164155382318144318241583783949107200
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public returns (string memory) {
|
||||||
|
return string.concat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileToEwasm: also
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 0
|
@ -0,0 +1,27 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public returns (string memory) {
|
||||||
|
string memory b = "";
|
||||||
|
return string.concat(
|
||||||
|
string.concat(b),
|
||||||
|
string.concat(b, b),
|
||||||
|
string.concat("", b),
|
||||||
|
string.concat(b, "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public returns (string memory) {
|
||||||
|
return string.concat("", "abc", hex"", "abc", unicode"");
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() public returns (string memory) {
|
||||||
|
string memory b = "";
|
||||||
|
return string.concat(b, "abc", b, "abc", b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileToEwasm: also
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 0
|
||||||
|
// g() -> 0x20, 6, "abcabc"
|
||||||
|
// h() -> 0x20, 6, "abcabc"
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
function f(string memory a, string memory b, string memory c) public returns (string memory) {
|
||||||
|
return string.concat(string.concat(a, b), c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(string,string,string): 0x60, 0x60, 0x60, 2, "ab" -> 0x20, 6, "ababab"
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function g() public pure returns (string memory) {
|
||||||
|
return string.concat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6359: (83-96): Return argument type function () pure returns (string memory) is not implicitly convertible to expected type (type of first return variable) string memory.
|
@ -0,0 +1,13 @@
|
|||||||
|
contract C {
|
||||||
|
function j() external {
|
||||||
|
string memory a = "hello";
|
||||||
|
string memory b = " world";
|
||||||
|
|
||||||
|
string memory d = string.concat(bytes(a), bytes(b));
|
||||||
|
string memory e = string.concat(a, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9977: (153-161): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_memory_ptr provided.
|
||||||
|
// TypeError 9977: (163-171): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_memory_ptr provided.
|
||||||
|
// TypeError 9977: (217-218): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
@ -0,0 +1,15 @@
|
|||||||
|
contract C {
|
||||||
|
string s;
|
||||||
|
function f(string calldata c, string calldata c1) public {
|
||||||
|
string memory a;
|
||||||
|
bytes16 b;
|
||||||
|
uint8[] memory num;
|
||||||
|
bytes1[] memory m;
|
||||||
|
string memory d = string.concat(a, b, c, num, s, "abc", m, c1, bytes(c1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9977: (232-233): Invalid type for argument in the string.concat function call. string type is required, but t_bytes16 provided.
|
||||||
|
// TypeError 9977: (238-241): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint8_$dyn_memory_ptr provided.
|
||||||
|
// TypeError 9977: (253-254): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_bytes1_$dyn_memory_ptr provided.
|
||||||
|
// TypeError 9977: (260-269): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_calldata_ptr provided.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public {
|
||||||
|
string.concat([], [], []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6378: (61-63): Unable to deduce common type for array elements.
|
@ -0,0 +1,40 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint x;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum E {A, B, C}
|
||||||
|
|
||||||
|
mapping(uint => E) m;
|
||||||
|
|
||||||
|
function f() public {
|
||||||
|
bool b;
|
||||||
|
uint u;
|
||||||
|
uint8 u8;
|
||||||
|
address a;
|
||||||
|
address payable ap;
|
||||||
|
function () external fext;
|
||||||
|
function () internal fint;
|
||||||
|
uint[] memory uDynamic;
|
||||||
|
uint[2] memory uStatic;
|
||||||
|
C c;
|
||||||
|
S memory s;
|
||||||
|
E e;
|
||||||
|
|
||||||
|
string.concat(b, u, u8, a, ap, fext, fint, uDynamic, uStatic, c, s, e, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9977: (426-427): Invalid type for argument in the string.concat function call. string type is required, but t_bool provided.
|
||||||
|
// TypeError 9977: (429-430): Invalid type for argument in the string.concat function call. string type is required, but t_uint256 provided.
|
||||||
|
// TypeError 9977: (432-434): Invalid type for argument in the string.concat function call. string type is required, but t_uint8 provided.
|
||||||
|
// TypeError 9977: (436-437): Invalid type for argument in the string.concat function call. string type is required, but t_address provided.
|
||||||
|
// TypeError 9977: (439-441): Invalid type for argument in the string.concat function call. string type is required, but t_address_payable provided.
|
||||||
|
// TypeError 9977: (443-447): Invalid type for argument in the string.concat function call. string type is required, but t_function_external_nonpayable$__$returns$__$ provided.
|
||||||
|
// TypeError 9977: (449-453): Invalid type for argument in the string.concat function call. string type is required, but t_function_internal_nonpayable$__$returns$__$ provided.
|
||||||
|
// TypeError 9977: (455-463): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint256_$dyn_memory_ptr provided.
|
||||||
|
// TypeError 9977: (465-472): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint256_$2_memory_ptr provided.
|
||||||
|
// TypeError 9977: (474-475): Invalid type for argument in the string.concat function call. string type is required, but t_contract$_C_$86 provided.
|
||||||
|
// TypeError 9977: (477-478): Invalid type for argument in the string.concat function call. string type is required, but t_struct$_S_$4_memory_ptr provided.
|
||||||
|
// TypeError 9977: (480-481): Invalid type for argument in the string.concat function call. string type is required, but t_enum$_E_$8 provided.
|
||||||
|
// TypeError 9977: (483-484): Invalid type for argument in the string.concat function call. string type is required, but t_mapping$_t_uint256_$_t_enum$_E_$8_$ provided.
|
@ -0,0 +1,58 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint x;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum E {A, B, C}
|
||||||
|
|
||||||
|
function f() public {
|
||||||
|
string.concat(
|
||||||
|
false,
|
||||||
|
1,
|
||||||
|
1e10,
|
||||||
|
1e-10,
|
||||||
|
0.1,
|
||||||
|
0x1234567,
|
||||||
|
0x11112222333344445555666677778888999900, // One byte less than an address
|
||||||
|
0x1111222233334444555566667777888899990000, // Address
|
||||||
|
0x111122223333444455556666777788889999000011, // One byte more than an address
|
||||||
|
0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff, // exactly 32 bytes
|
||||||
|
-0x0000000000000000000000000000000000000000000000000000000000000001, // exactly 32 bytes
|
||||||
|
bytes(bytes32(0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff))[:],
|
||||||
|
f,
|
||||||
|
(),
|
||||||
|
(0, 0),
|
||||||
|
[0],
|
||||||
|
[0][:],
|
||||||
|
[0][0],
|
||||||
|
new C(),
|
||||||
|
S(0),
|
||||||
|
E.A
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9640: (698-780): Explicit type conversion not allowed from "bytes32" to "bytes memory".
|
||||||
|
// TypeError 1227: (698-783): Index range access is only supported for dynamic calldata arrays.
|
||||||
|
// TypeError 1227: (865-871): Index range access is only supported for dynamic calldata arrays.
|
||||||
|
// TypeError 9977: (134-139): Invalid type for argument in the string.concat function call. string type is required, but t_bool provided.
|
||||||
|
// TypeError 9977: (153-154): Invalid type for argument in the string.concat function call. string type is required, but t_rational_1_by_1 provided.
|
||||||
|
// TypeError 9977: (168-172): Invalid type for argument in the string.concat function call. string type is required, but t_rational_10000000000_by_1 provided.
|
||||||
|
// TypeError 9977: (186-191): Invalid type for argument in the string.concat function call. string type is required, but t_rational_1_by_10000000000 provided.
|
||||||
|
// TypeError 9977: (205-208): Invalid type for argument in the string.concat function call. string type is required, but t_rational_1_by_10 provided.
|
||||||
|
// TypeError 9977: (222-231): Invalid type for argument in the string.concat function call. string type is required, but t_rational_19088743_by_1 provided.
|
||||||
|
// TypeError 9977: (245-285): Invalid type for argument in the string.concat function call. string type is required, but t_rational_380605192295934637532253317235440047844071680_by_1 provided.
|
||||||
|
// TypeError 9977: (336-378): Invalid type for argument in the string.concat function call. string type is required, but t_address provided.
|
||||||
|
// TypeError 9977: (405-449): Invalid type for argument in the string.concat function call. string type is required, but t_rational_24943341882306372405313753398341798975509081620497_by_1 provided.
|
||||||
|
// TypeError 9977: (496-562): Invalid type for argument in the string.concat function call. string type is required, but t_rational_30272441630670900764332283662402067049651745785153368133042924362431065855_by_1 provided.
|
||||||
|
// TypeError 9977: (597-664): Invalid type for argument in the string.concat function call. string type is required, but t_rational_minus_1_by_1 provided.
|
||||||
|
// TypeError 9977: (698-783): Invalid type for argument in the string.concat function call. string type is required, but t_bytes_memory_ptr_slice provided.
|
||||||
|
// TypeError 9977: (797-798): Invalid type for argument in the string.concat function call. string type is required, but t_function_internal_nonpayable$__$returns$__$ provided.
|
||||||
|
// TypeError 9977: (812-814): Invalid type for argument in the string.concat function call. string type is required, but t_tuple$__$ provided.
|
||||||
|
// TypeError 9977: (828-834): Invalid type for argument in the string.concat function call. string type is required, but t_tuple$_t_rational_0_by_1_$_t_rational_0_by_1_$ provided.
|
||||||
|
// TypeError 9977: (848-851): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint8_$1_memory_ptr provided.
|
||||||
|
// TypeError 9977: (865-871): Invalid type for argument in the string.concat function call. string type is required, but t_array$_t_uint8_$1_memory_ptr_slice provided.
|
||||||
|
// TypeError 9977: (885-891): Invalid type for argument in the string.concat function call. string type is required, but t_uint8 provided.
|
||||||
|
// TypeError 9977: (905-912): Invalid type for argument in the string.concat function call. string type is required, but t_contract$_C_$61 provided.
|
||||||
|
// TypeError 9977: (926-930): Invalid type for argument in the string.concat function call. string type is required, but t_struct$_S_$4_memory_ptr provided.
|
||||||
|
// TypeError 9977: (944-947): Invalid type for argument in the string.concat function call. string type is required, but t_enum$_E_$8 provided.
|
@ -0,0 +1,33 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
string.concat(
|
||||||
|
0,
|
||||||
|
-0,
|
||||||
|
0.0,
|
||||||
|
-0.0,
|
||||||
|
0e10,
|
||||||
|
-0e10,
|
||||||
|
0e-10,
|
||||||
|
-0e-10,
|
||||||
|
(0),
|
||||||
|
0x00,
|
||||||
|
-0x00,
|
||||||
|
0x0000000000000000000000000000000000000000000000000000000000000000, // exactly 32 bytes
|
||||||
|
-0x0000000000000000000000000000000000000000000000000000000000000000 // exactly 32 bytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9977: (79-80): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (94-96): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (110-113): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (127-131): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (145-149): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (163-168): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (182-187): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (201-207): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (221-224): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (238-242): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (256-261): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (275-341): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
||||||
|
// TypeError 9977: (375-442): Invalid type for argument in the string.concat function call. string type is required, but t_rational_0_by_1 provided.
|
Loading…
Reference in New Issue
Block a user