From 40ab01eddf22f85aeb423fc6e4d13d9363582fd5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Aug 2015 11:06:57 +0200 Subject: [PATCH] Allow explicit conversions bytes <-> string. --- libsolidity/AST.cpp | 10 ++++++-- libsolidity/CompilerUtils.cpp | 9 ++++++- libsolidity/Types.cpp | 15 +++++++++++ libsolidity/Types.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 25 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 17 +++++++++++++ 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 05e2d52e8..8bad6ccf8 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -844,9 +844,15 @@ void FunctionCall::checkTypeRequirements(TypePointers const*) BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion.")); if (!isPositionalCall) BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); - if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) - BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); m_type = type.getActualType(); + auto argType = m_arguments.front()->getType(); + if (auto argRefType = dynamic_cast(argType.get())) + // do not change the data location when converting + // (data location cannot yet be specified for type conversions) + m_type = ReferenceType::copyForLocationIfReference(argRefType->location(), m_type); + if (!argType->isExplicitlyConvertibleTo(*m_type)) + BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); + return; } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index e62f59e23..492475817 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -404,7 +404,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp case DataLocation::Storage: // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. solAssert( - targetType.isPointer() && + (targetType.isPointer() || (typeOnStack.isByteArray() && targetType.isByteArray())) && typeOnStack.location() == DataLocation::Storage, "Invalid conversion to storage type." ); @@ -469,6 +469,13 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp } break; } + case DataLocation::CallData: + solAssert( + targetType.isByteArray() && + typeOnStack.isByteArray() && + typeOnStack.location() == DataLocation::CallData, + "Invalid conversion to calldata type."); + break; default: solAssert( false, diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 89ff2134f..744362fb7 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -789,6 +789,21 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const } } +bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + // allow conversion bytes <-> string + if (_convertTo.getCategory() != getCategory()) + return false; + auto& convertTo = dynamic_cast(_convertTo); + if (convertTo.location() != location()) + return false; + if (!isByteArray() || !convertTo.isByteArray()) + return false; + return true; +} + bool ArrayType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index f18855b26..bc499d595 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -477,6 +477,7 @@ public: {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(const Type& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index fc1d2eab8..a10b47676 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5099,6 +5099,31 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(string_bytes_conversion) +{ + char const* sourceCode = R"( + contract Test { + string s; + bytes b; + function f(string _s, uint n) returns (byte) { + b = bytes(_s); + s = string(b); + return bytes(s)[n]; + } + function l() returns (uint) { return bytes(s).length; } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + BOOST_CHECK(callContractFunction( + "f(string,uint256)", + u256(0x40), + u256(2), + u256(6), + string("abcdef") + ) == encodeArgs("c")); + BOOST_CHECK(callContractFunction("l()") == encodeArgs(u256(6))); +} + BOOST_AUTO_TEST_CASE(string_as_mapping_key) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index cfc43df91..6b116f251 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2149,6 +2149,23 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(string_bytes_conversion) +{ + char const* text = R"( + contract Test { + string s; + bytes b; + function h(string _s) external { bytes(_s).length; } + function i(string _s) internal { bytes(_s).length; } + function j() internal { bytes(s).length; } + function k(bytes _b) external { string(_b); } + function l(bytes _b) internal { string(_b); } + function m() internal { string(b); } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_SUITE_END() }