diff options
26 files changed, 277 insertions, 250 deletions
diff --git a/docs/contributing.rst b/docs/contributing.rst index 43b2fd38..12dea7d1 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -61,7 +61,7 @@ New features and bugfixes should be added to the ``Changelog.md`` file: please follow the style of previous entries, when applicable. Finally, please make sure you respect the `coding style -<https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_ +<https://github.com/ethereum/solidity/blob/develop/CODING_STYLE.md>`_ for this project. Also, even though we do CI testing, please test your code and ensure that it builds locally before submitting a pull request. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 017d5b81..5a6f3875 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -224,9 +224,9 @@ for displaying the current position in the source code inside a debugger or for breakpoint handling. Both kinds of source mappings use integer identifiers to refer to source files. -These are regular array indices into a list of source files usually called -``"sourceList"``, which is part of the combined-json and the output of -the json / npm compiler. +The identifier of a source file is stored in +``output['sources'][sourceName]['id']`` where ``output`` is the output of the +standard-json compiler interface parsed as JSON. .. note :: In the case of instructions that are not associated with any particular source file, diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 9ba6caa5..4749ef1f 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -200,15 +200,27 @@ Input Description "MyLib": "0x123123..." } } - // The following can be used to select desired outputs. - // If this field is omitted, then the compiler loads and does type checking, but will not generate any outputs apart from errors. - // The first level key is the file name and the second is the contract name, where empty contract name refers to the file itself, - // while the star refers to all of the contracts. + // The following can be used to select desired outputs based + // on file and contract names. + // If this field is omitted, then the compiler loads and does type checking, + // but will not generate any outputs apart from errors. + // The first level key is the file name and the second level key is the contract name. + // An empty contract name is used for outputs that are not tied to a contract + // but to the whole source file like the AST. + // A star as contract name refers to all contracts in the file. + // Similarly, a star as a file name matches all files. + // To select all outputs the compiler can possibly generate, use + // "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }" + // but note that this might slow down the compilation process needlessly. // // The available output types are as follows: - // abi - ABI + // + // File level (needs empty string as contract name): // ast - AST of all source files // legacyAST - legacy AST of all source files + // + // Contract level (needs the contract name or "*"): + // abi - ABI // devdoc - Developer documentation (natspec) // userdoc - User documentation (natspec) // metadata - Metadata @@ -281,7 +293,7 @@ Output Description // This contains the file-level outputs. In can be limited/filtered by the outputSelection settings. sources: { "sourceFile.sol": { - // Identifier (used in source maps) + // Identifier of the source (used in source maps) id: 1, // The AST object ast: {}, diff --git a/libdevcore/IndentedWriter.cpp b/libdevcore/IndentedWriter.cpp index 96aaf0fa..1a85957b 100644 --- a/libdevcore/IndentedWriter.cpp +++ b/libdevcore/IndentedWriter.cpp @@ -36,7 +36,7 @@ string IndentedWriter::format() const void IndentedWriter::newLine() { if (!m_lines.back().contents.empty()) - m_lines.push_back({ string(), m_lines.back().indentation }); + m_lines.emplace_back(Line{string(), m_lines.back().indentation}); } void IndentedWriter::indent() diff --git a/libdevcore/StringUtils.cpp b/libdevcore/StringUtils.cpp index 50bf7cce..196ac8f7 100644 --- a/libdevcore/StringUtils.cpp +++ b/libdevcore/StringUtils.cpp @@ -91,7 +91,7 @@ string dev::quotedAlternativesList(vector<string> const& suggestions) vector<string> quotedSuggestions; for (auto& suggestion: suggestions) - quotedSuggestions.push_back("\"" + suggestion + "\""); + quotedSuggestions.emplace_back("\"" + suggestion + "\""); return joinHumanReadable(quotedSuggestions, ", ", " or "); } diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 29d9846d..231eed93 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -83,7 +83,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) { assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); m_deposit += _i.deposit(); - m_items.push_back(_i); + m_items.emplace_back(_i); if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) m_items.back().setLocation(m_currentSourceLocation); return back(); @@ -353,14 +353,14 @@ AssemblyItem Assembly::namedTag(string const& _name) assertThrow(!_name.empty(), AssemblyException, "Empty named tag."); if (!m_namedTags.count(_name)) m_namedTags[_name] = size_t(newTag().data()); - return AssemblyItem(Tag, m_namedTags.at(_name)); + return AssemblyItem{Tag, m_namedTags.at(_name)}; } AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) { h256 h(dev::keccak256(_identifier)); m_libraries[h] = _identifier; - return AssemblyItem(PushLibraryAddress, h); + return AssemblyItem{PushLibraryAddress, h}; } Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) @@ -415,14 +415,14 @@ map<u256, u256> Assembly::optimiseInternal( if (_settings.runJumpdestRemover) { - JumpdestRemover jumpdestOpt(m_items); + JumpdestRemover jumpdestOpt{m_items}; if (jumpdestOpt.optimise(_tagsReferencedFromOutside)) count++; } if (_settings.runPeephole) { - PeepholeOptimiser peepOpt(m_items); + PeepholeOptimiser peepOpt{m_items}; while (peepOpt.optimise()) { count++; @@ -433,7 +433,7 @@ map<u256, u256> Assembly::optimiseInternal( // This only modifies PushTags, we have to run again to actually remove code. if (_settings.runDeduplicate) { - BlockDeduplicator dedup(m_items); + BlockDeduplicator dedup{m_items}; if (dedup.deduplicate()) { tagReplacements.insert(dedup.replacedTags().begin(), dedup.replacedTags().end()); @@ -448,13 +448,13 @@ map<u256, u256> Assembly::optimiseInternal( // function types that can be stored in storage. AssemblyItems optimisedItems; - bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem(Instruction::MSIZE)) != m_items.end()); + bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem{Instruction::MSIZE}) != m_items.end()); auto iter = m_items.begin(); while (iter != m_items.end()) { KnownState emptyState; - CommonSubexpressionEliminator eliminator(emptyState); + CommonSubexpressionEliminator eliminator{emptyState}; auto orig = iter; iter = eliminator.feedItems(iter, m_items.end(), usesMSize); bool shouldReplace = false; diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp index b7c69531..ca439925 100644 --- a/libevmasm/BlockDeduplicator.cpp +++ b/libevmasm/BlockDeduplicator.cpp @@ -41,7 +41,7 @@ bool BlockDeduplicator::deduplicate() // Virtual tag that signifies "the current block" and which is used to optimise loops. // We abort if this virtual tag actually exists. - AssemblyItem pushSelf(PushTag, u256(-4)); + AssemblyItem pushSelf{PushTag, u256(-4)}; if ( std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) || std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag()) @@ -55,17 +55,17 @@ bool BlockDeduplicator::deduplicate() // To compare recursive loops, we have to already unify PushTag opcodes of the // block's own tag. - AssemblyItem pushFirstTag(pushSelf); - AssemblyItem pushSecondTag(pushSelf); + AssemblyItem pushFirstTag{pushSelf}; + AssemblyItem pushSecondTag{pushSelf}; if (_i < m_items.size() && m_items.at(_i).type() == Tag) pushFirstTag = m_items.at(_i).pushTag(); if (_j < m_items.size() && m_items.at(_j).type() == Tag) pushSecondTag = m_items.at(_j).pushTag(); - BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf); - BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf); - BlockIterator end(m_items.end(), m_items.end()); + BlockIterator first{m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf}; + BlockIterator second{m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf}; + BlockIterator end{m_items.end(), m_items.end()}; if (first != end && (*first).type() == Tag) ++first; @@ -126,7 +126,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++() { if (it == end) return *this; - if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(Instruction::JUMPI)) + if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI}) it = end; else { diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index d62f5436..e82c2903 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -87,7 +87,7 @@ void ControlFlowGraph::splitBlocks() m_blocks[id].begin = index; } if (item.type() == PushTag) - m_blocks[id].pushedTags.push_back(BlockId(item.data())); + m_blocks[id].pushedTags.emplace_back(item.data()); if (SemanticInformation::altersControlFlow(item)) { m_blocks[id].end = index + 1; diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index 1dce5f1e..b812cecc 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -209,7 +209,7 @@ ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation c item = _pattern.toAssemblyItem(_location); } for (auto const& arg: _pattern.arguments()) - arguments.push_back(ExpressionTemplate(arg, _location)); + arguments.emplace_back(arg, _location); } string ExpressionTemplate::toString() const diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h index 5ad31ab2..22deb058 100644 --- a/liblangutil/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -105,7 +105,7 @@ class SecondarySourceLocation public: SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation) { - infos.push_back(std::make_pair(_errMsg, _sourceLocation)); + infos.emplace_back(_errMsg, _sourceLocation); return *this; } diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index b32f14e9..63d8da3d 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -353,7 +353,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (j.tag() || j.which() != sp::utree_type::symbol_type) error<InvalidMacroArgs>(); auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>(); - args.push_back(string(sr.begin(), sr.end())); + args.emplace_back(sr.begin(), sr.end()); } else if (ii == 3) { @@ -464,9 +464,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) if (c++) { if (us == "LLL" && c == 1) - code.push_back(CodeFragment(i, ns, m_readFile)); + code.emplace_back(i, ns, m_readFile); else - code.push_back(CodeFragment(i, _s, m_readFile)); + code.emplace_back(i, _s, m_readFile); } auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); }; auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); }; diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index f944adbd..6296cbcf 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -46,22 +46,22 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer { if (_errors) { - _errors->push_back("Parse error."); - _errors->push_back(boost::diagnostic_information(_e)); + _errors->emplace_back("Parse error."); + _errors->emplace_back(boost::diagnostic_information(_e)); } } catch (std::exception const& _e) { if (_errors) { - _errors->push_back("Parse exception."); - _errors->push_back(boost::diagnostic_information(_e)); + _errors->emplace_back("Parse exception."); + _errors->emplace_back(boost::diagnostic_information(_e)); } } catch (...) { if (_errors) - _errors->push_back("Internal compiler exception."); + _errors->emplace_back("Internal compiler exception."); } return bytes(); } @@ -84,22 +84,22 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe { if (_errors) { - _errors->push_back("Parse error."); - _errors->push_back(boost::diagnostic_information(_e)); + _errors->emplace_back("Parse error."); + _errors->emplace_back(boost::diagnostic_information(_e)); } } catch (std::exception const& _e) { if (_errors) { - _errors->push_back("Parse exception."); - _errors->push_back(boost::diagnostic_information(_e)); + _errors->emplace_back("Parse exception."); + _errors->emplace_back(boost::diagnostic_information(_e)); } } catch (...) { if (_errors) - _errors->push_back("Internal compiler exception."); + _errors->emplace_back("Internal compiler exception."); } return string(); } diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 6dc564de..526caff9 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -227,7 +227,7 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con return _type->hasEqualParameterTypes(*_funAndFlag.first); }); if (it == overloads.end()) - overloads.push_back(make_pair(_type, _implemented)); + overloads.emplace_back(_type, _implemented); else if (it->second) { if (!_implemented) @@ -409,8 +409,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c auto functionType = make_shared<FunctionType>(*f); // under non error circumstances this should be true if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(f, functionType->asCallableFunction(false)) + externalDeclarations[functionType->externalSignature()].emplace_back( + f, functionType->asCallableFunction(false) ); } for (VariableDeclaration const* v: contract->stateVariables()) @@ -419,8 +419,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c auto functionType = make_shared<FunctionType>(*v); // under non error circumstances this should be true if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(v, functionType->asCallableFunction(false)) + externalDeclarations[functionType->externalSignature()].emplace_back( + v, functionType->asCallableFunction(false) ); } } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1c9f1956..d41415c0 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -935,30 +935,32 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) var.accept(*this); if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) { + auto errorMsg = "Type " + + valueComponentType->toString() + + " is not implicitly convertible to expected type " + + var.annotation().type->toString(); if ( valueComponentType->category() == Type::Category::RationalNumber && dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() && valueComponentType->mobileType() ) - m_errorReporter.typeError( - _statement.location(), - "Type " + - valueComponentType->toString() + - " is not implicitly convertible to expected type " + - var.annotation().type->toString() + - ". Try converting to type " + - valueComponentType->mobileType()->toString() + - " or use an explicit conversion." - ); + { + if (var.annotation().type->operator==(*valueComponentType->mobileType())) + m_errorReporter.typeError( + _statement.location(), + errorMsg + ", but it can be explicitly converted." + ); + else + m_errorReporter.typeError( + _statement.location(), + errorMsg + + ". Try converting to type " + + valueComponentType->mobileType()->toString() + + " or use an explicit conversion." + ); + } else - m_errorReporter.typeError( - _statement.location(), - "Type " + - valueComponentType->toString() + - " is not implicitly convertible to expected type " + - var.annotation().type->toString() + - "." - ); + m_errorReporter.typeError(_statement.location(), errorMsg + "."); } } } @@ -2331,30 +2333,32 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte _expression.accept(*this); if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) { + auto errorMsg = "Type " + + type(_expression)->toString() + + " is not implicitly convertible to expected type " + + _expectedType.toString(); if ( type(_expression)->category() == Type::Category::RationalNumber && dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() && type(_expression)->mobileType() ) - m_errorReporter.typeError( - _expression.location(), - "Type " + - type(_expression)->toString() + - " is not implicitly convertible to expected type " + - _expectedType.toString() + - ". Try converting to type " + - type(_expression)->mobileType()->toString() + - " or use an explicit conversion." - ); + { + if (_expectedType.operator==(*type(_expression)->mobileType())) + m_errorReporter.typeError( + _expression.location(), + errorMsg + ", but it can be explicitly converted." + ); + else + m_errorReporter.typeError( + _expression.location(), + errorMsg + + ". Try converting to type " + + type(_expression)->mobileType()->toString() + + " or use an explicit conversion." + ); + } else - m_errorReporter.typeError( - _expression.location(), - "Type " + - type(_expression)->toString() + - " is not implicitly convertible to expected type " + - _expectedType.toString() + - "." - ); + m_errorReporter.typeError(_expression.location(), errorMsg + "."); return false; } return true; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 3ae6bd6d..5779e4ad 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -198,7 +198,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter { signaturesSeen.insert(functionSignature); FixedHash<4> hash(dev::keccak256(functionSignature)); - m_interfaceFunctionList->push_back(make_pair(hash, fun)); + m_interfaceFunctionList->emplace_back(hash, fun); } } } diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index e92134a8..1a2b3345 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -234,7 +234,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)), make_pair("scope", idOrNull(_node.scope())) }; - attributes.push_back(make_pair("unitAlias", _node.name())); + attributes.emplace_back("unitAlias", _node.name()); Json::Value symbolAliases(Json::arrayValue); for (auto const& symbolAlias: _node.symbolAliases()) { @@ -244,7 +244,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue; symbolAliases.append(tuple); } - attributes.push_back(make_pair("symbolAliases", std::move(symbolAliases))); + attributes.emplace_back("symbolAliases", std::move(symbolAliases)); setJsonNode(_node, "ImportDirective", std::move(attributes)); return false; } @@ -357,7 +357,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node) make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }; if (m_inEvent) - attributes.push_back(make_pair("indexed", _node.isIndexed())); + attributes.emplace_back("indexed", _node.isIndexed()); setJsonNode(_node, "VariableDeclaration", std::move(attributes)); return false; } @@ -647,11 +647,11 @@ bool ASTJsonConverter::visit(FunctionCall const& _node) }; if (m_legacy) { - attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall)); - attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion)); + attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall); + attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion); } else - attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind))); + attributes.emplace_back("kind", functionCallKind(_node.annotation().kind)); appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "FunctionCall", std::move(attributes)); return false; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f5a38747..c35dde3c 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -125,6 +125,22 @@ bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); } +/// Checks whether _value fits into IntegerType _type. +bool fitsIntegerType(bigint const& _value, IntegerType const& _type) +{ + return (_type.minValue() <= _value) && (_value <= _type.maxValue()); +} + +/// Checks whether _value fits into _bits bits when having 1 bit as the sign bit +/// if _signed is true. +bool fitsIntoBits(bigint const& _value, unsigned _bits, bool _signed) +{ + return fitsIntegerType(_value, IntegerType( + _bits, + _signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned + )); +} + } void StorageOffsets::computeOffsets(TypePointers const& _types) @@ -446,7 +462,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition continue; FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true); if (_type.isImplicitlyConvertibleTo(*fun->selfType())) - members.push_back(MemberList::Member(function->name(), fun, function)); + members.emplace_back(function->name(), fun, function); } } return members; @@ -963,27 +979,21 @@ BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) if (isFractional()) return false; IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo); - if (m_value == rational(0)) - return true; - unsigned forSignBit = (targetType.isSigned() ? 1 : 0); - if (m_value > rational(0)) - { - if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit))) - return true; - return false; - } - if (targetType.isSigned()) - { - if (-m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit))) - return true; - } - return false; + return fitsIntegerType(m_value.numerator(), targetType); } case Category::FixedPoint: { - if (auto fixed = fixedPointType()) - return fixed->isImplicitlyConvertibleTo(_convertTo); - return false; + FixedPointType const& targetType = dynamic_cast<FixedPointType const&>(_convertTo); + // Store a negative number into an unsigned. + if (isNegative() && !targetType.isSigned()) + return false; + if (!isFractional()) + return (targetType.minIntegerValue() <= m_value) && (m_value <= targetType.maxIntegerValue()); + rational value = m_value * pow(bigint(10), targetType.fractionalDigits()); + // Need explicit conversion since truncation will occur. + if (value.denominator() != 1) + return false; + return fitsIntoBits(value.numerator(), targetType.numBits(), targetType.isSigned()); } case Category::FixedBytes: return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo); @@ -1811,23 +1821,23 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const MemberList::MemberMap members; if (!isString()) { - members.push_back({"length", make_shared<IntegerType>(256)}); + members.emplace_back("length", make_shared<IntegerType>(256)); if (isDynamicallySized() && location() == DataLocation::Storage) { - members.push_back({"push", make_shared<FunctionType>( + members.emplace_back("push", make_shared<FunctionType>( TypePointers{baseType()}, TypePointers{make_shared<IntegerType>(256)}, strings{string()}, strings{string()}, isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush - )}); - members.push_back({"pop", make_shared<FunctionType>( + )); + members.emplace_back("pop", make_shared<FunctionType>( TypePointers{}, TypePointers{}, strings{string()}, strings{string()}, FunctionType::Kind::ArrayPop - )}); + )); } } return members; @@ -1956,21 +1966,17 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con break; } if (!functionWithEqualArgumentsFound) - members.push_back(MemberList::Member( - function->name(), - functionType, - function - )); + members.emplace_back(function->name(), functionType, function); } } else if (!m_contract.isLibrary()) { for (auto const& it: m_contract.interfaceFunctions()) - members.push_back(MemberList::Member( + members.emplace_back( it.second->declaration().name(), it.second->asCallableFunction(m_contract.isLibrary()), &it.second->declaration() - )); + ); } return members; } @@ -1998,7 +2004,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets; for (size_t index = 0; index < variables.size(); ++index) if (auto const* offset = offsets.offset(index)) - variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second)); + variablesAndOffsets.emplace_back(variables[index], offset->first, offset->second); return variablesAndOffsets; } @@ -2088,10 +2094,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const // Skip all mapping members if we are not in storage. if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) continue; - members.push_back(MemberList::Member( + members.emplace_back( variable->name(), copyForLocationIfReference(type), - variable.get()) + variable.get() ); } return members; @@ -2428,7 +2434,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) { m_parameterTypes.push_back(mappingType->keyType()); - m_parameterNames.push_back(""); + m_parameterNames.emplace_back(""); returnType = mappingType->valueType(); } else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get())) @@ -2437,7 +2443,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): // Return byte arrays as whole. break; returnType = arrayType->baseType(); - m_parameterNames.push_back(""); + m_parameterNames.emplace_back(""); m_parameterTypes.push_back(make_shared<IntegerType>(256)); } else @@ -2468,7 +2474,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): DataLocation::Memory, returnType )); - m_returnParameterNames.push_back(""); + m_returnParameterNames.emplace_back(""); } } @@ -2837,14 +2843,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con { MemberList::MemberMap members; if (m_kind == Kind::External) - members.push_back(MemberList::Member( - "selector", - make_shared<FixedBytesType>(4) - )); + members.emplace_back("selector", make_shared<FixedBytesType>(4)); if (m_kind != Kind::BareDelegateCall) { if (isPayable()) - members.push_back(MemberList::Member( + members.emplace_back( "value", make_shared<FunctionType>( parseElementaryTypeVector({"uint"}), @@ -2858,10 +2861,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con m_gasSet, m_valueSet ) - )); + ); } if (m_kind != Kind::Creation) - members.push_back(MemberList::Member( + members.emplace_back( "gas", make_shared<FunctionType>( parseElementaryTypeVector({"uint"}), @@ -2875,7 +2878,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con m_gasSet, m_valueSet ) - )); + ); return members; } default: @@ -3203,24 +3206,24 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current if (contract.isLibrary()) for (FunctionDefinition const* function: contract.definedFunctions()) if (function->isVisibleAsLibraryMember()) - members.push_back(MemberList::Member( + members.emplace_back( function->name(), FunctionType(*function).asCallableFunction(true), function - )); + ); if (isBase) { // We are accessing the type of a base contract, so add all public and protected // members. Note that this does not add inherited functions on purpose. for (Declaration const* decl: contract.inheritableMembers()) - members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); + members.emplace_back(decl->name(), decl->type(), decl); } else { for (auto const& stru: contract.definedStructs()) - members.push_back(MemberList::Member(stru->name(), stru->type(), stru)); + members.emplace_back(stru->name(), stru->type(), stru); for (auto const& enu: contract.definedEnums()) - members.push_back(MemberList::Member(enu->name(), enu->type(), enu)); + members.emplace_back(enu->name(), enu->type(), enu); } } else if (m_actualType->category() == Category::Enum) @@ -3228,7 +3231,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition(); auto enumType = make_shared<EnumType>(enumDef); for (ASTPointer<EnumValue> const& enumValue: enumDef.members()) - members.push_back(MemberList::Member(enumValue->name(), enumType)); + members.emplace_back(enumValue->name(), enumType); } return members; } @@ -3293,7 +3296,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const MemberList::MemberMap symbols; for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols) for (Declaration const* symbol: symbolName.second) - symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol)); + symbols.emplace_back(symbolName.first, symbol->type(), symbol); return symbols; } diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index b02623de..5000131c 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -141,8 +141,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) vector<string> valueNamesLocal; for (size_t j = 0; j < sizeOnStack; j++) { - valueNamesLocal.push_back("value" + to_string(stackPos)); - valueReturnParams.push_back("value" + to_string(stackPos)); + valueNamesLocal.emplace_back("value" + to_string(stackPos)); + valueReturnParams.emplace_back("value" + to_string(stackPos)); stackPos++; } bool dynamic = decodingTypes[i]->isDynamicallyEncoded(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 79c53a1c..67876721 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -721,14 +721,14 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement) eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag(); - m_breakTags.push_back({loopEnd, m_context.stackHeight()}); + m_breakTags.emplace_back(loopEnd, m_context.stackHeight()); m_context << loopStart; if (_whileStatement.isDoWhile()) { eth::AssemblyItem condition = m_context.newTag(); - m_continueTags.push_back({condition, m_context.stackHeight()}); + m_continueTags.emplace_back(condition, m_context.stackHeight()); _whileStatement.body().accept(*this); @@ -739,7 +739,7 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement) } else { - m_continueTags.push_back({loopStart, m_context.stackHeight()}); + m_continueTags.emplace_back(loopStart, m_context.stackHeight()); compileExpression(_whileStatement.condition()); m_context << Instruction::ISZERO; m_context.appendConditionalJumpTo(loopEnd); @@ -770,8 +770,8 @@ bool ContractCompiler::visit(ForStatement const& _forStatement) if (_forStatement.initializationExpression()) _forStatement.initializationExpression()->accept(*this); - m_breakTags.push_back({loopEnd, m_context.stackHeight()}); - m_continueTags.push_back({loopNext, m_context.stackHeight()}); + m_breakTags.emplace_back(loopEnd, m_context.stackHeight()); + m_continueTags.emplace_back(loopNext, m_context.stackHeight()); m_context << loopStart; // if there is no terminating condition in for, default is to always be true @@ -997,7 +997,7 @@ void ContractCompiler::appendModifierOrFunctionCode() if (codeBlock) { - m_returnTags.push_back({m_context.newTag(), m_context.stackHeight()}); + m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight()); codeBlock->accept(*this); solAssert(!m_returnTags.empty(), ""); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6cab7be3..0b4eca7d 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -170,7 +170,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective() expectToken(Token::As); alias = expectIdentifierToken(); } - symbolAliases.push_back(make_pair(move(id), move(alias))); + symbolAliases.emplace_back(move(id), move(alias)); if (m_scanner->currentToken() != Token::Comma) break; m_scanner->next(); @@ -1690,7 +1690,7 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath() index = parseExpression(); SourceLocation indexLocation = iap.path.front()->location(); indexLocation.end = endPosition(); - iap.indices.push_back(make_pair(index, indexLocation)); + iap.indices.emplace_back(index, indexLocation); expectToken(Token::RBrack); } diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h new file mode 100644 index 00000000..be076059 --- /dev/null +++ b/test/InteractiveTests.h @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <test/TestCase.h> +#include <test/libsolidity/ASTJSONTest.h> +#include <test/libsolidity/SyntaxTest.h> +#include <test/libsolidity/SMTCheckerJSONTest.h> +#include <test/libyul/YulOptimizerTest.h> +#include <test/libyul/ObjectCompilerTest.h> + +#include <boost/filesystem.hpp> + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +/** Container for all information regarding a testsuite */ +struct Testsuite +{ + char const* title; + boost::filesystem::path const path; + boost::filesystem::path const subpath; + bool smt; + bool ipc; + TestCase::TestCaseCreator testCaseCreator; +}; + + +/// Array of testsuits that can be run interactively as well as automatically +Testsuite const g_interactiveTestsuites[] = { +/* + Title Path Subpath SMT IPC Creator function */ + {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, + {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, + {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, + {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, + {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SyntaxTest::create}, + {"SMT Checker JSON", "libsolidity", "smtCheckerTestsJSON", true, false, &SMTCheckerTest::create} +}; + +} +} +} + diff --git a/test/boostTest.cpp b/test/boostTest.cpp index cff0f5c0..d6e75cb9 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -35,16 +35,13 @@ #pragma GCC diagnostic pop +#include <test/InteractiveTests.h> #include <test/Options.h> -#include <test/libsolidity/ASTJSONTest.h> -#include <test/libsolidity/SyntaxTest.h> -#include <test/libsolidity/SMTCheckerJSONTest.h> -#include <test/libyul/YulOptimizerTest.h> -#include <test/libyul/ObjectCompilerTest.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem.hpp> +#include <string> using namespace boost::unit_test; using namespace dev::solidity::test; @@ -129,46 +126,26 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; dev::test::Options::get().validate(); - solAssert(registerTests( - master, - dev::test::Options::get().testPath / "libsolidity", - "syntaxTests", - SyntaxTest::create - ) > 0, "no syntax tests found"); - solAssert(registerTests( - master, - dev::test::Options::get().testPath / "libsolidity", - "ASTJSON", - ASTJSONTest::create - ) > 0, "no JSON AST tests found"); - solAssert(registerTests( - master, - dev::test::Options::get().testPath / "libyul", - "yulOptimizerTests", - yul::test::YulOptimizerTest::create - ) > 0, "no Yul Optimizer tests found"); - solAssert(registerTests( - master, - dev::test::Options::get().testPath / "libyul", - "objectCompiler", - yul::test::ObjectCompilerTest::create - ) > 0, "no Yul Object compiler tests found"); - if (!dev::test::Options::get().disableSMT) + + // Include the interactive tests in the automatic tests as well + for (auto const& ts: g_interactiveTestsuites) { - solAssert(registerTests( - master, - dev::test::Options::get().testPath / "libsolidity", - "smtCheckerTests", - SyntaxTest::create - ) > 0, "no SMT checker tests found"); + auto const& options = dev::test::Options::get(); + + if (ts.smt && options.disableSMT) + continue; + + if (ts.ipc && options.disableIPC) + continue; solAssert(registerTests( master, - dev::test::Options::get().testPath / "libsolidity", - "smtCheckerTestsJSON", - SMTCheckerTest::create - ) > 0, "no SMT checker JSON tests found"); + options.testPath / ts.path, + ts.subpath, + ts.testCaseCreator + ) > 0, std::string("no ") + ts.title + " tests found"); } + if (dev::test::Options::get().disableIPC) { for (auto suite: { @@ -189,6 +166,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) }) removeTestSuite(suite); } + if (dev::test::Options::get().disableSMT) removeTestSuite("SMTChecker"); diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol index 441cf81e..295cf4ea 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol @@ -10,4 +10,5 @@ contract test { } } // ---- -// Warning: (20-707): Function state mutability can be restricted to pure +// TypeError: (153-250): Type rational_const 9208...(70 digits omitted)...7637 / 1000...(71 digits omitted)...0000 is not implicitly convertible to expected type ufixed256x77, but it can be explicitly converted. +// TypeError: (470-566): Type rational_const -933...(70 digits omitted)...0123 / 1000...(70 digits omitted)...0000 is not implicitly convertible to expected type fixed256x76, but it can be explicitly converted. diff --git a/test/libsolidity/syntaxTests/types/rational_number_literal_to_fixed_implicit.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_to_fixed_implicit.sol new file mode 100644 index 00000000..ca50e03b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_literal_to_fixed_implicit.sol @@ -0,0 +1,15 @@ +contract C { + function literalToUFixed() public pure { + ufixed8x2 a = 0.10; + ufixed8x2 b = 0.00; + ufixed8x2 c = 2.55; + a; b; c; + } + function literalToFixed() public pure { + fixed8x1 a = 0.1; + fixed8x1 b = 12.7; + fixed8x1 c = -12.8; + a; b; c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol b/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol index 66bd9a8e..f86ffcdd 100644 --- a/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol +++ b/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol @@ -2,3 +2,4 @@ contract C { fixed8x80 a = -1e-100; } // ---- +// TypeError: (29-36): Type rational_const -1 / 1000...(93 digits omitted)...0000 is not implicitly convertible to expected type fixed8x80, but it can be explicitly converted. diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 13585887..e5578045 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -19,11 +19,7 @@ #include <test/Common.h> #include <test/libsolidity/AnalysisFramework.h> -#include <test/libsolidity/SyntaxTest.h> -#include <test/libsolidity/ASTJSONTest.h> -#include <test/libsolidity/SMTCheckerJSONTest.h> -#include <test/libyul/YulOptimizerTest.h> -#include <test/libyul/ObjectCompilerTest.h> +#include <test/InteractiveTests.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> @@ -380,59 +376,13 @@ Allowed options)", TestStats global_stats{0, 0}; // Actually run the tests. - // If you add new tests here, you also have to add them in boostTest.cpp - if (auto stats = runTestSuite("Syntax", testPath / "libsolidity", "syntaxTests", SyntaxTest::create, formatted)) - global_stats += *stats; - else - return 1; - - if (auto stats = runTestSuite("JSON AST", testPath / "libsolidity", "ASTJSON", ASTJSONTest::create, formatted)) - global_stats += *stats; - else - return 1; - - if (auto stats = runTestSuite( - "Yul Optimizer", - testPath / "libyul", - "yulOptimizerTests", - yul::test::YulOptimizerTest::create, - formatted - )) - global_stats += *stats; - else - return 1; - - if (auto stats = runTestSuite( - "Yul Object Compiler", - testPath / "libyul", - "objectCompiler", - yul::test::ObjectCompilerTest::create, - formatted - )) - global_stats += *stats; - else - return 1; - - if (!disableSMT) + // Interactive tests are added in InteractiveTests.h + for (auto const& ts: g_interactiveTestsuites) { - if (auto stats = runTestSuite( - "SMT Checker", - testPath / "libsolidity", - "smtCheckerTests", - SyntaxTest::create, - formatted - )) - global_stats += *stats; - else - return 1; + if (ts.smt && disableSMT) + continue; - if (auto stats = runTestSuite( - "SMT Checker JSON", - testPath / "libsolidity", - "smtCheckerTestsJSON", - SMTCheckerTest::create, - formatted - )) + if (auto stats = runTestSuite(ts.title, testPath / ts.path, ts.subpath, ts.testCaseCreator, formatted)) global_stats += *stats; else return 1; |