diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 191042e64..602d1723f 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2714,8 +2714,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) .render(); }); } - - if (_from.category() == Type::Category::ArraySlice) + else if (_from.category() == Type::Category::ArraySlice) { solAssert(_from.isDynamicallySized(), ""); solAssert(_from.dataStoredIn(DataLocation::CallData), ""); @@ -2746,6 +2745,14 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) .render(); }); } + else if (_from.category() == Type::Category::Array) + { + solAssert(_to.category() == Type::Category::Array, ""); + return arrayConversionFunction( + dynamic_cast(_from), + dynamic_cast(_to) + ); + } if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1) return conversionFunctionSpecial(_from, _to); @@ -2852,42 +2859,6 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) case Type::Category::FixedPoint: solUnimplemented("Fixed point types not implemented."); break; - case Type::Category::Array: - { - if (_from == _to) - body = "converted := value"; - else - { - ArrayType const& from = dynamic_cast(_from); - ArrayType const& to = dynamic_cast(_to); - - switch (to.location()) - { - case DataLocation::Storage: - // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. - solAssert( - (to.isPointer() || (from.isByteArray() && to.isByteArray())) && - from.location() == DataLocation::Storage, - "Invalid conversion to storage type." - ); - body = "converted := value"; - break; - case DataLocation::Memory: - // Copy the array to a free position in memory, unless it is already in memory. - if (from.location() == DataLocation::Memory) - body = "converted := value"; - else if (from.location() == DataLocation::CallData) - solUnimplemented("Conversion of calldata types not yet implemented."); - else - body = "converted := " + copyArrayFromStorageToMemoryFunction(from, to) + "(value)"; - break; - case DataLocation::CallData: - solUnimplemented("Conversion of calldata types not yet implemented."); - break; - } - } - break; - } case Type::Category::Struct: { solAssert(toCategory == Type::Category::Struct, ""); @@ -2980,6 +2951,71 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) }); } +string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayType const& _to) +{ + solUnimplementedAssert(_to.location() != DataLocation::CallData, "Conversion of calldata types not yet implemented."); + // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. + if (_to.location() == DataLocation::Storage) + solAssert( + (_to.isPointer() || (_from.isByteArray() && _to.isByteArray())) && + _from.location() == DataLocation::Storage, + "Invalid conversion to storage type." + ); + if (_to.location() == DataLocation::Memory && _from.location() == DataLocation::CallData) + { + solUnimplementedAssert(_from.isDynamicallySized(), ""); + solUnimplementedAssert(!_from.baseType()->isDynamicallyEncoded(), ""); + solUnimplementedAssert(_from.isByteArray() && _to.isByteArray() && _to.isDynamicallySized(), ""); + } + + string functionName = + "convert_array_" + + _from.identifier() + + "_to_" + + _to.identifier(); + + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function (value, length) -> converted { + + } + )"); + templ("functionName", functionName); + templ("fromCalldataDynamic", _from.dataStoredIn(DataLocation::CallData) && _from.isDynamicallySized()); + + if ( + _from == _to || + (_from.dataStoredIn(DataLocation::Memory) && _to.dataStoredIn(DataLocation::Memory)) || + _to.dataStoredIn(DataLocation::Storage) + ) + templ("body", "converted := value"); + else if (_to.dataStoredIn(DataLocation::Memory)) + templ( + "body", + Whiskers(R"( + // Copy the array to a free position in memory + + converted := (value) + + + converted := (length) + (value, add(converted, 0x20), length) + + )") + ("fromStorage", _from.dataStoredIn(DataLocation::Storage)) + ("fromCalldata", _from.dataStoredIn(DataLocation::CallData)) + ("allocateMemoryArray", _from.dataStoredIn(DataLocation::CallData) ? allocateMemoryArrayFunction(_to) : "") + ("copyToMemory", _from.dataStoredIn(DataLocation::CallData) ? copyToMemoryFunction(true) : "") + ("arrayStorageToMem", _from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : "") + .render() + ); + else + solAssert(false, ""); + + return templ.render(); + }); +} + string YulUtilFunctions::cleanupFunction(Type const& _type) { string functionName = string("cleanup_") + _type.identifier(); @@ -3434,29 +3470,6 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const .render(); } - if (_from.category() == Type::Category::Array && _to.category() == Type::Category::Array) - { - auto const& fromArrayType = dynamic_cast(_from); - auto const& toArrayType = dynamic_cast(_to); - - solAssert(!fromArrayType.baseType()->isDynamicallyEncoded(), ""); - solUnimplementedAssert(fromArrayType.isByteArray() && toArrayType.isByteArray(), ""); - solUnimplementedAssert(toArrayType.location() == DataLocation::Memory, ""); - solUnimplementedAssert(fromArrayType.location() == DataLocation::CallData, ""); - solUnimplementedAssert(toArrayType.isDynamicallySized(), ""); - - Whiskers templ(R"( - function (offset, length) -> converted { - converted := (length) - (offset, add(converted, 0x20), length) - } - )"); - templ("functionName", functionName); - templ("allocateMemoryArray", allocateMemoryArrayFunction(toArrayType)); - templ("copyToMemory", copyToMemoryFunction(fromArrayType.location() == DataLocation::CallData)); - return templ.render(); - } - solUnimplementedAssert( _from.category() == Type::Category::StringLiteral, "Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented." diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index d496d9642..161cdf64b 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -421,6 +421,9 @@ public: ); private: + /// Special case of conversion functions - handles all array conversions. + std::string arrayConversionFunction(ArrayType const& _from, ArrayType const& _to); + /// Special case of conversionFunction - handles everything that does not /// use exactly one variable to hold the value. std::string conversionFunctionSpecial(Type const& _from, Type const& _to); diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol index 5e4c35e7f..8defbb9c9 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol @@ -15,6 +15,6 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 77, 1, 2, 88