diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 7a4aa9942..ba989972c 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -441,7 +441,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) { bool allowed = false; if (auto arrayType = dynamic_cast(type)) - allowed = arrayType->isByteArray(); + allowed = arrayType->isByteArrayOrString(); if (!allowed) m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented."); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f3ba2884b..5b03717d9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1783,7 +1783,7 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( ( ( resultArrayType->isPointer() || - (argArrayType->isByteArray() && resultArrayType->isByteArray()) + (argArrayType->isByteArrayOrString() && resultArrayType->isByteArrayOrString()) ) && resultArrayType->location() == DataLocation::Storage ), @@ -1791,7 +1791,7 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( ); else solAssert( - argArrayType->isByteArray() && !argArrayType->isString() && resultType->category() == Type::Category::FixedBytes, + argArrayType->isByteArray() && resultType->category() == Type::Category::FixedBytes, "" ); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index fb31a9303..74cd76ee7 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1206,7 +1206,7 @@ BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) ); return arrayType->location() != DataLocation::CallData && - arrayType->isByteArray() && + arrayType->isByteArrayOrString() && !(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()); } else @@ -1571,11 +1571,11 @@ BoolResult ArrayType::isExplicitlyConvertibleTo(Type const& _convertTo) const return true; // allow conversion bytes <-> string and bytes -> bytesNN if (_convertTo.category() != category()) - return isByteArray() && !isString() && _convertTo.category() == Type::Category::FixedBytes; + return isByteArray() && _convertTo.category() == Type::Category::FixedBytes; auto& convertTo = dynamic_cast(_convertTo); if (convertTo.location() != location()) return false; - if (!isByteArray() || !convertTo.isByteArray()) + if (!isByteArrayOrString() || !convertTo.isByteArrayOrString()) return false; return true; } @@ -1585,7 +1585,7 @@ string ArrayType::richIdentifier() const string id; if (isString()) id = "t_string"; - else if (isByteArray()) + else if (isByteArrayOrString()) id = "t_bytes"; else { @@ -1751,7 +1751,7 @@ string ArrayType::toString(bool _short) const string ret; if (isString()) ret = "string"; - else if (isByteArray()) + else if (isByteArrayOrString()) ret = "bytes"; else { @@ -1770,7 +1770,7 @@ string ArrayType::canonicalName() const string ret; if (isString()) ret = "string"; - else if (isByteArray()) + else if (isByteArrayOrString()) ret = "bytes"; else { @@ -1784,7 +1784,7 @@ string ArrayType::canonicalName() const string ArrayType::signatureInExternalFunction(bool _structsByName) const { - if (isByteArray()) + if (isByteArrayOrString()) return canonicalName(); else { @@ -1899,7 +1899,7 @@ u256 ArrayType::memoryDataSize() const { solAssert(!isDynamicallySized(), ""); solAssert(m_location == DataLocation::Memory, ""); - solAssert(!isByteArray(), ""); + solAssert(!isByteArrayOrString(), ""); bigint size = bigint(m_length) * m_baseType->memoryHeadSize(); solAssert(size <= numeric_limits::max(), "Array size does not fit u256."); return u256(size); @@ -2701,7 +2701,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): } else if (auto arrayType = dynamic_cast(returnType)) { - if (arrayType->isByteArray()) + if (arrayType->isByteArrayOrString()) // Return byte arrays as whole. break; returnType = arrayType->baseType(); @@ -2720,7 +2720,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (member.type->category() != Category::Mapping) { if (auto arrayType = dynamic_cast(member.type)) - if (!arrayType->isByteArray()) + if (!arrayType->isByteArrayOrString()) continue; m_returnParameterTypes.push_back(TypeProvider::withLocationIfReference( DataLocation::Memory, @@ -3813,7 +3813,7 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons } else if ( auto const* arrayType = dynamic_cast(m_actualType); - arrayType && arrayType->isByteArray() + arrayType && arrayType->isByteArrayOrString() ) members.emplace_back("concat", TypeProvider::function( TypePointers{}, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index f15da58ab..3f134df17 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -837,8 +837,10 @@ public: BoolResult validForLocation(DataLocation _loc) const override; + /// @returns true if this is a byte array. + bool isByteArray() const { return m_arrayKind == ArrayKind::Bytes; } /// @returns true if this is a byte array or a string - bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; } + bool isByteArrayOrString() const { return m_arrayKind != ArrayKind::Ordinary; } /// @returns true if this is a string bool isString() const { return m_arrayKind == ArrayKind::String; } Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; } @@ -849,11 +851,11 @@ public: std::unique_ptr copyForLocation(DataLocation _location, bool _isPointer) const override; /// The offset to advance in calldata to move from one array element to the next. - unsigned calldataStride() const { return isByteArray() ? 1 : m_baseType->calldataHeadSize(); } + unsigned calldataStride() const { return isByteArrayOrString() ? 1 : m_baseType->calldataHeadSize(); } /// The offset to advance in memory to move from one array element to the next. - unsigned memoryStride() const { return isByteArray() ? 1 : m_baseType->memoryHeadSize(); } + unsigned memoryStride() const { return isByteArrayOrString() ? 1 : m_baseType->memoryHeadSize(); } /// The offset to advance in storage to move from one array element to the next. - unsigned storageStride() const { return isByteArray() ? 1 : m_baseType->storageBytes(); } + unsigned storageStride() const { return isByteArrayOrString() ? 1 : m_baseType->storageBytes(); } void clearCache() const override; diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 97cf3425d..65f9e1723 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -312,7 +312,7 @@ string ABIFunctions::abiEncodingFunction( { case DataLocation::CallData: if ( - fromArray->isByteArray() || + fromArray->isByteArrayOrString() || *fromArray->baseType() == *TypeProvider::uint256() || *fromArray->baseType() == FixedBytesType(32) ) @@ -320,7 +320,7 @@ string ABIFunctions::abiEncodingFunction( else return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options); case DataLocation::Memory: - if (fromArray->isByteArray()) + if (fromArray->isByteArrayOrString()) return abiEncodingFunctionMemoryByteArray(*fromArray, *toArray, _options); else return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options); @@ -448,7 +448,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup( solAssert(fromArrayType.location() == DataLocation::CallData, ""); solAssert( - fromArrayType.isByteArray() || + fromArrayType.isByteArrayOrString() || *fromArrayType.baseType() == *TypeProvider::uint256() || *fromArrayType.baseType() == FixedBytesType(32), "" @@ -468,7 +468,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup( _to.identifier() + _options.toFunctionNameSuffix(); return createFunction(functionName, [&]() { - bool needsPadding = _options.padded && fromArrayType.isByteArray(); + bool needsPadding = _options.padded && fromArrayType.isByteArrayOrString(); if (fromArrayType.isDynamicallySized()) { Whiskers templ(R"( @@ -482,7 +482,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup( )"); templ("storeLength", arrayStoreLengthForEncodingFunction(toArrayType, _options)); templ("functionName", functionName); - if (fromArrayType.isByteArray() || fromArrayType.calldataStride() == 1) + if (fromArrayType.isByteArrayOrString() || fromArrayType.calldataStride() == 1) templ("scaleLengthByStride", ""); else templ("scaleLengthByStride", @@ -536,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); - solAssert(!_from.isByteArray(), ""); + solAssert(!_from.isByteArrayOrString(), ""); if (_from.dataStoredIn(DataLocation::Storage)) solAssert(_from.baseType()->storageBytes() > 16, ""); @@ -647,10 +647,10 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray( solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.length() == _to.length(), ""); solAssert(_from.dataStoredIn(DataLocation::Memory), ""); - solAssert(_from.isByteArray(), ""); + solAssert(_from.isByteArrayOrString(), ""); return createFunction(functionName, [&]() { - solAssert(_to.isByteArray(), ""); + solAssert(_to.isByteArrayOrString(), ""); Whiskers templ(R"( function (value, pos) -> end { let length := (value) @@ -686,9 +686,9 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( solAssert(_from.dataStoredIn(DataLocation::Storage), ""); return createFunction(functionName, [&]() { - if (_from.isByteArray()) + if (_from.isByteArrayOrString()) { - solAssert(_to.isByteArray(), ""); + solAssert(_to.isByteArrayOrString(), ""); Whiskers templ(R"( // -> function (value, pos) -> ret { @@ -1168,7 +1168,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory); solAssert(_type.calldataStride() > 0, ""); @@ -1275,7 +1275,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); - solAssert(_type.isByteArray(), ""); + solAssert(_type.isByteArrayOrString(), ""); string functionName = "abi_decode_available_length_" + diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 27afe1498..d2691c877 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -50,8 +50,8 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons solAssert(_targetType.location() == DataLocation::Storage, ""); Type const* uint256 = TypeProvider::uint256(); - Type const* targetBaseType = _targetType.isByteArray() ? uint256 : _targetType.baseType(); - Type const* sourceBaseType = _sourceType.isByteArray() ? uint256 : _sourceType.baseType(); + Type const* targetBaseType = _targetType.isByteArrayOrString() ? uint256 : _targetType.baseType(); + Type const* sourceBaseType = _sourceType.isByteArrayOrString() ? uint256 : _sourceType.baseType(); // TODO unroll loop for small sizes @@ -97,7 +97,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref source_ref source_length target_ref target_length if (_targetType.isDynamicallySized()) // store new target length - if (!_targetType.isByteArray()) + if (!_targetType.isByteArrayOrString()) // Otherwise, length will be stored below. _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; if (sourceBaseType->category() == Type::Category::Mapping) @@ -126,7 +126,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons evmasm::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag(); // special case for short byte arrays: Store them together with their length. - if (_targetType.isByteArray()) + if (_targetType.isByteArrayOrString()) { // stack: target_ref target_data_end source_length target_data_pos source_ref _context << Instruction::DUP3; @@ -141,7 +141,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons _context << Instruction::DUP3 << u256(31) << Instruction::LT; evmasm::AssemblyItem longByteArray = _context.appendConditionalJump(); // store the short byte array - solAssert(_sourceType.isByteArray(), ""); + solAssert(_sourceType.isByteArrayOrString(), ""); if (_sourceType.location() == DataLocation::Storage) { // just copy the slot, it contains length and data @@ -323,7 +323,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord { if (!_sourceType.isDynamicallySized()) m_context << _sourceType.length(); - if (!_sourceType.isByteArray()) + if (!_sourceType.isByteArrayOrString()) convertLengthToSize(_sourceType); string routine = "calldatacopy(target, source, len)\n"; @@ -375,14 +375,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD; m_context << Instruction::SWAP1; } - if (!_sourceType.isByteArray()) + if (!_sourceType.isByteArrayOrString()) convertLengthToSize(_sourceType); // stack: m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4; // We can resort to copying full 32 bytes only if // - the length is known to be a multiple of 32 or // - we will pad to full 32 bytes later anyway. - if (!_sourceType.isByteArray() || _padToWordBoundaries) + if (!_sourceType.isByteArrayOrString() || _padToWordBoundaries) utils.memoryCopy32(); else utils.memoryCopy(); @@ -390,7 +390,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord m_context << Instruction::SWAP1 << Instruction::POP; // stack: - bool paddingNeeded = _padToWordBoundaries && _sourceType.isByteArray(); + bool paddingNeeded = _padToWordBoundaries && _sourceType.isByteArrayOrString(); if (paddingNeeded) { @@ -446,7 +446,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord m_context << Instruction::DUP1 << Instruction::ISZERO; evmasm::AssemblyItem loopEnd = m_context.appendConditionalJump(); // Special case for tightly-stored byte arrays - if (_sourceType.isByteArray()) + if (_sourceType.isByteArrayOrString()) { // stack here: memory_offset storage_offset length m_context << Instruction::DUP1 << u256(31) << Instruction::LT; @@ -482,14 +482,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord } // stack here: memory_end_offset storage_data_offset memory_offset - bool haveByteOffset = !_sourceType.isByteArray() && storageBytes <= 16; + bool haveByteOffset = !_sourceType.isByteArrayOrString() && storageBytes <= 16; if (haveByteOffset) m_context << u256(0) << Instruction::SWAP1; // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset evmasm::AssemblyItem loopStart = m_context.newTag(); m_context << loopStart; // load and store - if (_sourceType.isByteArray()) + if (_sourceType.isByteArrayOrString()) { // Packed both in storage and memory. m_context << Instruction::DUP2 << Instruction::SLOAD; @@ -528,12 +528,12 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset if (haveByteOffset) m_context << Instruction::SWAP1 << Instruction::POP; - if (!_sourceType.isByteArray()) + if (!_sourceType.isByteArrayOrString()) { solAssert(_sourceType.calldataStride() % 32 == 0, ""); solAssert(_sourceType.memoryStride() % 32 == 0, ""); } - if (_padToWordBoundaries && _sourceType.isByteArray()) + if (_padToWordBoundaries && _sourceType.isByteArrayOrString()) { // memory_end_offset - start is the actual length (we want to compute the ceil of). // memory_offset - start is its next multiple of 32, but it might be off by 32. @@ -624,7 +624,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE; // Special case: short byte arrays are stored togeher with their length evmasm::AssemblyItem endTag = m_context.newTag(); - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) { // stack: ref old_length m_context << Instruction::DUP1 << u256(31) << Instruction::LT; @@ -664,7 +664,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ArrayType const& _type = dynamic_cast(*type); solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); unsigned stackHeightStart = _context.stackHeight(); @@ -677,7 +677,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2"); // Special case for short byte arrays, they are stored together with their length - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) { evmasm::AssemblyItem regularPath = _context.newTag(); // We start by a large case-distinction about the old and new length of the byte array. @@ -766,7 +766,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const // stack: ref new_length old_length // store new length _context << Instruction::DUP2; - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) // For a "long" byte array, store length as 2*length+1 _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; _context << Instruction::DUP4 << Instruction::SSTORE; @@ -806,10 +806,10 @@ void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) { // We almost always just add 2 (length of byte arrays is shifted left by one) // except for the case where we transition from a short byte array @@ -850,10 +850,10 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) { m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1; m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); @@ -999,7 +999,7 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con } else { - if (!_arrayType.isByteArray()) + if (!_arrayType.isByteArrayOrString()) { if (_arrayType.location() == DataLocation::Memory) m_context << _arrayType.memoryStride(); @@ -1031,7 +1031,7 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept break; case DataLocation::Storage: m_context << Instruction::SLOAD; - if (_arrayType.isByteArray()) + if (_arrayType.isByteArrayOrString()) m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); break; } @@ -1062,7 +1062,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b { case DataLocation::Memory: // stack: - if (!_arrayType.isByteArray()) + if (!_arrayType.isByteArrayOrString()) m_context << u256(_arrayType.memoryHeadSize()) << Instruction::MUL; if (_arrayType.isDynamicallySized()) m_context << u256(32) << Instruction::ADD; @@ -1071,7 +1071,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b m_context << Instruction::ADD; break; case DataLocation::CallData: - if (!_arrayType.isByteArray()) + if (!_arrayType.isByteArrayOrString()) { m_context << _arrayType.calldataStride(); m_context << Instruction::MUL; @@ -1090,7 +1090,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b // stack: [] evmasm::AssemblyItem endTag = m_context.newTag(); - if (_arrayType.isByteArray()) + if (_arrayType.isByteArrayOrString()) { // Special case of short byte arrays. m_context << Instruction::SWAP1; @@ -1153,7 +1153,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d { solAssert(_arrayType.baseType()->storageBytes() <= 32, ""); if ( - !_arrayType.isByteArray() && + !_arrayType.isByteArrayOrString() && _arrayType.baseType()->storageBytes() < 32 && m_context.useABICoderV2() ) @@ -1165,7 +1165,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d CompilerUtils(m_context).loadFromMemoryDynamic( *_arrayType.baseType(), true, - !_arrayType.isByteArray(), + !_arrayType.isByteArrayOrString(), false ); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 1876b6ac0..0ad37241b 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -970,7 +970,7 @@ void CompilerUtils::convertType( else if (targetTypeCategory == Type::Category::Array) { auto const& arrayType = dynamic_cast(_targetType); - solAssert(arrayType.isByteArray()); + solAssert(arrayType.isByteArrayOrString()); size_t storageSize = 32 + ((data.size() + 31) / 32) * 32; allocateMemory(storageSize); // stack: mempos @@ -992,7 +992,7 @@ void CompilerUtils::convertType( if (_targetType.category() == Type::Category::FixedBytes) { solAssert( - typeOnStack.isByteArray() && !typeOnStack.isString(), + typeOnStack.isByteArray(), "Array types other than bytes not convertible to bytesNN." ); solAssert(typeOnStack.isDynamicallySized()); @@ -1019,7 +1019,7 @@ void CompilerUtils::convertType( case DataLocation::Storage: // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. solAssert( - (targetType.isPointer() || (typeOnStack.isByteArray() && targetType.isByteArray())) && + (targetType.isPointer() || (typeOnStack.isByteArrayOrString() && targetType.isByteArrayOrString())) && typeOnStack.location() == DataLocation::Storage, "Invalid conversion to storage type." ); @@ -1105,7 +1105,7 @@ void CompilerUtils::convertType( } case DataLocation::CallData: solAssert( - ((targetType.isByteArray() && typeOnStack.isByteArray()) || _typeOnStack == _targetType) && + ((targetType.isByteArrayOrString() && typeOnStack.isByteArrayOrString()) || _typeOnStack == _targetType) && typeOnStack.location() == DataLocation::CallData, "Invalid conversion to calldata type." ); @@ -1119,7 +1119,7 @@ void CompilerUtils::convertType( if (_targetType.category() == Type::Category::FixedBytes) { solAssert( - typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(), + typeOnStack.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN." ); solAssert(typeOnStack.isDynamicallySized()); @@ -1142,7 +1142,7 @@ void CompilerUtils::convertType( auto const& targetArrayType = dynamic_cast(_targetType); solAssert( typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType) || - (typeOnStack.arrayType().isByteArray() && targetArrayType.isByteArray()) + (typeOnStack.arrayType().isByteArrayOrString() && targetArrayType.isByteArrayOrString()) ); solAssert( typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) && diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index a76d1f3d9..da3022251 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -153,7 +153,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& if (paramTypes[i]->isDynamicallySized()) { solAssert( - dynamic_cast(*paramTypes[i]).isByteArray(), + dynamic_cast(*paramTypes[i]).isByteArrayOrString(), "Expected string or byte array for mapping key type" ); @@ -239,7 +239,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& if (returnTypes[i]->category() == Type::Category::Mapping) continue; if (auto arrayType = dynamic_cast(returnTypes[i])) - if (!arrayType->isByteArray()) + if (!arrayType->isByteArrayOrString()) continue; pair const& offsets = structType->storageOffsetsOfMember(names[i]); m_context << Instruction::DUP1 << u256(offsets.first) << Instruction::ADD << u256(offsets.second); @@ -1047,7 +1047,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // stack: ArrayReference (newLength-1) ArrayUtils(m_context).accessIndex(*arrayType, false); - if (arrayType->isByteArray()) + if (arrayType->isByteArrayOrString()) setLValue(_functionCall); else setLValueToStorageItem(_functionCall); @@ -1084,7 +1084,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack()); // stack: argValue storageSlot slotOffset - if (!arrayType->isByteArray()) + if (!arrayType->isByteArrayOrString()) StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true); else StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); @@ -1165,7 +1165,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // update free memory pointer m_context << Instruction::DUP1; // Stack: memptr requested_length requested_length - if (arrayType.isByteArray()) + if (arrayType.isByteArrayOrString()) // Round up to multiple of 32 m_context << u256(31) << Instruction::ADD << u256(31) << Instruction::NOT << Instruction::AND; else @@ -2086,7 +2086,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { case DataLocation::Storage: ArrayUtils(m_context).accessIndex(arrayType); - if (arrayType.isByteArray()) + if (arrayType.isByteArrayOrString()) { solAssert(!arrayType.isString(), "Index access to string is not allowed."); setLValue(_indexAccess); @@ -2096,7 +2096,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) break; case DataLocation::Memory: ArrayUtils(m_context).accessIndex(arrayType); - setLValue(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray()); + setLValue(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArrayOrString()); break; case DataLocation::CallData: ArrayUtils(m_context).accessCallDataArrayElement(arrayType); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 84f63a9e1..9b50d68cd 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1183,8 +1183,8 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) w("calldata", _type.location() == DataLocation::CallData); if (_type.location() == DataLocation::Storage) { - w("byteArray", _type.isByteArray()); - if (_type.isByteArray()) + w("byteArray", _type.isByteArrayOrString()); + if (_type.isByteArrayOrString()) w("extractByteArrayLength", extractByteArrayLengthFunction()); } @@ -1220,7 +1220,7 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) solAssert(_type.location() == DataLocation::Storage, ""); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32); - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) return resizeDynamicByteArrayFunction(_type); string functionName = "resize_array_" + _type.identifier(); @@ -1259,7 +1259,7 @@ string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.baseType()->category() != Type::Category::Mapping, ""); - solAssert(!_type.isByteArray(), ""); + solAssert(!_type.isByteArrayOrString(), ""); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32); string functionName = "cleanup_storage_array_end_" + _type.identifier(); @@ -1319,7 +1319,7 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type) string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type) { - solAssert(_type.isByteArray(), ""); + solAssert(_type.isByteArrayOrString(), ""); solAssert(_type.isDynamicallySized(), ""); string functionName = "clean_up_bytearray_end_slots_" + _type.identifier(); @@ -1479,7 +1479,7 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type) solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); - if (_type.isByteArray()) + if (_type.isByteArrayOrString()) return storageByteArrayPopFunction(_type); string functionName = "array_pop_" + _type.identifier(); @@ -1509,7 +1509,7 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - solAssert(_type.isByteArray(), ""); + solAssert(_type.isByteArrayOrString(), ""); string functionName = "byte_array_pop_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { @@ -1566,7 +1566,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type c return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array ) { - + let data := sload(array) let oldLen := (data) if iszero(lt(oldLen, )) { () } @@ -1598,20 +1598,20 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type c let slot, offset := (array, oldLen) (slot, offset ) } - + let oldLen := sload(array) if iszero(lt(oldLen, )) { () } sstore(array, add(oldLen, 1)) let slot, offset := (array, oldLen) (slot, offset ) - + })") ("functionName", functionName) ("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack())) ("panic", panicFunction(PanicCode::ResourceError)) - ("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") + ("extractByteArrayLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "") ("dataAreaFunction", arrayDataAreaFunction(_type)) - ("isByteArray", _type.isByteArray()) + ("isByteArrayOrString", _type.isByteArrayOrString()) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType())) ("maxArrayLength", (u256(1) << 64).str()) @@ -1642,9 +1642,9 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) slot, offset := (array, oldLen) })") ("functionName", functionName) - ("isBytes", _type.isByteArray()) - ("increaseBytesSize", _type.isByteArray() ? increaseByteArraySizeFunction(_type) : "") - ("extractLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") + ("isBytes", _type.isByteArrayOrString()) + ("increaseBytesSize", _type.isByteArrayOrString() ? increaseByteArraySizeFunction(_type) : "") + ("extractLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "") ("panic", panicFunction(PanicCode::ResourceError)) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) @@ -1795,7 +1795,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, if (!_toType.isDynamicallySized()) solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), ""); - if (_fromType.isByteArray()) + if (_fromType.isByteArrayOrString()) return copyByteArrayToStorageFunction(_fromType, _toType); if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType()) return copyValueArrayStorageToStorageFunction(_fromType, _toType); @@ -1902,8 +1902,8 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy *_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast(_toType), "" ); - solAssert(_fromType.isByteArray(), ""); - solAssert(_toType.isByteArray(), ""); + solAssert(_fromType.isByteArrayOrString(), ""); + solAssert(_toType.isByteArrayOrString(), ""); string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); return m_functionCollector.createFunction(functionName, [&](){ @@ -1980,8 +1980,8 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& solAssert(_toType.baseType()->isValueType(), ""); solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), ""); - solAssert(!_fromType.isByteArray(), ""); - solAssert(!_toType.isByteArray(), ""); + solAssert(!_fromType.isByteArrayOrString(), ""); + solAssert(!_toType.isByteArrayOrString(), ""); solAssert(_fromType.dataStoredIn(DataLocation::Storage), ""); solAssert(_toType.dataStoredIn(DataLocation::Storage), ""); @@ -2155,7 +2155,7 @@ string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) })") ("functionName", functionName) ("stride", to_string(_type.location() == DataLocation::Memory ? _type.memoryStride() : _type.calldataStride())) - ("byteArray", _type.isByteArray()) + ("byteArray", _type.isByteArrayOrString()) ("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256())) .render(); default: @@ -2187,7 +2187,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) )"); w("functionName", functionName); w("panic", panicFunction(PanicCode::ResourceError)); - w("byteArray", _type.isByteArray()); + w("byteArray", _type.isByteArrayOrString()); w("roundUp", roundUpFunction()); w("dynamic", _type.isDynamicallySized()); return w.render(); @@ -2262,7 +2262,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) ("dataAreaFunc", arrayDataAreaFunction(_type)) ("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction()) ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16) - ("isBytesArray", _type.isByteArray()) + ("isBytesArray", _type.isByteArrayOrString()) ("storageSize", _type.baseType()->storageSize().str()) ("storageBytes", toString(_type.baseType()->storageBytes())) ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())) @@ -2376,7 +2376,7 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) { - solAssert(!_type.isByteArray(), ""); + solAssert(!_type.isByteArrayOrString(), ""); if (_type.dataStoredIn(DataLocation::Storage)) solAssert(_type.baseType()->storageBytes() > 16, ""); string functionName = "array_nextElement_" + _type.identifier(); @@ -2447,7 +2447,7 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _ solAssert(_to.memoryStride() == 32, ""); solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), ""); solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), ""); - solAssert(!_from.isByteArray(), ""); + solAssert(!_from.isByteArrayOrString(), ""); solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, ""); return Whiskers(R"( function (slot) -> memPtr { @@ -2755,7 +2755,7 @@ string YulUtilFunctions::updateStorageValueFunction( solAssert(_fromType.category() == Type::Category::StringLiteral, ""); solAssert(toReferenceType->category() == Type::Category::Array, ""); auto const& toArrayType = dynamic_cast(*toReferenceType); - solAssert(toArrayType.isByteArray(), ""); + solAssert(toArrayType.isByteArrayOrString(), ""); return Whiskers(R"( function (slot, offset) { @@ -3224,7 +3224,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) solAssert( fromType.arrayType().isImplicitlyConvertibleTo(targetType) || - (fromType.arrayType().isByteArray() && targetType.isByteArray()) + (fromType.arrayType().isByteArrayOrString() && targetType.isByteArrayOrString()) ); solAssert( fromType.arrayType().dataStoredIn(DataLocation::CallData) && @@ -3460,7 +3460,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) string YulUtilFunctions::bytesToFixedBytesConversionFunction(ArrayType const& _from, FixedBytesType const& _to) { - solAssert(_from.isByteArray() && !_from.isString(), ""); + solAssert(_from.isByteArray(), ""); solAssert(_from.isDynamicallySized(), ""); string functionName = "convert_bytes_to_fixedbytes_from_" + _from.identifier() + "_to_" + _to.identifier(); return m_functionCollector.createFunction(functionName, [&](auto& _args, auto& _returnParams) { @@ -3633,14 +3633,14 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy { if (_to.dataStoredIn(DataLocation::CallData)) solAssert( - _from.dataStoredIn(DataLocation::CallData) && _from.isByteArray() && _to.isByteArray(), + _from.dataStoredIn(DataLocation::CallData) && _from.isByteArrayOrString() && _to.isByteArrayOrString(), "" ); // 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())) && + (_to.isPointer() || (_from.isByteArrayOrString() && _to.isByteArrayOrString())) && _from.location() == DataLocation::Storage, "Invalid conversion to storage type." ); @@ -4238,7 +4238,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const } else if (_to.category() == Type::Category::Array) { - solAssert(dynamic_cast(_to).isByteArray(), ""); + solAssert(dynamic_cast(_to).isByteArrayOrString(), ""); Whiskers templ(R"( function () -> converted { converted := () diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index c2bb0c3cf..e361ada47 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -677,7 +677,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) continue; if ( auto const* arrayType = dynamic_cast(returnTypes[i]); - arrayType && !arrayType->isByteArray() + arrayType && !arrayType->isByteArrayOrString() ) continue; @@ -698,7 +698,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) solAssert(returnTypes.size() == 1, ""); auto const* arrayType = dynamic_cast(returnTypes.front()); if (arrayType) - solAssert(arrayType->isByteArray(), ""); + solAssert(arrayType->isByteArrayOrString(), ""); vector retVars = IRVariable("ret", *returnTypes.front()).stackSlots(); returnVariables += retVars; code += Whiskers(R"( diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 86d00dbc9..065b351f6 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2088,7 +2088,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (dynamic_cast(&actualType)) solAssert(member == "wrap" || member == "unwrap"); else if (auto const* arrayType = dynamic_cast(&actualType)) - solAssert(arrayType->isByteArray() && member == "concat"); + solAssert(arrayType->isByteArrayOrString() && member == "concat"); else // The old code generator had a generic "else" case here // without any specific code being generated, @@ -2226,7 +2226,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) setLValue(_indexAccess, IRLValue{ *arrayType.baseType(), - IRLValue::Memory{memAddress, arrayType.isByteArray()} + IRLValue::Memory{memAddress, arrayType.isByteArrayOrString()} }); break; } @@ -2240,7 +2240,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) ", " + expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) + ")"; - if (arrayType.isByteArray()) + if (arrayType.isByteArrayOrString()) define(_indexAccess) << m_utils.cleanupFunction(*arrayType.baseType()) << "(calldataload(" << diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index b42e9b355..76342ba08 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -953,7 +953,7 @@ bool isReturnedFromStructGetter(Type const* _type) if (category == Type::Category::Mapping) return false; if (category == Type::Category::Array) - return dynamic_cast(*_type).isByteArray(); + return dynamic_cast(*_type).isByteArrayOrString(); // default return true; } @@ -990,7 +990,7 @@ void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall) { if ( type->isValueType() || - (type->category() == Type::Category::Array && dynamic_cast(*type).isByteArray()) + (type->category() == Type::Category::Array && dynamic_cast(*type).isByteArrayOrString()) ) { solAssert(symbArguments.empty(), ""); @@ -1071,7 +1071,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) if (auto sliceType = dynamic_cast(argType)) arrayType = &sliceType->arrayType(); - if (arrayType && arrayType->isByteArray() && smt::isFixedBytes(*funCallType)) + if (arrayType && arrayType->isByteArrayOrString() && smt::isFixedBytes(*funCallType)) { auto array = dynamic_pointer_cast(m_context.expression(*argument)); bytesToFixedBytesAssertions(*array, _funCall); @@ -2695,14 +2695,14 @@ Expression const* SMTEncoder::cleanExpression(Expression const& _expr) auto typeType = dynamic_cast(functionCall->expression().annotation().type); solAssert(typeType, ""); if (auto const* arrayType = dynamic_cast(typeType->actualType())) - if (arrayType->isByteArray()) + if (arrayType->isByteArrayOrString()) { // this is a cast to `bytes` solAssert(functionCall->arguments().size() == 1, ""); Expression const& arg = *functionCall->arguments()[0]; if ( auto const* argArrayType = dynamic_cast(arg.annotation().type); - argArrayType && argArrayType->isByteArray() + argArrayType && argArrayType->isByteArrayOrString() ) return cleanExpression(arg); } diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 36d9a0f35..b9041c6bd 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -96,7 +96,7 @@ SortPointer smtSort(frontend::Type const& _type) auto sliceArrayType = dynamic_cast(&_type); ArrayType const* arrayType = sliceArrayType ? &sliceArrayType->arrayType() : dynamic_cast(&_type); if ( - (arrayType && (arrayType->isString() || arrayType->isByteArray())) || + (arrayType && arrayType->isByteArrayOrString()) || _type.category() == frontend::Type::Category::StringLiteral ) tupleName = "bytes"; diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index cf395ffa0..9c7a645b6 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -176,7 +176,7 @@ Json::Value ABI::formatType( ret["type"] = _encodingType.canonicalName() + suffix; else if (ArrayType const* arrayType = dynamic_cast(&_encodingType)) { - if (arrayType->isByteArray()) + if (arrayType->isByteArrayOrString()) ret["type"] = _encodingType.canonicalName() + suffix; else { diff --git a/libsolidity/interface/StorageLayout.cpp b/libsolidity/interface/StorageLayout.cpp index a484503c1..74d7ccf4b 100644 --- a/libsolidity/interface/StorageLayout.cpp +++ b/libsolidity/interface/StorageLayout.cpp @@ -94,7 +94,7 @@ void StorageLayout::generate(Type const* _type) } else if (auto arrayType = dynamic_cast(_type)) { - if (arrayType->isByteArray()) + if (arrayType->isByteArrayOrString()) typeInfo["encoding"] = "bytes"; else {