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:
Daniel Kirchner 2022-02-10 19:20:29 +01:00 committed by GitHub
commit 1210c3e60f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 391 additions and 38 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:
{ {

View File

@ -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,

View File

@ -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;
} }

View File

@ -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()

View File

@ -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;

View File

@ -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 {

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,10 @@
contract C {
function f() public returns (string memory) {
return string.concat();
}
}
// ====
// compileToEwasm: also
// compileViaYul: also
// ----
// f() -> 0x20, 0

View File

@ -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"

View File

@ -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"

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,7 @@
contract C {
function f() public {
string.concat([], [], []);
}
}
// ----
// TypeError 6378: (61-63): Unable to deduce common type for array elements.

View File

@ -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.

View File

@ -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.

View File

@ -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.