From bc3261936471b0cc4f7b2c9ae1be3179d52ca71d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 26 Feb 2020 15:50:34 +0100 Subject: [PATCH 01/37] Enable optimized IR output via the commandline. --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 66 ++++++++++++++++++++++------------- solc/CommandLineInterface.h | 1 + 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Changelog.md b/Changelog.md index f151365ae..9f99cea33 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: * AssemblyStack: Support for source locations (source mappings) and thus debugging Yul sources. + * Commandline Interface: Enable output of experimental optimized IR via ``--ir-optimized``. Bugfixes: diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index ccb192ba1..e91f6446c 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -127,6 +127,7 @@ static string const g_strInterface = "interface"; static string const g_strYul = "yul"; static string const g_strYulDialect = "yul-dialect"; static string const g_strIR = "ir"; +static string const g_strIROptimized = "ir-optimized"; static string const g_strIPFS = "ipfs"; static string const g_strLicense = "license"; static string const g_strLibraries = "libraries"; @@ -190,6 +191,7 @@ static string const g_argImportAst = g_strImportAst; static string const g_argInputFile = g_strInputFile; static string const g_argYul = g_strYul; static string const g_argIR = g_strIR; +static string const g_argIROptimized = g_strIROptimized; static string const g_argEwasm = g_strEwasm; static string const g_argLibraries = g_strLibraries; static string const g_argLink = g_strLink; @@ -336,36 +338,50 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleIR(string const& _contractName) { - if (m_args.count(g_argIR)) + if (!m_args.count(g_argIR)) + return; + + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); + else { - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); - else - { - sout() << "IR:" << endl; - sout() << m_compiler->yulIR(_contractName) << endl; - } + sout() << "IR:" << endl; + sout() << m_compiler->yulIR(_contractName) << endl; + } +} + +void CommandLineInterface::handleIROptimized(string const& _contractName) +{ + if (!m_args.count(g_argIROptimized)) + return; + + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contractName) + "_opt.yul", m_compiler->yulIROptimized(_contractName)); + else + { + sout() << "Optimized IR:" << endl; + sout() << m_compiler->yulIROptimized(_contractName) << endl; } } void CommandLineInterface::handleEwasm(string const& _contractName) { - if (m_args.count(g_argEwasm)) + if (!m_args.count(g_argEwasm)) + return; + + if (m_args.count(g_argOutputDir)) { - if (m_args.count(g_argOutputDir)) - { - createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName)); - createFile( - m_compiler->filesystemFriendlyName(_contractName) + ".wasm", - asString(m_compiler->ewasmObject(_contractName).bytecode) - ); - } - else - { - sout() << "Ewasm text:" << endl; - sout() << m_compiler->ewasm(_contractName) << endl; - sout() << "Ewasm binary (hex): " << m_compiler->ewasmObject(_contractName).toHex() << endl; - } + createFile(m_compiler->filesystemFriendlyName(_contractName) + ".wast", m_compiler->ewasm(_contractName)); + createFile( + m_compiler->filesystemFriendlyName(_contractName) + ".wasm", + asString(m_compiler->ewasmObject(_contractName).bytecode) + ); + } + else + { + sout() << "Ewasm text:" << endl; + sout() << m_compiler->ewasm(_contractName) << endl; + sout() << "Ewasm binary (hex): " << m_compiler->ewasmObject(_contractName).toHex() << endl; } } @@ -812,6 +828,7 @@ Allowed options)", (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") (g_argAbi.c_str(), "ABI specification of the contracts.") (g_argIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (g_argIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") (g_argEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") @@ -1123,7 +1140,7 @@ bool CommandLineInterface::processInput() m_compiler->setRevertStringBehaviour(m_revertStrings); // TODO: Perhaps we should not compile unless requested - m_compiler->enableIRGeneration(m_args.count(g_argIR)); + m_compiler->enableIRGeneration(m_args.count(g_argIR) || m_args.count(g_argIROptimized)); m_compiler->enableEwasmGeneration(m_args.count(g_argEwasm)); OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::standard() : OptimiserSettings::minimal(); @@ -1631,6 +1648,7 @@ void CommandLineInterface::outputCompilationResults() handleBytecode(contract); handleIR(contract); + handleIROptimized(contract); handleEwasm(contract); handleSignatureHashes(contract); handleMetadata(contract); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 553f9e69b..417501907 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -65,6 +65,7 @@ private: void handleBinary(std::string const& _contract); void handleOpcode(std::string const& _contract); void handleIR(std::string const& _contract); + void handleIROptimized(std::string const& _contract); void handleEwasm(std::string const& _contract); void handleBytecode(std::string const& _contract); void handleSignatureHashes(std::string const& _contract); From ed02aae1d9cad7514df6131ed326dc33f87923f2 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Fri, 28 Feb 2020 15:41:22 +0100 Subject: [PATCH 02/37] Update solidity fuzzing dictionary with >0.6.0 keywords --- test/tools/ossfuzz/config/solidity.dict | 13 ++++++++++++- test/tools/ossfuzz/config/strict_assembly.dict | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/tools/ossfuzz/config/solidity.dict b/test/tools/ossfuzz/config/solidity.dict index 9fe773870..8190e7c55 100644 --- a/test/tools/ossfuzz/config/solidity.dict +++ b/test/tools/ossfuzz/config/solidity.dict @@ -119,7 +119,7 @@ "bytes8 " "bytes9 " "constant " -"constructor " +"constructor() " "continue;" "contract " "delete " @@ -212,3 +212,14 @@ "|" "}" "~" +"override" +"virtual" +" is " +"receive() " +"fallback() " +"catch Error() {}" +"catch (bytes memory ) {}" +"{value: 1, gas: 2}" +"{salt: "salt", value: 10}" +"leave" +"a[1:2]" diff --git a/test/tools/ossfuzz/config/strict_assembly.dict b/test/tools/ossfuzz/config/strict_assembly.dict index 4415c87f4..7f4fe4b5b 100644 --- a/test/tools/ossfuzz/config/strict_assembly.dict +++ b/test/tools/ossfuzz/config/strict_assembly.dict @@ -89,3 +89,4 @@ "xor(" "{" "}" +"leave" From f10c6500b2378e652c5e7e91f255501d9acec4ba Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Feb 2020 18:25:19 +0100 Subject: [PATCH 03/37] Immutable is not reserved anymore. --- liblangutil/Token.h | 2 +- test/libsolidity/SolidityParser.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/liblangutil/Token.h b/liblangutil/Token.h index ed6cff3e1..d9d4471cb 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -166,6 +166,7 @@ namespace solidity::langutil K(Indexed, "indexed", 0) \ K(Interface, "interface", 0) \ K(Internal, "internal", 0) \ + K(Immutable, "immutable", 0) \ K(Import, "import", 0) \ K(Is, "is", 0) \ K(Library, "library", 0) \ @@ -243,7 +244,6 @@ namespace solidity::langutil K(Default, "default", 0) \ K(Define, "define", 0) \ K(Final, "final", 0) \ - K(Immutable, "immutable", 0) \ K(Implements, "implements", 0) \ K(In, "in", 0) \ K(Inline, "inline", 0) \ diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 3f70f9160..192a26898 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -540,7 +540,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved) "default", "define", "final", - "immutable", "implements", "in", "inline", From 1488a1ceb807633beef3349ad785ee98be45742d Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Feb 2020 18:42:16 +0100 Subject: [PATCH 04/37] Refactor isConstant to add "immutable". --- libsolidity/ast/AST.cpp | 2 +- libsolidity/ast/AST.h | 10 ++++++---- libsolidity/ast/ASTJsonImporter.cpp | 8 +++++++- libsolidity/parsing/Parser.cpp | 6 +++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 90084f9c4..6b03c7b58 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -390,7 +390,7 @@ DeclarationAnnotation& Declaration::annotation() const bool VariableDeclaration::isLValue() const { // Constant declared variables are Read-Only - if (m_isConstant) + if (isConstant()) return false; // External function arguments of reference type are Read-Only if (isExternalCallableParameter() && dynamic_cast(type())) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 336c221c6..adb957cfe 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -814,6 +814,7 @@ class VariableDeclaration: public Declaration { public: enum Location { Unspecified, Storage, Memory, CallData }; + enum class Constantness { Mutable, Immutable, Constant }; VariableDeclaration( int64_t _id, @@ -824,7 +825,7 @@ public: Visibility _visibility, bool _isStateVar = false, bool _isIndexed = false, - bool _isConstant = false, + Constantness _constantness = Constantness::Mutable, ASTPointer const& _overrides = nullptr, Location _referenceLocation = Location::Unspecified ): @@ -833,7 +834,7 @@ public: m_value(_value), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed), - m_isConstant(_isConstant), + m_constantness(_constantness), m_overrides(_overrides), m_location(_referenceLocation) {} @@ -877,7 +878,7 @@ public: bool hasReferenceOrMappingType() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } - bool isConstant() const { return m_isConstant; } + bool isConstant() const { return m_constantness == Constantness::Constant; } ASTPointer const& overrides() const { return m_overrides; } Location referenceLocation() const { return m_location; } /// @returns a set of allowed storage locations for the variable. @@ -904,7 +905,8 @@ private: ASTPointer m_value; bool m_isStateVariable = false; ///< Whether or not this is a contract state variable bool m_isIndexed = false; ///< Whether this is an indexed variable (used by events). - bool m_isConstant = false; ///< Whether the variable is a compile-time constant. + /// Whether the variable is "constant", "immutable" or non-marked (mutable). + Constantness m_constantness = Constantness::Mutable; ASTPointer m_overrides; ///< Contains the override specifier node Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. }; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index aa28e3f17..26a7c7b7e 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -411,6 +411,12 @@ ASTPointer ASTJsonImporter::createVariableDeclaration(Json: { astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); + VariableDeclaration::Constantness constantness{}; + if (memberAsBool(_node, "constant")) + constantness = VariableDeclaration::Constantness::Constant; + else + constantness = VariableDeclaration::Constantness::Mutable; + return createASTNode( _node, nullOrCast(member(_node, "typeName")), @@ -419,7 +425,7 @@ ASTPointer ASTJsonImporter::createVariableDeclaration(Json: visibility(_node), memberAsBool(_node, "stateVariable"), _node.isMember("indexed") ? memberAsBool(_node, "indexed") : false, - memberAsBool(_node, "constant"), + constantness, _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), location(_node) ); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 99aa6e2a1..edf43377f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -695,7 +695,7 @@ ASTPointer Parser::parseVariableDeclaration( ); bool isIndexed = false; - bool isDeclaredConst = false; + VariableDeclaration::Constantness constantness = VariableDeclaration::Constantness::Mutable; ASTPointer overrides = nullptr; Visibility visibility(Visibility::Default); VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified; @@ -731,7 +731,7 @@ ASTPointer Parser::parseVariableDeclaration( if (_options.allowIndexed && token == Token::Indexed) isIndexed = true; else if (token == Token::Constant) - isDeclaredConst = true; + constantness = VariableDeclaration::Constantness::Constant; else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) @@ -790,7 +790,7 @@ ASTPointer Parser::parseVariableDeclaration( visibility, _options.isStateVariable, isIndexed, - isDeclaredConst, + constantness, overrides, location ); From 90fa56c7191d4353d8cdbc778c0f71a4459f4a56 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 2 Mar 2020 16:32:30 +0100 Subject: [PATCH 05/37] Allow use of yul util functions in legacy code generation. --- libsolidity/codegen/ABIFunctions.cpp | 11 +--------- libsolidity/codegen/ABIFunctions.h | 7 ------- libsolidity/codegen/CompilerContext.cpp | 14 +++++++++++++ libsolidity/codegen/CompilerContext.h | 20 ++++++++++++++++++- libsolidity/codegen/ContractCompiler.cpp | 8 ++++---- .../codegen/MultiUseYulFunctionCollector.cpp | 12 +++++++++-- .../codegen/MultiUseYulFunctionCollector.h | 19 ++++++++++++++++-- libsolidity/codegen/ir/IRGenerator.cpp | 6 +++--- 8 files changed, 68 insertions(+), 29 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 0ad044a54..b69f47346 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -240,13 +240,6 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) }); } -pair> ABIFunctions::requestedFunctions() -{ - std::set empty; - swap(empty, m_externallyUsedFunctions); - return make_pair(m_functionCollector->requestedFunctions(), std::move(empty)); -} - string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const { string suffix; @@ -1504,9 +1497,7 @@ string ABIFunctions::createFunction(string const& _name, function con string ABIFunctions::createExternallyUsedFunction(string const& _name, function const& _creator) { - string name = createFunction(_name, _creator); - m_externallyUsedFunctions.insert(name); - return name; + return m_functionCollector->createExternallyUsedFunction(_name, _creator); } size_t ABIFunctions::headSize(TypePointers const& _targetTypes) diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index ce3efe6ea..74bfdf7fa 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -104,12 +104,6 @@ public: /// stack slot, it takes exactly that number of values. std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); - /// @returns concatenation of all generated functions and a set of the - /// externally used functions. - /// Clears the internal list, i.e. calling it again will result in an - /// empty return value. - std::pair> requestedFunctions(); - private: struct EncodingOptions { @@ -260,7 +254,6 @@ private: langutil::EVMVersion m_evmVersion; RevertStrings const m_revertStrings; std::shared_ptr m_functionCollector; - std::set m_externallyUsedFunctions; YulUtilFunctions m_utils; }; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 8c1099855..f65ccae52 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -94,6 +94,20 @@ void CompilerContext::callLowLevelFunction( *this << retTag.tag(); } +void CompilerContext::callYulUtilFunction( + string const& _name, + unsigned _inArgs, + unsigned _outArgs +) +{ + m_functionCollector->markAsExternallyUsed(_name); + auto retTag = pushNewTag(); + CompilerUtils(*this).moveIntoStack(_inArgs); + appendJumpTo(namedTag(_name)); + adjustStackOffset(int(_outArgs) - 1 - _inArgs); + *this << retTag.tag(); +} + evmasm::AssemblyItem CompilerContext::lowLevelFunctionTag( string const& _name, unsigned _inArgs, diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index c37afbfa5..d9393e23a 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -65,7 +65,8 @@ public: m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), m_runtimeContext(_runtimeContext), - m_abiFunctions(m_evmVersion, m_revertStrings) + m_abiFunctions(m_evmVersion, m_revertStrings, m_functionCollector), + m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_functionCollector) { if (m_runtimeContext) m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); @@ -131,6 +132,14 @@ public: unsigned _outArgs, std::function const& _generator ); + + /// Appends a call to a yul util function and registers the function as externally used. + void callYulUtilFunction( + std::string const& _name, + unsigned _inArgs, + unsigned _outArgs + ); + /// Returns the tag of the named low-level function and inserts the generator into the /// list of low-level-functions to be generated, unless it already exists. /// Note that the generator should not assume that objects are still alive when it is called, @@ -144,6 +153,11 @@ public: /// Generates the code for missing low-level functions, i.e. calls the generators passed above. void appendMissingLowLevelFunctions(); ABIFunctions& abiFunctions() { return m_abiFunctions; } + YulUtilFunctions& utilFunctions() { return m_yulUtilFunctions; } + std::pair> requestedYulFunctions() + { + return m_functionCollector->requestedFunctions(); + } ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -355,8 +369,12 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map m_lowLevelFunctions; + // Collector for yul functions. + std::shared_ptr m_functionCollector = std::make_shared(); /// Container for ABI functions to be generated. ABIFunctions m_abiFunctions; + /// Container for Yul Util functions to be generated. + YulUtilFunctions m_yulUtilFunctions; /// The queue of low-level functions to generate. std::queue>> m_lowLevelFunctionGenerationQueue; }; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a99aefb5f..47187b1c8 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1267,12 +1267,12 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); - auto abiFunctions = m_context.abiFunctions().requestedFunctions(); - if (!abiFunctions.first.empty()) + auto yulFunctions = m_context.requestedYulFunctions(); + if (!yulFunctions.first.empty()) m_context.appendInlineAssembly( - "{" + move(abiFunctions.first) + "}", + "{" + move(yulFunctions.first) + "}", {}, - abiFunctions.second, + yulFunctions.second, true, m_optimiserSettings ); diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp index 2be3d29ae..8af292eda 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp @@ -30,13 +30,15 @@ using namespace std; using namespace solidity; using namespace solidity::frontend; -string MultiUseYulFunctionCollector::requestedFunctions() +pair> MultiUseYulFunctionCollector::requestedFunctions() { string result; for (auto const& f: m_requestedFunctions) result += f.second; m_requestedFunctions.clear(); - return result; + std::set empty; + swap(empty, m_externallyUsedFunctions); + return make_pair(result, std::move(empty)); } string MultiUseYulFunctionCollector::createFunction(string const& _name, function const& _creator) @@ -50,3 +52,9 @@ string MultiUseYulFunctionCollector::createFunction(string const& _name, functio } return _name; } + +string MultiUseYulFunctionCollector::createExternallyUsedFunction(string const& _name, function const& _creator) +{ + m_externallyUsedFunctions.insert(_name); + return createFunction(_name, _creator); +} diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.h b/libsolidity/codegen/MultiUseYulFunctionCollector.h index d839a31be..a1c733274 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.h +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.h @@ -23,6 +23,7 @@ #include #include +#include #include namespace solidity::frontend @@ -40,14 +41,28 @@ public: /// cases. std::string createFunction(std::string const& _name, std::function const& _creator); - /// @returns concatenation of all generated functions. + /// Helper function that uses @a _creator to create a function and add it to + /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both + /// cases. + std::string createExternallyUsedFunction(std::string const& _name, std::function const& _creator); + + /// Manually mark a function as externally used. + void markAsExternallyUsed(std::string const& _name) + { + m_externallyUsedFunctions.insert(_name); + } + + /// @returns concatenation of all generated functions and a set of the + /// externally used functions. /// Clears the internal list, i.e. calling it again will result in an /// empty return value. - std::string requestedFunctions(); + std::pair> requestedFunctions(); private: /// Map from function name to code for a multi-use function. std::map m_requestedFunctions; + // Set of externally used functions. + std::set m_externallyUsedFunctions; }; } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 9c2d8807c..0f2ffaf35 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -107,7 +107,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* fun: contract->definedFunctions()) generateFunction(*fun); - t("functions", m_context.functionCollector()->requestedFunctions()); + t("functions", m_context.functionCollector()->requestedFunctions().first); resetContext(_contract); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); @@ -116,7 +116,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* fun: contract->definedFunctions()) generateFunction(*fun); - t("runtimeFunctions", m_context.functionCollector()->requestedFunctions()); + t("runtimeFunctions", m_context.functionCollector()->requestedFunctions().first); return t.render(); } @@ -383,7 +383,7 @@ string IRGenerator::memoryInit() void IRGenerator::resetContext(ContractDefinition const& _contract) { solAssert( - m_context.functionCollector()->requestedFunctions().empty(), + m_context.functionCollector()->requestedFunctions().first.empty(), "Reset context while it still had functions." ); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); From 96a230af507187aa42d849a95f45cb551a61cd80 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 2 Mar 2020 22:10:15 +0100 Subject: [PATCH 06/37] [SMTChecker] Fix ICEs with tuples --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 15 +++++++++++---- .../smtCheckerTests/types/function_in_tuple_1.sol | 10 ++++++++++ .../smtCheckerTests/types/function_in_tuple_2.sol | 10 ++++++++++ .../types/tuple_single_element_1.sol | 9 +++++++++ .../types/tuple_single_element_2.sol | 9 +++++++++ .../types/tuple_single_non_tuple_element.sol | 9 +++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol create mode 100644 test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol create mode 100644 test/libsolidity/smtCheckerTests/types/tuple_single_element_1.sol create mode 100644 test/libsolidity/smtCheckerTests/types/tuple_single_element_2.sol create mode 100644 test/libsolidity/smtCheckerTests/types/tuple_single_non_tuple_element.sol diff --git a/Changelog.md b/Changelog.md index f151365ae..fc5d3d0d3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Compiler Features: Bugfixes: * isoltest: Added new keyword `wei` to express function value in semantic tests * Standard-JSON-Interface: Fix a bug related to empty filenames and imports. + * SMTChecker: Fix internal errors when analysing tuples. ### 0.6.3 (2020-02-18) diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index ac81a5d87..3b2f48816 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -407,19 +407,26 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_tuple)); solAssert(symbTuple, ""); auto const& symbComponents = symbTuple->components(); - auto const& tupleComponents = _tuple.components(); - solAssert(symbComponents.size() == _tuple.components().size(), ""); + auto const* tupleComponents = &_tuple.components(); + while (tupleComponents->size() == 1) + { + auto innerTuple = dynamic_pointer_cast(tupleComponents->front()); + solAssert(innerTuple, ""); + tupleComponents = &innerTuple->components(); + } + solAssert(symbComponents.size() == tupleComponents->size(), ""); for (unsigned i = 0; i < symbComponents.size(); ++i) { auto sComponent = symbComponents.at(i); - auto tComponent = tupleComponents.at(i); + auto tComponent = tupleComponents->at(i); if (sComponent && tComponent) { if (auto varDecl = identifierToVariable(*tComponent)) m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl)); else { - solAssert(m_context.knownExpression(*tComponent), ""); + if (!m_context.knownExpression(*tComponent)) + createExpr(*tComponent); m_context.addAssertion(sComponent->currentValue() == expr(*tComponent)); } } diff --git a/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol b/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol new file mode 100644 index 000000000..4d1d954f7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract K { + function f() public pure { + (abi.encode, 2); + } +} +// ---- +// Warning: (76-91): Statement has no effect. +// Warning: (77-80): Assertion checker does not yet implement type abi diff --git a/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol b/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol new file mode 100644 index 000000000..5b9c2f41c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract K { + function f() public pure { + (abi.encode, ""); + } +} +// ---- +// Warning: (76-92): Statement has no effect. +// Warning: (77-80): Assertion checker does not yet implement type abi diff --git a/test/libsolidity/smtCheckerTests/types/tuple_single_element_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_single_element_1.sol new file mode 100644 index 000000000..f14ca9bc1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_single_element_1.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + (("", 2)); + } +} +// ---- +// Warning: (76-85): Statement has no effect. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_single_element_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_single_element_2.sol new file mode 100644 index 000000000..29b9c9747 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_single_element_2.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + (("", "")); + } +} +// ---- +// Warning: (76-86): Statement has no effect. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_single_non_tuple_element.sol b/test/libsolidity/smtCheckerTests/types/tuple_single_non_tuple_element.sol new file mode 100644 index 000000000..2a99a552e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_single_non_tuple_element.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + (2); + } +} +// ---- +// Warning: (76-79): Statement has no effect. From 3bee348525de032cdc510dc62b7366c90cf13d89 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Feb 2020 22:52:35 -0300 Subject: [PATCH 07/37] Change CHC encoding to functions forest instead of explicit CFG --- libsolidity/formal/CHC.cpp | 385 +++++++++++++----- libsolidity/formal/CHC.h | 77 +++- libsolidity/formal/EncodingContext.h | 1 + libsolidity/formal/ModelChecker.cpp | 4 +- libsolidity/formal/ModelChecker.h | 6 +- libsolidity/formal/Z3Interface.h | 6 +- .../functions/library_constant.sol | 26 ++ .../functions/library_constant_2.sol | 9 + .../invariants/loop_nested.sol | 5 + .../invariants/loop_nested_for.sol | 5 + ...r_loop_array_assignment_storage_memory.sol | 6 +- ..._loop_array_assignment_storage_storage.sol | 8 +- .../operators/delete_array_index_2d.sol | 4 +- 13 files changed, 426 insertions(+), 116 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/library_constant.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/library_constant_2.sol diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 25201474b..af50b2722 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -27,6 +27,8 @@ #include +#include + using namespace std; using namespace solidity; using namespace solidity::langutil; @@ -75,16 +77,39 @@ void CHC::analyze(SourceUnit const& _source) m_context.setAssertionAccumulation(false); m_variableUsage.setFunctionInlining(false); + resetSourceAnalysis(); + auto boolSort = make_shared(smt::Kind::Bool); auto genesisSort = make_shared( vector(), boolSort ); m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); - auto genesis = (*m_genesisPredicate)({}); - addRule(genesis, genesis.name); + addRule(genesis(), "genesis"); - _source.accept(*this); + set sources; + sources.insert(&_source); + for (auto const& source: _source.referencedSourceUnits(true)) + sources.insert(source); + for (auto const* source: sources) + defineInterfacesAndSummaries(*source); + for (auto const* source: sources) + source->accept(*this); + + for (auto const& [scope, target]: m_verificationTargets) + { + auto assertions = transactionAssertions(scope); + for (auto const* assertion: assertions) + { + createErrorBlock(); + connectBlocks(target.value, error(), target.constraints && (target.errorId == assertion->id())); + auto [result, model] = query(error(), assertion->location()); + // This should be fine but it's a bug in the old compiler + (void)model; + if (result == smt::CheckResult::UNSATISFIABLE) + m_safeAssertions.insert(assertion); + } + } } vector CHC::unhandledQueries() const @@ -97,26 +122,15 @@ vector CHC::unhandledQueries() const bool CHC::visit(ContractDefinition const& _contract) { - if (!shouldVisit(_contract)) - return false; - - reset(); + resetContractAnalysis(); initContract(_contract); - m_stateVariables = _contract.stateVariablesIncludingInherited(); - - for (auto const& var: m_stateVariables) - // SMT solvers do not support function types as arguments. - if (var->type()->category() == Type::Category::Function) - m_stateSorts.push_back(make_shared(smt::Kind::Int)); - else - m_stateSorts.push_back(smt::smtSort(*var->type())); + m_stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); + m_stateSorts = stateSorts(_contract); clearIndices(&_contract); - string suffix = _contract.name() + "_" + to_string(_contract.id()); - m_interfacePredicate = createSymbolicBlock(interfaceSort(), "interface_" + suffix); // TODO create static instances for Bool/Int sorts in SolverInterface. auto boolSort = make_shared(smt::Kind::Bool); @@ -125,10 +139,12 @@ bool CHC::visit(ContractDefinition const& _contract) boolSort ); + string suffix = _contract.name() + "_" + to_string(_contract.id()); m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error_" + suffix); - m_constructorPredicate = createSymbolicBlock(constructorSort(), "implicit_constructor_" + to_string(_contract.id())); + m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix); + m_implicitConstructorPredicate = createSymbolicBlock(interfaceSort(), "implicit_constructor_" + suffix); auto stateExprs = currentStateVariables(); - setCurrentBlock(*m_interfacePredicate, &stateExprs); + setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); SMTEncoder::visit(_contract); return false; @@ -136,33 +152,33 @@ bool CHC::visit(ContractDefinition const& _contract) void CHC::endVisit(ContractDefinition const& _contract) { - if (!shouldVisit(_contract)) - return; - for (auto const& var: m_stateVariables) { solAssert(m_context.knownVariable(*var), ""); + auto const& symbVar = m_context.variable(*var); + symbVar->resetIndex(); m_context.setZeroValue(*var); + symbVar->increaseIndex(); } - auto genesisPred = (*m_genesisPredicate)({}); - auto implicitConstructor = (*m_constructorPredicate)(currentStateVariables()); - connectBlocks(genesisPred, implicitConstructor); + auto implicitConstructor = (*m_implicitConstructorPredicate)(initialStateVariables()); + connectBlocks(genesis(), implicitConstructor); m_currentBlock = implicitConstructor; + m_context.addAssertion(m_error.currentValue() == 0); if (auto constructor = _contract.constructor()) constructor->accept(*this); else inlineConstructorHierarchy(_contract); - connectBlocks(m_currentBlock, interface()); + auto summary = predicate(*m_constructorSummaryPredicate, vector{m_error.currentValue()} + currentStateVariables()); + connectBlocks(m_currentBlock, summary); - for (unsigned i = 0; i < m_verificationTargets.size(); ++i) - { - auto const& target = m_verificationTargets.at(i); - auto errorAppl = error(i + 1); - if (query(errorAppl, target->location())) - m_safeAssertions.insert(target); - } + clearIndices(m_currentContract, nullptr); + auto stateExprs = vector{m_error.currentValue()} + currentStateVariables(); + setCurrentBlock(*m_constructorSummaryPredicate, &stateExprs); + + addVerificationTarget(m_currentContract, m_currentBlock, smt::Expression(true), m_error.currentValue()); + connectBlocks(m_currentBlock, interface(), m_error.currentValue() == 0); SMTEncoder::endVisit(_contract); } @@ -182,7 +198,7 @@ bool CHC::visit(FunctionDefinition const& _function) return false; } - solAssert(!m_currentFunction, "Inlining internal function calls not yet implemented"); + solAssert(!m_currentFunction, "Function inlining should not happen in CHC."); m_currentFunction = &_function; initFunction(_function); @@ -193,7 +209,17 @@ bool CHC::visit(FunctionDefinition const& _function) auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables()); auto bodyPred = predicate(*bodyBlock); - connectBlocks(m_currentBlock, functionPred); + if (_function.isConstructor()) + connectBlocks(m_currentBlock, functionPred); + else + connectBlocks(genesis(), functionPred); + + m_context.addAssertion(m_error.currentValue() == 0); + for (auto const* var: m_stateVariables) + m_context.addAssertion(m_context.variable(*var)->valueAtIndex(0) == currentValue(*var)); + for (auto const& var: _function.parameters()) + m_context.addAssertion(m_context.variable(*var)->valueAtIndex(0) == currentValue(*var)); + connectBlocks(functionPred, bodyPred); setCurrentBlock(*bodyBlock); @@ -225,18 +251,30 @@ void CHC::endVisit(FunctionDefinition const& _function) // This is done in endVisit(ContractDefinition). if (_function.isConstructor()) { - auto constructorExit = createSymbolicBlock(interfaceSort(), "constructor_exit_" + to_string(_function.id())); - connectBlocks(m_currentBlock, predicate(*constructorExit, currentStateVariables())); + string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id()); + auto constructorExit = createSymbolicBlock(constructorSort(), "constructor_exit_" + suffix); + connectBlocks(m_currentBlock, predicate(*constructorExit, vector{m_error.currentValue()} + currentStateVariables())); + clearIndices(m_currentContract, m_currentFunction); - auto stateExprs = currentStateVariables(); + auto stateExprs = vector{m_error.currentValue()} + currentStateVariables(); setCurrentBlock(*constructorExit, &stateExprs); } else { - connectBlocks(m_currentBlock, interface()); - clearIndices(m_currentContract, m_currentFunction); - auto stateExprs = currentStateVariables(); - setCurrentBlock(*m_interfacePredicate, &stateExprs); + auto assertionError = m_error.currentValue(); + auto sum = summary(_function); + connectBlocks(m_currentBlock, sum); + + auto iface = interface(); + + auto stateExprs = initialStateVariables(); + setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); + + if (_function.isPublic()) + { + addVerificationTarget(&_function, m_currentBlock, sum, assertionError); + connectBlocks(m_currentBlock, iface, sum && (assertionError == 0)); + } } m_currentFunction = nullptr; } @@ -468,12 +506,23 @@ void CHC::visitAssert(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); - createErrorBlock(); + solAssert(m_currentContract, ""); + solAssert(m_currentFunction, ""); + if (m_currentFunction->isConstructor()) + m_functionAssertions[m_currentContract].insert(&_funCall); + else + m_functionAssertions[m_currentFunction].insert(&_funCall); - smt::Expression assertNeg = !(m_context.expression(*args.front())->currentValue()); - connectBlocks(m_currentBlock, error(), currentPathConditions() && assertNeg); + auto previousError = m_error.currentValue(); + m_error.increaseIndex(); - m_verificationTargets.push_back(&_funCall); + connectBlocks( + m_currentBlock, + m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction), + currentPathConditions() && !m_context.expression(*args.front())->currentValue() && (m_error.currentValue() == _funCall.id()) + ); + + m_context.addAssertion(m_error.currentValue() == previousError); } void CHC::unknownFunctionCall(FunctionCall const&) @@ -488,15 +537,23 @@ void CHC::unknownFunctionCall(FunctionCall const&) m_unknownFunctionCallSeen = true; } -void CHC::reset() +void CHC::resetSourceAnalysis() +{ + m_verificationTargets.clear(); + m_safeAssertions.clear(); + m_functionAssertions.clear(); + m_callGraph.clear(); + m_summaries.clear(); +} + +void CHC::resetContractAnalysis() { m_stateSorts.clear(); m_stateVariables.clear(); - m_verificationTargets.clear(); - m_safeAssertions.clear(); m_unknownFunctionCallSeen = false; m_breakDest = nullptr; m_continueDest = nullptr; + m_error.resetIndex(); } void CHC::eraseKnowledge() @@ -521,17 +578,6 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c } } - -bool CHC::shouldVisit(ContractDefinition const& _contract) const -{ - if ( - _contract.isLibrary() || - _contract.isInterface() - ) - return false; - return true; -} - bool CHC::shouldVisit(FunctionDefinition const& _function) const { if ( @@ -547,7 +593,8 @@ void CHC::setCurrentBlock( vector const* _arguments ) { - m_context.popSolver(); + if (m_context.solverStackHeigh() > 0) + m_context.popSolver(); solAssert(m_currentContract, ""); clearIndices(m_currentContract, m_currentFunction); m_context.pushSolver(); @@ -557,10 +604,42 @@ void CHC::setCurrentBlock( m_currentBlock = predicate(_block); } +set CHC::transactionAssertions(ASTNode const* _txRoot) +{ + set assertions; + solidity::util::BreadthFirstSearch{{_txRoot}}.run([&](auto const* function, auto&& _addChild) { + assertions.insert(m_functionAssertions[function].begin(), m_functionAssertions[function].end()); + for (auto const* called: m_callGraph[function]) + _addChild(called); + }); + return assertions; +} + +vector CHC::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) +{ + vector stateVars; + for (auto const& contract: _contract.annotation().linearizedBaseContracts) + for (auto var: contract->stateVariables()) + stateVars.push_back(var); + return stateVars; +} + +vector CHC::stateSorts(ContractDefinition const& _contract) +{ + vector stateSorts; + for (auto const& var: stateVariablesIncludingInheritedAndPrivate(_contract)) + stateSorts.push_back(smt::smtSortAbstractFunction(*var->type())); + return stateSorts; +} + smt::SortPointer CHC::constructorSort() { - // TODO this will change once we support function calls. - return interfaceSort(); + auto boolSort = make_shared(smt::Kind::Bool); + auto intSort = make_shared(smt::Kind::Int); + return make_shared( + vector{intSort} + m_stateSorts, + boolSort + ); } smt::SortPointer CHC::interfaceSort() @@ -572,20 +651,38 @@ smt::SortPointer CHC::interfaceSort() ); } +smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) +{ + auto boolSort = make_shared(smt::Kind::Bool); + return make_shared( + stateSorts(_contract), + boolSort + ); +} + +/// A function in the symbolic CFG requires: +/// - Index of failed assertion. 0 means no assertion failed. +/// - 2 sets of state variables: +/// - State variables at the beginning of the current function, immutable +/// - Current state variables +/// At the beginning of the function these must equal set 1 +/// - 2 sets of input variables: +/// - Input variables at the beginning of the current function, immutable +/// - Current input variables +/// At the beginning of the function these must equal set 1 +/// - 1 set of output variables smt::SortPointer CHC::sort(FunctionDefinition const& _function) { auto boolSort = make_shared(smt::Kind::Bool); - vector varSorts; - for (auto const& var: _function.parameters() + _function.returnParameters()) - { - // SMT solvers do not support function types as arguments. - if (var->type()->category() == Type::Category::Function) - varSorts.push_back(make_shared(smt::Kind::Int)); - else - varSorts.push_back(smt::smtSort(*var->type())); - } + auto intSort = make_shared(smt::Kind::Int); + vector inputSorts; + for (auto const& var: _function.parameters()) + inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); + vector outputSorts; + for (auto const& var: _function.returnParameters()) + outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - m_stateSorts + varSorts, + vector{intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, boolSort ); } @@ -601,19 +698,31 @@ smt::SortPointer CHC::sort(ASTNode const* _node) auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: m_currentFunction->localVariables()) - { - // SMT solvers do not support function types as arguments. - if (var->type()->category() == Type::Category::Function) - varSorts.push_back(make_shared(smt::Kind::Int)); - else - varSorts.push_back(smt::smtSort(*var->type())); - } + varSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( fSort->domain + varSorts, boolSort ); } +smt::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract) +{ + auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); + auto sorts = stateSorts(_contract); + + auto boolSort = make_shared(smt::Kind::Bool); + auto intSort = make_shared(smt::Kind::Int); + vector inputSorts, outputSorts; + for (auto const& var: _function.parameters()) + inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); + for (auto const& var: _function.returnParameters()) + outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); + return make_shared( + vector{intSort} + sorts + inputSorts + sorts + outputSorts, + boolSort + ); +} + unique_ptr CHC::createSymbolicBlock(smt::SortPointer _sort, string const& _name) { auto block = make_unique( @@ -625,12 +734,33 @@ unique_ptr CHC::createSymbolicBlock(smt::SortPoin return block; } +void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) +{ + for (auto const& node: _source.nodes()) + if (auto const* contract = dynamic_cast(node.get())) + for (auto const* base: contract->annotation().linearizedBaseContracts) + { + string suffix = base->name() + "_" + to_string(base->id()); + m_interfaces[base] = createSymbolicBlock(interfaceSort(*base), "interface_" + suffix); + for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*base)) + if (!m_context.knownVariable(*var)) + createVariable(*var); + for (auto const* function: base->definedFunctions()) + m_summaries[contract].emplace(function, createSummaryBlock(*function, *contract)); + } +} + smt::Expression CHC::interface() { vector paramExprs; for (auto const& var: m_stateVariables) paramExprs.push_back(m_context.variable(*var)->currentValue()); - return (*m_interfacePredicate)(paramExprs); + return (*m_interfaces.at(m_currentContract))(paramExprs); +} + +smt::Expression CHC::interface(ContractDefinition const& _contract) +{ + return (*m_interfaces.at(&_contract))(stateVariablesAtIndex(0, _contract)); } smt::Expression CHC::error() @@ -643,6 +773,27 @@ smt::Expression CHC::error(unsigned _idx) return m_errorPredicate->functionValueAtIndex(_idx)({}); } +smt::Expression CHC::summary(ContractDefinition const&) +{ + return (*m_constructorSummaryPredicate)( + vector{m_error.currentValue()} + + currentStateVariables() + ); +} + +smt::Expression CHC::summary(FunctionDefinition const& _function) +{ + vector args{m_error.currentValue()}; + auto contract = _function.annotation().contract; + args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables(); + for (auto const& var: _function.parameters()) + args.push_back(m_context.variable(*var)->valueAtIndex(0)); + args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(); + for (auto const& var: _function.returnParameters()) + args.push_back(m_context.variable(*var)->currentValue()); + return (*m_summaries.at(m_currentContract).at(&_function))(args); +} + unique_ptr CHC::createBlock(ASTNode const* _node, string const& _prefix) { return createSymbolicBlock(sort(_node), @@ -653,6 +804,15 @@ unique_ptr CHC::createBlock(ASTNode const* _node, predicateName(_node)); } +unique_ptr CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) +{ + return createSymbolicBlock(summarySort(_function, _contract), + "summary_" + + uniquePrefix() + + "_" + + predicateName(&_function, &_contract)); +} + void CHC::createErrorBlock() { solAssert(m_errorPredicate, ""); @@ -669,6 +829,28 @@ void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to addRule(edge, _from.name + "_to_" + _to.name); } +vector CHC::initialStateVariables() +{ + return stateVariablesAtIndex(0); +} + +vector CHC::stateVariablesAtIndex(int _index) +{ + solAssert(m_currentContract, ""); + vector exprs; + for (auto const& var: m_stateVariables) + exprs.push_back(m_context.variable(*var)->valueAtIndex(_index)); + return exprs; +} + +vector CHC::stateVariablesAtIndex(int _index, ContractDefinition const& _contract) +{ + vector exprs; + for (auto const& var: stateVariablesIncludingInheritedAndPrivate(_contract)) + exprs.push_back(m_context.variable(*var)->valueAtIndex(_index)); + return exprs; +} + vector CHC::currentStateVariables() { solAssert(m_currentContract, ""); @@ -680,11 +862,22 @@ vector CHC::currentStateVariables() vector CHC::currentFunctionVariables() { - vector paramExprs; - if (m_currentFunction) - for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) - paramExprs.push_back(m_context.variable(*var)->currentValue()); - return currentStateVariables() + paramExprs; + vector initInputExprs; + vector mutableInputExprs; + for (auto const& var: m_currentFunction->parameters()) + { + initInputExprs.push_back(m_context.variable(*var)->valueAtIndex(0)); + mutableInputExprs.push_back(m_context.variable(*var)->currentValue()); + } + vector returnExprs; + for (auto const& var: m_currentFunction->returnParameters()) + returnExprs.push_back(m_context.variable(*var)->currentValue()); + return vector{m_error.currentValue()} + + initialStateVariables() + + initInputExprs + + currentStateVariables() + + mutableInputExprs + + returnExprs; } vector CHC::currentBlockVariables() @@ -696,7 +889,7 @@ vector CHC::currentBlockVariables() return currentFunctionVariables() + paramExprs; } -string CHC::predicateName(ASTNode const* _node) +string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contract) { string prefix; if (auto funDef = dynamic_cast(_node)) @@ -705,7 +898,12 @@ string CHC::predicateName(ASTNode const* _node) if (!funDef->name().empty()) prefix += "_" + funDef->name() + "_"; } - return prefix + to_string(_node->id()); + else if (m_currentFunction && !m_currentFunction->name().empty()) + prefix += m_currentFunction->name(); + + auto contract = _contract ? _contract : m_currentContract; + solAssert(contract, ""); + return prefix + "_" + to_string(_node->id()) + "_" + to_string(contract->id()); } smt::Expression CHC::predicate(smt::SymbolicFunctionVariable const& _block) @@ -726,7 +924,7 @@ void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) m_interface->addRule(_rule, _ruleName); } -bool CHC::query(smt::Expression const& _query, langutil::SourceLocation const& _location) +pair> CHC::query(smt::Expression const& _query, langutil::SourceLocation const& _location) { smt::CheckResult result; vector values; @@ -736,7 +934,7 @@ bool CHC::query(smt::Expression const& _query, langutil::SourceLocation const& _ case smt::CheckResult::SATISFIABLE: break; case smt::CheckResult::UNSATISFIABLE: - return true; + break; case smt::CheckResult::UNKNOWN: break; case smt::CheckResult::CONFLICTING: @@ -746,7 +944,12 @@ bool CHC::query(smt::Expression const& _query, langutil::SourceLocation const& _ m_outerErrorReporter.warning(_location, "Error trying to invoke SMT solver."); break; } - return false; + return {result, values}; +} + +void CHC::addVerificationTarget(ASTNode const* _scope, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId) +{ + m_verificationTargets.emplace(_scope, CHCVerificationTarget{{VerificationTarget::Type::Assert, _from, _constraints}, _errorId}); } string CHC::uniquePrefix() diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 6f912fbd8..889c21b6a 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -79,22 +79,38 @@ private: void unknownFunctionCall(FunctionCall const& _funCall); //@} + struct IdCompare + { + bool operator()(ASTNode const* lhs, ASTNode const* rhs) const + { + return lhs->id() < rhs->id(); + } + }; + /// Helpers. //@{ - void reset(); + void resetSourceAnalysis(); + void resetContractAnalysis(); void eraseKnowledge(); void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; - bool shouldVisit(ContractDefinition const& _contract) const; bool shouldVisit(FunctionDefinition const& _function) const; void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector const* _arguments = nullptr); + std::set transactionAssertions(ASTNode const* _txRoot); + static std::vector stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); //@} /// Sort helpers. //@{ + static std::vector stateSorts(ContractDefinition const& _contract); smt::SortPointer constructorSort(); smt::SortPointer interfaceSort(); + static smt::SortPointer interfaceSort(ContractDefinition const& _const); smt::SortPointer sort(FunctionDefinition const& _function); smt::SortPointer sort(ASTNode const* _block); + /// @returns the sort of a predicate that represents the summary of _function in the scope of _contract. + /// The _contract is also needed because the same function might be in many contracts due to inheritance, + /// where the sort changes because the set of state variables might change. + static smt::SortPointer summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract); //@} /// Predicate helpers. @@ -102,14 +118,24 @@ private: /// @returns a new block of given _sort and _name. std::unique_ptr createSymbolicBlock(smt::SortPointer _sort, std::string const& _name); + /// Creates summary predicates for all functions of all contracts + /// in a given _source. + void defineInterfacesAndSummaries(SourceUnit const& _source); + + /// Genesis predicate. + smt::Expression genesis() { return (*m_genesisPredicate)({}); } /// Interface predicate over current variables. smt::Expression interface(); + smt::Expression interface(ContractDefinition const& _contract); /// Error predicate over current variables. smt::Expression error(); smt::Expression error(unsigned _idx); /// Creates a block for the given _node. std::unique_ptr createBlock(ASTNode const* _node, std::string const& _prefix = ""); + /// Creates a call block for the given function _function from contract _contract. + /// The contract is needed here because of inheritance. + std::unique_ptr createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract); /// Creates a new error block to be used by an assertion. /// Also registers the predicate. @@ -117,6 +143,11 @@ private: void connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints = smt::Expression(true)); + /// @returns the symbolic values of the state variables at the beginning + /// of the current transaction. + std::vector initialStateVariables(); + std::vector stateVariablesAtIndex(int _index); + std::vector stateVariablesAtIndex(int _index, ContractDefinition const& _contract); /// @returns the current symbolic values of the current state variables. std::vector currentStateVariables(); @@ -128,19 +159,26 @@ private: std::vector currentBlockVariables(); /// @returns the predicate name for a given node. - std::string predicateName(ASTNode const* _node); + std::string predicateName(ASTNode const* _node, ContractDefinition const* _contract = nullptr); /// @returns a predicate application over the current scoped variables. smt::Expression predicate(smt::SymbolicFunctionVariable const& _block); /// @returns a predicate application over @param _arguments. smt::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); + /// @returns a predicate that defines a constructor summary. + smt::Expression summary(ContractDefinition const& _contract); + /// @returns a predicate that defines a function summary. + smt::Expression summary(FunctionDefinition const& _function); //@} /// Solver related. //@{ /// Adds Horn rule to the solver. void addRule(smt::Expression const& _rule, std::string const& _ruleName); - /// @returns true if query is unsatisfiable (safe). - bool query(smt::Expression const& _query, langutil::SourceLocation const& _location); + /// @returns if query is unsatisfiable (safe). + /// @returns otherwise. + std::pair> query(smt::Expression const& _query, langutil::SourceLocation const& _location); + + void addVerificationTarget(ASTNode const* _scope, smt::Expression _from, smt::Expression _constraints, smt::Expression _errorId); //@} /// Misc. @@ -157,15 +195,29 @@ private: /// Implicit constructor predicate. /// Explicit constructors are handled as functions. - std::unique_ptr m_constructorPredicate; + std::unique_ptr m_implicitConstructorPredicate; + + /// Constructor summary predicate, exists after the constructor + /// (implicit or explicit) and before the interface. + std::unique_ptr m_constructorSummaryPredicate; /// Artificial Interface predicate. /// Single entry block for all functions. - std::unique_ptr m_interfacePredicate; + std::map> m_interfaces; /// Artificial Error predicate. /// Single error block for all assertions. std::unique_ptr m_errorPredicate; + + /// Function predicates. + std::map>> m_summaries; + + smt::SymbolicIntVariable m_error{ + TypeProvider::uint256(), + TypeProvider::uint256(), + "error", + m_context + }; //@} /// Variables. @@ -180,7 +232,12 @@ private: /// Verification targets. //@{ - std::vector m_verificationTargets; + struct CHCVerificationTarget: VerificationTarget + { + smt::Expression errorId; + }; + + std::map m_verificationTargets; /// Assertions proven safe. std::set m_safeAssertions; @@ -190,6 +247,10 @@ private: //@{ FunctionDefinition const* m_currentFunction = nullptr; + std::map, IdCompare> m_callGraph; + + std::map, IdCompare> m_functionAssertions; + /// The current block. smt::Expression m_currentBlock = smt::Expression(true); diff --git a/libsolidity/formal/EncodingContext.h b/libsolidity/formal/EncodingContext.h index 9b75065f6..648f7dc33 100644 --- a/libsolidity/formal/EncodingContext.h +++ b/libsolidity/formal/EncodingContext.h @@ -140,6 +140,7 @@ public: void pushSolver(); void popSolver(); void addAssertion(Expression const& _e); + unsigned solverStackHeigh() { return m_assertions.size(); } const SolverInterface* solver() { solAssert(m_solver, ""); diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index c69849a06..ebc1a8751 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -29,9 +29,9 @@ ModelChecker::ModelChecker( ReadCallback::Callback const& _smtCallback, smt::SMTSolverChoice _enabledSolvers ): + m_context(), m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers), - m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers), - m_context() + m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers) { } diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index ac19ba270..546f2df86 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -62,14 +62,14 @@ public: static smt::SMTSolverChoice availableSolvers(); private: + /// Stores the context of the encoding. + smt::EncodingContext m_context; + /// Bounded Model Checker engine. BMC m_bmc; /// Constrained Horn Clauses engine. CHC m_chc; - - /// Stores the context of the encoding. - smt::EncodingContext m_context; }; } diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h index a6247a541..3791af4c5 100644 --- a/libsolidity/formal/Z3Interface.h +++ b/libsolidity/formal/Z3Interface.h @@ -58,11 +58,11 @@ private: z3::sort z3Sort(smt::Sort const& _sort); z3::sort_vector z3Sort(std::vector const& _sorts); - std::map m_constants; - std::map m_functions; - z3::context m_context; z3::solver m_solver; + + std::map m_constants; + std::map m_functions; }; } diff --git a/test/libsolidity/smtCheckerTests/functions/library_constant.sol b/test/libsolidity/smtCheckerTests/functions/library_constant.sol new file mode 100644 index 000000000..e32b8565b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/library_constant.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +library l1 { + + uint private constant TON = 1000; + function f1() public pure { + assert(TON == 1000); + assert(TON == 2000); + } + function f2(uint x, uint y) internal pure returns (uint) { + return x + y; + } +} + +contract C { + function f(uint x) public pure { + uint z = l1.f2(x, 1); + assert(z == x + 1); + } +} +// ---- +// Warning: (136-155): Assertion violation happens here +// Warning: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (300-302): Assertion checker does not yet implement type type(library l1) +// Warning: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (327-332): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/library_constant_2.sol b/test/libsolidity/smtCheckerTests/functions/library_constant_2.sol new file mode 100644 index 000000000..9c04e6f9a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/library_constant_2.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +library l1 { + + uint private constant TON = 1000; + function f1() public pure { + assert(TON == 1000); + } +} diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol index 867403b61..1fb380649 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol @@ -17,3 +17,8 @@ contract Simple { } // ==== // SMTSolvers: z3 +// ---- +// Warning: (172-187): Error trying to invoke SMT solver. +// Warning: (195-209): Error trying to invoke SMT solver. +// Warning: (172-187): Assertion violation happens here +// Warning: (195-209): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol index 9cfd4569f..e7fde4d5a 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol @@ -14,3 +14,8 @@ contract Simple { } // ==== // SMTSolvers: z3 +// ---- +// Warning: (164-179): Error trying to invoke SMT solver. +// Warning: (187-201): Error trying to invoke SMT solver. +// Warning: (164-179): Assertion violation happens here +// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol index 9e7a83866..0b301505b 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol @@ -12,12 +12,12 @@ contract LoopFor2 { b[i] = i + 1; c[i] = b[i]; } + // This is safe but too hard to solve currently. assert(b[0] == c[0]); assert(a[0] == 900); assert(b[0] == 900); } } -// ==== -// SMTSolvers: z3 // ---- -// Warning: (312-331): Assertion violation happens here +// Warning: (316-336): Assertion violation happens here +// Warning: (363-382): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol index 8146a7f19..333273781 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol @@ -12,13 +12,13 @@ contract LoopFor2 { b[i] = i + 1; c[i] = b[i]; } + // This is safe but too hard to prove currently. assert(b[0] == c[0]); assert(a[0] == 900); assert(b[0] == 900); } } -// ==== -// SMTSolvers: z3 // ---- -// Warning: (290-309): Assertion violation happens here -// Warning: (313-332): Assertion violation happens here +// Warning: (317-337): Assertion violation happens here +// Warning: (341-360): Assertion violation happens here +// Warning: (364-383): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol index 06ef66be9..9cd7de9e6 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol @@ -13,5 +13,5 @@ contract C assert(a[1][1] == 0); } } -// ==== -// SMTSolvers: z3 +// ---- +// Warning: (184-204): Assertion violation happens here From 24d6e6295e75ee9da587ced7471df9e89713b21b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 2 Mar 2020 17:20:49 +0100 Subject: [PATCH 08/37] Reuse the mechanism for abi functions and move tracking of used functions to CompilerContext --- libsolidity/codegen/ABIFunctions.cpp | 11 +++-------- libsolidity/codegen/ABIFunctions.h | 6 ------ libsolidity/codegen/CompilerContext.cpp | 9 ++++++++- libsolidity/codegen/CompilerContext.h | 11 +++++++---- libsolidity/codegen/CompilerUtils.cpp | 16 +++------------- .../codegen/MultiUseYulFunctionCollector.cpp | 12 ++---------- .../codegen/MultiUseYulFunctionCollector.h | 19 ++----------------- libsolidity/codegen/ir/IRGenerator.cpp | 6 +++--- 8 files changed, 28 insertions(+), 62 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index b69f47346..b6d690898 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -55,7 +55,7 @@ string ABIFunctions::tupleEncoder( functionName += t->identifier() + "_"; functionName += options.toFunctionNameSuffix(); - return createExternallyUsedFunction(functionName, [&]() { + return createFunction(functionName, [&]() { // Note that the values are in reverse due to the difference in calling semantics. Whiskers templ(R"( function (headStart ) -> tail { @@ -121,7 +121,7 @@ string ABIFunctions::tupleEncoderPacked( functionName += t->identifier() + "_"; functionName += options.toFunctionNameSuffix(); - return createExternallyUsedFunction(functionName, [&]() { + return createFunction(functionName, [&]() { solAssert(!_givenTypes.empty(), ""); // Note that the values are in reverse due to the difference in calling semantics. @@ -173,7 +173,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) if (_fromMemory) functionName += "_fromMemory"; - return createExternallyUsedFunction(functionName, [&]() { + return createFunction(functionName, [&]() { TypePointers decodingTypes; for (auto const& t: _types) decodingTypes.emplace_back(t->decodingType()); @@ -1495,11 +1495,6 @@ string ABIFunctions::createFunction(string const& _name, function con return m_functionCollector->createFunction(_name, _creator); } -string ABIFunctions::createExternallyUsedFunction(string const& _name, function const& _creator) -{ - return m_functionCollector->createExternallyUsedFunction(_name, _creator); -} - size_t ABIFunctions::headSize(TypePointers const& _targetTypes) { size_t headSize = 0; diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 74bfdf7fa..1099e3593 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -31,7 +31,6 @@ #include #include -#include #include namespace solidity::frontend @@ -233,11 +232,6 @@ private: /// cases. std::string createFunction(std::string const& _name, std::function const& _creator); - /// Helper function that uses @a _creator to create a function and add it to - /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both - /// cases. Also adds it to the list of externally used functions. - std::string createExternallyUsedFunction(std::string const& _name, std::function const& _creator); - /// @returns the size of the static part of the encoding of the given types. static size_t headSize(TypePointers const& _targetTypes); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index f65ccae52..ee12b9b3e 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -100,7 +100,7 @@ void CompilerContext::callYulUtilFunction( unsigned _outArgs ) { - m_functionCollector->markAsExternallyUsed(_name); + m_externallyUsedFunctions.insert(_name); auto retTag = pushNewTag(); CompilerUtils(*this).moveIntoStack(_inArgs); appendJumpTo(namedTag(_name)); @@ -147,6 +147,13 @@ void CompilerContext::appendMissingLowLevelFunctions() } } +pair> CompilerContext::requestedYulFunctions() +{ + set empty; + swap(empty, m_externallyUsedFunctions); + return make_pair(m_functionCollector->requestedFunctions(), std::move(empty)); +} + void CompilerContext::addVariable( VariableDeclaration const& _declaration, unsigned _offsetToCurrent diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index d9393e23a..6d554da82 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -154,10 +154,11 @@ public: void appendMissingLowLevelFunctions(); ABIFunctions& abiFunctions() { return m_abiFunctions; } YulUtilFunctions& utilFunctions() { return m_yulUtilFunctions; } - std::pair> requestedYulFunctions() - { - return m_functionCollector->requestedFunctions(); - } + /// @returns concatenation of all generated functions and a set of the + /// externally used functions. + /// Clears the internal list, i.e. calling it again will result in an + /// empty return value. + std::pair> requestedYulFunctions(); ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -371,6 +372,8 @@ private: std::map m_lowLevelFunctions; // Collector for yul functions. std::shared_ptr m_functionCollector = std::make_shared(); + /// Set of externally used yul functions. + std::set m_externallyUsedFunctions; /// Container for ABI functions to be generated. ABIFunctions m_abiFunctions; /// Container for Yul Util functions to be generated. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 4c75081ff..a0d38eae5 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -595,31 +595,21 @@ void CompilerUtils::abiEncodeV2( // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> - auto ret = m_context.pushNewTag(); - moveIntoStack(sizeOnStack(_givenTypes) + 1); - string encoderName = _padToWordBoundaries ? m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes); - m_context.appendJumpTo(m_context.namedTag(encoderName)); - m_context.adjustStackOffset(-int(sizeOnStack(_givenTypes)) - 1); - m_context << ret.tag(); + m_context.callYulUtilFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1); } void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory) { // stack: [stack top] - auto ret = m_context.pushNewTag(); - moveIntoStack(2); - // stack: [stack top] m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; - // stack: + // stack: string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory); - m_context.appendJumpTo(m_context.namedTag(decoderName)); - m_context.adjustStackOffset(int(sizeOnStack(_parameterTypes)) - 3); - m_context << ret.tag(); + m_context.callYulUtilFunction(decoderName, 2, sizeOnStack(_parameterTypes)); } void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp index 8af292eda..2be3d29ae 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp @@ -30,15 +30,13 @@ using namespace std; using namespace solidity; using namespace solidity::frontend; -pair> MultiUseYulFunctionCollector::requestedFunctions() +string MultiUseYulFunctionCollector::requestedFunctions() { string result; for (auto const& f: m_requestedFunctions) result += f.second; m_requestedFunctions.clear(); - std::set empty; - swap(empty, m_externallyUsedFunctions); - return make_pair(result, std::move(empty)); + return result; } string MultiUseYulFunctionCollector::createFunction(string const& _name, function const& _creator) @@ -52,9 +50,3 @@ string MultiUseYulFunctionCollector::createFunction(string const& _name, functio } return _name; } - -string MultiUseYulFunctionCollector::createExternallyUsedFunction(string const& _name, function const& _creator) -{ - m_externallyUsedFunctions.insert(_name); - return createFunction(_name, _creator); -} diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.h b/libsolidity/codegen/MultiUseYulFunctionCollector.h index a1c733274..d839a31be 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.h +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.h @@ -23,7 +23,6 @@ #include #include -#include #include namespace solidity::frontend @@ -41,28 +40,14 @@ public: /// cases. std::string createFunction(std::string const& _name, std::function const& _creator); - /// Helper function that uses @a _creator to create a function and add it to - /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both - /// cases. - std::string createExternallyUsedFunction(std::string const& _name, std::function const& _creator); - - /// Manually mark a function as externally used. - void markAsExternallyUsed(std::string const& _name) - { - m_externallyUsedFunctions.insert(_name); - } - - /// @returns concatenation of all generated functions and a set of the - /// externally used functions. + /// @returns concatenation of all generated functions. /// Clears the internal list, i.e. calling it again will result in an /// empty return value. - std::pair> requestedFunctions(); + std::string requestedFunctions(); private: /// Map from function name to code for a multi-use function. std::map m_requestedFunctions; - // Set of externally used functions. - std::set m_externallyUsedFunctions; }; } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 0f2ffaf35..9c2d8807c 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -107,7 +107,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* fun: contract->definedFunctions()) generateFunction(*fun); - t("functions", m_context.functionCollector()->requestedFunctions().first); + t("functions", m_context.functionCollector()->requestedFunctions()); resetContext(_contract); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); @@ -116,7 +116,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* fun: contract->definedFunctions()) generateFunction(*fun); - t("runtimeFunctions", m_context.functionCollector()->requestedFunctions().first); + t("runtimeFunctions", m_context.functionCollector()->requestedFunctions()); return t.render(); } @@ -383,7 +383,7 @@ string IRGenerator::memoryInit() void IRGenerator::resetContext(ContractDefinition const& _contract) { solAssert( - m_context.functionCollector()->requestedFunctions().first.empty(), + m_context.functionCollector()->requestedFunctions().empty(), "Reset context while it still had functions." ); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); From 561e5d9b27439e56cbd136abecfde5727594155b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 2 Mar 2020 17:23:58 +0100 Subject: [PATCH 09/37] Rename variables and review suggestion. --- libsolidity/codegen/CompilerContext.cpp | 10 +++++----- libsolidity/codegen/CompilerContext.h | 14 +++++++------- libsolidity/codegen/CompilerUtils.cpp | 4 ++-- libsolidity/codegen/ContractCompiler.cpp | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ee12b9b3e..1d1eb92b9 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -94,14 +94,14 @@ void CompilerContext::callLowLevelFunction( *this << retTag.tag(); } -void CompilerContext::callYulUtilFunction( +void CompilerContext::callYulFunction( string const& _name, unsigned _inArgs, unsigned _outArgs ) { - m_externallyUsedFunctions.insert(_name); - auto retTag = pushNewTag(); + m_externallyUsedYulFunctions.insert(_name); + auto const retTag = pushNewTag(); CompilerUtils(*this).moveIntoStack(_inArgs); appendJumpTo(namedTag(_name)); adjustStackOffset(int(_outArgs) - 1 - _inArgs); @@ -150,8 +150,8 @@ void CompilerContext::appendMissingLowLevelFunctions() pair> CompilerContext::requestedYulFunctions() { set empty; - swap(empty, m_externallyUsedFunctions); - return make_pair(m_functionCollector->requestedFunctions(), std::move(empty)); + swap(empty, m_externallyUsedYulFunctions); + return make_pair(m_yulFunctionCollector->requestedFunctions(), std::move(empty)); } void CompilerContext::addVariable( diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 6d554da82..76c72770d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -65,8 +65,8 @@ public: m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), m_runtimeContext(_runtimeContext), - m_abiFunctions(m_evmVersion, m_revertStrings, m_functionCollector), - m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_functionCollector) + m_abiFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector), + m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector) { if (m_runtimeContext) m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); @@ -133,8 +133,8 @@ public: std::function const& _generator ); - /// Appends a call to a yul util function and registers the function as externally used. - void callYulUtilFunction( + /// Appends a call to a yul function and registers the function as externally used. + void callYulFunction( std::string const& _name, unsigned _inArgs, unsigned _outArgs @@ -370,10 +370,10 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map m_lowLevelFunctions; - // Collector for yul functions. - std::shared_ptr m_functionCollector = std::make_shared(); + /// Collector for yul functions. + std::shared_ptr m_yulFunctionCollector = std::make_shared(); /// Set of externally used yul functions. - std::set m_externallyUsedFunctions; + std::set m_externallyUsedYulFunctions; /// Container for ABI functions to be generated. ABIFunctions m_abiFunctions; /// Container for Yul Util functions to be generated. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index a0d38eae5..b39633377 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -599,7 +599,7 @@ void CompilerUtils::abiEncodeV2( _padToWordBoundaries ? m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes); - m_context.callYulUtilFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1); + m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1); } void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory) @@ -609,7 +609,7 @@ void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromM m_context << Instruction::SWAP1; // stack: string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory); - m_context.callYulUtilFunction(decoderName, 2, sizeOnStack(_parameterTypes)); + m_context.callYulFunction(decoderName, 2, sizeOnStack(_parameterTypes)); } void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 47187b1c8..8b9061af3 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1267,12 +1267,12 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); - auto yulFunctions = m_context.requestedYulFunctions(); - if (!yulFunctions.first.empty()) + auto [yulFunctions, externallyUsedYulFunctions] = m_context.requestedYulFunctions(); + if (!yulFunctions.empty()) m_context.appendInlineAssembly( - "{" + move(yulFunctions.first) + "}", + "{" + move(yulFunctions) + "}", {}, - yulFunctions.second, + externallyUsedYulFunctions, true, m_optimiserSettings ); From 495abee7693810b012032741e0723c55e8a26024 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Mar 2020 12:21:16 +0100 Subject: [PATCH 10/37] [test] Fixes ExecutionFramework providing different contract addresses when running test cases for both, old and new yul codegen. --- test/ExecutionFramework.cpp | 8 ++++++-- test/ExecutionFramework.h | 2 ++ test/libsolidity/SemanticTest.cpp | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index cc54e7a7b..170f5638c 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -53,12 +53,16 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): m_optimiserSettings = solidity::frontend::OptimiserSettings::full(); else if (solidity::test::CommonOptions::get().optimize) m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); - m_evmHost->reset(); + reset(); +} + +void ExecutionFramework::reset() +{ + m_evmHost->reset(); for (size_t i = 0; i < 10; i++) m_evmHost->accounts[EVMHost::convertToEVMC(account(i))].balance = EVMHost::convertToEVMC(u256(1) << 100); - } std::pair ExecutionFramework::compareAndCreateMessage( diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 6a3ffc0fb..e55a12aa6 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -251,6 +251,8 @@ private: } protected: + void reset(); + void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0); void sendEther(Address const& _to, u256 const& _value); size_t currentTimestamp(); diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index b1ef9372a..b2c4628da 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -101,6 +101,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref { for(bool compileViaYul: set{!m_runWithoutYul, m_runWithYul}) { + reset(); bool success = true; m_compileViaYul = compileViaYul; From 58c6b9070530d91ca1c7851a55b106dba3a8be99 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Tue, 3 Mar 2020 12:15:59 +0100 Subject: [PATCH 11/37] Deprecated warning for .value() and .gas() on function and constructror calls --- Changelog.md | 1 + docs/types/value-types.rst | 2 +- libsolidity/analysis/TypeChecker.cpp | 18 +++++++++++++++++- .../semanticTests/tryCatch/assert.sol | 2 +- .../semanticTests/tryCatch/trivial.sol | 2 +- .../types/function_type_members.sol | 3 +++ .../constructor/constructor_payable.sol | 10 ++++++++++ .../functionCalls/calloptions_repeated.sol | 1 + .../new_with_invalid_calloptions.sol | 8 ++++---- .../functionTypes/call_gas_on_function.sol | 7 +++++++ .../call_value_library_function.sol | 10 ++++++++++ ...call_value_on_non_payable_function_type.sol | 2 +- .../call_value_on_payable_function_type.sol | 2 +- ...ue_options_on_non_payable_function_type.sol | 8 ++++++++ .../warn_deprecate_gas_function.sol | 8 ++++++++ .../warn_deprecate_value_constructor.sol | 11 +++++++++++ .../warn_deprecate_value_function.sol | 8 ++++++++ .../348_unused_return_value_call_value.sol | 3 +++ .../361_calling_payable.sol | 9 +++++++-- .../362_calling_nonpayable.sol | 6 ++++-- ...not_warn_msg_value_in_internal_function.sol | 1 + ...unspecified_encoding_internal_functions.sol | 1 + .../viewPureChecker/gas_value_without_call.sol | 13 +++++++++++++ .../gas_with_call_nonpayable.sol | 15 +++++++++++++-- .../viewPureChecker/staticcall_gas_view.sol | 4 ++++ .../value_with_call_nonpayable.sol | 13 +++++++++++-- 26 files changed, 150 insertions(+), 18 deletions(-) create mode 100644 test/libsolidity/syntaxTests/constructor/constructor_payable.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/call_gas_on_function.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/call_value_library_function.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/call_value_options_on_non_payable_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/warn_deprecate_gas_function.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_constructor.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_function.sol diff --git a/Changelog.md b/Changelog.md index f151365ae..9a84d3012 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * Inline Assembly: Allow assigning to `_slot` of local storage variable pointers. + * General: Deprecated `value(...)` and `gas(...)` in favor of `{value: ...}` and `{gas: ...}` Compiler Features: diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 9dae37912..8984a5b8e 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -646,7 +646,7 @@ External (or public) functions have the following members: Example that shows how to use the members:: pragma solidity >=0.4.16 <0.7.0; - + // This will report a warning contract Example { function f() public payable returns (bytes4) { diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2f9526f87..f713ea289 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2312,7 +2312,11 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) else if (!expressionFunctionType->isPayable()) m_errorReporter.typeError( _functionCallOptions.location(), - "Cannot set option \"value\" on a non-payable function type." + kind == FunctionType::Kind::Creation ? + "Cannot set option \"value\", since the constructor of " + + expressionFunctionType->returnParameterTypes().front()->toString() + + " is not payable." : + "Cannot set option \"value\" on a non-payable function type." ); else { @@ -2522,12 +2526,24 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.type = possibleMembers.front().type; if (auto funType = dynamic_cast(annotation.type)) + { solAssert( !funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()), "Function \"" + memberName + "\" cannot be called on an object of type " + exprType->toString() + " (expected " + funType->selfType()->toString() + ")." ); + if ( + dynamic_cast(exprType) && + !annotation.referencedDeclaration && + (memberName == "value" || memberName == "gas") + ) + m_errorReporter.warning( + _memberAccess.location(), + "Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead." + ); + } + if (auto const* structType = dynamic_cast(exprType)) annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData); else if (exprType->category() == Type::Category::Array) diff --git a/test/libsolidity/semanticTests/tryCatch/assert.sol b/test/libsolidity/semanticTests/tryCatch/assert.sol index 367ab287b..8b6a7b996 100644 --- a/test/libsolidity/semanticTests/tryCatch/assert.sol +++ b/test/libsolidity/semanticTests/tryCatch/assert.sol @@ -4,7 +4,7 @@ contract C { } function f(bool x) public returns (uint) { // Set the gas to make this work on pre-byzantium VMs - try this.g.gas(8000)(x) { + try this.g{gas: 8000}(x) { return 1; } catch { return 2; diff --git a/test/libsolidity/semanticTests/tryCatch/trivial.sol b/test/libsolidity/semanticTests/tryCatch/trivial.sol index e2f8739bd..d43477e99 100644 --- a/test/libsolidity/semanticTests/tryCatch/trivial.sol +++ b/test/libsolidity/semanticTests/tryCatch/trivial.sol @@ -4,7 +4,7 @@ contract C { } function f(bool x) public returns (uint) { // Set the gas to make this work on pre-byzantium VMs - try this.g.gas(8000)(x) { + try this.g{gas: 8000}(x) { return 1; } catch { return 2; diff --git a/test/libsolidity/smtCheckerTests/types/function_type_members.sol b/test/libsolidity/smtCheckerTests/types/function_type_members.sol index 2296fa798..923767dd7 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_members.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_members.sol @@ -3,9 +3,12 @@ contract C { function f(function(uint) external payable g) internal { g.selector; g.gas(2).value(3)(4); + g{gas: 2, value: 3}(4); } } // ---- +// Warning: (122-127): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (122-136): Using ".value(...)" is deprecated. Use "{value: ...}" instead. // Warning: (108-118): Assertion checker does not yet support this expression. // Warning: (122-130): Assertion checker does not yet implement this type of function call. // Warning: (122-139): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_payable.sol b/test/libsolidity/syntaxTests/constructor/constructor_payable.sol new file mode 100644 index 000000000..e5c6ac28d --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/constructor_payable.sol @@ -0,0 +1,10 @@ +contract C { + constructor() public payable { } +} + +contract D { + function createC() public returns (C) { + C c = (new C){value: 1}(); + return c; + } +} diff --git a/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol b/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol index 2ffe4b3ed..181b325b0 100644 --- a/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol +++ b/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol @@ -15,6 +15,7 @@ contract C { // TypeError: (78-110): Option "gas" has already been set. // TypeError: (120-154): Option "gas" has already been set. // TypeError: (164-198): Option "value" has already been set. +// Warning: (208-222): Using ".value(...)" is deprecated. Use "{value: ...}" instead. // TypeError: (208-242): Option "value" has already been set. // TypeError: (252-293): Option "value" has already been set. // TypeError: (252-293): Option "gas" has already been set. diff --git a/test/libsolidity/syntaxTests/functionCalls/new_with_invalid_calloptions.sol b/test/libsolidity/syntaxTests/functionCalls/new_with_invalid_calloptions.sol index a8cabe0de..29798dde6 100644 --- a/test/libsolidity/syntaxTests/functionCalls/new_with_invalid_calloptions.sol +++ b/test/libsolidity/syntaxTests/functionCalls/new_with_invalid_calloptions.sol @@ -14,14 +14,14 @@ contract C { // ==== // EVMVersion: >=constantinople // ---- -// TypeError: (64-98): Cannot set option "value" on a non-payable function type. +// TypeError: (64-98): Cannot set option "value", since the constructor of contract D is not payable. // TypeError: (64-98): Function call option "gas" cannot be used with "new". // TypeError: (102-123): Unknown call option "slt". Valid options are "salt", "value" and "gas". -// TypeError: (102-123): Cannot set option "value" on a non-payable function type. +// TypeError: (102-123): Cannot set option "value", since the constructor of contract D is not payable. // TypeError: (127-139): Unknown call option "val". Valid options are "salt", "value" and "gas". // TypeError: (143-172): Duplicate option "salt". -// TypeError: (176-199): Cannot set option "value" on a non-payable function type. -// TypeError: (176-199): Cannot set option "value" on a non-payable function type. +// TypeError: (176-199): Cannot set option "value", since the constructor of contract D is not payable. +// TypeError: (176-199): Cannot set option "value", since the constructor of contract D is not payable. // TypeError: (203-220): Unknown call option "random". Valid options are "salt", "value" and "gas". // TypeError: (224-242): Unknown call option "what". Valid options are "salt", "value" and "gas". // TypeError: (246-259): Function call option "gas" cannot be used with "new". diff --git a/test/libsolidity/syntaxTests/functionTypes/call_gas_on_function.sol b/test/libsolidity/syntaxTests/functionTypes/call_gas_on_function.sol new file mode 100644 index 000000000..381fb6ea0 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/call_gas_on_function.sol @@ -0,0 +1,7 @@ +contract C { + function (uint) external returns (uint) x; + function f() public { + x{gas: 2}(1); + } +} + diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_library_function.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_library_function.sol new file mode 100644 index 000000000..97eebfd23 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_library_function.sol @@ -0,0 +1,10 @@ +library L { + function value(function()internal a, uint256 b) internal {} +} +contract C { + using L for function()internal; + function f() public { + function()internal x; + x.value(42); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol index 5efdf2407..822d3eb28 100644 --- a/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol @@ -1,7 +1,7 @@ contract C { function (uint) external returns (uint) x; function f() public { - x.value(2)(); + x.value(2)(1); } } // ---- diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol index ca2a01964..1fd0e7188 100644 --- a/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol @@ -1,6 +1,6 @@ contract C { function (uint) external payable returns (uint) x; function f() public { - x.value(2)(1); + x{value: 2}(1); } } diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_options_on_non_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_options_on_non_payable_function_type.sol new file mode 100644 index 000000000..30f36f219 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_options_on_non_payable_function_type.sol @@ -0,0 +1,8 @@ +contract C { + function (uint) external returns (uint) x; + function g() public { + x{value: 2}(1); + } +} +// ---- +// TypeError: (94-105): Cannot set option "value" on a non-payable function type. diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_gas_function.sol b/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_gas_function.sol new file mode 100644 index 000000000..36d42e349 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_gas_function.sol @@ -0,0 +1,8 @@ +contract C { + function (uint) external payable returns (uint) x; + function f() public { + x.gas(2)(1); + } +} +// ---- +// Warning: (102-107): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_constructor.sol b/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_constructor.sol new file mode 100644 index 000000000..28a573c93 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_constructor.sol @@ -0,0 +1,11 @@ +contract C { + constructor() payable public {} +} +contract D { + function createC() public returns (C) { + C c = (new C).value(2)(); + return c; + } +} +// ---- +// Warning: (122-135): Using ".value(...)" is deprecated. Use "{value: ...}" instead. diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_function.sol b/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_function.sol new file mode 100644 index 000000000..854ea28fa --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/warn_deprecate_value_function.sol @@ -0,0 +1,8 @@ +contract C { + function (uint) external payable returns (uint) x; + function f() public { + x.value(2)(1); + } +} +// ---- +// Warning: (102-109): Using ".value(...)" is deprecated. Use "{value: ...}" instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol index 1ac7c6f33..dffa55fdf 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol @@ -1,7 +1,10 @@ contract test { function f() public { address(0x12).call.value(2)("abc"); + address(0x12).call{value: 2}("abc"); } } // ---- +// Warning: (50-74): Using ".value(...)" is deprecated. Use "{value: ...}" instead. // Warning: (50-84): Return value of low-level calls not used. +// Warning: (94-129): Return value of low-level calls not used. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol index 8ef4d5799..a3c5a5f30 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol @@ -1,6 +1,11 @@ contract receiver { function pay() payable public {} } contract test { - function f() public { (new receiver()).pay.value(10)(); } + function f() public { (new receiver()).pay{value: 10}(); } + function g() public { (new receiver()).pay.value(10)(); } receiver r = new receiver(); - function g() public { r.pay.value(10)(); } + function h() public { r.pay{value: 10}(); } + function i() public { r.pay.value(10)(); } } +// ---- +// Warning: (160-186): Using ".value(...)" is deprecated. Use "{value: ...}" instead. +// Warning: (303-314): Using ".value(...)" is deprecated. Use "{value: ...}" instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol index 1c04be755..6f5706446 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol @@ -1,6 +1,8 @@ contract receiver { function nopay() public {} } contract test { - function f() public { (new receiver()).nopay.value(10)(); } + function f() public { (new receiver()).nopay{value: 10}(); } + function g() public { (new receiver()).nopay.value(10)(); } } // ---- -// TypeError: (91-119): Member "value" is only available for payable functions. +// TypeError: (91-124): Cannot set option "value" on a non-payable function type. +// TypeError: (156-184): Member "value" is only available for payable functions. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol index 8492e691e..00d8ad4c8 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol @@ -3,3 +3,4 @@ contract C { msg.value; } } + diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol index a30e428a6..ea11703f9 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol @@ -5,6 +5,7 @@ contract C { } } // ---- +// Warning: (105-115): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. // TypeError: (91-100): This type cannot be encoded. // TypeError: (102-103): This type cannot be encoded. // TypeError: (105-115): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/gas_value_without_call.sol b/test/libsolidity/syntaxTests/viewPureChecker/gas_value_without_call.sol index 2df3bbe4f..77dab9af2 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/gas_value_without_call.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/gas_value_without_call.sol @@ -2,12 +2,25 @@ contract C { function f() external payable {} function g(address a) external pure { a.call.value(42); + a.call{value: 42}; a.call.gas(42); + a.call{gas: 42}; a.staticcall.gas(42); + a.staticcall{gas: 42}; a.delegatecall.gas(42); + a.delegatecall{gas: 42}; } function h() external view { this.f.value(42); + this.f{value: 42}; this.f.gas(42); + this.f{gas: 42}; } } +// ---- +// Warning: (91-103): Using ".value(...)" is deprecated. Use "{value: ...}" instead. +// Warning: (132-142): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (169-185): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (218-236): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (304-316): Using ".value(...)" is deprecated. Use "{value: ...}" instead. +// Warning: (345-355): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/gas_with_call_nonpayable.sol b/test/libsolidity/syntaxTests/viewPureChecker/gas_with_call_nonpayable.sol index 0a58a516c..4a0da038e 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/gas_with_call_nonpayable.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/gas_with_call_nonpayable.sol @@ -1,16 +1,27 @@ contract C { function f(address a) external view returns (bool success) { (success,) = a.call.gas(42)(""); + (success,) = a.call{gas: 42}(""); } function g(address a) external view returns (bool success) { (success,) = a.call.gas(42)(""); + (success,) = a.call{gas: 42}(""); } function h() external payable {} function i() external view { this.h.gas(42)(); } + function j() external view { + this.h{gas: 42}(); + } } // ---- +// Warning: (90-100): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (226-236): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (351-361): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. // TypeError: (90-108): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. -// TypeError: (190-208): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. -// TypeError: (279-295): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (125-144): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (226-244): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (261-280): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (351-367): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (404-421): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/staticcall_gas_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/staticcall_gas_view.sol index 6f8b31bfc..2b4821c53 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/staticcall_gas_view.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/staticcall_gas_view.sol @@ -3,9 +3,13 @@ contract C { function test(address a) external view returns (bool status) { // This used to incorrectly raise an error about violating the view mutability. (status,) = a.staticcall.gas(42)(""); + (status,) = a.staticcall{gas: 42}(""); this.f.gas(42)(); + this.f{gas: 42}(); } } // ==== // EVMVersion: >=byzantium // ---- +// Warning: (207-223): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. +// Warning: (276-286): Using ".gas(...)" is deprecated. Use "{gas: ...}" instead. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/value_with_call_nonpayable.sol b/test/libsolidity/syntaxTests/viewPureChecker/value_with_call_nonpayable.sol index cf5d885c9..6b82f2fab 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/value_with_call_nonpayable.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/value_with_call_nonpayable.sol @@ -1,16 +1,25 @@ contract C { function f(address a) external view returns (bool success) { (success,) = a.call.value(42)(""); + (success,) = a.call{value: 42}(""); } function g(address a) external view returns (bool success) { (success,) = a.call.value(42)(""); + (success,) = a.call{value: 42}(""); } function h() external payable {} function i() external view { this.h.value(42)(); + this.h{value: 42}(); } } // ---- +// Warning: (90-102): Using ".value(...)" is deprecated. Use "{value: ...}" instead. +// Warning: (230-242): Using ".value(...)" is deprecated. Use "{value: ...}" instead. +// Warning: (359-371): Using ".value(...)" is deprecated. Use "{value: ...}" instead. // TypeError: (90-110): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. -// TypeError: (192-212): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. -// TypeError: (283-301): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (127-148): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (230-250): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (267-288): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (359-377): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (381-400): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. From 919888ddbc470a628676586fcc1b21a7030b1042 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 2 Mar 2020 16:33:12 +0100 Subject: [PATCH 12/37] Use yul function for calldata tail access, fix checks and add additional revert reason. --- libsolidity/codegen/CompilerUtils.cpp | 56 ++----------------- libsolidity/codegen/YulUtilFunctions.cpp | 9 ++- .../revertStrings/calldata_tail_short.sol | 9 +++ 3 files changed, 21 insertions(+), 53 deletions(-) create mode 100644 test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index b39633377..da82c1822 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -121,56 +121,12 @@ void CompilerUtils::returnDataToArray() void CompilerUtils::accessCalldataTail(Type const& _type) { - solAssert(_type.dataStoredIn(DataLocation::CallData), ""); - solAssert(_type.isDynamicallyEncoded(), ""); - - unsigned int tailSize = _type.calldataEncodedTailSize(); - solAssert(tailSize > 1, ""); - - // returns the absolute offset of the tail in "base_ref" - m_context.appendInlineAssembly(Whiskers(R"({ - let rel_offset_of_tail := calldataload(ptr_to_tail) - if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { } - base_ref := add(base_ref, rel_offset_of_tail) - })") - ("neededLength", toCompactHexWithPrefix(tailSize)) - ("revertString", m_context.revertReasonIfDebug("Invalid calldata tail offset")) - .render(), {"base_ref", "ptr_to_tail"}); - // stack layout: - - if (!_type.isDynamicallySized()) - { - m_context << Instruction::POP; - // stack layout: - solAssert( - _type.category() == Type::Category::Struct || - _type.category() == Type::Category::Array, - "Invalid dynamically encoded base type on tail access." - ); - } - else - { - auto const* arrayType = dynamic_cast(&_type); - solAssert(!!arrayType, "Invalid dynamically sized type."); - unsigned int calldataStride = arrayType->calldataStride(); - solAssert(calldataStride > 0, ""); - - // returns the absolute offset of the tail in "base_ref" - // and the length of the tail in "length" - m_context.appendInlineAssembly( - Whiskers(R"({ - length := calldataload(base_ref) - base_ref := add(base_ref, 0x20) - if gt(length, 0xffffffffffffffff) { } - if sgt(base_ref, sub(calldatasize(), mul(length, ))) { revert(0, 0) } - })") - ("calldataStride", toCompactHexWithPrefix(calldataStride)) - ("revertString", m_context.revertReasonIfDebug("Invalid calldata tail length")) - .render(), - {"base_ref", "length"} - ); - // stack layout: - } + m_context << Instruction::SWAP1; + m_context.callYulFunction( + m_context.utilFunctions().accessCalldataTailFunction(_type), + 2, + _type.isDynamicallySized() ? 2 : 1 + ); } unsigned CompilerUtils::loadFromMemory( diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 0472dd588..7a5036109 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -942,13 +942,13 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) return Whiskers(R"( function (base_ref, ptr_to_tail) -> addr, length { let rel_offset_of_tail := calldataload(ptr_to_tail) - if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { revert(0, 0) } + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { } addr := add(base_ref, rel_offset_of_tail) length := calldataload(addr) - if gt(length, 0xffffffffffffffff) { revert(0, 0) } - if sgt(base_ref, sub(calldatasize(), mul(length, ))) { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { } addr := add(addr, 32) + if sgt(addr, sub(calldatasize(), mul(length, ))) { } } )") @@ -956,6 +956,9 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) ("dynamicallySized", _type.isDynamicallySized()) ("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize())) ("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast(_type).calldataStride() : 0)) + ("invalidCalldataTailOffset", revertReasonIfDebug("Invalid calldata tail offset")) + ("invalidCalldataTailLength", revertReasonIfDebug("Invalid calldata tail length")) + ("shortCalldataTail", revertReasonIfDebug("Calldata tail too short")) .render(); }); } diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol b/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol new file mode 100644 index 000000000..b6cc5800d --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[][] calldata x) external { x[0]; } +} +// ==== +// EVMVersion: >=byzantium +// revertStrings: debug +// ---- +// f(uint256[][]): 0x20, 1, 0x20, 2, 0x42 -> FAILURE, hex"08c379a0", 0x20, 23, "Calldata tail too short" From 857ed12b056489eb53131b196bf8ea5e4257c956 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 2 Mar 2020 18:08:19 +0100 Subject: [PATCH 13/37] Use plain members and references instead of shared pointers for MultiUseYulFunctionCollector --- libsolidity/codegen/ABIFunctions.cpp | 2 +- libsolidity/codegen/ABIFunctions.h | 6 +- libsolidity/codegen/CompilerContext.cpp | 2 +- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 110 +++++++++--------- libsolidity/codegen/YulUtilFunctions.h | 6 +- .../codegen/ir/IRGenerationContext.cpp | 2 +- libsolidity/codegen/ir/IRGenerationContext.h | 7 +- libsolidity/codegen/ir/IRGenerator.cpp | 13 +-- 9 files changed, 74 insertions(+), 76 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index b6d690898..5cca1c1e4 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -1492,7 +1492,7 @@ string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, string ABIFunctions::createFunction(string const& _name, function const& _creator) { - return m_functionCollector->createFunction(_name, _creator); + return m_functionCollector.createFunction(_name, _creator); } size_t ABIFunctions::headSize(TypePointers const& _targetTypes) diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 1099e3593..e760917ac 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -57,11 +57,11 @@ public: explicit ABIFunctions( langutil::EVMVersion _evmVersion, RevertStrings _revertStrings, - std::shared_ptr _functionCollector = std::make_shared() + MultiUseYulFunctionCollector& _functionCollector ): m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), - m_functionCollector(std::move(_functionCollector)), + m_functionCollector(_functionCollector), m_utils(_evmVersion, m_revertStrings, m_functionCollector) {} @@ -247,7 +247,7 @@ private: langutil::EVMVersion m_evmVersion; RevertStrings const m_revertStrings; - std::shared_ptr m_functionCollector; + MultiUseYulFunctionCollector& m_functionCollector; YulUtilFunctions m_utils; }; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 1d1eb92b9..cb195c49f 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -151,7 +151,7 @@ pair> CompilerContext::requestedYulFunctions() { set empty; swap(empty, m_externallyUsedYulFunctions); - return make_pair(m_yulFunctionCollector->requestedFunctions(), std::move(empty)); + return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)}; } void CompilerContext::addVariable( diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 76c72770d..8c5775239 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -371,7 +371,7 @@ private: /// An index of low-level function labels by name. std::map m_lowLevelFunctions; /// Collector for yul functions. - std::shared_ptr m_yulFunctionCollector = std::make_shared(); + MultiUseYulFunctionCollector m_yulFunctionCollector; /// Set of externally used yul functions. std::set m_externallyUsedYulFunctions; /// Container for ABI functions to be generated. diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 0472dd588..bcb309c99 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -39,7 +39,7 @@ using namespace solidity::frontend; string YulUtilFunctions::combineExternalFunctionIdFunction() { string functionName = "combine_external_function_id"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (addr, selector) -> combined { combined := (or((addr), and(selector, 0xffffffff))) @@ -55,7 +55,7 @@ string YulUtilFunctions::combineExternalFunctionIdFunction() string YulUtilFunctions::splitExternalFunctionIdFunction() { string functionName = "split_external_function_id"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (combined) -> addr, selector { combined := (combined) @@ -73,7 +73,7 @@ string YulUtilFunctions::splitExternalFunctionIdFunction() string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata) { string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { if (_fromCalldata) { return Whiskers(R"( @@ -116,7 +116,7 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess solAssert(!_assert || !_messageType, "Asserts can't have messages!"); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { if (!_messageType) return Whiskers(R"( function (condition) { @@ -166,7 +166,7 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess string YulUtilFunctions::leftAlignFunction(Type const& _type) { string functionName = string("leftAlign_") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function (value) -> aligned { @@ -228,7 +228,7 @@ string YulUtilFunctions::shiftLeftFunction(size_t _numBits) solAssert(_numBits < 256, ""); string functionName = "shift_left_" + to_string(_numBits); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> newValue { @@ -251,7 +251,7 @@ string YulUtilFunctions::shiftLeftFunction(size_t _numBits) string YulUtilFunctions::shiftLeftFunctionDynamic() { string functionName = "shift_left_dynamic"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (bits, value) -> newValue { @@ -277,7 +277,7 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits) // the opcodes SAR and SDIV behave differently with regards to rounding! string functionName = "shift_right_" + to_string(_numBits) + "_unsigned"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> newValue { @@ -303,7 +303,7 @@ string YulUtilFunctions::shiftRightFunctionDynamic() // the opcodes SAR and SDIV behave differently with regards to rounding! string const functionName = "shift_right_unsigned_dynamic"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (bits, value) -> newValue { @@ -328,7 +328,7 @@ string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shift size_t numBits = _numBytes * 8; size_t shiftBits = _shiftBytes * 8; string functionName = "update_byte_slice_" + to_string(_numBytes) + "_shift_" + to_string(_shiftBytes); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value, toInsert) -> result { @@ -350,7 +350,7 @@ string YulUtilFunctions::updateByteSliceFunctionDynamic(size_t _numBytes) solAssert(_numBytes <= 32, ""); size_t numBits = _numBytes * 8; string functionName = "update_byte_slice_dynamic" + to_string(_numBytes); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value, shiftBytes, toInsert) -> result { @@ -371,7 +371,7 @@ string YulUtilFunctions::updateByteSliceFunctionDynamic(size_t _numBytes) string YulUtilFunctions::roundUpFunction() { string functionName = "round_up_to_mul_of_32"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> result { @@ -389,7 +389,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) // TODO: Consider to add a special case for unsigned 256-bit integers // and use the following instead: // sum := add(x, y) if lt(sum, x) { revert(0, 0) } - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (x, y) -> sum { @@ -416,7 +416,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) { string functionName = "checked_mul_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return // Multiplication by zero could be treated separately and directly return zero. Whiskers(R"( @@ -448,7 +448,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) { string functionName = "checked_div_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (x, y) -> r { @@ -473,7 +473,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type) { string functionName = "checked_mod_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (x, y) -> r { @@ -490,7 +490,7 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type) string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) { string functionName = "checked_sub_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { return Whiskers(R"( function (x, y) -> diff { @@ -516,7 +516,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) { string functionName = "array_length_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers w(R"( function (value) -> length { @@ -564,7 +564,7 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type) solUnimplementedAssert(_type.baseType()->storageSize() == 1, ""); string functionName = "resize_array_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array, newLen) { if gt(newLen, ) { @@ -604,7 +604,7 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type) solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); string functionName = "array_pop_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array) { let oldLen := (array) @@ -632,7 +632,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); string functionName = "array_push_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array, value) { let oldLen := (array) @@ -659,7 +659,7 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); string functionName = "array_push_zero_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array) -> slot, offset { let oldLen := (array) @@ -684,7 +684,7 @@ string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) solAssert(_type.storageBytes() >= 32, "Expected smaller value for storage bytes"); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (start, end) { for {} lt(start, end) { start := add(start, ) } @@ -715,7 +715,7 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) string functionName = "clear_storage_array_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (slot) { @@ -745,7 +745,7 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) { string functionName = "array_convert_length_to_size_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Type const& baseType = *_type.baseType(); switch (_type.location()) @@ -798,7 +798,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); string functionName = "array_allocation_size_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers w(R"( function (length) -> size { // Make sure we can allocate memory without overflow @@ -825,7 +825,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) { string functionName = "array_dataslot_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { // No special processing for calldata arrays, because they are stored as // offset of the data area and length on the stack, so the offset already // points to the data area. @@ -858,7 +858,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) solUnimplementedAssert(_type.baseType()->storageBytes() > 16, ""); string functionName = "storage_array_index_access_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array, index) -> slot, offset { if iszero(lt(index, (array))) { @@ -886,7 +886,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type) { string functionName = "memory_array_index_access_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (baseRef, index) -> addr { if iszero(lt(index, (baseRef))) { @@ -912,7 +912,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type { solAssert(_type.dataStoredIn(DataLocation::CallData), ""); string functionName = "calldata_array_index_access_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (base_ref, length, index) -> addr, len { if iszero(lt(index, length)) { invalid() } @@ -938,7 +938,7 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) solAssert(_type.isDynamicallyEncoded(), ""); solAssert(_type.dataStoredIn(DataLocation::CallData), ""); string functionName = "access_calldata_tail_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (base_ref, ptr_to_tail) -> addr, length { let rel_offset_of_tail := calldataload(ptr_to_tail) @@ -966,7 +966,7 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) if (_type.dataStoredIn(DataLocation::Storage)) solAssert(_type.baseType()->storageBytes() > 16, ""); string functionName = "array_nextElement_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function (ptr) -> next { next := add(ptr, ) @@ -1002,7 +1002,7 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT solAssert(_keyType.sizeOnStack() <= 1, ""); string functionName = "mapping_index_access_" + _mappingType.identifier() + "_of_" + _keyType.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { if (_mappingType.keyType()->isDynamicallySized()) return Whiskers(R"( function (slot ) -> dataSlot { @@ -1050,7 +1050,7 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool to_string(_offset) + "_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { solAssert(_type.sizeOnStack() == 1, ""); return Whiskers(R"( function (slot) -> value { @@ -1071,7 +1071,7 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu string(_splitFunctionTypes ? "split_" : "") + "_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { solAssert(_type.sizeOnStack() == 1, ""); return Whiskers(R"( function (slot, offset) -> value { @@ -1101,7 +1101,7 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::opti (_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { if (_type.isValueType()) { solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size."); @@ -1141,7 +1141,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) string("write_to_memory_") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { solAssert(!dynamic_cast(&_type), ""); if (auto ref = dynamic_cast(&_type)) { @@ -1201,7 +1201,7 @@ string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool "extract_from_storage_value_dynamic" + string(_splitFunctionTypes ? "split_" : "") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { return Whiskers(R"( function (slot_value, offset) -> value { value := ((mul(offset, 8), slot_value)) @@ -1224,7 +1224,7 @@ string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offs "offset_" + to_string(_offset) + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { return Whiskers(R"( function (slot_value) -> value { value := ((slot_value)) @@ -1243,7 +1243,7 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl solUnimplementedAssert(!_splitFunctionTypes, ""); string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { Whiskers templ(R"( function (value) -> cleaned { cleaned := @@ -1275,7 +1275,7 @@ string YulUtilFunctions::prepareStoreFunction(Type const& _type) solUnimplementedAssert(_type.category() != Type::Category::Function, ""); string functionName = "prepare_store_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function (value) -> ret { ret := @@ -1293,7 +1293,7 @@ string YulUtilFunctions::prepareStoreFunction(Type const& _type) string YulUtilFunctions::allocationFunction() { string functionName = "allocateMemory"; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (size) -> memPtr { memPtr := mload() @@ -1314,7 +1314,7 @@ string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type) solUnimplementedAssert(!_type.isByteArray(), ""); string functionName = "allocate_memory_array_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (length) -> memPtr { memPtr := ((length)) @@ -1341,7 +1341,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) _from.identifier() + "_to_" + _to.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function (value) -> converted { @@ -1519,7 +1519,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) string YulUtilFunctions::cleanupFunction(Type const& _type) { string functionName = string("cleanup_") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function (value) -> cleaned { @@ -1606,7 +1606,7 @@ string YulUtilFunctions::cleanupFunction(Type const& _type) string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFailure) { string functionName = string("validator_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function (value) { if iszero() { } @@ -1667,7 +1667,7 @@ string YulUtilFunctions::packedHashFunction( size_t sizeOnStack = 0; for (Type const* t: _givenTypes) sizeOnStack += t->sizeOnStack(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( function () -> hash { let pos := mload() @@ -1688,7 +1688,7 @@ string YulUtilFunctions::forwardingRevertFunction() { bool forward = m_evmVersion.supportsReturndata(); string functionName = "revert_forward_" + to_string(forward); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { if (forward) return Whiskers(R"( function () { @@ -1715,7 +1715,7 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type) string const functionName = "decrement_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { u256 minintval; // Smallest admissible value to decrement @@ -1743,7 +1743,7 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type) string const functionName = "increment_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { u256 maxintval; // Biggest admissible value to increment @@ -1774,7 +1774,7 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) u256 const minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1; - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (_value) -> ret { if slt(_value, ) { revert(0,0) } @@ -1794,7 +1794,7 @@ string YulUtilFunctions::zeroValueFunction(Type const& _type) string const functionName = "zero_value_for_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function () -> ret { @@ -1810,7 +1810,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type) { string const functionName = "storage_set_to_zero_" + _type.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { if (_type.isValueType()) return Whiskers(R"( function (slot, offset) { @@ -1842,7 +1842,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const _from.identifier() + "_to_" + _to.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { if ( auto fromTuple = dynamic_cast(&_from), toTuple = dynamic_cast(&_to); fromTuple && toTuple && fromTuple->components().size() == toTuple->components().size() @@ -1950,7 +1950,7 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC if (_fromCalldata) solAssert(!_type.isDynamicallyEncoded(), ""); - return m_functionCollector->createFunction(functionName, [&] { + return m_functionCollector.createFunction(functionName, [&] { if (auto refType = dynamic_cast(&_type)) { solAssert(refType->sizeOnStack() == 1, ""); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 8c92d7b4f..cf50c785f 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -47,11 +47,11 @@ public: explicit YulUtilFunctions( langutil::EVMVersion _evmVersion, RevertStrings _revertStrings, - std::shared_ptr _functionCollector + MultiUseYulFunctionCollector& _functionCollector ): m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), - m_functionCollector(std::move(_functionCollector)) + m_functionCollector(_functionCollector) {} /// @returns a function that combines the address and selector to a single value @@ -306,7 +306,7 @@ private: langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; - std::shared_ptr m_functionCollector; + MultiUseYulFunctionCollector& m_functionCollector; }; } diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 43cb6a040..7184247f4 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -100,7 +100,7 @@ string IRGenerationContext::newYulVariable() string IRGenerationContext::internalDispatch(size_t _in, size_t _out) { string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out); - return m_functions->createFunction(funName, [&]() { + return m_functions.createFunction(funName, [&]() { Whiskers templ(R"( function (fun ) { switch fun diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index d4c29ab10..473b62482 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -56,11 +56,10 @@ public: ): m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), - m_optimiserSettings(std::move(_optimiserSettings)), - m_functions(std::make_shared()) + m_optimiserSettings(std::move(_optimiserSettings)) {} - std::shared_ptr functionCollector() const { return m_functions; } + MultiUseYulFunctionCollector& functionCollector() { return m_functions; } /// Sets the current inheritance hierarchy from derived to base. void setInheritanceHierarchy(std::vector _hierarchy) @@ -108,7 +107,7 @@ private: std::map m_localVariables; /// Storage offsets of state variables std::map> m_stateVariables; - std::shared_ptr m_functions; + MultiUseYulFunctionCollector m_functions; size_t m_varCounter = 0; }; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 9c2d8807c..8da576055 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -107,7 +107,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* fun: contract->definedFunctions()) generateFunction(*fun); - t("functions", m_context.functionCollector()->requestedFunctions()); + t("functions", m_context.functionCollector().requestedFunctions()); resetContext(_contract); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); @@ -116,7 +116,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* fun: contract->definedFunctions()) generateFunction(*fun); - t("runtimeFunctions", m_context.functionCollector()->requestedFunctions()); + t("runtimeFunctions", m_context.functionCollector().requestedFunctions()); return t.render(); } @@ -130,7 +130,7 @@ string IRGenerator::generate(Block const& _block) string IRGenerator::generateFunction(FunctionDefinition const& _function) { string functionName = m_context.functionName(_function); - return m_context.functionCollector()->createFunction(functionName, [&]() { + return m_context.functionCollector().createFunction(functionName, [&]() { Whiskers t(R"( function () { @@ -160,7 +160,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) solAssert(_varDecl.isStateVariable(), ""); if (auto const* mappingType = dynamic_cast(type)) - return m_context.functionCollector()->createFunction(functionName, [&]() { + return m_context.functionCollector().createFunction(functionName, [&]() { pair slot_offset = m_context.storageLocationOfVariable(_varDecl); solAssert(slot_offset.second == 0, ""); FunctionType funType(_varDecl); @@ -209,7 +209,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) { solUnimplementedAssert(type->isValueType(), ""); - return m_context.functionCollector()->createFunction(functionName, [&]() { + return m_context.functionCollector().createFunction(functionName, [&]() { pair slot_offset = m_context.storageLocationOfVariable(_varDecl); return Whiskers(R"( @@ -383,11 +383,10 @@ string IRGenerator::memoryInit() void IRGenerator::resetContext(ContractDefinition const& _contract) { solAssert( - m_context.functionCollector()->requestedFunctions().empty(), + m_context.functionCollector().requestedFunctions().empty(), "Reset context while it still had functions." ); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); - m_utils = YulUtilFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); for (auto const& var: ContractType(_contract).stateVariables()) From 38b219d140b646705cebf2a92bb2d9b2838a9866 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 4 Mar 2020 16:16:30 +0100 Subject: [PATCH 14/37] Throwing stack too deep ICE in case of calling encode with too many arguments instead of invalid opcode --- libsolidity/codegen/CompilerUtils.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 4c75081ff..e958da05e 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -539,6 +539,10 @@ void CompilerUtils::encodeToMemory( if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) { // copy tail pointer (=mem_end - mem_start) to memory + solAssert( + (2 + dynPointers) <= 16, + "Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables." + ); m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2; m_context << Instruction::SUB; m_context << dupInstruction(2 + dynPointers - thisDynPointer); From 7483c6f13e818c8c75aa2e11014d0d7b7bd38a31 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Thu, 5 Mar 2020 10:37:52 +0100 Subject: [PATCH 15/37] ossfuzz: Update README.md with steps to build fuzzers via docker --- test/tools/ossfuzz/README.md | 68 ++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/test/tools/ossfuzz/README.md b/test/tools/ossfuzz/README.md index 70469513c..c360eb9a8 100644 --- a/test/tools/ossfuzz/README.md +++ b/test/tools/ossfuzz/README.md @@ -1,8 +1,63 @@ ## Intro -[oss-fuzz][1] is Google's fuzzing infrastructure that performs continuous fuzzing. What this means is that, each and every upstream commit is automatically fetched by the infrastructure and fuzzed. +[oss-fuzz][1] is Google's fuzzing infrastructure that performs continuous fuzzing. What this means is that, each and every upstream commit is automatically fetched by the infrastructure and fuzzed on a daily basis. -## What does this directory contain? +## How to build fuzzers? + +We have multiple fuzzers, some based on string input and others on protobuf input. To build them, please do the following: + +- Create a local docker image from `Dockerfile.ubuntu1604.clang.ossfuzz` in the `.circleci/docker` sub-directory. Please note that this step is going to take at least an hour to complete. Therefore, it is recommended to do it when you are away from computer (and the computer is plugged to power since we do not want a battery drain). + +``` +$ cd .circleci/docker +$ docker build -t solidity-ossfuzz-local -f Dockerfile.ubuntu1604.clang.ossfuzz . +``` + +- Create and login into the docker container sourced from the image built in the previous step from the solidity parent directory + +``` +## Host +$ cd solidity +$ docker run -v `pwd`:/src/solidity -ti solidity-ossfuzz-local /bin/bash +## Docker shell +$ cd /src/solidity +``` + +- Run cmake and build fuzzer harnesses + +``` +## Docker shell +$ cd /src/solidity +$ rm -rf fuzzer-build && mkdir fuzzer-build && cd fuzzer-build +## Compile protobuf C++ bindings +$ protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz +$ protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz +## Run cmake +$ export CC=clang CXX=clang++ +$ cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} .. +$ make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j +``` + +## Why the elaborate docker image to build fuzzers? + +For the following reasons: + +- Fuzzing binaries **must** link against libc++ and not libstdc++ + - This is [because][2] (1) MemorySanitizer (which flags uses of uninitialized memory) depends on libc++; and (2) because libc++ is instrumented (to check for memory and type errors) and libstdc++ not, the former may find more bugs. + +- Linking against libc++ requires us to compile everything solidity depends on from source (and link these against libc++ as well) + +- To reproduce the compiler versions used by upstream oss-fuzz bots, we need to reuse their docker image containing the said compiler versions + +- Some fuzzers depend on libprotobuf, libprotobuf-mutator, libevmone etc. which may not be available locally; even if they were they might not be the right versions + +## What is LIB\_FUZZING\_ENGINE? + +oss-fuzz contains multiple fuzzer back-ends i.e., fuzzers. Each back-end may require different linker flags. oss-fuzz builder bot defines the correct linker flags via a bash environment variable called `LIB_FUZZING_ENGINE`. + +For the solidity ossfuzz CI build, we use the libFuzzer back-end. This back-end requires us to manually set the `LIB_FUZZING_ENGINE` to `-fsanitize=fuzzer`. + +## What does the ossfuzz directory contain? To help oss-fuzz do this, we (as project maintainers) need to provide the following: @@ -17,12 +72,5 @@ To be consistent and aid better evaluation of the utility of the fuzzing diction - Incomplete tokens including function calls such as `msg.sender.send()` are abbreviated `.send(` to provide some leeway to the fuzzer to sythesize variants such as `address(this).send()` - Language keywords are suffixed by a whitespace with the exception of those that end a line of code such as `break;` and `continue;` -## What is libFuzzingEngine.a? - -`libFuzzingEngine.a` is an oss-fuzz-related dependency. It is present in the Dockerized environment in which Solidity's oss-fuzz code will be built. - -## Is this directory relevant for routine Solidity CI builds? - -No. This is the reason why the `add_subdirectory(ossfuzz)` cmake directive is nested under the `if (OSSFUZZ)` predicate. `OSSFUZZ` is a solidity-wide cmake option that is invoked by the ossfuzz solidity-builder-bot in order to compile solidity fuzzer binaries. - [1]: https://github.com/google/oss-fuzz +[2]: https://github.com/google/oss-fuzz/issues/1114#issuecomment-360660201 From 420f57aec301e7629721d8220699c7d6239e7333 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 5 Mar 2020 12:56:14 +0100 Subject: [PATCH 16/37] Fix yul ast import for blocks, switches and string literals. --- Changelog.md | 1 + libsolidity/ast/AsmJsonImporter.cpp | 19 ++- scripts/ASTImportTest.sh | 3 +- .../ASTJSON/assembly/empty_block.json | 95 ++++++++++++++ .../ASTJSON/assembly/empty_block.sol | 7 + .../ASTJSON/assembly/empty_block_legacy.json | 123 ++++++++++++++++++ .../ASTJSON/assembly/switch_default.json | 116 +++++++++++++++++ .../ASTJSON/assembly/switch_default.sol | 7 + .../assembly/switch_default_legacy.json | 123 ++++++++++++++++++ 9 files changed, 488 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/ASTJSON/assembly/empty_block.json create mode 100644 test/libsolidity/ASTJSON/assembly/empty_block.sol create mode 100644 test/libsolidity/ASTJSON/assembly/empty_block_legacy.json create mode 100644 test/libsolidity/ASTJSON/assembly/switch_default.json create mode 100644 test/libsolidity/ASTJSON/assembly/switch_default.sol create mode 100644 test/libsolidity/ASTJSON/assembly/switch_default_legacy.json diff --git a/Changelog.md b/Changelog.md index d187d2527..81522f511 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,7 @@ Bugfixes: * isoltest: Added new keyword `wei` to express function value in semantic tests * Standard-JSON-Interface: Fix a bug related to empty filenames and imports. * SMTChecker: Fix internal errors when analysing tuples. + * Yul AST Import: correctly import blocks as statements, switch statements and string literals. ### 0.6.3 (2020-02-18) diff --git a/libsolidity/ast/AsmJsonImporter.cpp b/libsolidity/ast/AsmJsonImporter.cpp index a47ea27f7..302388841 100644 --- a/libsolidity/ast/AsmJsonImporter.cpp +++ b/libsolidity/ast/AsmJsonImporter.cpp @@ -104,6 +104,8 @@ yul::Statement AsmJsonImporter::createStatement(Json::Value const& _node) return createContinue(_node); else if (nodeType == "Leave") return createLeave(_node); + else if (nodeType == "Block") + return createBlock(_node); else astAssert(false, "Invalid nodeType as statement"); } @@ -158,10 +160,9 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) lit.value = YulString{member(_node, "value").asString()}; lit.type= YulString{member(_node, "type").asString()}; - langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; - if (kind == "number") { + langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; lit.kind = yul::LiteralKind::Number; astAssert( scanner.currentToken() == Token::Number, @@ -170,6 +171,7 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) } else if (kind == "bool") { + langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; lit.kind = yul::LiteralKind::Boolean; astAssert( scanner.currentToken() == Token::TrueLiteral || @@ -180,7 +182,10 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) else if (kind == "string") { lit.kind = yul::LiteralKind::String; - astAssert(scanner.currentToken() == Token::StringLiteral, "Expected string literal!"); + astAssert( + lit.value.str().size() <= 32, + "String literal too long (" + to_string(lit.value.str().size()) + " > 32)" + ); } else solAssert(false, "unknown type of literal"); @@ -268,7 +273,11 @@ yul::If AsmJsonImporter::createIf(Json::Value const& _node) yul::Case AsmJsonImporter::createCase(Json::Value const& _node) { auto caseStatement = createAsmNode(_node); - caseStatement.value = member(_node, "value").asString() == "default" ? nullptr : make_unique(createLiteral(member(_node, "value"))); + auto const& value = member(_node, "value"); + if (value.isString()) + astAssert(value.asString() == "default", "Expected default case"); + else + caseStatement.value = make_unique(createLiteral(value)); caseStatement.body = createBlock(member(_node, "body")); return caseStatement; } @@ -276,7 +285,7 @@ yul::Case AsmJsonImporter::createCase(Json::Value const& _node) yul::Switch AsmJsonImporter::createSwitch(Json::Value const& _node) { auto switchStatement = createAsmNode(_node); - switchStatement.expression = make_unique(createExpression(member(_node, "value"))); + switchStatement.expression = make_unique(createExpression(member(_node, "expression"))); for (auto const& var: member(_node, "cases")) switchStatement.cases.emplace_back(createCase(var)); return switchStatement; diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 800c5a514..f9172df0c 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -10,6 +10,7 @@ SOLC=${REPO_ROOT}/${SOLIDITY_BUILD_DIR}/solc/solc SPLITSOURCES=${REPO_ROOT}/scripts/splitSources.py SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests" +ASTJSONTESTS_DIR="${REPO_ROOT}/test/libsolidity/ASTJSON" NSOURCES="$(find $SYNTAXTESTS_DIR -type f | wc -l)" # DEV_DIR="${REPO_ROOT}/../tmp/contracts/" @@ -75,7 +76,7 @@ echo "Looking at $NSOURCES .sol files..." WORKINGDIR=$PWD # for solfile in $(find $DEV_DIR -name *.sol) -for solfile in $(find $SYNTAXTESTS_DIR -name *.sol) +for solfile in $(find $SYNTAXTESTS_DIR $ASTJSONTESTS_DIR -name *.sol) do echo -n "." # create a temporary sub-directory diff --git a/test/libsolidity/ASTJSON/assembly/empty_block.json b/test/libsolidity/ASTJSON/assembly/empty_block.json new file mode 100644 index 000000000..575b1f7f4 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_block.json @@ -0,0 +1,95 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 6 + ] + }, + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 6, + "linearizedBaseContracts": + [ + 6 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:31:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:6:1", + "statements": + [ + { + "nodeType": "YulBlock", + "src": "63:2:1", + "statements": [] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:15:1" + } + ] + }, + "documentation": null, + "functionSelector": "e2179b8e", + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nodeType": "FunctionDefinition", + "overrides": null, + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "scope": 6, + "src": "17:56:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + } + ], + "scope": 7, + "src": "0:75:1" + } + ], + "src": "0:76:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/empty_block.sol b/test/libsolidity/ASTJSON/assembly/empty_block.sol new file mode 100644 index 000000000..23c2babbe --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_block.sol @@ -0,0 +1,7 @@ +contract C { + function g() view public { + assembly { {} } + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json b/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json new file mode 100644 index 000000000..108a76a00 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json @@ -0,0 +1,123 @@ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 6 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 6 + ], + "name": "C", + "scope": 7 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "functionSelector": "e2179b8e", + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "g", + "overrides": null, + "scope": 6, + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "27:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "42:0:1" + }, + { + "children": + [ + { + "attributes": + { + "evmVersion": %EVMVERSION%, + "externalReferences": + [ + null + ], + "operations": "{ { } }" + }, + "children": [], + "id": 3, + "name": "InlineAssembly", + "src": "52:15:1" + } + ], + "id": 4, + "name": "Block", + "src": "42:31:1" + } + ], + "id": 5, + "name": "FunctionDefinition", + "src": "17:56:1" + } + ], + "id": 6, + "name": "ContractDefinition", + "src": "0:75:1" + } + ], + "id": 7, + "name": "SourceUnit", + "src": "0:76:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.json b/test/libsolidity/ASTJSON/assembly/switch_default.json new file mode 100644 index 000000000..c0bf26438 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/switch_default.json @@ -0,0 +1,116 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 6 + ] + }, + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 6, + "linearizedBaseContracts": + [ + 6 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:48:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:23:1", + "statements": + [ + { + "cases": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "80:2:1", + "statements": [] + }, + "nodeType": "YulCase", + "src": "72:10:1", + "value": "default" + } + ], + "expression": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "70:1:1", + "type": "", + "value": "0" + }, + "nodeType": "YulSwitch", + "src": "63:19:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:32:1" + } + ] + }, + "documentation": null, + "functionSelector": "e2179b8e", + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nodeType": "FunctionDefinition", + "overrides": null, + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "scope": 6, + "src": "17:73:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + } + ], + "scope": 7, + "src": "0:92:1" + } + ], + "src": "0:93:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.sol b/test/libsolidity/ASTJSON/assembly/switch_default.sol new file mode 100644 index 000000000..8bfba2c11 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/switch_default.sol @@ -0,0 +1,7 @@ +contract C { + function g() view public { + assembly { switch 0 default {} } + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json new file mode 100644 index 000000000..2d5caa0fa --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json @@ -0,0 +1,123 @@ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 6 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 6 + ], + "name": "C", + "scope": 7 + }, + "children": + [ + { + "attributes": + { + "documentation": null, + "functionSelector": "e2179b8e", + "implemented": true, + "isConstructor": false, + "kind": "function", + "modifiers": + [ + null + ], + "name": "g", + "overrides": null, + "scope": 6, + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + "children": + [ + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 1, + "name": "ParameterList", + "src": "27:2:1" + }, + { + "attributes": + { + "parameters": + [ + null + ] + }, + "children": [], + "id": 2, + "name": "ParameterList", + "src": "42:0:1" + }, + { + "children": + [ + { + "attributes": + { + "evmVersion": %EVMVERSION%, + "externalReferences": + [ + null + ], + "operations": "{\n switch 0\n default { }\n}" + }, + "children": [], + "id": 3, + "name": "InlineAssembly", + "src": "52:32:1" + } + ], + "id": 4, + "name": "Block", + "src": "42:48:1" + } + ], + "id": 5, + "name": "FunctionDefinition", + "src": "17:73:1" + } + ], + "id": 6, + "name": "ContractDefinition", + "src": "0:92:1" + } + ], + "id": 7, + "name": "SourceUnit", + "src": "0:93:1" +} From 7f38cbb91d320c56118fc79dec4d4a711c53d2e6 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 6 Mar 2020 10:44:51 +0100 Subject: [PATCH 17/37] Fix calling unimplemented base function. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 27 +++++++++---------- libsolidity/ast/Types.cpp | 10 ++++++- .../functionCall/call_unimplemented_base.sol | 14 ++++++++++ .../functionCalls/call_unimplemented_base.sol | 8 ++++++ 5 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol create mode 100644 test/libsolidity/syntaxTests/functionCalls/call_unimplemented_base.sol diff --git a/Changelog.md b/Changelog.md index d187d2527..693874608 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: + * Inheritance: Fix incorrect error on calling unimplemented base functions. * isoltest: Added new keyword `wei` to express function value in semantic tests * Standard-JSON-Interface: Fix a bug related to empty filenames and imports. * SMTChecker: Fix internal errors when analysing tuples. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f713ea289..5cf003bed 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1703,22 +1703,21 @@ void TypeChecker::typeCheckFunctionCall( if (_functionType->kind() == FunctionType::Kind::Declaration) { - m_errorReporter.typeError( - _functionCall.location(), - "Cannot call function via contract type name." - ); + if ( + m_scope->derivesFrom(*_functionType->declaration().annotation().contract) && + !dynamic_cast(_functionType->declaration()).isImplemented() + ) + m_errorReporter.typeError( + _functionCall.location(), + "Cannot call unimplemented base function." + ); + else + m_errorReporter.typeError( + _functionCall.location(), + "Cannot call function via contract type name." + ); return; } - if (_functionType->kind() == FunctionType::Kind::Internal && _functionType->hasDeclaration()) - if (auto const* functionDefinition = dynamic_cast(&_functionType->declaration())) - // functionDefinition->annotation().contract != m_scope ensures that this is a qualified access, - // e.g. ``A.f();`` instead of a simple function call like ``f();`` (the latter is valid for unimplemented - // functions). - if (functionDefinition->annotation().contract != m_scope && !functionDefinition->isImplemented()) - m_errorReporter.typeError( - _functionCall.location(), - "Cannot call unimplemented base function." - ); // Check for unsupported use of bare static call if ( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c4d4c9fd4..258088adf 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3471,7 +3471,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current continue; if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts()) - members.emplace_back(declaration->name(), declaration->type(), declaration); + { + if ( + auto const* functionDefinition = dynamic_cast(declaration); + functionDefinition && !functionDefinition->isImplemented() + ) + members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); + else + members.emplace_back(declaration->name(), declaration->type(), declaration); + } else if ( (contract.isLibrary() && declaration->isVisibleAsLibraryMember()) || declaration->isVisibleViaContractTypeAccess() diff --git a/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol b/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol new file mode 100644 index 000000000..3041f0e4b --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol @@ -0,0 +1,14 @@ +abstract contract I +{ + function a() internal view virtual returns(uint256); +} +abstract contract V is I +{ + function b() public view returns(uint256) { return a(); } +} +contract C is V +{ + function a() internal view override returns (uint256) { return 42;} +} +// ---- +// b() -> 42 diff --git a/test/libsolidity/syntaxTests/functionCalls/call_unimplemented_base.sol b/test/libsolidity/syntaxTests/functionCalls/call_unimplemented_base.sol new file mode 100644 index 000000000..82c6eecb9 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/call_unimplemented_base.sol @@ -0,0 +1,8 @@ +abstract contract I +{ + function a() internal view virtual returns(uint256); +} +abstract contract V is I +{ + function b() public view returns(uint256) { return a(); } +} From 092827b7ad062fd59ec294a326c6b306c4ce62e3 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Thu, 5 Mar 2020 19:26:30 +0100 Subject: [PATCH 18/37] Adding sol->yul for f.selector and f.address --- libsolidity/ast/Types.cpp | 5 ++++- .../codegen/ir/IRGeneratorForStatements.cpp | 12 ++++++++++-- .../semanticTests/viaYul/function_address.sol | 17 +++++++++++++++++ .../semanticTests/viaYul/function_selector.sol | 13 +++++++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/function_address.sol create mode 100644 test/libsolidity/semanticTests/viaYul/function_selector.sol diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c4d4c9fd4..78a504ddc 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2924,7 +2924,10 @@ vector> FunctionType::makeStackItems() const { case Kind::External: case Kind::DelegateCall: - slots = {make_tuple("address", TypeProvider::address()), make_tuple("functionIdentifier", TypeProvider::fixedBytes(4))}; + slots = { + make_tuple("address", TypeProvider::address()), + make_tuple("functionIdentifier", TypeProvider::uint(32)) + }; break; case Kind::BareCall: case Kind::BareCallCode: diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 38d16eb69..b40768be2 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -800,11 +800,19 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) case Type::Category::Function: if (member == "selector") { - solUnimplementedAssert(false, ""); + solUnimplementedAssert( + dynamic_cast(*_memberAccess.expression().annotation().type).kind() == + FunctionType::Kind::External, "" + ); + define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionIdentifier")); } else if (member == "address") { - solUnimplementedAssert(false, ""); + solUnimplementedAssert( + dynamic_cast(*_memberAccess.expression().annotation().type).kind() == + FunctionType::Kind::External, "" + ); + define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("address")); } else solAssert( diff --git a/test/libsolidity/semanticTests/viaYul/function_address.sol b/test/libsolidity/semanticTests/viaYul/function_address.sol new file mode 100644 index 000000000..402ad1481 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/function_address.sol @@ -0,0 +1,17 @@ +contract C { + function f() external returns (address) { + return this.f.address; + } + function g() external returns (bool) { + return this.f.address == address(this); + } + function h(function() external a) public returns (address) { + return a.address; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x0fdd67305928fcac8d213d1e47bfa6165cd0b87b +// g() -> true +// h(function): left(0x1122334400112233445566778899AABBCCDDEEFF42424242) -> 0x1122334400112233445566778899AABBCCDDEEFF diff --git a/test/libsolidity/semanticTests/viaYul/function_selector.sol b/test/libsolidity/semanticTests/viaYul/function_selector.sol new file mode 100644 index 000000000..40faef1dd --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/function_selector.sol @@ -0,0 +1,13 @@ +contract C { + function f() external returns (bytes4) { + return this.f.selector; + } + function h(function() external a) public returns (bytes4) { + return a.selector; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> left(0x26121ff0) +// h(function): left(0x1122334400112233445566778899AABBCCDDEEFF42424242) -> left(0x42424242) From cdfb8723892d0b32bfa98de5a558f3f244d8fc9f Mon Sep 17 00:00:00 2001 From: chriseth Date: Sun, 8 Mar 2020 19:15:21 +0100 Subject: [PATCH 19/37] [DOCS] Fix pre-computation of salted address. --- docs/control-structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 407d2523c..d5448d0b3 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -252,7 +252,7 @@ which only need to be created if there is a dispute. /// This complicated expression just tells you how the address /// can be pre-computed. It is just there for illustration. /// You actually only need ``new D{salt: salt}(arg)``. - address predictedAddress = address(bytes20(keccak256(abi.encodePacked( + address predictedAddress = address(uint(keccak256(abi.encodePacked( byte(0xff), address(this), salt, From 2b804017fe25b230e28c8c854a50259e1cb64f8c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Mar 2020 09:42:03 +0100 Subject: [PATCH 20/37] Fix yul links. --- docs/assembly.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 7e498cd5a..f908c0f57 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -11,7 +11,7 @@ You can interleave Solidity statements with inline assembly in a language close to the one of the Ethereum virtual machine. This gives you more fine-grained control, which is especially useful when you are enhancing the language by writing libraries. -The language used for inline assembly in Solidity is called `Yul `_ +The language used for inline assembly in Solidity is called :ref:`Yul ` and it is documented in its own section. This section will only cover how the inline assembly code can interface with the surrounding Solidity code. @@ -24,7 +24,7 @@ how the inline assembly code can interface with the surrounding Solidity code. An inline assembly block is marked by ``assembly { ... }``, where the code inside -the curly braces is code in the `Yul `_ language. +the curly braces is code in the :ref:`Yul ` language. The inline assembly code can access local Solidity variables as explained below. From c8cbb980026413fb88bedc9cbdafd3cd979da215 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Mar 2020 12:18:26 +0100 Subject: [PATCH 21/37] [Sol2Yul] Fixes appendExternalFunctionCall for argumentStrings.size() == 0. --- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 38d16eb69..f35b81f4e 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -47,6 +47,7 @@ using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; +using namespace std::string_literals; namespace { @@ -1204,7 +1205,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( argumentTypes.emplace_back(&type(*arg)); argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList()); } - string argumentString = ", " + joinHumanReadable(argumentStrings); + string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings)); solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); From 2153a1ef1df8a004bc3fed4c0afca25b19a2ad81 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 9 Mar 2020 10:27:38 +0100 Subject: [PATCH 22/37] Update test/tools/ossfuzz/README.md Address review comments --- test/tools/ossfuzz/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tools/ossfuzz/README.md b/test/tools/ossfuzz/README.md index c360eb9a8..944595a3f 100644 --- a/test/tools/ossfuzz/README.md +++ b/test/tools/ossfuzz/README.md @@ -6,14 +6,14 @@ We have multiple fuzzers, some based on string input and others on protobuf input. To build them, please do the following: -- Create a local docker image from `Dockerfile.ubuntu1604.clang.ossfuzz` in the `.circleci/docker` sub-directory. Please note that this step is going to take at least an hour to complete. Therefore, it is recommended to do it when you are away from computer (and the computer is plugged to power since we do not want a battery drain). +- Create a local docker image from `Dockerfile.ubuntu1604.clang.ossfuzz` in the `.circleci/docker` sub-directory. Please note that this step is likely to take at least an hour to complete. Therefore, it is recommended to do it when you are away from the computer (and the computer is plugged to power since we do not want a battery drain). ``` $ cd .circleci/docker $ docker build -t solidity-ossfuzz-local -f Dockerfile.ubuntu1604.clang.ossfuzz . ``` -- Create and login into the docker container sourced from the image built in the previous step from the solidity parent directory +- Login to the docker container sourced from the image built in the previous step from the solidity parent directory ``` ## Host From e210026e7455f61a93a2b89bdc50f4d2f5c2097a Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 3 Mar 2020 12:20:04 +0100 Subject: [PATCH 23/37] [Sol2Yul] Implements function-to-function cast. --- libsolidity/codegen/YulUtilFunctions.cpp | 29 +++++++++++++++++++ .../viaYul/conversion/function_cast.sol | 23 +++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/libsolidity/semanticTests/viaYul/conversion/function_cast.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 0472dd588..a2af5f777 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1333,6 +1333,35 @@ string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type) string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) { + if (_from.category() == Type::Category::Function) + { + solAssert(_to.category() == Type::Category::Function, ""); + FunctionType const& fromType = dynamic_cast(_from); + FunctionType const& targetType = dynamic_cast(_to); + solAssert( + fromType.isImplicitlyConvertibleTo(targetType) && + fromType.sizeOnStack() == targetType.sizeOnStack() && + (fromType.kind() == FunctionType::Kind::Internal || fromType.kind() == FunctionType::Kind::External) && + fromType.kind() == targetType.kind(), + "Invalid function type conversion requested." + ); + string const functionName = + "convert_" + + _from.identifier() + + "_to_" + + _to.identifier(); + return m_functionCollector->createFunction(functionName, [&]() { + return Whiskers(R"( + function (addr, functionId) -> outAddr, outFunctionId { + outAddr := addr + outFunctionId := functionId + } + )") + ("functionName", functionName) + .render(); + }); + } + if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1) return conversionFunctionSpecial(_from, _to); diff --git a/test/libsolidity/semanticTests/viaYul/conversion/function_cast.sol b/test/libsolidity/semanticTests/viaYul/conversion/function_cast.sol new file mode 100644 index 000000000..08462c5f8 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/conversion/function_cast.sol @@ -0,0 +1,23 @@ +contract C { + function f(uint x) public pure returns (uint) { + return 2 * x; + } + function g() public view returns (function (uint) external returns (uint)) { + return this.f; + } + function h(uint x) public returns (uint) { + return this.g()(x) + 1; + } + function t() external view returns ( + function(uint) external returns (uint) a, + function(uint) external view returns (uint) b) { + a = this.f; + b = this.f; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 2 -> 4 +// h(uint256): 2 -> 5 +// t() -> 0xFDD67305928FCAC8D213D1E47BFA6165CD0B87BB3DE648B0000000000000000, 0xFDD67305928FCAC8D213D1E47BFA6165CD0B87BB3DE648B0000000000000000 From 105d89bea8119ab0dc9780b3fbf0d02a6d0ca737 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 9 Mar 2020 11:42:34 +0100 Subject: [PATCH 24/37] Compilation fix. --- libsolidity/codegen/YulUtilFunctions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 38ed88f4c..83e8272fe 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1350,7 +1350,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) _from.identifier() + "_to_" + _to.identifier(); - return m_functionCollector->createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (addr, functionId) -> outAddr, outFunctionId { outAddr := addr From 37e01a19c0c24f32ecdb978aa7c17db71fd5c769 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sun, 8 Mar 2020 17:27:43 +0100 Subject: [PATCH 25/37] Fix scoping following try/catch. --- Changelog.md | 1 + libsolidity/analysis/ReferencesResolver.cpp | 16 ++++++++++++++++ libsolidity/analysis/ReferencesResolver.h | 2 ++ .../libsolidity/syntaxTests/tryCatch/scoping.sol | 10 ++++++++++ 4 files changed, 29 insertions(+) create mode 100644 test/libsolidity/syntaxTests/tryCatch/scoping.sol diff --git a/Changelog.md b/Changelog.md index a86b3ac59..92e31b9dc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Compiler Features: Bugfixes: * Inheritance: Fix incorrect error on calling unimplemented base functions. * isoltest: Added new keyword `wei` to express function value in semantic tests + * Reference Resolver: Fix scoping issue following try/catch statements. * Standard-JSON-Interface: Fix a bug related to empty filenames and imports. * SMTChecker: Fix internal errors when analysing tuples. * Yul AST Import: correctly import blocks as statements, switch statements and string literals. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 4fe2d01c9..6867f049c 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -67,6 +67,22 @@ void ReferencesResolver::endVisit(Block const& _block) m_resolver.setScope(_block.scope()); } +bool ReferencesResolver::visit(TryCatchClause const& _tryCatchClause) +{ + if (!m_resolveInsideCode) + return false; + m_resolver.setScope(&_tryCatchClause); + return true; +} + +void ReferencesResolver::endVisit(TryCatchClause const& _tryCatchClause) +{ + if (!m_resolveInsideCode) + return; + + m_resolver.setScope(_tryCatchClause.scope()); +} + bool ReferencesResolver::visit(ForStatement const& _for) { if (!m_resolveInsideCode) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index c560be31e..488562f21 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -70,6 +70,8 @@ private: bool visit(Block const& _block) override; void endVisit(Block const& _block) override; + bool visit(TryCatchClause const& _tryCatchClause) override; + void endVisit(TryCatchClause const& _tryCatchClause) override; bool visit(ForStatement const& _for) override; void endVisit(ForStatement const& _for) override; void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; diff --git a/test/libsolidity/syntaxTests/tryCatch/scoping.sol b/test/libsolidity/syntaxTests/tryCatch/scoping.sol new file mode 100644 index 000000000..3c902c336 --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/scoping.sol @@ -0,0 +1,10 @@ +contract Test { + // This checks a scoping error, + // the variable "a" was not visible + // at the assignment. + function test(address _ext) external { + try Test(_ext).test(_ext) {} catch {} + uint a = 1; + a = 3; + } +} From 29b770c43439addcb29fd79ddbf6bc37dfed0f5e Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 18 Feb 2020 17:13:13 +0100 Subject: [PATCH 26/37] Introduced TestCase::shouldRun(). --- test/TestCase.cpp | 35 ++++++++++++++++++----------- test/TestCase.h | 9 +++++--- test/boostTest.cpp | 3 ++- test/libsolidity/SMTCheckerTest.cpp | 23 ++++++++----------- test/libsolidity/SMTCheckerTest.h | 2 -- test/libsolidity/SemanticTest.cpp | 10 +++------ test/libsolidity/SemanticTest.h | 2 -- test/libyul/SyntaxTest.cpp | 9 +++----- test/libyul/SyntaxTest.h | 5 +---- test/tools/isoltest.cpp | 3 ++- 10 files changed, 48 insertions(+), 53 deletions(-) diff --git a/test/TestCase.cpp b/test/TestCase.cpp index 01dc51e7c..6b0a16e27 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -15,6 +15,7 @@ along with solidity. If not, see . */ +#include #include #include @@ -52,14 +53,18 @@ bool TestCase::isTestFilename(boost::filesystem::path const& _filename) !boost::starts_with(_filename.string(), "."); } -bool TestCase::validateSettings(langutil::EVMVersion) +void TestCase::validateSettings() { if (!m_settings.empty()) throw runtime_error( "Unknown setting(s): " + util::joinHumanReadable(m_settings | boost::adaptors::map_keys) ); - return true; +} + +bool TestCase::shouldRun() +{ + return m_shouldRun; } pair, size_t> TestCase::parseSourcesAndSettingsWithLineNumbers(istream& _stream) @@ -157,20 +162,19 @@ void TestCase::expect(string::iterator& _it, string::iterator _end, string::valu ++_it; } -bool EVMVersionRestrictedTestCase::validateSettings(langutil::EVMVersion _evmVersion) +void EVMVersionRestrictedTestCase::validateSettings() { if (!m_settings.count("EVMVersion")) - return true; + return; string versionString = m_settings["EVMVersion"]; m_validatedSettings["EVMVersion"] = versionString; m_settings.erase("EVMVersion"); - if (!TestCase::validateSettings(_evmVersion)) - return false; + TestCase::validateSettings(); if (versionString.empty()) - return true; + return; string comparator; size_t versionBegin = 0; @@ -188,18 +192,23 @@ bool EVMVersionRestrictedTestCase::validateSettings(langutil::EVMVersion _evmVer if (!version) BOOST_THROW_EXCEPTION(runtime_error{"Invalid EVM version: \"" + versionString + "\""}); + langutil::EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); + bool comparisonResult; if (comparator == ">") - return _evmVersion > version; + comparisonResult = evmVersion > version; else if (comparator == ">=") - return _evmVersion >= version; + comparisonResult = evmVersion >= version; else if (comparator == "<") - return _evmVersion < version; + comparisonResult = evmVersion < version; else if (comparator == "<=") - return _evmVersion <= version; + comparisonResult = evmVersion <= version; else if (comparator == "=") - return _evmVersion == version; + comparisonResult = evmVersion == version; else if (comparator == "!") - return !(_evmVersion == version); + comparisonResult = !(evmVersion == version); else BOOST_THROW_EXCEPTION(runtime_error{"Invalid EVM comparator: \"" + comparator + "\""}); + + if (!comparisonResult) + m_shouldRun = false; } diff --git a/test/TestCase.h b/test/TestCase.h index 927490537..d6afff8b8 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -70,10 +70,12 @@ public: /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). + virtual void validateSettings(); + /// Returns true, if the test case is supported in the current environment and false /// otherwise which causes this test to be skipped. /// This might check e.g. for restrictions on the EVM version. - virtual bool validateSettings(langutil::EVMVersion /*_evmVersion*/); + bool shouldRun(); protected: std::pair, std::size_t> parseSourcesAndSettingsWithLineNumbers(std::istream& _file); @@ -102,13 +104,14 @@ protected: std::map m_settings; /// Updated settings after validation. std::map m_validatedSettings; + + bool m_shouldRun = true; }; class EVMVersionRestrictedTestCase: public TestCase { public: - /// Returns true, if the test case is supported for EVM version @arg _evmVersion, false otherwise. - bool validateSettings(langutil::EVMVersion _evmVersion) override; + void validateSettings() override; }; } diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 2b5606546..3137a5085 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -94,7 +94,8 @@ int registerTests( { stringstream errorStream; auto testCase = _testCaseCreator(config); - if (testCase->validateSettings(solidity::test::CommonOptions::get().evmVersion())) + testCase->validateSettings(); + if (testCase->shouldRun()) switch (testCase->run(errorStream)) { case TestCase::TestResult::Success: diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 7db668a2d..912349182 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -44,6 +44,15 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _ev } else m_enabledSolvers = smt::SMTSolverChoice::All(); + + auto available = ModelChecker::availableSolvers(); + if (!available.z3) + m_enabledSolvers.z3 = false; + if (!available.cvc4) + m_enabledSolvers.cvc4 = false; + + if (m_enabledSolvers.none()) + m_shouldRun = false; } TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) @@ -55,17 +64,3 @@ TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePr return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; } - -bool SMTCheckerTest::validateSettings(langutil::EVMVersion _evmVersion) -{ - auto available = ModelChecker::availableSolvers(); - if (!available.z3) - m_enabledSolvers.z3 = false; - if (!available.cvc4) - m_enabledSolvers.cvc4 = false; - - if (m_enabledSolvers.none()) - return false; - - return SyntaxTest::validateSettings(_evmVersion); -} diff --git a/test/libsolidity/SMTCheckerTest.h b/test/libsolidity/SMTCheckerTest.h index ce28409e6..ad38af25c 100644 --- a/test/libsolidity/SMTCheckerTest.h +++ b/test/libsolidity/SMTCheckerTest.h @@ -37,8 +37,6 @@ public: TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; - bool validateSettings(langutil::EVMVersion _evmVersion) override; - protected: /// This is set via option SMTSolvers in the test. /// The possible options are `all`, `z3`, `cvc4`, `none`, diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index b2c4628da..97c83a3e5 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -71,6 +71,9 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer m_settings.erase("ABIEncoderV1Only"); } + if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) + m_shouldRun = false; + if (m_settings.count("revertStrings")) { auto revertStrings = revertStringsFromString(m_settings["revertStrings"]); @@ -90,13 +93,6 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } -bool SemanticTest::validateSettings(langutil::EVMVersion _evmVersion) -{ - if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) - return false; - return EVMVersionRestrictedTestCase::validateSettings(_evmVersion); -} - TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { for(bool compileViaYul: set{!m_runWithoutYul, m_runWithYul}) diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 5cd8e5cc7..0ea486cad 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -44,8 +44,6 @@ public: explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion); - bool validateSettings(langutil::EVMVersion _evmVersion) override; - TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix = "") const override; diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index be1b9990b..75e38c844 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -114,13 +114,12 @@ void SyntaxTest::parseAndAnalyze() } -bool SyntaxTest::validateSettings(langutil::EVMVersion _evmVersion) +void SyntaxTest::validateSettings() { - if (!CommonSyntaxTest::validateSettings(_evmVersion)) - return false; + CommonSyntaxTest::validateSettings(); if (!m_settings.count("dialect")) - return true; + return; string const dialect = m_settings["dialect"]; m_validatedSettings["dialect"] = dialect; @@ -134,6 +133,4 @@ bool SyntaxTest::validateSettings(langutil::EVMVersion _evmVersion) joinHumanReadable(validDialectNames(), ", ", " and ") + "." }); - - return true; } diff --git a/test/libyul/SyntaxTest.h b/test/libyul/SyntaxTest.h index e355a5932..087a6326c 100644 --- a/test/libyul/SyntaxTest.h +++ b/test/libyul/SyntaxTest.h @@ -42,10 +42,7 @@ public: /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). - /// Returns true, if the test case is supported in the current environment and false - /// otherwise which causes this test to be skipped. - /// This might check e.g. for restrictions on the EVM version. - bool validateSettings(langutil::EVMVersion _evmVersion) override; + void validateSettings() override; protected: void parseAndAnalyze() override; }; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index a8c8748fb..e439aba64 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -161,7 +161,8 @@ TestTool::Result TestTool::process() (AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush(); m_test = m_testCaseCreator(TestCase::Config{m_path.string(), m_options.evmVersion()}); - if (m_test->validateSettings(m_options.evmVersion())) + m_test->validateSettings(); + if (m_test->shouldRun()) switch (TestCase::TestResult result = m_test->run(outputMessages, " ", formatted)) { case TestCase::TestResult::Success: From 809e3503baa8b1aa4d2bdcfb81d0ae8213fb1878 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 5 Mar 2020 10:47:01 +0100 Subject: [PATCH 27/37] Control flow analysis for inline assembly. --- Changelog.md | 1 + libevmasm/SemanticInformation.cpp | 20 ++ libevmasm/SemanticInformation.h | 2 + libsolidity/analysis/ControlFlowAnalyzer.cpp | 10 +- libsolidity/analysis/ControlFlowAnalyzer.h | 4 +- libsolidity/analysis/ControlFlowBuilder.cpp | 186 +++++++++++++++--- libsolidity/analysis/ControlFlowBuilder.h | 25 ++- libsolidity/analysis/ControlFlowGraph.h | 22 ++- libyul/ControlFlowSideEffects.h | 38 ++++ libyul/Dialect.h | 2 + libyul/backends/evm/EVMDialect.cpp | 2 + libyul/backends/wasm/WasmDialect.cpp | 16 +- .../controlFlow/leave_inside_function.sol | 11 ++ .../controlFlow/leave_outside_function.sol | 10 + .../storageReturn/assembly/for_err.sol | 29 +++ .../storageReturn/assembly/for_fine.sol | 15 ++ .../storageReturn/assembly/if_err.sol | 11 ++ .../assembly/returning_function.sol | 13 ++ .../assembly/reverting_function.sol | 13 ++ .../storageReturn/assembly/stub.sol | 10 + .../storageReturn/assembly/switch_err.sol | 27 +++ .../storageReturn/assembly/switch_fine.sol | 25 +++ .../uninitializedAccess/assembly.sol | 2 +- .../assembly/double_revert.sol | 17 ++ .../unreachableCode/assembly/for_break.sol | 13 ++ .../unreachableCode/assembly/for_continue.sol | 12 ++ .../unreachableCode/assembly/return.sol | 17 ++ .../unreachableCode/assembly/revert.sol | 17 ++ test/libyul/Parser.cpp | 2 +- 29 files changed, 526 insertions(+), 46 deletions(-) create mode 100644 libyul/ControlFlowSideEffects.h create mode 100644 test/libsolidity/syntaxTests/controlFlow/leave_inside_function.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/leave_outside_function.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/double_revert.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_break.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_continue.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/return.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/revert.sol diff --git a/Changelog.md b/Changelog.md index 192272595..0b955c0cb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Language Features: * Inline Assembly: Allow assigning to `_slot` of local storage variable pointers. * General: Deprecated `value(...)` and `gas(...)` in favor of `{value: ...}` and `{gas: ...}` + * Inline Assembly: Perform control flow analysis on inline assembly. Allows storage returns to be set in assembly only. Compiler Features: diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index b7fa069d2..4ea9a01ae 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -155,6 +155,26 @@ bool SemanticInformation::terminatesControlFlow(Instruction _instruction) } } +bool SemanticInformation::reverts(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + else + return reverts(_item.instruction()); +} + +bool SemanticInformation::reverts(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::INVALID: + case Instruction::REVERT: + return true; + default: + return false; + } +} + bool SemanticInformation::isDeterministic(AssemblyItem const& _item) { if (_item.type() != Operation) diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index f08c3d73d..39a1b2439 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -47,6 +47,8 @@ struct SemanticInformation static bool altersControlFlow(AssemblyItem const& _item); static bool terminatesControlFlow(AssemblyItem const& _item); static bool terminatesControlFlow(Instruction _instruction); + static bool reverts(AssemblyItem const& _item); + static bool reverts(Instruction _instruction); /// @returns false if the value put on the stack by _item depends on anything else than /// the information in the current block header, memory, storage or stack. static bool isDeterministic(AssemblyItem const& _item); diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index c8a824c54..1a001eb1e 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -37,7 +37,7 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function) { auto const& functionFlow = m_cfg.functionFlow(_function); checkUninitializedAccess(functionFlow.entry, functionFlow.exit); - checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert); + checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert, functionFlow.transactionReturn); } return false; } @@ -137,7 +137,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod m_errorReporter.typeError( variableOccurrence->occurrence() ? - variableOccurrence->occurrence()->location() : + *variableOccurrence->occurrence() : variableOccurrence->declaration().location(), ssl, string("This variable is of storage pointer type and can be ") + @@ -148,7 +148,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod } } -void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const +void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) const { // collect all nodes reachable from the entry point std::set reachable = util::BreadthFirstSearch{{_entry}}.run( @@ -158,10 +158,10 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* } ).visited; - // traverse all paths backwards from exit and revert + // traverse all paths backwards from exit, revert and transaction return // and extract (valid) source locations of unreachable nodes into sorted set std::set unreachable; - util::BreadthFirstSearch{{_exit, _revert}}.run( + util::BreadthFirstSearch{{_exit, _revert, _transactionReturn}}.run( [&](CFGNode const* _node, auto&& _addChild) { if (!reachable.count(_node) && _node->location.isValid()) unreachable.insert(_node->location); diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h index 24af990ab..a839c5679 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.h +++ b/libsolidity/analysis/ControlFlowAnalyzer.h @@ -36,9 +36,9 @@ public: private: /// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit. void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const; - /// Checks for unreachable code, i.e. code ending in @param _exit or @param _revert + /// Checks for unreachable code, i.e. code ending in @param _exit, @param _revert or @param _transactionReturn /// that can not be reached from @param _entry. - void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const; + void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) const; CFG const& m_cfg; langutil::ErrorReporter& m_errorReporter; diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index c7504897b..f4b6b5d5f 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -16,6 +16,8 @@ */ #include +#include +#include using namespace solidity; using namespace solidity::langutil; @@ -26,10 +28,12 @@ ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, Funct m_nodeContainer(_nodeContainer), m_currentNode(_functionFlow.entry), m_returnNode(_functionFlow.exit), - m_revertNode(_functionFlow.revert) + m_revertNode(_functionFlow.revert), + m_transactionReturnNode(_functionFlow.transactionReturn) { } + unique_ptr ControlFlowBuilder::createFunctionFlow( CFG::NodeContainer& _nodeContainer, FunctionDefinition const& _function @@ -39,6 +43,7 @@ unique_ptr ControlFlowBuilder::createFunctionFlow( functionFlow->entry = _nodeContainer.newNode(); functionFlow->exit = _nodeContainer.newNode(); functionFlow->revert = _nodeContainer.newNode(); + functionFlow->transactionReturn = _nodeContainer.newNode(); ControlFlowBuilder builder(_nodeContainer, *functionFlow); builder.appendControlFlow(_function); @@ -131,17 +136,17 @@ bool ControlFlowBuilder::visit(ForStatement const& _forStatement) if (_forStatement.condition()) appendControlFlow(*_forStatement.condition()); - auto loopExpression = newLabel(); + auto postPart = newLabel(); auto nodes = splitFlow<2>(); auto afterFor = nodes[1]; m_currentNode = nodes[0]; { - BreakContinueScope scope(*this, afterFor, loopExpression); + BreakContinueScope scope(*this, afterFor, postPart); appendControlFlow(_forStatement.body()); } - placeAndConnectLabel(loopExpression); + placeAndConnectLabel(postPart); if (auto expression = _forStatement.loopExpression()) appendControlFlow(*expression); @@ -315,8 +320,7 @@ bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition) appendControlFlow(*returnParameter); m_returnNode->variableOccurrences.emplace_back( *returnParameter, - VariableOccurrence::Kind::Return, - nullptr + VariableOccurrence::Kind::Return ); } @@ -345,7 +349,7 @@ bool ControlFlowBuilder::visit(Return const& _return) m_currentNode->variableOccurrences.emplace_back( *returnParameter, VariableOccurrence::Kind::Assignment, - &_return + _return.location() ); } connect(m_currentNode, m_returnNode); @@ -363,18 +367,158 @@ bool ControlFlowBuilder::visit(FunctionTypeName const& _functionTypeName) bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) { - solAssert(!!m_currentNode, ""); - visitNode(_inlineAssembly); - for (auto const& ref: _inlineAssembly.annotation().externalReferences) + solAssert(!!m_currentNode && !m_inlineAssembly, ""); + + m_inlineAssembly = &_inlineAssembly; + (*this)(_inlineAssembly.operations()); + m_inlineAssembly = nullptr; + + return false; +} + +void ControlFlowBuilder::visit(yul::Statement const& _statement) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, locationOf(_statement)); + ASTWalker::visit(_statement); +} + +void ControlFlowBuilder::operator()(yul::If const& _if) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + visit(*_if.condition); + + auto nodes = splitFlow<2>(); + m_currentNode = nodes[0]; + (*this)(_if.body); + nodes[0] = m_currentNode; + mergeFlow(nodes, nodes[1]); +} + +void ControlFlowBuilder::operator()(yul::Switch const& _switch) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + visit(*_switch.expression); + + auto beforeSwitch = m_currentNode; + + auto nodes = splitFlow(_switch.cases.size()); + for (size_t i = 0u; i < _switch.cases.size(); ++i) { - if (auto variableDeclaration = dynamic_cast(ref.second.declaration)) + m_currentNode = nodes[i]; + (*this)(_switch.cases[i].body); + nodes[i] = m_currentNode; + } + mergeFlow(nodes); + + bool hasDefault = util::contains_if(_switch.cases, [](yul::Case const& _case) { return !_case.value; }); + if (!hasDefault) + connect(beforeSwitch, m_currentNode); +} + +void ControlFlowBuilder::operator()(yul::ForLoop const& _forLoop) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + + (*this)(_forLoop.pre); + + auto condition = createLabelHere(); + + if (_forLoop.condition) + visit(*_forLoop.condition); + + auto loopExpression = newLabel(); + auto nodes = splitFlow<2>(); + auto afterFor = nodes[1]; + m_currentNode = nodes[0]; + + { + BreakContinueScope scope(*this, afterFor, loopExpression); + (*this)(_forLoop.body); + } + + placeAndConnectLabel(loopExpression); + + (*this)(_forLoop.post); + + connect(m_currentNode, condition); + m_currentNode = afterFor; +} + +void ControlFlowBuilder::operator()(yul::Break const&) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + solAssert(m_breakJump, ""); + connect(m_currentNode, m_breakJump); + m_currentNode = newLabel(); +} + +void ControlFlowBuilder::operator()(yul::Continue const&) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + solAssert(m_continueJump, ""); + connect(m_currentNode, m_continueJump); + m_currentNode = newLabel(); +} + +void ControlFlowBuilder::operator()(yul::Identifier const& _identifier) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + auto const& externalReferences = m_inlineAssembly->annotation().externalReferences; + if (externalReferences.count(&_identifier)) + { + if (auto const* declaration = dynamic_cast(externalReferences.at(&_identifier).declaration)) m_currentNode->variableOccurrences.emplace_back( - *variableDeclaration, - VariableOccurrence::Kind::InlineAssembly, - &_inlineAssembly + *declaration, + VariableOccurrence::Kind::Access, + _identifier.location ); } - return true; +} + +void ControlFlowBuilder::operator()(yul::Assignment const& _assignment) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + visit(*_assignment.value); + auto const& externalReferences = m_inlineAssembly->annotation().externalReferences; + for (auto const& variable: _assignment.variableNames) + if (externalReferences.count(&variable)) + if (auto const* declaration = dynamic_cast(externalReferences.at(&variable).declaration)) + m_currentNode->variableOccurrences.emplace_back( + *declaration, + VariableOccurrence::Kind::Assignment, + variable.location + ); +} + +void ControlFlowBuilder::operator()(yul::FunctionCall const& _functionCall) +{ + using namespace yul; + solAssert(m_currentNode && m_inlineAssembly, ""); + yul::ASTWalker::operator()(_functionCall); + + if (auto const *builtinFunction = m_inlineAssembly->dialect().builtin(_functionCall.functionName.name)) + if (builtinFunction->controlFlowSideEffects.terminates) + { + if (builtinFunction->controlFlowSideEffects.reverts) + connect(m_currentNode, m_revertNode); + else + connect(m_currentNode, m_transactionReturnNode); + m_currentNode = newLabel(); + } +} + +void ControlFlowBuilder::operator()(yul::FunctionDefinition const&) +{ + solAssert(m_currentNode && m_inlineAssembly, ""); + // External references cannot be accessed from within functions, so we can ignore their control flow. + // TODO: we might still want to track if they always revert or return, though. +} + +void ControlFlowBuilder::operator()(yul::Leave const&) +{ + // This has to be implemented, if we ever decide to visit functions. + solUnimplementedAssert(false, ""); } bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) @@ -384,8 +528,7 @@ bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) m_currentNode->variableOccurrences.emplace_back( _variableDeclaration, - VariableOccurrence::Kind::Declaration, - nullptr + VariableOccurrence::Kind::Declaration ); // Handle declaration with immediate assignment. @@ -393,14 +536,13 @@ bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) m_currentNode->variableOccurrences.emplace_back( _variableDeclaration, VariableOccurrence::Kind::Assignment, - _variableDeclaration.value().get() + _variableDeclaration.value()->location() ); // Function arguments are considered to be immediately assigned as well (they are "externally assigned"). else if (_variableDeclaration.isCallableOrCatchParameter() && !_variableDeclaration.isReturnParameter()) m_currentNode->variableOccurrences.emplace_back( _variableDeclaration, - VariableOccurrence::Kind::Assignment, - nullptr + VariableOccurrence::Kind::Assignment ); return true; } @@ -434,7 +576,7 @@ bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDecl m_currentNode->variableOccurrences.emplace_back( *var, VariableOccurrence::Kind::Assignment, - expression + expression ? std::make_optional(expression->location()) : std::optional{} ); } } @@ -452,7 +594,7 @@ bool ControlFlowBuilder::visit(Identifier const& _identifier) static_cast(_identifier).annotation().lValueRequested ? VariableOccurrence::Kind::Assignment : VariableOccurrence::Kind::Access, - &_identifier + _identifier.location() ); return true; diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index ecdedfbfe..ee1c3ab79 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,7 @@ namespace solidity::frontend { * Modifiers are not yet applied to the functions. This is done in a second * step in the CFG class. */ -class ControlFlowBuilder: private ASTConstVisitor +class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker { public: static std::unique_ptr createFunctionFlow( @@ -39,7 +40,10 @@ public: ); private: - explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow); + explicit ControlFlowBuilder( + CFG::NodeContainer& _nodeContainer, + FunctionFlow const& _functionFlow + ); // Visits for constructing the control flow. bool visit(BinaryOperation const& _operation) override; @@ -62,6 +66,17 @@ private: // Visits for filling variable occurrences. bool visit(FunctionTypeName const& _functionTypeName) override; bool visit(InlineAssembly const& _inlineAssembly) override; + void visit(yul::Statement const& _statement) override; + void operator()(yul::If const& _if) override; + void operator()(yul::Switch const& _switch) override; + void operator()(yul::ForLoop const& _for) override; + void operator()(yul::Break const&) override; + void operator()(yul::Continue const&) override; + void operator()(yul::Identifier const& _identifier) override; + void operator()(yul::Assignment const& _assignment) override; + void operator()(yul::FunctionCall const& _functionCall) override; + void operator()(yul::FunctionDefinition const& _functionDefinition) override; + void operator()(yul::Leave const& _leave) override; bool visit(VariableDeclaration const& _variableDeclaration) override; bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; bool visit(Identifier const& _identifier) override; @@ -70,6 +85,9 @@ protected: bool visitNode(ASTNode const&) override; private: + using ASTConstVisitor::visit; + using yul::ASTWalker::visit; + using yul::ASTWalker::operator(); /// Appends the control flow of @a _node to the current control flow. void appendControlFlow(ASTNode const& _node); @@ -136,6 +154,7 @@ private: CFGNode* m_currentNode = nullptr; CFGNode* m_returnNode = nullptr; CFGNode* m_revertNode = nullptr; + CFGNode* m_transactionReturnNode = nullptr; /// The current jump destination of break Statements. CFGNode* m_breakJump = nullptr; @@ -145,6 +164,8 @@ private: CFGNode* m_placeholderEntry = nullptr; CFGNode* m_placeholderExit = nullptr; + InlineAssembly const* m_inlineAssembly = nullptr; + /// Helper class that replaces the break and continue jump destinations for the /// current scope and restores the originals at the end of the scope. class BreakContinueScope diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index e40bb7623..93e2e3b89 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -33,8 +34,8 @@ namespace solidity::frontend /** * Occurrence of a variable in a block of control flow. * Stores the declaration of the referenced variable, the - * kind of the occurrence and possibly the node at which - * it occurred. + * kind of the occurrence and possibly the source location + * at which it occurred. */ class VariableOccurrence { @@ -47,7 +48,7 @@ public: Assignment, InlineAssembly }; - VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, ASTNode const* _occurrence): + VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional const& _occurrence = {}): m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence) { } @@ -57,8 +58,8 @@ public: { if (m_occurrence && _rhs.m_occurrence) { - if (m_occurrence->id() < _rhs.m_occurrence->id()) return true; - if (_rhs.m_occurrence->id() < m_occurrence->id()) return false; + if (*m_occurrence < *_rhs.m_occurrence) return true; + if (*_rhs.m_occurrence < *m_occurrence) return false; } else if (_rhs.m_occurrence) return true; @@ -74,14 +75,14 @@ public: VariableDeclaration const& declaration() const { return m_declaration; } Kind kind() const { return m_occurrenceKind; }; - ASTNode const* occurrence() const { return m_occurrence; } + std::optional const& occurrence() const { return m_occurrence; } private: /// Declaration of the occurring variable. VariableDeclaration const& m_declaration; /// Kind of occurrence. Kind m_occurrenceKind = Kind::Access; - /// AST node at which the variable occurred, if available (may be nullptr). - ASTNode const* m_occurrence = nullptr; + /// Source location at which the variable occurred, if available (may be nullptr). + std::optional m_occurrence; }; /** @@ -119,6 +120,10 @@ struct FunctionFlow /// This node is empty does not have any exits, but may have multiple entries /// (e.g. all assert, require, revert and throw statements). CFGNode* revert = nullptr; + /// Transaction return node. Destination node for inline assembly "return" calls. + /// This node is empty and does not have any exits, but may have multiple entries + /// (e.g. all inline assembly return calls). + CFGNode* transactionReturn = nullptr; }; class CFG: private ASTConstVisitor @@ -140,7 +145,6 @@ public: std::vector> m_nodes; }; private: - langutil::ErrorReporter& m_errorReporter; /// Node container. diff --git a/libyul/ControlFlowSideEffects.h b/libyul/ControlFlowSideEffects.h new file mode 100644 index 000000000..5621a122b --- /dev/null +++ b/libyul/ControlFlowSideEffects.h @@ -0,0 +1,38 @@ +/* + 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 . +*/ + +#pragma once + +#include + +namespace solidity::yul +{ + +/** + * Side effects of code related to control flow. + */ +struct ControlFlowSideEffects +{ + /// If true, this code terminates the control flow. + /// State may or may not be reverted as indicated by the ``reverts`` flag. + bool terminates = false; + /// If true, this code reverts all state changes in the transaction. + /// Whenever this is true, ``terminates`` has to be true as well. + bool reverts = false; +}; + +} diff --git a/libyul/Dialect.h b/libyul/Dialect.h index c137791ee..a2a596177 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -42,6 +43,7 @@ struct BuiltinFunction std::vector parameters; std::vector returns; SideEffects sideEffects; + ControlFlowSideEffects controlFlowSideEffects; /// If true, this is the msize instruction. bool isMSize = false; /// If true, can only accept literals as arguments and they cannot be moved to variables. diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 27a2b85e3..ea6586323 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -52,6 +52,8 @@ pair createEVMFunction( f.parameters.resize(info.args); f.returns.resize(info.ret); f.sideEffects = EVMDialect::sideEffectsOfInstruction(_instruction); + f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction); + f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction); f.isMSize = _instruction == evmasm::Instruction::MSIZE; f.literalArguments = false; f.instruction = _instruction; diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 7f2149adc..b8dc9f32f 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -99,6 +99,8 @@ WasmDialect::WasmDialect() addFunction("unreachable", {}, {}, false); m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; + m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true; + m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true; addFunction("datasize", {i64}, {i64}, true, true); addFunction("dataoffset", {i64}, {i64}, true, true); @@ -147,7 +149,12 @@ void WasmDialect::addEthereumExternals() static string const i64{"i64"}; static string const i32{"i32"}; static string const i32ptr{"i32"}; // Uses "i32" on purpose. - struct External { string name; vector parameters; vector returns; }; + struct External { + string name; + vector parameters; + vector returns; + ControlFlowSideEffects controlFlowSideEffects = {}; + }; static vector externals{ {"getAddress", {i32ptr}, {}}, {"getExternalBalance", {i32ptr, i32ptr}, {}}, @@ -175,11 +182,11 @@ void WasmDialect::addEthereumExternals() {"log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}}, {"getBlockNumber", {}, {i64}}, {"getTxOrigin", {i32ptr}, {}}, - {"finish", {i32ptr, i32}, {}}, - {"revert", {i32ptr, i32}, {}}, + {"finish", {i32ptr, i32}, {}, {true, false}}, + {"revert", {i32ptr, i32}, {}, {true, true}}, {"getReturnDataSize", {}, {i32}}, {"returnDataCopy", {i32ptr, i32, i32}, {}}, - {"selfDestruct", {i32ptr}, {}}, + {"selfDestruct", {i32ptr}, {}, {true, false}}, {"getBlockTimestamp", {}, {i64}} }; for (External const& ext: externals) @@ -193,6 +200,7 @@ void WasmDialect::addEthereumExternals() f.returns.emplace_back(YulString(p)); // TODO some of them are side effect free. f.sideEffects = SideEffects::worst(); + f.controlFlowSideEffects = ext.controlFlowSideEffects; f.isMSize = false; f.sideEffects.invalidatesStorage = (ext.name == "storageStore"); f.literalArguments = false; diff --git a/test/libsolidity/syntaxTests/controlFlow/leave_inside_function.sol b/test/libsolidity/syntaxTests/controlFlow/leave_inside_function.sol new file mode 100644 index 000000000..245106e13 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/leave_inside_function.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + function f() { + // Make sure this doesn't trigger the unimplemented assertion in the control flow builder. + leave + } + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/leave_outside_function.sol b/test/libsolidity/syntaxTests/controlFlow/leave_outside_function.sol new file mode 100644 index 000000000..772a108e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/leave_outside_function.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + assembly { + // Make sure this doesn't trigger the unimplemented assertion in the control flow builder. + leave + } + } +} +// ---- +// SyntaxError: (178-183): Keyword "leave" can only be used inside a function. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_err.sol new file mode 100644 index 000000000..909d4f333 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_err.sol @@ -0,0 +1,29 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure returns (S storage c) { + assembly { + for {} eq(0,0) { c_slot := s_slot } {} + } + } + function g() internal pure returns (S storage c) { + assembly { + for {} eq(0,1) { c_slot := s_slot } {} + } + } + function h() internal pure returns (S storage c) { + assembly { + for {} eq(0,0) {} { c_slot := s_slot } + } + } + function i() internal pure returns (S storage c) { + assembly { + for {} eq(0,1) {} { c_slot := s_slot } + } + } +} +// ---- +// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. +// TypeError: (228-239): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. +// TypeError: (369-380): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. +// TypeError: (510-521): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_fine.sol new file mode 100644 index 000000000..ac3532aaf --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_fine.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure returns (S storage c) { + assembly { + for { c_slot := s_slot } iszero(0) {} {} + } + } + function g() internal pure returns (S storage c) { + assembly { + for { c_slot := s_slot } iszero(1) {} {} + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_err.sol new file mode 100644 index 000000000..2f79ee10f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_err.sol @@ -0,0 +1,11 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal pure returns (S storage c) { + assembly { + if flag { c_slot := s_slot } + } + } +} +// ---- +// TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function.sol new file mode 100644 index 000000000..81b6bcfe7 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure returns (S storage c) { + // this should warn about unreachable code, but currently function flow is ignored + assembly { + function f() { return(0, 0) } + f() + c_slot := s_slot + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function.sol new file mode 100644 index 000000000..4619584ef --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure returns (S storage c) { + // this could be allowed, but currently control flow for functions is not analysed + assembly { + function f() { revert(0, 0) } + f() + } + } +} +// ---- +// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub.sol new file mode 100644 index 000000000..e5873f3d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub.sol @@ -0,0 +1,10 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure returns (S storage c) { + assembly { + c_slot := s_slot + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_err.sol new file mode 100644 index 000000000..0644d4b8f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_err.sol @@ -0,0 +1,27 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure returns (S storage c) { + assembly { + switch a + case 0 { c_slot := s_slot } + } + } + function g(bool flag) internal pure returns (S storage c) { + assembly { + switch flag + case 0 { c_slot := s_slot } + case 1 { c_slot := s_slot } + } + } + function h(uint256 a) internal pure returns (S storage c) { + assembly { + switch a + case 0 { c_slot := s_slot } + default { return(0,0) } + } + } +} +// ---- +// TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. +// TypeError: (256-267): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol new file mode 100644 index 000000000..9e73ecff1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol @@ -0,0 +1,25 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure returns (S storage c) { + assembly { + switch a + default { c_slot := s_slot } + } + } + function g(bool flag) internal pure returns (S storage c) { + assembly { + switch flag + case 0 { c_slot := s_slot } + default { c_slot := s_slot } + } + } + function h(uint256 a) internal pure returns (S storage c) { + assembly { + switch a + case 0 { revert(0, 0) } + default { c_slot := s_slot } + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol index e73d9cde6..89a13717e 100644 --- a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol +++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError: (92-116): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (107-113): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/double_revert.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/double_revert.sol new file mode 100644 index 000000000..8a441a5b2 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/double_revert.sol @@ -0,0 +1,17 @@ +contract C { + function f() public pure { + assembly { + revert(0, 0) + revert(0, 0) + } + } + function g() public pure { + assembly { + revert(0, 0) + } + revert(); + } +} +// ---- +// Warning: (100-112): Unreachable code. +// Warning: (222-230): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_break.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_break.sol new file mode 100644 index 000000000..466bf4fae --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_break.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + for { let a := 0} lt(a,1) { a := add(a, 1) } { + break + let b := 42 + } + } + } +} +// ---- +// Warning: (103-117): Unreachable code. +// Warning: (160-171): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_continue.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_continue.sol new file mode 100644 index 000000000..be09967f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/for_continue.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + assembly { + for { let a := 0} lt(a,1) { a := add(a, 1) } { + continue + let b := 42 + } + } + } +} +// ---- +// Warning: (163-174): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/return.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/return.sol new file mode 100644 index 000000000..9b13441b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/return.sol @@ -0,0 +1,17 @@ +contract C { + function f(uint256 y) public pure returns (uint256 x) { + assembly { + return(0, 0) + x := y + } + } + function g(uint256 y) public pure returns (uint256 x) { + assembly { + return(0, 0) + } + x = y; + } +} +// ---- +// Warning: (129-135): Unreachable code. +// Warning: (274-279): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/revert.sol b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/revert.sol new file mode 100644 index 000000000..66b6ae382 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/unreachableCode/assembly/revert.sol @@ -0,0 +1,17 @@ +contract C { + function f(uint256 y) public pure returns (uint256 x) { + assembly { + revert(0, 0) + x := y + } + } + function g(uint256 y) public pure returns (uint256 x) { + assembly { + revert(0, 0) + } + x = y; + } +} +// ---- +// Warning: (129-135): Unreachable code. +// Warning: (274-279): Unreachable code. diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 020f3d4c4..93b528721 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -539,7 +539,7 @@ BOOST_AUTO_TEST_CASE(builtins_analysis) { return _name == "builtin"_yulstring ? &f : nullptr; } - BuiltinFunction f{"builtin"_yulstring, vector(2), vector(3), {}}; + BuiltinFunction f{"builtin"_yulstring, vector(2), vector(3), {}, {}}; }; SimpleDialect dialect; From d541e222a2ee7194080cbe33edb842664fda6589 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2020 09:45:13 +0100 Subject: [PATCH 28/37] Prepare changelog for 0.6.4. --- Changelog.md | 5 ++--- docs/bugs_by_version.json | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0b955c0cb..19e89adf5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,8 @@ -### 0.6.4 (unreleased) +### 0.6.4 (2020-03-10) Language Features: - * Inline Assembly: Allow assigning to `_slot` of local storage variable pointers. * General: Deprecated `value(...)` and `gas(...)` in favor of `{value: ...}` and `{gas: ...}` + * Inline Assembly: Allow assigning to `_slot` of local storage variable pointers. * Inline Assembly: Perform control flow analysis on inline assembly. Allows storage returns to be set in assembly only. @@ -13,7 +13,6 @@ Compiler Features: Bugfixes: * Inheritance: Fix incorrect error on calling unimplemented base functions. - * isoltest: Added new keyword `wei` to express function value in semantic tests * Reference Resolver: Fix scoping issue following try/catch statements. * Standard-JSON-Interface: Fix a bug related to empty filenames and imports. * SMTChecker: Fix internal errors when analysing tuples. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 3bdc7d9e5..dc32e2687 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -888,5 +888,9 @@ "0.6.3": { "bugs": [], "released": "2020-02-18" + }, + "0.6.4": { + "bugs": [], + "released": "2020-03-10" } } \ No newline at end of file From 5d7a37024854990f175c4e93a881b259b6be18b1 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 24 Feb 2020 18:06:58 +0100 Subject: [PATCH 29/37] YulUtilFunctions: convertionFunction() to also handle array string/memory casts. --- libsolidity/codegen/YulUtilFunctions.cpp | 34 +++++++++++++------ .../conversions/string_to_bytes.sol | 9 +++++ 2 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 test/libsolidity/semanticTests/conversions/string_to_bytes.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 83e8272fe..9abe860b3 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1469,22 +1469,34 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) break; case Type::Category::Array: { - bool equal = _from == _to; - - if (!equal) + if (_from == _to) + body = "converted := value"; + else { ArrayType const& from = dynamic_cast(_from); ArrayType const& to = dynamic_cast(_to); - if (*from.mobileType() == *to.mobileType()) - equal = true; + switch (to.location()) + { + case DataLocation::Storage: + // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. + solAssert( + (to.isPointer() || (from.isByteArray() && to.isByteArray())) && + from.location() == DataLocation::Storage, + "Invalid conversion to storage type." + ); + body = "converted := value"; + break; + case DataLocation::Memory: + // Copy the array to a free position in memory, unless it is already in memory. + solUnimplementedAssert(from.location() == DataLocation::Memory, "Not implemented yet."); + body = "converted := value"; + break; + case DataLocation::CallData: + solUnimplemented("Conversion of calldata types not yet implemented."); + break; + } } - - if (equal) - body = "converted := value"; - else - solUnimplementedAssert(false, "Array conversion not implemented."); - break; } case Type::Category::Struct: diff --git a/test/libsolidity/semanticTests/conversions/string_to_bytes.sol b/test/libsolidity/semanticTests/conversions/string_to_bytes.sol new file mode 100644 index 000000000..ca258ca26 --- /dev/null +++ b/test/libsolidity/semanticTests/conversions/string_to_bytes.sol @@ -0,0 +1,9 @@ +contract C { + function f(string memory s) public pure returns (bytes memory t) { + t = bytes(s); + } +} +// ==== +// compileViaYul: also +// ---- +// f(string): 32, 5, "Hello" -> 32, 5, "Hello" From a3d5af30c6455d12e5f4849f5cfdd18fceb90bf6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2020 12:55:23 +0100 Subject: [PATCH 30/37] Mention ControlFlowSideEffects explicitly to ease the burden on MSVC. --- libyul/backends/wasm/WasmDialect.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index b8dc9f32f..c9817af76 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -149,11 +149,12 @@ void WasmDialect::addEthereumExternals() static string const i64{"i64"}; static string const i32{"i32"}; static string const i32ptr{"i32"}; // Uses "i32" on purpose. - struct External { + struct External + { string name; vector parameters; vector returns; - ControlFlowSideEffects controlFlowSideEffects = {}; + ControlFlowSideEffects controlFlowSideEffects = ControlFlowSideEffects{}; }; static vector externals{ {"getAddress", {i32ptr}, {}}, @@ -182,11 +183,11 @@ void WasmDialect::addEthereumExternals() {"log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}}, {"getBlockNumber", {}, {i64}}, {"getTxOrigin", {i32ptr}, {}}, - {"finish", {i32ptr, i32}, {}, {true, false}}, - {"revert", {i32ptr, i32}, {}, {true, true}}, + {"finish", {i32ptr, i32}, {}, ControlFlowSideEffects{true, false}}, + {"revert", {i32ptr, i32}, {}, ControlFlowSideEffects{true, true}}, {"getReturnDataSize", {}, {i32}}, {"returnDataCopy", {i32ptr, i32, i32}, {}}, - {"selfDestruct", {i32ptr}, {}, {true, false}}, + {"selfDestruct", {i32ptr}, {}, ControlFlowSideEffects{true, false}}, {"getBlockTimestamp", {}, {i64}} }; for (External const& ext: externals) From 437ab3d24ce33e74eb4be2228d92fc4ddec15554 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 10 Mar 2020 14:02:16 +0100 Subject: [PATCH 31/37] Fixed ControlFlowBuilder compilation error. --- libsolidity/analysis/ControlFlowBuilder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index ee1c3ab79..2d0910d6a 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -76,7 +76,7 @@ private: void operator()(yul::Assignment const& _assignment) override; void operator()(yul::FunctionCall const& _functionCall) override; void operator()(yul::FunctionDefinition const& _functionDefinition) override; - void operator()(yul::Leave const& _leave) override; + void operator()(yul::Leave const& _leaveStatement) override; bool visit(VariableDeclaration const& _variableDeclaration) override; bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; bool visit(Identifier const& _identifier) override; From 1b1781580802c48837a80e1e07eed2ecaed9bfea Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 10 Mar 2020 13:21:25 +0100 Subject: [PATCH 32/37] SMTChecker docs test may issue a warning --- docs/security-considerations.rst | 2 ++ test/cmdlineTests.sh | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index ebf54b3a4..829aab9e4 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -547,6 +547,7 @@ not mean loss of proving power. pragma solidity >=0.5.0; pragma experimental SMTChecker; + // This may report a warning if no SMT solver available. contract Recover { @@ -601,6 +602,7 @@ types. pragma solidity >=0.5.0; pragma experimental SMTChecker; // This will report a warning + contract Aliasing { uint[] array; diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 393f3da8b..ece4e581c 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -77,6 +77,11 @@ function compileFull() expect_output=1 shift; fi + if [[ $1 = '-o' ]] + then + expect_output=2 + shift; + fi local files="$*" local output @@ -93,7 +98,7 @@ function compileFull() if [[ \ "$exit_code" -ne "$expected_exit_code" || \ ( $expect_output -eq 0 && -n "$errors" ) || \ - ( $expect_output -ne 0 && -z "$errors" ) \ + ( $expect_output -eq 1 && -z "$errors" ) \ ]] then printError "Unexpected compilation result:" @@ -350,6 +355,10 @@ SOLTMPDIR=$(mktemp -d) then opts="$opts -w" fi + if grep "This may report a warning" "$f" >/dev/null + then + opts="$opts -o" + fi compileFull $opts "$SOLTMPDIR/$f" done ) From bcefda747ce0bba9fc54c0295c50681d439222f0 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 10 Mar 2020 13:41:10 +0100 Subject: [PATCH 33/37] Do not run smtCheckerTestsJSON if no solver available --- test/libsolidity/SMTCheckerJSONTest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp index 82c2b446b..fad5a127f 100644 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -17,14 +17,18 @@ #include #include + +#include #include #include #include + #include #include #include #include #include + #include #include #include @@ -50,6 +54,9 @@ SMTCheckerJSONTest::SMTCheckerJSONTest(string const& _filename, langutil::EVMVer !m_smtResponses.isObject() ) BOOST_THROW_EXCEPTION(runtime_error("Invalid JSON file.")); + + if (ModelChecker::availableSolvers().none()) + m_shouldRun = false; } TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) From f8344cb4df414dadb83363db2241ea43a2911a53 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2020 21:08:03 +0100 Subject: [PATCH 34/37] Set version to 0.6.5 --- CMakeLists.txt | 2 +- Changelog.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4278770f1..4a674c99e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.6.4") +set(PROJECT_VERSION "0.6.5") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) diff --git a/Changelog.md b/Changelog.md index 19e89adf5..15221bb7d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,15 @@ +### 0.6.5 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + + ### 0.6.4 (2020-03-10) Language Features: From 694da61b3910fdb2fd12ab0474b8f2f7f601250d Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2020 21:13:21 +0100 Subject: [PATCH 35/37] Remove PR template and empty general template. --- .github/ISSUE_TEMPLATE/general.md | 21 --------------------- .github/PULL_REQUEST_TEMPLATE.md | 22 ---------------------- 2 files changed, 43 deletions(-) delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE/general.md b/.github/ISSUE_TEMPLATE/general.md index 410b42e00..e69de29bb 100644 --- a/.github/ISSUE_TEMPLATE/general.md +++ b/.github/ISSUE_TEMPLATE/general.md @@ -1,21 +0,0 @@ ---- -name: General Feedback -about: Any general feedback (neither feature request nor bug reports) ---- - - -## Description - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 9fdbf158e..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ - - -### Description - - - -### Checklist -- [ ] Code compiles correctly -- [ ] All tests are passing -- [ ] New tests have been created which fail without the change (if possible) -- [ ] README / documentation was extended, if necessary -- [ ] Changelog entry (if change is visible to the user) -- [ ] Used meaningful commit messages From 37878cf8d250856f2516475e597ad9f3335ca560 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Thu, 27 Feb 2020 16:47:20 +0100 Subject: [PATCH 36/37] Adding support for ipfs large files. --- Changelog.md | 3 +- libsolidity/interface/CompilerStack.cpp | 6 +- libsolutil/IpfsHash.cpp | 153 ++++++++++++++++++++---- test/libsolutil/IpfsHash.cpp | 53 ++++++-- 4 files changed, 178 insertions(+), 37 deletions(-) diff --git a/Changelog.md b/Changelog.md index 15221bb7d..e92b536df 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,12 +4,12 @@ Language Features: Compiler Features: + * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. Bugfixes: - ### 0.6.4 (2020-03-10) Language Features: @@ -30,7 +30,6 @@ Bugfixes: * SMTChecker: Fix internal errors when analysing tuples. * Yul AST Import: correctly import blocks as statements, switch statements and string literals. - ### 0.6.3 (2020-02-18) Language Features: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 49b17cf33..781f68d6b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -899,8 +899,7 @@ h256 const& CompilerStack::Source::swarmHash() const string const& CompilerStack::Source::ipfsUrl() const { if (ipfsUrlCached.empty()) - if (scanner->source().size() < 1024 * 256) - ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); + ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); return ipfsUrlCached; } @@ -1373,10 +1372,7 @@ bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimen MetadataCBOREncoder encoder; if (m_metadataHash == MetadataHash::IPFS) - { - solAssert(_metadata.length() < 1024 * 256, "Metadata too large."); encoder.pushBytes("ipfs", util::ipfsHash(_metadata)); - } else if (m_metadataHash == MetadataHash::Bzzr1) encoder.pushBytes("bzzr1", util::bzzr1Hash(_metadata).asBytes()); else diff --git a/libsolutil/IpfsHash.cpp b/libsolutil/IpfsHash.cpp index d6a511a24..95605cdc1 100644 --- a/libsolutil/IpfsHash.cpp +++ b/libsolutil/IpfsHash.cpp @@ -40,6 +40,21 @@ bytes varintEncoding(size_t _n) return encoded; } +bytes encodeByteArray(bytes const& _data) +{ + return bytes{0x0a} + varintEncoding(_data.size()) + _data; +} + +bytes encodeHash(bytes const& _data) +{ + return bytes{0x12, 0x20} + picosha2::hash256(_data); +} + +bytes encodeLinkData(bytes const& _data) +{ + return bytes{0x12} + varintEncoding(_data.size()) + _data; +} + string base58Encode(bytes const& _data) { static string const alphabet{"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}; @@ -53,36 +68,132 @@ string base58Encode(bytes const& _data) reverse(output.begin(), output.end()); return output; } + +struct Chunk +{ + Chunk() = default; + Chunk(bytes _hash, size_t _size, size_t _blockSize): + hash(std::move(_hash)), + size(_size), + blockSize(_blockSize) + {} + + bytes hash = {}; + size_t size = 0; + size_t blockSize = 0; +}; + +using Chunks = vector; + +Chunk combineLinks(Chunks& _links) +{ + bytes data = {}; + bytes lengths = {}; + Chunk chunk = {}; + for (Chunk& link: _links) + { + chunk.size += link.size; + chunk.blockSize += link.blockSize; + + data += encodeLinkData( + bytes {0x0a} + + varintEncoding(link.hash.size()) + + std::move(link.hash) + + bytes{0x12, 0x00, 0x18} + + varintEncoding(link.blockSize) + ); + + lengths += bytes{0x20} + varintEncoding(link.size); + } + + bytes blockData = data + encodeByteArray(bytes{0x08, 0x02, 0x18} + varintEncoding(chunk.size) + lengths); + + chunk.blockSize += blockData.size(); + chunk.hash = encodeHash(blockData); + + return chunk; +} + +Chunks buildNextLevel(Chunks& _currentLevel) +{ + size_t const maxChildNum = 174; + + Chunks nextLevel; + Chunks links; + + for (Chunk& chunk: _currentLevel) + { + links.emplace_back(std::move(chunk.hash), chunk.size, chunk.blockSize); + if (links.size() == maxChildNum) + { + nextLevel.emplace_back(combineLinks(links)); + links = {}; + } + } + if (!links.empty()) + nextLevel.emplace_back(combineLinks(links)); + + return nextLevel; +} + +/// Builds a tree starting from the bottom level where nodes are data nodes. +/// Data nodes should be calculated and passed as the only level in chunk levels +/// Each next level is calculated as following: +/// - Pick up to maxChildNum (174) nodes until a whole level is added, group them and pass to the node in the next level +/// - Do this until the current level has only one node, return the hash in that node +bytes groupChunksBottomUp(Chunks _currentLevel) +{ + // when we reach root it will be the only node in that level + while (_currentLevel.size() != 1) + _currentLevel = buildNextLevel(_currentLevel); + + // top level's only node stores the hash for file + return _currentLevel.front().hash; +} } bytes solidity::util::ipfsHash(string _data) { - assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented."); + size_t const maxChunkSize = 1024 * 256; + size_t chunkCount = _data.length() / maxChunkSize + (_data.length() % maxChunkSize > 0 ? 1 : 0); + chunkCount = chunkCount == 0 ? 1 : chunkCount; - bytes lengthAsVarint = varintEncoding(_data.size()); + Chunks allChunks; - bytes protobufEncodedData; - // Type: File - protobufEncodedData += bytes{0x08, 0x02}; - if (!_data.empty()) + for (unsigned long chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++) { - // Data (length delimited bytes) - protobufEncodedData += bytes{0x12}; - protobufEncodedData += lengthAsVarint; - protobufEncodedData += asBytes(std::move(_data)); + bytes chunkBytes = asBytes( + _data.substr(chunkIndex * maxChunkSize, min(maxChunkSize, _data.length() - chunkIndex * maxChunkSize)) + ); + + bytes lengthAsVarint = varintEncoding(chunkBytes.size()); + + bytes protobufEncodedData; + // Type: File + protobufEncodedData += bytes{0x08, 0x02}; + if (!chunkBytes.empty()) + { + // Data (length delimited bytes) + protobufEncodedData += bytes{0x12}; + protobufEncodedData += lengthAsVarint; + protobufEncodedData += chunkBytes; + } + // filesize: length as varint + protobufEncodedData += bytes{0x18} + lengthAsVarint; + + // PBDag: + // Data: (length delimited bytes) + bytes blockData = encodeByteArray(protobufEncodedData); + + // Multihash: sha2-256, 256 bits + allChunks.emplace_back( + encodeHash(blockData), + chunkBytes.size(), + blockData.size() + ); } - // filesize: length as varint - protobufEncodedData += bytes{0x18} + lengthAsVarint; - // PBDag: - // Data: (length delimited bytes) - size_t protobufLength = protobufEncodedData.size(); - bytes blockData = bytes{0x0a} + varintEncoding(protobufLength) + std::move(protobufEncodedData); - // TODO Handle "large" files with multiple blocks - - // Multihash: sha2-256, 256 bits - bytes hash = bytes{0x12, 0x20} + picosha2::hash256(std::move(blockData)); - return hash; + return groupChunksBottomUp(std::move(allChunks)); } string solidity::util::ipfsHashBase58(string _data) diff --git a/test/libsolutil/IpfsHash.cpp b/test/libsolutil/IpfsHash.cpp index 4514b2f4c..9c4fb5633 100644 --- a/test/libsolutil/IpfsHash.cpp +++ b/test/libsolutil/IpfsHash.cpp @@ -60,15 +60,50 @@ BOOST_AUTO_TEST_CASE(test_largest_unchunked) BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmbNDspMkzkMFKyS3eCJGedG7GWRQHSCzJCZLjxP7wyVAx"); } -// TODO This needs chunking implemented -//BOOST_AUTO_TEST_CASE(test_large) -//{ -// size_t length = 1310710; -// string data; -// data.resize(length, 0); -// BOOST_REQUIRE_EQUAL(data.size(), length); -// BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmNg7BJo8gEMDK8yGQbHEwPtycesnE6FUULX5iVd5TAL9f"); -//} +BOOST_AUTO_TEST_CASE(test_smallest_chunked) +{ + size_t length = 1024 * 256 + 1; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmbVuw4C4vcmVKqxoWtgDVobvcHrSn51qsmQmyxjk4sB2Q"); +} + +BOOST_AUTO_TEST_CASE(test_large) +{ + size_t length = 1310710; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmNg7BJo8gEMDK8yGQbHEwPtycesnE6FUULX5iVd5TAL9f"); +} + +BOOST_AUTO_TEST_CASE(test_largest_one_level) +{ + size_t length = 45613056; // 1024 * 256 * 174; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmY4HSz1oVGdUzb8poVYPLsoqBZjH6LZrtgnme9wWn2Qko"); +} + +BOOST_AUTO_TEST_CASE(test_smallest_multi_level) +{ + size_t length = 45613057; // 1024 * 256 * 174 + 1; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmehMASWcBsX7VcEQqs6rpR5AHoBfKyBVEgmkJHjpPg8jq"); +} + +BOOST_AUTO_TEST_CASE(test_multi_level_tree) +{ + size_t length = 46661632; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmaTb1sT9hrSXJLmf8bxJ9NuwndiHuMLsgNLgkS2eXu3Xj"); +} BOOST_AUTO_TEST_SUITE_END() From 07368c2e1e18577d7697275c347ba0e3f0cfcc41 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Feb 2020 23:21:42 -0300 Subject: [PATCH 37/37] Add support to internal function calls --- Changelog.md | 1 + libsolidity/formal/BMC.cpp | 6 -- libsolidity/formal/CHC.cpp | 70 +++++++++++++++++-- libsolidity/formal/CHC.h | 3 + libsolidity/formal/SMTEncoder.cpp | 1 - .../short_circuit_and_inside_branch.sol | 14 ++-- .../functions/constructor_hierarchy_3.sol | 2 + .../constructor_hierarchy_diamond.sol | 2 + .../constructor_hierarchy_diamond_2.sol | 2 + .../constructor_hierarchy_diamond_3.sol | 3 + ...ctor_hierarchy_mixed_chain_with_params.sol | 1 + .../constructor_hierarchy_same_var.sol | 1 + ...unction_call_does_not_clear_local_vars.sol | 2 - .../functions/function_inline_chain.sol | 28 ++------ .../functions/functions_recursive.sol | 1 - .../functions_recursive_indirect.sol | 3 +- .../internal_call_with_assertion_1.sol | 25 +++++++ .../internal_call_with_assertion_1_fail.sol | 32 +++++++++ ...rnal_call_with_assertion_inheritance_1.sol | 21 ++++++ ...call_with_assertion_inheritance_1_fail.sol | 26 +++++++ ...ternal_multiple_calls_with_assertion_1.sol | 27 +++++++ ...l_multiple_calls_with_assertion_1_fail.sol | 30 ++++++++ .../functions/library_after_contract.sol | 18 +++++ .../functions/recursive_multi_return.sol | 1 - .../functions/recursive_multi_return_2.sol | 1 - ...ctor_hierarchy_mixed_chain_with_params.sol | 1 + ...ctor_state_variable_init_chain_run_all.sol | 6 ++ ...or_state_variable_init_chain_run_all_2.sol | 5 ++ ...ctor_state_variable_init_function_call.sol | 2 - .../invariants/loop_nested.sol | 7 +- .../invariants/loop_nested_for.sol | 6 -- .../loops/for_1_continue_fail.sol | 7 +- .../loops/for_1_false_positive.sol | 2 - ...or_loop_array_assignment_memory_memory.sol | 2 - .../loops/while_loop_simple_5.sol | 3 +- .../loops/while_nested_break_fail.sol | 2 - .../loops/while_nested_continue_fail.sol | 2 - .../modifier_code_after_placeholder.sol | 1 + .../modifier_multi_functions_recursive.sol | 3 - .../operators/delete_array_index_2d.sol | 6 +- .../smtCheckerTests/special/many.sol | 1 + ...unction_type_to_function_type_internal.sol | 20 ++++-- ...g_literal_to_fixed_bytes_function_call.sol | 1 - .../types/array_aliasing_memory_1.sol | 26 ++++--- .../types/array_aliasing_memory_2.sol | 9 +-- .../types/array_aliasing_memory_3.sol | 9 +-- .../types/array_aliasing_storage_1.sol | 35 ++++++---- .../types/array_aliasing_storage_2.sol | 12 +++- .../types/array_aliasing_storage_3.sol | 13 +++- .../types/array_aliasing_storage_4.sol | 14 ++-- .../types/array_aliasing_storage_5.sol | 21 ++++-- .../types/array_mapping_aliasing_1.sol | 15 ++-- .../types/array_mapping_aliasing_2.sol | 12 ++-- .../types/array_static_aliasing_memory_5.sol | 6 +- .../types/array_static_aliasing_storage_5.sol | 21 ++++-- .../types/array_static_mapping_aliasing_1.sol | 15 ++-- .../types/array_static_mapping_aliasing_2.sol | 12 ++-- .../smtCheckerTests/types/enum_in_library.sol | 2 +- .../types/enum_in_library_2.sol | 4 +- .../smtCheckerTests/types/enum_in_struct.sol | 17 ++--- .../smtCheckerTests/types/fixed_bytes_2.sol | 15 ++++ .../types/mapping_aliasing_1.sol | 7 +- .../types/mapping_aliasing_2.sol | 27 +++++-- .../types/string_literal_comparison_2.sol | 1 + .../smtCheckerTests/types/tuple_function.sol | 1 + .../types/tuple_function_3.sol | 1 + .../smtCheckerTestsJSON/multi.json | 2 +- 67 files changed, 506 insertions(+), 189 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/library_after_contract.sol create mode 100644 test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol diff --git a/Changelog.md b/Changelog.md index 15221bb7d..7dfb7a3e3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -44,6 +44,7 @@ Compiler Features: * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * Debug: Provide reason strings for compiler-generated internal reverts when using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting on ``debug`` mode. * Yul Optimizer: Prune functions that call each other but are otherwise unreferenced. + * SMTChecker: CHC support to internal function calls. Bugfixes: diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index dd1e5136c..141a3eb32 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -432,12 +432,6 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) m_context.newValue(*param); m_context.setUnknownValue(*param); } - - m_errorReporter.warning( - _funCall.location(), - "Assertion checker does not support recursive function calls.", - SecondarySourceLocation().append("Starting from function:", funDef->location()) - ); } else { diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index af50b2722..1e851937c 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -459,6 +459,8 @@ void CHC::endVisit(FunctionCall const& _funCall) SMTEncoder::endVisit(_funCall); break; case FunctionType::Kind::Internal: + internalFunctionCall(_funCall); + break; case FunctionType::Kind::External: case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCall: @@ -525,6 +527,39 @@ void CHC::visitAssert(FunctionCall const& _funCall) m_context.addAssertion(m_error.currentValue() == previousError); } +void CHC::internalFunctionCall(FunctionCall const& _funCall) +{ + solAssert(m_currentContract, ""); + + auto const* function = functionCallToDefinition(_funCall); + if (function) + { + if (m_currentFunction && !m_currentFunction->isConstructor()) + m_callGraph[m_currentFunction].insert(function); + else + m_callGraph[m_currentContract].insert(function); + auto const* contract = function->annotation().contract; + + // Libraries can have constants as their "state" variables, + // so we need to ensure they were constructed correctly. + if (contract->isLibrary()) + m_context.addAssertion(interface(*contract)); + } + + auto previousError = m_error.currentValue(); + + m_context.addAssertion(predicate(_funCall)); + + connectBlocks( + m_currentBlock, + (m_currentFunction && !m_currentFunction->isConstructor()) ? summary(*m_currentFunction) : summary(*m_currentContract), + (m_error.currentValue() > 0) + ); + m_context.addAssertion(m_error.currentValue() == 0); + m_error.increaseIndex(); + m_context.addAssertion(m_error.currentValue() == previousError); +} + void CHC::unknownFunctionCall(FunctionCall const&) { /// Function calls are not handled at the moment, @@ -580,12 +615,7 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c bool CHC::shouldVisit(FunctionDefinition const& _function) const { - if ( - _function.isPublic() && - _function.isImplemented() - ) - return true; - return false; + return _function.isImplemented(); } void CHC::setCurrentBlock( @@ -919,6 +949,34 @@ smt::Expression CHC::predicate( return _block(_arguments); } +smt::Expression CHC::predicate(FunctionCall const& _funCall) +{ + auto const* function = functionCallToDefinition(_funCall); + if (!function) + return smt::Expression(true); + + m_error.increaseIndex(); + vector args{m_error.currentValue()}; + auto const* contract = function->annotation().contract; + + args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : currentStateVariables(); + args += symbolicArguments(_funCall); + for (auto const& var: m_stateVariables) + m_context.variable(*var)->increaseIndex(); + args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(); + + auto const& returnParams = function->returnParameters(); + for (auto param: returnParams) + if (m_context.knownVariable(*param)) + m_context.variable(*param)->increaseIndex(); + else + createVariable(*param); + for (auto const& var: function->returnParameters()) + args.push_back(m_context.variable(*var)->currentValue()); + + return (*m_summaries.at(contract).at(function))(args); +} + void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) { m_interface->addRule(_rule, _ruleName); diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 889c21b6a..4f601a2d5 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -76,6 +76,7 @@ private: void endVisit(Continue const& _node) override; void visitAssert(FunctionCall const& _funCall); + void internalFunctionCall(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); //@} @@ -164,6 +165,8 @@ private: smt::Expression predicate(smt::SymbolicFunctionVariable const& _block); /// @returns a predicate application over @param _arguments. smt::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); + /// @returns the summary predicate for the called function. + smt::Expression predicate(FunctionCall const& _funCall); /// @returns a predicate that defines a constructor summary. smt::Expression summary(ContractDefinition const& _contract); /// @returns a predicate that defines a function summary. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 3b2f48816..0efaf90a9 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -673,7 +673,6 @@ void SMTEncoder::visitAssert(FunctionCall const& _funCall) auto const& args = _funCall.arguments(); solAssert(args.size() == 1, ""); solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); - addPathImpliedExpression(expr(*args.front())); } void SMTEncoder::visitRequire(FunctionCall const& _funCall) diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol index f49a572cf..290d73872 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol @@ -6,23 +6,17 @@ contract c { x = x + 1; return x; } - function g(bool a) public returns (bool) { + function g() public returns (bool) { bool b; - if (a) { - x = 0; - b = (f() == 0) && (f() == 0); - assert(x == 1); - assert(!b); - } else { x = 100; - b = (f() > 0) && (f() > 0); + b = f() > 0; assert(x == 102); // Should fail. assert(!b); - } return b; } } // ---- // Warning: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning: (362-372): Assertion violation happens here +// Warning: (202-218): Assertion violation happens here +// Warning: (242-252): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index 79acf49d7..f6e32d10a 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -19,4 +19,6 @@ contract A is B { } } // ---- +// Warning: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (265-270): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (253-271): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol index 5b339e004..79f0c03dc 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -26,4 +26,6 @@ contract A is B2, B1 { } // ---- // Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (342-347): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (330-348): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol index c0bf299f8..d33247a0e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -26,4 +26,6 @@ contract A is B2, B1 { } // ---- // Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (342-347): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (330-348): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol index 4ad9083fa..b5428d15b 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -31,4 +31,7 @@ contract A is B2, B1 { // Warning: (174-179): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (262-267): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (262-267): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (174-179): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (362-378): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol index 6959f6c88..b6363f8ef 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol @@ -26,4 +26,5 @@ contract A is B { } // ---- // Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (356-370): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol index a607a9556..c4dbe0219 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol @@ -14,3 +14,4 @@ contract A is C { } // ---- // Warning: (148-162): Assertion violation happens here +// Warning: (166-182): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol index ee4504287..1f002796f 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol @@ -9,5 +9,3 @@ contract C { } } // ---- -// Warning: (99-107): Assertion checker does not support recursive function calls. -// Warning: (141-144): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol b/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol index 2647e0771..dd3924e29 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol @@ -2,40 +2,22 @@ pragma experimental SMTChecker; contract C { - uint x; uint y; - uint z; function f() public { - if (x == 1) - x = 2; - else - x = 1; - g(); + if (y != 1) + g(); assert(y == 1); } - function g() public { + function g() internal { y = 1; h(); - assert(z == 1); } - function h() public { - z = 1; - x = 1; + function h() internal { f(); - // This fails for the following calls to the contract: - // h() - // g() h() - // It does not fail for f() g() h() because in that case - // h() will not inline f() since it already is in the callstack. - assert(x == 1); + assert(y == 1); } } // ---- -// Warning: (271-274): Assertion checker does not support recursive function calls. -// Warning: (140-143): Assertion checker does not support recursive function calls. -// Warning: (483-497): Assertion violation happens here -// Warning: (201-204): Assertion checker does not support recursive function calls. -// Warning: (483-497): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol index d2f8ab1db..ee24f0ae5 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol @@ -14,4 +14,3 @@ contract C } // ---- -// Warning: (111-114): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol index d5b83f007..5d3292992 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol @@ -22,5 +22,4 @@ contract C } } // ---- -// Warning: (206-209): Assertion checker does not support recursive function calls. -// Warning: (111-114): Assertion checker does not support recursive function calls. +// Warning: (130-144): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol new file mode 100644 index 000000000..e17e384dd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 0); + x = 1; + } + function f() public { + assert(x == 1); + ++x; + g(); + assert(x == 1); + } + + function g() internal { + assert(x == 2); + --x; + assert(x == 1); + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (245-248): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol new file mode 100644 index 000000000..3f765ecd2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol @@ -0,0 +1,32 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 1); + x = 1; + } + function f() public { + assert(x == 2); + ++x; + g(); + assert(x == 2); + } + + function g() internal { + assert(x == 3); + --x; + assert(x == 2); + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (145-159): Assertion violation happens here +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (227-241): Assertion violation happens here +// Warning: (252-266): Assertion violation happens here +// Warning: (177-191): Assertion violation happens here +// Warning: (227-241): Assertion violation happens here +// Warning: (245-248): Underflow (resulting value less than 0) happens here +// Warning: (252-266): Assertion violation happens here +// Warning: (89-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol new file mode 100644 index 000000000..0d2a3c794 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function f() internal { + assert(x == 1); + --x; + } +} + +contract C is A { + constructor() public { + assert(x == 0); + ++x; + f(); + assert(x == 0); + } +} +// ---- +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol new file mode 100644 index 000000000..da8dec6a2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function f() internal { + assert(x == 2); + --x; + } +} + +contract C is A { + constructor() public { + assert(x == 1); + ++x; + f(); + assert(x == 1); + } +} +// ---- +// Warning: (82-96): Assertion violation happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (82-96): Assertion violation happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (155-169): Assertion violation happens here +// Warning: (82-96): Assertion violation happens here +// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol new file mode 100644 index 000000000..b052f5070 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 0); + x = 1; + } + function f() public { + assert(x == 1); + ++x; + ++x; + g(); + g(); + assert(x == 1); + } + + function g() internal { + --x; + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (170-173): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol new file mode 100644 index 000000000..cb50a931e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 1); + x = 1; + } + function f() public { + assert(x == 2); + ++x; + ++x; + g(); + g(); + assert(x == 3); + } + + function g() internal { + --x; + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (145-159): Assertion violation happens here +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (170-173): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (191-205): Assertion violation happens here +// Warning: (241-244): Underflow (resulting value less than 0) happens here +// Warning: (89-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol new file mode 100644 index 000000000..5e69f7830 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function g(uint y) public { + uint z = L.f(y); + assert(z == y); + } +} + +library L { + function f(uint x) internal returns (uint) { + return x; + } +} + +// ---- +// Warning: (131-190): Function state mutability can be restricted to pure +// Warning: (86-87): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol index 17cf6ea64..0c2a781fc 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol @@ -9,4 +9,3 @@ contract C { } // // ---- -// Warning: (126-129): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol index 22b956584..00f78df3c 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol @@ -25,4 +25,3 @@ a; // ---- // Warning: (72-90): Statement has no effect. // Warning: (96-107): Statement has no effect. -// Warning: (304-307): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol index 6959f6c88..b6363f8ef 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol @@ -26,4 +26,5 @@ contract A is B { } // ---- // Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (356-370): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol index 82d411e47..7be0676d2 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -24,4 +24,10 @@ contract A is B { // ---- // Warning: (171-176): Underflow (resulting value less than 0) happens here // Warning: (171-176): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (171-176): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (260-265): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (282-287): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (282-291): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (308-313): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (296-314): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol index fc6643a42..ff316ce47 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -23,4 +23,9 @@ contract A is B { // ---- // Warning: (171-177): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (231-236): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (171-177): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (283-289): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (306-311): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (294-312): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol index 91798ec0c..36c8ce551 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol @@ -13,5 +13,3 @@ contract C { } } // ---- -// Warning: (162-175): Assertion violation happens here -// Warning: (179-193): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol index 1fb380649..6303158f7 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol @@ -1,5 +1,8 @@ pragma experimental SMTChecker; +// This test gets different results on Linux and OSX. +// Re-enable when fixed (SMTSolvers: z3) + contract Simple { function f() public pure { uint x = 10; @@ -16,9 +19,7 @@ contract Simple { } } // ==== -// SMTSolvers: z3 +// SMTSolvers: none // ---- -// Warning: (172-187): Error trying to invoke SMT solver. // Warning: (195-209): Error trying to invoke SMT solver. -// Warning: (172-187): Assertion violation happens here // Warning: (195-209): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol index e7fde4d5a..ea1c54851 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol @@ -12,10 +12,4 @@ contract Simple { assert(y == x); } } -// ==== -// SMTSolvers: z3 // ---- -// Warning: (164-179): Error trying to invoke SMT solver. -// Warning: (187-201): Error trying to invoke SMT solver. -// Warning: (164-179): Assertion violation happens here -// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol index 7f73f86c1..4b97a7ea0 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol @@ -5,10 +5,6 @@ contract C function f(uint x, bool b) public pure { require(x < 10); for (; x < 10; ) { - if (b) { - x = 20; - continue; - } ++x; } assert(x > 15); @@ -17,4 +13,5 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning: (185-199): Assertion violation happens here +// Warning: (66-72): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (142-156): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol index e57c62313..11cd22d11 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol @@ -13,8 +13,6 @@ contract C assert(x > 0); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (296-309): Error trying to invoke SMT solver. // Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol index 1a9216de0..0694493f5 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol @@ -14,8 +14,6 @@ contract LoopFor2 { assert(b[0] == 900); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (281-301): Assertion violation happens here // Warning: (305-324): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol index c85e7e3fb..e022e0f29 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol @@ -4,6 +4,7 @@ contract C { function f(uint x, uint y) public pure { x = 7; while ((x = y) > 0) { + --y; } assert(x == 7); } @@ -11,4 +12,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning: (216-230): Assertion violation happens here +// Warning: (224-238): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol index e9752b3a8..986e7205b 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -28,8 +28,6 @@ contract C assert(x >= 20); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (329-344): Assertion violation happens here // Warning: (380-395): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol index 4447cf6d7..71238c3be 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol @@ -26,8 +26,6 @@ contract C assert(x >= 20); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (323-338): Assertion violation happens here // Warning: (362-377): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol index 7a420c28f..702c24f4b 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,4 +21,5 @@ contract C } } // ---- +// Warning: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (136-149): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol index b01fe812e..16d071fdb 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol @@ -17,6 +17,3 @@ contract C } } // ---- -// Warning: (86-93): Assertion checker does not support recursive function calls. -// Warning: (86-93): Assertion checker does not support recursive function calls. -// Warning: (253-266): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol index 9cd7de9e6..8a1ba5e6e 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol @@ -4,7 +4,7 @@ contract C { uint[][] a; function f(bool b) public { - require(a[2][3] == 4); + a[2][3] = 4; if (b) delete a; else @@ -13,5 +13,7 @@ contract C assert(a[1][1] == 0); } } +// ==== +// SMTSolvers: z3 // ---- -// Warning: (184-204): Assertion violation happens here +// Warning: (174-194): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol index e9ef98b26..a8ae53997 100644 --- a/test/libsolidity/smtCheckerTests/special/many.sol +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -20,6 +20,7 @@ contract C // Warning: (165-204): Assertion violation happens here // Warning: (208-240): Assertion violation happens here // Warning: (244-275): Assertion violation happens here +// Warning: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (304-332): Assertion violation happens here // Warning: (336-352): Assertion violation happens here // Warning: (356-379): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol index 1657a95c6..abee678c0 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -1,13 +1,23 @@ pragma experimental SMTChecker; contract C { + function(uint) returns (uint) a; + function(uint) returns (uint) b; function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { assert(g(2) == h(2)); assert(g == h); } + function g() public { + f(a, b); + } } // ---- -// Warning: (146-150): Assertion checker does not yet implement this type of function call. -// Warning: (154-158): Assertion checker does not yet implement this type of function call. -// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons -// Warning: (139-159): Assertion violation happens here -// Warning: (163-177): Assertion violation happens here +// Warning: (214-218): Assertion checker does not yet implement this type of function call. +// Warning: (222-226): Assertion checker does not yet implement this type of function call. +// Warning: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (207-227): Assertion violation happens here +// Warning: (231-245): Assertion violation happens here +// Warning: (214-218): Assertion checker does not yet implement this type of function call. +// Warning: (222-226): Assertion checker does not yet implement this type of function call. +// Warning: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (207-227): Assertion violation happens here +// Warning: (231-245): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol index b2701fdf9..d94f3204b 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -10,5 +10,4 @@ contract B { } // ---- // Warning: (162-184): Assertion violation happens here -// Warning: (136-158): Assertion violation happens here // Warning: (162-184): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol index 8cd468652..00d2965bc 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -1,4 +1,5 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { @@ -8,24 +9,21 @@ contract C uint[][] memory cc, uint8[][] memory dd, uint[][][] memory eee - ) internal pure { - require(a[0] == 2); - require(cc[0][0] == 50); - require(dd[0][0] == 10); - require(eee[0][0][0] == 50); + ) public pure { + a[0] = 2; + cc[0][0] = 50; + dd[0][0] = 10; + eee[0][0][0] = 50; b[0] = 1; - // Fails because b == a is possible. - assert(a[0] == 2); - // Fails because b == cc[0] is possible. - assert(cc[0][0] == 50); + // Fails because + // b == a is possible + // b == cc[0] is possible + // b == ee[0][0] is possible + assert(a[0] == 2 || cc[0][0] == 50 || eee[0][0][0] == 50); // Should not fail since knowledge is erased only for uint[]. assert(dd[0][0] == 10); - // Fails because b == ee[0][0] is possible. - assert(eee[0][0][0] == 50); assert(b[0] == 1); } } // ---- -// Warning: (345-362): Assertion violation happens here -// Warning: (409-431): Assertion violation happens here -// Warning: (571-597): Assertion violation happens here +// Warning: (400-457): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol index d015d1471..94e7ccfcf 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol @@ -1,11 +1,12 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { uint[] array; - function f(uint[] memory a, uint[] memory b) internal view { - require(array[0] == 42); - require(a[0] == 2); + function f(uint[] memory a, uint[] memory b) public { + array[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. @@ -15,4 +16,4 @@ contract C } } // ---- -// Warning: (314-331): Assertion violation happens here +// Warning: (321-338): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol index f6d6b1977..1ae2b9356 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol @@ -1,12 +1,13 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { uint[] array; - function f(uint[] memory a, uint[] memory b) internal view { - require(array[0] == 42); + function f(uint[] memory a, uint[] memory b) public { + array[0] = 42; uint[] storage c = array; - require(a[0] == 2); + a[0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. @@ -19,4 +20,4 @@ contract C } } // ---- -// Warning: (469-486): Assertion violation happens here +// Warning: (476-493): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol index 6461634e1..2aacc5c0d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol @@ -4,7 +4,9 @@ contract C { uint[] array; uint[][] array2d; + uint[][][][] array4d; uint8[] tinyArray; + uint8[][][] tinyArray3d; function f( uint[] storage a, uint[] storage b, @@ -12,13 +14,13 @@ contract C uint8[][] storage dd, uint[][][] storage eee ) internal { - require(a[0] == 2); - require(array[0] == 42); - require(array2d[0][0] == 42); - require(tinyArray[0] == 42); - require(cc[0][0] == 42); - require(dd[0][0] == 42); - require(eee[0][0][0] == 42); + a[0] = 2; + array[0] = 42; + array2d[0][0] = 42; + tinyArray[0] = 42; + cc[0][0] = 42; + dd[0][0] = 42; + eee[0][0][0] = 42; b[0] = 1; // Fails because b == a is possible. assert(a[0] == 2); @@ -36,10 +38,19 @@ contract C assert(eee[0][0][0] == 42); assert(b[0] == 1); } + + function g(uint a, uint b, uint c, uint d, uint e) public { + f(array2d[a], array2d[b], array4d[c][c], tinyArray3d[d], array4d[e]); + } } // ---- -// Warning: (489-506): Assertion violation happens here -// Warning: (553-575): Assertion violation happens here -// Warning: (627-654): Assertion violation happens here -// Warning: (795-817): Assertion violation happens here -// Warning: (957-983): Assertion violation happens here +// Warning: (468-485): Assertion violation happens here +// Warning: (532-554): Assertion violation happens here +// Warning: (606-633): Assertion violation happens here +// Warning: (774-796): Assertion violation happens here +// Warning: (936-962): Assertion violation happens here +// Warning: (468-485): Assertion violation happens here +// Warning: (532-554): Assertion violation happens here +// Warning: (606-633): Assertion violation happens here +// Warning: (774-796): Assertion violation happens here +// Warning: (936-962): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol index 4b134e58e..3d60c836a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol @@ -2,9 +2,14 @@ pragma experimental SMTChecker; contract C { + uint[][] array2d; + function g(uint x, uint y, uint[] memory c) public { + f(array2d[x], array2d[y], c); + } + function f(uint[] storage a, uint[] storage b, uint[] memory c) internal { - require(c[0] == 42); - require(a[0] == 2); + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage references should not // erase knowledge about memory references. @@ -15,4 +20,5 @@ contract C } } // ---- -// Warning: (347-364): Assertion violation happens here +// Warning: (436-453): Assertion violation happens here +// Warning: (436-453): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol index d70cc18bb..68438eb6d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol @@ -2,10 +2,14 @@ pragma experimental SMTChecker; contract C { + uint[][] array2d; + function g(uint x, uint y, uint[] memory c) public { + f(array2d[x], array2d[y], c); + } function f(uint[] storage a, uint[] storage b, uint[] memory c) internal { uint[] memory d = c; - require(c[0] == 42); - require(a[0] == 2); + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage references should not // erase knowledge about memory references. @@ -19,4 +23,7 @@ contract C } } // ---- -// Warning: (497-514): Assertion violation happens here +// Warning: (524-542): Assertion violation happens here +// Warning: (585-602): Assertion violation happens here +// Warning: (524-542): Assertion violation happens here +// Warning: (585-602): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol index 13dc6fcc2..384a89b4f 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol @@ -3,9 +3,10 @@ pragma experimental SMTChecker; contract C { uint[] array; + uint[][] array2d; function f(uint[] storage a, uint[] storage b) internal { - require(a[0] == 2); - require(b[0] == 42); + a[0] = 2; + b[0] = 42; array[0] = 1; // Fails because array == a is possible. assert(a[0] == 2); @@ -13,7 +14,12 @@ contract C assert(b[0] == 42); assert(array[0] == 1); } + function g(uint x, uint y) public { + f(array2d[x], array2d[y]); + } } // ---- -// Warning: (226-243): Assertion violation happens here -// Warning: (290-308): Assertion violation happens here +// Warning: (225-242): Assertion violation happens here +// Warning: (289-307): Assertion violation happens here +// Warning: (225-242): Assertion violation happens here +// Warning: (289-307): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol index ff47cc0e4..e94ad4580 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol @@ -4,20 +4,29 @@ contract C { uint[] b; uint[] d; + uint[][] array2d; + function g(uint x, uint[] memory c) public { + f(array2d[x], c); + } function f(uint[] storage a, uint[] memory c) internal { - require(d[0] == 42); - require(c[0] == 42); - require(a[0] == 2); + d[0] = 42; + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage variables should not // erase knowledge about memory references. assert(c[0] == 42); - // Should not fail since b == d is not possible. + // Fails because d == a is possible. assert(d[0] == 42); - // Fails because b == a is possible. + // Fails because b == a and d == a are possible. assert(a[0] == 2); + // b == a is possible, but does not fail because b + // was the last assignment. assert(b[0] == 1); } } // ---- -// Warning: (446-463): Assertion violation happens here +// Warning: (431-449): Assertion violation happens here +// Warning: (504-521): Assertion violation happens here +// Warning: (431-449): Assertion violation happens here +// Warning: (504-521): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol index 062e9776d..dc69dd55a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol @@ -7,9 +7,9 @@ contract C mapping (uint => uint8)[] severalMaps8; mapping (uint => uint)[][] severalMaps3d; function f(mapping (uint => uint) storage map) internal { - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; map[0] = 2; // Should fail since map == severalMaps[0] is possible. assert(severalMaps[0][0] == 42); @@ -18,7 +18,12 @@ contract C // Should fail since map == severalMaps3d[0][0] is possible. assert(severalMaps3d[0][0][0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (451-482): Assertion violation happens here -// Warning: (665-701): Assertion violation happens here +// Warning: (421-452): Assertion violation happens here +// Warning: (635-671): Assertion violation happens here +// Warning: (421-452): Assertion violation happens here +// Warning: (635-671): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol index 1d5ab2687..2e16e7b5d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol @@ -8,9 +8,9 @@ contract C mapping (uint => uint)[][] severalMaps3d; function f(mapping (uint => uint) storage map) internal { map[0] = 42; - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; singleMap[0] = 2; // Should not fail since singleMap == severalMaps[0] is not possible. assert(severalMaps[0][0] == 42); @@ -21,6 +21,10 @@ contract C // Should fail since singleMap == map is possible. assert(map[0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (807-827): Assertion violation happens here +// Warning: (777-797): Assertion violation happens here +// Warning: (777-797): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol index 99e993032..dd846b483 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol @@ -2,7 +2,7 @@ pragma experimental SMTChecker; contract C { - function f(uint[2] memory a, uint[2] memory b, uint[2] memory c) internal pure { + function f(uint[2] memory a, uint[2] memory b, uint[2] memory c) public pure { require(c[0] == 42); require(a[0] == 2); b[0] = 1; @@ -14,5 +14,5 @@ contract C } } // ---- -// Warning: (230-248): Assertion violation happens here -// Warning: (295-312): Assertion violation happens here +// Warning: (228-246): Assertion violation happens here +// Warning: (293-310): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol index fa0c5eae1..45c12263b 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol @@ -2,18 +2,25 @@ pragma experimental SMTChecker; contract C { - uint[2] b; + uint[2] b1; + uint[2] b2; function f(uint[2] storage a, uint[2] memory c) internal { - require(c[0] == 42); - require(a[0] == 2); - b[0] = 1; + c[0] = 42; + a[0] = 2; + b1[0] = 1; // Erasing knowledge about storage variables should not // erase knowledge about memory references. assert(c[0] == 42); - // Fails because b == a is possible. + // Fails because b1 == a is possible. assert(a[0] == 2); - assert(b[0] == 1); + assert(b1[0] == 1); + } + function g(bool x, uint[2] memory c) public { + if (x) f(b1, c); + else f(b2, c); } } // ---- -// Warning: (342-359): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol index a675c29e2..7e31b10ae 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol @@ -7,9 +7,9 @@ contract C mapping (uint => uint8)[2] severalMaps8; mapping (uint => uint)[2][2] severalMaps3d; function f(mapping (uint => uint) storage map) internal { - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; map[0] = 2; // Should fail since map == severalMaps[0] is possible. assert(severalMaps[0][0] == 42); @@ -18,7 +18,12 @@ contract C // Should fail since map == severalMaps3d[0][0] is possible. assert(severalMaps3d[0][0][0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (455-486): Assertion violation happens here -// Warning: (669-705): Assertion violation happens here +// Warning: (425-456): Assertion violation happens here +// Warning: (639-675): Assertion violation happens here +// Warning: (425-456): Assertion violation happens here +// Warning: (639-675): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol index c9c96a0a7..e2f597943 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol @@ -8,9 +8,9 @@ contract C mapping (uint => uint)[2][2] severalMaps3d; function f(mapping (uint => uint) storage map) internal { map[0] = 42; - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; singleMap[0] = 2; // Should not fail since singleMap == severalMaps[0] is not possible. assert(severalMaps[0][0] == 42); @@ -21,6 +21,10 @@ contract C // Should fail since singleMap == map is possible. assert(map[0] == 42); } + function g(uint x) public { + f(severalMaps3d[x][0]); + } } // ---- -// Warning: (811-831): Assertion violation happens here +// Warning: (781-801): Assertion violation happens here +// Warning: (781-801): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library.sol index a1648c66e..9c1c3caaa 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library.sol @@ -8,7 +8,7 @@ library L contract C { enum E { Left, Right } - function f(E _d) internal pure { + function f(E _d) public pure { _d = E.Left; assert(_d == E.Left); } diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol index 2bd2afbda..be1d40be4 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol @@ -8,10 +8,10 @@ library L contract C { enum E { Left, Right } - function f(E _d) internal pure { + function f(E _d) public pure { _d = E.Right; assert(_d == E.Left); } } // ---- -// Warning: (161-181): Assertion violation happens here +// Warning: (159-179): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol index 47bdd0c66..4821518fd 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol @@ -1,19 +1,20 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { enum D { Left, Right } struct S { uint x; D d; } - function f(S memory s) internal pure { + function f(S memory s) public pure { s.d = D.Left; assert(s.d == D.Left); } } // ---- -// Warning: (109-119): Assertion checker does not yet support the type of this variable. -// Warning: (139-142): Assertion checker does not yet support this expression. -// Warning: (139-140): Assertion checker does not yet implement type struct C.S memory -// Warning: (139-151): Assertion checker does not yet implement such assignments. -// Warning: (162-165): Assertion checker does not yet support this expression. -// Warning: (162-163): Assertion checker does not yet implement type struct C.S memory -// Warning: (155-176): Assertion violation happens here +// Warning: (143-153): Assertion checker does not yet support the type of this variable. +// Warning: (171-174): Assertion checker does not yet support this expression. +// Warning: (171-172): Assertion checker does not yet implement type struct C.S memory +// Warning: (171-183): Assertion checker does not yet implement such assignments. +// Warning: (194-197): Assertion checker does not yet support this expression. +// Warning: (194-195): Assertion checker does not yet implement type struct C.S memory +// Warning: (187-208): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol new file mode 100644 index 000000000..bb2513790 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C +{ + bytes32 x; + function f(bytes8 y) public view { + assert(x == g()); + assert(x != y); + } + function g() public view returns (bytes32) { + return x; + } +} +// ---- +// Warning: (116-130): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol index 39d096f59..8170d67e6 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol @@ -5,8 +5,9 @@ contract C mapping (uint => uint) a; mapping (uint => uint) b; - function f() public { - require(a[1] == b[1]); + function f(uint x) public { + a[1] = x; + b[1] = x; a[1] = 2; mapping (uint => uint) storage c = a; assert(c[1] == 2); @@ -15,4 +16,4 @@ contract C } } // ---- -// Warning: (261-281): Assertion violation happens here +// Warning: (266-286): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol index 86af187ad..f31192b3a 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol @@ -6,10 +6,10 @@ contract C mapping (uint => mapping (uint => uint)) maps; mapping (uint => mapping (uint => uint8)) maps8; function f(mapping (uint => uint) storage map1, mapping (uint => uint) storage map2) internal { - require(map1[0] == 2); - require(a[0] == 42); - require(maps[0][0] == 42); - require(maps8[0][0] == 42); + map1[0] = 2; + a[0] = 42; + maps[0][0] = 42; + maps8[0][0] = 42; map2[0] = 1; // Fails because map2 == map1 is possible. assert(map1[0] == 2); @@ -21,8 +21,21 @@ contract C assert(maps8[0][0] == 42); assert(map2[0] == 1); } + + function g(bool b, uint x, uint y) public { + if (b) + f(a, maps[y]); + else + f(maps[x], maps[y]); + } } // ---- -// Warning: (437-457): Assertion violation happens here -// Warning: (503-521): Assertion violation happens here -// Warning: (573-597): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol index 4a9291f89..afb9a9ea0 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol @@ -11,3 +11,4 @@ contract C { } // ---- // Warning: (147-166): Assertion violation happens here +// Warning: (170-190): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function.sol b/test/libsolidity/smtCheckerTests/types/tuple_function.sol index 53b197395..8103a0203 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function.sol @@ -15,3 +15,4 @@ contract C } // ---- // Warning: (182-196): Assertion violation happens here +// Warning: (200-214): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol index 1e548403d..e76db56d1 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol @@ -17,3 +17,4 @@ contract C } // ---- // Warning: (205-219): Assertion violation happens here +// Warning: (223-237): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTestsJSON/multi.json b/test/libsolidity/smtCheckerTestsJSON/multi.json index b125712fd..b1be75073 100644 --- a/test/libsolidity/smtCheckerTestsJSON/multi.json +++ b/test/libsolidity/smtCheckerTestsJSON/multi.json @@ -3,7 +3,7 @@ { "smtlib2responses": { - "0x82fb8ee094f0f56b7a63a74177b54a1710d6fc531d426f288c18f36b76cf6a8b": "sat\n((|EVALEXPR_0| 1))\n", + "0x9c50514d749eabf3c13d97ad7d787e682dd99a114bad652b10a01b8c6ad6c1fb": "sat\n((|EVALEXPR_0| 1))\n", "0xb524e7c577188e2e36f0e67fead51269fa0f8b8fb41bff2d973dcf584d38cd1e": "sat\n((|EVALEXPR_0| 0))\n" } }