diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/CMakeLists.txt | 17 | ||||
-rw-r--r-- | libsolidity/analysis/ControlFlowAnalyzer.cpp | 5 | ||||
-rw-r--r-- | libsolidity/analysis/GlobalContext.cpp | 4 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 2 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 46 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 3 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 163 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 29 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/formal/CVC4Interface.h | 11 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 37 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 21 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 9 |
13 files changed, 218 insertions, 131 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 49095fff..91a4678a 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -11,17 +11,11 @@ else() list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") endif() -find_package(GMP QUIET) find_package(CVC4 QUIET) if (${CVC4_FOUND}) - if (${GMP_FOUND}) - include_directories(${CVC4_INCLUDE_DIR}) - add_definitions(-DHAVE_CVC4) - message("CVC4 SMT solver and GMP found. This enables optional SMT checking with CVC4.") - else() - message("CVC4 SMT solver found but its dependency GMP was NOT found. Optional SMT checking with CVC4 will not be available. Please install GMP if it is desired.") - list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") - endif() + include_directories(${CVC4_INCLUDE_DIR}) + add_definitions(-DHAVE_CVC4) + message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.") else() list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") endif() @@ -38,7 +32,6 @@ if (${Z3_FOUND}) target_link_libraries(solidity PUBLIC ${Z3_LIBRARY}) endif() -if (${CVC4_FOUND} AND ${GMP_FOUND}) - target_link_libraries(solidity PUBLIC ${CVC4_LIBRARY}) - target_link_libraries(solidity PUBLIC ${GMP_LIBRARY}) +if (${CVC4_FOUND}) + target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES}) endif() diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 483d08c8..ab6569be 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -75,7 +75,10 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues( { auto& unassignedAtFunctionEntry = unassigned[_functionEntry]; for (auto const& returnParameter: _function.returnParameterList()->parameters()) - if (returnParameter->type()->dataStoredIn(DataLocation::Storage)) + if ( + returnParameter->type()->dataStoredIn(DataLocation::Storage) || + returnParameter->type()->category() == Type::Category::Mapping + ) unassignedAtFunctionEntry.insert(returnParameter.get()); } diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 7b4bc3aa..3ac8fd47 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -42,7 +42,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("blockhash", make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)), make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), - make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA3, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), make_shared<MagicVariableDeclaration>("log2", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)), @@ -58,7 +58,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA3, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) }) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 2cf09eff..fa0888dd 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -344,7 +344,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) "(remove the \"memory\" or \"storage\" keyword)." ); } - if (varLoc == Location::Default) + if (varLoc == Location::Default || varLoc == Location::CallData) typeLoc = DataLocation::CallData; else typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6882a14f..601a5e7c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -580,9 +580,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) bool TypeChecker::visit(StructDefinition const& _struct) { - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces."); - for (ASTPointer<VariableDeclaration> const& member: _struct.members()) if (!type(*member)->canBeStored()) m_errorReporter.typeError(member->location(), "Type cannot be used in struct."); @@ -610,7 +607,10 @@ bool TypeChecker::visit(StructDefinition const& _struct) if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr) m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition."); + bool insideStruct = true; + swap(insideStruct, m_insideStruct); ASTNode::listAccept(_struct.members(), *this); + m_insideStruct = insideStruct; return false; } @@ -629,7 +629,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function) } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { - if (!type(*var)->canLiveOutsideStorage()) + if ( + !type(*var)->canLiveOutsideStorage() && + !(_function.visibility() <= FunctionDefinition::Visibility::Internal) + ) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); @@ -690,10 +693,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(VariableDeclaration const& _variable) { // Forbid any variable declarations inside interfaces unless they are part of - // a function's input/output parameters. + // * a function's input/output parameters, + // * or inside of a struct definition. if ( m_scope->contractKind() == ContractDefinition::ContractKind::Interface && !_variable.isCallableParameter() + && !m_insideStruct ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); @@ -1626,10 +1631,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else { TypePointer const& argType = type(*arguments.front()); + // Resulting data location is memory unless we are converting from a reference + // type with a different data location. + // (data location cannot yet be specified for type conversions) + DataLocation dataLoc = DataLocation::Memory; if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) - // do not change the data location when converting - // (data location cannot yet be specified for type conversions) - resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType); + dataLoc = argRefType->location(); + resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType); if (!argType->isExplicitlyConvertibleTo(*resultType)) m_errorReporter.typeError( _functionCall.location(), @@ -1685,7 +1693,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) { - if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::SHA3) + if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::KECCAK256) m_errorReporter.typeError(_functionCall.location(), "\"sha3\" has been deprecated in favour of \"keccak256\""); else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) m_errorReporter.typeError(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\""); @@ -1759,7 +1767,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it."; } else if ( - functionType->kind() == FunctionType::Kind::SHA3 || + functionType->kind() == FunctionType::Kind::KECCAK256 || functionType->kind() == FunctionType::Kind::SHA256 || functionType->kind() == FunctionType::Kind::RIPEMD160 ) @@ -1804,7 +1812,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ) msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it."; else if ( - functionType->kind() == FunctionType::Kind::SHA3 || + functionType->kind() == FunctionType::Kind::KECCAK256 || functionType->kind() == FunctionType::Kind::SHA256 || functionType->kind() == FunctionType::Kind::RIPEMD160 ) @@ -2348,22 +2356,6 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte "." ); } - - if ( - type(_expression)->category() == Type::Category::RationalNumber && - _expectedType.category() == Type::Category::FixedBytes - ) - { - auto literal = dynamic_cast<Literal const*>(&_expression); - - if (literal && !literal->isHexNumber()) - m_errorReporter.warning( - _expression.location(), - "Decimal literal assigned to bytesXX variable will be left-aligned. " - "Use an explicit conversion to silence this warning." - ); - } - } void TypeChecker::requireLValue(Expression const& _expression) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index b696de85..ed316f9c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -149,6 +149,9 @@ private: /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; + /// Flag indicating whether we are currently inside a StructDefinition. + bool m_insideStruct = false; + ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3dbbca91..68c201a8 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -355,13 +355,7 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared<BoolType>(); case Token::Number: - { - tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal); - if (get<0>(validLiteral) == true) - return make_shared<RationalNumberType>(get<1>(validLiteral)); - else - return TypePointer(); - } + return RationalNumberType::forLiteral(_literal); case Token::StringLiteral: return make_shared<StringLiteralType>(_literal); default: @@ -400,17 +394,17 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _p encodingType = encodingType->interfaceType(_inLibraryCall); if (encodingType) encodingType = encodingType->encodingType(); - if (auto structType = dynamic_cast<StructType const*>(encodingType.get())) - { - // Structs are fine in the following circumstances: - // - ABIv2 without packed encoding or, - // - storage struct for a library - if (!( - (_encoderV2 && !_packed) || - (structType->location() == DataLocation::Storage && _inLibraryCall) - )) + // Structs are fine in the following circumstances: + // - ABIv2 without packed encoding or, + // - storage struct for a library + if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage)) + return encodingType; + TypePointer baseType = encodingType; + while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get())) + baseType = arrayType->baseType(); + if (dynamic_cast<StructType const*>(baseType.get())) + if (!_encoderV2 || _packed) return TypePointer(); - } return encodingType; } @@ -779,6 +773,30 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value) } } +TypePointer RationalNumberType::forLiteral(Literal const& _literal) +{ + solAssert(_literal.token() == Token::Number, ""); + tuple<bool, rational> validLiteral = isValidLiteral(_literal); + if (get<0>(validLiteral)) + { + TypePointer compatibleBytesType; + if (_literal.isHexNumber()) + { + size_t digitCount = count_if( + _literal.value().begin() + 2, // skip "0x" + _literal.value().end(), + [](unsigned char _c) -> bool { return isxdigit(_c); } + ); + // require even number of digits + if (!(digitCount & 1)) + compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2); + } + + return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType); + } + return TypePointer(); +} + tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal) { rational value; @@ -918,14 +936,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } case Category::FixedBytes: - { - FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); - if (isFractional()) - return false; - if (integerType()) - return fixedBytes.numBytes() * 8 >= integerType()->numBits(); - return false; - } + return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo); default: return false; } @@ -933,11 +944,15 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer mobType = mobileType(); - return - (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)) || - (!isFractional() && _convertTo.category() == Category::FixedBytes) - ; + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + else if (_convertTo.category() != Category::FixedBytes) + { + TypePointer mobType = mobileType(); + return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)); + } + else + return false; } TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const @@ -1263,7 +1278,8 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const return shared_ptr<FixedPointType const>(); // This means we round towards zero for positive and negative values. bigint v = value.numerator() / value.denominator(); - if (negative) + + if (negative && v != 0) // modify value to satisfy bit requirements for negative numbers: // add one bit for sign and decrement because negative numbers can be larger v = (v - 1) << 1; @@ -2509,7 +2525,7 @@ string FunctionType::richIdentifier() const case Kind::Creation: id += "creation"; break; case Kind::Send: id += "send"; break; case Kind::Transfer: id += "transfer"; break; - case Kind::SHA3: id += "sha3"; break; + case Kind::KECCAK256: id += "keccak256"; break; case Kind::Selfdestruct: id += "selfdestruct"; break; case Kind::Revert: id += "revert"; break; case Kind::ECRecover: id += "ecrecover"; break; @@ -2554,28 +2570,10 @@ bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) return false; - FunctionType const& other = dynamic_cast<FunctionType const&>(_other); - if ( - m_kind != other.m_kind || - m_stateMutability != other.stateMutability() || - m_parameterTypes.size() != other.m_parameterTypes.size() || - m_returnParameterTypes.size() != other.m_returnParameterTypes.size() - ) - return false; - - auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; - if ( - !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) || - !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare) - ) - return false; - //@todo this is ugly, but cannot be prevented right now - if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet) + if (!equalExcludingStateMutability(other)) return false; - if (bound() != other.bound()) - return false; - if (bound() && *selfType() != *other.selfType()) + if (m_stateMutability != other.stateMutability()) return false; return true; } @@ -2591,6 +2589,31 @@ bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.category() == category(); } +bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.category() != category()) + return false; + + FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo); + + if (!equalExcludingStateMutability(convertTo)) + return false; + + // non-payable should not be convertible to payable + if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable) + return false; + + // payable should be convertible to non-payable, because you are free to pay 0 ether + if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable) + return true; + + // e.g. pure should be convertible to view, but not the other way around. + if (m_stateMutability > convertTo.stateMutability()) + return false; + + return true; +} + TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const { if (_operator == Token::Value::Delete) @@ -2848,6 +2871,38 @@ bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const ); } +bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const +{ + if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size()) + return false; + return equal( + m_returnParameterTypes.cbegin(), + m_returnParameterTypes.cend(), + _other.m_returnParameterTypes.cbegin(), + [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; } + ); +} + +bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const +{ + if (m_kind != _other.m_kind) + return false; + + if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other)) + return false; + + //@todo this is ugly, but cannot be prevented right now + if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet) + return false; + + if (bound() != _other.bound()) + return false; + + solAssert(!bound() || *selfType() == *_other.selfType(), ""); + + return true; +} + bool FunctionType::isBareCall() const { switch (m_kind) @@ -2894,7 +2949,7 @@ bool FunctionType::isPure() const // FIXME: replace this with m_stateMutability == StateMutability::Pure once // the callgraph analyzer is in place return - m_kind == Kind::SHA3 || + m_kind == Kind::KECCAK256 || m_kind == Kind::ECRecover || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || @@ -2999,7 +3054,7 @@ bool FunctionType::padArguments() const case Kind::BareDelegateCall: case Kind::SHA256: case Kind::RIPEMD160: - case Kind::SHA3: + case Kind::KECCAK256: case Kind::ABIEncodePacked: return false; default: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 1fa2b2f5..34f862c3 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -423,12 +423,12 @@ public: virtual Category category() const override { return Category::RationalNumber; } - /// @returns true if the literal is a valid integer. - static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); + static TypePointer forLiteral(Literal const& _literal); - explicit RationalNumberType(rational const& _value): - m_value(_value) + explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()): + m_value(_value), m_compatibleBytesType(_compatibleBytesType) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -446,7 +446,8 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; - /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. + /// @returns the smallest fixed type that can hold the value or incurs the least precision loss, + /// unless the value was truncated, then a suitable type will be chosen to indicate such event. /// If the integer part does not fit, returns an empty pointer. std::shared_ptr<FixedPointType const> fixedPointType() const; @@ -462,6 +463,13 @@ public: private: rational m_value; + /// Bytes type to which the rational can be explicitly converted. + /// Empty for all rationals that are not directly parsed from hex literals. + TypePointer m_compatibleBytesType; + + /// @returns true if the literal is a valid integer. + static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); + /// @returns true if the literal is a valid rational number. static std::tuple<bool, rational> parseRational(std::string const& _value); @@ -899,7 +907,7 @@ public: Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas Transfer, ///< CALL, but without data and throws on error - SHA3, ///< SHA3 + KECCAK256, ///< KECCAK256 Selfdestruct, ///< SELFDESTRUCT Revert, ///< REVERT ECRecover, ///< CALL to special contract for ecrecover @@ -1005,6 +1013,7 @@ public: virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override; @@ -1034,8 +1043,12 @@ public: /// @param _selfType if the function is bound, this has to be supplied and is the type of the /// expression the function is called on. bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const; - /// @returns true if the types of parameters are equal (doesn't check return parameter types) + /// @returns true if the types of parameters are equal (does not check return parameter types) bool hasEqualParameterTypes(FunctionType const& _other) const; + /// @returns true iff the return types are equal (does not check parameter types) + bool hasEqualReturnTypes(FunctionType const& _other) const; + /// @returns true iff the function type is equal to the given type, ignoring state mutability differences. + bool equalExcludingStateMutability(FunctionType const& _other) const; /// @returns true if the ABI is used for this call (only meaningful for external calls) bool isBareCall() const; @@ -1070,7 +1083,7 @@ public: { switch (m_kind) { - case FunctionType::Kind::SHA3: + case FunctionType::Kind::KECCAK256: case FunctionType::Kind::SHA256: case FunctionType::Kind::RIPEMD160: case FunctionType::Kind::BareCall: diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 54518906..412a7255 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -701,7 +701,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context.appendRevert(); break; } - case FunctionType::Kind::SHA3: + case FunctionType::Kind::KECCAK256: { solAssert(arguments.size() == 1, ""); solAssert(!function.padArguments(), ""); diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h index dcd87525..cd6d761d 100644 --- a/libsolidity/formal/CVC4Interface.h +++ b/libsolidity/formal/CVC4Interface.h @@ -21,8 +21,19 @@ #include <boost/noncopyable.hpp> +#if defined(__GLIBC__) +// The CVC4 headers includes the deprecated system headers <ext/hash_map> +// and <ext/hash_set>. These headers cause a warning that will break the +// build, unless _GLIBCXX_PERMIT_BACKWARD_HASH is set. +#define _GLIBCXX_PERMIT_BACKWARD_HASH +#endif + #include <cvc4/cvc4.h> +#if defined(__GLIBC__) +#undef _GLIBCXX_PERMIT_BACKWARD_HASH +#endif + namespace dev { namespace solidity diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 836e30d2..e800b278 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -58,22 +58,31 @@ using namespace std; using namespace dev; using namespace dev::solidity; -void CompilerStack::setRemappings(vector<string> const& _remappings) +boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping) +{ + auto eq = find(_remapping.begin(), _remapping.end(), '='); + if (eq == _remapping.end()) + return {}; + + auto colon = find(_remapping.begin(), eq, ':'); + + Remapping r; + + r.context = colon == eq ? string() : string(_remapping.begin(), colon); + r.prefix = colon == eq ? string(_remapping.begin(), eq) : string(colon + 1, eq); + r.target = string(eq + 1, _remapping.end()); + + if (r.prefix.empty()) + return {}; + + return r; +} + +void CompilerStack::setRemappings(vector<Remapping> const& _remappings) { - vector<Remapping> remappings; for (auto const& remapping: _remappings) - { - auto eq = find(remapping.begin(), remapping.end(), '='); - if (eq == remapping.end()) - continue; // ignore - auto colon = find(remapping.begin(), eq, ':'); - Remapping r; - r.context = colon == eq ? string() : string(remapping.begin(), colon); - r.prefix = colon == eq ? string(remapping.begin(), eq) : string(colon + 1, eq); - r.target = string(eq + 1, remapping.end()); - remappings.push_back(r); - } - swap(m_remappings, remappings); + solAssert(!remapping.prefix.empty(), ""); + m_remappings = _remappings; } void CompilerStack::setEVMVersion(EVMVersion _version) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 2234a8c9..9a15fbf0 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -84,6 +84,13 @@ public: CompilationSuccessful }; + struct Remapping + { + std::string context; + std::string prefix; + std::string target; + }; + /// Creates a new compiler stack. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. @@ -103,8 +110,11 @@ public: /// All settings, with the exception of remappings, are reset. void reset(bool _keepSources = false); - /// Sets path remappings in the format "context:prefix=target" - void setRemappings(std::vector<std::string> const& _remappings); + // Parses a remapping of the format "context:prefix=target". + static boost::optional<Remapping> parseRemapping(std::string const& _remapping); + + /// Sets path remappings. + void setRemappings(std::vector<Remapping> const& _remappings); /// Sets library addresses. Addresses are cleared iff @a _libraries is missing. /// Will not take effect before running compile. @@ -319,13 +329,6 @@ private: FunctionDefinition const& _function ) const; - struct Remapping - { - std::string context; - std::string prefix; - std::string target; - }; - ReadCallback::Callback m_readFile; ReadCallback::Callback m_smtQuery; bool m_optimize = false; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 58b84163..c1996777 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -326,9 +326,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setEVMVersion(*version); } - vector<string> remappings; + vector<CompilerStack::Remapping> remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) - remappings.push_back(remapping.asString()); + { + if (auto r = CompilerStack::parseRemapping(remapping.asString())) + remappings.emplace_back(std::move(*r)); + else + return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\""); + } m_compilerStack.setRemappings(remappings); Json::Value optimizerSettings = settings.get("optimizer", Json::Value()); |