mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Added support for FunctionType::Kind::StringConcat and functions string.concat
This commit is contained in:
@@ -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(
|
||||
FunctionCall const& _functionCall,
|
||||
FunctionType const* _functionType
|
||||
)
|
||||
{
|
||||
solAssert(_functionType, "");
|
||||
solAssert(_functionType->kind() == FunctionType::Kind::BytesConcat, "");
|
||||
solAssert(_functionCall.names().empty(), "");
|
||||
solAssert(_functionType);
|
||||
solAssert(_functionType->kind() == FunctionType::Kind::BytesConcat);
|
||||
solAssert(_functionCall.names().empty());
|
||||
|
||||
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
|
||||
|
||||
@@ -2651,6 +2679,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
returnTypes = functionType->returnParameterTypes();
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::StringConcat:
|
||||
{
|
||||
typeCheckStringConcatFunction(_functionCall, functionType);
|
||||
returnTypes = functionType->returnParameterTypes();
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Wrap:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
{
|
||||
|
||||
@@ -113,6 +113,12 @@ private:
|
||||
/// Performs checks specific to the ABI encode functions of type ABIEncodeCall
|
||||
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
|
||||
void typeCheckBytesConcatFunction(
|
||||
FunctionCall const& _functionCall,
|
||||
|
||||
@@ -2928,6 +2928,7 @@ string FunctionType::richIdentifier() const
|
||||
case Kind::ArrayPush: id += "arraypush"; break;
|
||||
case Kind::ArrayPop: id += "arraypop"; break;
|
||||
case Kind::BytesConcat: id += "bytesconcat"; break;
|
||||
case Kind::StringConcat: id += "stringconcat"; break;
|
||||
case Kind::ObjectCreation: id += "objectcreation"; break;
|
||||
case Kind::Assert: id += "assert"; 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(
|
||||
TypePointers{},
|
||||
TypePointers{TypeProvider::bytesMemory()},
|
||||
TypePointers{arrayType->isString() ? TypeProvider::stringMemory() : TypeProvider::bytesMemory()},
|
||||
strings{},
|
||||
strings{string()},
|
||||
FunctionType::Kind::BytesConcat,
|
||||
strings{string{}},
|
||||
arrayType->isString() ? FunctionType::Kind::StringConcat : FunctionType::Kind::BytesConcat,
|
||||
StateMutability::Pure,
|
||||
nullptr,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
));
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
|
||||
@@ -1228,6 +1228,7 @@ public:
|
||||
ArrayPush, ///< .push() to a dynamically sized array in storage
|
||||
ArrayPop, ///< .pop() from a dynamically sized array in storage
|
||||
BytesConcat, ///< .concat() on bytes (type type)
|
||||
StringConcat, ///< .concat() on string (type type)
|
||||
ObjectCreation, ///< array creation using new
|
||||
Assert, ///< assert()
|
||||
Require, ///< require()
|
||||
|
||||
@@ -1101,6 +1101,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
ArrayUtils(m_context).popStorageArrayElement(*arrayType);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::StringConcat:
|
||||
case FunctionType::Kind::BytesConcat:
|
||||
{
|
||||
_functionCall.expression().accept(*this);
|
||||
@@ -1121,8 +1122,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
else
|
||||
{
|
||||
solAssert(!dynamic_cast<RationalNumberType const*>(argument->annotation().type), "");
|
||||
solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
|
||||
targetTypes.emplace_back(TypeProvider::bytesMemory());
|
||||
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()), "");
|
||||
targetTypes.emplace_back(TypeProvider::bytesMemory());
|
||||
}
|
||||
}
|
||||
}
|
||||
utils().fetchFreeMemoryPointer();
|
||||
|
||||
@@ -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;
|
||||
vector<Type const*> targetTypes;
|
||||
|
||||
for (Type const* argumentType: _argumentTypes)
|
||||
{
|
||||
solAssert(
|
||||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
|
||||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32)),
|
||||
""
|
||||
);
|
||||
if (_functionTypeKind == FunctionType::Kind::StringConcat)
|
||||
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()));
|
||||
else if (_functionTypeKind == FunctionType::Kind::BytesConcat)
|
||||
solAssert(
|
||||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
|
||||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32))
|
||||
);
|
||||
|
||||
if (argumentType->category() == Type::Category::FixedBytes)
|
||||
targetTypes.emplace_back(argumentType);
|
||||
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())));
|
||||
else
|
||||
{
|
||||
solAssert(!dynamic_cast<RationalNumberType const*>(argumentType), "");
|
||||
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
|
||||
targetTypes.emplace_back(TypeProvider::bytesMemory());
|
||||
solAssert(!dynamic_cast<RationalNumberType const*>(argumentType));
|
||||
targetTypes.emplace_back(
|
||||
_functionTypeKind == FunctionType::Kind::StringConcat ?
|
||||
TypeProvider::stringMemory() :
|
||||
TypeProvider::bytesMemory()
|
||||
);
|
||||
}
|
||||
|
||||
totalParams += argumentType->sizeOnStack();
|
||||
functionName += "_" + argumentType->identifier();
|
||||
}
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(<parameters>) -> outPtr {
|
||||
|
||||
@@ -312,9 +312,13 @@ public:
|
||||
/// of the storage array into it.
|
||||
std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to);
|
||||
|
||||
/// @returns the name of a function that does concatenation of variadic number of bytes
|
||||
/// or fixed bytes
|
||||
std::string bytesConcatFunction(std::vector<Type const*> const& _argumentTypes);
|
||||
/// @returns the name of a function that does concatenation of variadic number of
|
||||
/// bytes if @a functionTypeKind is FunctionType::Kind::BytesConcat,
|
||||
/// 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.
|
||||
/// @param _mappingType the type of the mapping
|
||||
|
||||
@@ -1389,6 +1389,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::StringConcat:
|
||||
case FunctionType::Kind::BytesConcat:
|
||||
{
|
||||
TypePointers argumentTypes;
|
||||
@@ -1399,11 +1400,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
argumentVars += IRVariable(*argument).stackSlots();
|
||||
}
|
||||
define(IRVariable(_functionCall)) <<
|
||||
m_utils.bytesConcatFunction(argumentTypes) <<
|
||||
m_utils.bytesOrStringConcatFunction(argumentTypes, functionType->kind()) <<
|
||||
"(" <<
|
||||
joinHumanReadable(argumentVars) <<
|
||||
")\n";
|
||||
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::MetaType:
|
||||
|
||||
Reference in New Issue
Block a user