From 0e614bf5cbaeb2659b74d9a7e224b760c3bb939b Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 27 Jul 2020 15:06:39 +0200 Subject: [PATCH 001/139] Document encoding of mappings in library calls. --- docs/contracts/libraries.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index e28a16da2..5630c7212 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -229,7 +229,9 @@ The following identifiers are used for the types in the signatures: - Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and ``[M]`` for fixed-size arrays of ``M`` elements. - Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. - - Storage pointer types use the type identifier of their corresponding non-storage type, but append a single space + - Storage pointer mappings use ``mapping( => ) storage`` where ```` and ```` are + the identifiers for the key and value types of the mapping, respectively. + - Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space followed by ``storage`` to it. The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a From 6227edda14e3019e59838aa308968beecad35fa5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Jul 2020 13:22:51 +0200 Subject: [PATCH 002/139] Speed up osx build. --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index f92a94212..ea5507633 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -540,6 +540,7 @@ jobs: environment: TERM: xterm CMAKE_BUILD_TYPE: Release + MAKEFLAGS: -j 5 steps: - checkout - restore_cache: From d9cb8c8c5e68e8156daf064d25847054ddb4a32c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Jul 2020 16:05:52 +0100 Subject: [PATCH 003/139] Add ScannerKind and replace supportPeriodInIdentifier --- liblangutil/Scanner.cpp | 10 ++-------- liblangutil/Scanner.h | 21 ++++++++++++++++----- libyul/AsmParser.cpp | 4 ++-- test/libsolidity/SolidityScanner.cpp | 6 +++--- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 3e125f04e..017a65e18 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -147,7 +147,7 @@ void Scanner::reset(shared_ptr _source) void Scanner::reset() { m_source->reset(); - m_supportPeriodInIdentifier = false; + m_kind = ScannerKind::Solidity; m_char = m_source->get(); skipWhitespace(); next(); @@ -163,12 +163,6 @@ void Scanner::setPosition(size_t _offset) next(); } -void Scanner::supportPeriodInIdentifier(bool _value) -{ - m_supportPeriodInIdentifier = _value; - rescan(); -} - bool Scanner::scanHexByte(char& o_scannedByte) { char x = 0; @@ -970,7 +964,7 @@ tuple Scanner::scanIdentifierOrKeyword() LiteralScope literal(this, LITERAL_TYPE_STRING); addLiteralCharAndAdvance(); // Scan the rest of the identifier characters. - while (isIdentifierPart(m_char) || (m_char == '.' && m_supportPeriodInIdentifier)) + while (isIdentifierPart(m_char) || (m_char == '.' && m_kind == ScannerKind::Yul)) addLiteralCharAndAdvance(); literal.complete(); return TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 24ec098ab..f44a9940a 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -68,6 +68,12 @@ class AstRawString; class AstValueFactory; class ParserRecorder; +enum class ScannerKind +{ + Solidity, + Yul +}; + enum class ScannerError { NoError, @@ -107,9 +113,14 @@ public: /// Resets scanner to the start of input. void reset(); - /// Enables or disables support for period in identifier. - /// This re-scans the current token and comment literal and thus invalidates it. - void supportPeriodInIdentifier(bool _value); + /// Changes the scanner mode. + void setScannerMode(ScannerKind _kind) + { + m_kind = _kind; + + // Invalidate lookahead buffer. + rescan(); + } /// @returns the next token and advances input Token next(); @@ -249,8 +260,6 @@ private: size_t sourcePos() const { return m_source->position(); } bool isSourcePastEndOfInput() const { return m_source->isPastEndOfInput(); } - bool m_supportPeriodInIdentifier = false; - enum TokenIndex { Current, Next, NextNext }; TokenDesc m_skippedComments[3] = {}; // desc for the current, next and nextnext skipped comment @@ -258,6 +267,8 @@ private: std::shared_ptr m_source; + ScannerKind m_kind = ScannerKind::Solidity; + /// one character look-ahead, equals 0 at end of input char m_char; }; diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 6c5d97e36..5dd0d854e 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -42,8 +42,8 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ { m_recursionDepth = 0; - _scanner->supportPeriodInIdentifier(true); - ScopeGuard resetScanner([&]{ _scanner->supportPeriodInIdentifier(false); }); + _scanner->setScannerMode(ScannerKind::Yul); + ScopeGuard resetScanner([&]{ _scanner->setScannerMode(ScannerKind::Solidity); }); try { diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 8f2e6ed59..381e8e318 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE(leading_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream("function .a(", "")); - scanner.supportPeriodInIdentifier(true); + scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -233,7 +233,7 @@ BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream("function a...a(", "")); - scanner.supportPeriodInIdentifier(true); + scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(trailing_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream("function a.(", "")); - scanner.supportPeriodInIdentifier(true); + scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); From 8a136c96e992750a0be869881e72206a945d2405 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Jul 2020 17:16:57 +0200 Subject: [PATCH 004/139] Set version to 0.7.1 --- CMakeLists.txt | 2 +- Changelog.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 31a36aae2..fba1830b9 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.7.0") +set(PROJECT_VERSION "0.7.1") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index 2b72ccef7..2422ad026 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,15 @@ +### 0.7.1 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + + ### 0.7.0 (2020-07-28) Breaking changes: From 34d66431d67ec76916b83d35146dcf89f427ed07 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Jul 2020 17:30:40 +0200 Subject: [PATCH 005/139] More parallel jobs. --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index f92a94212..365d24194 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -410,6 +410,7 @@ jobs: CC: clang CXX: clang++ CMAKE_OPTIONS: -DSANITIZE=address + MAKEFLAGS: -j 3 steps: - checkout - run: *run_build @@ -433,6 +434,7 @@ jobs: <<: *build_ubuntu2004 environment: FORCE_RELEASE: ON + MAKEFLAGS: -j 10 b_ubu18: &build_ubuntu1804 docker: @@ -440,6 +442,7 @@ jobs: environment: CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 CMAKE_BUILD_TYPE: RelWithDebugInfo + MAKEFLAGS: -j 3 steps: - checkout - run: *run_build @@ -451,6 +454,7 @@ jobs: environment: COVERAGE: ON CMAKE_BUILD_TYPE: Debug + MAKEFLAGS: -j 10 steps: - checkout - run: *run_build @@ -484,6 +488,7 @@ jobs: environment: CMAKE_BUILD_TYPE: Debug CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF + MAKEFLAGS: -j 10 steps: - checkout - run: *run_build @@ -496,6 +501,7 @@ jobs: CXX: clang++ TERM: xterm CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake + MAKEFLAGS: -j 3 steps: - checkout - run: *setup_prerelease_commit_hash @@ -524,6 +530,7 @@ jobs: - image: archlinux/base environment: TERM: xterm + MAKEFLAGS: -j 3 steps: - run: name: Install build dependencies @@ -625,6 +632,7 @@ jobs: <<: *build_ubuntu2004 environment: CMAKE_OPTIONS: -DSANITIZE=address + MAKEFLAGS: -j 10 CMAKE_BUILD_TYPE: Release steps: - checkout From 5394435bea4e553a86f872b6b2512c50bdef1628 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 27 Jul 2020 20:31:51 +0200 Subject: [PATCH 006/139] Use assert to check zero size base type --- libsolidity/analysis/DeclarationTypeChecker.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 15cbc30ba..96b71f45e 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -243,12 +243,7 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) solAssert(!m_errorReporter.errors().empty(), ""); return; } - if (baseType->storageBytes() == 0) - m_errorReporter.fatalTypeError( - 6493_error, - _typeName.baseType().location(), - "Illegal base type of storage size zero for array." - ); + solAssert(baseType->storageBytes() != 0, "Illegal base type of storage size zero for array."); if (Expression const* length = _typeName.length()) { TypePointer& lengthTypeGeneric = length->annotation().type; From ad1798b000ebae16cca9a782859ca605114282f9 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 27 Jul 2020 12:11:10 +0200 Subject: [PATCH 007/139] [SMTChecker] Fix ICE on fixed bytes access --- Changelog.md | 2 +- libsolidity/formal/SMTEncoder.cpp | 19 +++++++++---------- .../types/fixed_bytes_access_1.sol | 9 +++++++++ .../types/fixed_bytes_access_2.sol | 11 +++++++++++ .../types/fixed_bytes_access_3.sol | 18 ++++++++++++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol create mode 100644 test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol create mode 100644 test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol diff --git a/Changelog.md b/Changelog.md index 2422ad026..2e857a89f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,7 +7,7 @@ Compiler Features: Bugfixes: - + * SMTChecker: Fix internal error on fixed bytes index access. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index b101a3147..50ede6d13 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -923,6 +923,15 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) if (_indexAccess.annotation().type->category() == Type::Category::TypeType) return; + if (_indexAccess.baseExpression().annotation().type->category() == Type::Category::FixedBytes) + { + m_errorReporter.warning( + 7989_error, + _indexAccess.location(), + "Assertion checker does not yet support index accessing fixed bytes." + ); + return; + } shared_ptr array; if (auto const* id = dynamic_cast(&_indexAccess.baseExpression())) @@ -930,16 +939,6 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) auto varDecl = identifierToVariable(*id); solAssert(varDecl, ""); array = m_context.variable(*varDecl); - - if (varDecl->type()->category() == Type::Category::FixedBytes) - { - m_errorReporter.warning( - 7989_error, - _indexAccess.location(), - "Assertion checker does not yet support index accessing fixed bytes." - ); - return; - } } else if (auto const* innerAccess = dynamic_cast(&_indexAccess.baseExpression())) { diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol new file mode 100644 index 000000000..636dd7ff1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract c { + bytes10[6] data2; + function test() public view returns (bytes10 r2) { + r2 = data2[4][5]; + } +} +// ---- +// Warning 7989: (123-134): Assertion checker does not yet support index accessing fixed bytes. diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol new file mode 100644 index 000000000..e81d093f8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bytes calldata x, uint y) external pure { + x[8][0]; + x[8][5%y]; + } +} +// ---- +// Warning 7989: (101-108): Assertion checker does not yet support index accessing fixed bytes. +// Warning 7989: (112-121): Assertion checker does not yet support index accessing fixed bytes. +// Warning 3046: (117-120): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol new file mode 100644 index 000000000..5348ca1b4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; +contract C { + bytes16[][] a; + function g() internal view returns (bytes16[] storage) { + return a[2]; + } + function h() internal view returns (bytes16) { + return a[2][2]; + } + function f() external view { + g()[3][4]; + h()[5]; + } +} +// ---- +// Warning 9118: (238-244): Assertion checker does not yet implement this expression. +// Warning 7989: (238-247): Assertion checker does not yet support index accessing fixed bytes. +// Warning 7989: (251-257): Assertion checker does not yet support index accessing fixed bytes. From dbeacbafa5ab3d066c1aa4753b4cef01fd0bdf3b Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 27 Jul 2020 23:42:26 +0200 Subject: [PATCH 008/139] Cover "Override changes modifier to public state variable." error --- .../syntaxTests/modifiers/modifier_overrides_variable.sol | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol new file mode 100644 index 000000000..1f170601f --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol @@ -0,0 +1,5 @@ +contract A { modifier mod(uint a) { _; } } +contract B is A { uint public mod; } +// ---- +// DeclarationError 9097: (61-76): Identifier already declared. +// TypeError 1456: (61-76): Override changes modifier to public state variable. From ffdb0e37ff788afdf54ea1224a8c8184043322f9 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 28 Jul 2020 01:37:51 +0200 Subject: [PATCH 009/139] Complete error coverage of ReferenceResolver --- libsolidity/analysis/ReferencesResolver.cpp | 10 +--------- .../natspec/invalid/docstring_inheritdoc_twice.sol | 9 +++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 82cb18937..e2e92a126 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -231,15 +231,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) string(".slot").size() : string(".offset").size() )); - if (realName.empty()) - { - m_errorReporter.declarationError( - 4794_error, - _identifier.location, - "In variable names .slot and .offset can only be used as a suffix." - ); - return; - } + solAssert(!realName.empty(), "Empty name."); declarations = m_resolver.nameFromCurrentScope(realName); if (!declarations.empty()) // To support proper path resolution, we have to use pathFromCurrentScope. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol new file mode 100644 index 000000000..d59dea299 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol @@ -0,0 +1,9 @@ +contract B {} + +contract C { + /// @inheritdoc B + /// @inheritdoc B + function f() internal {} +} +// ---- +// DocstringParsingError 5142: (32-71): Documentation tag @inheritdoc can only be given once. From b8b78a69821681c8312f1c797adb6e43a0db14b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Jul 2020 23:20:49 +0200 Subject: [PATCH 010/139] Use cbor metadata in gas cost tests. --- libsolidity/interface/CompilerStack.cpp | 27 +++++++++++++++++-------- libsolidity/interface/CompilerStack.h | 5 ++++- test/libsolidity/GasCosts.cpp | 24 +++++++++++----------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 41a9040c4..b79cf000c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -824,6 +824,14 @@ string const& CompilerStack::metadata(string const& _contractName) const return metadata(contract(_contractName)); } +bytes CompilerStack::cborMetadata(string const& _contractName) const +{ + if (m_stackState < AnalysisPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + return createCBORMetadata(contract(_contractName)); +} + string const& CompilerStack::metadata(Contract const& _contract) const { if (m_stackState < AnalysisPerformed) @@ -1063,10 +1071,7 @@ void CompilerStack::compileContract( shared_ptr compiler = make_shared(m_evmVersion, m_revertStrings, m_optimiserSettings); compiledContract.compiler = compiler; - bytes cborEncodedMetadata = createCBORMetadata( - metadata(compiledContract), - !onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures) - ); + bytes cborEncodedMetadata = createCBORMetadata(compiledContract); try { @@ -1390,18 +1395,24 @@ private: bytes m_data; }; -bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimentalMode) +bytes CompilerStack::createCBORMetadata(Contract const& _contract) const { + bool const experimentalMode = !onlySafeExperimentalFeaturesActivated( + _contract.contract->sourceUnit().annotation().experimentalFeatures + ); + + string meta = metadata(_contract); + MetadataCBOREncoder encoder; if (m_metadataHash == MetadataHash::IPFS) - encoder.pushBytes("ipfs", util::ipfsHash(_metadata)); + encoder.pushBytes("ipfs", util::ipfsHash(meta)); else if (m_metadataHash == MetadataHash::Bzzr1) - encoder.pushBytes("bzzr1", util::bzzr1Hash(_metadata).asBytes()); + encoder.pushBytes("bzzr1", util::bzzr1Hash(meta).asBytes()); else solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); - if (_experimentalMode) + if (experimentalMode) encoder.pushBool("experimental", true); if (m_release) encoder.pushBytes("solc", VersionCompactBytes); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index ce0f949b9..37472d907 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -314,6 +314,9 @@ public: /// @returns the Contract Metadata std::string const& metadata(std::string const& _contractName) const; + /// @returns the cbor-encoded metadata. + bytes cborMetadata(std::string const& _contractName) const; + /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions Json::Value gasEstimates(std::string const& _contractName) const; @@ -402,7 +405,7 @@ private: std::string createMetadata(Contract const& _contract) const; /// @returns the metadata CBOR for the given serialised metadata JSON. - bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode); + bytes createCBORMetadata(Contract const& _contract) const; /// @returns the contract ABI as a JSON object. /// This will generate the JSON object and store it in the Contract object if it is not present yet. diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index bb86aaa83..946a5d8a4 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -39,18 +39,18 @@ namespace solidity::frontend::test #define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt, _evmVersion) \ do \ { \ - u256 ipfsCost = GasMeter::dataGas(util::ipfsHash(m_compiler.metadata(m_compiler.lastContractName())), true, _evmVersion); \ + u256 metaCost = GasMeter::dataGas(m_compiler.cborMetadata(m_compiler.lastContractName()), true, _evmVersion); \ u256 gasOpt{_gasOpt}; \ u256 gasNoOpt{_gasNoOpt}; \ u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \ BOOST_CHECK_MESSAGE( \ - m_gasUsed >= ipfsCost, \ + m_gasUsed >= metaCost, \ "Gas used: " + \ m_gasUsed.str() + \ - " is less than the data cost for the IPFS hash: " + \ - u256(ipfsCost).str() \ + " is less than the data cost for the cbor metadata: " + \ + u256(metaCost).str() \ ); \ - u256 gasUsed = m_gasUsed - ipfsCost; \ + u256 gasUsed = m_gasUsed - metaCost; \ BOOST_CHECK_MESSAGE( \ gas == gasUsed, \ "Gas used: " + \ @@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(string_storage) auto evmVersion = solidity::test::CommonOptions::get().evmVersion(); if (evmVersion <= EVMVersion::byzantium()) - CHECK_DEPLOY_GAS(134145, 130831, evmVersion); + CHECK_DEPLOY_GAS(133045, 129731, evmVersion); // This is only correct on >=Constantinople. else if (CommonOptions::get().useABIEncoderV2) { @@ -105,22 +105,22 @@ BOOST_AUTO_TEST_CASE(string_storage) { // Costs with 0 are cases which cannot be triggered in tests. if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(0, 123969, evmVersion); + CHECK_DEPLOY_GAS(0, 122869, evmVersion); else - CHECK_DEPLOY_GAS(0, 110969, evmVersion); + CHECK_DEPLOY_GAS(0, 110701, evmVersion); } else { if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(147835, 123969, evmVersion); + CHECK_DEPLOY_GAS(146671, 123969, evmVersion); else - CHECK_DEPLOY_GAS(131871, 110969, evmVersion); + CHECK_DEPLOY_GAS(131591, 110969, evmVersion); } } else if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(126929, 119659, evmVersion); + CHECK_DEPLOY_GAS(125829, 118559, evmVersion); else - CHECK_DEPLOY_GAS(114345, 107335, evmVersion); + CHECK_DEPLOY_GAS(114077, 107067, evmVersion); if (evmVersion >= EVMVersion::byzantium()) { From d41fc31046520c6a5b0dcba5202256b37dfa4be3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Jul 2020 16:59:32 +0100 Subject: [PATCH 011/139] Do not use Parser::instructions() but depend on the dialect in NameDispenser and VarNameCleaner --- libyul/optimiser/CallGraphGenerator.cpp | 2 -- libyul/optimiser/NameDispenser.cpp | 6 +----- libyul/optimiser/VarNameCleaner.cpp | 5 +---- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/libyul/optimiser/CallGraphGenerator.cpp b/libyul/optimiser/CallGraphGenerator.cpp index 85fb1c66c..76f5220c8 100644 --- a/libyul/optimiser/CallGraphGenerator.cpp +++ b/libyul/optimiser/CallGraphGenerator.cpp @@ -22,8 +22,6 @@ #include #include -#include - #include using namespace std; diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index cfc7fdcb9..ec091b2d7 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -24,10 +24,8 @@ #include #include #include -#include -#include -#include +#include using namespace std; using namespace solidity; @@ -61,7 +59,5 @@ bool NameDispenser::illegalName(YulString _name) { if (_name.empty() || m_usedNames.count(_name) || m_dialect.builtin(_name)) return true; - if (dynamic_cast(&m_dialect)) - return Parser::instructions().count(_name.str()); return false; } diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index 15d215f2c..edea33d2b 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -19,8 +19,7 @@ #include #include #include -#include -#include + #include #include #include @@ -113,8 +112,6 @@ bool VarNameCleaner::isUsedName(YulString const& _name) const { if (_name.empty() || m_dialect.builtin(_name) || m_usedNames.count(_name)) return true; - if (dynamic_cast(&m_dialect)) - return Parser::instructions().count(_name.str()); return false; } From 24bb6252ac6ea51400c9facc9255e51aad433f84 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Jul 2020 16:59:51 +0100 Subject: [PATCH 012/139] Merge Parser::instructions() into EVMDialect --- libyul/AsmParser.cpp | 21 --------------------- libyul/AsmParser.h | 5 ----- libyul/backends/evm/EVMDialect.cpp | 25 +++++++++++++++---------- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 5dd0d854e..2c7aa1f93 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -61,27 +61,6 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ return nullptr; } -std::map const& Parser::instructions() -{ - // Allowed instructions, lowercase names. - static map s_instructions; - if (s_instructions.empty()) - { - for (auto const& instruction: evmasm::c_instructions) - { - if ( - instruction.second == evmasm::Instruction::JUMPDEST || - evmasm::isPushInstruction(instruction.second) - ) - continue; - string name = instruction.first; - transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); - s_instructions[name] = instruction.second; - } - } - return s_instructions; -} - Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index ecd62228a..c48910064 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -30,8 +30,6 @@ #include #include -#include - #include #include #include @@ -62,9 +60,6 @@ public: /// @returns an empty shared pointer on error. std::unique_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); - /// @returns a map of all EVM instructions available to assembly. - static std::map const& instructions(); - protected: using ElementaryOperation = std::variant; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index c552b6e22..6e2409fe2 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -115,18 +115,23 @@ pair createFunction( map createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess) { map builtins; - // NOTE: Parser::instructions() will filter JUMPDEST and PUSHnn too - for (auto const& instr: Parser::instructions()) + for (auto const& instr: evmasm::c_instructions) + { + string name = instr.first; + transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); + auto const opcode = instr.second; + if ( - !evmasm::isDupInstruction(instr.second) && - !evmasm::isSwapInstruction(instr.second) && - !evmasm::isPushInstruction(instr.second) && - instr.second != evmasm::Instruction::JUMP && - instr.second != evmasm::Instruction::JUMPI && - instr.second != evmasm::Instruction::JUMPDEST && - _evmVersion.hasOpcode(instr.second) + !evmasm::isDupInstruction(opcode) && + !evmasm::isSwapInstruction(opcode) && + !evmasm::isPushInstruction(opcode) && + opcode != evmasm::Instruction::JUMP && + opcode != evmasm::Instruction::JUMPI && + opcode != evmasm::Instruction::JUMPDEST && + _evmVersion.hasOpcode(opcode) ) - builtins.emplace(createEVMFunction(instr.first, instr.second)); + builtins.emplace(createEVMFunction(name, opcode)); + } if (_objectAccess) { From f61e8ed05dcfc8e173c085f81f4ab89ee328cb2b Mon Sep 17 00:00:00 2001 From: Goh Chun Lin Date: Fri, 31 Jul 2020 22:53:12 +0800 Subject: [PATCH 013/139] Corrected typo. --- docs/layout-of-source-files.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 54cb8c59a..d50ea06a3 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -14,7 +14,7 @@ SPDX License Identifier Trust in smart contract can be better established if their source code is available. Since making source code available always touches on legal problems -with regards to copyright, the Solidity compiler encouranges the use +with regards to copyright, the Solidity compiler encourages the use of machine-readable `SPDX license identifiers `_. Every source file should start with a comment indicating its license: From 0c3e0a65cfd6435e9ba0c7f0768db9804743b6e2 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 3 Aug 2020 04:28:35 +0200 Subject: [PATCH 014/139] Extend error_codes.py to to detect newly introduced error codes, not covered by tests --- scripts/error_codes.py | 63 ++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/scripts/error_codes.py b/scripts/error_codes.py index eaf401cb0..6e9159b74 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -167,7 +167,7 @@ def print_ids_per_file(ids, id_to_file_names, top_dir): print() -def examine_id_coverage(top_dir, source_id_to_file_names): +def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): test_sub_dirs = [ path.join("test", "libsolidity", "errorRecoveryTests"), path.join("test", "libsolidity", "smtCheckerTests"), @@ -196,24 +196,51 @@ def examine_id_coverage(top_dir, source_id_to_file_names): assert len(test_ids & white_ids) == 0, "The sets are not supposed to intersect" test_ids |= white_ids - print(f"IDs in source files: {len(source_ids)}") - print(f"IDs in test files : {len(test_ids)} ({len(test_ids) - len(source_ids)})") - print() - test_only_ids = test_ids - source_ids - if len(test_only_ids) != 0: - print("Error. The following error codes found in tests, but not in sources:") - print_ids(test_only_ids) - return 1 - source_only_ids = source_ids - test_ids - if len(source_only_ids) != 0: - print("The following error codes found in sources, but not in tests:") - print_ids_per_file(source_only_ids, source_id_to_file_names, top_dir) - print("\n\nPlease make sure to add appropriate tests.") - return 1 - return 0 + if not new_ids_only: + print(f"IDs in source files: {len(source_ids)}") + print(f"IDs in test files : {len(test_ids)} ({len(test_ids) - len(source_ids)})") + print() + + if len(test_only_ids) != 0: + print("Error. The following error codes found in tests, but not in sources:") + print_ids(test_only_ids) + return False + + if len(source_only_ids) != 0: + print("The following error codes found in sources, but not in tests:") + print_ids_per_file(source_only_ids, source_id_to_file_names, top_dir) + print("\n\nPlease make sure to add appropriate tests.") + return False + + old_source_only_ids = { + "1054", "1123", "1133", "1220", "1395", "1584", "1733", "1823", "1950", "1957", + "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", "3069", + "3263", "3287", "3299", "3356", "3384", "3441", "3517", "3682", "3781", "3876", + "3893", "3947", "3997", "4010", "4110", "4294", "4802", "4805", "4828", "4846", + "4904", "4990", "5052", "5073", "5170", "5188", "5272", "5333", "5347", "5473", + "5622", "6041", "6052", "6272", "6708", "6792", "6931", "7110", "7128", "7186", + "7319", "7589", "7593", "7653", "7771", "7812", "7885", "8065", "8084", "8140", + "8143", "8261", "8311", "8312", "8452", "8592", "8758", "8794", "9005", "9011", + "9085", "9102", "9114", "9390", "9439", "9440", "9547", "9551", "9615", "9980" + } + new_source_only_ids = source_only_ids - old_source_only_ids + if len(new_source_only_ids) != 0: + print("The following new error code(s), not covered by tests, found:") + print_ids(new_source_only_ids) + print( + "\nYou can:\n" + "- create appropriate test(s);\n" + "- add the error code(s) to old_source_only_ids in error_codes.py\n" + " (to silence the checking script, with a promise to add a test later);\n" + "- add the error code(s) to white_ids in error_codes.py\n" + " (for rare cases when the error is not supposed to be tested)" + ) + return False + + return True def main(argv): @@ -267,9 +294,11 @@ def main(argv): if not ok: print("Incorrect IDs have to be fixed before applying --examine-coverage") exit(1) - res = examine_id_coverage(cwd, source_id_to_file_names) + res = 0 if examine_id_coverage(cwd, source_id_to_file_names) else 1 exit(res) + ok &= examine_id_coverage(cwd, source_id_to_file_names, new_ids_only=True) + random.seed() if next_id: From 0b45168bcbf5ed5874668f21a3c3d1476cab5620 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 27 Jul 2020 22:26:42 +0200 Subject: [PATCH 015/139] Use assert to check if return tag applies to a public state-variable --- libsolidity/analysis/DocStringTagParser.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index 70f677c6a..7e488afb7 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -165,12 +165,7 @@ void DocStringTagParser::parseDocStrings( returnTagsVisited++; if (auto const* varDecl = dynamic_cast(&_node)) { - if (!varDecl->isPublic()) - m_errorReporter.docstringParsingError( - 9440_error, - _node.documentation()->location(), - "Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables." - ); + solAssert(varDecl->isPublic(), "@return is only allowed on public state-variables."); if (returnTagsVisited > 1) m_errorReporter.docstringParsingError( 5256_error, From 5100effeec8c99d451a48fe383493f6f3e197f38 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Sun, 2 Aug 2020 13:29:19 +0200 Subject: [PATCH 016/139] yul: Set non movable flag to true in simplification rule for byte of right-shifted expression --- Changelog.md | 1 + libevmasm/RuleList.h | 15 +++++++++++++-- .../yulInterpreterTests/pop_byte_shr_call.yul | 10 ++++++++++ .../yulInterpreterTests/pop_byte_shr_func.yul | 11 +++++++++++ .../expressionSimplifier/pop_byte_shr_call.yul | 11 +++++++++++ .../expressionSimplifier/pop_byte_shr_func.yul | 14 ++++++++++++++ .../pop_byte_shr_func_trivial.yul | 14 ++++++++++++++ 7 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 test/libyul/yulInterpreterTests/pop_byte_shr_call.yul create mode 100644 test/libyul/yulInterpreterTests/pop_byte_shr_func.yul create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul diff --git a/Changelog.md b/Changelog.md index 2e857a89f..28a1eff05 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. * SMTChecker: Fix internal error on fixed bytes index access. diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 93ccdcaf9..1e3fd7800 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -581,9 +581,20 @@ std::vector> simplificationRuleListPart7( rules.push_back({ Builtins::BYTE(A, Builtins::SHR(B, X)), - [=]() -> Pattern { return A.d() < B.d() / 8 ? Word(0) : Builtins::BYTE(A.d() - B.d() / 8, X); }, + [=]() -> Pattern { return Word(0); }, + true, + [=] { + return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() < B.d() / 8; + } + }); + + rules.push_back({ + Builtins::BYTE(A, Builtins::SHR(B, X)), + [=]() -> Pattern { return Builtins::BYTE(A.d() - B.d() / 8, X); }, false, - [=] { return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize; } + [=] { + return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() >= B.d() / 8; + } }); return rules; diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul new file mode 100644 index 000000000..4e431a6b1 --- /dev/null +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul @@ -0,0 +1,10 @@ +{ + pop(byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// CALL(0, 0, 0, 0, 0, 0, 0) +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul new file mode 100644 index 000000000..b520b9181 --- /dev/null +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul @@ -0,0 +1,11 @@ +{ + function f() -> x { mstore(0, 0x1337) } + pop(byte(0, shr(0x8, f()))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000001337 +// Storage dump: diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul new file mode 100644 index 000000000..e30a0418c --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul @@ -0,0 +1,11 @@ +{ + pop(byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { +// pop(byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul new file mode 100644 index 000000000..f24add1dd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul @@ -0,0 +1,14 @@ +{ + function f() -> x { mstore(0, 1337) } + pop(byte(0, shr(0x8, f()))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { +// function f() -> x +// { mstore(0, 1337) } +// pop(byte(0, shr(0x8, f()))) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul new file mode 100644 index 000000000..3fc5ee558 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul @@ -0,0 +1,14 @@ +{ + function f() -> x {} + pop(byte(0, shr(0x8, f()))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { +// function f() -> x +// { } +// pop(byte(0, shr(0x8, f()))) +// } From 55624d641675e386659dc3725a17a022d241fc67 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 28 Jul 2020 17:58:10 +0200 Subject: [PATCH 017/139] Add test from Aon blog post --- .../invariants/aon_blog_post.sol | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol diff --git a/test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol b/test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol new file mode 100644 index 000000000..50644943d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol @@ -0,0 +1,47 @@ +pragma experimental SMTChecker; +contract C { + bool a; + bool b; + bool c; + bool d; + bool e; + bool f; + function press_A() public { + if(e) { a = true; } else { reset(); } + } + function press_B() public { + if(c) { b = true; } else { reset(); } + } + function press_C() public { + if(a) { c = true; } else { reset(); } + } + function press_D() public { + d = true; + } + function press_E() public { + if(d) { e = true; } else { reset(); } + } + function press_F() public { + if(b) { f = true; } else { reset(); } + } + function is_not_solved() view public { + // f = true can be reached by calling the functions + // press_D() + // press_E() + // press_A() + // press_C() + // press_B() + // press_F() + assert(!f); + } + function reset() internal { + a = false; + b = false; + c = false; + d = false; + e = false; + f = false; + } +} +// ---- +// Warning 6328: (689-699): Assertion violation happens here From c96e997a3c4c3a14f55bc31a09557ff85b0558af Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 Aug 2020 16:35:13 +0200 Subject: [PATCH 018/139] Fix member lookup for constructor in library. --- Changelog.md | 1 + libsolidity/ast/AST.h | 1 + libsolidity/ast/Types.cpp | 4 +++- .../memberLookup/constructor_as_potential_library_member.sol | 4 ++++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol diff --git a/Changelog.md b/Changelog.md index 2e857a89f..cff21081d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: * SMTChecker: Fix internal error on fixed bytes index access. + * References Resolver: Fix internal bug when using constructor for library. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index de358a9e7..28d71c2b7 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -792,6 +792,7 @@ public: m_body(_body) { solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, ""); + solAssert(isOrdinary() == !name().empty(), ""); } void accept(ASTVisitor& _visitor) override; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index ebe935502..a87ae4c3f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -451,7 +451,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc ); for (FunctionDefinition const* function: library.definedFunctions()) { - if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) + if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; seenFunctions.insert(function); if (function->parameters().empty()) @@ -3838,6 +3838,8 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons { if (dynamic_cast(declaration)) continue; + if (declaration->name().empty()) + continue; if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts()) { diff --git a/test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol b/test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol new file mode 100644 index 000000000..71b63faa5 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol @@ -0,0 +1,4 @@ +library L{ constructor() { L.x; } } +// ---- +// TypeError 7634: (11-33): Constructor cannot be defined in libraries. +// TypeError 9582: (27-30): Member "x" not found or not visible after argument-dependent lookup in type(library L). From 0ea5aae9aa4d458f90f4b4eb3c61f0767c5632bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Aug 2020 12:13:23 +0200 Subject: [PATCH 019/139] Require inheritdoc tag to be non-empty. --- libsolidity/analysis/ReferencesResolver.cpp | 10 ++++++++++ .../natspec/invalid/docstring_inheritdoc_emptys.sol | 7 +++++++ 2 files changed, 17 insertions(+) create mode 100644 test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index e2e92a126..208749de9 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -313,6 +313,16 @@ void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _docum case 1: { string const& name = _annotation.docTags.find("inheritdoc")->second.content; + if (name.empty()) + { + m_errorReporter.docstringParsingError( + 1933_error, + _documentation.location(), + "Expected contract name following documentation tag @inheritdoc." + ); + return; + } + vector path; boost::split(path, name, boost::is_any_of(".")); Declaration const* result = m_resolver.pathFromCurrentScope(path); diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol new file mode 100644 index 000000000..e518021b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol @@ -0,0 +1,7 @@ +contract C { + /// @inheritdoc + function f() internal { + } +} +// ---- +// DocstringParsingError 1933: (17-32): Expected contract name following documentation tag @inheritdoc. From a3a9630d8b0d9b6fb032f282af23771785fe5fd5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Aug 2020 11:25:09 +0200 Subject: [PATCH 020/139] Optimize byte-after-shr for shift amounts that are not multiples of 8. --- libevmasm/RuleList.h | 4 +--- test/formal/combine_byte_shr_2.py | 5 +---- .../expressionSimplifier/byte_after_shr_non_mul_of_8.yul | 9 +++++++++ 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 1e3fd7800..c2cf1eef3 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -583,9 +583,7 @@ std::vector> simplificationRuleListPart7( Builtins::BYTE(A, Builtins::SHR(B, X)), [=]() -> Pattern { return Word(0); }, true, - [=] { - return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() < B.d() / 8; - } + [=] { return A.d() < B.d() / 8; } }); rules.push_back({ diff --git a/test/formal/combine_byte_shr_2.py b/test/formal/combine_byte_shr_2.py index d74114e85..4f9be05a4 100644 --- a/test/formal/combine_byte_shr_2.py +++ b/test/formal/combine_byte_shr_2.py @@ -3,7 +3,7 @@ from opcodes import * """ byte(A, shr(B, X)) -given B % 8 == 0 && A < n_bits/8 && B <= n_bits && A < B / 8 +given A < B / 8 -> 0 """ @@ -22,9 +22,6 @@ nonopt = BYTE(A, SHR(B, X)) # Optimized result opt = 0 -rule.require(B % 8 == 0) -rule.require(ULT(A, n_bits/8)) -rule.require(ULE(B, n_bits)) rule.require(ULT(A, DIV(B,8))) rule.check(nonopt, opt) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul new file mode 100644 index 000000000..e7c7bbf88 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul @@ -0,0 +1,9 @@ +{ + sstore(0, byte(0, shr(0x9, calldataload(0)))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { sstore(0, 0) } From 9da3410f1b106794964f697b0cba8c37488f6563 Mon Sep 17 00:00:00 2001 From: hactrox Date: Wed, 5 Aug 2020 12:14:51 +0800 Subject: [PATCH 021/139] Update introduction-to-smart-contracts.rst Fixed a typo --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 556d486f7..39454ece1 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -37,7 +37,7 @@ GPL version 3.0. Machine-readable license specifiers are important in a setting where publishing the source code is the default. The next line specifies that the source code is written for -Solidity version 0.4.16, or a newer version of the language up to, but not including version 0.7.0. +Solidity version 0.4.16, or a newer version of the language up to, but not including version 0.8.0. This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently. :ref:`Pragmas` are common instructions for compilers about how to treat the source code (e.g. `pragma once `_). From 8df8c6e14ff5295b608d868c8339343c298d7d02 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 27 Jul 2020 15:40:23 +0200 Subject: [PATCH 022/139] [SMTChecker] Fix ICE in BMC function inlining --- Changelog.md | 1 + libsolidity/formal/BMC.cpp | 2 +- libsolidity/formal/CHC.cpp | 1 - libsolidity/formal/VariableUsage.cpp | 4 ++-- libsolidity/formal/VariableUsage.h | 4 ++-- ...unction_external_call_should_not_inline_1.sol | 16 ++++++++++++++++ ...unction_external_call_should_not_inline_2.sol | 16 ++++++++++++++++ 7 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol diff --git a/Changelog.md b/Changelog.md index 28a1eff05..309e9e9ab 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. + * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on fixed bytes index access. diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 363910edd..69165841b 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -61,7 +61,7 @@ void BMC::analyze(SourceUnit const& _source, map touchedVariables(ASTNode const& _node, std::vector const& _outerCallstack); /// Sets whether to inline function calls. - void setFunctionInlining(bool _inlineFunction) { m_inlineFunctionCalls = _inlineFunction; } + void setFunctionInlining(std::function _inlineFunctionCalls) { m_inlineFunctionCalls = _inlineFunctionCalls; } private: void endVisit(Identifier const& _node) override; @@ -54,7 +54,7 @@ private: std::vector m_callStack; CallableDeclaration const* m_lastCall = nullptr; - bool m_inlineFunctionCalls = false; + std::function m_inlineFunctionCalls = [](FunctionCall const&) { return false; }; }; } diff --git a/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol new file mode 100644 index 000000000..f32cfa4fa --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; +contract State { + C c; + function f() public returns (uint) { + while(true) + c.setOwner(); + } +} +contract C { + address owner; + function setOwner() public { + owner = address(0); + } +} +// ---- +// Warning 5084: (198-208): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol new file mode 100644 index 000000000..06dc94cce --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; +contract Other { + C c; + function h(bool b) public { + if (b) + c.setOwner(address(0)); + } +} +contract C { + address owner; + function setOwner(address _owner) public { + owner = _owner; + } +} +// ---- +// Warning 5084: (107-117): Type conversion is not yet fully supported and might yield false positives. From f6a57af8090b7b5506fb21b346c9d883b85e9096 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 4 Aug 2020 16:30:16 +0100 Subject: [PATCH 023/139] Add LiteralKind to Dialect.literalArguments Co-authored-by: Daniel Kirchner --- libyul/AsmAnalysis.cpp | 8 +++--- libyul/Dialect.h | 9 ++++-- libyul/backends/evm/EVMDialect.cpp | 19 ++++++------- libyul/backends/evm/NoOutputAssembly.cpp | 2 +- libyul/backends/wasm/WasmCodeTransform.cpp | 28 +++++++++---------- libyul/backends/wasm/WasmDialect.cpp | 14 ++++------ libyul/backends/wasm/WasmDialect.h | 2 +- libyul/backends/wasm/WordSizeTransform.cpp | 14 ++++++---- .../CommonSubexpressionEliminator.cpp | 2 +- libyul/optimiser/ExpressionSplitter.cpp | 8 ++---- 10 files changed, 51 insertions(+), 55 deletions(-) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index b8f0dd4e7..428861472 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -272,14 +272,14 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) auto watcher = m_errorReporter.errorWatcher(); vector const* parameterTypes = nullptr; vector const* returnTypes = nullptr; - vector const* needsLiteralArguments = nullptr; + vector> const* literalArguments = nullptr; if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name)) { parameterTypes = &f->parameters; returnTypes = &f->returns; - if (f->literalArguments) - needsLiteralArguments = &f->literalArguments.value(); + if (!f->literalArguments.empty()) + literalArguments = &f->literalArguments; validateInstructions(_funCall); } @@ -318,7 +318,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) for (size_t i = _funCall.arguments.size(); i > 0; i--) { Expression const& arg = _funCall.arguments[i - 1]; - bool isLiteralArgument = needsLiteralArguments && (*needsLiteralArguments)[i - 1]; + bool isLiteralArgument = literalArguments && (*literalArguments)[i - 1].has_value(); bool isStringLiteral = holds_alternative(arg) && get(arg).kind == LiteralKind::String; if (isLiteralArgument && isStringLiteral) diff --git a/libyul/Dialect.h b/libyul/Dialect.h index 553f9049a..6634d6cfd 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -48,8 +48,13 @@ struct BuiltinFunction ControlFlowSideEffects controlFlowSideEffects; /// If true, this is the msize instruction. bool isMSize = false; - /// If set, same length as the arguments, if true at index i, the i'th argument has to be a literal which means it can't be moved to variables. - std::optional> literalArguments; + /// Must be empty or the same length as the arguments. + /// If set at index i, the i'th argument has to be a literal which means it can't be moved to variables. + std::vector> literalArguments{}; + std::optional literalArgument(size_t i) const + { + return literalArguments.empty() ? std::nullopt : literalArguments.at(i); + } }; struct Dialect: boost::noncopyable diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 6e2409fe2..f6affcdbf 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -70,7 +70,7 @@ pair createEVMFunction( f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction); f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction); f.isMSize = _instruction == evmasm::Instruction::MSIZE; - f.literalArguments.reset(); + f.literalArguments.clear(); f.instruction = _instruction; f.generateCode = [_instruction]( FunctionCall const& _call, @@ -90,7 +90,7 @@ pair createFunction( size_t _params, size_t _returns, SideEffects _sideEffects, - vector _literalArguments, + vector> _literalArguments, std::function)> _generateCode ) { @@ -102,10 +102,7 @@ pair createFunction( f.parameters.resize(_params); f.returns.resize(_returns); f.sideEffects = std::move(_sideEffects); - if (!_literalArguments.empty()) - f.literalArguments = std::move(_literalArguments); - else - f.literalArguments.reset(); + f.literalArguments = std::move(_literalArguments); f.isMSize = false; f.instruction = {}; f.generateCode = std::move(_generateCode); @@ -135,7 +132,7 @@ map createBuiltins(langutil::EVMVersion _evmVe if (_objectAccess) { - builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {true}, []( + builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&, @@ -145,7 +142,7 @@ map createBuiltins(langutil::EVMVersion _evmVe Expression const& arg = _call.arguments.front(); _assembly.appendLinkerSymbol(std::get(arg).value.str()); })); - builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, []( + builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext& _context, @@ -167,7 +164,7 @@ map createBuiltins(langutil::EVMVersion _evmVe _assembly.appendDataSize(subIdPath); } })); - builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {true}, []( + builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext& _context, @@ -210,7 +207,7 @@ map createBuiltins(langutil::EVMVersion _evmVe 2, 0, SideEffects{false, false, false, false, true}, - {true, false}, + {LiteralKind::String, std::nullopt}, []( FunctionCall const& _call, AbstractAssembly& _assembly, @@ -230,7 +227,7 @@ map createBuiltins(langutil::EVMVersion _evmVe 1, 1, SideEffects{}, - {true}, + {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index ced32af6a..2ddeae05d 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -170,7 +170,7 @@ NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): for (size_t j = 0; j < _call.arguments.size(); j++) { size_t const i = _call.arguments.size() - j - 1; - if (!(fun.second.literalArguments && (*fun.second.literalArguments)[i])) + if (!fun.second.literalArgument(i)) { _visitExpression(_call.arguments[i]); visited++; diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index de87028a9..fae7cd6cb 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -140,22 +140,20 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) m_functionsToImport[builtin->name] = std::move(imp); } } - else if (builtin->literalArguments && contains(builtin->literalArguments.value(), true)) - { - vector literals; - for (size_t i = 0; i < _call.arguments.size(); i++) - if (builtin->literalArguments.value()[i]) - literals.emplace_back(wasm::StringLiteral{std::get(_call.arguments[i]).value.str()}); - else - literals.emplace_back(visitReturnByValue(_call.arguments[i])); - - return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)}; - } else - return wasm::BuiltinCall{ - _call.functionName.name.str(), - visit(_call.arguments) - }; + { + vector arguments; + for (size_t i = 0; i < _call.arguments.size(); i++) + if (builtin->literalArgument(i)) + { + yulAssert(builtin->literalArgument(i) == LiteralKind::String, ""); + arguments.emplace_back(wasm::StringLiteral{std::get(_call.arguments[i]).value.str()}); + } + else + arguments.emplace_back(visitReturnByValue(_call.arguments[i])); + + return wasm::BuiltinCall{_call.functionName.name.str(), std::move(arguments)}; + } } // If this function returns multiple values, then the first one will diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index edf113a13..6d2bf8524 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -21,6 +21,7 @@ #include +#include #include using namespace std; @@ -119,8 +120,8 @@ WasmDialect::WasmDialect() 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}); + addFunction("datasize", {i64}, {i64}, true, {LiteralKind::String}); + addFunction("dataoffset", {i64}, {i64}, true, {LiteralKind::String}); addEthereumExternals(); } @@ -221,7 +222,7 @@ void WasmDialect::addEthereumExternals() f.controlFlowSideEffects = ext.controlFlowSideEffects; f.isMSize = false; f.sideEffects.invalidatesStorage = (ext.name == "storageStore"); - f.literalArguments.reset(); + f.literalArguments.clear(); } } @@ -230,7 +231,7 @@ void WasmDialect::addFunction( vector _params, vector _returns, bool _movable, - std::vector _literalArguments + vector> _literalArguments ) { YulString name{move(_name)}; @@ -241,8 +242,5 @@ void WasmDialect::addFunction( f.returns = std::move(_returns); f.sideEffects = _movable ? SideEffects{} : SideEffects::worst(); f.isMSize = false; - if (!_literalArguments.empty()) - f.literalArguments = std::move(_literalArguments); - else - f.literalArguments.reset(); + f.literalArguments = std::move(_literalArguments); } diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index a718f0b74..a9cf826ae 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -62,7 +62,7 @@ private: std::vector _params, std::vector _returns, bool _movable = true, - std::vector _literalArguments = std::vector{} + std::vector> _literalArguments = std::vector>{} ); std::map m_functions; diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 73dda9d0c..3e322bc05 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -42,16 +42,16 @@ void WordSizeTransform::operator()(FunctionDefinition& _fd) void WordSizeTransform::operator()(FunctionCall& _fc) { - vector const* literalArguments = nullptr; + vector> const* literalArguments = nullptr; if (BuiltinFunction const* fun = m_inputDialect.builtin(_fc.functionName.name)) - if (fun->literalArguments) - literalArguments = &fun->literalArguments.value(); + if (!fun->literalArguments.empty()) + literalArguments = &fun->literalArguments; vector newArgs; for (size_t i = 0; i < _fc.arguments.size(); i++) - if (!literalArguments || !(*literalArguments)[i]) + if (!literalArguments || !(*literalArguments)[i].has_value()) newArgs += expandValueToVector(_fc.arguments[i]); else { @@ -109,7 +109,8 @@ void WordSizeTransform::operator()(Block& _block) if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*varDecl.value).functionName.name)) if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring) { - yulAssert(f->literalArguments && f->literalArguments.value()[0], ""); + yulAssert(f->literalArguments.size() == 1, ""); + yulAssert(f->literalArguments.at(0) == LiteralKind::String, ""); yulAssert(varDecl.variables.size() == 1, ""); auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name); vector ret; @@ -169,7 +170,8 @@ void WordSizeTransform::operator()(Block& _block) if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*assignment.value).functionName.name)) if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring) { - yulAssert(f->literalArguments && f->literalArguments.value()[0], ""); + yulAssert(f->literalArguments.size() == 1, ""); + yulAssert(f->literalArguments[0] == LiteralKind::String, ""); yulAssert(assignment.variableNames.size() == 1, ""); auto newLhs = generateU64IdentifierNames(assignment.variableNames[0].name); vector ret; diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 3b0b6bec1..2f80e7bc4 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -67,7 +67,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) // We should not modify function arguments that have to be literals // Note that replacing the function call entirely is fine, // if the function call is movable. - if (!builtin->literalArguments || !builtin->literalArguments.value()[i - 1]) + if (!builtin->literalArgument(i - 1)) visit(funCall.arguments[i - 1]); descend = false; diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 2c15b6eb5..6ce26bf81 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -46,14 +46,10 @@ void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast) void ExpressionSplitter::operator()(FunctionCall& _funCall) { - vector const* literalArgs = nullptr; - - if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) - if (builtin->literalArguments) - literalArgs = &builtin->literalArguments.value(); + BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name); for (size_t i = _funCall.arguments.size(); i > 0; i--) - if (!literalArgs || !(*literalArgs)[i - 1]) + if (!builtin || !builtin->literalArgument(i - 1)) outlineExpression(_funCall.arguments[i - 1]); } From 3a617f9cf27e81044914726a0f3576ca95611578 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 4 Aug 2020 16:37:36 +0100 Subject: [PATCH 024/139] Validate expected literal kind for yul::Dialect builtins Co-authored-by: Daniel Kirchner --- Changelog.md | 2 +- libyul/AsmAnalysis.cpp | 56 ++++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Changelog.md b/Changelog.md index 28a1eff05..5b2babfa1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ Language Features: Compiler Features: - + * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. Bugfixes: * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 428861472..bf968d950 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -44,6 +44,20 @@ using namespace solidity::yul; using namespace solidity::util; using namespace solidity::langutil; +namespace +{ +inline string to_string(LiteralKind _kind) +{ + switch (_kind) + { + case LiteralKind::Number: return "number"; + case LiteralKind::Boolean: return "boolean"; + case LiteralKind::String: return "string"; + default: yulAssert(false, ""); + } +} +} + bool AsmAnalyzer::analyze(Block const& _block) { auto watcher = m_errorReporter.errorWatcher(); @@ -318,15 +332,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) for (size_t i = _funCall.arguments.size(); i > 0; i--) { Expression const& arg = _funCall.arguments[i - 1]; - bool isLiteralArgument = literalArguments && (*literalArguments)[i - 1].has_value(); - bool isStringLiteral = holds_alternative(arg) && get(arg).kind == LiteralKind::String; - - if (isLiteralArgument && isStringLiteral) - argTypes.emplace_back(expectUnlimitedStringLiteral(get(arg))); - else - argTypes.emplace_back(expectExpression(arg)); - - if (isLiteralArgument) + if (auto literalArgumentKind = literalArguments ? literalArguments->at(i - 1) : std::nullopt) { if (!holds_alternative(arg)) m_errorReporter.typeError( @@ -334,17 +340,29 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) _funCall.functionName.location, "Function expects direct literals as arguments." ); - else if ( - _funCall.functionName.name.str() == "datasize" || - _funCall.functionName.name.str() == "dataoffset" - ) - if (!m_dataNames.count(std::get(arg).value)) - m_errorReporter.typeError( - 3517_error, - _funCall.functionName.location, - "Unknown data object \"" + std::get(arg).value.str() + "\"." - ); + else if (*literalArgumentKind != get(arg).kind) + m_errorReporter.typeError( + 5859_error, + get(arg).location, + "Function expects " + to_string(*literalArgumentKind) + " literal." + ); + else if (*literalArgumentKind == LiteralKind::String) + { + if ( + _funCall.functionName.name.str() == "datasize" || + _funCall.functionName.name.str() == "dataoffset" + ) + if (!m_dataNames.count(get(arg).value)) + m_errorReporter.typeError( + 3517_error, + get(arg).location, + "Unknown data object \"" + std::get(arg).value.str() + "\"." + ); + argTypes.emplace_back(expectUnlimitedStringLiteral(get(arg))); + continue; + } } + argTypes.emplace_back(expectExpression(arg)); } std::reverse(argTypes.begin(), argTypes.end()); From 6e381326fe054072bad2b6b1192eb44f968eead0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 3 Aug 2020 19:48:15 +0100 Subject: [PATCH 025/139] Adjust and add more tests --- .../output.json | 6 +++--- test/libyul/ObjectParser.cpp | 20 +++++++++++++++++++ test/libyul/yulSyntaxTests/datacopy.yul | 12 +++++++++++ .../linkersymbol_bad_literal.yul | 11 ++++++++++ .../loadimmutable_bad_literal.yul | 11 ++++++++++ .../setimmutable_bad_literal.yul | 11 ++++++++++ 6 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 test/libyul/yulSyntaxTests/datacopy.yul create mode 100644 test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul create mode 100644 test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul create mode 100644 test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul diff --git a/test/cmdlineTests/standard_yul_object_invalid_sub/output.json b/test/cmdlineTests/standard_yul_object_invalid_sub/output.json index 5008d4916..a8f78b985 100644 --- a/test/cmdlineTests/standard_yul_object_invalid_sub/output.json +++ b/test/cmdlineTests/standard_yul_object_invalid_sub/output.json @@ -1,4 +1,4 @@ -{"errors":[{"component":"general","formattedMessage":"A:1:40: TypeError: Unknown data object \"NamedObject.\". +{"errors":[{"component":"general","formattedMessage":"A:1:51: TypeError: Unknown data object \"NamedObject.\". object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } } - ^--------^ -","message":"Unknown data object \"NamedObject.\".","severity":"error","sourceLocation":{"end":49,"file":"A","start":39},"type":"TypeError"}]} + ^------------^ +","message":"Unknown data object \"NamedObject.\".","severity":"error","sourceLocation":{"end":64,"file":"A","start":50},"type":"TypeError"}]} diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 2d932006e..2d7034897 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -258,6 +258,16 @@ BOOST_AUTO_TEST_CASE(arg_to_dataoffset_must_be_literal) CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); } +BOOST_AUTO_TEST_CASE(arg_to_dataoffset_must_be_string_literal) +{ + string code = R"( + object "outer" { + code { let y := dataoffset(0) } + } + )"; + CHECK_ERROR(code, TypeError, "Function expects string literal."); +} + BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_literal) { string code = R"( @@ -268,6 +278,16 @@ BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_literal) CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); } +BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_string_literal) +{ + string code = R"( + object "outer" { + code { let y := datasize(0) } + } + )"; + CHECK_ERROR(code, TypeError, "Function expects string literal."); +} + BOOST_AUTO_TEST_CASE(args_to_datacopy_are_arbitrary) { string code = R"( diff --git a/test/libyul/yulSyntaxTests/datacopy.yul b/test/libyul/yulSyntaxTests/datacopy.yul new file mode 100644 index 000000000..73d2bcd6d --- /dev/null +++ b/test/libyul/yulSyntaxTests/datacopy.yul @@ -0,0 +1,12 @@ +{ + datacopy(0, 1, 2) + datasize("") + datasize(0) // This should not be valid. + dataoffset("") + dataoffset(0) // This should not be valid. +} +// ---- +// TypeError 3517: (37-39): Unknown data object "". +// TypeError 5859: (54-55): Function expects string literal. +// TypeError 3517: (101-103): Unknown data object "". +// TypeError 5859: (120-121): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul b/test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul new file mode 100644 index 000000000..15ac5dccb --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul @@ -0,0 +1,11 @@ +{ + pop(linkersymbol(0)) + pop(linkersymbol(true)) + pop(linkersymbol(false)) +} +// ==== +// dialect: evm +// ---- +// TypeError 5859: (23-24): Function expects string literal. +// TypeError 5859: (48-52): Function expects string literal. +// TypeError 5859: (76-81): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul b/test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul new file mode 100644 index 000000000..2aadd2f20 --- /dev/null +++ b/test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul @@ -0,0 +1,11 @@ +{ + pop(loadimmutable(0)) + pop(loadimmutable(true)) + pop(loadimmutable(false)) +} +// ==== +// dialect: evm +// ---- +// TypeError 5859: (24-25): Function expects string literal. +// TypeError 5859: (50-54): Function expects string literal. +// TypeError 5859: (79-84): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul b/test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul new file mode 100644 index 000000000..6f19c6edb --- /dev/null +++ b/test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul @@ -0,0 +1,11 @@ +{ + setimmutable(0, 0x1234567890123456789012345678901234567890) + setimmutable(true, 0x1234567890123456789012345678901234567890) + setimmutable(false, 0x1234567890123456789012345678901234567890) +} +// ==== +// dialect: evm +// ---- +// TypeError 5859: (19-20): Function expects string literal. +// TypeError 5859: (83-87): Function expects string literal. +// TypeError 5859: (150-155): Function expects string literal. From a27b79fece7141829795fcc492d149845eac0f26 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 4 Aug 2020 17:18:57 +0100 Subject: [PATCH 026/139] Add yulSyntaxTests to error_codes.py --- scripts/error_codes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 6e9159b74..7b5966412 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -171,12 +171,13 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): test_sub_dirs = [ path.join("test", "libsolidity", "errorRecoveryTests"), path.join("test", "libsolidity", "smtCheckerTests"), - path.join("test", "libsolidity", "syntaxTests") + path.join("test", "libsolidity", "syntaxTests"), + path.join("test", "libyul", "yulSyntaxTests") ] test_file_names = find_files( top_dir, test_sub_dirs, - [".sol"] + [".sol", ".yul"] ) source_ids = source_id_to_file_names.keys() test_ids = find_ids_in_test_files(test_file_names) From ad6f39376f59fe18ed5d2b5d54902d349bda24a5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 00:32:18 +0100 Subject: [PATCH 027/139] Return object format in expectation for yulOptimizerTests --- test/libyul/Common.cpp | 4 +- test/libyul/Common.h | 6 +- test/libyul/Inliner.cpp | 1 + test/libyul/YulOptimizerTest.cpp | 165 +++++++++--------- test/libyul/YulOptimizerTest.h | 4 +- .../object_access.yul | 17 +- .../expressionSplitter/object_access.yul | 23 +-- 7 files changed, 115 insertions(+), 105 deletions(-) diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index b0b07a8fb..bc598d3c2 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -76,7 +76,7 @@ pair, shared_ptr> yul::test::parse(strin return make_pair(stack.parserResult()->code, stack.parserResult()->analysisInfo); } -pair, shared_ptr> yul::test::parse( +pair, shared_ptr> yul::test::parse( string const& _source, Dialect const& _dialect, ErrorList& _errors @@ -94,7 +94,7 @@ pair, shared_ptr> yul::test::parse( // TODO this should be done recursively. if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors()) return {}; - return {std::move(parserResult->code), std::move(analysisInfo)}; + return {std::move(parserResult), std::move(analysisInfo)}; } yul::Block yul::test::disambiguate(string const& _source, bool _yul) diff --git a/test/libyul/Common.h b/test/libyul/Common.h index b5f175ac2..57f659400 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -21,8 +21,6 @@ #pragma once -#include - #include #include @@ -38,6 +36,8 @@ using ErrorList = std::vector>; namespace solidity::yul { struct AsmAnalysisInfo; +struct Block; +struct Object; struct Dialect; } @@ -49,7 +49,7 @@ void printErrors(langutil::ErrorList const& _errors); std::pair, std::shared_ptr> parse(std::string const& _source, bool _yul = true); -std::pair, std::shared_ptr> +std::pair, std::shared_ptr> parse(std::string const& _source, Dialect const& _dialect, langutil::ErrorList& _errors); Block disambiguate(std::string const& _source, bool _yul = true); diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp index f44d86618..926e29811 100644 --- a/test/libyul/Inliner.cpp +++ b/test/libyul/Inliner.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 436c46a65..a09725b1c 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -126,225 +126,225 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line NameDisplacer{ *m_nameDispenser, {"illegal1"_yulstring, "illegal2"_yulstring, "illegal3"_yulstring, "illegal4"_yulstring, "illegal5"_yulstring} - }(*m_ast); + }(*m_object->code); } else if (m_optimizerStep == "blockFlattener") { disambiguate(); - BlockFlattener::run(*m_context, *m_ast); + BlockFlattener::run(*m_context, *m_object->code); } else if (m_optimizerStep == "constantOptimiser") { GasMeter meter(dynamic_cast(*m_dialect), false, 200); - ConstantOptimiser{dynamic_cast(*m_dialect), meter}(*m_ast); + ConstantOptimiser{dynamic_cast(*m_dialect), meter}(*m_object->code); } else if (m_optimizerStep == "varDeclInitializer") - VarDeclInitializer::run(*m_context, *m_ast); + VarDeclInitializer::run(*m_context, *m_object->code); else if (m_optimizerStep == "varNameCleaner") - VarNameCleaner::run(*m_context, *m_ast); + VarNameCleaner::run(*m_context, *m_object->code); else if (m_optimizerStep == "forLoopConditionIntoBody") { disambiguate(); - ForLoopConditionIntoBody::run(*m_context, *m_ast); + ForLoopConditionIntoBody::run(*m_context, *m_object->code); } else if (m_optimizerStep == "forLoopInitRewriter") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); } else if (m_optimizerStep == "commonSubexpressionEliminator") { disambiguate(); - CommonSubexpressionEliminator::run(*m_context, *m_ast); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "conditionalUnsimplifier") { disambiguate(); - ConditionalUnsimplifier::run(*m_context, *m_ast); + ConditionalUnsimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "conditionalSimplifier") { disambiguate(); - ConditionalSimplifier::run(*m_context, *m_ast); + ConditionalSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "expressionSplitter") - ExpressionSplitter::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); else if (m_optimizerStep == "expressionJoiner") { disambiguate(); - ExpressionJoiner::run(*m_context, *m_ast); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "splitJoin") { disambiguate(); - ExpressionSplitter::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "functionGrouper") { disambiguate(); - FunctionGrouper::run(*m_context, *m_ast); + FunctionGrouper::run(*m_context, *m_object->code); } else if (m_optimizerStep == "functionHoister") { disambiguate(); - FunctionHoister::run(*m_context, *m_ast); + FunctionHoister::run(*m_context, *m_object->code); } else if (m_optimizerStep == "expressionInliner") { disambiguate(); - ExpressionInliner::run(*m_context, *m_ast); + ExpressionInliner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "fullInliner") { disambiguate(); - FunctionHoister::run(*m_context, *m_ast); - FunctionGrouper::run(*m_context, *m_ast); - ExpressionSplitter::run(*m_context, *m_ast); - FullInliner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + FunctionHoister::run(*m_context, *m_object->code); + FunctionGrouper::run(*m_context, *m_object->code); + ExpressionSplitter::run(*m_context, *m_object->code); + FullInliner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "mainFunction") { disambiguate(); - FunctionGrouper::run(*m_context, *m_ast); - MainFunction::run(*m_context, *m_ast); + FunctionGrouper::run(*m_context, *m_object->code); + MainFunction::run(*m_context, *m_object->code); } else if (m_optimizerStep == "rematerialiser") { disambiguate(); - Rematerialiser::run(*m_context, *m_ast); + Rematerialiser::run(*m_context, *m_object->code); } else if (m_optimizerStep == "expressionSimplifier") { disambiguate(); - ExpressionSimplifier::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); + ExpressionSimplifier::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "fullSimplify") { disambiguate(); - ExpressionSplitter::run(*m_context, *m_ast); - ForLoopInitRewriter::run(*m_context, *m_ast); - CommonSubexpressionEliminator::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); - UnusedPruner::run(*m_context, *m_ast); - CircularReferencesPruner::run(*m_context, *m_ast); - DeadCodeEliminator::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); + ForLoopInitRewriter::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); + UnusedPruner::run(*m_context, *m_object->code); + CircularReferencesPruner::run(*m_context, *m_object->code); + DeadCodeEliminator::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "unusedPruner") { disambiguate(); - UnusedPruner::run(*m_context, *m_ast); + UnusedPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "circularReferencesPruner") { disambiguate(); - FunctionHoister::run(*m_context, *m_ast); - CircularReferencesPruner::run(*m_context, *m_ast); + FunctionHoister::run(*m_context, *m_object->code); + CircularReferencesPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "deadCodeEliminator") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - DeadCodeEliminator::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + DeadCodeEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaTransform") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - SSATransform::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + SSATransform::run(*m_context, *m_object->code); } else if (m_optimizerStep == "redundantAssignEliminator") { disambiguate(); - RedundantAssignEliminator::run(*m_context, *m_ast); + RedundantAssignEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaPlusCleanup") { disambiguate(); - SSATransform::run(*m_context, *m_ast); - RedundantAssignEliminator::run(*m_context, *m_ast); + SSATransform::run(*m_context, *m_object->code); + RedundantAssignEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "loadResolver") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - ExpressionSplitter::run(*m_context, *m_ast); - CommonSubexpressionEliminator::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + ExpressionSplitter::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); - LoadResolver::run(*m_context, *m_ast); + LoadResolver::run(*m_context, *m_object->code); - UnusedPruner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + UnusedPruner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "loopInvariantCodeMotion") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - LoopInvariantCodeMotion::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + LoopInvariantCodeMotion::run(*m_context, *m_object->code); } else if (m_optimizerStep == "controlFlowSimplifier") { disambiguate(); - ControlFlowSimplifier::run(*m_context, *m_ast); + ControlFlowSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "structuralSimplifier") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - LiteralRematerialiser::run(*m_context, *m_ast); - StructuralSimplifier::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + LiteralRematerialiser::run(*m_context, *m_object->code); + StructuralSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "equivalentFunctionCombiner") { disambiguate(); - EquivalentFunctionCombiner::run(*m_context, *m_ast); + EquivalentFunctionCombiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaReverser") { disambiguate(); - SSAReverser::run(*m_context, *m_ast); + SSAReverser::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaAndBack") { disambiguate(); // apply SSA - SSATransform::run(*m_context, *m_ast); - RedundantAssignEliminator::run(*m_context, *m_ast); + SSATransform::run(*m_context, *m_object->code); + RedundantAssignEliminator::run(*m_context, *m_object->code); // reverse SSA - SSAReverser::run(*m_context, *m_ast); - CommonSubexpressionEliminator::run(*m_context, *m_ast); - UnusedPruner::run(*m_context, *m_ast); + SSAReverser::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + UnusedPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "stackCompressor") { disambiguate(); - FunctionGrouper::run(*m_context, *m_ast); + FunctionGrouper::run(*m_context, *m_object->code); size_t maxIterations = 16; Object obj; - obj.code = m_ast; + obj.code = m_object->code; StackCompressor::run(*m_dialect, obj, true, maxIterations); - m_ast = obj.code; - BlockFlattener::run(*m_context, *m_ast); + m_object->code = obj.code; + BlockFlattener::run(*m_context, *m_object->code); } else if (m_optimizerStep == "wordSizeTransform") { disambiguate(); - ExpressionSplitter::run(*m_context, *m_ast); - WordSizeTransform::run(*m_dialect, *m_dialect, *m_ast, *m_nameDispenser); + ExpressionSplitter::run(*m_context, *m_object->code); + WordSizeTransform::run(*m_dialect, *m_dialect, *m_object->code, *m_nameDispenser); } else if (m_optimizerStep == "fullSuite") { GasMeter meter(dynamic_cast(*m_dialect), false, 200); yul::Object obj; - obj.code = m_ast; + obj.code = m_object->code; obj.analysisInfo = m_analysisInfo; OptimiserSuite::run(*m_dialect, &meter, obj, true, solidity::frontend::OptimiserSettings::DefaultYulOptimiserSteps); } @@ -354,7 +354,10 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line return TestResult::FatalError; } - m_obtainedResult = "step: " + m_optimizerStep + "\n\n" + AsmPrinter{ *m_dialect }(*m_ast) + "\n"; + m_obtainedResult = + "step: " + m_optimizerStep + "\n\n" + + (m_object->subObjects.empty() ? AsmPrinter{ *m_dialect }(*m_object->code) : m_object->toString(m_dialect)) + + "\n"; return checkResult(_stream, _linePrefix, _formatted); } @@ -363,8 +366,8 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c { ErrorList errors; soltestAssert(m_dialect, ""); - std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors); - if (!m_ast || !m_analysisInfo || !Error::containsOnlyWarnings(errors)) + std::tie(m_object, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors); + if (!m_object || !m_analysisInfo || !Error::containsOnlyWarnings(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; printErrors(_stream, errors); @@ -375,14 +378,14 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c void YulOptimizerTest::disambiguate() { - *m_ast = std::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); + *m_object->code = std::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_object->code)); m_analysisInfo.reset(); updateContext(); } void YulOptimizerTest::updateContext() { - m_nameDispenser = make_unique(*m_dialect, *m_ast, m_reservedIdentifiers); + m_nameDispenser = make_unique(*m_dialect, *m_object->code, m_reservedIdentifiers); m_context = make_unique(OptimiserStepContext{ *m_dialect, *m_nameDispenser, diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 5bba65682..2cebf3057 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -38,7 +38,7 @@ using ErrorList = std::vector>; namespace solidity::yul { struct AsmAnalysisInfo; -struct Block; +struct Object; struct Dialect; } @@ -71,7 +71,7 @@ private: std::unique_ptr m_nameDispenser; std::unique_ptr m_context; - std::shared_ptr m_ast; + std::shared_ptr m_object; std::shared_ptr m_analysisInfo; }; diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul index 0f723c6c1..c8c4e4e13 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul @@ -17,11 +17,14 @@ object "main" { // ---- // step: commonSubexpressionEliminator // -// { -// let r := "abc" -// let a := datasize("abc") -// let x := dataoffset("abc") -// let y := a -// datacopy(r, x, a) -// mstore(a, x) +// object "main" { +// code { +// let r := "abc" +// let a := datasize("abc") +// let x := dataoffset("abc") +// let y := a +// datacopy(r, x, a) +// mstore(a, x) +// } +// data "abc" hex"48656c6c6f2c20576f726c6421" // } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul index 60492e027..bcd6d0f7b 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul @@ -12,14 +12,17 @@ object "main" { // ---- // step: expressionSplitter // -// { -// let x := dataoffset("abc") -// let y := datasize("abc") -// let _1 := 2 -// let _2 := mload(_1) -// let _3 := 1 -// let _4 := mload(_3) -// let _5 := 0 -// let _6 := mload(_5) -// datacopy(_6, _4, _2) +// object "main" { +// code { +// let x := dataoffset("abc") +// let y := datasize("abc") +// let _1 := 2 +// let _2 := mload(_1) +// let _3 := 1 +// let _4 := mload(_3) +// let _5 := 0 +// let _6 := mload(_5) +// datacopy(_6, _4, _2) +// } +// data "abc" hex"48656c6c6f2c20576f726c6421" // } From e77ae3f06e6d12836a8fb1420bc2d57fc64c268c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 12:33:45 +0100 Subject: [PATCH 028/139] Parse optimiser output in yulOptimiserTests --- test/libyul/YulOptimizerTest.cpp | 37 +++++++++++++++++++++++--------- test/libyul/YulOptimizerTest.h | 4 +++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index a09725b1c..530f5efec 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -111,7 +111,8 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename): TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - if (!parse(_stream, _linePrefix, _formatted)) + std::tie(m_object, m_analysisInfo) = parse(_stream, _linePrefix, _formatted, m_source); + if (!m_object) return TestResult::FatalError; soltestAssert(m_dialect, "Dialect not set."); @@ -354,26 +355,42 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line return TestResult::FatalError; } - m_obtainedResult = - "step: " + m_optimizerStep + "\n\n" + - (m_object->subObjects.empty() ? AsmPrinter{ *m_dialect }(*m_object->code) : m_object->toString(m_dialect)) + - "\n"; + auto const printed = (m_object->subObjects.empty() ? AsmPrinter{ *m_dialect }(*m_object->code) : m_object->toString(m_dialect)); + + // Re-parse new code for compilability + // TODO: support for wordSizeTransform which needs different input and output dialects + if (m_optimizerStep != "wordSizeTransform" && !std::get<0>(parse(_stream, _linePrefix, _formatted, printed))) + { + util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) + << _linePrefix << "Result after the optimiser:" << endl; + printIndented(_stream, printed, _linePrefix + " "); + return TestResult::FatalError; + } + + m_obtainedResult = "step: " + m_optimizerStep + "\n\n" + printed + "\n"; return checkResult(_stream, _linePrefix, _formatted); } -bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) +std::pair, std::shared_ptr> YulOptimizerTest::parse( + ostream& _stream, + string const& _linePrefix, + bool const _formatted, + string const& _source +) { ErrorList errors; soltestAssert(m_dialect, ""); - std::tie(m_object, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors); - if (!m_object || !m_analysisInfo || !Error::containsOnlyWarnings(errors)) + shared_ptr object; + shared_ptr analysisInfo; + std::tie(object, analysisInfo) = yul::test::parse(_source, *m_dialect, errors); + if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; printErrors(_stream, errors); - return false; + return {}; } - return true; + return {std::move(object), std::move(analysisInfo)}; } void YulOptimizerTest::disambiguate() diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 2cebf3057..6baeca728 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -58,7 +58,9 @@ public: TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; private: - bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); + std::pair, std::shared_ptr> parse( + std::ostream& _stream, std::string const& _linePrefix, bool const _formatted, std::string const& _source + ); void disambiguate(); void updateContext(); From 42a777e9e202ede062952b1b968a82cdae62d242 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 13:31:51 +0100 Subject: [PATCH 029/139] Use forward declaration in EwasmTranslationTest --- test/libyul/EwasmTranslationTest.cpp | 1 + test/libyul/EwasmTranslationTest.h | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index be9ed77d9..bf01b7765 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/test/libyul/EwasmTranslationTest.h b/test/libyul/EwasmTranslationTest.h index f6307bdbc..5175430e3 100644 --- a/test/libyul/EwasmTranslationTest.h +++ b/test/libyul/EwasmTranslationTest.h @@ -19,7 +19,6 @@ #pragma once #include -#include namespace solidity::langutil { @@ -28,6 +27,11 @@ class Error; using ErrorList = std::vector>; } +namespace solidity::yul +{ +struct Object; +} + namespace solidity::yul::test { From 99d96870ae5d6bb8ee1ba6c39baad715ad2849ca Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 13:33:32 +0100 Subject: [PATCH 030/139] Rename libsolidity/SolidityScanner to liblangutil/Scanner in tests --- test/CMakeLists.txt | 2 +- test/liblangutil/CharStream.cpp | 2 +- .../SolidityScanner.cpp => liblangutil/Scanner.cpp} | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) rename test/{libsolidity/SolidityScanner.cpp => liblangutil/Scanner.cpp} (99%) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 06a082e66..3567f20ce 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -50,6 +50,7 @@ detect_stray_source_files("${libevmasm_sources}" "libevmasm/") set(liblangutil_sources liblangutil/CharStream.cpp + liblangutil/Scanner.cpp liblangutil/SourceLocation.cpp ) detect_stray_source_files("${liblangutil_sources}" "liblangutil/") @@ -92,7 +93,6 @@ set(libsolidity_sources libsolidity/SolidityNatspecJSON.cpp libsolidity/SolidityOptimizer.cpp libsolidity/SolidityParser.cpp - libsolidity/SolidityScanner.cpp libsolidity/SolidityTypes.cpp libsolidity/StandardCompiler.cpp libsolidity/SyntaxTest.cpp diff --git a/test/liblangutil/CharStream.cpp b/test/liblangutil/CharStream.cpp index d1189c3e9..9321b14e0 100644 --- a/test/liblangutil/CharStream.cpp +++ b/test/liblangutil/CharStream.cpp @@ -31,7 +31,7 @@ namespace solidity::langutil::test { -BOOST_AUTO_TEST_SUITE(CharStreamtest) +BOOST_AUTO_TEST_SUITE(CharStreamTest) BOOST_AUTO_TEST_CASE(test_fail) { diff --git a/test/libsolidity/SolidityScanner.cpp b/test/liblangutil/Scanner.cpp similarity index 99% rename from test/libsolidity/SolidityScanner.cpp rename to test/liblangutil/Scanner.cpp index 381e8e318..bbdf2a469 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -21,15 +21,16 @@ */ #include + #include using namespace std; using namespace solidity::langutil; -namespace solidity::frontend::test +namespace solidity::langutil::test { -BOOST_AUTO_TEST_SUITE(SolidityScanner) +BOOST_AUTO_TEST_SUITE(ScannerTest) BOOST_AUTO_TEST_CASE(test_empty) { From e7c99918ed9f061bebaf6030df87bcca52374f31 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 4 Aug 2020 23:51:58 +0100 Subject: [PATCH 031/139] Use yul::test::parse in libyul/SynaxtTests to support objects --- test/libyul/SyntaxTest.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index bb0751b82..f01728571 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include using namespace std; @@ -43,17 +45,9 @@ void SyntaxTest::parseAndAnalyze() string const& source = m_sources.begin()->second; ErrorList errorList{}; - ErrorReporter errorReporter{errorList}; - - auto scanner = make_shared(CharStream(source, name)); - auto parserResult = yul::Parser(errorReporter, *m_dialect).parse(scanner, false); - - if (parserResult) - { - yul::AsmAnalysisInfo analysisInfo; - yul::AsmAnalyzer(analysisInfo, errorReporter, *m_dialect).analyze(*parserResult); - } - + soltestAssert(m_dialect, ""); + // Silently ignoring the results. + yul::test::parse(source, *m_dialect, errorList); for (auto const& error: errorList) { int locationStart = -1; From 14fc453cb43114059a0ec0edeea04a851d3f436e Mon Sep 17 00:00:00 2001 From: cakesoft-omkar <69253915+cakesoft-omkar@users.noreply.github.com> Date: Thu, 6 Aug 2020 00:51:10 +0530 Subject: [PATCH 032/139] Update installing-solidity.rst Typo- Correction --- docs/installing-solidity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 4f03a6b76..e992e9491 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -55,7 +55,7 @@ Please refer to the solc-js repository for instructions. The commandline executable is named ``solcjs``. - The comandline options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``) + The commandline options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``) expecting the behaviour of ``solc`` will not work with ``solcjs``. Docker From 291c359ee90aaedbabcbab4f2d1e086839d0d596 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 21 Jul 2020 22:14:45 +0100 Subject: [PATCH 033/139] Add more syntax tests for Yul objects Also move some of them from boost tests. --- test/libyul/yulSyntaxTests/datacopy.yul | 12 ------- .../objects/basic_subobject.yul | 7 ++++ test/libyul/yulSyntaxTests/objects/code.yul | 18 ++++++++++ .../objects/code_without_object.yul | 4 +++ .../objects/complex_subobject.yul | 35 +++++++++++++++++++ .../objects/conflict_data_data.yul | 8 +++++ .../objects/conflict_data_parent.yul | 9 +++++ .../objects/conflict_object_data.yul | 10 ++++++ .../objects/conflict_object_object.yul | 12 +++++++ .../objects/conflict_object_parent.yul | 7 ++++ test/libyul/yulSyntaxTests/objects/data.yul | 9 +++++ .../yulSyntaxTests/objects/data_access.yul | 9 +++++ .../yulSyntaxTests/objects/data_first.yul | 6 ++++ .../yulSyntaxTests/objects/data_hex_name.yul | 6 ++++ .../objects/data_invalid_hex1.yul | 8 +++++ .../objects/data_invalid_hex2.yul | 8 +++++ .../yulSyntaxTests/objects/datacopy.yul | 8 +++++ .../objects/dataoffset_nonliteral.yul | 10 ++++++ .../objects/dataoffset_nonstring.yul | 7 ++++ .../objects/dataoffset_notfound.yul | 8 +++++ .../objects/datasize_nonliteral.yul | 10 ++++++ .../objects/datasize_nonstring.yul | 7 ++++ .../objects/datasize_notfound.yul | 8 +++++ .../yulSyntaxTests/objects/empty_code.yul | 3 ++ .../yulSyntaxTests/objects/empty_data.yul | 5 +++ .../yulSyntaxTests/objects/empty_object.yul | 4 +++ .../objects/empty_object_name.yul | 5 +++ .../yulSyntaxTests/objects/incomplete1.yul | 3 ++ .../yulSyntaxTests/objects/incomplete2.yul | 7 ++++ .../yulSyntaxTests/objects/multiple_code.yul | 6 ++++ .../yulSyntaxTests/objects/multiple_data.yul | 6 ++++ .../objects/multiple_root_object.yul | 8 +++++ .../yulSyntaxTests/objects/nested_object.yul | 10 ++++++ .../objects/object_hex_name.yul | 5 +++ .../objects/subobject_access.yul | 11 ++++++ .../objects/subobject_first.yul | 9 +++++ .../objects/subobject_hex_name.yul | 8 +++++ 37 files changed, 304 insertions(+), 12 deletions(-) delete mode 100644 test/libyul/yulSyntaxTests/datacopy.yul create mode 100644 test/libyul/yulSyntaxTests/objects/basic_subobject.yul create mode 100644 test/libyul/yulSyntaxTests/objects/code.yul create mode 100644 test/libyul/yulSyntaxTests/objects/code_without_object.yul create mode 100644 test/libyul/yulSyntaxTests/objects/complex_subobject.yul create mode 100644 test/libyul/yulSyntaxTests/objects/conflict_data_data.yul create mode 100644 test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul create mode 100644 test/libyul/yulSyntaxTests/objects/conflict_object_data.yul create mode 100644 test/libyul/yulSyntaxTests/objects/conflict_object_object.yul create mode 100644 test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul create mode 100644 test/libyul/yulSyntaxTests/objects/data.yul create mode 100644 test/libyul/yulSyntaxTests/objects/data_access.yul create mode 100644 test/libyul/yulSyntaxTests/objects/data_first.yul create mode 100644 test/libyul/yulSyntaxTests/objects/data_hex_name.yul create mode 100644 test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul create mode 100644 test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul create mode 100644 test/libyul/yulSyntaxTests/objects/datacopy.yul create mode 100644 test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul create mode 100644 test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul create mode 100644 test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul create mode 100644 test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul create mode 100644 test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul create mode 100644 test/libyul/yulSyntaxTests/objects/datasize_notfound.yul create mode 100644 test/libyul/yulSyntaxTests/objects/empty_code.yul create mode 100644 test/libyul/yulSyntaxTests/objects/empty_data.yul create mode 100644 test/libyul/yulSyntaxTests/objects/empty_object.yul create mode 100644 test/libyul/yulSyntaxTests/objects/empty_object_name.yul create mode 100644 test/libyul/yulSyntaxTests/objects/incomplete1.yul create mode 100644 test/libyul/yulSyntaxTests/objects/incomplete2.yul create mode 100644 test/libyul/yulSyntaxTests/objects/multiple_code.yul create mode 100644 test/libyul/yulSyntaxTests/objects/multiple_data.yul create mode 100644 test/libyul/yulSyntaxTests/objects/multiple_root_object.yul create mode 100644 test/libyul/yulSyntaxTests/objects/nested_object.yul create mode 100644 test/libyul/yulSyntaxTests/objects/object_hex_name.yul create mode 100644 test/libyul/yulSyntaxTests/objects/subobject_access.yul create mode 100644 test/libyul/yulSyntaxTests/objects/subobject_first.yul create mode 100644 test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul diff --git a/test/libyul/yulSyntaxTests/datacopy.yul b/test/libyul/yulSyntaxTests/datacopy.yul deleted file mode 100644 index 73d2bcd6d..000000000 --- a/test/libyul/yulSyntaxTests/datacopy.yul +++ /dev/null @@ -1,12 +0,0 @@ -{ - datacopy(0, 1, 2) - datasize("") - datasize(0) // This should not be valid. - dataoffset("") - dataoffset(0) // This should not be valid. -} -// ---- -// TypeError 3517: (37-39): Unknown data object "". -// TypeError 5859: (54-55): Function expects string literal. -// TypeError 3517: (101-103): Unknown data object "". -// TypeError 5859: (120-121): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/objects/basic_subobject.yul b/test/libyul/yulSyntaxTests/objects/basic_subobject.yul new file mode 100644 index 000000000..031198868 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/basic_subobject.yul @@ -0,0 +1,7 @@ +object "A" { + code {} + + object "B" { + code {} + } +} diff --git a/test/libyul/yulSyntaxTests/objects/code.yul b/test/libyul/yulSyntaxTests/objects/code.yul new file mode 100644 index 000000000..89caa8e50 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/code.yul @@ -0,0 +1,18 @@ +object "A" { + code { + function f() -> offset, len { + offset := dataoffset("A") + len := datasize("A") + } + + let offset, len := f() + codecopy(0, offset, len) + } + + data "hello" "hello world text" + object "hello" { + code {} + } +} +// ---- +// ParserError 8794: (226-233): Object name "hello" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/code_without_object.yul b/test/libyul/yulSyntaxTests/objects/code_without_object.yul new file mode 100644 index 000000000..e67c5c36d --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/code_without_object.yul @@ -0,0 +1,4 @@ +code { +} +// ---- +// ParserError 4294: (0-4): Expected keyword "object". diff --git a/test/libyul/yulSyntaxTests/objects/complex_subobject.yul b/test/libyul/yulSyntaxTests/objects/complex_subobject.yul new file mode 100644 index 000000000..de48bc549 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/complex_subobject.yul @@ -0,0 +1,35 @@ +object "A" { + code {} + data "1" hex"001122" + + object "B" { + code {} + data "2" "" + + object "B_C" { + code {} + + object "B_C_1" { + code {} + } + object "B_C_2" { + code {} + } + } + } + + object "C" { + code {} + } + + object "D" { + code {} + + object "D_1" { + code {} + } + object "D_2" { + code {} + } + } +} diff --git a/test/libyul/yulSyntaxTests/objects/conflict_data_data.yul b/test/libyul/yulSyntaxTests/objects/conflict_data_data.yul new file mode 100644 index 000000000..e09bd8b37 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_data_data.yul @@ -0,0 +1,8 @@ +object "A" { + code {} + + data "B" "" + data "B" hex"00" +} +// ---- +// ParserError 8794: (45-48): Object name "B" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul b/test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul new file mode 100644 index 000000000..5edb1bd3a --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul @@ -0,0 +1,9 @@ +object "A" { + code {} + + object "A" { + code {} + } +} +// ---- +// ParserError 8311: (33-36): Object name cannot be the same as the name of the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_object_data.yul b/test/libyul/yulSyntaxTests/objects/conflict_object_data.yul new file mode 100644 index 000000000..fd64093ad --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_object_data.yul @@ -0,0 +1,10 @@ +object "A" { + code {} + + data "B" "" + object "B" { + code {} + } +} +// ---- +// ParserError 8794: (47-50): Object name "B" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_object_object.yul b/test/libyul/yulSyntaxTests/objects/conflict_object_object.yul new file mode 100644 index 000000000..1b83be4f7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_object_object.yul @@ -0,0 +1,12 @@ +object "A" { + code {} + + object "B" { + code {} + } + object "B" { + code {} + } +} +// ---- +// ParserError 8794: (64-67): Object name "B" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul b/test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul new file mode 100644 index 000000000..44a3d3a2d --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul @@ -0,0 +1,7 @@ +object "A" { + code {} + + data "A" "" +} +// ---- +// ParserError 8311: (31-34): Object name cannot be the same as the name of the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/data.yul b/test/libyul/yulSyntaxTests/objects/data.yul new file mode 100644 index 000000000..47d7b59c7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data.yul @@ -0,0 +1,9 @@ +object "A" { + code { + datacopy(0, dataoffset("3"), datasize("3")) + } + data "1" "" + data "2" hex"0011" + data "3" "hello world this is longer than 32 bytes and should still work" +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/data_access.yul b/test/libyul/yulSyntaxTests/objects/data_access.yul new file mode 100644 index 000000000..e9455fafa --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_access.yul @@ -0,0 +1,9 @@ +object "A" { + code { + pop(dataoffset("B")) + pop(datasize("B")) + } + + data "B" hex"00" +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/data_first.yul b/test/libyul/yulSyntaxTests/objects/data_first.yul new file mode 100644 index 000000000..018674765 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_first.yul @@ -0,0 +1,6 @@ +object "A" { + data "B" "" + code {} +} +// ---- +// ParserError 4846: (15-19): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/data_hex_name.yul b/test/libyul/yulSyntaxTests/objects/data_hex_name.yul new file mode 100644 index 000000000..28f7282d7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_hex_name.yul @@ -0,0 +1,6 @@ +object "A" { + code {} + data hex"11" "" +} +// ---- +// ParserError 2314: (30-37): Expected 'StringLiteral' but got 'HexStringLiteral' diff --git a/test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul b/test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul new file mode 100644 index 000000000..c80f82c20 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul @@ -0,0 +1,8 @@ +object "A" { + code { + } + data "1" hex"0" + data "1" hex"wronghexencoding" +} +// ---- +// ParserError 2314: (37-41): Expected 'StringLiteral' but got 'ILLEGAL' diff --git a/test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul b/test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul new file mode 100644 index 000000000..17029e4f7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul @@ -0,0 +1,8 @@ +object "A" { + code { + } + data "1" hex"wronghexencoding" + data "2" hex"0" +} +// ---- +// ParserError 2314: (37-41): Expected 'StringLiteral' but got 'ILLEGAL' diff --git a/test/libyul/yulSyntaxTests/objects/datacopy.yul b/test/libyul/yulSyntaxTests/objects/datacopy.yul new file mode 100644 index 000000000..bc7e211d8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datacopy.yul @@ -0,0 +1,8 @@ +{ + datacopy(0, 1, 2) + + // datacopy also accepts pretty much anything which can be turned into a number + let x := 0 + let s := "" + datacopy(x, "11", s) +} diff --git a/test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul b/test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul new file mode 100644 index 000000000..d53e26281 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul @@ -0,0 +1,10 @@ +object "A" { + code { + let x := "B" + pop(dataoffset(x)) + } + + data "B" hex"00" +} +// ---- +// TypeError 9114: (47-57): Function expects direct literals as arguments. diff --git a/test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul b/test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul new file mode 100644 index 000000000..93a981d92 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul @@ -0,0 +1,7 @@ +object "A" { + code { + pop(dataoffset(0)) + } +} +// ---- +// TypeError 5859: (41-42): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul b/test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul new file mode 100644 index 000000000..14df40201 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul @@ -0,0 +1,8 @@ +object "A" { + code { + pop(dataoffset("C")) + } + data "B" "" +} +// ---- +// TypeError 3517: (41-44): Unknown data object "C". diff --git a/test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul b/test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul new file mode 100644 index 000000000..837408b9a --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul @@ -0,0 +1,10 @@ +object "A" { + code { + let x := "B" + pop(datasize(x)) + } + + data "B" hex"00" +} +// ---- +// TypeError 9114: (47-55): Function expects direct literals as arguments. diff --git a/test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul b/test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul new file mode 100644 index 000000000..19042a2df --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul @@ -0,0 +1,7 @@ +object "A" { + code { + pop(datasize(0)) + } +} +// ---- +// TypeError 5859: (39-40): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/objects/datasize_notfound.yul b/test/libyul/yulSyntaxTests/objects/datasize_notfound.yul new file mode 100644 index 000000000..48a2c268e --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datasize_notfound.yul @@ -0,0 +1,8 @@ +object "A" { + code { + pop(datasize("C")) + } + data "B" "" +} +// ---- +// TypeError 3517: (39-42): Unknown data object "C". diff --git a/test/libyul/yulSyntaxTests/objects/empty_code.yul b/test/libyul/yulSyntaxTests/objects/empty_code.yul new file mode 100644 index 000000000..a32efaaff --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_code.yul @@ -0,0 +1,3 @@ +object "A" { + code { } +} diff --git a/test/libyul/yulSyntaxTests/objects/empty_data.yul b/test/libyul/yulSyntaxTests/objects/empty_data.yul new file mode 100644 index 000000000..b2456a7ae --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_data.yul @@ -0,0 +1,5 @@ +object "A" { + data "tmp" "" +} +// ---- +// ParserError 4846: (15-19): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/empty_object.yul b/test/libyul/yulSyntaxTests/objects/empty_object.yul new file mode 100644 index 000000000..cc9019e55 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_object.yul @@ -0,0 +1,4 @@ +object "A" { +} +// ---- +// ParserError 4846: (13-14): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/empty_object_name.yul b/test/libyul/yulSyntaxTests/objects/empty_object_name.yul new file mode 100644 index 000000000..653bf4a0f --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_object_name.yul @@ -0,0 +1,5 @@ +object "" { +} +// ---- +// ParserError 3287: (7-9): Object name cannot be empty. +// ParserError 4846: (12-13): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/incomplete1.yul b/test/libyul/yulSyntaxTests/objects/incomplete1.yul new file mode 100644 index 000000000..6698f45a2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/incomplete1.yul @@ -0,0 +1,3 @@ +object { +// ---- +// ParserError 2314: (7-8): Expected 'StringLiteral' but got '{' diff --git a/test/libyul/yulSyntaxTests/objects/incomplete2.yul b/test/libyul/yulSyntaxTests/objects/incomplete2.yul new file mode 100644 index 000000000..f421074f5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/incomplete2.yul @@ -0,0 +1,7 @@ +object "A" { + code {} +} + +object +// ---- +// ParserError 2314: (26-32): Expected end of source but got identifier diff --git a/test/libyul/yulSyntaxTests/objects/multiple_code.yul b/test/libyul/yulSyntaxTests/objects/multiple_code.yul new file mode 100644 index 000000000..fd3347600 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/multiple_code.yul @@ -0,0 +1,6 @@ +object "A" { + code { } + code { } +} +// ---- +// ParserError 8143: (26-30): Expected keyword "data" or "object" or "}". diff --git a/test/libyul/yulSyntaxTests/objects/multiple_data.yul b/test/libyul/yulSyntaxTests/objects/multiple_data.yul new file mode 100644 index 000000000..5a48edd91 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/multiple_data.yul @@ -0,0 +1,6 @@ +object "A" { + code { } + data "one" "" + data "two" "" +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/multiple_root_object.yul b/test/libyul/yulSyntaxTests/objects/multiple_root_object.yul new file mode 100644 index 000000000..842cfcfb5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/multiple_root_object.yul @@ -0,0 +1,8 @@ +object "A" { + code { } +} +object "B" { + code { } +} +// ---- +// ParserError 2314: (26-32): Expected end of source but got identifier diff --git a/test/libyul/yulSyntaxTests/objects/nested_object.yul b/test/libyul/yulSyntaxTests/objects/nested_object.yul new file mode 100644 index 000000000..426cc5586 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/nested_object.yul @@ -0,0 +1,10 @@ +object "outer" { + code { let x := mload(0) } + data "x" "stringdata" + object "inner" { + code { mstore(0, 1) } + object "inner inner" { code {} } + data "innerx" "abc" + data "innery" "def" + } +} diff --git a/test/libyul/yulSyntaxTests/objects/object_hex_name.yul b/test/libyul/yulSyntaxTests/objects/object_hex_name.yul new file mode 100644 index 000000000..c58b5fd19 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/object_hex_name.yul @@ -0,0 +1,5 @@ +object hex"11" { + code {} +} +// ---- +// ParserError 2314: (7-14): Expected 'StringLiteral' but got 'HexStringLiteral' diff --git a/test/libyul/yulSyntaxTests/objects/subobject_access.yul b/test/libyul/yulSyntaxTests/objects/subobject_access.yul new file mode 100644 index 000000000..59426a0aa --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/subobject_access.yul @@ -0,0 +1,11 @@ +object "A" { + code { + pop(dataoffset("B")) + pop(datasize("B")) + } + + object "B" { + code {} + } +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/subobject_first.yul b/test/libyul/yulSyntaxTests/objects/subobject_first.yul new file mode 100644 index 000000000..369e7180e --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/subobject_first.yul @@ -0,0 +1,9 @@ +object "A" { + object "B" { + code {} + } + + code {} +} +// ---- +// ParserError 4846: (15-21): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul b/test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul new file mode 100644 index 000000000..af0581067 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul @@ -0,0 +1,8 @@ +object "A" { + code {} + object hex"11" { + code {} + } +} +// ---- +// ParserError 2314: (32-39): Expected 'StringLiteral' but got 'HexStringLiteral' From 0a8bc861b208b0776f369a47be7ccc9d05fe9fa0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 00:15:05 +0100 Subject: [PATCH 034/139] Remove old boost tests from libyul/ObjectParser --- test/libyul/ObjectParser.cpp | 149 ----------------------------------- 1 file changed, 149 deletions(-) diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 2d7034897..de5b00f3e 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -118,21 +118,6 @@ BOOST_AUTO_TEST_CASE(empty_code) BOOST_CHECK(successParse("{ }")); } -BOOST_AUTO_TEST_CASE(object_with_empty_code) -{ - BOOST_CHECK(successParse("object \"a\" { code { } }")); -} - -BOOST_AUTO_TEST_CASE(non_object) -{ - CHECK_ERROR("code {}", ParserError, "Expected keyword \"object\""); -} - -BOOST_AUTO_TEST_CASE(empty_name) -{ - CHECK_ERROR("object \"\" { code {} }", ParserError, "Object name cannot be empty"); -} - BOOST_AUTO_TEST_CASE(recursion_depth) { string input; @@ -144,77 +129,6 @@ BOOST_AUTO_TEST_CASE(recursion_depth) CHECK_ERROR(input, ParserError, "recursion"); } -BOOST_AUTO_TEST_CASE(object_with_code) -{ - BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } }")); -} - -BOOST_AUTO_TEST_CASE(object_with_code_and_data) -{ - BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } data \"b\" hex\"01010202\" }")); -} - -BOOST_AUTO_TEST_CASE(object_with_non_code_at_start) -{ - CHECK_ERROR("object \"a\" { data \"d\" hex\"0102\" code { } }", ParserError, "Expected keyword \"code\""); -} - -BOOST_AUTO_TEST_CASE(nested_object) -{ - string code = R"( - object "outer" { - code { let x := mload(0) } - data "x" "stringdata" - object "inner" { - code { mstore(0, 1) } - object "inner inner" { code {} } - data "innerx" "abc" - data "innery" "def" - } - } - )"; - BOOST_CHECK(successParse(code)); -} - -BOOST_AUTO_TEST_CASE(incomplete) -{ - CHECK_ERROR("object \"abc\" { code {} } object", ParserError, "Expected end of source"); -} - -BOOST_AUTO_TEST_CASE(reuse_object_name) -{ - string code = R"( - object "outer" { - code { } - data "outer" "stringdata" - } - )"; - CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); -} - -BOOST_AUTO_TEST_CASE(reuse_object_in_subobject) -{ - string code = R"( - object "outer" { - code { } - object "outer" { code {} } - } - )"; - CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); -} - -BOOST_AUTO_TEST_CASE(reuse_object_of_sibling) -{ - string code = R"( - object "O" { - code { } - object "i" { code {} } - data "i" "abc" - } - )"; - CHECK_ERROR(code, ParserError, "already exists inside the"); -} - BOOST_AUTO_TEST_CASE(to_string) { string code = R"( @@ -248,69 +162,6 @@ BOOST_AUTO_TEST_CASE(to_string) BOOST_CHECK_EQUAL(asmStack.print(), expectation); } -BOOST_AUTO_TEST_CASE(arg_to_dataoffset_must_be_literal) -{ - string code = R"( - object "outer" { - code { let x := "outer" let y := dataoffset(x) } - } - )"; - CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); -} - -BOOST_AUTO_TEST_CASE(arg_to_dataoffset_must_be_string_literal) -{ - string code = R"( - object "outer" { - code { let y := dataoffset(0) } - } - )"; - CHECK_ERROR(code, TypeError, "Function expects string literal."); -} - -BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_literal) -{ - string code = R"( - object "outer" { - code { let x := "outer" let y := datasize(x) } - } - )"; - CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); -} - -BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_string_literal) -{ - string code = R"( - object "outer" { - code { let y := datasize(0) } - } - )"; - CHECK_ERROR(code, TypeError, "Function expects string literal."); -} - -BOOST_AUTO_TEST_CASE(args_to_datacopy_are_arbitrary) -{ - string code = R"( - object "outer" { - code { let x := 0 let y := 2 let s := 3 datacopy(x, y, s) } - } - )"; - BOOST_CHECK(successParse(code)); -} - - -BOOST_AUTO_TEST_CASE(non_existing_objects) -{ - BOOST_CHECK(successParse( - "object \"main\" { code { pop(datasize(\"main\")) } }" - )); - CHECK_ERROR( - "object \"main\" { code { pop(datasize(\"abc\")) } }", - TypeError, - "Unknown data object" - ); -} - BOOST_AUTO_TEST_SUITE_END() } From 028f77e8cc0df9cd19f45e5ecb5129555a482f66 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 21:09:49 +0100 Subject: [PATCH 035/139] Remove covered test cases from error_codes.py exception list --- scripts/error_codes.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 7b5966412..6b28b1fc6 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -217,15 +217,15 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): return False old_source_only_ids = { - "1054", "1123", "1133", "1220", "1395", "1584", "1733", "1823", "1950", "1957", - "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", "3069", - "3263", "3287", "3299", "3356", "3384", "3441", "3517", "3682", "3781", "3876", - "3893", "3947", "3997", "4010", "4110", "4294", "4802", "4805", "4828", "4846", + "1054", "1123", "1133", "1220", "1584", "1823", "1950", "1957", + "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", + "3263", "3299", "3356", "3441", "3682", "3876", + "3893", "3997", "4010", "4110", "4802", "4805", "4828", "4904", "4990", "5052", "5073", "5170", "5188", "5272", "5333", "5347", "5473", "5622", "6041", "6052", "6272", "6708", "6792", "6931", "7110", "7128", "7186", - "7319", "7589", "7593", "7653", "7771", "7812", "7885", "8065", "8084", "8140", - "8143", "8261", "8311", "8312", "8452", "8592", "8758", "8794", "9005", "9011", - "9085", "9102", "9114", "9390", "9439", "9440", "9547", "9551", "9615", "9980" + "7319", "7589", "7593", "7653", "7812", "7885", "8065", "8084", "8140", + "8261", "8312", "8452", "8592", "8758", "9011", + "9085", "9102", "9390", "9439", "9440", "9547", "9551", "9615", "9980" } new_source_only_ids = source_only_ids - old_source_only_ids if len(new_source_only_ids) != 0: From 2e8616e2e4e943bf2645c280271effc7b30b5034 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 5 Aug 2020 22:12:51 +0100 Subject: [PATCH 036/139] Run prequisite steps for VarNameCleaner in tests --- test/libyul/YulOptimizerTest.cpp | 4 ++++ .../yulOptimizerTests/varNameCleaner/builtins.yul | 2 +- .../varNameCleaner/function_names.yul | 8 +++++--- .../varNameCleaner/function_parameters.yul | 6 ++++-- .../varNameCleaner/function_scopes.yul | 1 + .../yulOptimizerTests/varNameCleaner/instructions.yul | 2 +- .../varNameCleaner/name_stripping.yul | 10 ++++++---- .../varNameCleaner/reshuffling-inverse.yul | 10 ++++++---- .../yulOptimizerTests/varNameCleaner/reshuffling.yul | 8 +++++--- 9 files changed, 33 insertions(+), 18 deletions(-) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 530f5efec..13c9ed5c7 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -142,7 +142,11 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line else if (m_optimizerStep == "varDeclInitializer") VarDeclInitializer::run(*m_context, *m_object->code); else if (m_optimizerStep == "varNameCleaner") + { + disambiguate(); + FunctionGrouper::run(*m_context, *m_object->code); VarNameCleaner::run(*m_context, *m_object->code); + } else if (m_optimizerStep == "forLoopConditionIntoBody") { disambiguate(); diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul index b6ee0377f..43c30137a 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul @@ -4,4 +4,4 @@ // ---- // step: varNameCleaner // -// { let datasize_1 := 1 } +// { { let datasize_1 := 1 } } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul index ea4ca9ea2..c8b2bef24 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul @@ -1,14 +1,16 @@ { - let f_2 function f() { let f_1 } + let f_2 let f_10 } // ---- // step: varNameCleaner // // { -// let f_1 +// { +// let f_1 +// let f_2 +// } // function f() // { let f_1 } -// let f_2 // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul index 1608dd6fb..c0656056e 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul @@ -12,12 +12,14 @@ // step: varNameCleaner // // { -// let f_1 +// { +// let f_1 +// let f_2 +// } // function f(x) -> x_1, y // { // let y_1 := x // y := y_1 // x_1 := y // } -// let f_2 // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul index c7d86ef98..2882459db 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul @@ -6,6 +6,7 @@ // step: varNameCleaner // // { +// { } // function f() // { let x := 0 } // function g() diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul index 2d7dbeb4a..325ec7d84 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul @@ -4,4 +4,4 @@ // ---- // step: varNameCleaner // -// { let mul_1 := 1 } +// { { let mul_1 := 1 } } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul index cd32d8530..7d7d763c5 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul @@ -8,8 +8,10 @@ // step: varNameCleaner // // { -// let a := 1 -// let a_1 := 2 -// let a_2 := 0xdeadbeef -// let _1 := 21718 +// { +// let a := 1 +// let a_1 := 2 +// let a_2 := 0xdeadbeef +// let _1 := 21718 +// } // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul index 100e5ccc6..237ab8612 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul @@ -8,8 +8,10 @@ // step: varNameCleaner // // { -// let x := 1 -// let x_1 := 2 -// let x_2 := 3 -// let x_3 := 4 +// { +// let x := 1 +// let x_1 := 2 +// let x_2 := 3 +// let x_3 := 4 +// } // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul index d197d4ff3..0df1715d7 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul @@ -7,7 +7,9 @@ // step: varNameCleaner // // { -// let x := 1 -// let x_1 := 2 -// let x_2 := 3 +// { +// let x := 1 +// let x_1 := 2 +// let x_2 := 3 +// } // } From 0226d104b7f65b52f4dd49985fb8c25a793a9023 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 27 Jul 2020 16:22:05 +0200 Subject: [PATCH 037/139] Fix literal arguments in interpreter. --- .../yulInterpreterTests/long_obect_name.yul | 19 ++++++++ .../EVMInstructionInterpreter.cpp | 42 ++++++++++++----- .../EVMInstructionInterpreter.h | 6 ++- .../EwasmBuiltinInterpreter.cpp | 45 ++++++++++++------- .../yulInterpreter/EwasmBuiltinInterpreter.h | 6 ++- test/tools/yulInterpreter/Interpreter.cpp | 22 ++++++--- test/tools/yulInterpreter/Interpreter.h | 7 ++- 7 files changed, 110 insertions(+), 37 deletions(-) create mode 100644 test/libyul/yulInterpreterTests/long_obect_name.yul diff --git a/test/libyul/yulInterpreterTests/long_obect_name.yul b/test/libyul/yulInterpreterTests/long_obect_name.yul new file mode 100644 index 000000000..4105c6e42 --- /dev/null +++ b/test/libyul/yulInterpreterTests/long_obect_name.yul @@ -0,0 +1,19 @@ +object "t" { + code { + datacopy(not(datasize("object2.object3.object4.datablock")), 0, 0) + } + object "object2" { + code{} + object "object3" { + code{} + object "object4" { + code{} + data "datablock" "" + } + } + } +} +// ---- +// Trace: +// Memory dump: +// Storage dump: diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index e5acf3948..75c59c180 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -430,28 +430,46 @@ u256 EVMInstructionInterpreter::eval( return 0; } -u256 EVMInstructionInterpreter::evalBuiltin(BuiltinFunctionForEVM const& _fun, const std::vector& _arguments) +u256 EVMInstructionInterpreter::evalBuiltin( + BuiltinFunctionForEVM const& _fun, + vector const& _arguments, + vector const& _evaluatedArguments +) { if (_fun.instruction) - return eval(*_fun.instruction, _arguments); - else if (_fun.name == "datasize"_yulstring) - return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; - else if (_fun.name == "dataoffset"_yulstring) - return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; - else if (_fun.name == "datacopy"_yulstring) + return eval(*_fun.instruction, _evaluatedArguments); + + string fun = _fun.name.str(); + // Evaluate datasize/offset/copy instructions + if (fun == "datasize" || fun == "dataoffset") + { + string arg = std::get(_arguments.at(0)).value.str(); + if (arg.length() < 32) + arg.resize(32, 0); + if (fun == "datasize") + return u256(keccak256(arg)) & 0xfff; + else + { + // Force different value than for datasize + arg[31] += 2; + return u256(keccak256(arg)) & 0xfff; + } + } + else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_arguments.at(0), _arguments.at(2))) + if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) copyZeroExtended( m_state.memory, m_state.code, - size_t(_arguments.at(0)), - size_t(_arguments.at(1) & numeric_limits::max()), - size_t(_arguments.at(2)) + size_t(_evaluatedArguments.at(0)), + size_t(_evaluatedArguments.at(1) & numeric_limits::max()), + size_t(_evaluatedArguments.at(2)) ); + return 0; } else - yulAssert(false, "Unknown builtin: " + _fun.name.str()); + yulAssert(false, "Unknown builtin: " + fun); return 0; } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index 05d27df96..3747a3fb1 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -71,7 +71,11 @@ public: /// Evaluate instruction u256 eval(evmasm::Instruction _instruction, std::vector const& _arguments); /// Evaluate builtin function - u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector const& _arguments); + u256 evalBuiltin( + BuiltinFunctionForEVM const& _fun, + std::vector const& _arguments, + std::vector const& _evaluatedArguments + ); private: /// Checks if the memory access is not too large for the interpreter and adjusts diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index 61c7b4817..3ec9a84a7 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -116,27 +116,40 @@ uint64_t popcnt(uint64_t _v) using u512 = boost::multiprecision::number>; -u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _arguments) +u256 EwasmBuiltinInterpreter::evalBuiltin( + YulString _functionName, + vector const& _arguments, + vector const& _evaluatedArguments +) { vector arg; - for (u256 const& a: _arguments) + for (u256 const& a: _evaluatedArguments) arg.emplace_back(uint64_t(a & uint64_t(-1))); - string fun = _fun.str(); - if (fun == "datasize") - return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; - else if (fun == "dataoffset") - return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; + string const fun = _functionName.str(); + if (fun == "datasize" || fun == "dataoffset") + { + string arg = std::get(_arguments.at(0)).value.str(); + if (arg.length() < 32) + arg.resize(32, 0); + if (fun == "datasize") + return u256(util::keccak256(arg)) & 0xfff; + else if (fun == "dataoffset") + { + arg[31] += 2; + return u256(util::keccak256(arg)) & 0xfff; + } + } else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_arguments.at(0), _arguments.at(2))) + if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) copyZeroExtended( m_state.memory, m_state.code, - static_cast(_arguments.at(0)), - static_cast(_arguments.at(1) & numeric_limits::max()), - static_cast(_arguments.at(2)) + static_cast(_evaluatedArguments.at(0)), + static_cast(_evaluatedArguments.at(1) & numeric_limits::max()), + static_cast(_evaluatedArguments.at(2)) ); return 0; } @@ -180,14 +193,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a accessMemory(arg[0], 4); return readMemoryHalfWord(arg[0]); } - else if (_fun == "i32.clz"_yulstring) + else if (fun == "i32.clz") // NOTE: the clz implementation assumes 64-bit inputs, hence the adjustment return clz64(arg[0] & uint32_t(-1)) - 32; - else if (_fun == "i64.clz"_yulstring) + else if (fun == "i64.clz") return clz64(arg[0]); - else if (_fun == "i32.ctz"_yulstring) + else if (fun == "i32.ctz") return ctz32(uint32_t(arg[0] & uint32_t(-1))); - else if (_fun == "i64.ctz"_yulstring) + else if (fun == "i64.ctz") return ctz64(arg[0]); string prefix = fun; @@ -211,7 +224,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a else if (prefix == "eth") return evalEthBuiltin(suffix, arg); - yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); + yulAssert(false, "Unknown builtin: " + fun + " (or implementation did not return)"); return 0; } diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h index 78cf9216e..5a855932d 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h @@ -69,7 +69,11 @@ public: m_state(_state) {} /// Evaluate builtin function - u256 evalBuiltin(YulString _fun, std::vector const& _arguments); + u256 evalBuiltin( + YulString _functionName, + std::vector const& _arguments, + std::vector const& _evaluatedArguments + ); private: template diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index a8496793f..c2fd9bff9 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -261,14 +261,18 @@ void ExpressionEvaluator::operator()(Identifier const& _identifier) void ExpressionEvaluator::operator()(FunctionCall const& _funCall) { - evaluateArgs(_funCall.arguments); + vector> const* literalArguments = nullptr; + if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) + if (!builtin->literalArguments.empty()) + literalArguments = &builtin->literalArguments; + evaluateArgs(_funCall.arguments, literalArguments); if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) { if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) { EVMInstructionInterpreter interpreter(m_state); - setValue(interpreter.evalBuiltin(*fun, values())); + setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values())); return; } } @@ -276,7 +280,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) if (dialect->builtin(_funCall.functionName.name)) { EwasmBuiltinInterpreter interpreter(m_state); - setValue(interpreter.evalBuiltin(_funCall.functionName.name, values())); + setValue(interpreter.evalBuiltin(_funCall.functionName.name, _funCall.arguments, values())); return; } @@ -317,14 +321,22 @@ void ExpressionEvaluator::setValue(u256 _value) m_values.emplace_back(std::move(_value)); } -void ExpressionEvaluator::evaluateArgs(vector const& _expr) +void ExpressionEvaluator::evaluateArgs( + vector const& _expr, + vector> const* _literalArguments +) { vector values; + size_t i = 0; /// Function arguments are evaluated in reverse. for (auto const& expr: _expr | boost::adaptors::reversed) { - visit(expr); + if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1)) + visit(expr); + else + m_values = {0}; values.push_back(value()); + ++i; } m_values = std::move(values); std::reverse(m_values.begin(), m_values.end()); diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 00e02f6e2..0f2fa5c7e 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include @@ -197,7 +197,10 @@ private: /// Evaluates the given expression from right to left and /// stores it in m_value. - void evaluateArgs(std::vector const& _expr); + void evaluateArgs( + std::vector const& _expr, + std::vector> const* _literalArguments + ); InterpreterState& m_state; Dialect const& m_dialect; From 89c99800d01d74d1a8cb4df761cf050680b344a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 13:58:14 +0200 Subject: [PATCH 038/139] docs: Fix solidity code blocks by using the capitalized name - This only works with `Solidity`, not with `solidity`. --- docs/natspec-format.rst | 4 ++-- docs/using-the-compiler.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index d4777df31..acb7a35d3 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -46,7 +46,7 @@ The following example shows a contract and a function using all available tags. public. You are welcome to use similar comments for your internal and private functions, but those will not be parsed. -.. code:: solidity +.. code:: Solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >0.6.10 <0.8.0; @@ -126,7 +126,7 @@ JSON output, for example the end-user client software, may present this to the e For example, some client software will render: -.. code:: solidity +.. code:: Solidity /// @notice This function will multiply `a` by 7 diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 9ef1758c1..c1d416dc2 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -637,7 +637,7 @@ Example Assume that you have the following contract in ``Source.sol``: -.. code-block:: solidity +.. code-block:: Solidity pragma solidity >=0.6.0 <0.6.4; // This will not compile after 0.7.0 @@ -691,7 +691,7 @@ It is recommended to explicitly specify the upgrade modules by using ``--modules The command above applies all changes as shown below. Please review them carefully (the pragmas will have to be updated manually.) -.. code-block:: solidity +.. code-block:: Solidity pragma solidity >0.6.99 <0.8.0; // SPDX-License-Identifier: GPL-3.0 From 143b37af56dbcd7b21ddee836418f50c24592975 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 6 Aug 2020 11:43:36 +0200 Subject: [PATCH 039/139] Only conditionally run old code generation in CompilerStack, StandardCompiler and semantic tests. --- Changelog.md | 1 + libsolidity/interface/CompilerStack.cpp | 6 ++--- libsolidity/interface/CompilerStack.h | 8 ++++-- libsolidity/interface/StandardCompiler.cpp | 26 ++++++++++++++++++- .../SolidityExecutionFramework.cpp | 1 + 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4f4e078c3..8f97f68ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. Bugfixes: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b79cf000c..42acb75ec 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -85,9 +85,6 @@ static int g_compilerStackCounts = 0; CompilerStack::CompilerStack(ReadCallback::Callback _readFile): m_readFile{std::move(_readFile)}, m_enabledSMTSolvers{smtutil::SMTSolverChoice::All()}, - m_generateIR{false}, - m_generateEwasm{false}, - m_errorList{}, m_errorReporter{m_errorList} { // Because TypeProvider is currently a singleton API, we must ensure that @@ -518,7 +515,8 @@ bool CompilerStack::compile() { try { - compileContract(*contract, otherCompilers); + if (m_generateEvmBytecode) + compileContract(*contract, otherCompilers); } catch (Error const& _error) { diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 37472d907..1f4575913 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -176,6 +176,9 @@ public: m_requestedContractNames = _contractNames; } + /// Enable EVM Bytecode generation. This is enabled by default. + void enableEvmBytecodeGeneration(bool _enable = true) { m_generateEvmBytecode = _enable; } + /// Enable experimental generation of Yul IR code. void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; } @@ -440,8 +443,9 @@ private: langutil::EVMVersion m_evmVersion; smtutil::SMTSolverChoice m_enabledSMTSolvers; std::map> m_requestedContractNames; - bool m_generateIR; - bool m_generateEwasm; + bool m_generateEvmBytecode = true; + bool m_generateIR = false; + bool m_generateEwasm = false; std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum /// "context:prefix=target" diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index b1e355fab..4506a6f7e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -257,6 +257,30 @@ bool isBinaryRequested(Json::Value const& _outputSelection) return false; } +/// @returns true if EVM bytecode was requested, i.e. we have to run the old code generator. +bool isEvmBytecodeRequested(Json::Value const& _outputSelection) +{ + if (!_outputSelection.isObject()) + return false; + + static vector const outputsThatRequireEvmBinaries{ + "*", + "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", + "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.immutableReferences", + "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", + "evm.bytecode.linkReferences", + "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" + }; + + for (auto const& fileRequests: _outputSelection) + for (auto const& requests: fileRequests) + for (auto const& output: outputsThatRequireEvmBinaries) + if (isArtifactRequested(requests, output, false)) + return true; + return false; +} + /// @returns true if any Ewasm code was requested. Note that as an exception, '*' does not /// yet match "ewasm.wast" or "ewasm" bool isEwasmRequested(Json::Value const& _outputSelection) @@ -840,8 +864,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting compilerStack.setMetadataHash(_inputsAndSettings.metadataHash); compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection)); + compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection)); compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection)); - compilerStack.enableEwasmGeneration(isEwasmRequested(_inputsAndSettings.outputSelection)); Json::Value errors = std::move(_inputsAndSettings.errors); diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index d0f4227cc..6ad8ddd15 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -48,6 +48,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimiserSettings); + m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul); m_compiler.enableIRGeneration(m_compileViaYul); m_compiler.setRevertStringBehaviour(m_revertStrings); if (!m_compiler.compile()) From ec31d971e6f603890205db751849d73a3c7528ea Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 27 Jul 2020 16:55:54 +0200 Subject: [PATCH 040/139] [SMTChecker] Fix tuple name for arrays --- Changelog.md | 1 + libsolidity/formal/SymbolicTypes.cpp | 28 +++++++++++++++++-- ..._loop_array_assignment_storage_storage.sol | 7 +++-- .../types/array_aliasing_memory_1.sol | 2 +- .../types/static_array_implicit_push_1.sol | 7 +++++ .../types/static_array_implicit_push_2.sol | 7 +++++ .../types/static_array_implicit_push_3.sol | 9 ++++++ .../types/static_array_implicit_push_4.sol | 9 ++++++ 8 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol create mode 100644 test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol create mode 100644 test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol create mode 100644 test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol diff --git a/Changelog.md b/Changelog.md index 8f97f68ae..9235faf6e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. * SMTChecker: Fix internal error in BMC function inlining. + * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. * References Resolver: Fix internal bug when using constructor for library. diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index b149cb69c..568368054 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -92,13 +92,35 @@ SortPointer smtSort(frontend::Type const& _type) string tupleName; if ( auto arrayType = dynamic_cast(&_type); - (arrayType && arrayType->isString()) || + (arrayType && (arrayType->isString() || arrayType->isByteArray())) || _type.category() == frontend::Type::Category::ArraySlice || _type.category() == frontend::Type::Category::StringLiteral ) - tupleName = "bytes_tuple"; + tupleName = "bytes"; + else if (auto arrayType = dynamic_cast(&_type)) + { + auto baseType = arrayType->baseType(); + // Solidity allows implicit conversion also when assigning arrays. + // So if the base type potentially has a size, that size cannot go + // in the tuple's name. + if (auto tupleSort = dynamic_pointer_cast(array->range)) + tupleName = tupleSort->name; + else if ( + baseType->category() == frontend::Type::Category::Integer || + baseType->category() == frontend::Type::Category::FixedPoint + ) + tupleName = "uint"; + else if (baseType->category() == frontend::Type::Category::FixedBytes) + tupleName = "fixedbytes"; + else + tupleName = arrayType->baseType()->toString(true); + + tupleName += "[]"; + } else - tupleName = _type.toString(true) + "_tuple"; + tupleName = _type.toString(true); + + tupleName += "_tuple"; return make_shared( tupleName, diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol index de397d4d3..c26a19a1a 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol @@ -20,6 +20,9 @@ contract LoopFor2 { assert(b[0] == 900); } } +// ==== +// SMTSolvers: cvc4 // ---- -// Warning 6328: (320-339): Assertion violation happens here -// Warning 6328: (343-362): Assertion violation happens here. +// Warning 4661: (296-316): Assertion violation happens here +// Warning 4661: (320-339): Assertion violation happens here +// Warning 4661: (343-362): 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 6b09f780d..f267f0b99 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -26,4 +26,4 @@ contract C } } // ---- -// Warning 6328: (400-457): Assertion violation happens here +// Warning 6328: (400-457): Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol new file mode 100644 index 000000000..15e9c4dfd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + uint[][] a; + function f(uint[1] memory x) public { + a.push(x); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol new file mode 100644 index 000000000..2a267cdd9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + uint[][] a; + function f(uint[1][] memory x) public { + a.push(x[2]); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol new file mode 100644 index 000000000..09b8a9773 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract D { + bytes16[] inner; + bytes32[][] data; + function t() public { + data.push(inner); + } +} + diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol new file mode 100644 index 000000000..cbfd162ca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract D { + int16[] inner; + int[][] data; + function t() public { + data.push(inner); + } +} + From d685bee2ec44e98e80532f6f9aa5487f20e2383f Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 24 Jul 2020 18:42:15 +0200 Subject: [PATCH 041/139] [Sol->Yul] Implementing struct copying from calldata to memory. Co-authored-by: Daniel Kirchner --- libsolidity/codegen/YulUtilFunctions.cpp | 17 ++++++++++++++++- .../calldata/calldata_struct_to_memory.sol | 3 ++- .../nested_calldata_struct_to_memory.sol | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 69e8a7131..c687924b3 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1911,8 +1911,23 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) break; } case Type::Category::Struct: - solUnimplementedAssert(false, "Struct conversion not implemented."); + { + solAssert(toCategory == Type::Category::Struct, ""); + auto const& fromStructType = dynamic_cast(_from); + auto const& toStructType = dynamic_cast(_to); + solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); + + solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), ""); + solUnimplementedAssert(toStructType.location() == DataLocation::Memory, ""); + solUnimplementedAssert(fromStructType.location() == DataLocation::CallData, ""); + + body = Whiskers(R"( + converted := (value, calldatasize()) + )")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder( + {&toStructType} + )).render(); break; + } case Type::Category::FixedBytes: { FixedBytesType const& from = dynamic_cast(_from); diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol index db8171a20..04d675371 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol @@ -1,6 +1,5 @@ pragma experimental ABIEncoderV2; - contract C { struct S { uint256 a; @@ -13,5 +12,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol index e0a25a857..19b2f64a7 100644 --- a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol @@ -23,5 +23,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 From 888d7037cd267daee9ec06e088d978a0d1a2d30c Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 8 Apr 2020 13:38:30 -0400 Subject: [PATCH 042/139] Make FunctionCallAnnotation::kind a SetOnce --- liblangutil/ErrorReporter.cpp | 1 + libsolidity/analysis/PostTypeChecker.cpp | 2 +- libsolidity/analysis/StaticAnalyzer.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 18 ++-- libsolidity/analysis/ViewPureChecker.cpp | 2 +- libsolidity/ast/ASTAnnotations.h | 5 +- libsolidity/ast/ASTJsonConverter.cpp | 9 +- libsolidity/codegen/ExpressionCompiler.cpp | 8 +- .../codegen/ir/IRGeneratorForStatements.cpp | 11 +-- libsolidity/formal/BMC.cpp | 5 +- libsolidity/formal/CHC.cpp | 4 +- libsolidity/formal/SMTEncoder.cpp | 11 +-- libsolutil/CMakeLists.txt | 1 + libsolutil/SetOnce.h | 86 +++++++++++++++++++ 14 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 libsolutil/SetOnce.h diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 29a20a8d8..02a983651 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using namespace std; diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index cb04eec6f..47be22810 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -277,7 +277,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker bool visit(FunctionCall const& _functionCall) override { - if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return true; if (FunctionTypePointer const functionType = dynamic_cast(_functionCall.expression().annotation().type)) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 68f52ac26..cd932cf6d 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -292,7 +292,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation) bool StaticAnalyzer::visit(FunctionCall const& _functionCall) { - if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall) { auto functionType = dynamic_cast(_functionCall.expression().annotation().type); solAssert(functionType, ""); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f0b174d19..8ebc76291 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -885,7 +885,7 @@ bool TypeChecker::visit(IfStatement const& _ifStatement) void TypeChecker::endVisit(TryStatement const& _tryStatement) { FunctionCall const* externalCall = dynamic_cast(&_tryStatement.externalCall()); - if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall) + if (!externalCall || *externalCall->annotation().kind != FunctionCallKind::FunctionCall) { m_errorReporter.typeError( 5347_error, @@ -1095,7 +1095,7 @@ void TypeChecker::endVisit(Return const& _return) void TypeChecker::endVisit(EmitStatement const& _emit) { if ( - _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + *_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || type(_emit.eventCall().expression())->category() != Type::Category::Function || dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event ) @@ -1591,7 +1591,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( FunctionCall const& _functionCall ) { - solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(*_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); TypePointer const& expressionType = type(_functionCall.expression()); vector> const& arguments = _functionCall.arguments(); @@ -1956,8 +1956,10 @@ void TypeChecker::typeCheckFunctionGeneralChecks( bool const isPositionalCall = _functionCall.names().empty(); bool const isVariadic = _functionType->takesArbitraryParameters(); + auto functionCallKind = *_functionCall.annotation().kind; + solAssert( - !isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall, + !isVariadic || functionCallKind == FunctionCallKind::FunctionCall, "Struct constructor calls cannot be variadic." ); @@ -1972,7 +1974,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( ) { bool const isStructConstructorCall = - _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + functionCallKind == FunctionCallKind::StructConstructorCall; auto [errorId, description] = [&]() -> tuple { string msg = isVariadic ? @@ -2235,13 +2237,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) default: m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable"); - funcCallAnno.kind = FunctionCallKind::Unset; + // Unreachable, because fatalTypeError throws. We don't set kind, but that's okay because the switch below + // is never reached. And, even if it was, SetOnce would trigger an assertion violation and not UB. funcCallAnno.isPure = argumentsArePure; break; } // Determine return types - switch (funcCallAnno.kind) + switch (*funcCallAnno.kind) { case FunctionCallKind::TypeConversion: funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); @@ -2291,7 +2294,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } - case FunctionCallKind::Unset: // fall-through default: // for non-callables, ensure error reported and annotate node to void function solAssert(m_errorReporter.hasErrors(), ""); diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 469d399b1..a1bf31d77 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -309,7 +309,7 @@ void ViewPureChecker::reportMutability( void ViewPureChecker::endVisit(FunctionCall const& _functionCall) { - if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return; StateMutability mutability = dynamic_cast(*_functionCall.expression().annotation().type).stateMutability(); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 04f661020..1f6e039e3 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -285,7 +287,6 @@ struct BinaryOperationAnnotation: ExpressionAnnotation enum class FunctionCallKind { - Unset, FunctionCall, TypeConversion, StructConstructorCall @@ -293,7 +294,7 @@ enum class FunctionCallKind struct FunctionCallAnnotation: ExpressionAnnotation { - FunctionCallKind kind = FunctionCallKind::Unset; + util::SetOnce kind; /// If true, this is the external call of a try statement. bool tryCall = false; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 5403b05c5..064068005 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -726,13 +726,16 @@ bool ASTJsonConverter::visit(FunctionCall const& _node) make_pair("arguments", toJson(_node.arguments())), make_pair("tryCall", _node.annotation().tryCall) }; + + FunctionCallKind nodeKind = *_node.annotation().kind; + if (m_legacy) { - attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall); - attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion); + attributes.emplace_back("isStructConstructorCall", nodeKind == FunctionCallKind::StructConstructorCall); + attributes.emplace_back("type_conversion", nodeKind == FunctionCallKind::TypeConversion); } else - attributes.emplace_back("kind", functionCallKind(_node.annotation().kind)); + attributes.emplace_back("kind", functionCallKind(nodeKind)); appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "FunctionCall", std::move(attributes)); return false; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b43e0cf9f..8b834031f 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { + auto functionCallKind = *_functionCall.annotation().kind; + CompilerContext::LocationSetter locationSetter(m_context, _functionCall); - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); @@ -517,7 +519,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } FunctionTypePointer functionType; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -548,7 +550,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(found, ""); } - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index a7ef82784..1004ccf1b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -624,12 +624,9 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { - solUnimplementedAssert( - _functionCall.annotation().kind != FunctionCallKind::Unset, - "This type of function call is not yet implemented" - ); + auto functionCallKind = *_functionCall.annotation().kind; - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { solAssert( _functionCall.expression().annotation().type->category() == Type::Category::TypeType, @@ -641,7 +638,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } FunctionTypePointer functionType = nullptr; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -672,7 +669,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) arguments.push_back(callArguments[static_cast(std::distance(callArgumentNames.begin(), it))]); } - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 69165841b..74f814e00 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -337,8 +337,9 @@ void BMC::endVisit(UnaryOperation const& _op) void BMC::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + auto functionCallKind = *_funCall.annotation().kind; + + if (functionCallKind != FunctionCallKind::FunctionCall) { SMTEncoder::endVisit(_funCall); return; diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 304754529..3cf1470bc 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -424,9 +424,9 @@ bool CHC::visit(ForStatement const& _for) void CHC::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + auto functionCallKind = *_funCall.annotation().kind; - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (functionCallKind != FunctionCallKind::FunctionCall) { SMTEncoder::endVisit(_funCall); return; diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 50ede6d13..f9ae5794e 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -581,9 +581,10 @@ void SMTEncoder::endVisit(BinaryOperation const& _op) void SMTEncoder::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + auto functionCallKind = *_funCall.annotation().kind; + createExpr(_funCall); - if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { m_errorReporter.warning( 4639_error, @@ -593,7 +594,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) return; } - if (_funCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { visitTypeConversion(_funCall); return; @@ -753,7 +754,7 @@ void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(*_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); solAssert(_funCall.arguments().size() == 1, ""); auto argument = _funCall.arguments().front(); unsigned argSize = argument->annotation().type->storageBytes(); @@ -1948,7 +1949,7 @@ string SMTEncoder::extraComment() FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall const& _funCall) { - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall) return nullptr; FunctionDefinition const* funDef = nullptr; diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 3672657e8..485218999 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -22,6 +22,7 @@ set(sources LazyInit.h picosha2.h Result.h + SetOnce.h StringUtils.cpp StringUtils.h SwarmHash.cpp diff --git a/libsolutil/SetOnce.h b/libsolutil/SetOnce.h new file mode 100644 index 000000000..289258e7b --- /dev/null +++ b/libsolutil/SetOnce.h @@ -0,0 +1,86 @@ +/* + 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 +#include + +#include +#include +#include + +namespace solidity::util +{ + +DEV_SIMPLE_EXCEPTION(BadSetOnceReassignment); +DEV_SIMPLE_EXCEPTION(BadSetOnceAccess); + +/// A class that stores a value that can only be set once +/// \tparam T the type of the stored value +template +class SetOnce +{ +public: + /// Initializes the class to have no stored value. + SetOnce() = default; + + // Not copiable + SetOnce(SetOnce const&) = delete; + SetOnce(SetOnce&&) = delete; + + // Not movable + SetOnce& operator=(SetOnce const&) = delete; + SetOnce& operator=(SetOnce&&) = delete; + + /// @brief Sets the stored value to \p _newValue + /// @throws BadSetOnceReassignment when the stored value has already been set + /// @return `*this` + constexpr SetOnce& operator=(T _newValue) & + { + assertThrow( + !m_value.has_value(), + BadSetOnceReassignment, + "Attempt to reassign to a SetOnce that already has a value." + ); + + m_value.emplace(std::move(_newValue)); + return *this; + } + + /// @return A reference to the stored value. The returned reference has the same lifetime as `*this`. + /// @throws BadSetOnceAccess when the stored value has not yet been set + T const& operator*() const + { + assertThrow( + m_value.has_value(), + BadSetOnceAccess, + "Attempt to access the value of a SetOnce that does not have a value." + ); + + return m_value.value(); + } + + /// @return A reference to the stored value. The referent of the returned pointer has the same lifetime as `*this`. + /// @throws BadSetOnceAccess when the stored value has not yet been set + T const* operator->() const { return std::addressof(**this); } + +private: + std::optional m_value = std::nullopt; +}; + +} From bb29307b0216dfcd517c35ae568ff0df88294930 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 12 Aug 2020 23:28:19 +0100 Subject: [PATCH 043/139] Add/Update tests for Yul function definition --- test/liblangutil/Scanner.cpp | 68 +++++++++++++++++++ ...inline_assembly_embedded_function_call.sol | 2 +- .../inline_assembly_function_call.sol | 2 +- .../inline_assembly_function_call2.sol | 2 +- ...line_assembly_function_call_assignment.sol | 2 +- .../inlineAssembly/function_definition.sol | 8 +++ .../function_definition_whitespace.sol | 8 +++ .../yulSyntaxTests/function_definition.yul | 4 ++ .../function_definition_whitespace.yul | 4 ++ 9 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol create mode 100644 test/libyul/yulSyntaxTests/function_definition.yul create mode 100644 test/libyul/yulSyntaxTests/function_definition_whitespace.yul diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index bbdf2a469..6aca4c849 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -771,6 +771,74 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) } } +BOOST_AUTO_TEST_CASE(yul_function) +{ + string sig = "function f(a, b) -> x, y"; + Scanner scanner(CharStream(sig, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(sig, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) +{ + string sig = "function f (a, b) - > x, y"; + Scanner scanner(CharStream(sig, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(sig, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol index 3d31c1add..98a6039d5 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol @@ -3,7 +3,7 @@ contract C { assembly { let d:= 0x10 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := g(a) function g(r) - > s { s := mul(r, r) diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol index 17a271d5b..386db6244 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol @@ -1,7 +1,7 @@ contract C { function f() public { assembly { - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol index 26d3d43b7..1cbc510b9 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol @@ -3,7 +3,7 @@ contract C { assembly { let d := 0x10 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol index 2dec9761b..77d28fd4b 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol @@ -3,7 +3,7 @@ contract C { assembly { let a1, b1, c1 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol new file mode 100644 index 000000000..c842060ff --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + assembly { + function f (a, b , c ) -> y,x,z { + } + } + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol new file mode 100644 index 000000000..d336c2325 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + assembly { + function f (a, b , c ) - > y,x,z { + } + } + } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/function_definition.yul b/test/libyul/yulSyntaxTests/function_definition.yul new file mode 100644 index 000000000..9cd21193e --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definition.yul @@ -0,0 +1,4 @@ +{ + function f (a, b , c ) -> y,x,z { + } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/function_definition_whitespace.yul b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul new file mode 100644 index 000000000..fc1035507 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul @@ -0,0 +1,4 @@ +{ + function f (a, b , c ) - > y,x,z { + } +} \ No newline at end of file From 590572d50d1003204e9d143e6dee10aedd6816f0 Mon Sep 17 00:00:00 2001 From: Harry Altman Date: Wed, 12 Aug 2020 19:16:10 -0400 Subject: [PATCH 044/139] Correct immutableReferences documentation from array to object --- docs/using-the-compiler.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index c1d416dc2..008855179 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -441,11 +441,11 @@ Output Description }, "deployedBytecode": { ..., // The same layout as above. - "immutableReferences": [ + "immutableReferences": { // There are two references to the immutable with AST ID 3, both 32 bytes long. One is // at bytecode offset 42, the other at bytecode offset 80. "3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }] - ] + } }, // The list of function hashes "methodIdentifiers": { From acb1e8c280b8238cd24c45bdf291c1550e2177fe Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 12 Aug 2020 23:58:21 +0100 Subject: [PATCH 045/139] Add tests showing hex literals are not allowed in Yul --- .../syntaxTests/inlineAssembly/hex_assignment.sol | 9 +++++++++ .../syntaxTests/inlineAssembly/hex_expression.sol | 9 +++++++++ .../syntaxTests/inlineAssembly/hex_switch_case.sol | 11 +++++++++++ test/libyul/yulSyntaxTests/hex_assignment.yul | 5 +++++ test/libyul/yulSyntaxTests/hex_expression.yul | 5 +++++ test/libyul/yulSyntaxTests/hex_switch_case.yul | 7 +++++++ 6 files changed, 46 insertions(+) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol create mode 100644 test/libyul/yulSyntaxTests/hex_assignment.yul create mode 100644 test/libyul/yulSyntaxTests/hex_expression.yul create mode 100644 test/libyul/yulSyntaxTests/hex_switch_case.yul diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol new file mode 100644 index 000000000..18107e044 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + let x := hex"0011" + } + } +} +// ---- +// ParserError 1856: (72-81): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol new file mode 100644 index 000000000..53cd23a29 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + pop(hex"2233") + } + } +} +// ---- +// ParserError 1856: (67-76): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol new file mode 100644 index 000000000..bc0ff56fe --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + switch codesize() + case hex"00" {} + case hex"1122" {} + } + } +} +// ---- +// ParserError 1856: (92-99): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/hex_assignment.yul b/test/libyul/yulSyntaxTests/hex_assignment.yul new file mode 100644 index 000000000..c9af448c1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_assignment.yul @@ -0,0 +1,5 @@ +{ + let x := hex"0011" +} +// ---- +// ParserError 1856: (15-24): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/hex_expression.yul b/test/libyul/yulSyntaxTests/hex_expression.yul new file mode 100644 index 000000000..f9c1cb52d --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_expression.yul @@ -0,0 +1,5 @@ +{ + pop(hex"2233") +} +// ---- +// ParserError 1856: (10-19): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/hex_switch_case.yul b/test/libyul/yulSyntaxTests/hex_switch_case.yul new file mode 100644 index 000000000..0f3636b4f --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_switch_case.yul @@ -0,0 +1,7 @@ +{ + switch codesize() + case hex"00" {} + case hex"1122" {} +} +// ---- +// ParserError 1856: (33-40): Literal or identifier expected. From 18eed4468857f316d5c8dbbbe2d524bc9a1f2045 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 12 Aug 2020 23:59:37 +0100 Subject: [PATCH 046/139] Drop hex literals from the Yul documentation --- docs/yul.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/yul.rst b/docs/yul.rst index 2054f35c7..7d064059b 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -476,9 +476,8 @@ which are explained in their own chapter. TypeName = Identifier TypedIdentifierList = Identifier ( ':' TypeName )? ( ',' Identifier ( ':' TypeName )? )* Literal = - (NumberLiteral | StringLiteral | HexLiteral | TrueLiteral | FalseLiteral) ( ':' TypeName )? + (NumberLiteral | StringLiteral | TrueLiteral | FalseLiteral) ( ':' TypeName )? NumberLiteral = HexNumber | DecimalNumber - HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' TrueLiteral = 'true' FalseLiteral = 'false' @@ -688,8 +687,6 @@ We will use a destructuring notation for the AST nodes. L'[$parami] = vi and L'[$reti] = 0 for all i. Let G'', L'', mode = E(Gn, L', block) G'', Ln, L''[$ret1], ..., L''[$retm] - E(G, L, l: HexLiteral) = G, L, hexString(l), - where hexString decodes l from hex and left-aligns it into 32 bytes E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l), where utf8EncodeLeftAligned performs a utf8 encoding of l and aligns it left into 32 bytes From b752551ebd63054bc3ba5970f9129dc8914d65c3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 13 Aug 2020 00:26:52 +0100 Subject: [PATCH 047/139] Add test case for string literals in switch statements in Yul --- .../inlineAssembly/string_literal_switch_case.yul | 9 +++++++++ .../libyul/yulSyntaxTests/string_literal_switch_case.yul | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul create mode 100644 test/libyul/yulSyntaxTests/string_literal_switch_case.yul diff --git a/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul b/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul new file mode 100644 index 000000000..d068a72e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + switch codesize() + case "1" {} + case "2" {} + } + } +} diff --git a/test/libyul/yulSyntaxTests/string_literal_switch_case.yul b/test/libyul/yulSyntaxTests/string_literal_switch_case.yul new file mode 100644 index 000000000..02d1045c3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_switch_case.yul @@ -0,0 +1,5 @@ +{ + switch codesize() + case "1" {} + case "2" {} +} \ No newline at end of file From babb175d868ca07d361c2d760d9f0c616c37aaee Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 30 Jul 2020 11:19:08 +0100 Subject: [PATCH 048/139] Add more syntax tests for Yul --- .../inlineAssembly/invalid/dup_disallowed.sol | 43 +++++++++++ .../invalid/jumpdest_disallowed.sol | 9 +++ .../invalid/push_disallowed.sol | 73 +++++++++++++++++++ .../invalid/swap_disallowed.sol | 43 +++++++++++ .../yulSyntaxTests/datacopy_shadowing.yul | 5 ++ .../yulSyntaxTests/dataoffset_shadowing.yul | 5 ++ .../yulSyntaxTests/datasize_shadowing.yul | 5 ++ .../yulSyntaxTests/invalid/dup_disallowed.yul | 39 ++++++++++ .../invalid/invalid_octal_number.yul | 5 ++ .../invalid/jump_disallowed.yul | 5 ++ .../invalid/jumpdest_disallowed.yul | 5 ++ .../invalid/jumpi_disallowed.yul | 5 ++ .../invalid/label_disallowed.yul | 5 ++ .../invalid/leave_items_on_tack.yul | 5 ++ .../invalid/literals_on_stack_disallowed.yul | 5 ++ .../{pc.yul => invalid/pc_disallowed.yul} | 0 .../invalid/push_disallowed.yul | 69 ++++++++++++++++++ .../invalid/swap_disallowed.yul | 39 ++++++++++ .../yulSyntaxTests/linkersymbol_shadowing.yul | 7 ++ .../loadimmutable_shadowing.yul | 7 ++ .../yulSyntaxTests/setimmutable_shadowing.yul | 7 ++ 21 files changed, 386 insertions(+) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol create mode 100644 test/libyul/yulSyntaxTests/datacopy_shadowing.yul create mode 100644 test/libyul/yulSyntaxTests/dataoffset_shadowing.yul create mode 100644 test/libyul/yulSyntaxTests/datasize_shadowing.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/label_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul rename test/libyul/yulSyntaxTests/{pc.yul => invalid/pc_disallowed.yul} (100%) create mode 100644 test/libyul/yulSyntaxTests/invalid/push_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul create mode 100644 test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul create mode 100644 test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul create mode 100644 test/libyul/yulSyntaxTests/setimmutable_shadowing.yul diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol new file mode 100644 index 000000000..9d9b35e28 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol @@ -0,0 +1,43 @@ +contract C { + function f() pure public { + assembly { + dup0() + dup1() + dup2() + dup3() + dup4() + dup5() + dup6() + dup7() + dup8() + dup9() + dup10() + dup11() + dup12() + dup13() + dup14() + dup15() + dup16() + dup32() + } + } +} +// ---- +// DeclarationError 4619: (75-79): Function not found. +// DeclarationError 4619: (94-98): Function not found. +// DeclarationError 4619: (113-117): Function not found. +// DeclarationError 4619: (132-136): Function not found. +// DeclarationError 4619: (151-155): Function not found. +// DeclarationError 4619: (170-174): Function not found. +// DeclarationError 4619: (189-193): Function not found. +// DeclarationError 4619: (208-212): Function not found. +// DeclarationError 4619: (227-231): Function not found. +// DeclarationError 4619: (246-250): Function not found. +// DeclarationError 4619: (265-270): Function not found. +// DeclarationError 4619: (285-290): Function not found. +// DeclarationError 4619: (305-310): Function not found. +// DeclarationError 4619: (325-330): Function not found. +// DeclarationError 4619: (345-350): Function not found. +// DeclarationError 4619: (365-370): Function not found. +// DeclarationError 4619: (385-390): Function not found. +// DeclarationError 4619: (405-410): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol new file mode 100644 index 000000000..7da213776 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol @@ -0,0 +1,9 @@ +contract C { + function f() pure public { + assembly { + jumpdest() + } + } +} +// ---- +// DeclarationError 4619: (75-83): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol new file mode 100644 index 000000000..72e3fcd98 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol @@ -0,0 +1,73 @@ +contract C { + function f() pure public { + assembly { + push0() + push1() + push2() + push3() + push4() + push5() + push6() + push7() + push8() + push9() + push10() + push11() + push12() + push13() + push14() + push15() + push16() + push17() + push18() + push19() + push20() + push21() + push22() + push23() + push24() + push25() + push26() + push27() + push28() + push29() + push30() + push31() + push32() + } + } +} +// ---- +// DeclarationError 4619: (75-80): Function not found. +// DeclarationError 4619: (95-100): Function not found. +// DeclarationError 4619: (115-120): Function not found. +// DeclarationError 4619: (135-140): Function not found. +// DeclarationError 4619: (155-160): Function not found. +// DeclarationError 4619: (175-180): Function not found. +// DeclarationError 4619: (195-200): Function not found. +// DeclarationError 4619: (215-220): Function not found. +// DeclarationError 4619: (235-240): Function not found. +// DeclarationError 4619: (255-260): Function not found. +// DeclarationError 4619: (275-281): Function not found. +// DeclarationError 4619: (296-302): Function not found. +// DeclarationError 4619: (317-323): Function not found. +// DeclarationError 4619: (338-344): Function not found. +// DeclarationError 4619: (359-365): Function not found. +// DeclarationError 4619: (380-386): Function not found. +// DeclarationError 4619: (401-407): Function not found. +// DeclarationError 4619: (422-428): Function not found. +// DeclarationError 4619: (443-449): Function not found. +// DeclarationError 4619: (464-470): Function not found. +// DeclarationError 4619: (485-491): Function not found. +// DeclarationError 4619: (506-512): Function not found. +// DeclarationError 4619: (527-533): Function not found. +// DeclarationError 4619: (548-554): Function not found. +// DeclarationError 4619: (569-575): Function not found. +// DeclarationError 4619: (590-596): Function not found. +// DeclarationError 4619: (611-617): Function not found. +// DeclarationError 4619: (632-638): Function not found. +// DeclarationError 4619: (653-659): Function not found. +// DeclarationError 4619: (674-680): Function not found. +// DeclarationError 4619: (695-701): Function not found. +// DeclarationError 4619: (716-722): Function not found. +// DeclarationError 4619: (737-743): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol new file mode 100644 index 000000000..278398218 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol @@ -0,0 +1,43 @@ +contract C { + function f() pure public { + assembly { + swap0() + swap1() + swap2() + swap3() + swap4() + swap5() + swap6() + swap7() + swap8() + swap9() + swap10() + swap11() + swap12() + swap13() + swap14() + swap15() + swap16() + swap32() + } + } +} +// ---- +// DeclarationError 4619: (75-80): Function not found. +// DeclarationError 4619: (95-100): Function not found. +// DeclarationError 4619: (115-120): Function not found. +// DeclarationError 4619: (135-140): Function not found. +// DeclarationError 4619: (155-160): Function not found. +// DeclarationError 4619: (175-180): Function not found. +// DeclarationError 4619: (195-200): Function not found. +// DeclarationError 4619: (215-220): Function not found. +// DeclarationError 4619: (235-240): Function not found. +// DeclarationError 4619: (255-260): Function not found. +// DeclarationError 4619: (275-281): Function not found. +// DeclarationError 4619: (296-302): Function not found. +// DeclarationError 4619: (317-323): Function not found. +// DeclarationError 4619: (338-344): Function not found. +// DeclarationError 4619: (359-365): Function not found. +// DeclarationError 4619: (380-386): Function not found. +// DeclarationError 4619: (401-407): Function not found. +// DeclarationError 4619: (422-428): Function not found. diff --git a/test/libyul/yulSyntaxTests/datacopy_shadowing.yul b/test/libyul/yulSyntaxTests/datacopy_shadowing.yul new file mode 100644 index 000000000..3f14f4722 --- /dev/null +++ b/test/libyul/yulSyntaxTests/datacopy_shadowing.yul @@ -0,0 +1,5 @@ +{ + function datacopy(a, b, c) {} +} +// ---- +// ParserError 5568: (15-23): Cannot use builtin function name "datacopy" as identifier name. diff --git a/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul b/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul new file mode 100644 index 000000000..fb32fb61c --- /dev/null +++ b/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul @@ -0,0 +1,5 @@ +{ + function dataoffset(a) -> b {} +} +// ---- +// ParserError 5568: (15-25): Cannot use builtin function name "dataoffset" as identifier name. diff --git a/test/libyul/yulSyntaxTests/datasize_shadowing.yul b/test/libyul/yulSyntaxTests/datasize_shadowing.yul new file mode 100644 index 000000000..379401754 --- /dev/null +++ b/test/libyul/yulSyntaxTests/datasize_shadowing.yul @@ -0,0 +1,5 @@ +{ + function datasize(a) -> b {} +} +// ---- +// ParserError 5568: (15-23): Cannot use builtin function name "datasize" as identifier name. diff --git a/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul new file mode 100644 index 000000000..ffcd60c3e --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul @@ -0,0 +1,39 @@ +{ + dup0() + dup1() + dup2() + dup3() + dup4() + dup5() + dup6() + dup7() + dup8() + dup9() + dup10() + dup11() + dup12() + dup13() + dup14() + dup15() + dup16() + dup32() +} +// ---- +// DeclarationError 4619: (6-10): Function not found. +// DeclarationError 4619: (17-21): Function not found. +// DeclarationError 4619: (28-32): Function not found. +// DeclarationError 4619: (39-43): Function not found. +// DeclarationError 4619: (50-54): Function not found. +// DeclarationError 4619: (61-65): Function not found. +// DeclarationError 4619: (72-76): Function not found. +// DeclarationError 4619: (83-87): Function not found. +// DeclarationError 4619: (94-98): Function not found. +// DeclarationError 4619: (105-109): Function not found. +// DeclarationError 4619: (116-121): Function not found. +// DeclarationError 4619: (128-133): Function not found. +// DeclarationError 4619: (140-145): Function not found. +// DeclarationError 4619: (152-157): Function not found. +// DeclarationError 4619: (164-169): Function not found. +// DeclarationError 4619: (176-181): Function not found. +// DeclarationError 4619: (188-193): Function not found. +// DeclarationError 4619: (200-205): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul b/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul new file mode 100644 index 000000000..c3b5e02c2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul @@ -0,0 +1,5 @@ +{ + let x := 0100 +} +// ---- +// ParserError 1856: (15-16): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul new file mode 100644 index 000000000..5ab50a4c6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul @@ -0,0 +1,5 @@ +{ + jump(2) +} +// ---- +// DeclarationError 4619: (6-10): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul new file mode 100644 index 000000000..579f34892 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul @@ -0,0 +1,5 @@ +{ + jumpdest() +} +// ---- +// DeclarationError 4619: (6-14): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul new file mode 100644 index 000000000..e22a86ce1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul @@ -0,0 +1,5 @@ +{ + jumpi(2, 1) +} +// ---- +// DeclarationError 4619: (6-11): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul new file mode 100644 index 000000000..63762bd31 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul @@ -0,0 +1,5 @@ +{ + label: +} +// ---- +// ParserError 6913: (11-12): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul b/test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul new file mode 100644 index 000000000..dca68b7a0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul @@ -0,0 +1,5 @@ +{ + mload(0) +} +// ---- +// TypeError 3083: (6-14): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. diff --git a/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul new file mode 100644 index 000000000..f57faf32a --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul @@ -0,0 +1,5 @@ +{ + 1 +} +// ---- +// ParserError 6913: (8-9): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/pc.yul b/test/libyul/yulSyntaxTests/invalid/pc_disallowed.yul similarity index 100% rename from test/libyul/yulSyntaxTests/pc.yul rename to test/libyul/yulSyntaxTests/invalid/pc_disallowed.yul diff --git a/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul new file mode 100644 index 000000000..c863e58c8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul @@ -0,0 +1,69 @@ +{ + push0() + push1() + push2() + push3() + push4() + push5() + push6() + push7() + push8() + push9() + push10() + push11() + push12() + push13() + push14() + push15() + push16() + push17() + push18() + push19() + push20() + push21() + push22() + push23() + push24() + push25() + push26() + push27() + push28() + push29() + push30() + push31() + push32() +} +// ---- +// DeclarationError 4619: (6-11): Function not found. +// DeclarationError 4619: (18-23): Function not found. +// DeclarationError 4619: (30-35): Function not found. +// DeclarationError 4619: (42-47): Function not found. +// DeclarationError 4619: (54-59): Function not found. +// DeclarationError 4619: (66-71): Function not found. +// DeclarationError 4619: (78-83): Function not found. +// DeclarationError 4619: (90-95): Function not found. +// DeclarationError 4619: (102-107): Function not found. +// DeclarationError 4619: (114-119): Function not found. +// DeclarationError 4619: (126-132): Function not found. +// DeclarationError 4619: (139-145): Function not found. +// DeclarationError 4619: (152-158): Function not found. +// DeclarationError 4619: (165-171): Function not found. +// DeclarationError 4619: (178-184): Function not found. +// DeclarationError 4619: (191-197): Function not found. +// DeclarationError 4619: (204-210): Function not found. +// DeclarationError 4619: (217-223): Function not found. +// DeclarationError 4619: (230-236): Function not found. +// DeclarationError 4619: (243-249): Function not found. +// DeclarationError 4619: (256-262): Function not found. +// DeclarationError 4619: (269-275): Function not found. +// DeclarationError 4619: (282-288): Function not found. +// DeclarationError 4619: (295-301): Function not found. +// DeclarationError 4619: (308-314): Function not found. +// DeclarationError 4619: (321-327): Function not found. +// DeclarationError 4619: (334-340): Function not found. +// DeclarationError 4619: (347-353): Function not found. +// DeclarationError 4619: (360-366): Function not found. +// DeclarationError 4619: (373-379): Function not found. +// DeclarationError 4619: (386-392): Function not found. +// DeclarationError 4619: (399-405): Function not found. +// DeclarationError 4619: (412-418): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul new file mode 100644 index 000000000..4103cab6f --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul @@ -0,0 +1,39 @@ +{ + swap0() + swap1() + swap2() + swap3() + swap4() + swap5() + swap6() + swap7() + swap8() + swap9() + swap10() + swap11() + swap12() + swap13() + swap14() + swap15() + swap16() + swap32() +} +// ---- +// DeclarationError 4619: (6-11): Function not found. +// DeclarationError 4619: (18-23): Function not found. +// DeclarationError 4619: (30-35): Function not found. +// DeclarationError 4619: (42-47): Function not found. +// DeclarationError 4619: (54-59): Function not found. +// DeclarationError 4619: (66-71): Function not found. +// DeclarationError 4619: (78-83): Function not found. +// DeclarationError 4619: (90-95): Function not found. +// DeclarationError 4619: (102-107): Function not found. +// DeclarationError 4619: (114-119): Function not found. +// DeclarationError 4619: (126-132): Function not found. +// DeclarationError 4619: (139-145): Function not found. +// DeclarationError 4619: (152-158): Function not found. +// DeclarationError 4619: (165-171): Function not found. +// DeclarationError 4619: (178-184): Function not found. +// DeclarationError 4619: (191-197): Function not found. +// DeclarationError 4619: (204-210): Function not found. +// DeclarationError 4619: (217-223): Function not found. diff --git a/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul b/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul new file mode 100644 index 000000000..68ce920e1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul @@ -0,0 +1,7 @@ +{ + function linkersymbol(a) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-27): Cannot use builtin function name "linkersymbol" as identifier name. diff --git a/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul b/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul new file mode 100644 index 000000000..8711d0b17 --- /dev/null +++ b/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul @@ -0,0 +1,7 @@ +{ + function loadimmutable(a) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-28): Cannot use builtin function name "loadimmutable" as identifier name. diff --git a/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul b/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul new file mode 100644 index 000000000..b76a5de9b --- /dev/null +++ b/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul @@ -0,0 +1,7 @@ +{ + function setimmutable(a, b) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-27): Cannot use builtin function name "setimmutable" as identifier name. From 8a06041bbeef7eb8a6ba895f14756d3800a3b567 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Aug 2020 12:01:45 +0200 Subject: [PATCH 049/139] [SMTChecker] Add underflow/overflow target to CHC --- libsolidity/formal/BMC.cpp | 64 ++++++-- libsolidity/formal/CHC.cpp | 145 +++++++++++++++--- libsolidity/formal/CHC.h | 10 +- libsolidity/formal/SMTEncoder.cpp | 1 - .../smtCheckerTests/overflow/overflow_mul.sol | 4 +- .../overflow/overflow_mul_signed.sol | 4 +- .../smtCheckerTests/overflow/overflow_sum.sol | 4 +- .../overflow/overflow_sum_signed.sol | 6 +- .../overflow/simple_overflow.sol | 2 +- .../overflow/underflow_sub.sol | 4 +- .../overflow/underflow_sub_signed.sol | 6 +- 11 files changed, 201 insertions(+), 49 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 69165841b..255f7c2ac 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -498,8 +498,37 @@ pair BMC::arithmeticOperation( auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); + auto const* intType = dynamic_cast(_commonType); + if (!intType) + intType = TypeProvider::uint256(); + + // Mod does not need underflow/overflow checks. + if (_op == Token::Mod) + return values; + + VerificationTarget::Type type; + // The order matters here: + // If _op is Div and intType is signed, we only care about overflow. + if (_op == Token::Div) + { + if (intType->isSigned()) + // Signed division can only overflow. + type = VerificationTarget::Type::Overflow; + else + // Unsigned division cannot underflow/overflow. + return values; + } + else if (intType->isSigned()) + type = VerificationTarget::Type::UnderOverflow; + else if (_op == Token::Sub) + type = VerificationTarget::Type::Underflow; + else if (_op == Token::Add || _op == Token::Mul) + type = VerificationTarget::Type::Overflow; + else + solAssert(false, ""); + addVerificationTarget( - VerificationTarget::Type::UnderOverflow, + type, values.second, &_expression ); @@ -605,12 +634,19 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con _target.type == VerificationTarget::Type::UnderOverflow, "" ); - IntegerType const* intType = nullptr; - if (auto const* type = dynamic_cast(_target.expression->annotation().type)) - intType = type; - else + + if ( + m_solvedTargets.count(_target.expression) && ( + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Underflow) || + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow) + ) + ) + return; + + auto const* intType = dynamic_cast(_target.expression->annotation().type); + if (!intType) intType = TypeProvider::uint256(); - solAssert(intType, ""); + checkCondition( _target.constraints && _constraints && _target.value < smt::minValue(*intType), _target.callStack, @@ -631,13 +667,19 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons _target.type == VerificationTarget::Type::UnderOverflow, "" ); - IntegerType const* intType = nullptr; - if (auto const* type = dynamic_cast(_target.expression->annotation().type)) - intType = type; - else + + if ( + m_solvedTargets.count(_target.expression) && ( + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Overflow) || + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow) + ) + ) + return; + + auto const* intType = dynamic_cast(_target.expression->annotation().type); + if (!intType) intType = TypeProvider::uint256(); - solAssert(intType, ""); checkCondition( _target.constraints && _constraints && _target.value > smt::maxValue(*intType), _target.callStack, diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 304754529..b69306727 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -147,7 +147,7 @@ void CHC::endVisit(ContractDefinition const& _contract) else inlineConstructorHierarchy(_contract); - connectBlocks(m_currentBlock, summary(_contract), m_error.currentValue() == 0); + connectBlocks(m_currentBlock, summary(_contract)); clearIndices(m_currentContract, nullptr); vector symbArgs = currentFunctionVariables(*m_currentContract); @@ -452,6 +452,9 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::Creation: + SMTEncoder::endVisit(_funCall); + unknownFunctionCall(_funCall); + break; case FunctionType::Kind::KECCAK256: case FunctionType::Kind::ECRecover: case FunctionType::Kind::SHA256: @@ -459,9 +462,7 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BlockHash: case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: - SMTEncoder::endVisit(_funCall); - unknownFunctionCall(_funCall); - break; + [[fallthrough]]; default: SMTEncoder::endVisit(_funCall); break; @@ -601,14 +602,74 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) auto previousError = m_error.currentValue(); m_error.increaseIndex(); - addArrayPopVerificationTarget(&_arrayPop, m_error.currentValue()); - connectBlocks( - m_currentBlock, - m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction), - currentPathConditions() && symbArray->length() <= 0 && m_error.currentValue() == newErrorId(_arrayPop) + addVerificationTarget(&_arrayPop, VerificationTarget::Type::PopEmptyArray, m_error.currentValue()); + + smtutil::Expression target = (symbArray->length() <= 0) && (m_error.currentValue() == newErrorId(_arrayPop)); + m_context.addAssertion((m_error.currentValue() == previousError) || target); +} + +pair CHC::arithmeticOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType, + frontend::Expression const& _expression +) +{ + auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); + + IntegerType const* intType = nullptr; + if (auto const* type = dynamic_cast(_commonType)) + intType = type; + else + intType = TypeProvider::uint256(); + + // Mod does not need underflow/overflow checks. + // Div only needs overflow check for signed types. + if (_op == Token::Mod || (_op == Token::Div && !intType->isSigned())) + return values; + + auto previousError = m_error.currentValue(); + m_error.increaseIndex(); + + VerificationTarget::Type targetType; + unsigned errorId = newErrorId(_expression); + + optional target; + if (_op == Token::Div) + { + targetType = VerificationTarget::Type::Overflow; + target = values.second > intType->maxValue() && m_error.currentValue() == errorId; + } + else if (intType->isSigned()) + { + unsigned secondErrorId = newErrorId(_expression); + targetType = VerificationTarget::Type::UnderOverflow; + target = (values.second < intType->minValue() && m_error.currentValue() == errorId) || + (values.second > intType->maxValue() && m_error.currentValue() == secondErrorId); + } + else if (_op == Token::Sub) + { + targetType = VerificationTarget::Type::Underflow; + target = values.second < intType->minValue() && m_error.currentValue() == errorId; + } + else if (_op == Token::Add || _op == Token::Mul) + { + targetType = VerificationTarget::Type::Overflow; + target = values.second > intType->maxValue() && m_error.currentValue() == errorId; + } + else + solAssert(false, ""); + + addVerificationTarget( + &_expression, + targetType, + m_error.currentValue() ); - m_context.addAssertion(m_error.currentValue() == previousError); + m_context.addAssertion((m_error.currentValue() == previousError) || *target); + + return values; } void CHC::resetSourceAnalysis() @@ -1174,26 +1235,25 @@ void CHC::addVerificationTarget( m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId}); } -void CHC::addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId) -{ - addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId); -} - -void CHC::addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId) +void CHC::addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId) { solAssert(m_currentContract, ""); - solAssert(m_currentFunction, ""); - if (m_currentFunction->isConstructor()) - addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, summary(*m_currentContract), smtutil::Expression(true), _errorId); + if (!m_currentFunction || m_currentFunction->isConstructor()) + addVerificationTarget(_scope, _type, summary(*m_currentContract), smtutil::Expression(true), _errorId); else { auto iface = (*m_interfaces.at(m_currentContract))(initialStateVariables()); auto sum = summary(*m_currentFunction); - addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, iface, sum, _errorId); + addVerificationTarget(_scope, _type, iface, sum, _errorId); } } +void CHC::addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId) +{ + addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId); +} + void CHC::checkVerificationTargets() { for (auto const& [scope, target]: m_verificationTargets) @@ -1203,8 +1263,12 @@ void CHC::checkVerificationTargets() else { string satMsg; + string satMsgUnderflow; + string satMsgOverflow; string unknownMsg; ErrorId errorReporterId; + ErrorId underflowErrorId = 3944_error; + ErrorId overflowErrorId = 4984_error; if (target.type == VerificationTarget::Type::PopEmptyArray) { @@ -1213,12 +1277,51 @@ void CHC::checkVerificationTargets() unknownMsg = "Empty array \"pop\" might happen here."; errorReporterId = 2529_error; } + else if ( + target.type == VerificationTarget::Type::Underflow || + target.type == VerificationTarget::Type::Overflow || + target.type == VerificationTarget::Type::UnderOverflow + ) + { + auto const* expr = dynamic_cast(scope); + solAssert(expr, ""); + auto const* intType = dynamic_cast(expr->annotation().type); + if (!intType) + intType = TypeProvider::uint256(); + + satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ") happens here"; + satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ") happens here"; + if (target.type == VerificationTarget::Type::Underflow) + { + satMsg = satMsgUnderflow; + errorReporterId = underflowErrorId; + } + else if (target.type == VerificationTarget::Type::Overflow) + { + satMsg = satMsgOverflow; + errorReporterId = overflowErrorId; + } + } else solAssert(false, ""); auto it = m_errorIds.find(scope->id()); solAssert(it != m_errorIds.end(), ""); - checkAndReportTarget(scope, target, it->second, errorReporterId, satMsg, unknownMsg); + unsigned errorId = it->second; + + if (target.type != VerificationTarget::Type::UnderOverflow) + checkAndReportTarget(scope, target, errorId, errorReporterId, satMsg, unknownMsg); + else + { + auto specificTarget = target; + specificTarget.type = VerificationTarget::Type::Underflow; + checkAndReportTarget(scope, specificTarget, errorId, underflowErrorId, satMsgUnderflow, unknownMsg); + + ++it; + solAssert(it != m_errorIds.end(), ""); + specificTarget.type = VerificationTarget::Type::Overflow; + checkAndReportTarget(scope, specificTarget, it->second, overflowErrorId, satMsgOverflow, unknownMsg); + } } } } diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8342524bb..ac1315317 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -84,6 +84,14 @@ private: void externalFunctionCall(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override; + /// Creates underflow/overflow verification targets. + std::pair arithmeticOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType, + Expression const& _expression + ) override; //@} struct IdCompare @@ -197,8 +205,8 @@ private: std::pair query(smtutil::Expression const& _query, langutil::SourceLocation const& _location); void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); + void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId); void addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); - void addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId); void checkVerificationTargets(); // Forward declaration. Definition is below. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 50ede6d13..78711c52f 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1083,7 +1083,6 @@ void SMTEncoder::arrayPop(FunctionCall const& _funCall) auto oldElements = symbArray->elements(); auto oldLength = symbArray->length(); - m_context.addAssertion(oldLength > 0); symbArray->increaseIndex(); m_context.addAssertion(symbArray->elements() == oldElements); diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol index 86697dea0..05b15987f 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol @@ -13,5 +13,5 @@ contract C } } // ---- -// Warning 2661: (120-125): Overflow (resulting value larger than 255) happens here -// Warning 2661: (163-168): Overflow (resulting value larger than 255) happens here +// Warning 4984: (120-125): Overflow (resulting value larger than 255) happens here +// Warning 4984: (163-168): Overflow (resulting value larger than 255) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol index 7663fa34e..7a71a7d2f 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 2661: (117-122): Overflow (resulting value larger than 127) happens here -// Warning 2661: (150-157): Overflow (resulting value larger than 127) happens here +// Warning 4984: (117-122): Overflow (resulting value larger than 127) happens here +// Warning 4984: (150-157): Overflow (resulting value larger than 127) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol index 3eb3fd13f..c94b09b97 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol @@ -14,5 +14,5 @@ contract C } } // ---- -// Warning 2661: (154-159): Overflow (resulting value larger than 255) happens here -// Warning 2661: (185-192): Overflow (resulting value larger than 255) happens here +// Warning 4984: (154-159): Overflow (resulting value larger than 255) happens here +// Warning 4984: (185-192): Overflow (resulting value larger than 255) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol index 7e9938c74..8fc6b4997 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol @@ -14,6 +14,6 @@ contract C } } // ---- -// Warning 2661: (117-122): Overflow (resulting value larger than 127) happens here -// Warning 2661: (151-158): Overflow (resulting value larger than 127) happens here -// Warning 4144: (197-205): Underflow (resulting value less than -128) happens here +// Warning 4984: (117-122): Overflow (resulting value larger than 127) happens here +// Warning 4984: (151-158): Overflow (resulting value larger than 127) happens here +// Warning 3944: (197-205): Underflow (resulting value less than -128) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol index 117554eca..41ba3d0d5 100644 --- a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol @@ -3,4 +3,4 @@ contract C { function f(uint a, uint b) public pure returns (uint) { return a + b; } } // ---- -// Warning 2661: (112-117): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (112-117): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol b/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol index 1029e90a5..1ecf1e870 100644 --- a/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol +++ b/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 4144: (117-122): Underflow (resulting value less than 0) happens here -// Warning 4144: (150-157): Underflow (resulting value less than 0) happens here +// Warning 3944: (117-122): Underflow (resulting value less than 0) happens here +// Warning 3944: (150-157): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol b/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol index 84634a213..6dab43c79 100644 --- a/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol @@ -16,6 +16,6 @@ contract C } } // ---- -// Warning 4144: (116-123): Underflow (resulting value less than -128) happens here -// Warning 4144: (163-170): Underflow (resulting value less than -128) happens here -// Warning 2661: (207-217): Overflow (resulting value larger than 127) happens here +// Warning 3944: (116-123): Underflow (resulting value less than -128) happens here +// Warning 3944: (163-170): Underflow (resulting value less than -128) happens here +// Warning 4984: (207-217): Overflow (resulting value larger than 127) happens here From 80ab56dbc6b9af42989390484db680e59964fd8c Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Aug 2020 13:39:23 +0200 Subject: [PATCH 050/139] Update overflow tests --- .../smtCheckerTests/overflow/signed_div_overflow.sol | 11 +++++++++++ .../overflow/signed_guard_sub_overflow.sol | 10 ++++++++++ .../overflow/signed_guard_sum_overflow.sol | 11 +++++++++++ .../smtCheckerTests/overflow/signed_mod_overflow.sol | 9 +++++++++ .../smtCheckerTests/overflow/signed_mul_overflow.sol | 10 ++++++++++ .../smtCheckerTests/overflow/signed_sub_overflow.sol | 10 ++++++++++ .../smtCheckerTests/overflow/signed_sum_overflow.sol | 10 ++++++++++ .../overflow/unsigned_div_overflow.sol | 9 +++++++++ .../overflow/unsigned_guard_sub_overflow.sol | 8 ++++++++ .../overflow/unsigned_guard_sum_overflow.sol | 8 ++++++++ .../overflow/unsigned_mod_overflow.sol | 9 +++++++++ .../overflow/unsigned_mul_overflow.sol | 9 +++++++++ .../overflow/unsigned_sub_overflow.sol | 9 +++++++++ .../overflow/unsigned_sum_overflow.sol | 9 +++++++++ 14 files changed, 132 insertions(+) create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol create mode 100644 test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol new file mode 100644 index 000000000..fb14d7694 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x / y; + } +} +// ---- +// Warning 1218: (110-115): Error trying to invoke SMT solver. +// Warning 3046: (110-115): Division by zero happens here +// Warning 2661: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol new file mode 100644 index 000000000..a27c709cd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + require(x >= y); + return x - y; + } +} +// ---- +// Warning 4984: (129-134): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol new file mode 100644 index 000000000..a834311cf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + require(x + y >= x); + return x + y; + } +} +// ---- +// Warning 3944: (111-116): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 3944: (133-138): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol new file mode 100644 index 000000000..d773af2f6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x % y; + } +} +// ---- +// Warning 3046: (110-115): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol new file mode 100644 index 000000000..34cc55a7b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x * y; + } +} +// ---- +// Warning 3944: (110-115): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 4984: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol new file mode 100644 index 000000000..4a207c53f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x - y; + } +} +// ---- +// Warning 3944: (110-115): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 4984: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol new file mode 100644 index 000000000..3edc402eb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x + y; + } +} +// ---- +// Warning 3944: (110-115): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 4984: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol new file mode 100644 index 000000000..01eeb27ac --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x / y; + } +} +// ---- +// Warning 3046: (113-118): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol new file mode 100644 index 000000000..879587e06 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + require(x >= y); + return x - y; + } +} diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol new file mode 100644 index 000000000..68b782eca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + require(x + y >= x); + return x + y; + } +} diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol new file mode 100644 index 000000000..327e2bdcc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x % y; + } +} +// ---- +// Warning 3046: (113-118): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol new file mode 100644 index 000000000..bf7b697e8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x * y; + } +} +// ---- +// Warning 4984: (113-118): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol new file mode 100644 index 000000000..6a44c893b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x - y; + } +} +// ---- +// Warning 3944: (113-118): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol new file mode 100644 index 000000000..b0632ef48 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x + y; + } +} +// ---- +// Warning 4984: (113-118): Overflow (resulting value larger than 2**256 - 1) happens here From 0a160b1ba0e82a0d133fff3f2c7fb0a8fd34d655 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Aug 2020 15:53:24 +0200 Subject: [PATCH 051/139] Update remaining tests --- .../array_members/pop_2d_unsafe.sol | 2 +- .../array_members/push_2d_arg_1_unsafe.sol | 2 +- .../push_storage_ref_safe_aliasing.sol | 2 +- .../complex/slither/const_state_variables.sol | 3 +-- .../control_flow/short_circuit_and.sol | 1 - .../control_flow/short_circuit_and_fail.sol | 1 - .../short_circuit_and_inside_branch.sol | 1 - .../control_flow/short_circuit_and_need_both.sol | 1 - .../short_circuit_and_need_both_fail.sol | 1 - .../control_flow/short_circuit_or.sol | 1 - .../control_flow/short_circuit_or_fail.sol | 1 - .../short_circuit_or_inside_branch.sol | 1 - .../control_flow/short_circuit_or_need_both.sol | 1 - .../short_circuit_or_need_both_fail.sol | 1 - .../functions/constructor_hierarchy_3.sol | 4 ++-- .../functions/constructor_hierarchy_4.sol | 6 +++--- .../functions/constructor_hierarchy_diamond.sol | 5 ++--- .../functions/constructor_hierarchy_diamond_2.sol | 5 ++--- .../functions/constructor_hierarchy_diamond_3.sol | 10 +++------- ...nstructor_hierarchy_mixed_chain_with_params.sol | 3 +-- .../constructor_state_value_parameter.sol | 2 +- .../functions/internal_call_state_var_init_2.sol | 3 --- .../smtCheckerTests/functions/library_constant.sol | 5 ++--- ...nstructor_hierarchy_mixed_chain_with_params.sol | 3 +-- ...nstructor_state_variable_init_chain_run_all.sol | 14 ++++++-------- ...tructor_state_variable_init_chain_run_all_2.sol | 11 +++++------ .../smtCheckerTests/loops/do_while_1_fail.sol | 4 +--- .../loops/do_while_1_false_positives.sol | 5 ----- .../smtCheckerTests/loops/for_1_fail.sol | 1 + .../while_loop_array_assignment_memory_memory.sol | 1 + .../while_loop_array_assignment_memory_storage.sol | 1 + .../modifiers/modifier_code_after_placeholder.sol | 2 +- .../modifiers/modifier_overflow.sol | 1 - .../smtCheckerTests/operators/division_3.sol | 1 + .../smtCheckerTests/operators/division_4.sol | 2 ++ .../smtCheckerTests/operators/division_5.sol | 2 ++ .../smtCheckerTests/operators/division_6.sol | 2 ++ .../operators/division_truncates_correctly_2.sol | 1 + .../operators/division_truncates_correctly_3.sol | 1 + .../operators/division_truncates_correctly_4.sol | 1 + .../operators/division_truncates_correctly_5.sol | 1 + .../smtCheckerTests/operators/fixed_point_add.sol | 3 +-- .../operators/fixed_point_compound_add.sol | 2 -- .../smtCheckerTests/operators/mod_even.sol | 1 + test/libsolidity/smtCheckerTests/special/many.sol | 2 +- .../smtCheckerTests/types/address_balance.sol | 2 +- .../types/tuple_declarations_function_2.sol | 2 +- 47 files changed, 53 insertions(+), 75 deletions(-) diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol index 5d4bd3a97..2795dfe51 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 2529: (111-121): Empty array "pop" detected here +// Warning 2529: (111-121): Empty array "pop" detected here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol index c409a0e09..866cfe1eb 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol @@ -10,5 +10,5 @@ contract C { } } // ---- +// Warning 3944: (162-177): Underflow (resulting value less than 0) happens here // Warning 6328: (150-184): Assertion violation happens here -// Warning 4144: (162-177): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol index 8192f673d..5fa9ef7d9 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol @@ -12,5 +12,5 @@ contract C { } } // ---- +// Warning 3944: (217-232): Underflow (resulting value less than 0) happens here // Warning 6328: (205-239): Assertion violation happens here -// Warning 4144: (217-232): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol index 44808ce4c..4e92b34eb 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol @@ -53,5 +53,4 @@ contract MyConc{ // ---- // Warning 2519: (773-792): This declaration shadows an existing declaration. // Warning 2018: (1009-1086): Function state mutability can be restricted to view -// Warning 6084: (985-1002): Underflow (resulting value less than 0) happens here. -// Warning 6084: (985-1002): Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (985-1002): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol index e969e3c3d..66f262787 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol index 186a2e1a8..650bce10a 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (227-236): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here 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 1dd387cc9..77e2f247f 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 @@ -19,4 +19,3 @@ contract c { // ---- // Warning 6328: (202-218): Assertion violation happens here // Warning 6328: (242-252): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol index ff526db60..905964ed5 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol index 32fbccdd7..bc9c800af 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol index d6007e676..1a6cb909a 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol index edada1444..6640e399c 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol index eb54e2236..77ec37ebd 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol @@ -25,4 +25,3 @@ contract c { } // ---- // Warning 6328: (360-370): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol index c585abdb4..c4a823dbc 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol index 1a055a9d7..6615651fd 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index b50177619..789b98368 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -19,6 +19,6 @@ contract A is B { } } // ---- +// Warning 4984: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (244-249): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (232-250): Assertion violation happens here -// Warning 2661: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (244-249): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol index c2466c0d1..6e8c4fe95 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol @@ -18,6 +18,6 @@ contract A is B { } } // ---- -// Warning 2661: (207-212): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (198-203): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (198-203): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (207-212): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol index 29c2d41b2..e5c8d5b38 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -25,7 +25,6 @@ contract A is B2, B1 { } } // ---- +// Warning 4984: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (314-319): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (302-320): Assertion violation happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (314-319): Overflow (resulting value larger than 2**256 - 1) 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 83cb5dc64..b09b836ec 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -25,7 +25,6 @@ contract A is B2, B1 { } } // ---- +// Warning 4984: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (314-319): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (302-320): Assertion violation happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (314-319): Overflow (resulting value larger than 2**256 - 1) 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 cdd479268..f6bfc6d94 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -27,11 +27,7 @@ contract A is B2, B1 { } } // ---- +// Warning 4984: (160-165): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (334-350): Assertion violation happens here -// Warning 4144: (160-165): Underflow (resulting value less than 0) happens here -// Warning 2661: (160-165): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (160-165): Overflow (resulting value larger than 2**256 - 1) 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 223703be4..92e53f40c 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 @@ -25,6 +25,5 @@ contract A is B { } } // ---- +// Warning 4984: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (328-342): Assertion violation happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol index 106e60440..6771f6a05 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol @@ -13,5 +13,5 @@ contract C { } } // ---- +// Warning 4984: (115-120): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (162-176): Assertion violation happens here -// Warning 2661: (115-120): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol index 3439ec6d4..ef7cb6647 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol @@ -7,6 +7,3 @@ contract c { bool b = (f() > 0) || (f() > 0); } // ---- -// Warning 2661: (100-105): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (100-105): Underflow (resulting value less than 0) happens here -// Warning 2661: (100-105): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/library_constant.sol b/test/libsolidity/smtCheckerTests/functions/library_constant.sol index ee521a181..2a72c5216 100644 --- a/test/libsolidity/smtCheckerTests/functions/library_constant.sol +++ b/test/libsolidity/smtCheckerTests/functions/library_constant.sol @@ -20,7 +20,6 @@ contract C { } // ---- // Warning 6328: (136-155): Assertion violation happens here -// Warning 2661: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (327-332): Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 8364: (300-302): Assertion checker does not yet implement type type(library l1) -// Warning 2661: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (327-332): Overflow (resulting value larger than 2**256 - 1) happens here 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 223703be4..92e53f40c 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 @@ -25,6 +25,5 @@ contract A is B { } } // ---- +// Warning 4984: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (328-342): Assertion violation happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) 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 a9323fdbc..5538b9ee8 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 @@ -22,12 +22,10 @@ contract A is B { } // ---- +// Warning 4984: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (216-221): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (261-270): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (287-292): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (275-293): Assertion violation happens here -// Warning 4144: (157-162): Underflow (resulting value less than 0) happens here -// Warning 2661: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (216-221): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (261-270): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (287-292): Overflow (resulting value larger than 2**256 - 1) 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 c0ad1b083..f597d60e4 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 @@ -22,10 +22,9 @@ contract A is B { } // ---- +// Warning 4984: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (240-245): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (262-268): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (285-290): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (273-291): Assertion violation happens here -// Warning 2661: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (240-245): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (262-268): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (285-290): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol index b4d819887..c6cbafaef 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol @@ -5,7 +5,6 @@ contract C function f(uint x) public pure { require(x < 100); do { - // Overflows due to resetting x. x = x + 1; } while (x < 10); assert(x < 14); @@ -14,5 +13,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (179-193): Assertion violation happens here -// Warning 2661: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (143-157): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol index 55f7975f0..2345cc4f3 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol @@ -5,15 +5,10 @@ contract C function f(uint x) public pure { require(x < 100); do { - // Overflows due to resetting x. x = x + 1; } while (x < 1000); - // The assertion is true but we can't infer so - // because x is touched in the loop. assert(x > 0); } } // ==== // SMTSolvers: z3 -// ---- -// Warning 2661: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol index de7fdf973..0579325df 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol @@ -14,5 +14,6 @@ contract C // ==== // SMTSolvers: z3 // ---- +// Warning 1218: (176-181): Error trying to invoke SMT solver. // Warning 6328: (189-203): Assertion violation happens here // Warning 2661: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol index ba6a6a8d2..d10ba5b42 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol @@ -19,6 +19,7 @@ contract LoopFor2 { // ==== // SMTSolvers: z3 // ---- +// Warning 1218: (244-249): Error trying to invoke SMT solver. // Warning 6328: (281-301): Assertion violation happens here // Warning 6328: (305-324): Assertion violation happens here // Warning 6328: (328-347): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol index b977ed326..b61baaac9 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol @@ -23,5 +23,6 @@ contract LoopFor2 { // ==== // SMTSolvers: z3 // ---- +// Warning 1218: (237-242): Error trying to invoke SMT solver. // Warning 6328: (362-382): Assertion violation happens here // Warning 6328: (409-428): 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 5a2dbbcba..f543be8db 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,5 +21,5 @@ contract C } } // ---- +// Warning 4984: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (136-149): Assertion violation happens here -// Warning 2661: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol index 2b5f131d7..52dd6ee5f 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol @@ -15,4 +15,3 @@ contract C } } // ---- -// Warning 2661: (145-150): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/division_3.sol b/test/libsolidity/smtCheckerTests/operators/division_3.sol index 03af864ef..dd82c5bb7 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_3.sol @@ -6,4 +6,5 @@ contract C { } } // ---- +// Warning 1218: (127-132): Error trying to invoke SMT solver. // Warning 2661: (127-132): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/division_4.sol b/test/libsolidity/smtCheckerTests/operators/division_4.sol index ce67e3375..52e3db08b 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_4.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_4.sol @@ -6,3 +6,5 @@ contract C { return x / y; } } +// ---- +// Warning 1218: (147-152): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_5.sol b/test/libsolidity/smtCheckerTests/operators/division_5.sol index ed6966223..6458dd8e3 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_5.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_5.sol @@ -9,3 +9,5 @@ contract C { return c; } } +// ---- +// Warning 1218: (151-156): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_6.sol b/test/libsolidity/smtCheckerTests/operators/division_6.sol index 24e7a6325..7da33cbaa 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_6.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_6.sol @@ -11,3 +11,5 @@ contract C { return c; } } +// ---- +// Warning 1218: (265-270): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol index 6473b98f3..a5a8daf59 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (112-117): Error trying to invoke SMT solver. // Warning 1218: (105-123): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol index c9a932a42..8c903a242 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (113-118): Error trying to invoke SMT solver. // Warning 1218: (106-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol index 95743c934..0514b894f 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (113-118): Error trying to invoke SMT solver. // Warning 1218: (106-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol index 2570b375f..348ad0dd5 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (114-119): Error trying to invoke SMT solver. // Warning 1218: (107-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol index c92a1ac5d..e8e388c15 100644 --- a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol @@ -6,7 +6,6 @@ contract test { } // ---- // Warning 2072: (80-88): Unused local variable. +// Warning 4984: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 5084: (91-100): Type conversion is not yet fully supported and might yield false positives. // Warning 5084: (103-112): Type conversion is not yet fully supported and might yield false positives. -// Warning 4144: (91-112): Underflow (resulting value less than 0) happens here -// Warning 2661: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol index b815b0c94..8a8af3a5c 100644 --- a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol @@ -4,5 +4,3 @@ contract C { function f() internal { b[0] += 1; } } // ---- -// Warning 4144: (84-93): Underflow (resulting value less than 0) happens here -// Warning 2661: (84-93): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/mod_even.sol b/test/libsolidity/smtCheckerTests/operators/mod_even.sol index ab88a7ede..9e8a9cd96 100644 --- a/test/libsolidity/smtCheckerTests/operators/mod_even.sol +++ b/test/libsolidity/smtCheckerTests/operators/mod_even.sol @@ -9,4 +9,5 @@ contract C } } // ---- +// Warning 1218: (113-118): Error trying to invoke SMT solver. // Warning 1218: (122-142): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol index 1fbd2f905..2e9b3c79a 100644 --- a/test/libsolidity/smtCheckerTests/special/many.sol +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -15,6 +15,7 @@ contract C } } // ---- +// Warning 4984: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (79-115): Assertion violation happens here // Warning 6328: (119-161): Assertion violation happens here // Warning 6328: (165-204): Assertion violation happens here @@ -23,4 +24,3 @@ contract C // Warning 6328: (304-332): Assertion violation happens here // Warning 6328: (336-364): Assertion violation happens here // Warning 6328: (368-391): Assertion violation happens here -// Warning 2661: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_balance.sol b/test/libsolidity/smtCheckerTests/types/address_balance.sol index 8edae0c8c..c4d10fead 100644 --- a/test/libsolidity/smtCheckerTests/types/address_balance.sol +++ b/test/libsolidity/smtCheckerTests/types/address_balance.sol @@ -9,5 +9,5 @@ contract C } // ---- // Warning 2072: (96-102): Unused local variable. +// Warning 4984: (105-127): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (131-160): Assertion violation happens here -// Warning 2661: (105-127): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol index 0a89c9975..9cea35cbb 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 2661: (152-157): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (152-157): Overflow (resulting value larger than 2**256 - 1) happens here From 1b720cf67485aaf5ea4b2e3d9d7b364bc7d3dec8 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Aug 2020 15:54:16 +0200 Subject: [PATCH 052/139] Changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 9235faf6e..43b507151 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * SMTChecker: Add underflow and overflow as verification conditions in the CHC engine. * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. From 5afd1219f555f6f70d1ec257e2735b361329d90d Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 11 Aug 2020 21:17:02 +0200 Subject: [PATCH 053/139] Add test with unused error id --- test/libsolidity/smtCheckerTests/operators/div_zero.sol | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/libsolidity/smtCheckerTests/operators/div_zero.sol diff --git a/test/libsolidity/smtCheckerTests/operators/div_zero.sol b/test/libsolidity/smtCheckerTests/operators/div_zero.sol new file mode 100644 index 000000000..81f32822b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/div_zero.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + uint z = 0; + uint x = 2 / z; +} +// ---- +// Warning 6084: (69-74): Division by zero happens here. From 58bfe0b0d2169f18a078686a1d0670175140e415 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Fri, 14 Aug 2020 17:15:35 +0200 Subject: [PATCH 054/139] Tests for refactor-SideEffects --- test/libyul/functionSideEffects/memory.yul | 14 ++++++++++++++ .../libyul/functionSideEffects/otherImmovables.yul | 12 ++++++++++++ test/libyul/functionSideEffects/state.yul | 10 ++++++++++ test/libyul/functionSideEffects/storage.yul | 12 ++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 test/libyul/functionSideEffects/memory.yul create mode 100644 test/libyul/functionSideEffects/otherImmovables.yul create mode 100644 test/libyul/functionSideEffects/state.yul create mode 100644 test/libyul/functionSideEffects/storage.yul diff --git a/test/libyul/functionSideEffects/memory.yul b/test/libyul/functionSideEffects/memory.yul new file mode 100644 index 000000000..6db272b16 --- /dev/null +++ b/test/libyul/functionSideEffects/memory.yul @@ -0,0 +1,14 @@ +{ + function a() { mstore8(0, 32) } + function f() { a() } + function g() { sstore(0, 1) } // does not affect memory + function h() { pop(mload(0)) } + function i() { pop(msize()) } +} +// ---- +// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: invalidatesMemory +// f: invalidatesMemory +// g: invalidatesStorage +// h: sideEffectFreeIfNoMSize +// i: sideEffectFree, sideEffectFreeIfNoMSize diff --git a/test/libyul/functionSideEffects/otherImmovables.yul b/test/libyul/functionSideEffects/otherImmovables.yul new file mode 100644 index 000000000..18e050fa0 --- /dev/null +++ b/test/libyul/functionSideEffects/otherImmovables.yul @@ -0,0 +1,12 @@ +{ + function a() { pop(gas()) } + function f() { a() } + function g() { stop() } + function h() { invalid() } +} +// ---- +// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: sideEffectFree, sideEffectFreeIfNoMSize +// f: sideEffectFree, sideEffectFreeIfNoMSize +// g: +// h: diff --git a/test/libyul/functionSideEffects/state.yul b/test/libyul/functionSideEffects/state.yul new file mode 100644 index 000000000..2b6cd5ce1 --- /dev/null +++ b/test/libyul/functionSideEffects/state.yul @@ -0,0 +1,10 @@ +{ + function a() { pop(call(100, 0x010, 10, 0x00, 32, 0x0100, 32))} + function f() { a() } + function g() { sstore(0, 1) } +} +// ---- +// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: invalidatesStorage, invalidatesMemory +// f: invalidatesStorage, invalidatesMemory +// g: invalidatesStorage diff --git a/test/libyul/functionSideEffects/storage.yul b/test/libyul/functionSideEffects/storage.yul new file mode 100644 index 000000000..0f9c22fde --- /dev/null +++ b/test/libyul/functionSideEffects/storage.yul @@ -0,0 +1,12 @@ +{ + function a() { sstore(0, 1) } + function f() { a() } + function g() { pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32))} + function h() { pop(sload(0))} +} +// ---- +// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: invalidatesStorage +// f: invalidatesStorage +// g: invalidatesStorage, invalidatesMemory +// h: sideEffectFree, sideEffectFreeIfNoMSize From 833f973f0e43df6f27e8ec20c79686bca9c490fb Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 17 Aug 2020 16:49:13 +0200 Subject: [PATCH 055/139] Fix assertion for receive function in libraries. --- libsolidity/codegen/ContractCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 225628a6c..cbad912a8 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -404,7 +404,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have fallback functions"); FunctionDefinition const* etherReceiver = _contract.receiveFunction(); - solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have ether receiver functions"); + solAssert(!_contract.isLibrary() || !etherReceiver, "Libraries can't have ether receiver functions"); bool needToAddCallvalueCheck = true; if (!hasPayableFunctions(_contract) && !interfaceFunctions.empty() && !_contract.isLibrary()) From 53dd818e9674c0898eab29eb5dda4f3578eb4bfb Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Mon, 17 Aug 2020 17:06:06 +0200 Subject: [PATCH 056/139] Ignore UTF errors when handling test files in python --- scripts/isolate_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 0f2bc1d79..16648b26b 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -43,7 +43,7 @@ def extract_docs_cases(path): tests = [] # Collect all snippets of indented blocks - for l in open(path, mode='r', encoding='utf8').read().splitlines(): + for l in open(path, mode='r', errors='ignore', encoding='utf8').read().splitlines(): if l != '': if not inside and l.startswith(' '): # start new test From 9324fb4f20a848609ea417299869ece0044c44f5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 May 2020 18:38:00 +0200 Subject: [PATCH 057/139] Free functions. --- libsolidity/analysis/GlobalContext.cpp | 17 +++- libsolidity/analysis/GlobalContext.h | 1 + libsolidity/analysis/NameAndTypeResolver.cpp | 10 ++ libsolidity/analysis/StaticAnalyzer.cpp | 1 + libsolidity/analysis/SyntaxChecker.cpp | 30 +++++- libsolidity/analysis/SyntaxChecker.h | 3 +- libsolidity/analysis/TypeChecker.cpp | 95 +++++++++++++------ libsolidity/analysis/TypeChecker.h | 10 ++ libsolidity/analysis/ViewPureChecker.cpp | 36 +++---- libsolidity/analysis/ViewPureChecker.h | 1 + libsolidity/ast/AST.cpp | 11 ++- libsolidity/ast/AST.h | 8 ++ libsolidity/ast/ASTAnnotations.h | 3 - libsolidity/ast/ASTJsonConverter.cpp | 2 +- libsolidity/ast/ASTJsonImporter.cpp | 16 +++- libsolidity/ast/Types.cpp | 16 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 10 +- libsolidity/parsing/Parser.cpp | 8 +- libsolidity/parsing/Parser.h | 2 +- .../semanticTests/freeFunctions/easy.sol | 13 +++ .../freeFunctions/free_runtimecode.sol | 15 +++ .../semanticTests/freeFunctions/import.sol | 18 ++++ .../freeFunctions/libraries_from_free.sol | 21 ++++ .../freeFunctions/new_operator.sol | 15 +++ .../semanticTests/freeFunctions/overloads.sol | 16 ++++ .../semanticTests/freeFunctions/recursion.sol | 23 +++++ .../freeFunctions/storage_calldata_refs.sol | 17 ++++ .../free_call_via_contract_type.sol | 8 ++ .../freeFunctions/free_constructor.sol | 3 + .../freeFunctions/free_fallback.sol | 3 + .../freeFunctions/free_function_modifier.sol | 4 + .../free_function_qualified_modifier.sol | 9 ++ .../freeFunctions/free_function_shadowing.sol | 9 ++ .../free_function_visibility.sol | 5 + .../free_function_without_body.sol | 3 + .../freeFunctions/free_functions.sol | 4 + .../freeFunctions/free_mutability.sol | 8 ++ .../freeFunctions/free_overload.sol | 9 ++ .../freeFunctions/free_override.sol | 4 + .../freeFunctions/free_payable.sol | 4 + .../freeFunctions/free_receive.sol | 3 + .../freeFunctions/free_storage.sol | 4 + .../freeFunctions/free_virtual.sol | 4 + .../function_same_name_as_contract.sol | 4 + .../function_using_struct_after_contract.sol | 6 ++ .../syntaxTests/freeFunctions/gas_value.sol | 6 ++ .../freeFunctions/qualified_struct_access.sol | 7 ++ .../freeFunctions/struct_after_function.sol | 3 + .../freeFunctions/super_in_free_function.sol | 6 ++ .../freeFunctions/this_in_free_function.sol | 6 ++ .../imports/importing_free_functions.sol | 14 +++ 51 files changed, 481 insertions(+), 73 deletions(-) create mode 100644 test/libsolidity/semanticTests/freeFunctions/easy.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/import.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/new_operator.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/overloads.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/recursion.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_functions.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_overload.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_override.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_payable.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_receive.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_storage.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/gas_value.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol create mode 100644 test/libsolidity/syntaxTests/imports/importing_free_functions.sol diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 885ae49a7..dee7e8220 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -134,15 +134,26 @@ vector GlobalContext::declarations() const MagicVariableDeclaration const* GlobalContext::currentThis() const { if (!m_thisPointer[m_currentContract]) - m_thisPointer[m_currentContract] = make_shared(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract); + m_thisPointer[m_currentContract] = + make_shared(magicVariableToID("this"), "this", type); + } return m_thisPointer[m_currentContract].get(); - } MagicVariableDeclaration const* GlobalContext::currentSuper() const { if (!m_superPointer[m_currentContract]) - m_superPointer[m_currentContract] = make_shared(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract, true); + m_superPointer[m_currentContract] = + make_shared(magicVariableToID("super"), "super", type); + } return m_superPointer[m_currentContract].get(); } diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h index b84e3a026..191359c60 100644 --- a/libsolidity/analysis/GlobalContext.h +++ b/libsolidity/analysis/GlobalContext.h @@ -46,6 +46,7 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + void resetCurrentContract() { m_currentContract = nullptr; } MagicVariableDeclaration const* currentThis() const; MagicVariableDeclaration const* currentSuper() const; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index c7be364a3..957046810 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -275,6 +275,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!resolveNamesAndTypesInternal(*node, true)) success = false; } + + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); + m_globalContext.resetCurrentContract(); + return success; } else @@ -548,6 +554,10 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) void DeclarationRegistrationHelper::endVisit(ContractDefinition&) { + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); + m_globalContext.resetCurrentContract(); m_currentContract = nullptr; closeCurrentScope(); } diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index cd932cf6d..a6d797910 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -311,6 +311,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) ); } if ( + m_currentContract && m_currentContract->isLibrary() && functionType->kind() == FunctionType::Kind::DelegateCall && functionType->declaration().scope() == m_currentContract diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c0380ffe5..a47d522d9 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -295,7 +295,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(ContractDefinition const& _contract) { - m_isInterface = _contract.isInterface(); + m_currentContractKind = _contract.contractKind(); ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) @@ -309,19 +309,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) return true; } +void SyntaxChecker::endVisit(ContractDefinition const&) +{ + m_currentContractKind = std::nullopt; +} + bool SyntaxChecker::visit(FunctionDefinition const& _function) { - if (!_function.isConstructor() && _function.noVisibilitySpecified()) + solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); + + if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified()) { - string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; + string suggestedVisibility = + _function.isFallback() || + _function.isReceive() || + m_currentContractKind == ContractKind::Interface + ? "external" : "public"; m_errorReporter.syntaxError( 4937_error, _function.location(), "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" ); } + else if (_function.isFree()) + { + if (!_function.noVisibilitySpecified()) + m_errorReporter.syntaxError( + 4126_error, + _function.location(), + "Free functions cannot have visibility." + ); + if (!_function.isImplemented()) + m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented."); + } - if (m_isInterface && !_function.modifiers().empty()) + if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty()) m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers."); else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 1ace2f36a..a5b786e34 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -83,6 +83,7 @@ private: bool visit(PlaceholderStatement const& _placeholderStatement) override; bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; bool visit(FunctionDefinition const& _function) override; bool visit(FunctionTypeName const& _node) override; @@ -102,7 +103,7 @@ private: bool m_versionPragmaFound = false; int m_inLoopDepth = 0; - bool m_isInterface = false; + std::optional m_currentContractKind; SourceUnit const* m_sourceUnit = nullptr; }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8ebc76291..28f2b4152 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -271,6 +271,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); + solAssert(m_currentContract, ""); if (m_currentContract->isInterface() && !base->isInterface()) m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); @@ -327,7 +328,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) { if (_function.markedVirtual()) { - if (_function.isConstructor()) + if (_function.isFree()) + m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual."); + else if (_function.isConstructor()) m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); else if (_function.annotation().contract->isInterface()) m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); @@ -336,12 +339,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); } + if (_function.overrides() && _function.isFree()) + m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override."); if (_function.isPayable()) { if (_function.libraryFunction()) m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); - if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) + else if (_function.isFree()) + m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable."); + else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); } @@ -415,9 +422,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function) set modifiers; for (ASTPointer const& modifier: _function.modifiers()) { - auto baseContracts = dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts; - // Delete first base which is just the main contract itself - baseContracts.erase(baseContracts.begin()); + vector baseContracts; + if (auto contract = dynamic_cast(_function.scope())) + { + baseContracts = contract->annotation().linearizedBaseContracts; + // Delete first base which is just the main contract itself + baseContracts.erase(baseContracts.begin()); + } visitManually( *modifier, @@ -432,7 +443,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_currentContract->isInterface()) + + solAssert(_function.isFree() == !m_currentContract, ""); + if (!m_currentContract) + { + solAssert(!_function.isConstructor(), ""); + solAssert(!_function.isFallback(), ""); + solAssert(!_function.isReceive(), ""); + } + else if (m_currentContract->isInterface()) { if (_function.isImplemented()) m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); @@ -445,6 +464,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (m_currentContract->contractKind() == ContractKind::Library) if (_function.isConstructor()) m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); + if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) @@ -452,7 +472,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); else if (!_function.virtualSemantics()) - m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + { + if (_function.isFree()) + solAssert(m_errorReporter.hasErrors(), ""); + else + m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + } if (_function.isFallback()) @@ -1726,7 +1751,9 @@ void TypeChecker::typeCheckFunctionCall( if (_functionType->kind() == FunctionType::Kind::Declaration) { + solAssert(_functionType->declaration().annotation().contract, ""); if ( + m_currentContract && m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && !dynamic_cast(_functionType->declaration()).isImplemented() ) @@ -2454,18 +2481,23 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (contract->abstract()) m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); - solAssert(!!m_currentContract, ""); - m_currentContract->annotation().contractDependencies.insert(contract); - solAssert( - !contract->annotation().linearizedBaseContracts.empty(), - "Linearized base contracts not yet available." - ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4579_error, - _newExpression.location(), - "Circular reference for contract creation (cannot create instance of derived or same contract)." + if (m_currentContract) + { + // TODO this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + m_currentContract->annotation().contractDependencies.insert(contract); + solAssert( + !contract->annotation().linearizedBaseContracts.empty(), + "Linearized base contracts not yet available." ); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4579_error, + _newExpression.location(), + "Circular reference for contract creation (cannot create instance of derived or same contract)." + ); + } _newExpression.annotation().type = FunctionType::newExpressionType(*contract); } @@ -2507,7 +2539,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) // Retrieve the types of the arguments if this is used to call a function. auto const& arguments = _memberAccess.annotation().arguments; - MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName); + MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName); size_t const initialMemberCount = possibleMembers.size(); if (initialMemberCount > 1 && arguments) { @@ -2533,7 +2565,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) DataLocation::Storage, exprType ); - if (!storageType->members(m_currentContract).membersByName(memberName).empty()) + if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty()) m_errorReporter.fatalTypeError( 4994_error, _memberAccess.location(), @@ -2691,8 +2723,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) { annotation.isPure = true; ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); - m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); - if ( memberName == "runtimeCode" && !accessedContractType.immutableVariables().empty() @@ -2703,12 +2733,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "\"runtimeCode\" is not available for contracts containing immutable variables." ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4224_error, - _memberAccess.location(), - "Circular reference for contract code access." - ); + if (m_currentContract) + { + // TODO in the same way as with ``new``, + // this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + + m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4224_error, + _memberAccess.location(), + "Circular reference for contract code access." + ); + } } else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name") annotation.isPure = true; diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index e34c9e9d0..87ae1d83c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -167,6 +167,16 @@ private: bool experimentalFeatureActive(ExperimentalFeature _feature) const; + /// @returns the current scope that can have function or type definitions. + /// This is either a contract or a source unit. + ASTNode const* currentDefinitionScope() const + { + if (m_currentContract) + return m_currentContract; + else + return m_currentSourceUnit; + } + SourceUnit const* m_currentSourceUnit = nullptr; ContractDefinition const* m_currentContract = nullptr; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index a1bf31d77..1bf6028e5 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -128,30 +128,23 @@ private: bool ViewPureChecker::check() { - vector contracts; + // Process modifiers first to infer their state mutability. + m_checkModifiers = true; + for (auto const& source: m_ast) + source->accept(*this); - for (auto const& node: m_ast) - { - SourceUnit const* source = dynamic_cast(node.get()); - solAssert(source, ""); - contracts += source->filteredNodes(source->nodes()); - } - - // Check modifiers first to infer their state mutability. - for (auto const& contract: contracts) - for (ModifierDefinition const* mod: contract->functionModifiers()) - mod->accept(*this); - - for (auto const& contract: contracts) - contract->accept(*this); + m_checkModifiers = false; + for (auto const& source: m_ast) + source->accept(*this); return !m_errors; } - - bool ViewPureChecker::visit(FunctionDefinition const& _funDef) { + if (m_checkModifiers) + return false; + solAssert(!m_currentFunction, ""); m_currentFunction = &_funDef; m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()}; @@ -160,6 +153,9 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef) void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) { + if (m_checkModifiers) + return; + solAssert(m_currentFunction == &_funDef, ""); if ( m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() && @@ -181,6 +177,9 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) bool ViewPureChecker::visit(ModifierDefinition const& _modifier) { + if (!m_checkModifiers) + return false; + solAssert(m_currentFunction == nullptr, ""); m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()}; return true; @@ -188,6 +187,9 @@ bool ViewPureChecker::visit(ModifierDefinition const& _modifier) void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) { + if (!m_checkModifiers) + return; + solAssert(m_currentFunction == nullptr, ""); m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation); } diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 87135b4f7..62444e086 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -75,6 +75,7 @@ private: langutil::ErrorReporter& m_errorReporter; bool m_errors = false; + bool m_checkModifiers = false; MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()}; FunctionDefinition const* m_currentFunction = nullptr; std::map m_inferredMutability; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 662ea81d3..621fb3936 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -338,7 +338,7 @@ TypePointer FunctionDefinition::type() const TypePointer FunctionDefinition::typeViaContractName() const { - if (annotation().contract->isLibrary()) + if (libraryFunction()) { if (isPublic()) return FunctionType(*this).asExternallyCallableFunction(true); @@ -374,7 +374,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual( if (_searchStart == nullptr && !virtualSemantics()) return *this; - solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + solAssert(!isFree(), ""); + solAssert(isOrdinary(), ""); + solAssert(!libraryFunction(), ""); FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false); @@ -603,9 +605,8 @@ bool VariableDeclaration::isLibraryFunctionParameter() const if (!isCallableOrCatchParameter()) return false; if (auto const* funDef = dynamic_cast(scope())) - return dynamic_cast(*funDef->scope()).isLibrary(); - else - return false; + return funDef->libraryFunction(); + return false; } bool VariableDeclaration::isEventParameter() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 28d71c2b7..8b4025a45 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -774,6 +774,7 @@ public: ASTPointer const& _name, Visibility _visibility, StateMutability _stateMutability, + bool _free, Token _kind, bool _isVirtual, ASTPointer const& _overrides, @@ -787,6 +788,7 @@ public: StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), + m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), m_body(_body) @@ -804,6 +806,7 @@ public: bool isConstructor() const { return m_kind == Token::Constructor; } bool isFallback() const { return m_kind == Token::Fallback; } bool isReceive() const { return m_kind == Token::Receive; } + bool isFree() const { return m_free; } Token kind() const { return m_kind; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector> const& modifiers() const { return m_functionModifiers; } @@ -815,6 +818,7 @@ public: } bool isVisibleViaContractTypeAccess() const override { + solAssert(!isFree(), ""); return isOrdinary() && visibility() >= Visibility::Public; } bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); } @@ -848,8 +852,12 @@ public: ContractDefinition const* _searchStart = nullptr ) const override; +protected: + Visibility defaultVisibility() const override { return isFree() ? Visibility::Internal : Visibility::Public; } + private: StateMutability m_stateMutability; + bool m_free; Token const m_kind; std::vector> m_functionModifiers; ASTPointer m_body; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 1f6e039e3..2aac79837 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -236,9 +236,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation { /// Referenced declaration, set during reference resolution stage. Declaration const* referencedDeclaration = nullptr; - /// Stores a reference to the current contract. - /// This is needed because types of base contracts change depending on the context. - ContractDefinition const* contractScope = nullptr; }; struct ExpressionAnnotation: ASTAnnotation diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 064068005..15ac0c620 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -361,6 +361,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("kind", TokenTraits::toString(_node.kind())), + make_pair("freeFunction", _node.isFree()), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(visibility)), make_pair("virtual", _node.markedVirtual()), @@ -467,7 +468,6 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) setJsonNode(_node, "UserDefinedTypeName", { make_pair("name", namePathToString(_node.namePath())), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), - make_pair("contractScope", idOrNull(_node.annotation().contractScope)), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 2d1fda322..8bc657abf 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -397,11 +397,25 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V std::vector> modifiers; for (auto& mod: member(_node, "modifiers")) modifiers.push_back(createModifierInvocation(mod)); + + Visibility vis = Visibility::Default; + bool const freeFunction = _node.isMember("freeFunction") ? _node["freeFunction"].asBool() : false; + if (kind == Token::Constructor) + { + } + else if (freeFunction) + astAssert( + vis == Visibility::Internal || vis == Visibility::Default, + "Expected internal or default visibility for free function." + ); + else + vis = visibility(_node); return createASTNode( _node, memberAsASTString(_node, "name"), - kind == Token::Constructor ? Visibility::Default : visibility(_node), + vis, stateMutability(_node), + freeFunction, kind, memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a87ae4c3f..956412425 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3151,10 +3151,8 @@ string FunctionType::toString(bool _short) const { auto const* functionDefinition = dynamic_cast(m_declaration); solAssert(functionDefinition, ""); - auto const* contract = dynamic_cast(functionDefinition->scope()); - solAssert(contract, ""); - name += contract->annotation().canonicalName; - name += '.'; + if (auto const* contract = dynamic_cast(functionDefinition->scope())) + name += contract->annotation().canonicalName + "."; name += functionDefinition->name(); } name += '('; @@ -3275,7 +3273,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const { // Note that m_declaration might also be a state variable! solAssert(m_declaration, "Declaration needed to determine interface function type."); - bool isLibraryFunction = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool isLibraryFunction = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + isLibraryFunction = contract->isLibrary(); util::Result paramTypes = transformParametersToExternal(m_parameterTypes, isLibraryFunction); @@ -3569,7 +3570,10 @@ string FunctionType::externalSignature() const } // "inLibrary" is only relevant if this is not an event. - bool const inLibrary = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool inLibrary = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + inLibrary = contract->isLibrary(); auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 8b834031f..d4f70691f 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1736,9 +1736,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) Type::Category category = _memberAccess.annotation().type->category(); solAssert( category == Type::Category::TypeType || - category == Type::Category::Module, + category == Type::Category::Module || + category == Type::Category::Function, "" ); + if (auto funType = dynamic_cast(_memberAccess.annotation().type)) + { + auto const* funDef = dynamic_cast(_memberAccess.annotation().referencedDeclaration); + solAssert(funDef && funDef->isFree(), ""); + solAssert(funType->kind() == FunctionType::Kind::Internal, ""); + utils().pushCombinedFunctionEntryLabel(*funDef); + } break; } default: diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cfb7de46e..47be4039a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -107,8 +107,11 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) case Token::Enum: nodes.push_back(parseEnumDefinition()); break; + case Token::Function: + nodes.push_back(parseFunctionDefinition(true)); + break; default: - fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition."); + fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition."); } } solAssert(m_recursionDepth == 0, ""); @@ -548,7 +551,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari return result; } -ASTPointer Parser::parseFunctionDefinition() +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -607,6 +610,7 @@ ASTPointer Parser::parseFunctionDefinition() name, header.visibility, header.stateMutability, + _freeFunction, kind, header.isVirtual, header.overrides, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index b769545da..0047ed6e4 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -92,7 +92,7 @@ private: ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(); + ASTPointer parseFunctionDefinition(bool _freeFunction = false); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseEnumValue(); diff --git a/test/libsolidity/semanticTests/freeFunctions/easy.sol b/test/libsolidity/semanticTests/freeFunctions/easy.sol new file mode 100644 index 000000000..04b023d52 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/easy.sol @@ -0,0 +1,13 @@ +function add(uint a, uint b) pure returns (uint) { + return a + b; +} + +contract C { + function f(uint x) public pure returns (uint) { + return add(x, 2); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 7 -> 9 diff --git a/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol new file mode 100644 index 000000000..7a05ff5d0 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol @@ -0,0 +1,15 @@ +contract C { + uint public x = 2; +} + +function test() returns (bool) { + return type(C).runtimeCode.length > 20; +} + +contract D { + function f() public returns (bool) { + return test(); + } +} +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/freeFunctions/import.sol b/test/libsolidity/semanticTests/freeFunctions/import.sol new file mode 100644 index 000000000..edd44de49 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/import.sol @@ -0,0 +1,18 @@ +==== Source: A ==== +struct S { uint x; } +function set(S storage a, uint v) { a.x = v; } + +==== Source: B ==== +import "A"; +import "A" as A; +contract C { + A.S data; + function f(uint v) public returns (uint one, uint two) { + A.set(data, v); + one = data.x; + set(data, v + 1); + two = data.x; + } +} +// ---- +// f(uint256): 7 -> 7, 8 diff --git a/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol new file mode 100644 index 000000000..3632f6a6a --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol @@ -0,0 +1,21 @@ +library L { + function pub() public pure returns (uint) { + return 7; + } + function inter() internal pure returns (uint) { + return 8; + } +} + +function fu() pure returns (uint, uint) { + return (L.pub(), L.inter()); +} + +contract C { + function f() public pure returns (uint, uint) { + return fu(); + } +} +// ---- +// library: L +// f() -> 7, 8 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/freeFunctions/new_operator.sol b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol new file mode 100644 index 000000000..e35f1c9ea --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol @@ -0,0 +1,15 @@ +contract C { + uint public x = 2; +} + +function test() returns (uint) { + return (new C()).x(); +} + +contract D { + function f() public returns (uint) { + return test(); + } +} +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/freeFunctions/overloads.sol b/test/libsolidity/semanticTests/freeFunctions/overloads.sol new file mode 100644 index 000000000..9b2a914eb --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/overloads.sol @@ -0,0 +1,16 @@ +function f(uint) returns (uint) { + return 2; +} +function f(string memory) returns (uint) { + return 3; +} + +contract C { + function g() public returns (uint, uint) { + return (f(2), f("abc")); + } +} +// ==== +// compileViaYul: also +// ---- +// g() -> 2, 3 diff --git a/test/libsolidity/semanticTests/freeFunctions/recursion.sol b/test/libsolidity/semanticTests/freeFunctions/recursion.sol new file mode 100644 index 000000000..fd21fe097 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/recursion.sol @@ -0,0 +1,23 @@ +function exp(uint base, uint exponent) pure returns (uint power) { + if (exponent == 0) + return 1; + power = exp(base, exponent / 2); + power *= power; + if (exponent & 1 == 1) + power *= base; +} + +contract C { + function g(uint base, uint exponent) public pure returns (uint) { + return exp(base, exponent); + } +} +// ==== +// compileViaYul: also +// ---- +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 10 -> 59049 +// g(uint256,uint256): 2, 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol new file mode 100644 index 000000000..3b94b06fc --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol @@ -0,0 +1,17 @@ +contract C { + uint[] data; + function f(uint x, uint[] calldata input) public returns (uint, uint) { + data.push(x); + (uint a, uint[] calldata b) = fun(input, data); + return (a, b[1]); + + } +} + +function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256[]): 7, 0x40, 3, 8, 9, 10 -> 7, 9 diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol new file mode 100644 index 000000000..749a98d48 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure {} +} +function fun() { + C.f(); +} +// ---- +// TypeError 3419: (68-73): Cannot call function via contract type name. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol new file mode 100644 index 000000000..afb375ca0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol @@ -0,0 +1,3 @@ +constructor() {} +// ---- +// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol new file mode 100644 index 000000000..31b410211 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol @@ -0,0 +1,3 @@ +fallback(){} +// ---- +// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol new file mode 100644 index 000000000..dd702853a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol @@ -0,0 +1,4 @@ +function fun() someModifier { +} +// ---- +// DeclarationError 7576: (15-27): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol new file mode 100644 index 000000000..779d44c72 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol @@ -0,0 +1,9 @@ +contract C { + modifier someModifier() { _; } +} + +function fun() C.someModifier { + +} +// ---- +// ParserError 2314: (65-66): Expected '{' but got '.' diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol new file mode 100644 index 000000000..bd1b2bd87 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol @@ -0,0 +1,9 @@ +function f() {} +contract C { + function f() public {} + function g() public { + f(); + } +} +// ---- +// Warning 2519: (31-53): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol new file mode 100644 index 000000000..36a0e0e0c --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol @@ -0,0 +1,5 @@ +function fun1() public { } +function fun2() internal { } +// ---- +// SyntaxError 4126: (0-26): Free functions cannot have visibility. +// SyntaxError 4126: (27-55): Free functions cannot have visibility. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol new file mode 100644 index 000000000..2fec9db1d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol @@ -0,0 +1,3 @@ +function f(); +// ---- +// TypeError 4668: (0-13): Free functions must be implemented. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol new file mode 100644 index 000000000..4862e8e63 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol @@ -0,0 +1,4 @@ +function fun(uint256, uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol new file mode 100644 index 000000000..dafa91ccb --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol @@ -0,0 +1,8 @@ +function f() { + uint x = 2; + x; +} +function g(uint[] storage x) pure { x[0] = 1; } +// ---- +// Warning 2018: (0-39): Function state mutability can be restricted to pure +// TypeError 8961: (76-80): Function declared as pure, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol new file mode 100644 index 000000000..440b9449f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol @@ -0,0 +1,9 @@ +function f(uint) returns (bytes memory) {} +function f(uint[] memory x) returns (bytes memory) { return f(x[0]); } +function g(uint8) {} +function g(uint16) {} +function t() { + g(2); +} +// ---- +// TypeError 4487: (176-177): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_override.sol b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol new file mode 100644 index 000000000..da54a2fa6 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol @@ -0,0 +1,4 @@ +function fun() override { +} +// ---- +// SyntaxError 1750: (0-27): Free functions cannot override. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol new file mode 100644 index 000000000..563c118f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol @@ -0,0 +1,4 @@ +function fun() payable { +} +// ---- +// TypeError 9559: (0-26): Free functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol new file mode 100644 index 000000000..5801b9e9e --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol @@ -0,0 +1,3 @@ +receive() {} +// ---- +// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol new file mode 100644 index 000000000..7d356a9a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol @@ -0,0 +1,4 @@ +struct S { uint x; } +function fun(S storage) { +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol new file mode 100644 index 000000000..a4261be1f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol @@ -0,0 +1,4 @@ +function fun() virtual { +} +// ---- +// SyntaxError 4493: (0-26): Free functions cannot be virtual. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol new file mode 100644 index 000000000..3b16749ae --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol @@ -0,0 +1,4 @@ +contract C {} +function C() {} +// ---- +// DeclarationError 2333: (14-29): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol new file mode 100644 index 000000000..b029c0d90 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint x; } +} +function f() returns (uint) { S storage t; } +// ---- +// DeclarationError 7920: (70-71): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol new file mode 100644 index 000000000..7e2dafe8a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol @@ -0,0 +1,6 @@ +function fun() { + fun{gas: 1}(); + fun{value: 1}(); +} +// ---- +// TypeError 2193: (21-32): Function call options can only be set on external function calls or contract creations. diff --git a/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol new file mode 100644 index 000000000..ab027e21d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol @@ -0,0 +1,7 @@ +function f() returns (uint) { C.S storage t; t.x; } + +contract C { + struct S { uint x; } +} +// ---- +// TypeError 3464: (45-46): 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/freeFunctions/struct_after_function.sol b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol new file mode 100644 index 000000000..c41693e97 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol @@ -0,0 +1,3 @@ +function f(S storage g) view returns (uint) { S storage t = g; return t.x; } +struct S { uint x; } +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol new file mode 100644 index 000000000..ff6061c65 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + super; +} +// ---- +// DeclarationError 7576: (33-38): Undeclared identifier. "super" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol new file mode 100644 index 000000000..cdea708d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + this; +} +// ---- +// DeclarationError 7576: (33-37): Undeclared identifier. "this" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/imports/importing_free_functions.sol b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol new file mode 100644 index 000000000..02bc3673e --- /dev/null +++ b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol @@ -0,0 +1,14 @@ +==== Source: a ==== +function f(uint x) pure returns (uint) { return x * 3; } +==== Source: b ==== +import "a" as A; +function g(uint x) pure returns (uint) { return A.f(x) * 3; } +==== Source: c ==== +import "b" as B; +contract C { + function f() public pure { + B.g(2); + B.A.f(3); + } +} +// ---- From e9f91edc4b6d1298a9ed3506c11362a714bde21f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jun 2020 03:35:09 +0200 Subject: [PATCH 058/139] Update existing tests. --- libsolidity/ast/AST.cpp | 2 +- libsolidity/ast/AST.h | 3 --- test/cmdlineTests/recovery_ast_constructor/output | 2 ++ test/cmdlineTests/recovery_ast_empty_contract/err | 2 +- test/cmdlineTests/standard_only_ast_requested/output.json | 2 +- test/libsolidity/ASTJSON/abstract_contract.json | 1 + test/libsolidity/ASTJSON/abstract_contract_legacy.json | 1 + test/libsolidity/ASTJSON/address_payable.json | 1 + test/libsolidity/ASTJSON/address_payable_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/call.json | 1 + test/libsolidity/ASTJSON/assembly/call_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/empty_block.json | 1 + test/libsolidity/ASTJSON/assembly/empty_block_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/function.json | 1 + test/libsolidity/ASTJSON/assembly/function_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/leave.json | 1 + test/libsolidity/ASTJSON/assembly/leave_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/loop.json | 1 + test/libsolidity/ASTJSON/assembly/loop_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/nested_functions.json | 1 + .../ASTJSON/assembly/nested_functions_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/slot_offset.json | 1 + test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/stringlit.json | 1 + test/libsolidity/ASTJSON/assembly/stringlit_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/switch.json | 1 + test/libsolidity/ASTJSON/assembly/switch_default.json | 1 + test/libsolidity/ASTJSON/assembly/switch_default_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/switch_legacy.json | 1 + test/libsolidity/ASTJSON/assembly/var_access.json | 1 + test/libsolidity/ASTJSON/assembly/var_access_legacy.json | 1 + test/libsolidity/ASTJSON/constructor.json | 1 + test/libsolidity/ASTJSON/constructor_legacy.json | 1 + test/libsolidity/ASTJSON/documentation.json | 1 + test/libsolidity/ASTJSON/documentation_legacy.json | 1 + test/libsolidity/ASTJSON/fallback.json | 1 + test/libsolidity/ASTJSON/fallback_and_reveice_ether.json | 2 ++ .../ASTJSON/fallback_and_reveice_ether_legacy.json | 2 ++ test/libsolidity/ASTJSON/fallback_legacy.json | 1 + test/libsolidity/ASTJSON/fallback_payable.json | 1 + test/libsolidity/ASTJSON/fallback_payable_legacy.json | 1 + test/libsolidity/ASTJSON/function_type.json | 1 + test/libsolidity/ASTJSON/function_type_legacy.json | 1 + .../libsolidity/ASTJSON/long_type_name_binary_operation.json | 1 + .../ASTJSON/long_type_name_binary_operation_legacy.json | 1 + test/libsolidity/ASTJSON/long_type_name_identifier.json | 1 + .../ASTJSON/long_type_name_identifier_legacy.json | 1 + test/libsolidity/ASTJSON/modifier_definition.json | 1 + test/libsolidity/ASTJSON/modifier_definition_legacy.json | 1 + test/libsolidity/ASTJSON/modifier_invocation.json | 1 + test/libsolidity/ASTJSON/modifier_invocation_legacy.json | 1 + test/libsolidity/ASTJSON/non_utf8.json | 1 + test/libsolidity/ASTJSON/non_utf8_legacy.json | 1 + test/libsolidity/ASTJSON/override.json | 5 +++++ test/libsolidity/ASTJSON/override_legacy.json | 5 +++++ test/libsolidity/ASTJSON/receive_ether.json | 1 + test/libsolidity/ASTJSON/receive_ether_legacy.json | 1 + test/libsolidity/ASTJSON/short_type_name.json | 1 + test/libsolidity/ASTJSON/short_type_name_legacy.json | 1 + test/libsolidity/ASTJSON/short_type_name_ref.json | 1 + test/libsolidity/ASTJSON/short_type_name_ref_legacy.json | 1 + test/libsolidity/ASTJSON/source_location.json | 1 + test/libsolidity/ASTJSON/source_location_legacy.json | 1 + test/libsolidity/ASTJSON/string.json | 1 + test/libsolidity/ASTJSON/string_legacy.json | 1 + test/libsolidity/ASTJSON/two_base_functions.json | 3 +++ test/libsolidity/ASTJSON/two_base_functions_legacy.json | 3 +++ test/libsolidity/ASTJSON/unicode.json | 1 + test/libsolidity/ASTJSON/unicode_legacy.json | 1 + test/libsolidity/syntaxTests/unexpected.sol | 2 +- 70 files changed, 84 insertions(+), 7 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 621fb3936..cac28eac0 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -292,7 +292,7 @@ bool FunctionDefinition::libraryFunction() const Visibility FunctionDefinition::defaultVisibility() const { solAssert(!isConstructor(), ""); - return Declaration::defaultVisibility(); + return isFree() ? Visibility::Internal : Declaration::defaultVisibility(); } FunctionTypePointer FunctionDefinition::functionType(bool _internal) const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 8b4025a45..cfa438ce5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -852,9 +852,6 @@ public: ContractDefinition const* _searchStart = nullptr ) const override; -protected: - Visibility defaultVisibility() const override { return isFree() ? Visibility::Internal : Visibility::Public; } - private: StateMutability m_stateMutability; bool m_free; diff --git a/test/cmdlineTests/recovery_ast_constructor/output b/test/cmdlineTests/recovery_ast_constructor/output index d4a7d0288..319fb3c88 100644 --- a/test/cmdlineTests/recovery_ast_constructor/output +++ b/test/cmdlineTests/recovery_ast_constructor/output @@ -58,6 +58,7 @@ JSON AST: { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": true, "kind": "constructor", @@ -120,6 +121,7 @@ JSON AST: { "attributes": { + "freeFunction": false, "functionSelector": "af11c34c", "implemented": true, "isConstructor": false, diff --git a/test/cmdlineTests/recovery_ast_empty_contract/err b/test/cmdlineTests/recovery_ast_empty_contract/err index 588067877..ab976c155 100644 --- a/test/cmdlineTests/recovery_ast_empty_contract/err +++ b/test/cmdlineTests/recovery_ast_empty_contract/err @@ -1,4 +1,4 @@ -Error: Expected pragma, import directive or contract/interface/library/struct/enum definition. +Error: Expected pragma, import directive or contract/interface/library/struct/enum/function definition. --> recovery_ast_empty_contract/input.sol:3:1: | 3 | c diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json index c1ffe5892..c164153ca 100644 --- a/test/cmdlineTests/standard_only_ast_requested/output.json +++ b/test/cmdlineTests/standard_only_ast_requested/output.json @@ -1 +1 @@ -{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"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":"97:2:0","statements":[]},"documentation":null,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} +{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"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":"97:2:0","statements":[]},"documentation":null,"freeFunction":false,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} diff --git a/test/libsolidity/ASTJSON/abstract_contract.json b/test/libsolidity/ASTJSON/abstract_contract.json index 08b1e6d95..41cd1330f 100644 --- a/test/libsolidity/ASTJSON/abstract_contract.json +++ b/test/libsolidity/ASTJSON/abstract_contract.json @@ -34,6 +34,7 @@ "src": "37:4:1", "statements": [] }, + "freeFunction": false, "id": 4, "implemented": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/abstract_contract_legacy.json b/test/libsolidity/ASTJSON/abstract_contract_legacy.json index c13abca7f..f8da2425b 100644 --- a/test/libsolidity/ASTJSON/abstract_contract_legacy.json +++ b/test/libsolidity/ASTJSON/abstract_contract_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 3365ec436..8f7ca12be 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -462,6 +462,7 @@ } ] }, + "freeFunction": false, "functionSelector": "fc68521a", "id": 38, "implemented": true, diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json index bda50fc57..15567b5e5 100644 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -91,6 +91,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "fc68521a", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/call.json b/test/libsolidity/ASTJSON/assembly/call.json index 9538fe5cc..5fe3e3f20 100644 --- a/test/libsolidity/ASTJSON/assembly/call.json +++ b/test/libsolidity/ASTJSON/assembly/call.json @@ -131,6 +131,7 @@ } ] }, + "freeFunction": false, "functionSelector": "b582ec5f", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/call_legacy.json b/test/libsolidity/ASTJSON/assembly/call_legacy.json index 2902873fa..fa9a9830b 100644 --- a/test/libsolidity/ASTJSON/assembly/call_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/call_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "b582ec5f", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/empty_block.json b/test/libsolidity/ASTJSON/assembly/empty_block.json index 2d22cc035..18180e76b 100644 --- a/test/libsolidity/ASTJSON/assembly/empty_block.json +++ b/test/libsolidity/ASTJSON/assembly/empty_block.json @@ -56,6 +56,7 @@ } ] }, + "freeFunction": false, "functionSelector": "e2179b8e", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json b/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json index 54811bf1a..8ab74c144 100644 --- a/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "e2179b8e", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/function.json b/test/libsolidity/ASTJSON/assembly/function.json index ccbf3d853..0fc33c271 100644 --- a/test/libsolidity/ASTJSON/assembly/function.json +++ b/test/libsolidity/ASTJSON/assembly/function.json @@ -118,6 +118,7 @@ } ] }, + "freeFunction": false, "functionSelector": "b8c9d365", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/function_legacy.json b/test/libsolidity/ASTJSON/assembly/function_legacy.json index 940a4b6ac..c7a928b54 100644 --- a/test/libsolidity/ASTJSON/assembly/function_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/function_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "b8c9d365", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/leave.json b/test/libsolidity/ASTJSON/assembly/leave.json index ba765146f..7edbfe11f 100644 --- a/test/libsolidity/ASTJSON/assembly/leave.json +++ b/test/libsolidity/ASTJSON/assembly/leave.json @@ -68,6 +68,7 @@ } ] }, + "freeFunction": false, "functionSelector": "ece866b9", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/leave_legacy.json b/test/libsolidity/ASTJSON/assembly/leave_legacy.json index fa71bcba0..02fba96c7 100644 --- a/test/libsolidity/ASTJSON/assembly/leave_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/leave_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "ece866b9", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/loop.json b/test/libsolidity/ASTJSON/assembly/loop.json index 9c99cf491..f565915e8 100644 --- a/test/libsolidity/ASTJSON/assembly/loop.json +++ b/test/libsolidity/ASTJSON/assembly/loop.json @@ -131,6 +131,7 @@ } ] }, + "freeFunction": false, "functionSelector": "e2179b8e", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/loop_legacy.json b/test/libsolidity/ASTJSON/assembly/loop_legacy.json index 84890475f..4f1051e03 100644 --- a/test/libsolidity/ASTJSON/assembly/loop_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/loop_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "e2179b8e", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.json b/test/libsolidity/ASTJSON/assembly/nested_functions.json index f945494ba..8f9acb56f 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.json @@ -95,6 +95,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 7, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json index 393646221..2dd973e84 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset.json b/test/libsolidity/ASTJSON/assembly/slot_offset.json index 0858fe617..5a9b1489f 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset.json @@ -196,6 +196,7 @@ } ] }, + "freeFunction": false, "functionSelector": "ffae15ba", "id": 10, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json index 50747baaa..d84497d1a 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json @@ -112,6 +112,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "ffae15ba", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/stringlit.json b/test/libsolidity/ASTJSON/assembly/stringlit.json index 08e0ea4f6..872e953ee 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit.json @@ -72,6 +72,7 @@ } ] }, + "freeFunction": false, "functionSelector": "5a2ee019", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json b/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json index 325d25274..87c79d107 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "5a2ee019", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/switch.json b/test/libsolidity/ASTJSON/assembly/switch.json index 9ae71f15c..7380897a0 100644 --- a/test/libsolidity/ASTJSON/assembly/switch.json +++ b/test/libsolidity/ASTJSON/assembly/switch.json @@ -180,6 +180,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.json b/test/libsolidity/ASTJSON/assembly/switch_default.json index 4226a8ea3..2eb2cd9dc 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default.json @@ -95,6 +95,7 @@ } ] }, + "freeFunction": false, "functionSelector": "e2179b8e", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json index 324330f86..5f0d10dfc 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "e2179b8e", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/switch_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_legacy.json index 92e2e2c61..9f907e633 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/var_access.json b/test/libsolidity/ASTJSON/assembly/var_access.json index f10c45cc4..bc0a9a3c7 100644 --- a/test/libsolidity/ASTJSON/assembly/var_access.json +++ b/test/libsolidity/ASTJSON/assembly/var_access.json @@ -121,6 +121,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/var_access_legacy.json b/test/libsolidity/ASTJSON/assembly/var_access_legacy.json index fcd798ad9..2052a8aed 100644 --- a/test/libsolidity/ASTJSON/assembly/var_access_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/var_access_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/constructor.json b/test/libsolidity/ASTJSON/constructor.json index 31747901e..c4b7e9f87 100644 --- a/test/libsolidity/ASTJSON/constructor.json +++ b/test/libsolidity/ASTJSON/constructor.json @@ -34,6 +34,7 @@ "src": "28:4:1", "statements": [] }, + "freeFunction": false, "id": 4, "implemented": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/constructor_legacy.json b/test/libsolidity/ASTJSON/constructor_legacy.json index 68a648bc9..ade4e5629 100644 --- a/test/libsolidity/ASTJSON/constructor_legacy.json +++ b/test/libsolidity/ASTJSON/constructor_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json index f0beba98f..8cf35a71e 100644 --- a/test/libsolidity/ASTJSON/documentation.json +++ b/test/libsolidity/ASTJSON/documentation.json @@ -216,6 +216,7 @@ "src": "162:25:3", "text": "Some comment on fn." }, + "freeFunction": false, "functionSelector": "a4a2c40b", "id": 22, "implemented": true, diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index 4284c4f8f..014b70fe9 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -161,6 +161,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "a4a2c40b", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/fallback.json b/test/libsolidity/ASTJSON/fallback.json index 7037b960a..cf706e4fe 100644 --- a/test/libsolidity/ASTJSON/fallback.json +++ b/test/libsolidity/ASTJSON/fallback.json @@ -34,6 +34,7 @@ "src": "43:5:1", "statements": [] }, + "freeFunction": false, "id": 4, "implemented": true, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json index 009363e98..e8219b6db 100644 --- a/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json +++ b/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json @@ -34,6 +34,7 @@ "src": "42:5:1", "statements": [] }, + "freeFunction": false, "id": 4, "implemented": true, "kind": "receive", @@ -68,6 +69,7 @@ "src": "78:5:1", "statements": [] }, + "freeFunction": false, "id": 8, "implemented": true, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json index 8ac17607e..1fa662d64 100644 --- a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "receive", @@ -100,6 +101,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_legacy.json b/test/libsolidity/ASTJSON/fallback_legacy.json index 1ee8bf6b3..9f31a225e 100644 --- a/test/libsolidity/ASTJSON/fallback_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_payable.json b/test/libsolidity/ASTJSON/fallback_payable.json index 2391f57bb..883ba1bfd 100644 --- a/test/libsolidity/ASTJSON/fallback_payable.json +++ b/test/libsolidity/ASTJSON/fallback_payable.json @@ -34,6 +34,7 @@ "src": "34:2:1", "statements": [] }, + "freeFunction": false, "id": 4, "implemented": true, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_payable_legacy.json b/test/libsolidity/ASTJSON/fallback_payable_legacy.json index 69e25ab6e..2e4a69077 100644 --- a/test/libsolidity/ASTJSON/fallback_payable_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_payable_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/function_type.json b/test/libsolidity/ASTJSON/function_type.json index c0d2efc68..fb706e5e8 100644 --- a/test/libsolidity/ASTJSON/function_type.json +++ b/test/libsolidity/ASTJSON/function_type.json @@ -34,6 +34,7 @@ "src": "120:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "d6cd4974", "id": 16, "implemented": true, diff --git a/test/libsolidity/ASTJSON/function_type_legacy.json b/test/libsolidity/ASTJSON/function_type_legacy.json index 627ce45f0..cc02a7dd9 100644 --- a/test/libsolidity/ASTJSON/function_type_legacy.json +++ b/test/libsolidity/ASTJSON/function_type_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "d6cd4974", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json index 7b598093b..dd9210e5f 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json @@ -134,6 +134,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 10, "implemented": true, diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json index 46e5b0417..319b1e022 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.json b/test/libsolidity/ASTJSON/long_type_name_identifier.json index 4293f6df4..6df967ea9 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.json @@ -142,6 +142,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 14, "implemented": true, diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json index ced331a6d..f6ee2b948 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json @@ -79,6 +79,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json index 198afe454..d1355a51f 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.json +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -94,6 +94,7 @@ "src": "64:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "28811f59", "id": 13, "implemented": true, diff --git a/test/libsolidity/ASTJSON/modifier_definition_legacy.json b/test/libsolidity/ASTJSON/modifier_definition_legacy.json index cc3089395..a48e02104 100644 --- a/test/libsolidity/ASTJSON/modifier_definition_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_definition_legacy.json @@ -102,6 +102,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "28811f59", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json index 198afe454..d1355a51f 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.json +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -94,6 +94,7 @@ "src": "64:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "28811f59", "id": 13, "implemented": true, diff --git a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json index cc3089395..a48e02104 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json @@ -102,6 +102,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "28811f59", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json index 98db4273f..22d95bf28 100644 --- a/test/libsolidity/ASTJSON/non_utf8.json +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -94,6 +94,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/non_utf8_legacy.json b/test/libsolidity/ASTJSON/non_utf8_legacy.json index d9430c82d..c8d5c312f 100644 --- a/test/libsolidity/ASTJSON/non_utf8_legacy.json +++ b/test/libsolidity/ASTJSON/non_utf8_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/override.json b/test/libsolidity/ASTJSON/override.json index 49f85144e..e38305bf1 100644 --- a/test/libsolidity/ASTJSON/override.json +++ b/test/libsolidity/ASTJSON/override.json @@ -42,6 +42,7 @@ "src": "36:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "a399b6a2", "id": 4, "implemented": true, @@ -113,6 +114,7 @@ "nodes": [ { + "freeFunction": false, "functionSelector": "c2985578", "id": 10, "implemented": false, @@ -152,6 +154,7 @@ "src": "115:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "a399b6a2", "id": 15, "implemented": true, @@ -243,6 +246,7 @@ "src": "170:3:1", "statements": [] }, + "freeFunction": false, "functionSelector": "c2985578", "id": 23, "implemented": true, @@ -289,6 +293,7 @@ "src": "212:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "a399b6a2", "id": 30, "implemented": true, diff --git a/test/libsolidity/ASTJSON/override_legacy.json b/test/libsolidity/ASTJSON/override_legacy.json index 5cdc2f364..bbd03937f 100644 --- a/test/libsolidity/ASTJSON/override_legacy.json +++ b/test/libsolidity/ASTJSON/override_legacy.json @@ -46,6 +46,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "a399b6a2", "implemented": true, "isConstructor": false, @@ -154,6 +155,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "c2985578", "implemented": false, "isConstructor": false, @@ -208,6 +210,7 @@ [ 4 ], + "freeFunction": false, "functionSelector": "a399b6a2", "implemented": true, "isConstructor": false, @@ -334,6 +337,7 @@ [ 10 ], + "freeFunction": false, "functionSelector": "c2985578", "implemented": true, "isConstructor": false, @@ -413,6 +417,7 @@ [ 15 ], + "freeFunction": false, "functionSelector": "a399b6a2", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/receive_ether.json b/test/libsolidity/ASTJSON/receive_ether.json index 8a1f0ac9a..8b678a593 100644 --- a/test/libsolidity/ASTJSON/receive_ether.json +++ b/test/libsolidity/ASTJSON/receive_ether.json @@ -34,6 +34,7 @@ "src": "42:5:1", "statements": [] }, + "freeFunction": false, "id": 4, "implemented": true, "kind": "receive", diff --git a/test/libsolidity/ASTJSON/receive_ether_legacy.json b/test/libsolidity/ASTJSON/receive_ether_legacy.json index d9544f138..1155129d4 100644 --- a/test/libsolidity/ASTJSON/receive_ether_legacy.json +++ b/test/libsolidity/ASTJSON/receive_ether_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "receive", diff --git a/test/libsolidity/ASTJSON/short_type_name.json b/test/libsolidity/ASTJSON/short_type_name.json index 882fcfe24..f569f59af 100644 --- a/test/libsolidity/ASTJSON/short_type_name.json +++ b/test/libsolidity/ASTJSON/short_type_name.json @@ -88,6 +88,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 10, "implemented": true, diff --git a/test/libsolidity/ASTJSON/short_type_name_legacy.json b/test/libsolidity/ASTJSON/short_type_name_legacy.json index 8e255954d..b7912f585 100644 --- a/test/libsolidity/ASTJSON/short_type_name_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index 2d07a9c60..e8845981f 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -99,6 +99,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 11, "implemented": true, diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json index 5316c56ac..f6cd04ed3 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/source_location.json b/test/libsolidity/ASTJSON/source_location.json index f56231ed7..fa95c0e4a 100644 --- a/test/libsolidity/ASTJSON/source_location.json +++ b/test/libsolidity/ASTJSON/source_location.json @@ -131,6 +131,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 11, "implemented": true, diff --git a/test/libsolidity/ASTJSON/source_location_legacy.json b/test/libsolidity/ASTJSON/source_location_legacy.json index bf3dfaedd..529ca4444 100644 --- a/test/libsolidity/ASTJSON/source_location_legacy.json +++ b/test/libsolidity/ASTJSON/source_location_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/string.json b/test/libsolidity/ASTJSON/string.json index f47b41e47..41fe9fa00 100644 --- a/test/libsolidity/ASTJSON/string.json +++ b/test/libsolidity/ASTJSON/string.json @@ -95,6 +95,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/string_legacy.json b/test/libsolidity/ASTJSON/string_legacy.json index 96c984209..89c9a5e55 100644 --- a/test/libsolidity/ASTJSON/string_legacy.json +++ b/test/libsolidity/ASTJSON/string_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/two_base_functions.json b/test/libsolidity/ASTJSON/two_base_functions.json index 769e5c3f8..7e6526316 100644 --- a/test/libsolidity/ASTJSON/two_base_functions.json +++ b/test/libsolidity/ASTJSON/two_base_functions.json @@ -42,6 +42,7 @@ "src": "45:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 4, "implemented": true, @@ -96,6 +97,7 @@ "src": "95:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 9, "implemented": true, @@ -199,6 +201,7 @@ "src": "160:2:1", "statements": [] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 21, "implemented": true, diff --git a/test/libsolidity/ASTJSON/two_base_functions_legacy.json b/test/libsolidity/ASTJSON/two_base_functions_legacy.json index a5bd2d725..405576ab6 100644 --- a/test/libsolidity/ASTJSON/two_base_functions_legacy.json +++ b/test/libsolidity/ASTJSON/two_base_functions_legacy.json @@ -46,6 +46,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, @@ -137,6 +138,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, @@ -272,6 +274,7 @@ 4, 9 ], + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/unicode.json b/test/libsolidity/ASTJSON/unicode.json index 81c8ad0da..99f9eb619 100644 --- a/test/libsolidity/ASTJSON/unicode.json +++ b/test/libsolidity/ASTJSON/unicode.json @@ -95,6 +95,7 @@ } ] }, + "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/unicode_legacy.json b/test/libsolidity/ASTJSON/unicode_legacy.json index 22976f6f9..826c3ce44 100644 --- a/test/libsolidity/ASTJSON/unicode_legacy.json +++ b/test/libsolidity/ASTJSON/unicode_legacy.json @@ -38,6 +38,7 @@ { "attributes": { + "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/syntaxTests/unexpected.sol b/test/libsolidity/syntaxTests/unexpected.sol index 79ee1b6cb..971c78000 100644 --- a/test/libsolidity/syntaxTests/unexpected.sol +++ b/test/libsolidity/syntaxTests/unexpected.sol @@ -1,3 +1,3 @@ unexpected // ---- -// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum definition. +// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. From cf5aa450bdbf2188a83e80221eb4a9f67b630bda Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jun 2020 18:20:37 +0200 Subject: [PATCH 059/139] Documentation. --- Changelog.md | 1 + docs/Solidity.g4 | 2 +- docs/contracts/function-modifiers.rst | 2 +- docs/contracts/functions.rst | 28 +++++++++++++++++++++++++++ docs/layout-of-source-files.rst | 2 +- docs/structure-of-a-contract.rst | 11 +++++++++-- 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9235faf6e..4b47af706 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.7.1 (unreleased) Language Features: + * Allow function definitions outside of contracts, behaving much like internal library functions. Compiler Features: diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 index 91f59d78c..dee054492 100644 --- a/docs/Solidity.g4 +++ b/docs/Solidity.g4 @@ -8,7 +8,7 @@ grammar Solidity; sourceUnit - : (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ; + : (pragmaDirective | importDirective | structDefinition | enumDefinition | functionDefinition | contractDefinition)* EOF ; pragmaDirective : 'pragma' pragmaName ( ~';' )* ';' ; diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index 823e6e6ac..881b8f787 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >0.7.0 <0.8.0; contract owned { constructor() { owner = msg.sender; } diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 41174c958..e7c97c33d 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -6,6 +6,34 @@ Functions ********* +Functions can be defined inside and outside of contracts. + +Functions outside of a contract, also called "free functions", always have implicit ``internal`` +:ref:`visibility`. Their code is included in all contracts +that call them, similar to internal library functions. + +:: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0 <0.8.0; + + function sum(uint[] memory _arr) pure returns (uint s) { + for (uint i = 0; i < _arr.length; i++) + s += _arr[i]; + } + + contract ArrayExample { + bool found; + function f(uint[] memory _arr) public { + // This calls the free function internally. + // The compiler will add its code to the contract. + uint s = sum(_arr); + require(s >= 10); + found = true; + } + } + + .. _function-parameters-return-variables: Function Parameters and Return Variables diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index d50ea06a3..d80af49f7 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -5,7 +5,7 @@ Layout of a Solidity Source File Source files can contain an arbitrary number of :ref:`contract definitions`, import_ directives, :ref:`pragma directives` and -:ref:`struct` and :ref:`enum` definitions. +:ref:`struct`, :ref:`enum` and :ref:`function` definitions. .. index:: ! license, spdx diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 016130730..80cd5f617 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -43,12 +43,14 @@ visibility. Functions ========= -Functions are the executable units of code within a contract. +Functions are the executable units of code. Functions are usually +defined inside a contract, but they can also be defined outside of +contracts. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >0.7.0 <0.8.0; contract SimpleAuction { function bid() public payable { // Function @@ -56,6 +58,11 @@ Functions are the executable units of code within a contract. } } + // Helper function defined outside of a contract + function helper(uint x) pure returns (uint) { + return x * 2; + } + :ref:`function-calls` can happen internally or externally and have different levels of :ref:`visibility` towards other contracts. :ref:`Functions` accept :ref:`parameters and return variables` to pass parameters From 8d92fd1c041f850d6a8f5a8c00570ea73e9a81f1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Aug 2020 10:38:31 +0200 Subject: [PATCH 060/139] Use opportunistic visiting order for modifiers. --- libsolidity/analysis/ViewPureChecker.cpp | 42 ++++++++++++------------ libsolidity/analysis/ViewPureChecker.h | 4 ++- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 1bf6028e5..b75b4a240 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -128,12 +128,6 @@ private: bool ViewPureChecker::check() { - // Process modifiers first to infer their state mutability. - m_checkModifiers = true; - for (auto const& source: m_ast) - source->accept(*this); - - m_checkModifiers = false; for (auto const& source: m_ast) source->accept(*this); @@ -142,9 +136,6 @@ bool ViewPureChecker::check() bool ViewPureChecker::visit(FunctionDefinition const& _funDef) { - if (m_checkModifiers) - return false; - solAssert(!m_currentFunction, ""); m_currentFunction = &_funDef; m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()}; @@ -153,9 +144,6 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef) void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) { - if (m_checkModifiers) - return; - solAssert(m_currentFunction == &_funDef, ""); if ( m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() && @@ -177,9 +165,6 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) bool ViewPureChecker::visit(ModifierDefinition const& _modifier) { - if (!m_checkModifiers) - return false; - solAssert(m_currentFunction == nullptr, ""); m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()}; return true; @@ -187,9 +172,6 @@ bool ViewPureChecker::visit(ModifierDefinition const& _modifier) void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) { - if (!m_checkModifiers) - return; - solAssert(m_currentFunction == nullptr, ""); m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation); } @@ -306,7 +288,26 @@ void ViewPureChecker::reportMutability( m_currentFunction->stateMutability() == StateMutability::Pure || m_currentFunction->stateMutability() == StateMutability::NonPayable, "" - ); + ); +} + +ViewPureChecker::MutabilityAndLocation const& ViewPureChecker::modifierMutability( + ModifierDefinition const& _modifier +) +{ + if (!m_inferredMutability.count(&_modifier)) + { + MutabilityAndLocation bestMutabilityAndLocation{}; + FunctionDefinition const* currentFunction = nullptr; + swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation); + swap(currentFunction, m_currentFunction); + + _modifier.accept(*this); + + swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation); + swap(currentFunction, m_currentFunction); + } + return m_inferredMutability.at(&_modifier); } void ViewPureChecker::endVisit(FunctionCall const& _functionCall) @@ -431,8 +432,7 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) solAssert(_modifier.name(), ""); if (ModifierDefinition const* mod = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) { - solAssert(m_inferredMutability.count(mod), ""); - auto const& mutAndLocation = m_inferredMutability.at(mod); + MutabilityAndLocation const& mutAndLocation = modifierMutability(*mod); reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location); } else diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 62444e086..dfbb2f747 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -71,11 +71,13 @@ private: std::optional const& _nestedLocation = {} ); + /// Determines the mutability of modifier if not already cached. + MutabilityAndLocation const& modifierMutability(ModifierDefinition const& _modifier); + std::vector> const& m_ast; langutil::ErrorReporter& m_errorReporter; bool m_errors = false; - bool m_checkModifiers = false; MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()}; FunctionDefinition const* m_currentFunction = nullptr; std::map m_inferredMutability; From 2ff954ec2dd00adfe511e5e9951ce230335355d4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 6 Aug 2020 14:10:42 +0200 Subject: [PATCH 061/139] Use kind in json AST for free function. --- libsolidity/ast/ASTJsonConverter.cpp | 3 +-- libsolidity/ast/ASTJsonImporter.cpp | 17 +++++++---------- .../recovery_ast_constructor/output | 2 -- .../standard_only_ast_requested/output.json | 2 +- test/libsolidity/ASTJSON/abstract_contract.json | 1 - .../ASTJSON/abstract_contract_legacy.json | 1 - test/libsolidity/ASTJSON/address_payable.json | 1 - .../ASTJSON/address_payable_legacy.json | 1 - test/libsolidity/ASTJSON/assembly/call.json | 1 - .../ASTJSON/assembly/call_legacy.json | 1 - .../ASTJSON/assembly/empty_block.json | 1 - .../ASTJSON/assembly/empty_block_legacy.json | 1 - test/libsolidity/ASTJSON/assembly/function.json | 1 - .../ASTJSON/assembly/function_legacy.json | 1 - test/libsolidity/ASTJSON/assembly/leave.json | 1 - .../ASTJSON/assembly/leave_legacy.json | 1 - test/libsolidity/ASTJSON/assembly/loop.json | 1 - .../ASTJSON/assembly/loop_legacy.json | 1 - .../ASTJSON/assembly/nested_functions.json | 1 - .../assembly/nested_functions_legacy.json | 1 - .../ASTJSON/assembly/slot_offset.json | 1 - .../ASTJSON/assembly/slot_offset_legacy.json | 1 - .../libsolidity/ASTJSON/assembly/stringlit.json | 1 - .../ASTJSON/assembly/stringlit_legacy.json | 1 - test/libsolidity/ASTJSON/assembly/switch.json | 1 - .../ASTJSON/assembly/switch_default.json | 1 - .../ASTJSON/assembly/switch_default_legacy.json | 1 - .../ASTJSON/assembly/switch_legacy.json | 1 - .../ASTJSON/assembly/var_access.json | 1 - .../ASTJSON/assembly/var_access_legacy.json | 1 - test/libsolidity/ASTJSON/constructor.json | 1 - .../libsolidity/ASTJSON/constructor_legacy.json | 1 - test/libsolidity/ASTJSON/documentation.json | 1 - .../ASTJSON/documentation_legacy.json | 1 - test/libsolidity/ASTJSON/fallback.json | 1 - .../ASTJSON/fallback_and_reveice_ether.json | 2 -- .../fallback_and_reveice_ether_legacy.json | 2 -- test/libsolidity/ASTJSON/fallback_legacy.json | 1 - test/libsolidity/ASTJSON/fallback_payable.json | 1 - .../ASTJSON/fallback_payable_legacy.json | 1 - test/libsolidity/ASTJSON/function_type.json | 1 - .../ASTJSON/function_type_legacy.json | 1 - .../long_type_name_binary_operation.json | 1 - .../long_type_name_binary_operation_legacy.json | 1 - .../ASTJSON/long_type_name_identifier.json | 1 - .../long_type_name_identifier_legacy.json | 1 - .../ASTJSON/modifier_definition.json | 1 - .../ASTJSON/modifier_definition_legacy.json | 1 - .../ASTJSON/modifier_invocation.json | 1 - .../ASTJSON/modifier_invocation_legacy.json | 1 - test/libsolidity/ASTJSON/non_utf8.json | 1 - test/libsolidity/ASTJSON/non_utf8_legacy.json | 1 - test/libsolidity/ASTJSON/override.json | 5 ----- test/libsolidity/ASTJSON/override_legacy.json | 5 ----- test/libsolidity/ASTJSON/receive_ether.json | 1 - .../ASTJSON/receive_ether_legacy.json | 1 - test/libsolidity/ASTJSON/short_type_name.json | 1 - .../ASTJSON/short_type_name_legacy.json | 1 - .../ASTJSON/short_type_name_ref.json | 1 - .../ASTJSON/short_type_name_ref_legacy.json | 1 - test/libsolidity/ASTJSON/source_location.json | 1 - .../ASTJSON/source_location_legacy.json | 1 - test/libsolidity/ASTJSON/string.json | 1 - test/libsolidity/ASTJSON/string_legacy.json | 1 - .../libsolidity/ASTJSON/two_base_functions.json | 3 --- .../ASTJSON/two_base_functions_legacy.json | 3 --- test/libsolidity/ASTJSON/unicode.json | 1 - test/libsolidity/ASTJSON/unicode_legacy.json | 1 - 68 files changed, 9 insertions(+), 93 deletions(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 15ac0c620..04283cc34 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -360,8 +360,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) std::vector> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("kind", TokenTraits::toString(_node.kind())), - make_pair("freeFunction", _node.isFree()), + make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(visibility)), make_pair("virtual", _node.markedVirtual()), diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 8bc657abf..5e44b8616 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -381,6 +381,7 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!"); Token kind; + bool freeFunction = false; string kindStr = member(_node, "kind").asString(); if (kindStr == "constructor") @@ -391,6 +392,11 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V kind = Token::Fallback; else if (kindStr == "receive") kind = Token::Receive; + else if (kindStr == "freeFunction") + { + kind = Token::Function; + freeFunction = true; + } else astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]"); @@ -399,16 +405,7 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V modifiers.push_back(createModifierInvocation(mod)); Visibility vis = Visibility::Default; - bool const freeFunction = _node.isMember("freeFunction") ? _node["freeFunction"].asBool() : false; - if (kind == Token::Constructor) - { - } - else if (freeFunction) - astAssert( - vis == Visibility::Internal || vis == Visibility::Default, - "Expected internal or default visibility for free function." - ); - else + if (!freeFunction) vis = visibility(_node); return createASTNode( _node, diff --git a/test/cmdlineTests/recovery_ast_constructor/output b/test/cmdlineTests/recovery_ast_constructor/output index 319fb3c88..d4a7d0288 100644 --- a/test/cmdlineTests/recovery_ast_constructor/output +++ b/test/cmdlineTests/recovery_ast_constructor/output @@ -58,7 +58,6 @@ JSON AST: { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": true, "kind": "constructor", @@ -121,7 +120,6 @@ JSON AST: { "attributes": { - "freeFunction": false, "functionSelector": "af11c34c", "implemented": true, "isConstructor": false, diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json index c164153ca..c1ffe5892 100644 --- a/test/cmdlineTests/standard_only_ast_requested/output.json +++ b/test/cmdlineTests/standard_only_ast_requested/output.json @@ -1 +1 @@ -{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"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":"97:2:0","statements":[]},"documentation":null,"freeFunction":false,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} +{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"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":"97:2:0","statements":[]},"documentation":null,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} diff --git a/test/libsolidity/ASTJSON/abstract_contract.json b/test/libsolidity/ASTJSON/abstract_contract.json index 41cd1330f..08b1e6d95 100644 --- a/test/libsolidity/ASTJSON/abstract_contract.json +++ b/test/libsolidity/ASTJSON/abstract_contract.json @@ -34,7 +34,6 @@ "src": "37:4:1", "statements": [] }, - "freeFunction": false, "id": 4, "implemented": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/abstract_contract_legacy.json b/test/libsolidity/ASTJSON/abstract_contract_legacy.json index f8da2425b..c13abca7f 100644 --- a/test/libsolidity/ASTJSON/abstract_contract_legacy.json +++ b/test/libsolidity/ASTJSON/abstract_contract_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 8f7ca12be..3365ec436 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -462,7 +462,6 @@ } ] }, - "freeFunction": false, "functionSelector": "fc68521a", "id": 38, "implemented": true, diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json index 15567b5e5..bda50fc57 100644 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -91,7 +91,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "fc68521a", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/call.json b/test/libsolidity/ASTJSON/assembly/call.json index 5fe3e3f20..9538fe5cc 100644 --- a/test/libsolidity/ASTJSON/assembly/call.json +++ b/test/libsolidity/ASTJSON/assembly/call.json @@ -131,7 +131,6 @@ } ] }, - "freeFunction": false, "functionSelector": "b582ec5f", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/call_legacy.json b/test/libsolidity/ASTJSON/assembly/call_legacy.json index fa9a9830b..2902873fa 100644 --- a/test/libsolidity/ASTJSON/assembly/call_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/call_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "b582ec5f", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/empty_block.json b/test/libsolidity/ASTJSON/assembly/empty_block.json index 18180e76b..2d22cc035 100644 --- a/test/libsolidity/ASTJSON/assembly/empty_block.json +++ b/test/libsolidity/ASTJSON/assembly/empty_block.json @@ -56,7 +56,6 @@ } ] }, - "freeFunction": false, "functionSelector": "e2179b8e", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json b/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json index 8ab74c144..54811bf1a 100644 --- a/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/empty_block_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "e2179b8e", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/function.json b/test/libsolidity/ASTJSON/assembly/function.json index 0fc33c271..ccbf3d853 100644 --- a/test/libsolidity/ASTJSON/assembly/function.json +++ b/test/libsolidity/ASTJSON/assembly/function.json @@ -118,7 +118,6 @@ } ] }, - "freeFunction": false, "functionSelector": "b8c9d365", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/function_legacy.json b/test/libsolidity/ASTJSON/assembly/function_legacy.json index c7a928b54..940a4b6ac 100644 --- a/test/libsolidity/ASTJSON/assembly/function_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/function_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "b8c9d365", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/leave.json b/test/libsolidity/ASTJSON/assembly/leave.json index 7edbfe11f..ba765146f 100644 --- a/test/libsolidity/ASTJSON/assembly/leave.json +++ b/test/libsolidity/ASTJSON/assembly/leave.json @@ -68,7 +68,6 @@ } ] }, - "freeFunction": false, "functionSelector": "ece866b9", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/leave_legacy.json b/test/libsolidity/ASTJSON/assembly/leave_legacy.json index 02fba96c7..fa71bcba0 100644 --- a/test/libsolidity/ASTJSON/assembly/leave_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/leave_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "ece866b9", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/loop.json b/test/libsolidity/ASTJSON/assembly/loop.json index f565915e8..9c99cf491 100644 --- a/test/libsolidity/ASTJSON/assembly/loop.json +++ b/test/libsolidity/ASTJSON/assembly/loop.json @@ -131,7 +131,6 @@ } ] }, - "freeFunction": false, "functionSelector": "e2179b8e", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/loop_legacy.json b/test/libsolidity/ASTJSON/assembly/loop_legacy.json index 4f1051e03..84890475f 100644 --- a/test/libsolidity/ASTJSON/assembly/loop_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/loop_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "e2179b8e", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.json b/test/libsolidity/ASTJSON/assembly/nested_functions.json index 8f9acb56f..f945494ba 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.json @@ -95,7 +95,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 7, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json index 2dd973e84..393646221 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset.json b/test/libsolidity/ASTJSON/assembly/slot_offset.json index 5a9b1489f..0858fe617 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset.json @@ -196,7 +196,6 @@ } ] }, - "freeFunction": false, "functionSelector": "ffae15ba", "id": 10, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json index d84497d1a..50747baaa 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json @@ -112,7 +112,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "ffae15ba", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/stringlit.json b/test/libsolidity/ASTJSON/assembly/stringlit.json index 872e953ee..08e0ea4f6 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit.json @@ -72,7 +72,6 @@ } ] }, - "freeFunction": false, "functionSelector": "5a2ee019", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json b/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json index 87c79d107..325d25274 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "5a2ee019", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/switch.json b/test/libsolidity/ASTJSON/assembly/switch.json index 7380897a0..9ae71f15c 100644 --- a/test/libsolidity/ASTJSON/assembly/switch.json +++ b/test/libsolidity/ASTJSON/assembly/switch.json @@ -180,7 +180,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.json b/test/libsolidity/ASTJSON/assembly/switch_default.json index 2eb2cd9dc..4226a8ea3 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default.json @@ -95,7 +95,6 @@ } ] }, - "freeFunction": false, "functionSelector": "e2179b8e", "id": 5, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json index 5f0d10dfc..324330f86 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "e2179b8e", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/switch_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_legacy.json index 9f907e633..92e2e2c61 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/assembly/var_access.json b/test/libsolidity/ASTJSON/assembly/var_access.json index bc0a9a3c7..f10c45cc4 100644 --- a/test/libsolidity/ASTJSON/assembly/var_access.json +++ b/test/libsolidity/ASTJSON/assembly/var_access.json @@ -121,7 +121,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/var_access_legacy.json b/test/libsolidity/ASTJSON/assembly/var_access_legacy.json index 2052a8aed..fcd798ad9 100644 --- a/test/libsolidity/ASTJSON/assembly/var_access_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/var_access_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/constructor.json b/test/libsolidity/ASTJSON/constructor.json index c4b7e9f87..31747901e 100644 --- a/test/libsolidity/ASTJSON/constructor.json +++ b/test/libsolidity/ASTJSON/constructor.json @@ -34,7 +34,6 @@ "src": "28:4:1", "statements": [] }, - "freeFunction": false, "id": 4, "implemented": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/constructor_legacy.json b/test/libsolidity/ASTJSON/constructor_legacy.json index ade4e5629..68a648bc9 100644 --- a/test/libsolidity/ASTJSON/constructor_legacy.json +++ b/test/libsolidity/ASTJSON/constructor_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": true, "kind": "constructor", diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json index 8cf35a71e..f0beba98f 100644 --- a/test/libsolidity/ASTJSON/documentation.json +++ b/test/libsolidity/ASTJSON/documentation.json @@ -216,7 +216,6 @@ "src": "162:25:3", "text": "Some comment on fn." }, - "freeFunction": false, "functionSelector": "a4a2c40b", "id": 22, "implemented": true, diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index 014b70fe9..4284c4f8f 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -161,7 +161,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "a4a2c40b", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/fallback.json b/test/libsolidity/ASTJSON/fallback.json index cf706e4fe..7037b960a 100644 --- a/test/libsolidity/ASTJSON/fallback.json +++ b/test/libsolidity/ASTJSON/fallback.json @@ -34,7 +34,6 @@ "src": "43:5:1", "statements": [] }, - "freeFunction": false, "id": 4, "implemented": true, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json index e8219b6db..009363e98 100644 --- a/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json +++ b/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json @@ -34,7 +34,6 @@ "src": "42:5:1", "statements": [] }, - "freeFunction": false, "id": 4, "implemented": true, "kind": "receive", @@ -69,7 +68,6 @@ "src": "78:5:1", "statements": [] }, - "freeFunction": false, "id": 8, "implemented": true, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json index 1fa662d64..8ac17607e 100644 --- a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "receive", @@ -101,7 +100,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_legacy.json b/test/libsolidity/ASTJSON/fallback_legacy.json index 9f31a225e..1ee8bf6b3 100644 --- a/test/libsolidity/ASTJSON/fallback_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_payable.json b/test/libsolidity/ASTJSON/fallback_payable.json index 883ba1bfd..2391f57bb 100644 --- a/test/libsolidity/ASTJSON/fallback_payable.json +++ b/test/libsolidity/ASTJSON/fallback_payable.json @@ -34,7 +34,6 @@ "src": "34:2:1", "statements": [] }, - "freeFunction": false, "id": 4, "implemented": true, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/fallback_payable_legacy.json b/test/libsolidity/ASTJSON/fallback_payable_legacy.json index 2e4a69077..69e25ab6e 100644 --- a/test/libsolidity/ASTJSON/fallback_payable_legacy.json +++ b/test/libsolidity/ASTJSON/fallback_payable_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "fallback", diff --git a/test/libsolidity/ASTJSON/function_type.json b/test/libsolidity/ASTJSON/function_type.json index fb706e5e8..c0d2efc68 100644 --- a/test/libsolidity/ASTJSON/function_type.json +++ b/test/libsolidity/ASTJSON/function_type.json @@ -34,7 +34,6 @@ "src": "120:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "d6cd4974", "id": 16, "implemented": true, diff --git a/test/libsolidity/ASTJSON/function_type_legacy.json b/test/libsolidity/ASTJSON/function_type_legacy.json index cc02a7dd9..627ce45f0 100644 --- a/test/libsolidity/ASTJSON/function_type_legacy.json +++ b/test/libsolidity/ASTJSON/function_type_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "d6cd4974", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json index dd9210e5f..7b598093b 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json @@ -134,7 +134,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 10, "implemented": true, diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json index 319b1e022..46e5b0417 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.json b/test/libsolidity/ASTJSON/long_type_name_identifier.json index 6df967ea9..4293f6df4 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.json @@ -142,7 +142,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 14, "implemented": true, diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json index f6ee2b948..ced331a6d 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json @@ -79,7 +79,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json index d1355a51f..198afe454 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.json +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -94,7 +94,6 @@ "src": "64:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "28811f59", "id": 13, "implemented": true, diff --git a/test/libsolidity/ASTJSON/modifier_definition_legacy.json b/test/libsolidity/ASTJSON/modifier_definition_legacy.json index a48e02104..cc3089395 100644 --- a/test/libsolidity/ASTJSON/modifier_definition_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_definition_legacy.json @@ -102,7 +102,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "28811f59", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json index d1355a51f..198afe454 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.json +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -94,7 +94,6 @@ "src": "64:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "28811f59", "id": 13, "implemented": true, diff --git a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json index a48e02104..cc3089395 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json +++ b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json @@ -102,7 +102,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "28811f59", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json index 22d95bf28..98db4273f 100644 --- a/test/libsolidity/ASTJSON/non_utf8.json +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -94,7 +94,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/non_utf8_legacy.json b/test/libsolidity/ASTJSON/non_utf8_legacy.json index c8d5c312f..d9430c82d 100644 --- a/test/libsolidity/ASTJSON/non_utf8_legacy.json +++ b/test/libsolidity/ASTJSON/non_utf8_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/override.json b/test/libsolidity/ASTJSON/override.json index e38305bf1..49f85144e 100644 --- a/test/libsolidity/ASTJSON/override.json +++ b/test/libsolidity/ASTJSON/override.json @@ -42,7 +42,6 @@ "src": "36:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "a399b6a2", "id": 4, "implemented": true, @@ -114,7 +113,6 @@ "nodes": [ { - "freeFunction": false, "functionSelector": "c2985578", "id": 10, "implemented": false, @@ -154,7 +152,6 @@ "src": "115:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "a399b6a2", "id": 15, "implemented": true, @@ -246,7 +243,6 @@ "src": "170:3:1", "statements": [] }, - "freeFunction": false, "functionSelector": "c2985578", "id": 23, "implemented": true, @@ -293,7 +289,6 @@ "src": "212:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "a399b6a2", "id": 30, "implemented": true, diff --git a/test/libsolidity/ASTJSON/override_legacy.json b/test/libsolidity/ASTJSON/override_legacy.json index bbd03937f..5cdc2f364 100644 --- a/test/libsolidity/ASTJSON/override_legacy.json +++ b/test/libsolidity/ASTJSON/override_legacy.json @@ -46,7 +46,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "a399b6a2", "implemented": true, "isConstructor": false, @@ -155,7 +154,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "c2985578", "implemented": false, "isConstructor": false, @@ -210,7 +208,6 @@ [ 4 ], - "freeFunction": false, "functionSelector": "a399b6a2", "implemented": true, "isConstructor": false, @@ -337,7 +334,6 @@ [ 10 ], - "freeFunction": false, "functionSelector": "c2985578", "implemented": true, "isConstructor": false, @@ -417,7 +413,6 @@ [ 15 ], - "freeFunction": false, "functionSelector": "a399b6a2", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/receive_ether.json b/test/libsolidity/ASTJSON/receive_ether.json index 8b678a593..8a1f0ac9a 100644 --- a/test/libsolidity/ASTJSON/receive_ether.json +++ b/test/libsolidity/ASTJSON/receive_ether.json @@ -34,7 +34,6 @@ "src": "42:5:1", "statements": [] }, - "freeFunction": false, "id": 4, "implemented": true, "kind": "receive", diff --git a/test/libsolidity/ASTJSON/receive_ether_legacy.json b/test/libsolidity/ASTJSON/receive_ether_legacy.json index 1155129d4..d9544f138 100644 --- a/test/libsolidity/ASTJSON/receive_ether_legacy.json +++ b/test/libsolidity/ASTJSON/receive_ether_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "implemented": true, "isConstructor": false, "kind": "receive", diff --git a/test/libsolidity/ASTJSON/short_type_name.json b/test/libsolidity/ASTJSON/short_type_name.json index f569f59af..882fcfe24 100644 --- a/test/libsolidity/ASTJSON/short_type_name.json +++ b/test/libsolidity/ASTJSON/short_type_name.json @@ -88,7 +88,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 10, "implemented": true, diff --git a/test/libsolidity/ASTJSON/short_type_name_legacy.json b/test/libsolidity/ASTJSON/short_type_name_legacy.json index b7912f585..8e255954d 100644 --- a/test/libsolidity/ASTJSON/short_type_name_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index e8845981f..2d07a9c60 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -99,7 +99,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 11, "implemented": true, diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json index f6cd04ed3..5316c56ac 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/source_location.json b/test/libsolidity/ASTJSON/source_location.json index fa95c0e4a..f56231ed7 100644 --- a/test/libsolidity/ASTJSON/source_location.json +++ b/test/libsolidity/ASTJSON/source_location.json @@ -131,7 +131,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 11, "implemented": true, diff --git a/test/libsolidity/ASTJSON/source_location_legacy.json b/test/libsolidity/ASTJSON/source_location_legacy.json index 529ca4444..bf3dfaedd 100644 --- a/test/libsolidity/ASTJSON/source_location_legacy.json +++ b/test/libsolidity/ASTJSON/source_location_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/string.json b/test/libsolidity/ASTJSON/string.json index 41fe9fa00..f47b41e47 100644 --- a/test/libsolidity/ASTJSON/string.json +++ b/test/libsolidity/ASTJSON/string.json @@ -95,7 +95,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/string_legacy.json b/test/libsolidity/ASTJSON/string_legacy.json index 89c9a5e55..96c984209 100644 --- a/test/libsolidity/ASTJSON/string_legacy.json +++ b/test/libsolidity/ASTJSON/string_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/two_base_functions.json b/test/libsolidity/ASTJSON/two_base_functions.json index 7e6526316..769e5c3f8 100644 --- a/test/libsolidity/ASTJSON/two_base_functions.json +++ b/test/libsolidity/ASTJSON/two_base_functions.json @@ -42,7 +42,6 @@ "src": "45:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 4, "implemented": true, @@ -97,7 +96,6 @@ "src": "95:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 9, "implemented": true, @@ -201,7 +199,6 @@ "src": "160:2:1", "statements": [] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 21, "implemented": true, diff --git a/test/libsolidity/ASTJSON/two_base_functions_legacy.json b/test/libsolidity/ASTJSON/two_base_functions_legacy.json index 405576ab6..a5bd2d725 100644 --- a/test/libsolidity/ASTJSON/two_base_functions_legacy.json +++ b/test/libsolidity/ASTJSON/two_base_functions_legacy.json @@ -46,7 +46,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, @@ -138,7 +137,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, @@ -274,7 +272,6 @@ 4, 9 ], - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, diff --git a/test/libsolidity/ASTJSON/unicode.json b/test/libsolidity/ASTJSON/unicode.json index 99f9eb619..81c8ad0da 100644 --- a/test/libsolidity/ASTJSON/unicode.json +++ b/test/libsolidity/ASTJSON/unicode.json @@ -95,7 +95,6 @@ } ] }, - "freeFunction": false, "functionSelector": "26121ff0", "id": 8, "implemented": true, diff --git a/test/libsolidity/ASTJSON/unicode_legacy.json b/test/libsolidity/ASTJSON/unicode_legacy.json index 826c3ce44..22976f6f9 100644 --- a/test/libsolidity/ASTJSON/unicode_legacy.json +++ b/test/libsolidity/ASTJSON/unicode_legacy.json @@ -38,7 +38,6 @@ { "attributes": { - "freeFunction": false, "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, From c34e34957268f9c58cf3aa4abe8fcc309f014a54 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 22 Jul 2020 16:52:28 +0200 Subject: [PATCH 062/139] Implement checked exponentiation. --- libsolidity/codegen/YulUtilFunctions.cpp | 133 ++++++++++++++++++ libsolidity/codegen/YulUtilFunctions.h | 15 ++ .../codegen/ir/IRGeneratorForStatements.cpp | 11 +- .../exponentiation/signed_base.sol | 2 + test/libsolidity/semanticTests/viaYul/exp.sol | 19 +++ .../semanticTests/viaYul/exp_neg.sol | 28 ++++ .../semanticTests/viaYul/exp_neg_overflow.sol | 38 +++++ .../semanticTests/viaYul/exp_overflow.sol | 31 ++++ .../semanticTests/viaYul/exp_various.sol | 50 +++++++ 9 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/exp.sol create mode 100644 test/libsolidity/semanticTests/viaYul/exp_neg.sol create mode 100644 test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol create mode 100644 test/libsolidity/semanticTests/viaYul/exp_overflow.sol create mode 100644 test/libsolidity/semanticTests/viaYul/exp_various.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index c687924b3..542a67613 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -597,6 +597,139 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) }); } +string YulUtilFunctions::overflowCheckedIntExpFunction( + IntegerType const& _type, + IntegerType const& _exponentType +) +{ + solAssert(!_exponentType.isSigned(), ""); + + string functionName = "checked_exp_" + _type.identifier() + "_" + _exponentType.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent) -> power { + base := (base) + exponent := (exponent) + + power := (base, exponent, , ) + + power := (base, exponent, ) + + + } + )") + ("functionName", functionName) + ("signed", _type.isSigned()) + ("exp", _type.isSigned() ? overflowCheckedSignedExpFunction() : overflowCheckedUnsignedExpFunction()) + ("maxValue", toCompactHexWithPrefix(_type.max())) + ("minValue", toCompactHexWithPrefix(_type.min())) + ("baseCleanupFunction", cleanupFunction(_type)) + ("exponentCleanupFunction", cleanupFunction(_exponentType)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedUnsignedExpFunction() +{ + string functionName = "checked_exp_unsigned"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent, max) -> power { + // This function currently cannot be inlined because of the + // "leave" statements. We have to improve the optimizer. + + // Note that 0**0 == 1 + if iszero(exponent) { power := 1 leave } + if iszero(base) { power := 0 leave } + + power := 1 + + for { } gt(exponent, 1) {} + { + // overflow check for base * base + if gt(base, div(max, base)) { revert(0, 0) } + if and(exponent, 1) + { + // no check needed here because base >= power + power := mul(power, base) + } + base := mul(base, base) + exponent := (exponent) + } + if gt(power, div(max, base)) { revert(0, 0) } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedSignedExpFunction() +{ + string functionName = "checked_exp_signed"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent, min, max) -> power { + // Currently, `leave` avoids this function being inlined. + // We have to improve the optimizer. + + // Note that 0**0 == 1 + switch exponent + case 0 { power := 1 leave } + case 1 { power := base leave } + if iszero(base) { power := 0 leave } + + power := 1 + + // We pull out the first iteration because it is the only one in which + // base can be negative. + // Exponent is at least 2 here. + + // overflow check for base * base + switch sgt(base, 0) + case 1 { if gt(base, div(max, base)) { revert(0, 0) } } + case 0 { if slt(base, sdiv(max, base)) { revert(0, 0) } } + if and(exponent, 1) + { + power := base + } + base := mul(base, base) + exponent := (exponent) + + // Below this point, base is always positive. + + for { } gt(exponent, 1) {} + { + // overflow check for base * base + if gt(base, div(max, base)) { revert(0, 0) } + if and(exponent, 1) + { + // No checks for power := mul(power, base) needed, because the check + // for base * base above is sufficient, since: + // |power| <= base (proof by induction) and thus: + // |power * base| <= base * base <= max <= |min| + power := mul(power, base) + } + base := mul(base, base) + exponent := (exponent) + } + + if and(sgt(power, 0), gt(power, div(max, base))) { revert(0, 0) } + if and(slt(power, 0), slt(power, sdiv(min, base))) { revert(0, 0) } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + string YulUtilFunctions::extractByteArrayLengthFunction() { string functionName = "extract_byte_array_length"; diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 19ca72dd0..7af910a6d 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -125,6 +125,21 @@ public: /// signature: (x, y) -> diff std::string overflowCheckedIntSubFunction(IntegerType const& _type); + /// @returns the name of the exponentiation function. + /// signature: (base, exponent) -> power + std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType); + + /// Generic unsigned checked exponentiation function. + /// Reverts if the result is larger than max. + /// signature: (base, exponent, max) -> power + std::string overflowCheckedUnsignedExpFunction(); + + /// Generic signed checked exponentiation function. + /// Reverts if the result is smaller than min or larger than max. + /// The code relies on max <= |min| and min < 0. + /// signature: (base, exponent, min, max) -> power + std::string overflowCheckedSignedExpFunction(); + /// @returns the name of a function that fetches the length of the given /// array /// signature: (array) -> length diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 1004ccf1b..846a93167 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -277,6 +277,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types."); IRVariable leftIntermediate = readFromLValue(*m_currentLValue); + solAssert(binaryOperator != Token::Exp, ""); if (TokenTraits::isShiftOp(binaryOperator)) { solAssert(type(_assignment) == leftIntermediate.type(), ""); @@ -593,11 +594,17 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) solAssert(false, "Unknown comparison operator."); define(_binOp) << expr << "\n"; } - else if (TokenTraits::isShiftOp(op)) + else if (TokenTraits::isShiftOp(op) || op == Token::Exp) { IRVariable left = convert(_binOp.leftExpression(), *commonType); IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType()); - define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n"; + if (op == Token::Exp) + define(_binOp) << m_utils.overflowCheckedIntExpFunction( + dynamic_cast(left.type()), + dynamic_cast(right.type()) + ) << "(" << left.name() << ", " << right.name() << ")\n"; + else + define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n"; } else { diff --git a/test/libsolidity/semanticTests/exponentiation/signed_base.sol b/test/libsolidity/semanticTests/exponentiation/signed_base.sol index f58c08129..045d322d0 100644 --- a/test/libsolidity/semanticTests/exponentiation/signed_base.sol +++ b/test/libsolidity/semanticTests/exponentiation/signed_base.sol @@ -10,5 +10,7 @@ contract test { return (x**y1, x**y2); } } +// ==== +// compileViaYul: also // ---- // f() -> 9, -27 diff --git a/test/libsolidity/semanticTests/viaYul/exp.sol b/test/libsolidity/semanticTests/viaYul/exp.sol new file mode 100644 index 000000000..aa8318d08 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256): 0, 0 -> 1 +// f(uint256,uint256): 0, 1 -> 0x00 +// f(uint256,uint256): 0, 2 -> 0x00 +// f(uint256,uint256): 1, 0 -> 1 +// f(uint256,uint256): 1, 1 -> 1 +// f(uint256,uint256): 1, 2 -> 1 +// f(uint256,uint256): 2, 0 -> 1 +// f(uint256,uint256): 2, 1 -> 2 +// f(uint256,uint256): 2, 2 -> 4 +// f(uint256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343 +// f(uint256,uint256): 128, 2 -> 0x4000 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/viaYul/exp_neg.sol b/test/libsolidity/semanticTests/viaYul/exp_neg.sol new file mode 100644 index 000000000..aa2f5eb18 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_neg.sol @@ -0,0 +1,28 @@ +contract C { + function f(int x, uint y) public returns (int) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(int256,uint256): 0, 0 -> 1 +// f(int256,uint256): 0, 1 -> 0x00 +// f(int256,uint256): 0, 2 -> 0x00 +// f(int256,uint256): 1, 0 -> 1 +// f(int256,uint256): 1, 1 -> 1 +// f(int256,uint256): 1, 2 -> 1 +// f(int256,uint256): 2, 0 -> 1 +// f(int256,uint256): 2, 1 -> 2 +// f(int256,uint256): 2, 2 -> 4 +// f(int256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343 +// f(int256,uint256): 128, 2 -> 0x4000 +// f(int256,uint256): -1, 0 -> 1 +// f(int256,uint256): -1, 1 -> -1 +// f(int256,uint256): -1, 2 -> 1 +// f(int256,uint256): -2, 0 -> 1 +// f(int256,uint256): -2, 1 -> -2 +// f(int256,uint256): -2, 2 -> 4 +// f(int256,uint256): -7, 63 -> -174251498233690814305510551794710260107945042018748343 +// f(int256,uint256): -128, 2 -> 0x4000 +// f(int256,uint256): -1, 115792089237316195423570985008687907853269984665640564039457584007913129639935 -> -1 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol b/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol new file mode 100644 index 000000000..a55cbebaa --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol @@ -0,0 +1,38 @@ +contract C { + function f(int8 x, uint y) public returns (int) { + return x**y; + } + function g(int256 x, uint y) public returns (int) { + return x**y; + } +} +// ==== +// compileViaYul: true +// ---- +// f(int8,uint256): 2, 6 -> 64 +// f(int8,uint256): 2, 7 -> FAILURE +// f(int8,uint256): 2, 8 -> FAILURE +// f(int8,uint256): -2, 6 -> 64 +// f(int8,uint256): -2, 7 -> -128 +// f(int8,uint256): -2, 8 -> FAILURE +// f(int8,uint256): 6, 3 -> FAILURE +// f(int8,uint256): 7, 2 -> 0x31 +// f(int8,uint256): 7, 3 -> FAILURE +// f(int8,uint256): -7, 2 -> 0x31 +// f(int8,uint256): -7, 3 -> FAILURE +// f(int8,uint256): -7, 4 -> FAILURE +// f(int8,uint256): 127, 31 -> FAILURE +// f(int8,uint256): 127, 131 -> FAILURE +// f(int8,uint256): -128, 0 -> 1 +// f(int8,uint256): -128, 1 -> -128 +// f(int8,uint256): -128, 31 -> FAILURE +// f(int8,uint256): -128, 131 -> FAILURE +// f(int8,uint256): -11, 2 -> 121 +// f(int8,uint256): -12, 2 -> FAILURE +// f(int8,uint256): 12, 2 -> FAILURE +// f(int8,uint256): -5, 3 -> -125 +// f(int8,uint256): -6, 3 -> FAILURE +// g(int256,uint256): -7, 90 -> 11450477594321044359340126713545146077054004823284978858214566372120240027249 +// g(int256,uint256): -7, 91 -> FAILURE +// g(int256,uint256): -63, 42 -> 3735107253208426854890677539053540390278853997836851167913009474475553834369 +// g(int256,uint256): -63, 43 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/exp_overflow.sol b/test/libsolidity/semanticTests/viaYul/exp_overflow.sol new file mode 100644 index 000000000..c23b61a86 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_overflow.sol @@ -0,0 +1,31 @@ +contract C { + function f(uint8 x, uint8 y) public returns (uint) { + return x**y; + } + function g(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: true +// ---- +// f(uint8,uint8): 2, 7 -> 0x80 +// f(uint8,uint8): 2, 8 -> FAILURE +// f(uint8,uint8): 15, 2 -> 225 +// f(uint8,uint8): 6, 3 -> 0xd8 +// f(uint8,uint8): 7, 2 -> 0x31 +// f(uint8,uint8): 7, 3 -> FAILURE +// f(uint8,uint8): 7, 4 -> FAILURE +// f(uint8,uint8): 255, 31 -> FAILURE +// f(uint8,uint8): 255, 131 -> FAILURE +// g(uint256,uint256): 0x200000000000000000000000000000000, 1 -> 0x0200000000000000000000000000000000 +// g(uint256,uint256): 0x100000000000000000000000000000010, 2 -> FAILURE +// g(uint256,uint256): 0x200000000000000000000000000000000, 2 -> FAILURE +// g(uint256,uint256): 0x200000000000000000000000000000000, 3 -> FAILURE +// g(uint256,uint256): 255, 31 -> 400631961586894742455537928461950192806830589109049416147172451019287109375 +// g(uint256,uint256): 255, 32 -> -13630939032658036097408813250890608687528184442832962921928608997994916749311 +// g(uint256,uint256): 255, 33 -> FAILURE +// g(uint256,uint256): 255, 131 -> FAILURE +// g(uint256,uint256): 258, 31 -> 575719427506838823084316385994930914701079543089399988096291424922125729792 +// g(uint256,uint256): 258, 37 -> FAILURE +// g(uint256,uint256): 258, 131 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/exp_various.sol b/test/libsolidity/semanticTests/viaYul/exp_various.sol new file mode 100644 index 000000000..7d7a166d9 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_various.sol @@ -0,0 +1,50 @@ +contract C { + function f(uint8 x, uint8 y) public returns (uint) { + return x**y; + } + function g(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint8,uint8): 0, 0 -> 1 +// f(uint8,uint8): 0, 1 -> 0x00 +// f(uint8,uint8): 0, 2 -> 0x00 +// f(uint8,uint8): 0, 3 -> 0x00 +// f(uint8,uint8): 1, 0 -> 1 +// f(uint8,uint8): 1, 1 -> 1 +// f(uint8,uint8): 1, 2 -> 1 +// f(uint8,uint8): 1, 3 -> 1 +// f(uint8,uint8): 2, 0 -> 1 +// f(uint8,uint8): 2, 1 -> 2 +// f(uint8,uint8): 2, 2 -> 4 +// f(uint8,uint8): 2, 3 -> 8 +// f(uint8,uint8): 3, 0 -> 1 +// f(uint8,uint8): 3, 1 -> 3 +// f(uint8,uint8): 3, 2 -> 9 +// f(uint8,uint8): 3, 3 -> 0x1b +// f(uint8,uint8): 10, 0 -> 1 +// f(uint8,uint8): 10, 1 -> 0x0a +// f(uint8,uint8): 10, 2 -> 100 +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 0, 2 -> 0x00 +// g(uint256,uint256): 0, 3 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 1, 1 -> 1 +// g(uint256,uint256): 1, 2 -> 1 +// g(uint256,uint256): 1, 3 -> 1 +// g(uint256,uint256): 2, 0 -> 1 +// g(uint256,uint256): 2, 1 -> 2 +// g(uint256,uint256): 2, 2 -> 4 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 0 -> 1 +// g(uint256,uint256): 3, 1 -> 3 +// g(uint256,uint256): 3, 2 -> 9 +// g(uint256,uint256): 3, 3 -> 0x1b +// g(uint256,uint256): 10, 10 -> 10000000000 +// g(uint256,uint256): 10, 77 -> -15792089237316195423570985008687907853269984665640564039457584007913129639936 +// g(uint256,uint256): 256, 2 -> 0x010000 +// g(uint256,uint256): 256, 31 -> 0x0100000000000000000000000000000000000000000000000000000000000000 From 3cbe65e4f313d95e92d993e6e74c826ea787422d Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Wed, 1 Jul 2020 15:37:00 +0530 Subject: [PATCH 063/139] Refactor SideEffects struct --- libevmasm/KnownState.cpp | 7 +- libevmasm/SemanticInformation.cpp | 88 +++++++++++++++++-- libevmasm/SemanticInformation.h | 24 +++-- libyul/SideEffects.h | 67 ++++++++++---- libyul/backends/evm/EVMDialect.cpp | 20 +++-- libyul/backends/wasm/WasmDialect.cpp | 55 ++++++++---- libyul/optimiser/Semantics.cpp | 5 +- libyul/optimiser/Semantics.h | 13 +-- libyul/optimiser/UnusedPruner.cpp | 4 +- test/libyul/FunctionSideEffects.cpp | 29 ++++-- .../functionSideEffects/cyclic_graph.yul | 8 +- .../doubly_recursive_function.yul | 6 +- test/libyul/functionSideEffects/empty.yul | 2 +- .../functionSideEffects/empty_with_sstore.yul | 2 +- test/libyul/functionSideEffects/memory.yul | 12 +-- .../functionSideEffects/mload_in_function.yul | 4 +- .../functionSideEffects/multi_calls.yul | 10 +-- .../functionSideEffects/otherImmovables.yul | 6 +- .../recursive_function.yul | 4 +- .../functionSideEffects/simple_functions.yul | 12 +-- test/libyul/functionSideEffects/state.yul | 8 +- test/libyul/functionSideEffects/storage.yul | 10 +-- .../libyul/functionSideEffects/structures.yul | 14 +-- test/libyul/functionSideEffects/with_loop.yul | 6 +- 24 files changed, 290 insertions(+), 126 deletions(-) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index ec6c50702..3741486a9 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -155,8 +155,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool ); break; default: - bool invMem = SemanticInformation::invalidatesMemory(_item.instruction()); - bool invStor = SemanticInformation::invalidatesStorage(_item.instruction()); + bool invMem = + SemanticInformation::memory(_item.instruction()) == SemanticInformation::Write; + bool invStor = + SemanticInformation::storage(_item.instruction()) == SemanticInformation::Write; // We could be a bit more fine-grained here (CALL only invalidates part of // memory, etc), but we do not for now. if (invMem) @@ -420,4 +422,3 @@ KnownState::Id KnownState::tagUnion(set _tags) return id; } } - diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 3d4ead64f..1826aac5f 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -233,7 +233,7 @@ bool SemanticInformation::movable(Instruction _instruction) return true; } -bool SemanticInformation::sideEffectFree(Instruction _instruction) +bool SemanticInformation::canBeRemoved(Instruction _instruction) { // These are not really functional. assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, ""); @@ -241,15 +241,15 @@ bool SemanticInformation::sideEffectFree(Instruction _instruction) return !instructionInfo(_instruction).sideEffects; } -bool SemanticInformation::sideEffectFreeIfNoMSize(Instruction _instruction) +bool SemanticInformation::canBeRemovedIfNoMSize(Instruction _instruction) { if (_instruction == Instruction::KECCAK256 || _instruction == Instruction::MLOAD) return true; else - return sideEffectFree(_instruction); + return canBeRemoved(_instruction); } -bool SemanticInformation::invalidatesMemory(Instruction _instruction) +SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction) { switch (_instruction) { @@ -263,13 +263,47 @@ bool SemanticInformation::invalidatesMemory(Instruction _instruction) case Instruction::CALLCODE: case Instruction::DELEGATECALL: case Instruction::STATICCALL: - return true; + return SemanticInformation::Write; + + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::KECCAK256: + case Instruction::MLOAD: + case Instruction::MSIZE: + case Instruction::RETURN: + case Instruction::REVERT: + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + return SemanticInformation::Read; + default: - return false; + return SemanticInformation::None; } } -bool SemanticInformation::invalidatesStorage(Instruction _instruction) +bool SemanticInformation::movableApartFromEffects(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::EXTCODEHASH: + case Instruction::EXTCODESIZE: + case Instruction::RETURNDATASIZE: + case Instruction::BALANCE: + case Instruction::SELFBALANCE: + case Instruction::SLOAD: + case Instruction::KECCAK256: + case Instruction::MLOAD: + return true; + + default: + return movable(_instruction); + } +} + +SemanticInformation::Effect SemanticInformation::storage(Instruction _instruction) { switch (_instruction) { @@ -279,9 +313,45 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) case Instruction::CREATE: case Instruction::CREATE2: case Instruction::SSTORE: - return true; + return SemanticInformation::Write; + + case Instruction::SLOAD: + case Instruction::STATICCALL: + return SemanticInformation::Read; + default: - return false; + return SemanticInformation::None; + } +} + +SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::SELFDESTRUCT: + case Instruction::STATICCALL: // because it can affect returndatasize + // Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they + // are just marked as having 'other side effects.' + return SemanticInformation::Write; + + case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: + case Instruction::RETURNDATASIZE: + case Instruction::BALANCE: + case Instruction::SELFBALANCE: + case Instruction::RETURNDATACOPY: + case Instruction::EXTCODECOPY: + // PC and GAS are specifically excluded here. Instructions such as CALLER, CALLVALUE, + // ADDRESS are excluded because they cannot change during execution. + return SemanticInformation::Read; + + default: + return SemanticInformation::None; } } diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index f919b4207..679766e66 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -36,6 +36,15 @@ class AssemblyItem; */ struct SemanticInformation { + /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory + /// and other blockchain state). + enum Effect + { + None, + Read, + Write + }; + /// @returns true if the given items starts a new block for common subexpression analysis. /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize. static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant); @@ -57,20 +66,23 @@ struct SemanticInformation /// without altering the semantics. This means it cannot depend on storage or memory, /// cannot have any side-effects, but it can depend on a call-constant state of the blockchain. static bool movable(Instruction _instruction); + /// If true, the expressions in this code can be moved or copied (together with their arguments) + /// across control flow branches and instructions as long as these instructions' 'effects' do + /// not influence the 'effects' of the aforementioned expressions. + static bool movableApartFromEffects(Instruction _instruction); /// @returns true if the instruction can be removed without changing the semantics. /// This does not mean that it has to be deterministic or retrieve information from /// somewhere else than purely the values of its arguments. - static bool sideEffectFree(Instruction _instruction); + static bool canBeRemoved(Instruction _instruction); /// @returns true if the instruction can be removed without changing the semantics. /// This does not mean that it has to be deterministic or retrieve information from /// somewhere else than purely the values of its arguments. /// If true, the instruction is still allowed to influence the value returned by the /// msize instruction. - static bool sideEffectFreeIfNoMSize(Instruction _instruction); - /// @returns true if the given instruction modifies memory. - static bool invalidatesMemory(Instruction _instruction); - /// @returns true if the given instruction modifies storage (even indirectly). - static bool invalidatesStorage(Instruction _instruction); + static bool canBeRemovedIfNoMSize(Instruction _instruction); + static Effect memory(Instruction _instruction); + static Effect storage(Instruction _instruction); + static Effect otherState(Instruction _instruction); static bool invalidInPureFunctions(Instruction _instruction); static bool invalidInViewFunctions(Instruction _instruction); }; diff --git a/libyul/SideEffects.h b/libyul/SideEffects.h index e9c12d9c8..7433e40d2 100644 --- a/libyul/SideEffects.h +++ b/libyul/SideEffects.h @@ -18,6 +18,7 @@ #pragma once +#include #include namespace solidity::yul @@ -30,6 +31,20 @@ namespace solidity::yul */ struct SideEffects { + /// Corresponds to the effect that a Yul-builtin has on a generic data location (storage, memory + /// and other blockchain state). + enum Effect + { + None, + Read, + Write + }; + + friend Effect operator+(Effect const& _a, Effect const& _b) + { + return static_cast(std::max(static_cast(_a), static_cast(_b))); + } + /// If true, expressions in this code can be freely moved and copied without altering the /// semantics. /// At statement level, it means that functions containing this code can be @@ -38,22 +53,34 @@ struct SideEffects /// This means it cannot depend on storage or memory, cannot have any side-effects, /// but it can depend on state that is constant across an EVM-call. bool movable = true; + /// If true, the expressions in this code can be moved or copied (together with their arguments) + /// across control flow branches and instructions as long as these instructions' 'effects' do + /// not influence the 'effects' of the aforementioned expressions. + bool movableApartFromEffects = true; /// If true, the code can be removed without changing the semantics. - bool sideEffectFree = true; + bool canBeRemoved = true; /// If true, the code can be removed without changing the semantics as long as /// the whole program does not contain the msize instruction. - bool sideEffectFreeIfNoMSize = true; - /// If false, storage is guaranteed to be unchanged by the code under all - /// circumstances. - bool invalidatesStorage = false; - /// If false, memory is guaranteed to be unchanged by the code under all - /// circumstances. - bool invalidatesMemory = false; + bool canBeRemovedIfNoMSize = true; + /// If false, the code calls a for-loop or a recursive function, and therefore potentially loops + /// infinitely. All builtins are set to true by default, even `invalid()`. + bool cannotLoop = true; + /// Can write, read or have no effect on the blockchain state, when the value of `otherState` is + /// `Write`, `Read` or `None` respectively. + Effect otherState = None; + /// Can write, read or have no effect on storage, when the value of `storage` is `Write`, `Read` + /// or `None` respectively. When the value is `Write`, the expression can invalidate storage, + /// potentially indirectly through external calls. + Effect storage = None; + /// Can write, read or have no effect on memory, when the value of `memory` is `Write`, `Read` + /// or `None` respectively. Note that, when the value is `Read`, the expression can have an + /// effect on `msize()`. + Effect memory = None; /// @returns the worst-case side effects. static SideEffects worst() { - return SideEffects{false, false, false, true, true}; + return SideEffects{false, false, false, false, false, Write, Write, Write}; } /// @returns the combined side effects of two pieces of code. @@ -61,10 +88,13 @@ struct SideEffects { return SideEffects{ movable && _other.movable, - sideEffectFree && _other.sideEffectFree, - sideEffectFreeIfNoMSize && _other.sideEffectFreeIfNoMSize, - invalidatesStorage || _other.invalidatesStorage, - invalidatesMemory || _other.invalidatesMemory + movableApartFromEffects && _other.movableApartFromEffects, + canBeRemoved && _other.canBeRemoved, + canBeRemovedIfNoMSize && _other.canBeRemovedIfNoMSize, + cannotLoop && _other.cannotLoop, + otherState + _other.otherState, + storage + _other.storage, + memory + _other.memory }; } @@ -79,10 +109,13 @@ struct SideEffects { return movable == _other.movable && - sideEffectFree == _other.sideEffectFree && - sideEffectFreeIfNoMSize == _other.sideEffectFreeIfNoMSize && - invalidatesStorage == _other.invalidatesStorage && - invalidatesMemory == _other.invalidatesMemory; + movableApartFromEffects == _other.movableApartFromEffects && + canBeRemoved == _other.canBeRemoved && + canBeRemovedIfNoMSize == _other.canBeRemovedIfNoMSize && + cannotLoop == _other.cannotLoop && + otherState == _other.otherState && + storage == _other.storage && + memory == _other.memory; } }; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index f6affcdbf..dc648248b 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -190,7 +190,7 @@ map createBuiltins(langutil::EVMVersion _evmVe "datacopy", 3, 0, - SideEffects{false, false, false, false, true}, + SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, {}, []( FunctionCall const& _call, @@ -206,7 +206,7 @@ map createBuiltins(langutil::EVMVersion _evmVe "setimmutable", 2, 0, - SideEffects{false, false, false, false, true}, + SideEffects{false, false, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, {LiteralKind::String, std::nullopt}, []( FunctionCall const& _call, @@ -281,12 +281,20 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instruction) { + auto translate = [](evmasm::SemanticInformation::Effect _e) -> SideEffects::Effect + { + return static_cast(_e); + }; + return SideEffects{ evmasm::SemanticInformation::movable(_instruction), - evmasm::SemanticInformation::sideEffectFree(_instruction), - evmasm::SemanticInformation::sideEffectFreeIfNoMSize(_instruction), - evmasm::SemanticInformation::invalidatesStorage(_instruction), - evmasm::SemanticInformation::invalidatesMemory(_instruction) + evmasm::SemanticInformation::movableApartFromEffects(_instruction), + evmasm::SemanticInformation::canBeRemoved(_instruction), + evmasm::SemanticInformation::canBeRemovedIfNoMSize(_instruction), + true, // cannotLoop + translate(evmasm::SemanticInformation::otherState(_instruction)), + translate(evmasm::SemanticInformation::storage(_instruction)), + translate(evmasm::SemanticInformation::memory(_instruction)), }; } diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 6d2bf8524..1c7755d6a 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -86,27 +86,34 @@ WasmDialect::WasmDialect() addFunction("i64.extend_i32_u", {i32}, {i64}); addFunction("i32.store", {i32, i32}, {}, false); - m_functions["i32.store"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.store"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.store"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i64.store", {i32, i64}, {}, false); - m_functions["i64.store"_yulstring].sideEffects.invalidatesStorage = false; // TODO: add i32.store16, i64.store8, i64.store16, i64.store32 + m_functions["i64.store"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.store"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i32.store8", {i32, i32}, {}, false); - m_functions["i32.store8"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.store8"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.store8"_yulstring].sideEffects.otherState = SideEffects::None; + addFunction("i64.store8", {i32, i64}, {}, false); - m_functions["i64.store8"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i64.store8"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.store8"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i32.load", {i32}, {i32}, false); - m_functions["i32.load"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["i32.load"_yulstring].sideEffects.invalidatesMemory = false; - m_functions["i32.load"_yulstring].sideEffects.sideEffectFree = true; - m_functions["i32.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; + m_functions["i32.load"_yulstring].sideEffects.canBeRemoved = true; + m_functions["i32.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true; + m_functions["i32.load"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.load"_yulstring].sideEffects.memory = SideEffects::Read; + m_functions["i32.load"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i64.load", {i32}, {i64}, false); - m_functions["i64.load"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["i64.load"_yulstring].sideEffects.invalidatesMemory = false; - m_functions["i64.load"_yulstring].sideEffects.sideEffectFree = true; - m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; // TODO: add i32.load8, i32.load16, i64.load8, i64.load16, i64.load32 + m_functions["i64.load"_yulstring].sideEffects.canBeRemoved = true; + m_functions["i64.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true; + m_functions["i64.load"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.load"_yulstring].sideEffects.memory = SideEffects::Read; + m_functions["i64.load"_yulstring].sideEffects.otherState = SideEffects::None; // Drop is actually overloaded for all types, but Yul does not support that. // Because of that, we introduce "i32.drop" and "i64.drop". @@ -115,8 +122,9 @@ WasmDialect::WasmDialect() addFunction("nop", {}, {}); addFunction("unreachable", {}, {}, false); - m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; + m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None; + m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None; m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true; m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true; @@ -219,10 +227,24 @@ void WasmDialect::addEthereumExternals() f.returns.emplace_back(YulString(p)); // TODO some of them are side effect free. f.sideEffects = SideEffects::worst(); + f.sideEffects.cannotLoop = true; + f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminates; f.controlFlowSideEffects = ext.controlFlowSideEffects; f.isMSize = false; - f.sideEffects.invalidatesStorage = (ext.name == "storageStore"); f.literalArguments.clear(); + + static set const writesToStorage{ + "storageStore", + "call", + "callcode", + "callDelegate", + "create" + }; + static set const readsStorage{"storageLoad", "callStatic"}; + if (readsStorage.count(ext.name)) + f.sideEffects.storage = SideEffects::Read; + else if (!writesToStorage.count(ext.name)) + f.sideEffects.storage = SideEffects::None; } } @@ -241,6 +263,9 @@ void WasmDialect::addFunction( yulAssert(_returns.size() <= 1, "The Wasm 1.0 specification only allows up to 1 return value."); f.returns = std::move(_returns); f.sideEffects = _movable ? SideEffects{} : SideEffects::worst(); + f.sideEffects.cannotLoop = true; + // TODO This should be improved when LoopInvariantCodeMotion gets specialized for WASM + f.sideEffects.movableApartFromEffects = _movable; f.isMSize = false; f.literalArguments = std::move(_literalArguments); } diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 4a7b8db12..1ceeec3e2 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -106,8 +106,9 @@ map SideEffectsPropagator::sideEffects( for (auto const& function: _directCallGraph.functionsWithLoops + _directCallGraph.recursiveFunctions()) { ret[function].movable = false; - ret[function].sideEffectFree = false; - ret[function].sideEffectFreeIfNoMSize = false; + ret[function].canBeRemoved = false; + ret[function].canBeRemovedIfNoMSize = false; + ret[function].cannotLoop = false; } for (auto const& call: _directCallGraph.functionCalls) diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index a4f7b8102..1ff7e0d7e 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -59,16 +59,17 @@ public: void operator()(FunctionCall const& _functionCall) override; bool movable() const { return m_sideEffects.movable; } - bool sideEffectFree(bool _allowMSizeModification = false) const + bool canBeRemoved(bool _allowMSizeModification = false) const { if (_allowMSizeModification) - return sideEffectFreeIfNoMSize(); + return m_sideEffects.canBeRemovedIfNoMSize; else - return m_sideEffects.sideEffectFree; + return m_sideEffects.canBeRemoved; } - bool sideEffectFreeIfNoMSize() const { return m_sideEffects.sideEffectFreeIfNoMSize; } - bool invalidatesStorage() const { return m_sideEffects.invalidatesStorage; } - bool invalidatesMemory() const { return m_sideEffects.invalidatesMemory; } + bool cannotLoop() const { return m_sideEffects.cannotLoop; } + bool invalidatesStorage() const { return m_sideEffects.storage == SideEffects::Write; } + bool invalidatesMemory() const { return m_sideEffects.memory == SideEffects::Write; } + private: Dialect const& m_dialect; diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index a0e0f535d..3779c0d63 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -93,7 +93,7 @@ void UnusedPruner::operator()(Block& _block) statement = Block{std::move(varDecl.location), {}}; else if ( SideEffectsCollector(m_dialect, *varDecl.value, m_functionSideEffects). - sideEffectFree(m_allowMSizeOptimization) + canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); @@ -112,7 +112,7 @@ void UnusedPruner::operator()(Block& _block) ExpressionStatement& exprStmt = std::get(statement); if ( SideEffectsCollector(m_dialect, exprStmt.expression, m_functionSideEffects). - sideEffectFree(m_allowMSizeOptimization) + canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 56051ffdd..e72d98be2 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -49,14 +49,27 @@ string toString(SideEffects const& _sideEffects) vector ret; if (_sideEffects.movable) ret.emplace_back("movable"); - if (_sideEffects.sideEffectFree) - ret.emplace_back("sideEffectFree"); - if (_sideEffects.sideEffectFreeIfNoMSize) - ret.emplace_back("sideEffectFreeIfNoMSize"); - if (_sideEffects.invalidatesStorage) - ret.emplace_back("invalidatesStorage"); - if (_sideEffects.invalidatesMemory) - ret.emplace_back("invalidatesMemory"); + if (_sideEffects.movableApartFromEffects) + ret.emplace_back("movable apart from effects"); + if (_sideEffects.canBeRemoved) + ret.emplace_back("can be removed"); + if (_sideEffects.canBeRemovedIfNoMSize) + ret.emplace_back("can be removed if no msize"); + if (!_sideEffects.cannotLoop) + ret.emplace_back("can loop"); + if (_sideEffects.otherState == SideEffects::Write) + ret.emplace_back("writes other state"); + else if (_sideEffects.otherState == SideEffects::Read) + ret.emplace_back("reads other state"); + if (_sideEffects.storage == SideEffects::Write) + ret.emplace_back("writes storage"); + else if (_sideEffects.storage == SideEffects::Read) + ret.emplace_back("reads storage"); + if (_sideEffects.memory == SideEffects::Write) + ret.emplace_back("writes memory"); + else if (_sideEffects.memory == SideEffects::Read) + ret.emplace_back("reads memory"); + return joinHumanReadable(ret); } } diff --git a/test/libyul/functionSideEffects/cyclic_graph.yul b/test/libyul/functionSideEffects/cyclic_graph.yul index 58eba0acf..640bb7057 100644 --- a/test/libyul/functionSideEffects/cyclic_graph.yul +++ b/test/libyul/functionSideEffects/cyclic_graph.yul @@ -4,7 +4,7 @@ function c() { b() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: -// c: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop +// b: movable apart from effects, can loop +// c: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/doubly_recursive_function.yul b/test/libyul/functionSideEffects/doubly_recursive_function.yul index 9afcfb0af..aa137abb0 100644 --- a/test/libyul/functionSideEffects/doubly_recursive_function.yul +++ b/test/libyul/functionSideEffects/doubly_recursive_function.yul @@ -3,6 +3,6 @@ function b() { a() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop +// b: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/empty.yul b/test/libyul/functionSideEffects/empty.yul index 9e85e64b7..e3cc25d67 100644 --- a/test/libyul/functionSideEffects/empty.yul +++ b/test/libyul/functionSideEffects/empty.yul @@ -1,4 +1,4 @@ { } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize diff --git a/test/libyul/functionSideEffects/empty_with_sstore.yul b/test/libyul/functionSideEffects/empty_with_sstore.yul index 42a1b564c..b2cd4fa57 100644 --- a/test/libyul/functionSideEffects/empty_with_sstore.yul +++ b/test/libyul/functionSideEffects/empty_with_sstore.yul @@ -2,4 +2,4 @@ sstore(0, 1) } // ---- -// : invalidatesStorage +// : writes storage diff --git a/test/libyul/functionSideEffects/memory.yul b/test/libyul/functionSideEffects/memory.yul index 6db272b16..d39ffce68 100644 --- a/test/libyul/functionSideEffects/memory.yul +++ b/test/libyul/functionSideEffects/memory.yul @@ -6,9 +6,9 @@ function i() { pop(msize()) } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: invalidatesMemory -// f: invalidatesMemory -// g: invalidatesStorage -// h: sideEffectFreeIfNoMSize -// i: sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes memory +// f: writes memory +// g: writes storage +// h: movable apart from effects, can be removed if no msize, reads memory +// i: can be removed, can be removed if no msize, reads memory diff --git a/test/libyul/functionSideEffects/mload_in_function.yul b/test/libyul/functionSideEffects/mload_in_function.yul index 170535926..d7977e2b5 100644 --- a/test/libyul/functionSideEffects/mload_in_function.yul +++ b/test/libyul/functionSideEffects/mload_in_function.yul @@ -7,5 +7,5 @@ sstore(0, mload(0)) } // ---- -// : invalidatesStorage, invalidatesMemory -// foo: invalidatesMemory +// : can loop, writes storage, writes memory +// foo: can loop, writes memory diff --git a/test/libyul/functionSideEffects/multi_calls.yul b/test/libyul/functionSideEffects/multi_calls.yul index f60b15e3d..5d36c10df 100644 --- a/test/libyul/functionSideEffects/multi_calls.yul +++ b/test/libyul/functionSideEffects/multi_calls.yul @@ -15,8 +15,8 @@ } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: invalidatesStorage -// b: invalidatesStorage -// c: invalidatesStorage, invalidatesMemory -// d: movable, sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: can loop, writes storage +// b: can loop, writes storage +// c: can loop, writes storage, writes memory +// d: movable, movable apart from effects, can be removed, can be removed if no msize diff --git a/test/libyul/functionSideEffects/otherImmovables.yul b/test/libyul/functionSideEffects/otherImmovables.yul index 18e050fa0..bb02dba1d 100644 --- a/test/libyul/functionSideEffects/otherImmovables.yul +++ b/test/libyul/functionSideEffects/otherImmovables.yul @@ -5,8 +5,8 @@ function h() { invalid() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: sideEffectFree, sideEffectFreeIfNoMSize -// f: sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: can be removed, can be removed if no msize +// f: can be removed, can be removed if no msize // g: // h: diff --git a/test/libyul/functionSideEffects/recursive_function.yul b/test/libyul/functionSideEffects/recursive_function.yul index 875339cba..e65a00c2b 100644 --- a/test/libyul/functionSideEffects/recursive_function.yul +++ b/test/libyul/functionSideEffects/recursive_function.yul @@ -2,5 +2,5 @@ function a() { a() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/simple_functions.yul b/test/libyul/functionSideEffects/simple_functions.yul index fb0f5378e..61db9d27b 100644 --- a/test/libyul/functionSideEffects/simple_functions.yul +++ b/test/libyul/functionSideEffects/simple_functions.yul @@ -6,9 +6,9 @@ function i() { let z := mload(0) } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: movable, sideEffectFree, sideEffectFreeIfNoMSize -// f: invalidatesMemory -// g: invalidatesStorage -// h: sideEffectFree, sideEffectFreeIfNoMSize -// i: sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable, movable apart from effects, can be removed, can be removed if no msize +// f: writes memory +// g: writes storage +// h: can be removed, can be removed if no msize, reads memory +// i: movable apart from effects, can be removed if no msize, reads memory diff --git a/test/libyul/functionSideEffects/state.yul b/test/libyul/functionSideEffects/state.yul index 2b6cd5ce1..e605ff1e5 100644 --- a/test/libyul/functionSideEffects/state.yul +++ b/test/libyul/functionSideEffects/state.yul @@ -4,7 +4,7 @@ function g() { sstore(0, 1) } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: invalidatesStorage, invalidatesMemory -// f: invalidatesStorage, invalidatesMemory -// g: invalidatesStorage +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes other state, writes storage, writes memory +// f: writes other state, writes storage, writes memory +// g: writes storage diff --git a/test/libyul/functionSideEffects/storage.yul b/test/libyul/functionSideEffects/storage.yul index 0f9c22fde..038c368a2 100644 --- a/test/libyul/functionSideEffects/storage.yul +++ b/test/libyul/functionSideEffects/storage.yul @@ -5,8 +5,8 @@ function h() { pop(sload(0))} } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: invalidatesStorage -// f: invalidatesStorage -// g: invalidatesStorage, invalidatesMemory -// h: sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes storage +// f: writes storage +// g: writes other state, writes storage, writes memory +// h: movable apart from effects, can be removed, can be removed if no msize, reads storage diff --git a/test/libyul/functionSideEffects/structures.yul b/test/libyul/functionSideEffects/structures.yul index de1fa5b0c..9c844f66f 100644 --- a/test/libyul/functionSideEffects/structures.yul +++ b/test/libyul/functionSideEffects/structures.yul @@ -31,10 +31,10 @@ } } // ---- -// : invalidatesStorage, invalidatesMemory -// f: sideEffectFreeIfNoMSize -// g: invalidatesStorage, invalidatesMemory -// h: invalidatesStorage, invalidatesMemory -// i: invalidatesStorage -// r: movable, sideEffectFree, sideEffectFreeIfNoMSize -// t: invalidatesMemory +// : writes storage, writes memory +// f: movable apart from effects, can be removed if no msize, reads memory +// g: writes storage, writes memory +// h: writes storage, writes memory +// i: writes storage +// r: movable, movable apart from effects, can be removed, can be removed if no msize +// t: writes memory diff --git a/test/libyul/functionSideEffects/with_loop.yul b/test/libyul/functionSideEffects/with_loop.yul index d68aa1fec..6e9bc6436 100644 --- a/test/libyul/functionSideEffects/with_loop.yul +++ b/test/libyul/functionSideEffects/with_loop.yul @@ -4,6 +4,6 @@ pop(f()) } // ---- -// : -// f: -// g: +// : movable apart from effects, can loop +// f: movable apart from effects, can loop +// g: movable apart from effects, can loop From 0f5d0b6455cf2b135b4ebb2c53badc12357a4817 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Wed, 19 Aug 2020 01:15:08 +0200 Subject: [PATCH 064/139] Improve error coverage of parser --- scripts/error_codes.py | 4 ++-- test/libsolidity/SolidityParser.cpp | 16 ---------------- .../parsing/multiple_modifier_overrides.sol | 5 +++++ .../parsing/multiple_visibility_specifiers.sol | 7 +++++++ 4 files changed, 14 insertions(+), 18 deletions(-) create mode 100644 test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol create mode 100644 test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 6b28b1fc6..c834b3060 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -220,12 +220,12 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "1054", "1123", "1133", "1220", "1584", "1823", "1950", "1957", "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", "3263", "3299", "3356", "3441", "3682", "3876", - "3893", "3997", "4010", "4110", "4802", "4805", "4828", + "3893", "3997", "4010", "4802", "4805", "4828", "4904", "4990", "5052", "5073", "5170", "5188", "5272", "5333", "5347", "5473", "5622", "6041", "6052", "6272", "6708", "6792", "6931", "7110", "7128", "7186", "7319", "7589", "7593", "7653", "7812", "7885", "8065", "8084", "8140", "8261", "8312", "8452", "8592", "8758", "9011", - "9085", "9102", "9390", "9439", "9440", "9547", "9551", "9615", "9980" + "9085", "9390", "9440", "9547", "9551", "9615", "9980" } new_source_only_ids = source_only_ids - old_source_only_ids if len(new_source_only_ids) != 0: diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 6ad68d85b..53c93c726 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -511,22 +511,6 @@ BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) -{ - char const* text = R"( - contract c { - uint private internal a; - } - )"; - CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); - text = R"( - contract c { - function f() private external {} - } - )"; - CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); -} - BOOST_AUTO_TEST_CASE(keyword_is_reserved) { auto keywords = { diff --git a/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol b/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol new file mode 100644 index 000000000..8dbe4ac67 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol @@ -0,0 +1,5 @@ +contract C { + modifier f() override override {} +} +// ---- +// ParserError 9102: (39-47): Override already specified. diff --git a/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol b/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol new file mode 100644 index 000000000..c04558171 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol @@ -0,0 +1,7 @@ +contract C { + uint private internal a; + function f() private external {} +} +// ---- +// ParserError 4110: (30-38): Visibility already specified as "private". +// ParserError 9439: (67-75): Visibility already specified as "private". From 7b8cc0c49bdefa2a35c6162af5c49cefba3d379f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 18 Aug 2020 16:23:55 +0200 Subject: [PATCH 065/139] Remove null values from AST also in standard-json mode. --- Changelog.md | 1 + libsolidity/ast/ASTJsonConverter.cpp | 6 +++--- libsolidity/ast/ASTJsonConverter.h | 2 +- test/cmdlineTests/recovery_standard_json/output.json | 2 +- test/cmdlineTests/standard_only_ast_requested/output.json | 2 +- test/libsolidity/StandardCompiler.cpp | 4 ++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4b47af706..268a7ad00 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. Bugfixes: + * AST: Remove ``null`` member values also when the compiler is used in standard-json-mode. * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on array implicit conversion. diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 04283cc34..6d6d40775 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -203,13 +203,13 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair Json::Value toJson(std::vector> const& _nodes) { diff --git a/test/cmdlineTests/recovery_standard_json/output.json b/test/cmdlineTests/recovery_standard_json/output.json index a4113e186..7aa7214e4 100644 --- a/test/cmdlineTests/recovery_standard_json/output.json +++ b/test/cmdlineTests/recovery_standard_json/output.json @@ -4,4 +4,4 @@ pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name ","message":"Expected type name","severity":"error","sourceLocation":{"end":94,"file":"A","start":93},"type":"ParserError"},{"component":"general","errorCode":"3796","formattedMessage":"A:2:84: Warning: Recovered in ContractDefinition at '}'. pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ } ^ -","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}} +","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json index c1ffe5892..16f8bfddb 100644 --- a/test/cmdlineTests/standard_only_ast_requested/output.json +++ b/test/cmdlineTests/standard_only_ast_requested/output.json @@ -1 +1 @@ -{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"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":"97:2:0","statements":[]},"documentation":null,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} +{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 570350466..d71cf10f1 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -428,9 +428,9 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject()); BOOST_CHECK_EQUAL( util::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]), - "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]},\"license\":null},\"children\":" + "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":" "[{\"attributes\":{\"abstract\":false,\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\"," - "\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," + "\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" ); } From da8eb98cecae9aed6d8d7dfd1044dd72f068ca39 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Aug 2020 11:07:14 +0200 Subject: [PATCH 066/139] Fix typo. --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index ab5163e11..5d7dc9591 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -102,7 +102,7 @@ The ``./scripts/tests.sh`` script executes most Solidity tests automatically, including those bundled into the `Boost C++ Test Framework `_ application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command line tests and compilation tests. -The test system automatically tries try to discover the location of the ``evmone`` library +The test system automatically tries to discover the location of the ``evmone`` library starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``, From 5c6e7f03b497cc155d6f92763e32c45d95b8a5cf Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Fri, 3 Jul 2020 18:40:29 +0530 Subject: [PATCH 067/139] Extended LoopInvariantCodeMotion for storage and state --- libyul/optimiser/LoopInvariantCodeMotion.cpp | 16 ++++++--- libyul/optimiser/LoopInvariantCodeMotion.h | 11 ++++-- libyul/optimiser/Semantics.cpp | 10 ++++++ libyul/optimiser/Semantics.h | 38 ++++++++++++++++++++ 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/libyul/optimiser/LoopInvariantCodeMotion.cpp b/libyul/optimiser/LoopInvariantCodeMotion.cpp index 9a81e5fc8..3cc8397a9 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.cpp +++ b/libyul/optimiser/LoopInvariantCodeMotion.cpp @@ -35,9 +35,9 @@ void LoopInvariantCodeMotion::run(OptimiserStepContext& _context, Block& _ast) { map functionSideEffects = SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)); - + bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast); set ssaVars = SSAValueTracker::ssaVariables(_ast); - LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects}(_ast); + LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects, containsMSize}(_ast); } void LoopInvariantCodeMotion::operator()(Block& _block) @@ -57,7 +57,8 @@ void LoopInvariantCodeMotion::operator()(Block& _block) bool LoopInvariantCodeMotion::canBePromoted( VariableDeclaration const& _varDecl, - set const& _varsDefinedInCurrentScope + set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects ) const { // A declaration can be promoted iff @@ -73,7 +74,8 @@ bool LoopInvariantCodeMotion::canBePromoted( for (auto const& ref: ReferencesCounter::countReferences(*_varDecl.value, ReferencesCounter::OnlyVariables)) if (_varsDefinedInCurrentScope.count(ref.first) || !m_ssaVariables.count(ref.first)) return false; - if (!SideEffectsCollector{m_dialect, *_varDecl.value, &m_functionSideEffects}.movable()) + SideEffectsCollector sideEffects{m_dialect, *_varDecl.value, &m_functionSideEffects}; + if (!sideEffects.movableRelativeTo(_forLoopSideEffects, m_containsMSize)) return false; } return true; @@ -82,6 +84,10 @@ bool LoopInvariantCodeMotion::canBePromoted( optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) { assertThrow(_for.pre.statements.empty(), OptimizerException, ""); + + auto forLoopSideEffects = + SideEffectsCollector{m_dialect, _for, &m_functionSideEffects}.sideEffects(); + vector replacement; for (Block* block: {&_for.post, &_for.body}) { @@ -93,7 +99,7 @@ optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) if (holds_alternative(_s)) { VariableDeclaration const& varDecl = std::get(_s); - if (canBePromoted(varDecl, varsDefinedInScope)) + if (canBePromoted(varDecl, varsDefinedInScope, forLoopSideEffects)) { replacement.emplace_back(std::move(_s)); // Do not add the variables declared here to varsDefinedInScope because we are moving them. diff --git a/libyul/optimiser/LoopInvariantCodeMotion.h b/libyul/optimiser/LoopInvariantCodeMotion.h index a3bf1784f..1b2647924 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.h +++ b/libyul/optimiser/LoopInvariantCodeMotion.h @@ -49,17 +49,24 @@ private: explicit LoopInvariantCodeMotion( Dialect const& _dialect, std::set const& _ssaVariables, - std::map const& _functionSideEffects + std::map const& _functionSideEffects, + bool _containsMSize ): + m_containsMSize(_containsMSize), m_dialect(_dialect), m_ssaVariables(_ssaVariables), m_functionSideEffects(_functionSideEffects) { } /// @returns true if the given variable declaration can be moved to in front of the loop. - bool canBePromoted(VariableDeclaration const& _varDecl, std::set const& _varsDefinedInCurrentScope) const; + bool canBePromoted( + VariableDeclaration const& _varDecl, + std::set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects + ) const; std::optional> rewriteLoop(ForLoop& _for); + bool m_containsMSize = true; Dialect const& m_dialect; std::set const& m_ssaVariables; std::map const& m_functionSideEffects; diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 1ceeec3e2..735ed13d0 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -62,6 +62,16 @@ SideEffectsCollector::SideEffectsCollector( operator()(_ast); } +SideEffectsCollector::SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + map const* _functionSideEffects +): + SideEffectsCollector(_dialect, _functionSideEffects) +{ + operator()(_ast); +} + void SideEffectsCollector::operator()(FunctionCall const& _functionCall) { ASTWalker::operator()(_functionCall); diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 1ff7e0d7e..70ec119ac 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -54,11 +54,48 @@ public: Block const& _ast, std::map const* _functionSideEffects = nullptr ); + SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + std::map const* _functionSideEffects = nullptr + ); using ASTWalker::operator(); void operator()(FunctionCall const& _functionCall) override; bool movable() const { return m_sideEffects.movable; } + + bool movableRelativeTo(SideEffects const& _other, bool _codeContainsMSize) + { + if (!m_sideEffects.cannotLoop) + return false; + + if (m_sideEffects.movable) + return true; + + if ( + !m_sideEffects.movableApartFromEffects || + m_sideEffects.storage == SideEffects::Write || + m_sideEffects.otherState == SideEffects::Write || + m_sideEffects.memory == SideEffects::Write + ) + return false; + + if (m_sideEffects.otherState == SideEffects::Read) + if (_other.otherState == SideEffects::Write) + return false; + + if (m_sideEffects.storage == SideEffects::Read) + if (_other.storage == SideEffects::Write) + return false; + + if (m_sideEffects.memory == SideEffects::Read) + if (_codeContainsMSize || _other.memory == SideEffects::Write) + return false; + + return true; + } + bool canBeRemoved(bool _allowMSizeModification = false) const { if (_allowMSizeModification) @@ -70,6 +107,7 @@ public: bool invalidatesStorage() const { return m_sideEffects.storage == SideEffects::Write; } bool invalidatesMemory() const { return m_sideEffects.memory == SideEffects::Write; } + SideEffects sideEffects() { return m_sideEffects; } private: Dialect const& m_dialect; From bbf15c9af3ddf5084548d18742f7a0efc5f72548 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Fri, 3 Jul 2020 19:50:27 +0530 Subject: [PATCH 068/139] Tests and Changelog --- Changelog.md | 1 + test/cmdlineTests/optimizer_array_sload/args | 1 + .../optimizer_array_sload/input.sol | 15 ++++ .../cmdlineTests/optimizer_array_sload/output | 83 +++++++++++++++++++ test/cmdlineTests/optimizer_user_yul/output | 15 ++-- .../loopInvariantCodeMotion/complex_move.yul | 23 +++++ .../loopInvariantCodeMotion/create_sload.yul | 30 +++++++ .../move_memory_function.yul | 24 ++++++ .../move_state_function.yul | 25 ++++++ .../move_storage_function.yul | 25 ++++++ .../no_move_immovables.yul | 29 +++++++ .../no_move_memory.yul | 38 +++++++++ .../no_move_memory_loop.yul | 29 +++++++ .../no_move_memory_msize.yul | 35 ++++++++ .../loopInvariantCodeMotion/no_move_state.yul | 62 ++++++++++++++ .../no_move_state_function.yul | 32 +++++++ .../no_move_state_loop.yul | 29 +++++++ .../no_move_state_recursive_function.yul | 26 ++++++ .../no_move_staticall_returndatasize.yul | 32 +++++++ .../no_move_storage.yul | 57 +++++++++++++ .../no_move_storage_function.yul | 28 +++++++ .../no_move_storage_loop.yul | 25 ++++++ .../loopInvariantCodeMotion/not_first.yul | 21 +++++ .../loopInvariantCodeMotion/simple_memory.yul | 25 ++++++ .../loopInvariantCodeMotion/simple_state.yul | 23 +++++ .../simple_storage.yul | 23 +++++ 26 files changed, 750 insertions(+), 6 deletions(-) create mode 100644 test/cmdlineTests/optimizer_array_sload/args create mode 100644 test/cmdlineTests/optimizer_array_sload/input.sol create mode 100644 test/cmdlineTests/optimizer_array_sload/output create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul diff --git a/Changelog.md b/Changelog.md index 9235faf6e..485ae57c0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. + * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. Bugfixes: * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. diff --git a/test/cmdlineTests/optimizer_array_sload/args b/test/cmdlineTests/optimizer_array_sload/args new file mode 100644 index 000000000..8539e69f5 --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/args @@ -0,0 +1 @@ +--optimize --ir-optimized --metadata-hash none diff --git a/test/cmdlineTests/optimizer_array_sload/input.sol b/test/cmdlineTests/optimizer_array_sload/input.sol new file mode 100644 index 000000000..1ad596601 --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Arraysum { + uint256[] values; + + function sumArray() public view returns(uint) { + uint sum = 0; + // The optimizer should read the length of the array only once, because + // LoopInvariantCodeMotion can move the `sload` corresponding to the length outside of the + // loop. + for(uint i = 0; i < values.length; i++) + sum += values[i]; + } +} diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output new file mode 100644 index 000000000..ea4ae03ff --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -0,0 +1,83 @@ +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "Arraysum_33" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("Arraysum_33_deployed") + codecopy(0, dataoffset("Arraysum_33_deployed"), _1) + return(0, _1) + } + } + object "Arraysum_33_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x81d73423, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + let vloc_sum := _1 + let vloc_i := _1 + let _2 := sload(_1) + for { } + lt(vloc_i, _2) + { + vloc_i := increment_t_uint256(vloc_i) + } + { + let _3, _4 := storage_array_index_access_t_array$_t_uint256_$dyn_storage(_1, vloc_i) + vloc_sum := checked_add_t_uint256(vloc_sum, extract_from_storage_value_dynamict_uint256(sload(_3), _4)) + } + let memPos := allocateMemory(_1) + return(memPos, sub(abi_encode_tuple_t_uint256__to_t_uint256__fromStack(memPos, _1), memPos)) + } + } + revert(0, 0) + } + function abi_encode_tuple_t_uint256__to_t_uint256__fromStack(headStart, value0) -> tail + { + tail := add(headStart, 32) + mstore(headStart, value0) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + function checked_add_t_uint256(x, y) -> sum + { + if gt(x, not(y)) { revert(sum, sum) } + sum := add(x, y) + } + function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value + { + value := shr(mul(offset, 8), slot_value) + } + function increment_t_uint256(value) -> ret + { + if gt(value, not(1)) { revert(ret, ret) } + ret := add(value, 1) + } + function storage_array_index_access_t_array$_t_uint256_$dyn_storage(array, index) -> slot, offset + { + if iszero(lt(index, sload(array))) { invalid() } + mstore(slot, array) + slot := add(keccak256(slot, 0x20), index) + offset := offset + } + } + } +} diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output index 34f35c643..9a4301aa4 100644 --- a/test/cmdlineTests/optimizer_user_yul/output +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -48,19 +48,22 @@ tag_5: 0x02 /* "optimizer_user_yul/input.sol":359:371 sstore(2, 3) */ sstore - /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ -tag_6: /* "optimizer_user_yul/input.sol":390:391 5 */ 0x05 /* "optimizer_user_yul/input.sol":384:392 sload(5) */ sload - tag_9 - jumpi - jump(tag_8) -tag_9: + iszero /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ +tag_6: + /* "optimizer_user_yul/input.sol":384:392 sload(5) */ + dup1 + /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ + tag_8 + jumpi jump(tag_6) tag_8: + /* "optimizer_user_yul/input.sol":380:383 { } */ + pop /* "optimizer_user_yul/input.sol":340:513 {... */ pop /* "optimizer_user_yul/input.sol":60:518 contract C... */ diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul new file mode 100644 index 000000000..3c11ca65f --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(keccak256(mul(mload(inv), 3), 32)) + a := add(x, 1) + sstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := extcodesize(keccak256(mul(mload(inv), 3), 32)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// sstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul new file mode 100644 index 000000000..f48cb78f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul @@ -0,0 +1,30 @@ +{ + function g() -> x { x := create(100, 0, 32) } + function f() -> x { x := mload(0) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + // cannot be moved because of the create call in g() + let q := sload(5) + let r := g() + // This can be moved + let z := f() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := create(100, 0, 32) } +// function f() -> x_1 +// { x_1 := mload(0) } +// let b := 1 +// let a := 1 +// let z := f() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let q := sload(5) +// let r := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul new file mode 100644 index 000000000..a4506a8e5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul @@ -0,0 +1,24 @@ +{ + function g() -> x { x := add(sload(mload(x)), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := mload(g()) + let s := keccak256(g(), 32) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := add(sload(mload(x)), 1) } +// let b := 1 +// let a := 1 +// let t := mload(g()) +// let s := keccak256(g(), 32) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul new file mode 100644 index 000000000..a5117cb61 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := mload(g()) } + function g() -> x { x := add(sload(x), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := mload(g()) } +// function g() -> x_1 +// { x_1 := add(sload(x_1), 1) } +// let b := 1 +// let a := 1 +// let t := extcodesize(f()) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul new file mode 100644 index 000000000..7790c0e6a --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := add(x, 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := sload(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := add(x_1, 1) } +// let b := 1 +// let a := 1 +// let t := sload(f()) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul new file mode 100644 index 000000000..b08899613 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul @@ -0,0 +1,29 @@ +{ + let a := 1 + function f() -> x {invalid()} + function g() -> y {return(0, 0)} + for { let i := 1 } iszero(eq(i, 10)) { a := add(i, 1) } { + let b := f() + let c := gas() + let d := g() + let e := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let a := 1 +// function f() -> x +// { invalid() } +// function g() -> y +// { return(0, 0) } +// let i := 1 +// for { } iszero(eq(i, 10)) { a := add(i, 1) } +// { +// let b := f() +// let c := gas() +// let d := g() +// let e := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul new file mode 100644 index 000000000..942a3c580 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul @@ -0,0 +1,38 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := keccak256(inv, 32) + a := add(x, 1) + mstore(a, inv) + } + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + // mload prevents moving of extcodesize + let x := extcodesize(mload(mul(inv, 3))) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := keccak256(inv, 32) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := extcodesize(mload(mul(inv_2, 3))) +// a_1 := add(x_3, 1) +// mstore(a_1, inv_2) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul new file mode 100644 index 000000000..1ebef6f9c --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := mload(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := mload(f()) +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul new file mode 100644 index 000000000..2033a9be9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul @@ -0,0 +1,35 @@ +{ + let b := 1 + let c := msize() // prevents moving keccak and mload + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := keccak256(inv, 32) + a := add(x, 1) + } + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(mload(mul(inv, 3))) + a := add(x, 1) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let c := msize() +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := keccak256(inv, 32) +// a := add(x, 1) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := extcodesize(mload(mul(inv_2, 3))) +// a_1 := add(x_3, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul new file mode 100644 index 000000000..fa6c985b4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul @@ -0,0 +1,62 @@ +{ + let b := 1 + // invalidates state in post + for { let a := 1 } iszero(eq(a, 10)) {pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32))} { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + pop(create(0x08, 0x00, 0x02)) // invalidates state + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + // invalidates state in loop-condition + for { let a := 1 } iszero(create(0x08, 0x00, 0x02)) { a := add(a, 1)} { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } +// iszero(eq(a, 10)) +// { +// pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32)) +// } +// { +// let x := extcodesize(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// pop(create(0x08, 0x00, 0x02)) +// let x_3 := extcodesize(mul(inv_2, 3)) +// a_1 := add(x_3, 1) +// mstore(a_1, inv_2) +// } +// let a_4 := 1 +// let inv_5 := add(b, 42) +// for { } iszero(create(0x08, 0x00, 0x02)) { a_4 := add(a_4, 1) } +// { +// let x_6 := extcodesize(mul(inv_5, 3)) +// a_4 := add(x_6, 1) +// mstore(a_4, inv_5) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul new file mode 100644 index 000000000..ab5be62d9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul @@ -0,0 +1,32 @@ +{ + function f() -> x { x := g() } + function g() -> x { + x := add(x, 1) + sstore(0x00, 0x00) + } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// x_1 := add(x_1, 1) +// sstore(0x00, 0x00) +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul new file mode 100644 index 000000000..d7e16ed68 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul new file mode 100644 index 000000000..431453941 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul @@ -0,0 +1,26 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := g() } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := g() } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul new file mode 100644 index 000000000..ae20f4904 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul @@ -0,0 +1,32 @@ +{ + let b := 1 + // invalidates state in post + for { let a := 1 } iszero(eq(a, 10)) {pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32))} { + let inv := add(b, 42) + let x := returndatasize() + a := add(x, 1) + pop(staticcall(2, 3, 0, 32, 64, 32)) // prevents moving returndatasize + mstore(a, inv) + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } +// iszero(eq(a, 10)) +// { +// pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32)) +// } +// { +// let x := returndatasize() +// a := add(x, 1) +// pop(staticcall(2, 3, 0, 32, 64, 32)) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul new file mode 100644 index 000000000..0ef2fd78a --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul @@ -0,0 +1,57 @@ +{ + let b := 1 + // invalidates storage in post + for { let a := 1 } iszero(eq(a, 10)) { sstore(0x00, 0x01)} { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + // invalidates storage in body + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + sstore(a, inv) + } + + // invalidates state in body + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32)) + let x := sload(mul(inv, 3)) + a := add(x, 1) + } + +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { sstore(0x00, 0x01) } +// { +// let x := sload(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := sload(mul(inv_2, 3)) +// a_1 := add(x_3, 1) +// sstore(a_1, inv_2) +// } +// let a_4 := 1 +// let inv_5 := add(b, 42) +// for { } iszero(eq(a_4, 10)) { a_4 := add(a_4, 1) } +// { +// pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32)) +// let x_6 := sload(mul(inv_5, 3)) +// a_4 := add(x_6, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul new file mode 100644 index 000000000..f1a2d6283 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul @@ -0,0 +1,28 @@ +{ + function f() -> x { x := g() } + function g() -> x { + x := add(x, 1) + sstore(0x00, 0x00) + } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// x_1 := add(x_1, 1) +// sstore(0x00, 0x00) +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { let q := sload(g()) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul new file mode 100644 index 000000000..d799fa6ae --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := sload(f()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { let t := sload(f()) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul new file mode 100644 index 000000000..2840d3445 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul @@ -0,0 +1,21 @@ +{ + function g() -> x { x := add(mload(x), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + sstore(0, a) + let q := keccak256(g(), 32) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := add(mload(x), 1) } +// let b := 1 +// let a := 1 +// let q := keccak256(g(), 32) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { sstore(0, a) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul new file mode 100644 index 000000000..223f54ee7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul @@ -0,0 +1,25 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := mload(mul(inv, 3)) + let y := keccak256(mul(b, 13), 32) + a := add(x, 1) + sstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := mload(mul(inv, 3)) +// let y := keccak256(mul(b, 13), 32) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// sstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul new file mode 100644 index 000000000..f8b5a3d53 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := extcodesize(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul new file mode 100644 index 000000000..022d66b2d --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := sload(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } From ab56c859158357bd056bc16191467fb30d92f7d9 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 19 Aug 2020 13:11:39 +0200 Subject: [PATCH 069/139] Fixes compilation with MSVC. --- libsolutil/CommonData.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index b61ba82a2..5fe0c2bfb 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -37,7 +37,10 @@ #include #include -/// Operators need to stay in the global namespace. +namespace std +{ + +/// Operator overloads for STL containers should be in std namespace for ADL to work properly. /// Concatenate the contents of a container onto a vector template std::vector& operator+=(std::vector& _a, U& _b) @@ -78,6 +81,7 @@ template std::set& operator+=(std::set& _a, U& _a.insert(std::move(x)); return _a; } + /// Concatenate two vectors of elements. template inline std::vector operator+(std::vector const& _a, std::vector const& _b) @@ -86,6 +90,7 @@ inline std::vector operator+(std::vector const& _a, std::vector const& ret += _b; return ret; } + /// Concatenate two vectors of elements, moving them. template inline std::vector operator+(std::vector&& _a, std::vector&& _b) @@ -97,6 +102,7 @@ inline std::vector operator+(std::vector&& _a, std::vector&& _b) ret += std::move(_b); return ret; } + /// Concatenate something to a sets of elements. template inline std::set operator+(std::set const& _a, U&& _b) @@ -105,6 +111,7 @@ inline std::set operator+(std::set const& _a, U&& _b) ret += std::forward(_b); return ret; } + /// Concatenate something to a sets of elements, move variant. template inline std::set operator+(std::set&& _a, U&& _b) @@ -140,6 +147,8 @@ inline std::multiset& operator-=(std::multiset& _a, C const& _b) return _a; } +} // end namespace std + namespace solidity::util { From bd21535dd152cae32955b486afd07f586f56bb4a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 12 Aug 2020 03:05:53 +0200 Subject: [PATCH 070/139] Rewrite of the antlr grammar. --- docs/Solidity.g4 | 487 ----------------- docs/conf.py | 6 +- docs/grammar.rst | 11 +- docs/grammar/Solidity.g4 | 511 ++++++++++++++++++ docs/grammar/SolidityLexer.g4 | 335 ++++++++++++ docs/requirements.txt | 1 + scripts/test_antlr_grammar.sh | 86 ++- .../parsing/constructor_internal_internal.sol | 5 + .../parsing/constructor_internal_public.sol | 5 + .../parsing/constructor_payable_payable.sol | 5 + .../parsing/constructor_public_internal.sol | 5 + .../parsing/constructor_public_public.sol | 5 + .../function_type_multiple_mutability.sol | 7 + .../function_type_multiple_visibility.sol | 7 + 14 files changed, 963 insertions(+), 513 deletions(-) delete mode 100644 docs/Solidity.g4 create mode 100644 docs/grammar/Solidity.g4 create mode 100644 docs/grammar/SolidityLexer.g4 create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_public_public.sol create mode 100644 test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol create mode 100644 test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 deleted file mode 100644 index dee054492..000000000 --- a/docs/Solidity.g4 +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright 2020 Gonçalo Sá -// Copyright 2016-2019 Federico Bond -// Licensed under the MIT license. See LICENSE file in the project root for details. - -// This grammar is much less strict than what Solidity currently parses -// to allow this to pass with older versions of Solidity. - -grammar Solidity; - -sourceUnit - : (pragmaDirective | importDirective | structDefinition | enumDefinition | functionDefinition | contractDefinition)* EOF ; - -pragmaDirective - : 'pragma' pragmaName ( ~';' )* ';' ; - -pragmaName - : identifier ; - -importDirective - : 'import' StringLiteralFragment ('as' identifier)? ';' - | 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';' - | 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ; - -importDeclaration - : identifier ('as' identifier)? ; - -contractDefinition - : 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier - ( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )? - '{' contractPart* '}' ; - -inheritanceSpecifier - : userDefinedTypeName ( '(' expressionList? ')' )? ; - -contractPart - : stateVariableDeclaration - | usingForDeclaration - | structDefinition - | modifierDefinition - | functionDefinition - | eventDefinition - | enumDefinition ; - -stateVariableDeclaration - : typeName - ( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )* - identifier ('=' expression)? ';' ; - -overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ; - -usingForDeclaration - : 'using' identifier 'for' ('*' | typeName) ';' ; - -structDefinition - : 'struct' identifier - '{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ; - -modifierDefinition - : 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* ( ';' | block ) ; - -functionDefinition - : functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ; - -functionDescriptor - : 'function' ( identifier | ReceiveKeyword | FallbackKeyword )? - | ConstructorKeyword - | FallbackKeyword - | ReceiveKeyword ; - -returnParameters - : 'returns' parameterList ; - -modifierList - : ( modifierInvocation | stateMutability | ExternalKeyword - | PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ; - -modifierInvocation - : identifier ( '(' expressionList? ')' )? ; - -eventDefinition - : 'event' identifier eventParameterList AnonymousKeyword? ';' ; - -enumDefinition - : 'enum' identifier '{' enumValue? (',' enumValue)* '}' ; - -enumValue - : identifier ; - -parameterList - : '(' ( parameter (',' parameter)* )? ')' ; - -parameter - : typeName storageLocation? identifier? ; - -eventParameterList - : '(' ( eventParameter (',' eventParameter)* )? ')' ; - -eventParameter - : typeName IndexedKeyword? identifier? ; - -variableDeclaration - : typeName storageLocation? identifier ; - -typeName - : elementaryTypeName - | userDefinedTypeName - | mapping - | typeName '[' expression? ']' - | functionTypeName ; - -userDefinedTypeName - : identifier ( '.' identifier )* ; - -mapping - : 'mapping' '(' mappingKey '=>' typeName ')' ; - -mappingKey - : elementaryTypeName - | userDefinedTypeName ; - -functionTypeName - : 'function' parameterList modifierList returnParameters? ; - -storageLocation - : 'memory' | 'storage' | 'calldata'; - -stateMutability - : PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ; - -block - : '{' statement* '}' ; - -statement - : ifStatement - | tryStatement - | whileStatement - | forStatement - | block - | inlineAssemblyStatement - | doWhileStatement - | continueStatement - | breakStatement - | returnStatement - | throwStatement - | emitStatement - | simpleStatement ; - -expressionStatement - : expression ';' ; - -ifStatement - : 'if' '(' expression ')' statement ( 'else' statement )? ; - -tryStatement : 'try' expression returnParameters? block catchClause+ ; - -// In reality catch clauses still are not processed as below -// the identifier can only be a set string: "Error". But plans -// of the Solidity team include possible expansion so we'll -// leave this as is, befitting with the Solidity docs. -catchClause : 'catch' ( identifier? parameterList )? block ; - -whileStatement - : 'while' '(' expression ')' statement ; - -forStatement - : 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ; - -simpleStatement - : ( variableDeclarationStatement | expressionStatement ) ; - -inlineAssemblyStatement - : 'assembly' StringLiteralFragment? assemblyBlock ; - -doWhileStatement - : 'do' statement 'while' '(' expression ')' ';' ; - -continueStatement - : 'continue' ';' ; - -breakStatement - : 'break' ';' ; - -returnStatement - : 'return' expression? ';' ; - -// throw is no longer supported by latest Solidity. -throwStatement - : 'throw' ';' ; - -emitStatement - : 'emit' functionCall ';' ; - -// 'var' is no longer supported by latest Solidity. -variableDeclarationStatement - : ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';'; - -variableDeclarationList - : variableDeclaration? (',' variableDeclaration? )* ; - -identifierList - : '(' ( identifier? ',' )* identifier? ')' ; - -elementaryTypeName - : 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ; - -Int - : 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ; - -Uint - : 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ; - -Byte - : 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ; - -Fixed - : 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ; - -Ufixed - : 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ; - -expression - : expression ('++' | '--') - | 'new' typeName - | expression '[' expression? ']' - | expression '[' expression? ':' expression? ']' - | expression '.' identifier - | expression '{' nameValueList '}' - | expression '(' functionCallArguments ')' - | PayableKeyword '(' expression ')' - | '(' expression ')' - | ('++' | '--') expression - | ('+' | '-') expression - | ('after' | 'delete') expression - | '!' expression - | '~' expression - | expression '**' expression - | expression ('*' | '/' | '%') expression - | expression ('+' | '-') expression - | expression ('<<' | '>>') expression - | expression '&' expression - | expression '^' expression - | expression '|' expression - | expression ('<' | '>' | '<=' | '>=') expression - | expression ('==' | '!=') expression - | expression '&&' expression - | expression '||' expression - | expression '?' expression ':' expression - | expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression - | primaryExpression ; - -primaryExpression - : BooleanLiteral - | numberLiteral - | hexLiteral - | stringLiteral - | unicodeStringLiteral - | identifier ('[' ']')? - | TypeKeyword - | tupleExpression - | typeNameExpression ('[' ']')? ; - -expressionList - : expression (',' expression)* ; - -nameValueList - : nameValue (',' nameValue)* ','? ; - -nameValue - : identifier ':' expression ; - -functionCallArguments - : '{' nameValueList? '}' - | expressionList? ; - -functionCall - : expression '(' functionCallArguments ')' ; - -tupleExpression - : '(' ( expression? ( ',' expression? )* ) ')' - | '[' ( expression ( ',' expression )* )? ']' ; - -typeNameExpression - : elementaryTypeName - | userDefinedTypeName ; - -assemblyItem - : identifier - | assemblyBlock - | assemblyExpression - | assemblyLocalDefinition - | assemblyAssignment - | assemblyStackAssignment - | labelDefinition - | assemblySwitch - | assemblyFunctionDefinition - | assemblyFor - | assemblyIf - | BreakKeyword - | ContinueKeyword - | LeaveKeyword - | subAssembly - | numberLiteral - | stringLiteral - | hexLiteral ; - -assemblyBlock - : '{' assemblyItem* '}' ; - -assemblyExpression - : assemblyCall | assemblyLiteral | assemblyIdentifier ; - -assemblyCall - : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; - -assemblyLocalDefinition - : 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ; - -assemblyAssignment - : assemblyIdentifierList ':=' assemblyExpression ; - -assemblyIdentifierList - : assemblyIdentifier ( ',' assemblyIdentifier )* ; - -assemblyIdentifier - : identifier ( '.' identifier )* ; - -assemblyStackAssignment - : '=:' identifier ; - -labelDefinition - : identifier ':' ; - -assemblySwitch - : 'switch' assemblyExpression assemblyCase* ; - -assemblyCase - : 'case' assemblyLiteral assemblyType? assemblyBlock - | 'default' assemblyBlock ; - -assemblyFunctionDefinition - : 'function' identifier '(' assemblyTypedVariableList? ')' - assemblyFunctionReturns? assemblyBlock ; - -assemblyFunctionReturns - : ( '-' '>' assemblyTypedVariableList ) ; - -assemblyFor - : 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ; - -assemblyIf - : 'if' assemblyExpression assemblyBlock ; - -assemblyLiteral - : ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ; - -assemblyTypedVariableList - : identifier assemblyType? ( ',' assemblyTypedVariableList )? ; - -assemblyType - : ':' identifier ; - -subAssembly - : 'assembly' identifier assemblyBlock ; - -// 'finney' and 'szabo' are no longer supported as denominations by latest Solidity. -numberLiteral - : (DecimalNumber | HexNumber) (NumberUnit | Gwei | Finney | Szabo)?; - -identifier - : (Gwei | Finney | Szabo | 'from' | 'calldata' | 'address' | Identifier) ; - -BooleanLiteral - : 'true' | 'false' ; - -DecimalNumber - : ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ; - -fragment -DecimalDigits - : [0-9] ( '_'? [0-9] )* ; - -HexNumber - : '0' [xX] HexDigits ; - -fragment -HexDigits - : HexCharacter ( '_'? HexCharacter )* ; - -NumberUnit - : 'wei' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; - -Gwei: 'gwei' ; -Szabo: 'szabo' ; -Finney: 'finney' ; - -HexLiteralFragment - : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; - -hexLiteral : HexLiteralFragment+ ; - -fragment -HexPair - : HexCharacter HexCharacter ; - -fragment -HexCharacter - : [0-9A-Fa-f] ; - -ReservedKeyword - : 'after' - | 'case' - | 'default' - | 'final' - | 'in' - | 'inline' - | 'let' - | 'match' - | 'null' - | 'of' - | 'relocatable' - | 'static' - | 'switch' - | 'typeof' ; - -AnonymousKeyword : 'anonymous' ; -BreakKeyword : 'break' ; -ConstantKeyword : 'constant' ; -ImmutableKeyword : 'immutable' ; -ContinueKeyword : 'continue' ; -LeaveKeyword : 'leave' ; -ExternalKeyword : 'external' ; -IndexedKeyword : 'indexed' ; -InternalKeyword : 'internal' ; -PayableKeyword : 'payable' ; -PrivateKeyword : 'private' ; -PublicKeyword : 'public' ; -VirtualKeyword : 'virtual' ; -PureKeyword : 'pure' ; -TypeKeyword : 'type' ; -ViewKeyword : 'view' ; - -ConstructorKeyword : 'constructor' ; -FallbackKeyword : 'fallback' ; -ReceiveKeyword : 'receive' ; - -Identifier - : IdentifierStart IdentifierPart* ; - -fragment -IdentifierStart - : [a-zA-Z$_] ; - -fragment -IdentifierPart - : [a-zA-Z0-9$_] ; - -stringLiteral - : StringLiteralFragment+ ; - -StringLiteralFragment - : '"' DoubleQuotedStringCharacter* '"' - | '\'' SingleQuotedStringCharacter* '\'' ; - -unicodeStringLiteral - : UnicodeStringLiteralFragment+ ; - -UnicodeStringLiteralFragment - : 'unicode"' DoubleQuotedStringCharacter* '"' - | 'unicode\'' SingleQuotedStringCharacter* '\'' ; - -fragment -DoubleQuotedStringCharacter - : ~["\r\n\\] | ('\\' .) ; - -fragment -SingleQuotedStringCharacter - : ~['\r\n\\] | ('\\' .) ; - -WS - : [ \t\r\n\u000C]+ -> skip ; - -COMMENT - : '/*' .*? '*/' -> channel(HIDDEN) ; - -LINE_COMMENT - : '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/conf.py b/docs/conf.py index 1ff794722..191955c9a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,7 +39,9 @@ def setup(sphinx): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = [ 'sphinx_a4doc' ] + +a4_base_path = os.path.dirname(__file__) + '/grammar' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -83,7 +85,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts', 'types', 'examples'] +exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/grammar.rst b/docs/grammar.rst index c7a22dd7e..d69f86b08 100644 --- a/docs/grammar.rst +++ b/docs/grammar.rst @@ -2,5 +2,12 @@ Language Grammar **************** -.. literalinclude:: Solidity.g4 - :language: antlr +.. a4:autogrammar:: Solidity + :only-reachable-from: Solidity.sourceUnit + :undocumented: + :cc-to-dash: + +.. a4:autogrammar:: SolidityLexer + :only-reachable-from: Solidity.sourceUnit + :fragments: + :cc-to-dash: \ No newline at end of file diff --git a/docs/grammar/Solidity.g4 b/docs/grammar/Solidity.g4 new file mode 100644 index 000000000..626ffc87b --- /dev/null +++ b/docs/grammar/Solidity.g4 @@ -0,0 +1,511 @@ +/** + * Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. + */ +grammar Solidity; + +options { tokenVocab=SolidityLexer; } + +/** + * On top level, Solidity allows pragmas, import directives, and + * definitions of contracts, interfaces, libraries, structs and enums. + */ +sourceUnit: ( + pragmaDirective + | importDirective + | contractDefinition + | interfaceDefinition + | libraryDefinition + | functionDefinition + | structDefinition + | enumDefinition +)* EOF; + +//@doc: inline +pragmaDirective: Pragma PragmaToken+ PragmaSemicolon; + +/** + * Import directives import identifiers from different files. + */ +importDirective: + Import ( + (path (As unitAlias=identifier)?) + | (symbolAliases From path) + | (Mul As unitAlias=identifier From path) + ) Semicolon; +//@doc: inline +//@doc:name aliases +importAliases: symbol=identifier (As alias=identifier)?; +/** + * Path of a file to be imported. + */ +path: NonEmptyStringLiteral; +/** + * List of aliases for symbols to be imported. + */ +symbolAliases: LBrace aliases+=importAliases (Comma aliases+=importAliases)* RBrace; + +/** + * Top-level definition of a contract. + */ +contractDefinition: + Abstract? Contract name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of an interface. + */ +interfaceDefinition: + Interface name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of a library. + */ +libraryDefinition: Library name=identifier LBrace contractBodyElement* RBrace; + +//@doc:inline +inheritanceSpecifierList: + Is inheritanceSpecifiers+=inheritanceSpecifier + (Comma inheritanceSpecifiers+=inheritanceSpecifier)*?; +/** + * Inheritance specifier for contracts and interfaces. + * Can optionally supply base constructor arguments. + */ +inheritanceSpecifier: name=userDefinedTypeName arguments=callArgumentList?; + +/** + * Declarations that can be used in contracts, interfaces and libraries. + * + * Note that interfaces and libraries may not contain constructors, interfaces may not contain state variables + * and libraries may not contain fallback, receive functions nor non-constant state variables. + */ +contractBodyElement: + constructorDefinition + | functionDefinition + | modifierDefinition + | fallbackReceiveFunctionDefinition + | structDefinition + | enumDefinition + | stateVariableDeclaration + | eventDefinition + | usingDirective; +//@doc:inline +namedArgument: name=identifier Colon value=expression; +/** + * Arguments when calling a function or a similar callable object. + * The arguments are either given as comma separated list or as map of named arguments. + */ +callArgumentList: LParen ((expression (Comma expression)*)? | LBrace (namedArgument (Comma namedArgument)*)? RBrace) RParen; +/** + * Qualified name of a user defined type. + */ +userDefinedTypeName: identifier (Period identifier)*; + +/** + * Call to a modifier. If the modifier takes no arguments, the argument list can be skipped entirely + * (including opening and closing parentheses). + */ +modifierInvocation: identifier callArgumentList?; +/** + * Visibility for functions and function types. + */ +visibility: Internal | External | Private | Public; +/** + * A list of parameters, such as function arguments or return values. + */ +parameterList: parameters+=parameterDeclaration (Comma parameters+=parameterDeclaration)*; +//@doc:inline +parameterDeclaration: type=typeName location=dataLocation? name=identifier?; +/** + * Definition of a constructor. + * Must always supply an implementation. + * Note that specifying internal or public visibility is deprecated. + */ +constructorDefinition +locals[boolean payableSet = false, boolean visibilitySet = false] +: + Constructor LParen (arguments=parameterList)? RParen + ( + modifierInvocation + | {!$payableSet}? Payable {$payableSet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$visibilitySet}? Public {$visibilitySet = true;} + )* + body=block; + +/** + * State mutability for function types. + * The default mutability 'non-payable' is assumed if no mutability is specified. + */ +stateMutability: Pure | View | Payable; +/** + * An override specifier used for functions, modifiers or state variables. + * In cases where there are ambiguous declarations in several base contracts being overridden, + * a complete list of base contracts has to be given. + */ +overrideSpecifier: Override (LParen overrides+=userDefinedTypeName (Comma overrides+=userDefinedTypeName)* RParen)?; +/** + * The definition of contract, library and interface functions. + * Depending on the context in which the function is defined, further restrictions may apply, + * e.g. functions in interfaces have to be unimplemented, i.e. may not contain a body block. + */ +functionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Function (identifier | Fallback | Receive) + LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)? + (Semicolon | body=block); +/** + * The definition of a modifier. + * Note that within the body block of a modifier, the underscore cannot be used as identifier, + * but is used as placeholder statement for the body of a function to which the modifier is applied. + */ +modifierDefinition +locals[ + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Modifier name=identifier + (LParen (arguments=parameterList)? RParen)? + ( + {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definitions of the special fallback and receive functions. + */ +fallbackReceiveFunctionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + kind=(Fallback | Receive) LParen RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definition of a struct. Can occur at top-level within a source unit or within a contract, library or interface. + */ +structDefinition: Struct name=identifier LBrace members=structMember+ RBrace; +/** + * The declaration of a named struct member. + */ +structMember: type=typeName name=identifier Semicolon; +/** + * Definition of an enum. Can occur at top-level within a source unit or within a contract, library or interface. + */ +enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace; + +/** + * The declaration of a state variable. + */ +stateVariableDeclaration +locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false] +: + type=typeName + ( + {!$visibilitySet}? Public {$visibilitySet = true;} + | {!$visibilitySet}? Private {$visibilitySet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$constantnessSet}? Constant {$constantnessSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + | {!$constantnessSet}? Immutable {$constantnessSet = true;} + )* + name=identifier + (Assign initialValue=expression)? + Semicolon; + +/** + * Parameter of an event. + */ +eventParameter: type=typeName Indexed? name=identifier?; +/** + * Definition of an event. Can occur in contracts, libraries or interfaces. + */ +eventDefinition: + Event name=identifier + LParen (parameters+=eventParameter (Comma parameters+=eventParameter)*)? RParen + Anonymous? + Semicolon; + +/** + * Using directive to bind library functions to types. + * Can occur within contracts and libraries. + */ +usingDirective: Using userDefinedTypeName For (Mul | typeName) Semicolon; +/** + * A type name can be an elementary type, a function type, a mapping type, a user-defined type + * (e.g. a contract or struct) or an array type. + */ +typeName: elementaryTypeName[true] | functionTypeName | mappingType | userDefinedTypeName | typeName LBrack expression? RBrack; +elementaryTypeName[boolean allowAddressPayable]: Address | {$allowAddressPayable}? Address Payable | Bool | String | Bytes | SignedIntegerType | UnsignedIntegerType | FixedBytes | Fixed | Ufixed; +functionTypeName +locals [boolean visibilitySet = false, boolean mutabilitySet = false] +: + Function LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)?; + +/** + * The declaration of a single variable. + */ +variableDeclaration: type=typeName location=dataLocation? name=identifier; +dataLocation: Memory | Storage | Calldata; + +/** + * Complex expression. + * Can be an index access, an index range access, a member access, a function call (with optional function call options), + * a type conversion, an unary or binary expression, a comparison or assignment, a ternary expression, + * a new-expression (i.e. a contract creation or the allocation of a dynamic memory array), + * a tuple, an inline array or a primary expression (i.e. an identifier, literal or type name). + */ +expression: + expression LBrack index=expression? RBrack # IndexAccess + | expression LBrack start=expression? Colon end=expression? RBrack # IndexRangeAccess + | expression Period (identifier | Address) # MemberAccess + | expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions + | expression callArgumentList # FunctionCall + | Payable callArgumentList # PayableConversion + | Type LParen typeName RParen # MetaType + | (Inc | Dec | Not | BitNot | Delete | Sub) expression # UnaryPrefixOperation + | expression (Inc | Dec) # UnarySuffixOperation + | expression Exp expression # ExpOperation + | expression (Mul | Div | Mod) expression # MulDivModOperation + | expression (Add | Sub) expression # AddSubOperation + | expression (Shl | Sar | Shr) expression # ShiftOperation + | expression BitAnd expression # BitAndOperation + | expression BitXor expression # BitXorOperation + | expression BitOr expression # BitOrOperation + | expression (LessThan | GreaterThan | LessThanOrEqual | GreaterThanOrEqual) expression # OrderComparison + | expression (Equal | NotEqual) expression # EqualityComparison + | expression And expression # AndOperation + | expression Or expression # OrOperation + | expression Conditional expression Colon expression # Conditional + | expression assignOp expression # Assignment + | New typeName # NewExpression + | tupleExpression # Tuple + | inlineArrayExpression # InlineArray + | ( + identifier + | literal + | elementaryTypeName[false] + | userDefinedTypeName + ) # PrimaryExpression +; + +//@doc:inline +assignOp: Assign | AssignBitOr | AssignBitXor | AssignBitAnd | AssignShl | AssignSar | AssignShr | AssignAdd | AssignSub | AssignMul | AssignDiv | AssignMod; +tupleExpression: LParen (expression? ( Comma expression?)* ) RParen; +/** + * An inline array expression denotes a statically sized array of the common type of the contained expressions. + */ +inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; + +/** + * Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements. + */ +identifier: Identifier | From; + +literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral; +booleanLiteral: True | False; +/** + * A full string literal consists of either one or several consecutive quoted strings. + */ +stringLiteral: StringLiteral+; +/** + * A full hex string literal that consists of either one or several consecutive hex strings. + */ +hexStringLiteral: HexString+; +/** + * A full unicode string literal that consists of either one or several consecutive unicode strings. + */ +unicodeStringLiteral: UnicodeStringLiteral+; + +/** + * Number literals can be decimal or hexadecimal numbers with an optional unit. + */ +numberLiteral: (DecimalNumber | HexNumber) NumberUnit?; +/** + * A curly-braced block of statements. Opens its own scope. + */ +block: LBrace statement* RBrace; + +statement: + block + | simpleStatement + | ifStatement + | forStatement + | whileStatement + | doWhileStatement + | continueStatement + | breakStatement + | tryStatement + | returnStatement + | emitStatement + | assemblyStatement +; + +//@doc:inline +simpleStatement: variableDeclarationStatement | expressionStatement; +/** + * If statement with optional else part. + */ +ifStatement: If LParen expression RParen statement (Else statement)?; +/** + * For statement with optional init, condition and post-loop part. + */ +forStatement: For LParen (simpleStatement | Semicolon) (expressionStatement | Semicolon) expression? RParen statement; +whileStatement: While LParen expression RParen statement; +doWhileStatement: Do statement While LParen expression RParen Semicolon; +/** + * A continue statement. Only allowed inside for, while or do-while loops. + */ +continueStatement: Continue Semicolon; +/** + * A break statement. Only allowed inside for, while or do-while loops. + */ +breakStatement: Break Semicolon; +/** + * A try statement. The contained expression needs to be an external function call or a contract creation. + */ +tryStatement: Try expression (Returns LParen returnParameters=parameterList RParen)? block catchClause+; +/** + * The catch clause of a try statement. + */ +catchClause: Catch (identifier? LParen (arguments=parameterList) RParen)? block; + +returnStatement: Return expression? Semicolon; +/** + * An emit statement. The contained expression needs to refer to an event. + */ +emitStatement: Emit expression callArgumentList Semicolon; +/** + * An inline assembly block. + * The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and + * allowed identifiers is different inside an inline assembly block. + */ +assemblyStatement: Assembly AssemblyDialect? AssemblyLBrace yulStatement* YulRBrace; + +//@doc:inline +variableDeclarationList: variableDeclarations+=variableDeclaration (Comma variableDeclarations+=variableDeclaration)*; +/** + * A tuple of variable names to be used in variable declarations. + * May contain empty fields. + */ +variableDeclarationTuple: + LParen + (Comma* variableDeclarations+=variableDeclaration) + (Comma (variableDeclarations+=variableDeclaration)?)* + RParen; +/** + * A variable declaration statement. + * A single variable may be declared without initial value, whereas a tuple of variables can only be + * declared with initial value. + */ +variableDeclarationStatement: ((variableDeclaration (Assign expression)?) | (variableDeclarationTuple Assign expression)) Semicolon; +expressionStatement: expression Semicolon; + +mappingType: Mapping LParen key=mappingKeyType Arrow value=typeName RParen; +/** + * Only elementary types or user defined types are viable as mapping keys. + */ +mappingKeyType: elementaryTypeName[false] | userDefinedTypeName; + +/** + * A Yul statement within an inline assembly block. + * continue and break statements are only valid within for loops. + * leave statements are only valid within function bodies. + */ +yulStatement: + yulBlock + | yulVariableDeclaration + | yulAssignment + | yulFunctionCall + | yulIfStatement + | yulForStatement + | yulSwitchStatement + | YulLeave + | YulBreak + | YulContinue + | yulFunctionDefinition; + +yulBlock: YulLBrace yulStatement* YulRBrace; + +/** + * The declaration of one or more Yul variables with optional initial value. + * If multiple variables are declared, only a function call is a valid initial value. + */ +yulVariableDeclaration: + (YulLet variables+=YulIdentifier (YulAssign yulExpression)?) + | (YulLet variables+=YulIdentifier (YulComma variables+=YulIdentifier)* (YulAssign yulFunctionCall)?); + +/** + * Any expression can be assigned to a single Yul variable, whereas + * multi-assignments require a function call on the right-hand side. + */ +yulAssignment: yulPath YulAssign yulExpression | (yulPath (YulComma yulPath)+) YulAssign yulFunctionCall; + +yulIfStatement: YulIf cond=yulExpression body=yulBlock; + +yulForStatement: YulFor init=yulBlock cond=yulExpression post=yulBlock body=yulBlock; + +//@doc:inline +yulSwitchCase: YulCase yulLiteral yulBlock; +/** + * A Yul switch statement can consist of only a default-case (deprecated) or + * one or more non-default cases optionally followed by a default-case. + */ +yulSwitchStatement: + YulSwitch yulExpression + ( + (yulSwitchCase+ (YulDefault yulBlock)?) + | (YulDefault yulBlock) + ); + +yulFunctionDefinition: + YulFunction YulIdentifier + YulLParen (arguments+=YulIdentifier (YulComma arguments+=YulIdentifier)*)? YulRParen + (YulArrow returnParameters+=YulIdentifier (YulComma returnParameters+=YulIdentifier)*)? + body=yulBlock; + +/** + * While only identifiers without dots can be declared within inline assembly, + * paths containing dots can refer to declarations outside the inline assembly block. + */ +yulPath: YulIdentifier (YulPeriod YulIdentifier)*; +/** + * A call to a function with return values can only occur as right-hand side of an assignment or + * a variable declaration. + */ +yulFunctionCall: (YulIdentifier | YulEVMBuiltin) YulLParen (yulExpression (YulComma yulExpression)*)? YulRParen; +yulBoolean: YulTrue | YulFalse; +yulLiteral: YulDecimalNumber | YulStringLiteral | YulHexNumber | yulBoolean; +yulExpression: yulPath | yulFunctionCall | yulLiteral; diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 new file mode 100644 index 000000000..441e1f8fd --- /dev/null +++ b/docs/grammar/SolidityLexer.g4 @@ -0,0 +1,335 @@ +lexer grammar SolidityLexer; + +/** + * Keywords reserved for future use in Solidity. + */ +ReservedKeywords: + 'after' | 'alias' | 'apply' | 'auto' | 'case' | 'copyof' | 'default' | 'define' | 'final' + | 'implements' | 'in' | 'inline' | 'let' | 'macro' | 'match' | 'mutable' | 'null' | 'of' + | 'partial' | 'promise' | 'reference' | 'relocatable' | 'sealed' | 'sizeof' | 'static' + | 'supports' | 'switch' | 'typedef' | 'typeof' | 'unchecked' | 'var'; + +Pragma: 'pragma' -> pushMode(PragmaMode); +Abstract: 'abstract'; +Anonymous: 'anonymous'; +Address: 'address'; +As: 'as'; +Assembly: 'assembly' -> pushMode(AssemblyBlockMode); +Bool: 'bool'; +Break: 'break'; +Bytes: 'bytes'; +Calldata: 'calldata'; +Catch: 'catch'; +Constant: 'constant'; +Constructor: 'constructor'; +Continue: 'continue'; +Contract: 'contract'; +Delete: 'delete'; +Do: 'do'; +Else: 'else'; +Emit: 'emit'; +Enum: 'enum'; +Event: 'event'; +External: 'external'; +Fallback: 'fallback'; +False: 'false'; +Fixed: 'fixed' | ('fixed' [0-9]+ 'x' [0-9]+); +From: 'from'; +/** + * Bytes types of fixed length. + * byte is an alias of bytes1. + */ +FixedBytes: + 'byte' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | + 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | + 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | + 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32'; +For: 'for'; +Function: 'function'; +Hex: 'hex'; +If: 'if'; +Immutable: 'immutable'; +Import: 'import'; +Indexed: 'indexed'; +Interface: 'interface'; +Internal: 'internal'; +Is: 'is'; +Library: 'library'; +Mapping: 'mapping'; +Memory: 'memory'; +Modifier: 'modifier'; +New: 'new'; +/** + * Unit denomination for numbers. + */ +NumberUnit: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'; +Override: 'override'; +Payable: 'payable'; +Private: 'private'; +Public: 'public'; +Pure: 'pure'; +Receive: 'receive'; +Return: 'return'; +Returns: 'returns'; +/** + * Sized signed integer types. + * int is an alias of int256. + */ +SignedIntegerType: + 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | + 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | + 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | + 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256'; +Storage: 'storage'; +String: 'string'; +Struct: 'struct'; +True: 'true'; +Try: 'try'; +Type: 'type'; +Ufixed: 'ufixed' | ('ufixed' [0-9]+ 'x' [0-9]+); +/** + * Sized unsigned integer types. + * uint is an alias of uint256. + */ +UnsignedIntegerType: + 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | + 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | + 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | + 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256'; +Using: 'using'; +View: 'view'; +Virtual: 'virtual'; +While: 'while'; + +LParen: '('; +RParen: ')'; +LBrack: '['; +RBrack: ']'; +LBrace: '{'; +RBrace: '}'; +Colon: ':'; +Semicolon: ';'; +Period: '.'; +Conditional: '?'; +Arrow: '=>'; + +Assign: '='; +AssignBitOr: '|='; +AssignBitXor: '^='; +AssignBitAnd: '&='; +AssignShl: '<<='; +AssignSar: '>>='; +AssignShr: '>>>='; +AssignAdd: '+='; +AssignSub: '-='; +AssignMul: '*='; +AssignDiv: '/='; +AssignMod: '%='; + +Comma: ','; +Or: '||'; +And: '&&'; +BitOr: '|'; +BitXor: '^'; +BitAnd: '&'; +Shl: '<<'; +Sar: '>>'; +Shr: '>>>'; +Add: '+'; +Sub: '-'; +Mul: '*'; +Div: '/'; +Mod: '%'; +Exp: '**'; + +Equal: '=='; +NotEqual: '!='; +LessThan: '<'; +GreaterThan: '>'; +LessThanOrEqual: '<='; +GreaterThanOrEqual: '>='; +Not: '!'; +BitNot: '~'; +Inc: '++'; +Dec: '--'; + +/** + * A single quoted string literal restricted to printable characters. + */ +StringLiteral: '"' DoubleQuotedStringCharacter* '"' | '\'' SingleQuotedStringCharacter* '\''; +/** + * A single non-empty quoted string literal. + */ +NonEmptyStringLiteral: '"' DoubleQuotedStringCharacter+ '"' | '\'' SingleQuotedStringCharacter+ '\''; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment DoubleQuotedStringCharacter: DoubleQuotedPrintable | EscapeSequence; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment SingleQuotedStringCharacter: SingleQuotedPrintable | EscapeSequence; +/** + * Any printable character except single quote or back slash. + */ +fragment SingleQuotedPrintable: [\u0020-\u0026\u0028-\u005B\u005D-\u007E]; +/** + * Any printable character except double quote or back slash. + */ +fragment DoubleQuotedPrintable: [\u0020-\u0021\u0023-\u005B\u005D-\u007E]; +/** + * Escape sequence. + * Apart from common single character escape sequences, line breaks can be escaped + * as well as four hex digit unicode escapes \\uXXXX and two digit hex escape sequences \\xXX are allowed. + */ +fragment EscapeSequence: + '\\' ( + ['"\\bfnrtv\n\r] + | 'u' HexCharacter HexCharacter HexCharacter HexCharacter + | 'x' HexCharacter HexCharacter + ); +/** + * A single quoted string literal allowing arbitrary unicode characters. + */ +UnicodeStringLiteral: + 'unicode"' DoubleQuotedUnicodeStringCharacter* '"' + | 'unicode\'' SingleQuotedUnicodeStringCharacter* '\''; +//@doc:inline +fragment DoubleQuotedUnicodeStringCharacter: ~["\r\n\\] | EscapeSequence; +//@doc:inline +fragment SingleQuotedUnicodeStringCharacter: ~['\r\n\\] | EscapeSequence; + +/** + * Hex strings need to consist of an even number of hex digits that may be grouped using underscores. + */ +HexString: 'hex' (('"' EvenHexDigits? '"') | ('\'' EvenHexDigits? '\'')); +/** + * Hex numbers consist of a prefix and an arbitrary number of hex digits that may be delimited by underscores. + */ +HexNumber: '0' 'x' HexDigits; +//@doc:inline +fragment HexDigits: HexCharacter ('_'? HexCharacter)*; +//@doc:inline +fragment EvenHexDigits: HexCharacter HexCharacter ('_'? HexCharacter HexCharacter)*; +//@doc:inline +fragment HexCharacter: [0-9A-Fa-f]; + +/** + * A decimal number literal consists of decimal digits that may be delimited by underscores and + * an optional positive or negative exponent. + * If the digits contain a decimal point, the literal has fixed point type. + */ +DecimalNumber: (DecimalDigits | (DecimalDigits? '.' DecimalDigits)) ([eE] '-'? DecimalDigits)?; +//@doc:inline +fragment DecimalDigits: [0-9] ('_'? [0-9])* ; + + +/** + * An identifier in solidity has to start with a letter, a dollar-sign or an underscore and + * may additionally contain numbers after the first symbol. + */ +Identifier: IdentifierStart IdentifierPart*; +//@doc:inline +fragment IdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment IdentifierPart: [a-zA-Z0-9$_]; + +WS: [ \t\r\n\u000C]+ -> skip ; +COMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +mode AssemblyBlockMode; + +//@doc:inline +AssemblyDialect: '"evmasm"'; +AssemblyLBrace: '{' -> popMode, pushMode(YulMode); + +AssemblyBlockWS: [ \t\r\n\u000C]+ -> skip ; +AssemblyBlockCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +AssemblyBlockLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode YulMode; + +YulBreak: 'break'; +YulCase: 'case'; +YulContinue: 'continue'; +YulDefault: 'default'; +YulFalse: 'false'; +YulFor: 'for'; +YulFunction: 'function'; +YulIf: 'if'; +YulLeave: 'leave'; +YulLet: 'let'; +YulSwitch: 'switch'; +YulTrue: 'true'; + +/** + * Builtin functions in the EVM Yul dialect. + */ +YulEVMBuiltin: + 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' + | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' + | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' + | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' + | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' + | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' + | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' + | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit'; + +YulLBrace: '{' -> pushMode(YulMode); +YulRBrace: '}' -> popMode; +YulLParen: '('; +YulRParen: ')'; +YulAssign: ':='; +YulPeriod: '.'; +YulComma: ','; +// TODO: remove whitespace workaround once the parser disallows it. +//@doc:name -> +YulArrow: '->' | '-' YulWS+ '>'; + +/** + * Yul identifiers consist of letters, dollar signs, underscores and numbers, but may not start with a number. + * In inline assembly there cannot be dots in user-defined identifiers. Instead see yulPath for expressions + * consisting of identifiers with dots. + */ +YulIdentifier: YulIdentifierStart YulIdentifierPart*; +//@doc:inline +fragment YulIdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment YulIdentifierPart: [a-zA-Z0-9$_]; +/** + * Hex literals in Yul consist of a prefix and one or more hexadecimal digits. + */ +YulHexNumber: '0' 'x' [0-9a-fA-F]+; +/** + * Decimal literals in Yul may be zero or any sequence of decimal digits without leading zeroes. + */ +YulDecimalNumber: '0' | ([1-9] [0-9]*); +/** + * String literals in Yul consist of one or more double-quoted or single-quoted strings + * that may contain escape sequences and printable characters except unescaped line breaks or + * unescaped double-quotes or single-quotes, respectively. + */ +YulStringLiteral: + '"' DoubleQuotedStringCharacter* '"' + | '\'' SingleQuotedStringCharacter* '\''; + + +YulWS: [ \t\r\n\u000C]+ -> skip ; +YulCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +YulLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode PragmaMode; + +/** + * Pragma token. Can contain any kind of symbol except a semicolon. + * Note that currently the solidity parser only allows a subset of this. + */ +//@doc:name pragma-token +//@doc:no-diagram +PragmaToken: ~[;]+; +PragmaSemicolon: ';' -> popMode; + +PragmaWS: [ \t\r\n\u000C]+ -> skip ; +PragmaCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +PragmaLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/requirements.txt b/docs/requirements.txt index 8f67f9594..65cc192fd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ sphinx_rtd_theme>=0.3.1 pygments-lexer-solidity>=0.5.1 +sphinx-a4doc>=1.2.1 diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 97cbe4ab4..846cecf80 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -2,11 +2,10 @@ set -e -ROOT_DIR="$(dirname "$0")"/.. +ROOT_DIR=$(readlink -f "$(dirname "$0")"/..) WORKDIR="${ROOT_DIR}/build/antlr" ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" -ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar" -GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")" +ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" SGR_RESET="\033[0m" SGR_BOLD="\033[1m" @@ -40,11 +39,14 @@ if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \ [ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ] then echo "Creating parser" + ( + cd "${ROOT_DIR}"/docs/grammar # Create lexer/parser from grammar - java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/" + java -jar "${ANTLR_JAR}" Solidity.g4 SolidityLexer.g4 -o "${WORKDIR}/src/" # Compile lexer/parser sources javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" + ) fi # Run tests @@ -55,47 +57,87 @@ test_file() SOL_FILE="$(readlink -m "${1}")" local cur=${2} local max=${3} + local solOrYul=${4} echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..." local output - output=$( - java \ - -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ - "org.antlr.v4.gui.TestRig" \ - Solidity \ - sourceUnit <"${SOL_FILE}" 2>&1 - ) + if [[ "${solOrYul}" == "sol" ]]; then + output=$( + java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + sourceUnit <"${SOL_FILE}" 2>&1 + ) + else + output=$( + echo "assembly $(cat "${SOL_FILE}")" | java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + assemblyStatement 2>&1 + ) + fi vt_cursor_up vt_cursor_begin_of_line - if [[ "${output}" == "" ]] - then - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + if grep -qE "^\/\/ ParserError" "${SOL_FILE}"; then + if [[ "${output}" != "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}SUCCEEDED DESPITE PARSER ERROR${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi else - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" - echo "${output}" - failed_count=$((failed_count + 1)) - exit 1 + if [[ "${output}" == "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi fi } -# we only want to use files that do not contain errors or multi-source files. +# we only want to use files that do not contain excluded parser errors, analysis errors or multi-source files. SOL_FILES=() while IFS='' read -r line do SOL_FILES+=("$line") done < <( grep -riL -E \ - "^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \ + "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (6275|3716|6281|2837|6933)|^==== Source:" \ "${ROOT_DIR}/test/libsolidity/syntaxTests" \ "${ROOT_DIR}/test/libsolidity/semanticTests" \ ) +YUL_FILES=() +# Add all yul optimizer tests without objects and types. +while IFS='' read -r line +do + YUL_FILES+=("$line") +done < <( + grep -riL -E \ + "object|\:[ ]*[uib]" \ + "${ROOT_DIR}/test/libyul/yulOptimizerTests" +) + +num_tests=$((${#SOL_FILES[*]} + ${#YUL_FILES[*]})) test_count=0 for SOL_FILE in "${SOL_FILES[@]}" do test_count=$((test_count + 1)) - test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]} + test_file "${SOL_FILE}" ${test_count} $num_tests "sol" +done +for YUL_FILE in "${YUL_FILES[@]}" +do + test_count=$((test_count + 1)) + test_file "${YUL_FILE}" ${test_count} $num_tests "yul" done -echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed." +echo "Summary: ${failed_count} of $num_tests sources failed." exit ${failed_count} diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol new file mode 100644 index 000000000..8b84de055 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal internal {} +} +// ---- +// ParserError 9439: (38-46): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol new file mode 100644 index 000000000..15d60c392 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal public {} +} +// ---- +// ParserError 9439: (38-44): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol new file mode 100644 index 000000000..495e3759c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol @@ -0,0 +1,5 @@ +contract C { + constructor() payable payable {} +} +// ---- +// ParserError 9680: (37-44): State mutability already specified as "payable". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol new file mode 100644 index 000000000..7d58520e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public internal {} +} +// ---- +// ParserError 9439: (36-44): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol new file mode 100644 index 000000000..c0674f2e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public public {} +} +// ---- +// ParserError 9439: (36-42): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol new file mode 100644 index 000000000..8d67db095 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() pure pure g; + } +} +// ---- +// ParserError 9680: (62-66): State mutability already specified as "pure". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol new file mode 100644 index 000000000..c9b035864 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() public public g; + } +} +// ---- +// ParserError 9439: (64-70): Visibility already specified as "public". From ad311fae1902cf3159834de6d494b46386d0cede Mon Sep 17 00:00:00 2001 From: a3d4 Date: Thu, 20 Aug 2020 02:35:15 +0200 Subject: [PATCH 071/139] Improve error coverage of syntax checker --- libsolidity/analysis/SyntaxChecker.cpp | 17 ----------------- libsolidity/analysis/SyntaxChecker.h | 2 -- scripts/error_codes.py | 8 +++++--- scripts/isolate_tests.py | 5 ++++- .../string/invalid_utf8_sequence.sol | 6 ++++++ 5 files changed, 15 insertions(+), 23 deletions(-) create mode 100644 test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index a47d522d9..670939321 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -364,23 +364,6 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node) return true; } -bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) -{ - // Report if none of the variable components in the tuple have a name (only possible via deprecated "var") - if (std::all_of( - _statement.declarations().begin(), - _statement.declarations().end(), - [](ASTPointer const& declaration) { return declaration == nullptr; } - )) - m_errorReporter.syntaxError( - 3299_error, - _statement.location(), - "The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty." - ); - - return true; -} - bool SyntaxChecker::visit(StructDefinition const& _struct) { if (_struct.members().empty()) diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index a5b786e34..52c82ff6e 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -87,8 +87,6 @@ private: bool visit(FunctionDefinition const& _function) override; bool visit(FunctionTypeName const& _node) override; - bool visit(VariableDeclarationStatement const& _statement) override; - bool visit(StructDefinition const& _struct) override; bool visit(Literal const& _literal) override; diff --git a/scripts/error_codes.py b/scripts/error_codes.py index c834b3060..fde0de648 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -12,8 +12,10 @@ SOURCE_FILE_PATTERN = r"\b\d+_error\b" def read_file(file_name): content = None + _, tail = path.split(file_name) + is_latin = tail == "invalid_utf8_sequence.sol" try: - with open(file_name, "r", encoding=ENCODING) as f: + with open(file_name, "r", encoding="latin-1" if is_latin else ENCODING) as f: content = f.read() finally: if content == None: @@ -219,12 +221,12 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): old_source_only_ids = { "1054", "1123", "1133", "1220", "1584", "1823", "1950", "1957", "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", - "3263", "3299", "3356", "3441", "3682", "3876", + "3263", "3356", "3441", "3682", "3876", "3893", "3997", "4010", "4802", "4805", "4828", "4904", "4990", "5052", "5073", "5170", "5188", "5272", "5333", "5347", "5473", "5622", "6041", "6052", "6272", "6708", "6792", "6931", "7110", "7128", "7186", "7319", "7589", "7593", "7653", "7812", "7885", "8065", "8084", "8140", - "8261", "8312", "8452", "8592", "8758", "9011", + "8261", "8312", "8592", "8758", "9011", "9085", "9390", "9440", "9547", "9551", "9615", "9980" } new_source_only_ids = source_only_ids - old_source_only_ids diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 16648b26b..33fdc0165 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -10,7 +10,7 @@ import sys import re import os import hashlib -from os.path import join, isfile +from os.path import join, isfile, split def extract_test_cases(path): lines = open(path, encoding="utf8", errors='ignore', mode='r').read().splitlines() @@ -99,5 +99,8 @@ if __name__ == '__main__': if 'compilationTests' in subdirs: subdirs.remove('compilationTests') for f in files: + _, tail = split(f) + if tail == "invalid_utf8_sequence.sol": + continue # ignore the test with broken utf-8 encoding path = join(root, f) extract_and_write(f, path) diff --git a/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol b/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol new file mode 100644 index 000000000..f5d5077f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol @@ -0,0 +1,6 @@ +contract C { + string s = unicode"À"; +} +// ---- +// SyntaxError 8452: (28-38): Invalid UTF-8 sequence found +// TypeError 7407: (28-38): Type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type string storage ref. From 0ee4a85a841f6494c16a93bd60f3a9ec1a7f2c6e Mon Sep 17 00:00:00 2001 From: a3d4 Date: Thu, 20 Aug 2020 16:07:04 +0200 Subject: [PATCH 072/139] Complete test coverage for errors related to parser error recovery --- liblangutil/ParserBase.cpp | 12 +++++------- libsolidity/parsing/Parser.cpp | 2 +- scripts/error_codes.py | 2 +- .../errorRecoveryTests/recovery_failed_eos.sol | 10 ++++++++++ 4 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index 19c4d187c..089f606fa 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -94,6 +94,8 @@ void ParserBase::expectToken(Token _value, bool _advance) void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentNodeName, bool _advance) { + solAssert(m_inParserRecovery, "The function is supposed to be called during parser recovery only."); + Token tok = m_scanner->currentToken(); if (tok != _value) { @@ -103,24 +105,20 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN m_scanner->next(); string const expectedToken = ParserBase::tokenName(_value); - string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; if (m_scanner->currentToken() == Token::EOS) { // rollback to where the token started, and raise exception to be caught at a higher level. m_scanner->setPosition(static_cast(startPosition)); - m_inParserRecovery = true; + string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; fatalParserError(1957_error, errorLoc, msg); } else { - if (m_inParserRecovery) - parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); - else - parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken); + parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); m_inParserRecovery = false; } } - else if (m_inParserRecovery) + else { string expectedToken = ParserBase::tokenName(_value); parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 47be4039a..90fb256e2 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1106,7 +1106,7 @@ ASTPointer Parser::parseBlock(ASTPointer const& _docString) BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ m_inParserRecovery = true; } - if (m_parserErrorRecovery) + if (m_inParserRecovery) expectTokenOrConsumeUntil(Token::RBrace, "Block"); else expectToken(Token::RBrace); diff --git a/scripts/error_codes.py b/scripts/error_codes.py index fde0de648..69e452b2a 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -219,7 +219,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): return False old_source_only_ids = { - "1054", "1123", "1133", "1220", "1584", "1823", "1950", "1957", + "1123", "1133", "1220", "1584", "1823", "1950", "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", "3263", "3356", "3441", "3682", "3876", "3893", "3997", "4010", "4802", "4805", "4828", diff --git a/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol b/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol new file mode 100644 index 000000000..67c4cf7b4 --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.0.0; + +contract Error7 { + constructor() { + a = +// ---- +// ParserError 6933: (76-76): Expected primary expression. +// ParserError 1957: (76-76): In Statement, ';'is expected; got end of source instead. +// ParserError 1957: (76-76): In Block, '}'is expected; got end of source instead. +// ParserError 1957: (76-76): In ContractDefinition, '}'is expected; got end of source instead. From 3f97a1012af4afc3d4f88f2f2c81b88014ebd337 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Tue, 18 Aug 2020 19:25:36 +0200 Subject: [PATCH 073/139] [SMTChecker] Supporting conditional operator --- Changelog.md | 1 + libsolidity/formal/BMC.cpp | 18 +++++++++++++ libsolidity/formal/BMC.h | 1 + libsolidity/formal/SMTEncoder.cpp | 21 +++++++++++++++ libsolidity/formal/SMTEncoder.h | 1 + .../operators/conditional_assignment_1.sol | 10 +++++++ .../operators/conditional_assignment_2.sol | 11 ++++++++ .../operators/conditional_assignment_3.sol | 13 +++++++++ .../operators/conditional_assignment_4.sol | 25 +++++++++++++++++ .../operators/conditional_assignment_5.sol | 27 +++++++++++++++++++ .../operators/conditional_assignment_6.sol | 26 ++++++++++++++++++ .../conditional_assignment_always_false.sol | 11 ++++++++ .../conditional_assignment_always_true.sol | 12 +++++++++ .../conditional_assignment_function_1.sol | 13 +++++++++ .../conditional_assignment_function_2.sol | 19 +++++++++++++ ...ditional_assignment_nested_always_true.sol | 11 ++++++++ .../conditional_assignment_nested_unsafe.sol | 10 +++++++ .../conditional_assignment_statevar_1.sol | 13 +++++++++ .../types/mapping_as_local_var_1.sol | 1 - 19 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol diff --git a/Changelog.md b/Changelog.md index 59ce52435..e90c0b6da 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. + * SMTChecker: Support conditional operator. Bugfixes: * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 74f814e00..568b2f326 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -198,6 +198,24 @@ bool BMC::visit(IfStatement const& _node) return false; } +bool BMC::visit(Conditional const& _op) +{ + m_context.pushSolver(); + _op.condition().accept(*this); + + if (isRootFunction()) + addVerificationTarget( + VerificationTarget::Type::ConstantCondition, + expr(_op.condition()), + &_op.condition() + ); + m_context.popSolver(); + + SMTEncoder::visit(_op); + + return false; +} + // Here we consider the execution of two branches: // Branch 1 assumes the loop condition to be true and executes the loop once, // after resetting touched variables. diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 324595bba..bd86d7360 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -84,6 +84,7 @@ private: bool visit(FunctionDefinition const& _node) override; void endVisit(FunctionDefinition const& _node) override; bool visit(IfStatement const& _node) override; + bool visit(Conditional const& _node) override; bool visit(WhileStatement const& _node) override; bool visit(ForStatement const& _node) override; void endVisit(UnaryOperation const& _node) override; diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index f9ae5794e..730b42bf2 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -579,6 +579,27 @@ void SMTEncoder::endVisit(BinaryOperation const& _op) ); } +bool SMTEncoder::visit(Conditional const& _op) +{ + _op.condition().accept(*this); + + auto indicesEndTrue = visitBranch(&_op.trueExpression(), expr(_op.condition())); + auto touchedVars = touchedVariables(_op.trueExpression()); + + auto indicesEndFalse = visitBranch(&_op.falseExpression(), !expr(_op.condition())); + touchedVars += touchedVariables(_op.falseExpression()); + + mergeVariables(touchedVars, expr(_op.condition()), indicesEndTrue, indicesEndFalse); + + defineExpr(_op, smtutil::Expression::ite( + expr(_op.condition()), + expr(_op.trueExpression()), + expr(_op.falseExpression()) + )); + + return false; +} + void SMTEncoder::endVisit(FunctionCall const& _funCall) { auto functionCallKind = *_funCall.annotation().kind; diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index ec097a2c9..4eb5ce965 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -83,6 +83,7 @@ protected: void endVisit(UnaryOperation const& _node) override; bool visit(BinaryOperation const& _node) override; void endVisit(BinaryOperation const& _node) override; + bool visit(Conditional const& _node) override; void endVisit(FunctionCall const& _node) override; bool visit(ModifierInvocation const& _node) override; void endVisit(Identifier const& _node) override; diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol new file mode 100644 index 000000000..f01fb6029 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b) public pure { + uint a = b ? 2 : 3; + assert(a > 2); + } +} +// ---- +// Warning 6328: (104-117): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol new file mode 100644 index 000000000..99ee3c2a0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint b) public pure { + require(b < 3); + uint c = (b > 0) ? b++ : ++b; + assert(c == 0); + } +} +// ---- +// Warning 6328: (132-146): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol new file mode 100644 index 000000000..12c580e24 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a, uint b) public pure { + require(a < 10); + require(b <= a); + + uint c = (b > 4) ? a++ : b++; + assert(c > a); + } +} +// ---- +// Warning 6328: (161-174): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol new file mode 100644 index 000000000..87b48e982 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() public { + x = 3; + } +} +// ---- +// Warning 2072: (273-279): Unused local variable. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol new file mode 100644 index 000000000..675ae3eb5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + d.d(); + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() public { + x = 3; + } +} +// ---- +// Warning 2072: (288-294): Unused local variable. +// Warning 6328: (318-342): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol new file mode 100644 index 000000000..d1fe62aac --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + d.d(); + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() internal { + x = 3; + } +} +// ---- +// Warning 2072: (288-294): Unused local variable. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol new file mode 100644 index 000000000..7958e0237 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint b) public pure returns (uint d) { + require(b < 10); + uint c = b < 5 ? 5 : 1; + d = c > 5 ? 3 : 2; + } +} +// ---- +// Warning 6838: (148-153): Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol new file mode 100644 index 000000000..705ca9c2b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b) public pure { + require(b); + uint c = b ? 5 : 1; + assert(c < 5); + } +} +// ---- +// Warning 6328: (118-131): Assertion violation happens here +// Warning 6838: (105-106): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol new file mode 100644 index 000000000..890db61ca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) internal pure returns (bool b) { + b = a > 5; + } + function g(uint a) public pure { + uint c = f(a) ? 3 : 4; + assert(c > 5); + } +} +// ---- +// Warning 6328: (203-216): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol new file mode 100644 index 000000000..a934a273c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) internal pure returns (uint b) { + require(a < 1000); + return a * a; + } + function g(uint a) internal pure returns (uint b) { + require(a < 1000); + return a + 100; + } + function h(uint a) public pure { + uint c = a < 5 ? g(a) : f(a); + assert(c >= 25); + assert(c < 20); + } +} +// ---- +// Warning 6328: (378-392): Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol new file mode 100644 index 000000000..cd85df0c4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b1, bool b2) public pure { + require(b1 || b2); + uint c = b1 ? 3 : (b2 ? 2 : 1); + assert(c > 1); + } +} +// ---- +// Warning 6838: (147-149): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol new file mode 100644 index 000000000..0dabdf0f6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b1, bool b2) public pure { + uint c = b1 ? 3 : (b2 ? 2 : 1); + assert(c > 1); + } +} +// ---- +// Warning 6328: (141-154): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol new file mode 100644 index 000000000..07cf3fef4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + uint a; + bool b; + + function f() public returns(uint c) { + c = b ? a + 1 : a--; + assert(c > a); + } +} +// ---- +// Warning 2661: (129-134): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol index ec8c795a5..e99eea071 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol @@ -18,4 +18,3 @@ contract c { // ---- // Warning 6328: (288-324): Assertion violation happens here // Warning 6328: (336-372): Assertion violation happens here -// Warning 6031: (166-178): Internal error: Expression undefined for SMT solver. From 00e765f4787a45470212e06d25daa8436d1a7855 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Sat, 22 Aug 2020 07:52:55 +0200 Subject: [PATCH 074/139] Fix tests for conditional operator on latest develop. --- .../operators/conditional_assignment_function_2.sol | 2 +- .../operators/conditional_assignment_statevar_1.sol | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol index a934a273c..47ec79ab5 100644 --- a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol @@ -16,4 +16,4 @@ contract C { } } // ---- -// Warning 6328: (378-392): Assertion violation happens here. +// Warning 6328: (378-392): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol index 07cf3fef4..3c402fceb 100644 --- a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol @@ -10,4 +10,3 @@ contract C { } } // ---- -// Warning 2661: (129-134): Overflow (resulting value larger than 2**256 - 1) happens here From 0b97cf1154592a69b15590f40694b78a6345901f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 24 Aug 2020 20:11:02 +0200 Subject: [PATCH 075/139] Move asan builds and tests to nightly. --- .circleci/config.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b0197625..f5ab32ccc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -893,13 +893,6 @@ workflows: - t_ubu_release_cli: *workflow_ubuntu2004_release - t_ubu_release_soltest: *workflow_ubuntu2004_release - # ASan build and tests - - b_ubu_asan: *workflow_trigger_on_tags - - b_ubu_asan_clang: *workflow_trigger_on_tags - - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan - - t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang - - t_ubu_asan_cli: *workflow_ubuntu2004_asan - # Emscripten build and selected tests - b_ems: *workflow_trigger_on_tags - t_ems_solcjs: *workflow_emscripten @@ -926,3 +919,10 @@ workflows: # Code Coverage enabled build and tests - b_ubu_codecov: *workflow_trigger_on_tags - t_ubu_codecov: *workflow_ubuntu2004_codecov + + # ASan build and tests + - b_ubu_asan: *workflow_trigger_on_tags + - b_ubu_asan_clang: *workflow_trigger_on_tags + - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan + - t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang + - t_ubu_asan_cli: *workflow_ubuntu2004_asan From e72afcba47fb09837cd86031de8982a123159ac4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Aug 2020 16:56:59 +0200 Subject: [PATCH 076/139] Fix inlining order to correspond to source order. --- Changelog.md | 1 + libyul/optimiser/FullInliner.cpp | 16 +++- .../standard_eWasm_requested/output.json | 90 ++++++++----------- test/libsolidity/gasTests/abiv2_optimised.sol | 6 +- .../fullInliner/multi_fun_callback.yul | 17 ++-- 5 files changed, 59 insertions(+), 71 deletions(-) diff --git a/Changelog.md b/Changelog.md index 06c21e128..cc92dd025 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ Bugfixes: * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. * References Resolver: Fix internal bug when using constructor for library. + * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. ### 0.7.0 (2020-07-28) diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 2e9dd1e05..cd17b7aff 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -80,10 +80,20 @@ void FullInliner::run() // TODO it might be good to determine a visiting order: // first handle functions that are called from many places. - for (auto const& fun: m_functions) + + // Note that the order of inlining can result in very different code. + // Since AST IDs and thus function names depend on whether or not a contract + // is compiled together with other source files, a change in AST IDs + // should have as little an impact as possible. This is the case + // if we handle inlining in source (and thus, for the IR generator, + // function name) order. + for (auto& statement: m_ast.statements) { - handleBlock(fun.second->name, fun.second->body); - updateCodeSize(*fun.second); + if (!holds_alternative(statement)) + continue; + FunctionDefinition& fun = std::get(statement); + handleBlock(fun.name, fun.body); + updateCodeSize(fun); } } diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index a3885d128..7b2af14bf 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -6,6 +6,9 @@ (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) (memory $memory (export \"memory\") 1) (export \"main\" (func $main)) + (global $global_ (mut i64) (i64.const 0)) + (global $global__1 (mut i64) (i64.const 0)) + (global $global__2 (mut i64) (i64.const 0)) (func $main (local $_1 i64) @@ -15,6 +18,7 @@ (local $z1 i64) (local $z2 i64) (local $z3 i64) + (local $z4 i64) (local $_3 i64) (block $label_ (local.set $_1 (i64.const 0)) @@ -28,14 +32,18 @@ (i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128))) (call $eth.getCallValue (i32.const 0)) - (local.set $z1 (call $endian_swap (i64.load (i32.const 0)))) - (local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8))))) - (local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16))))) - (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then - (call $revert (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)))) + (block + (local.set $z1 (call $mload_internal (i32.const 0))) + (local.set $z2 (global.get $global_)) + (local.set $z3 (global.get $global__1)) + (local.set $z4 (global.get $global__2)) + + ) + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (local.get $z4))))) (then + (call $eth.revert (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))))) (local.set $_3 (datasize \"C_2_deployed\")) - (call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) - (call $return (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) + (call $eth.codeCopy (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\")) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))) + (call $eth.finish (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))) ) ) @@ -46,7 +54,7 @@ (param $x4 i64) (result i32) (local $v i32) - (block $label__1 + (block $label__3 (if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then (unreachable))) (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then @@ -65,7 +73,7 @@ (result i32) (local $r i32) (local $p i32) - (block $label__2 + (block $label__4 (local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (local.set $r (i32.add (local.get $p) (i32.const 64))) (if (i32.lt_u (local.get $r) (local.get $p)) (then @@ -75,29 +83,11 @@ (local.get $r) ) -(func $codecopy - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) - (param $z1 i64) - (param $z2 i64) - (param $z3 i64) - (param $z4 i64) - (block $label__3 - (call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4))) - ) -) - (func $endian_swap_16 (param $x i64) (result i64) (local $y i64) - (block $label__4 + (block $label__5 (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) ) @@ -109,7 +99,7 @@ (result i64) (local $y i64) (local $hi i64) - (block $label__5 + (block $label__6 (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) @@ -122,7 +112,7 @@ (result i64) (local $y i64) (local $hi i64) - (block $label__6 + (block $label__7 (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) @@ -130,32 +120,24 @@ (local.get $y) ) -(func $return - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) - (block $label__7 - (call $eth.finish (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))) - ) -) - -(func $revert - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) +(func $mload_internal + (param $pos i32) + (result i64) + (local $z1 i64) + (local $z2 i64) + (local $z3 i64) + (local $z4 i64) (block $label__8 - (call $eth.revert (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))) + (local.set $z1 (call $endian_swap (i64.load (local.get $pos)))) + (local.set $z2 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 8))))) + (local.set $z3 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 16))))) + (local.set $z4 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 24))))) + ) + (global.set $global_ (local.get $z2)) + (global.set $global__1 (local.get $z3)) + (global.set $global__2 (local.get $z4)) + (local.get $z1) ) ) diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index b1bc1ff5d..74cd89a42 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 603000 -// executionCost: 638 -// totalCost: 603638 +// codeDepositCost: 616600 +// executionCost: 651 +// totalCost: 617251 // external: // a(): 1029 // b(uint256): 2084 diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index 98c8e3010..df3119103 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -36,20 +36,15 @@ // function f(x) // { // mstore(0, x) -// let t_20 := 0 -// t_20 := 2 -// mstore(7, t_20) -// g(10) +// let t_14 := 0 +// t_14 := 2 +// mstore(7, t_14) +// let x_1_15 := 10 +// f(1) // mstore(1, x) // } // function g(x_1) -// { -// let x_14 := 1 -// mstore(0, x_14) -// mstore(7, h()) -// g(10) -// mstore(1, x_14) -// } +// { f(1) } // function h() -> t // { t := 2 } // } From d99b81ef02bc7e6c90fd433d1f2f4b6fd04a272c Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 21 Aug 2020 14:27:00 +0200 Subject: [PATCH 077/139] [Sol->Yul] Enabling storage pointers to local vars in inline assembly. --- libsolidity/codegen/ir/IRGenerationContext.h | 3 +- libsolidity/codegen/ir/IRGenerator.cpp | 2 +- .../codegen/ir/IRGeneratorForStatements.cpp | 42 ++++++++++++------- libsolidity/codegen/ir/IRVariable.cpp | 11 +++++ libsolidity/codegen/ir/IRVariable.h | 4 ++ ...line_assembly_storage_access_local_var.sol | 18 ++++++++ 6 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 469beea2f..a833fdfa5 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -98,8 +98,9 @@ public: void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset); bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); } - std::pair storageLocationOfVariable(VariableDeclaration const& _varDecl) const + std::pair storageLocationOfStateVariable(VariableDeclaration const& _varDecl) const { + solAssert(isStateVariable(_varDecl), ""); return m_stateVariables.at(&_varDecl); } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index f7f66b4f5..8fb932d01 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -305,7 +305,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) string code; - auto const& location = m_context.storageLocationOfVariable(_varDecl); + auto const& location = m_context.storageLocationOfStateVariable(_varDecl); code += Whiskers(R"( let slot := let offset := diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 846a93167..3ab3c8888 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -75,18 +75,30 @@ struct CopyTranslate: public yul::ASTCopier { solAssert(reference.isOffset != reference.isSlot, ""); - pair slot_offset = m_context.storageLocationOfVariable(*varDecl); + string value; + if (varDecl->isStateVariable()) + value = + reference.isSlot ? + m_context.storageLocationOfStateVariable(*varDecl).first.str() : + to_string(m_context.storageLocationOfStateVariable(*varDecl).second); + else + { + solAssert(varDecl->isLocalVariable(), ""); + if (reference.isSlot) + value = IRVariable{*varDecl}.part("slot").name(); + else if (varDecl->type()->isValueType()) + value = IRVariable{*varDecl}.part("offset").name(); + else + { + solAssert(!IRVariable{*varDecl}.hasPart("offset"), ""); + value = "0"; + } + } - string const value = reference.isSlot ? - slot_offset.first.str() : - to_string(slot_offset.second); - - return yul::Literal{ - _identifier.location, - yul::LiteralKind::Number, - yul::YulString{value}, - {} - }; + if (isdigit(value.front())) + return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}}; + else + return yul::Identifier{_identifier.location, yul::YulString{value}}; } } return ASTCopier::operator()(_identifier); @@ -152,8 +164,8 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va _varDecl.immutable() ? IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ - util::toCompactHexWithPrefix(m_context.storageLocationOfVariable(_varDecl).first), - m_context.storageLocationOfVariable(_varDecl).second + util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), + m_context.storageLocationOfStateVariable(_varDecl).second }}, *_varDecl.value() ); @@ -2026,8 +2038,8 @@ void IRGeneratorForStatements::handleVariableReference( setLValue(_referencingExpression, IRLValue{ *_variable.annotation().type, IRLValue::Storage{ - toCompactHexWithPrefix(m_context.storageLocationOfVariable(_variable).first), - m_context.storageLocationOfVariable(_variable).second + toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first), + m_context.storageLocationOfStateVariable(_variable).second } }); else diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp index 3fce5288a..92ec87da6 100644 --- a/libsolidity/codegen/ir/IRVariable.cpp +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -53,6 +53,17 @@ IRVariable IRVariable::part(string const& _name) const solAssert(false, "Invalid stack item name: " + _name); } +bool IRVariable::hasPart(std::string const& _name) const +{ + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemName == _name) + { + solAssert(itemName.empty() || itemType, ""); + return true; + } + return false; +} + vector IRVariable::stackSlots() const { vector result; diff --git a/libsolidity/codegen/ir/IRVariable.h b/libsolidity/codegen/ir/IRVariable.h index 71413cc38..1f403a07d 100644 --- a/libsolidity/codegen/ir/IRVariable.h +++ b/libsolidity/codegen/ir/IRVariable.h @@ -71,6 +71,10 @@ public: /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. IRVariable part(std::string const& _slot) const; + /// @returns true if variable contains @a _name component + /// @a _name name of the component that is being checked + bool hasPart(std::string const& _name) const; + /// @returns a vector containing the names of the stack slots of the variable. std::vector stackSlots() const; diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol new file mode 100644 index 000000000..03f334658 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol @@ -0,0 +1,18 @@ +contract C { + uint256[] public a; + + function f() public returns (uint256) { + uint256[] storage x = a; + uint256 off; + assembly { + sstore(x.slot, 7) + off := x.offset + } + assert(off == 0); + return a.length; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 7 From 11a7763f492411d1fb77841caa48fd918dca64d4 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Sun, 23 Aug 2020 21:21:45 +0200 Subject: [PATCH 078/139] [SMTChecker] Support bitwise or, xor and not. --- Changelog.md | 1 + libsmtutil/CVC4Interface.cpp | 6 +++ libsmtutil/SolverInterface.h | 18 +++++++++ libsmtutil/Z3Interface.cpp | 6 +++ libsolidity/formal/SMTEncoder.cpp | 39 +++++++++---------- libsolidity/formal/SMTEncoder.h | 1 + libsolidity/formal/SymbolicTypes.cpp | 12 ++++++ libsolidity/formal/SymbolicTypes.h | 2 + .../operators/bitwise_combo.sol | 12 ++++++ .../operators/bitwise_not_fixed_bytes.sol | 8 ++++ .../operators/bitwise_not_int.sol | 21 ++++++++++ .../operators/bitwise_not_uint.sol | 15 +++++++ .../operators/bitwise_or_fixed_bytes.sol | 9 +++++ .../operators/bitwise_or_int.sol | 21 ++++++++++ .../operators/bitwise_or_uint.sol | 17 ++++++++ .../operators/bitwise_xor_fixed_bytes.sol | 1 - .../operators/bitwise_xor_int.sol | 19 +++++++++ .../operators/bitwise_xor_uint.sol | 17 ++++++++ 18 files changed, 203 insertions(+), 22 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol diff --git a/Changelog.md b/Changelog.md index 64c2de607..8a43bce46 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. * SMTChecker: Support conditional operator. + * SMTChecker: Support bitwise or, xor and not operators. Bugfixes: * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. diff --git a/libsmtutil/CVC4Interface.cpp b/libsmtutil/CVC4Interface.cpp index 2a795d827..40526306f 100644 --- a/libsmtutil/CVC4Interface.cpp +++ b/libsmtutil/CVC4Interface.cpp @@ -188,8 +188,14 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]); else if (n == "mod") return m_context.mkExpr(CVC4::kind::INTS_MODULUS, arguments[0], arguments[1]); + else if (n == "bvnot") + return m_context.mkExpr(CVC4::kind::BITVECTOR_NOT, arguments[0]); else if (n == "bvand") return m_context.mkExpr(CVC4::kind::BITVECTOR_AND, arguments[0], arguments[1]); + else if (n == "bvor") + return m_context.mkExpr(CVC4::kind::BITVECTOR_OR, arguments[0], arguments[1]); + else if (n == "bvxor") + return m_context.mkExpr(CVC4::kind::BITVECTOR_XOR, arguments[0], arguments[1]); else if (n == "int2bv") { size_t size = std::stoul(_expr.arguments[1].name); diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index d4c4d7fef..c45ebd77b 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -95,7 +95,10 @@ public: {"*", 2}, {"/", 2}, {"mod", 2}, + {"bvnot", 1}, {"bvand", 2}, + {"bvor", 2}, + {"bvxor", 2}, {"int2bv", 2}, {"bv2int", 1}, {"select", 2}, @@ -286,11 +289,26 @@ public: auto intSort = _a.sort; return Expression("mod", {std::move(_a), std::move(_b)}, intSort); } + friend Expression operator~(Expression _a) + { + auto bvSort = _a.sort; + return Expression("bvnot", {std::move(_a)}, bvSort); + } friend Expression operator&(Expression _a, Expression _b) { auto bvSort = _a.sort; return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort); } + friend Expression operator^(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort); + } + friend Expression operator|(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort); + } Expression operator()(std::vector _arguments) const { smtAssert( diff --git a/libsmtutil/Z3Interface.cpp b/libsmtutil/Z3Interface.cpp index 501446553..909a5cde2 100644 --- a/libsmtutil/Z3Interface.cpp +++ b/libsmtutil/Z3Interface.cpp @@ -181,8 +181,14 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] / arguments[1]; else if (n == "mod") return z3::mod(arguments[0], arguments[1]); + else if (n == "bvnot") + return ~arguments[0]; else if (n == "bvand") return arguments[0] & arguments[1]; + else if (n == "bvor") + return arguments[0] | arguments[1]; + else if (n == "bvxor") + return arguments[0] ^ arguments[1]; else if (n == "int2bv") { size_t size = std::stoul(_expr.arguments[1].name); diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 53f390902..7088f485d 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -456,6 +456,8 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) void SMTEncoder::endVisit(UnaryOperation const& _op) { + if (TokenTraits::isBitOp(_op.getOperator())) + return bitwiseNotOperation(_op); if (_op.annotation().type->category() == Type::Category::RationalNumber) return; @@ -1406,20 +1408,7 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) auto commonType = _op.annotation().commonType; solAssert(commonType, ""); - unsigned bvSize = 256; - bool isSigned = false; - if (auto const* intType = dynamic_cast(commonType)) - { - bvSize = intType->numBits(); - isSigned = intType->isSigned(); - } - else if (auto const* fixedType = dynamic_cast(commonType)) - { - bvSize = fixedType->numBits(); - isSigned = fixedType->isSigned(); - } - else if (auto const* fixedBytesType = dynamic_cast(commonType)) - bvSize = fixedBytesType->numBytes() * 8; + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(commonType); auto bvLeft = smtutil::Expression::int2bv(expr(_op.leftExpression(), commonType), bvSize); auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize); @@ -1427,18 +1416,26 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) optional result; if (_op.getOperator() == Token::BitAnd) result = bvLeft & bvRight; - // TODO implement the other operators - else - m_errorReporter.warning( - 1093_error, - _op.location(), - "Assertion checker does not yet implement this bitwise operator." - ); + else if (_op.getOperator() == Token::BitOr) + result = bvLeft | bvRight; + else if (_op.getOperator() == Token::BitXor) + result = bvLeft ^ bvRight; + solAssert(result, ""); if (result) defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned)); } +void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op) +{ + solAssert(_op.getOperator() == Token::BitNot, ""); + + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_op.annotation().type); + + auto bvOperand = smtutil::Expression::int2bv(expr(_op.subExpression(), _op.annotation().type), bvSize); + defineExpr(_op, smtutil::Expression::bv2int(~bvOperand, isSigned)); +} + smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Expression _right, IntegerType const& _type) { // Signed division in SMTLIB2 rounds differently for negative division. diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 4eb5ce965..122c8544f 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -115,6 +115,7 @@ protected: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); void bitwiseOperation(BinaryOperation const& _op); + void bitwiseNotOperation(UnaryOperation const& _op); void initContract(ContractDefinition const& _contract); void initFunction(FunctionDefinition const& _function); diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 568368054..965130bb7 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -432,6 +432,18 @@ smtutil::Expression zeroValue(frontend::TypePointer const& _type) return 0; } +pair typeBvSizeAndSignedness(frontend::TypePointer const& _type) +{ + if (auto const* intType = dynamic_cast(_type)) + return {intType->numBits(), intType->isSigned()}; + else if (auto const* fixedType = dynamic_cast(_type)) + return {fixedType->numBits(), fixedType->isSigned()}; + else if (auto const* fixedBytesType = dynamic_cast(_type)) + return {fixedBytesType->numBytes() * 8, false}; + else + solAssert(false, ""); +} + void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context) { setSymbolicUnknownValue(_variable.currentValue(), _variable.type(), _context); diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index 401c9f0b6..8e08783d8 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -67,6 +67,8 @@ smtutil::Expression minValue(frontend::IntegerType const& _type); smtutil::Expression maxValue(frontend::IntegerType const& _type); smtutil::Expression zeroValue(frontend::TypePointer const& _type); +std::pair typeBvSizeAndSignedness(frontend::TypePointer const& type); + void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _context); void setSymbolicZeroValue(smtutil::Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context); void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context); diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol new file mode 100644 index 000000000..dc1d8f206 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 0xff; + uint8 y = ~x; + assert(x & y == 0); + assert(x | y == 0xff); + assert(x ^ y == 0xff); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol new file mode 100644 index 000000000..1431fb248 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (byte) { + return (~byte(0xFF)); + } +} +// ---- +// Warning 5084: (102-112): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol new file mode 100644 index 000000000..d879f8ee7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int16 x = 1; + assert(~x == 0); + x = 0xff; + assert(~x == 0); + x = 0x0f; + assert(~x == 0xf0); + x = -1; + assert(~x != 0); + x = -2; + assert(~x == 1); + } +} +// ---- +// Warning 6328: (91-106): Assertion violation happens here +// Warning 6328: (122-137): Assertion violation happens here +// Warning 6328: (153-171): Assertion violation happens here +// Warning 6328: (185-200): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol new file mode 100644 index 000000000..8b733de18 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 0xff; + assert(~x == 0x00); + uint16 y = 0xff00; + assert(~y == 0xff); + assert(~y == 0xffff); + assert(~y == 0x0000); + } +} +// ---- +// Warning 6328: (159-179): Assertion violation happens here +// Warning 6328: (183-203): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol new file mode 100644 index 000000000..4dad0286e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (byte) { + return (byte(0x0F) | (byte(0xF0))); + } +} +// ---- +// Warning 5084: (101-111): Type conversion is not yet fully supported and might yield false positives. +// Warning 5084: (115-125): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol new file mode 100644 index 000000000..140c255c6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int16 x = 1; + int16 y = 0; + assert(x | y == 1); + x = 0; y = 0; + assert(x | y != 0); + y = 240; + x = 15; + int16 z = x | y; + assert(z == 255); + x = -1; y = 200; + assert(x | y == x); + assert(x | z != -1); + } +} +// ---- +// Warning 6328: (144-162): Assertion violation happens here +// Warning 6328: (267-286): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol new file mode 100644 index 000000000..f93c59a69 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 1; + uint16 y = 0; + assert(x | y != 0); + x = 0xff; + y = 0xff00; + assert(x | y == 0xff); + assert(x | y == 0xffff); + assert(x | y == 0x0000); + } +} +// ---- +// Warning 6328: (155-176): Assertion violation happens here +// Warning 6328: (207-230): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol index 3852563be..69cea474f 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol @@ -6,4 +6,3 @@ contract Simp { } } // ---- -// Warning 1093: (142-152): Assertion checker does not yet implement this bitwise operator. diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol new file mode 100644 index 000000000..87bdd7f8d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int8 x = 1; + int16 y = 0; + assert(x ^ y == 1); + int16 z = -1; + assert(x ^ z == -2); + assert(y ^ z == -1); + assert(y ^ z > 0); + x = 7; y = 3; + assert(x ^ y < 5); + assert(x ^ y > 5); + } +} +// ---- +// Warning 6328: (189-206): Assertion violation happens here +// Warning 6328: (247-264): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol new file mode 100644 index 000000000..70ae0eac6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 1; + uint16 y = 0; + assert(x ^ y != 0); + x = 0xff; + y = 0xff00; + assert(x ^ y == 0xff); + assert(x ^ y == 0xffff); + assert(x ^ y == 0x0000); + } +} +// ---- +// Warning 6328: (155-176): Assertion violation happens here +// Warning 6328: (207-230): Assertion violation happens here From 79b2e41252a96f3f077ae2eeaaf873487aa0ce90 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Wed, 26 Aug 2020 21:25:22 +0200 Subject: [PATCH 079/139] Fix inheritdoc tags trying to resolve 'empty' elements --- libsolidity/analysis/ReferencesResolver.cpp | 11 +++++++++++ .../natspec/invalid/docstring_inheritdoc_emptys.sol | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 208749de9..75445f229 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -325,6 +325,17 @@ void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _docum vector path; boost::split(path, name, boost::is_any_of(".")); + if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); })) + { + m_errorReporter.docstringParsingError( + 5967_error, + _documentation.location(), + "Documentation tag @inheritdoc reference \"" + + name + + "\" is malformed." + ); + return; + } Declaration const* result = m_resolver.pathFromCurrentScope(path); if (result == nullptr) diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol index e518021b4..d2c41cd2c 100644 --- a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol @@ -2,6 +2,18 @@ contract C { /// @inheritdoc function f() internal { } + /// @inheritdoc . + function f() internal { + } + /// @inheritdoc C..f + function f() internal { + } + /// @inheritdoc C. + function f() internal { + } } // ---- // DocstringParsingError 1933: (17-32): Expected contract name following documentation tag @inheritdoc. +// DocstringParsingError 5967: (71-88): Documentation tag @inheritdoc reference "." is malformed. +// DocstringParsingError 5967: (127-147): Documentation tag @inheritdoc reference "C..f" is malformed. +// DocstringParsingError 5967: (186-204): Documentation tag @inheritdoc reference "C." is malformed. From 6f228991533b421bae50c71855896ba933e91a74 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Wed, 26 Aug 2020 20:50:13 +0200 Subject: [PATCH 080/139] Disallow the exponent to be a signed integer literal --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 2 ++ .../nameAndTypeResolution/201_integer_signed_exp_signed.sol | 2 ++ 3 files changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 2557da9de..2ce326511 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * SMTChecker: Fix internal error on fixed bytes index access. * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. + * Type Checker: Disallow signed literals as exponent in exponentiation operator. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 956412425..3199390a5 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -712,6 +712,8 @@ TypeResult IntegerType::binaryOperatorResult(Token _operator, Type const* _other return TypeResult::err("Exponent is fractional."); if (!rationalNumberType->integerType()) return TypeResult::err("Exponent too large."); + if (rationalNumberType->isNegative()) + return TypeResult::err("Exponentiation power is not allowed to be a negative integer literal."); } return this; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol index 17508ed8d..188dda39c 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol @@ -1,8 +1,10 @@ contract test { function f() public { int x = 3; int y = 4; x ** y; } function h() public { uint8 x = 3; int16 y = 4; x ** y; } + function i() public { int16 x = 4; x ** -3; } } // ---- // TypeError 2271: (64-70): Operator ** not compatible with types int256 and int256. Exponentiation power is not allowed to be a signed integer type. // TypeError 2271: (126-132): Operator ** not compatible with types uint8 and int16. Exponentiation power is not allowed to be a signed integer type. // Warning 3149: (126-132): The result type of the exponentiation operation is equal to the type of the first operand (uint8) ignoring the (larger) type of the second operand (int16) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other. +// TypeError 2271: (175-182): Operator ** not compatible with types int16 and int_const -3. Exponentiation power is not allowed to be a negative integer literal. From 4e5b403c37ba1c741f47fdfbfc5b61d8a5a499c7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 12 Aug 2020 18:56:24 +0100 Subject: [PATCH 081/139] Introduce RightArrow (->) token in the scanner --- Changelog.md | 1 + docs/grammar/SolidityLexer.g4 | 5 ++--- liblangutil/Scanner.cpp | 4 +++- liblangutil/Token.h | 1 + libyul/AsmParser.cpp | 5 ++--- test/liblangutil/Scanner.cpp | 6 ++---- .../inline_assembly_embedded_function_call.sol | 2 +- .../inlineAssembly/function_definition_whitespace.sol | 4 +++- .../yulSyntaxTests/function_definition_whitespace.yul | 4 +++- 9 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2557da9de..1f4e925be 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,7 @@ Compiler Features: Bugfixes: * AST: Remove ``null`` member values also when the compiler is used in standard-json-mode. * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. + * Scanner: Fix bug where whitespace would be allowed within the ``->`` token (e.g. ``function f() - > x {}`` becomes invalid in inline assembly and Yul). * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index 441e1f8fd..ad5f89bd5 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -112,6 +112,7 @@ Semicolon: ';'; Period: '.'; Conditional: '?'; Arrow: '=>'; +RightArrow: '->'; Assign: '='; AssignBitOr: '|='; @@ -283,9 +284,7 @@ YulRParen: ')'; YulAssign: ':='; YulPeriod: '.'; YulComma: ','; -// TODO: remove whitespace workaround once the parser disallows it. -//@doc:name -> -YulArrow: '->' | '-' YulWS+ '>'; +YulArrow: '->'; /** * Yul identifiers consist of letters, dollar signs, underscores and numbers, but may not start with a number. diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 017a65e18..f6902b2de 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -563,12 +563,14 @@ void Scanner::scanToken() token = Token::Add; break; case '-': - // - -- -= + // - -- -= -> advance(); if (m_char == '-') token = selectToken(Token::Dec); else if (m_char == '=') token = selectToken(Token::AssignSub); + else if (m_char == '>') + token = selectToken(Token::RightArrow); else token = Token::Sub; break; diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 366c354cd..721fe1582 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -84,6 +84,7 @@ namespace solidity::langutil T(Period, ".", 0) \ T(Conditional, "?", 3) \ T(Arrow, "=>", 0) \ + T(RightArrow, "->", 0) \ \ /* Assignment operators. */ \ /* IsAssignmentOp() relies on this block of enum values being */ \ diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 2c7aa1f93..83f17a0d3 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -401,10 +401,9 @@ FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::Comma); } expectToken(Token::RParen); - if (currentToken() == Token::Sub) + if (currentToken() == Token::RightArrow) { - expectToken(Token::Sub); - expectToken(Token::GreaterThan); + expectToken(Token::RightArrow); while (true) { funDef.returnVariables.emplace_back(parseTypedName()); diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index 6aca4c849..c949c3c5d 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -782,8 +782,7 @@ BOOST_AUTO_TEST_CASE(yul_function) BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); - BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); - BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::RightArrow); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -797,8 +796,7 @@ BOOST_AUTO_TEST_CASE(yul_function) BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); - BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); - BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::RightArrow); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol index 98a6039d5..63e9f0a2d 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol @@ -5,7 +5,7 @@ contract C { function asmfun(a, b, c) -> x, y, z { x := g(a) - function g(r) - > s { + function g(r) -> s { s := mul(r, r) } y := g(b) diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol index d336c2325..d3c56f3e2 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol @@ -5,4 +5,6 @@ contract C { } } } -} \ No newline at end of file +} +// ---- +// ParserError 2314: (87-88): Expected '{' but got '-' diff --git a/test/libyul/yulSyntaxTests/function_definition_whitespace.yul b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul index fc1035507..1c397982b 100644 --- a/test/libyul/yulSyntaxTests/function_definition_whitespace.yul +++ b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul @@ -1,4 +1,6 @@ { function f (a, b , c ) - > y,x,z { } -} \ No newline at end of file +} +// ---- +// ParserError 2314: (30-31): Expected '{' but got '-' From 662f7d8fac331178fdc920e6c53d0474279ce622 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Aug 2020 11:42:00 +0100 Subject: [PATCH 082/139] Rename Arrow to DoubleArrow --- docs/grammar/Solidity.g4 | 2 +- docs/grammar/SolidityLexer.g4 | 2 +- liblangutil/Scanner.cpp | 2 +- liblangutil/Token.h | 2 +- libsolidity/parsing/Parser.cpp | 2 +- test/liblangutil/Scanner.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/grammar/Solidity.g4 b/docs/grammar/Solidity.g4 index 626ffc87b..abf536cde 100644 --- a/docs/grammar/Solidity.g4 +++ b/docs/grammar/Solidity.g4 @@ -433,7 +433,7 @@ variableDeclarationTuple: variableDeclarationStatement: ((variableDeclaration (Assign expression)?) | (variableDeclarationTuple Assign expression)) Semicolon; expressionStatement: expression Semicolon; -mappingType: Mapping LParen key=mappingKeyType Arrow value=typeName RParen; +mappingType: Mapping LParen key=mappingKeyType DoubleArrow value=typeName RParen; /** * Only elementary types or user defined types are viable as mapping keys. */ diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index ad5f89bd5..c70a99d91 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -111,7 +111,7 @@ Colon: ':'; Semicolon: ';'; Period: '.'; Conditional: '?'; -Arrow: '=>'; +DoubleArrow: '=>'; RightArrow: '->'; Assign: '='; diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index f6902b2de..0b17a0883 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -540,7 +540,7 @@ void Scanner::scanToken() if (m_char == '=') token = selectToken(Token::Equal); else if (m_char == '>') - token = selectToken(Token::Arrow); + token = selectToken(Token::DoubleArrow); else token = Token::Assign; break; diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 721fe1582..53d8131f1 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -83,7 +83,7 @@ namespace solidity::langutil T(Semicolon, ";", 0) \ T(Period, ".", 0) \ T(Conditional, "?", 3) \ - T(Arrow, "=>", 0) \ + T(DoubleArrow, "=>", 0) \ T(RightArrow, "->", 0) \ \ /* Assignment operators. */ \ diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 47be4039a..d36a0f98e 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1050,7 +1050,7 @@ ASTPointer Parser::parseMapping() } else fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type"); - expectToken(Token::Arrow); + expectToken(Token::DoubleArrow); ASTPointer valueType = parseTypeName(); nodeFactory.markEndPosition(); expectToken(Token::RParen); diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index c949c3c5d..c2f926389 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_CHECK_EQUAL(scanner.next(), Token::AssignAdd); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Inc); - BOOST_CHECK_EQUAL(scanner.next(), Token::Arrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::DoubleArrow); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); BOOST_CHECK_EQUAL(scanner.next(), Token::SAR); BOOST_CHECK_EQUAL(scanner.next(), Token::AssignSar); From 1c066b105991108330577c5f281dc8f79faf6ee5 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Wed, 26 Aug 2020 20:32:27 +0200 Subject: [PATCH 083/139] Allow `type(Contract).name` for abstract contracts and interfaces --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 1 + .../metaTypes/name_other_contract.sol | 29 +++++++++++++++++++ .../metaTypes/name_other_contract.sol | 14 +++++++++ 4 files changed, 45 insertions(+) create mode 100644 test/libsolidity/semanticTests/metaTypes/name_other_contract.sol diff --git a/Changelog.md b/Changelog.md index 2ce326511..cbce12cba 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,7 @@ Bugfixes: * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. * Type Checker: Disallow signed literals as exponent in exponentiation operator. + * Allow `type(Contract).name` for abstract contracts and interfaces. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3199390a5..63c7e7221 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -4079,6 +4079,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const else return MemberList::MemberMap({ {"interfaceId", TypeProvider::fixedBytes(4)}, + {"name", TypeProvider::stringMemory()}, }); } else if (m_typeArgument->category() == Type::Category::Integer) diff --git a/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol b/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol new file mode 100644 index 000000000..069e46532 --- /dev/null +++ b/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol @@ -0,0 +1,29 @@ +abstract contract A { + function f() virtual public pure; +} + +interface I { + function f() external pure; +} + +contract C { + function f() pure public { + } +} + +contract Test { + function c() public pure returns (string memory) { + return type(C).name; + } + function a() public pure returns (string memory) { + return type(A).name; + } + function i() public pure returns (string memory) { + return type(I).name; + } +} + +// ---- +// c() -> 0x20, 1, "C" +// a() -> 0x20, 1, "A" +// i() -> 0x20, 1, "I" diff --git a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol index baa4a2286..91cd21e49 100644 --- a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol +++ b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol @@ -2,6 +2,20 @@ contract Test { function f() public pure returns (string memory) { return type(C).name; } + function g() public pure returns (string memory) { + return type(A).name; + } + function h() public pure returns (string memory) { + return type(I).name; + } +} + +abstract contract A { + function f() virtual public pure; +} + +interface I { + function f() external pure; } contract C { From 78265642262cb50a3136de024ffa0f0f52f58c3c Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Thu, 27 Aug 2020 10:00:08 +0200 Subject: [PATCH 084/139] Fix infinite loop for structs in library function parameter --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 82 ++++++++++--------- ...recursive_struct_nested_mapping_memory.sol | 10 +++ ...ecursive_struct_nested_mapping_storage.sol | 6 ++ 4 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol diff --git a/Changelog.md b/Changelog.md index cbce12cba..cf3989662 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ Bugfixes: * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. * Type Checker: Disallow signed literals as exponent in exponentiation operator. * Allow `type(Contract).name` for abstract contracts and interfaces. + * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 63c7e7221..52b206737 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2463,52 +2463,56 @@ TypeResult StructType::interfaceType(bool _inLibrary) const TypeResult result{TypePointer{}}; + if (recursive() && !(_inLibrary && location() == DataLocation::Storage)) + return TypeResult::err( + "Recursive structs can only be passed as storage pointers to libraries, " + "not as memory objects to contract functions." + ); + util::BreadthFirstSearch breadthFirstSearch{{&m_struct}}; breadthFirstSearch.run( - [&](StructDefinition const* _struct, auto&& _addChild) { - // Check that all members have interface types. - // Return an error if at least one struct member does not have a type. - // This might happen, for example, if the type of the member does not exist. - for (ASTPointer const& variable: _struct->members()) + [&](StructDefinition const* _struct, auto&& _addChild) + { + // Check that all members have interface types. + // Return an error if at least one struct member does not have a type. + // This might happen, for example, if the type of the member does not exist. + for (ASTPointer const& variable: _struct->members()) + { + // If the struct member does not have a type return false. + // A TypeError is expected in this case. + if (!variable->annotation().type) { - // If the struct member does not have a type return false. - // A TypeError is expected in this case. - if (!variable->annotation().type) + result = TypeResult::err("Invalid type!"); + breadthFirstSearch.abort(); + return; + } + + Type const* memberType = variable->annotation().type; + + while ( + memberType->category() == Type::Category::Array || + memberType->category() == Type::Category::Mapping + ) + { + if (auto arrayType = dynamic_cast(memberType)) + memberType = arrayType->finalBaseType(false); + else if (auto mappingType = dynamic_cast(memberType)) + memberType = mappingType->valueType(); + } + + if (StructType const* innerStruct = dynamic_cast(memberType)) + _addChild(&innerStruct->structDefinition()); + else + { + auto iType = memberType->interfaceType(_inLibrary); + if (!iType.get()) { - result = TypeResult::err("Invalid type!"); + solAssert(!iType.message().empty(), "Expected detailed error message!"); + result = iType; breadthFirstSearch.abort(); return; } - - Type const* memberType = variable->annotation().type; - - while (dynamic_cast(memberType)) - memberType = dynamic_cast(memberType)->baseType(); - - if (StructType const* innerStruct = dynamic_cast(memberType)) - { - if (innerStruct->recursive() && !(_inLibrary && location() == DataLocation::Storage)) - { - result = TypeResult::err( - "Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions." - ); - breadthFirstSearch.abort(); - return; - } - else - _addChild(&innerStruct->structDefinition()); - } - else - { - auto iType = memberType->interfaceType(_inLibrary); - if (!iType.get()) - { - solAssert(!iType.message().empty(), "Expected detailed error message!"); - result = iType; - breadthFirstSearch.abort(); - return; - } - } + } } } ); diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol new file mode 100644 index 000000000..6009ee94d --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol @@ -0,0 +1,10 @@ +library a { + struct b { + mapping (uint => b) c ; + } + // Segfaults in https://github.com/ethereum/solidity/issues/9443 + function d(b memory) public {} +} +// ---- +// TypeError 4103: (149-157): Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions. +// TypeError 4061: (149-157): Type struct a.b is only valid in storage because it contains a (nested) mapping. diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol new file mode 100644 index 000000000..d40a4407c --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol @@ -0,0 +1,6 @@ +library a { + struct b { + mapping (uint => b) c ; + } + function d(b storage) public {} +} From a25c666e4ad9326e974dfc67ccb50cdfa0586bec Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 26 Aug 2020 11:37:40 +0200 Subject: [PATCH 085/139] Docker: Install antlr4 deps in ossfuzz docker --- .../Dockerfile.ubuntu1604.clang.ossfuzz | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index 32f84573c..c30573fe1 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="2" +LABEL version="3" ARG DEBIAN_FRONTEND=noninteractive @@ -31,7 +31,8 @@ RUN apt-get update; \ build-essential \ software-properties-common \ ninja-build git wget \ - libbz2-dev zlib1g-dev git curl; \ + libbz2-dev zlib1g-dev git curl uuid-dev \ + pkg-config openjdk-8-jdk liblzma-dev unzip; \ apt-get install -qy python-pip python-sphinx; # Install cmake 3.14 (minimum requirement is cmake 3.10) @@ -108,6 +109,24 @@ RUN set -ex; \ ninja install/strip; \ rm -rf /usr/src/hera +# ANTLR4 RUNTIME +RUN set -ex; \ + cd /usr/src; \ + wget https://www.antlr.org/download/antlr4-cpp-runtime-4.8-source.zip; \ + rm -rf antlr4-runtime && mkdir antlr4-runtime; \ + unzip antlr4-cpp-runtime-4.8-source.zip -d antlr4-runtime; \ + cd antlr4-runtime && mkdir build && cd build; \ + cmake .. -DWITH_LIBCXX=On -DCMAKE_BUILD_TYPE=Release -DWITH_DEMO=False; \ + make -j; \ + DESTDIR=run make install; \ + # Manually copy needed library and includes since install script + # does not respect -DCMAKE_INSTALL_DIR and there is no option + # to disable shared library build/installation + cp -Rf run/usr/local/include/antlr4-runtime /usr/include; \ + cp -f run/usr/local/lib/libantlr4-runtime.a /usr/lib; \ + rm -rf /usr/src/antlr4-cpp-runtime-4.8-source.zip; \ + rm -rf /usr/src/antlr4-runtime + FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin From c4962df2628653b0fa496aae88db00a0144fc544 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 26 Aug 2020 19:01:25 +0200 Subject: [PATCH 086/139] Refactor ASTJSONTest to reduce redundancy --- .../ASTJSON/documentation_legacy.json | 122 ++++++++++++ test/libsolidity/ASTJSONTest.cpp | 187 +++++++++--------- test/libsolidity/ASTJSONTest.h | 23 +++ 3 files changed, 243 insertions(+), 89 deletions(-) diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index 4284c4f8f..d09d9b10e 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -1,3 +1,124 @@ +[ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 2 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 2 + ], + "name": "C", + "nodes": + [ + null + ], + "scope": 3 + }, + "children": + [ + { + "attributes": + { + "text": "This contract is empty" + }, + "id": 1, + "name": "StructuredDocumentation", + "src": "0:27:1" + } + ], + "id": 2, + "name": "ContractDefinition", + "src": "28:13:1" + } + ], + "id": 3, + "name": "SourceUnit", + "src": "28:14:1" +}, +{ + "attributes": + { + "absolutePath": "b", + "exportedSymbols": + { + "C": + [ + 5 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodes": + [ + null + ], + "scope": 6 + }, + "children": + [ + { + "attributes": + { + "text": "This contract is empty\nand has a line-breaking comment." + }, + "id": 4, + "name": "StructuredDocumentation", + "src": "0:61:2" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "62:13:2" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "62:14:2" +}, { "attributes": { @@ -240,3 +361,4 @@ "name": "SourceUnit", "src": "0:214:3" } +] diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 979c79aa5..76c8e50c7 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -131,9 +131,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi } c.setSources(sources); c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - if (c.parse()) - c.analyze(); - else + + if (!c.parse()) { SourceReferenceFormatterHuman formatter(_stream, _formatted, false); for (auto const& error: c.errors()) @@ -141,85 +140,99 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi return TestResult::FatalError; } - if (m_sources.size() > 1) - m_result += "[\n"; + c.analyze(); - for (size_t i = 0; i < m_sources.size(); i++) - { - ostringstream result; - ASTJsonConverter(false, sourceIndices).print(result, c.ast(m_sources[i].first)); - m_result += result.str(); - if (i != m_sources.size() - 1) - m_result += ","; - m_result += "\n"; - } + bool resultsMatch = runTest( + m_expectation, + m_result, + sourceIndices, + c, + false, + "", + _stream, + _linePrefix, + _formatted + ); - if (m_sources.size() > 1) - m_result += "]\n"; - - bool resultsMatch = true; - - replaceTagWithVersion(m_expectation); - - if (m_expectation != m_result) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - { - istringstream stream(m_expectation); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - { - istringstream stream(m_result); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - resultsMatch = false; - } - - for (size_t i = 0; i < m_sources.size(); i++) - { - ostringstream result; - ASTJsonConverter(true, sourceIndices).print(result, c.ast(m_sources[i].first)); - m_resultLegacy = result.str(); - if (i != m_sources.size() - 1) - m_resultLegacy += ","; - m_resultLegacy += "\n"; - } - - replaceTagWithVersion(m_expectationLegacy); - - if (m_expectationLegacy != m_resultLegacy) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result (legacy):" << endl; - { - istringstream stream(m_expectationLegacy); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result (legacy):" << endl; - { - istringstream stream(m_resultLegacy); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - resultsMatch = false; - } + resultsMatch = runTest( + m_expectationLegacy, + m_resultLegacy, + sourceIndices, + c, + true, + "legacy", + _stream, + _linePrefix, + _formatted + ) && resultsMatch; return resultsMatch ? TestResult::Success : TestResult::Failure; } +bool ASTJSONTest::runTest( + string& _expectation, + string& _result, + map const& _sourceIndices, + CompilerStack& _compiler, + bool _legacy, + string const& _variation, + ostream& _stream, + string const& _linePrefix, + bool const _formatted +) +{ + if (m_sources.size() > 1) + _result += "[\n"; + + for (size_t i = 0; i < m_sources.size(); i++) + { + ostringstream result; + ASTJsonConverter(_legacy, _sourceIndices).print(result, _compiler.ast(m_sources[i].first)); + _result += result.str(); + if (i != m_sources.size() - 1) + _result += ","; + _result += "\n"; + } + + if (m_sources.size() > 1) + _result += "]\n"; + + replaceTagWithVersion(_expectation); + + if (_expectation != _result) + { + string nextIndentLevel = _linePrefix + " "; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << + _linePrefix << + "Expected result" << + (!_variation.empty() ? " (" + _variation + "):" : ":") << + endl; + { + istringstream stream(_expectation); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << + _linePrefix << + "Obtained result" << + (!_variation.empty() ? " (" + _variation + "):" : ":") << + endl; + { + istringstream stream(_result); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + return false; + } + + return true;; +} + void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const { for (auto const& source: m_sources) @@ -236,24 +249,20 @@ void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) const { - ofstream file(m_astFilename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write AST expectation to \"" + m_astFilename + "\".")); + updateExpectation(m_astFilename, m_result, ""); + updateExpectation(m_legacyAstFilename, m_resultLegacy, "legacy "); +} + +void ASTJSONTest::updateExpectation(string const& _filename, string const& _expectation, string const& _variation) const +{ + ofstream file(_filename.c_str()); + if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write " + _variation + "AST expectation to \"" + _filename + "\".")); file.exceptions(ios::badbit); - string replacedResult = m_result; + string replacedResult = _expectation; replaceVersionWithTag(replacedResult); file << replacedResult; file.flush(); file.close(); - - file.open(m_legacyAstFilename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write legacy AST expectation to \"" + m_legacyAstFilename + "\".")); - - string replacedResultLegacy = m_resultLegacy; - replaceVersionWithTag(replacedResultLegacy); - - file << replacedResultLegacy; - file.flush(); - file.close(); } diff --git a/test/libsolidity/ASTJSONTest.h b/test/libsolidity/ASTJSONTest.h index 4e74d79f3..5a5caac05 100644 --- a/test/libsolidity/ASTJSONTest.h +++ b/test/libsolidity/ASTJSONTest.h @@ -26,9 +26,15 @@ #include #include +namespace solidity::frontend +{ +class CompilerStack; +} + namespace solidity::frontend::test { + class ASTJSONTest: public TestCase { public: @@ -41,6 +47,23 @@ public: void printSource(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; private: + bool runTest( + std::string& _expectation, + std::string& _result, + std::map const& _sourceIndicies, + CompilerStack& _compiler, + bool _legacy, + std::string const& _variation, + std::ostream& _stream, + std::string const& _linePrefix = "", + bool const _formatted = false + ); + void updateExpectation( + std::string const& _filename, + std::string const& _expectation, + std::string const& _variation + ) const; + std::vector> m_sources; std::string m_expectationLegacy; std::string m_astFilename; From c7dc42a794abea52eb07b31ffb6c7903e96420fe Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Thu, 27 Aug 2020 14:39:52 +0200 Subject: [PATCH 087/139] Disallow ``using for`` directive for interfaces. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 10 ++++++++++ libsolidity/analysis/TypeChecker.h | 1 + .../syntaxTests/bound/interface_using_for.sol | 10 ++++++++++ 4 files changed, 22 insertions(+) create mode 100644 test/libsolidity/syntaxTests/bound/interface_using_for.sol diff --git a/Changelog.md b/Changelog.md index cf3989662..1d2d81796 100644 --- a/Changelog.md +++ b/Changelog.md @@ -23,6 +23,7 @@ Bugfixes: * Type Checker: Disallow signed literals as exponent in exponentiation operator. * Allow `type(Contract).name` for abstract contracts and interfaces. * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. + * Type Checker: Disallow ``using for`` directive inside interfaces. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 28f2b4152..19272b924 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3148,6 +3148,16 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().isPure = true; } +void TypeChecker::endVisit(UsingForDirective const& _usingFor) +{ + if (m_currentContract->isInterface()) + m_errorReporter.typeError( + 9088_error, + _usingFor.location(), + "The \"using for\" directive is not allowed inside interfaces." + ); +} + bool TypeChecker::contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 87ae1d83c..70041766d 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -143,6 +143,7 @@ private: bool visit(Identifier const& _identifier) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; + void endVisit(UsingForDirective const& _usingForDirective) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, diff --git a/test/libsolidity/syntaxTests/bound/interface_using_for.sol b/test/libsolidity/syntaxTests/bound/interface_using_for.sol new file mode 100644 index 000000000..727ff2c46 --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/interface_using_for.sol @@ -0,0 +1,10 @@ +library L { + function f() public {} +} + +interface I { + using L for int; + function g() external; +} +// ---- +// TypeError 9088: (60-76): The "using for" directive is not allowed inside interfaces. From 6101590d9300ff13230b93f762a7bd9896d382f6 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 26 Aug 2020 11:37:40 +0200 Subject: [PATCH 088/139] ossfuzz: Generate antlr4 runtime deps --- .circleci/config.yml | 5 ++- scripts/ci/build_ossfuzz.sh | 58 ++++++++++++++++++++++++++----- test/tools/ossfuzz/CMakeLists.txt | 30 +++++++++++++--- 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f5ab32ccc..85b0e9d6c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,8 +21,8 @@ parameters: default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a4d5271b5552139d9f2caefc50d42f401bf74132cf8f253e199e11c80ab42de" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:efaabb3c143f64171be596932c62013bcfd7f73b1fbcb832025a34dd2b6e6922" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-3 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:6fa6914bd81abcac4b162c738e6ff05d87cefe7655e3859c7a827e5a8ec20dc7" emscripten-docker-image: type: string default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc" @@ -500,7 +500,6 @@ jobs: CC: clang CXX: clang++ TERM: xterm - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake MAKEFLAGS: -j 3 steps: - checkout diff --git a/scripts/ci/build_ossfuzz.sh b/scripts/ci/build_ossfuzz.sh index 42f7b3a39..fc7edf349 100755 --- a/scripts/ci/build_ossfuzz.sh +++ b/scripts/ci/build_ossfuzz.sh @@ -1,15 +1,55 @@ #!/usr/bin/env bash -set -e +set -ex -ROOTDIR="$(dirname "$0")/../.." +ROOTDIR="/root/project" BUILDDIR="${ROOTDIR}/build" +mkdir -p "${BUILDDIR}" && mkdir -p "$BUILDDIR/deps" -mkdir -p "${BUILDDIR}" -cd "${BUILDDIR}" +ANTLRJAR="${ROOTDIR}/build/deps/antlr4.8.jar" +ANTLRJAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" -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 -protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz -cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/libfuzzer.cmake +download_antlr4() +{ + if [[ ! -e "${ANTLRJAR}" ]] + then + wget -O "${ANTLRJAR}" "${ANTLRJAR_URI}" + fi +} -make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 +generate_protobuf_bindings() +{ + cd "${ROOTDIR}"/test/tools/ossfuzz + # Generate protobuf C++ bindings + for protoName in yul abiV2 sol; + do + protoc "${protoName}"Proto.proto --cpp_out . + done +} + +generate_antlr4_bindings() +{ + cd "${ROOTDIR}" + # Replace boolean with bool to suit c++ syntax + sed -i 's/boolean /bool /g' docs/grammar/Solidity.g4 + # Generate antlr4 visitor/parser/lexer c++ bindings + java -jar "${ANTLRJAR}" -Dlanguage=Cpp \ + -Xexact-output-dir -package solidity::test::fuzzer -o test/tools/ossfuzz \ + -no-listener -visitor docs/grammar/SolidityLexer.g4 docs/grammar/Solidity.g4 + # Delete unnecessary autogen files + rm -f "${ROOTDIR}"/test/tools/ossfuzz/Solidity*Visitor.cpp \ + "${ROOTDIR}"/test/tools/ossfuzz/Solidity*.interp \ + "${ROOTDIR}"/test/tools/ossfuzz/Solidity*.tokens +} + +build_fuzzers() +{ + cd "${BUILDDIR}" + cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ + -DCMAKE_TOOLCHAIN_FILE="${ROOTDIR}"/cmake/toolchains/libfuzzer.cmake + make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 +} + +download_antlr4 +generate_protobuf_bindings +generate_antlr4_bindings +build_fuzzers \ No newline at end of file diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index a799bd736..372e899a3 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -23,12 +23,34 @@ if (OSSFUZZ) endif() if (OSSFUZZ) - add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp) - target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm) + add_executable(solc_opt_ossfuzz + solc_opt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + SolidityLexer.cpp + SolidityParser.cpp + ) + target_compile_options(solc_opt_ossfuzz + PUBLIC + ${COMPILE_OPTIONS} -Wno-extra-semi -Wno-unused-parameter + ) + target_include_directories(solc_opt_ossfuzz PRIVATE /usr/include/antlr4-runtime) + target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm antlr4-runtime) set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp) - target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm) + add_executable(solc_noopt_ossfuzz + solc_noopt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + SolidityLexer.cpp + SolidityParser.cpp + ) + target_compile_options(solc_noopt_ossfuzz + PUBLIC + ${COMPILE_OPTIONS} -Wno-extra-semi -Wno-unused-parameter + ) + target_include_directories(solc_noopt_ossfuzz PRIVATE /usr/include/antlr4-runtime) + target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm antlr4-runtime) set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) add_executable(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp) From fdf68a9525d1b2393b51ff7fa42146cff86fa7da Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 May 2020 16:28:26 +0100 Subject: [PATCH 089/139] Add some asserts to WasmCodeTransform --- libyul/backends/wasm/WasmCodeTransform.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index fae7cd6cb..22d2188d0 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -275,11 +275,13 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) wasm::Expression WasmCodeTransform::operator()(Break const&) { + yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}}; } wasm::Expression WasmCodeTransform::operator()(Continue const&) { + yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}}; } From dd44fc1f06a897922dc0962e7c8967ee2b8c4254 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 14 Jul 2020 11:22:11 +0100 Subject: [PATCH 090/139] Use explicit yul namespace in WasmCodeTransform --- libyul/backends/wasm/WasmCodeTransform.cpp | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index 22d2188d0..541f1f9f8 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -90,7 +90,7 @@ wasm::Expression WasmCodeTransform::generateMultiAssignment( return { std::move(block) }; } -wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDecl) +wasm::Expression WasmCodeTransform::operator()(yul::VariableDeclaration const& _varDecl) { vector variableNames; for (auto const& var: _varDecl.variables) @@ -105,7 +105,7 @@ wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDe return wasm::BuiltinCall{"nop", {}}; } -wasm::Expression WasmCodeTransform::operator()(Assignment const& _assignment) +wasm::Expression WasmCodeTransform::operator()(yul::Assignment const& _assignment) { vector variableNames; for (auto const& var: _assignment.variableNames) @@ -113,12 +113,12 @@ wasm::Expression WasmCodeTransform::operator()(Assignment const& _assignment) return generateMultiAssignment(move(variableNames), visit(*_assignment.value)); } -wasm::Expression WasmCodeTransform::operator()(ExpressionStatement const& _statement) +wasm::Expression WasmCodeTransform::operator()(yul::ExpressionStatement const& _statement) { return visitReturnByValue(_statement.expression); } -wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) +wasm::Expression WasmCodeTransform::operator()(yul::FunctionCall const& _call) { if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) { @@ -164,17 +164,17 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; } -wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier) +wasm::Expression WasmCodeTransform::operator()(yul::Identifier const& _identifier) { return wasm::LocalVariable{_identifier.name.str()}; } -wasm::Expression WasmCodeTransform::operator()(Literal const& _literal) +wasm::Expression WasmCodeTransform::operator()(yul::Literal const& _literal) { return makeLiteral(translatedType(_literal.type), valueOfLiteral(_literal)); } -wasm::Expression WasmCodeTransform::operator()(If const& _if) +wasm::Expression WasmCodeTransform::operator()(yul::If const& _if) { yul::Type conditionType = m_typeInfo.typeOf(*_if.condition); @@ -196,7 +196,7 @@ wasm::Expression WasmCodeTransform::operator()(If const& _if) return wasm::If{make_unique(move(condition)), visit(_if.body.statements), {}}; } -wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) +wasm::Expression WasmCodeTransform::operator()(yul::Switch const& _switch) { yul::Type expressionType = m_typeInfo.typeOf(*_switch.expression); YulString eq_instruction = YulString(expressionType.str() + ".eq"); @@ -240,13 +240,13 @@ wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) return { std::move(block) }; } -wasm::Expression WasmCodeTransform::operator()(FunctionDefinition const&) +wasm::Expression WasmCodeTransform::operator()(yul::FunctionDefinition const&) { yulAssert(false, "Should not have visited here."); return {}; } -wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) +wasm::Expression WasmCodeTransform::operator()(yul::ForLoop const& _for) { string breakLabel = newLabel(); string continueLabel = newLabel(); @@ -273,25 +273,25 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) return wasm::Block{breakLabel, move(statements)}; } -wasm::Expression WasmCodeTransform::operator()(Break const&) +wasm::Expression WasmCodeTransform::operator()(yul::Break const&) { yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}}; } -wasm::Expression WasmCodeTransform::operator()(Continue const&) +wasm::Expression WasmCodeTransform::operator()(yul::Continue const&) { yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}}; } -wasm::Expression WasmCodeTransform::operator()(Leave const&) +wasm::Expression WasmCodeTransform::operator()(yul::Leave const&) { yulAssert(!m_functionBodyLabel.empty(), ""); return wasm::Branch{wasm::Label{m_functionBodyLabel}}; } -wasm::Expression WasmCodeTransform::operator()(Block const& _block) +wasm::Expression WasmCodeTransform::operator()(yul::Block const& _block) { return wasm::Block{{}, visit(_block.statements)}; } From 672f391660595215642f73e27851e83e6d72aa95 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Thu, 27 Aug 2020 18:45:36 +0200 Subject: [PATCH 091/139] Changed canonicalName() to toString() because of internal functions --- libsolidity/analysis/TypeChecker.cpp | 2 +- .../syntaxTests/iceRegressionTests/oversized_var.sol | 8 ++++++-- .../syntaxTests/largeTypes/storage_parameter.sol | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 28f2b4152..deeb2cba9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -616,7 +616,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (_variable.isStateVariable()) m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true)); else - m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->canonicalName(), false)); + m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->toString(true), false)); } vector oversizedSubtypes = frontend::oversizedSubtypes(*varType); for (Type const* subtype: oversizedSubtypes) diff --git a/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol b/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol index 15118322f..4c3d45385 100644 --- a/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol +++ b/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol @@ -4,11 +4,15 @@ contract b { } c d; + function e() public view { c storage x = d; - x.a[0]; + x.a[0]; + function()[3**44] storage fs; } } // ---- // Warning 3408: (66-69): Variable "d" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 2332: (110-111): Type "b.c" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2332: (111-112): Type "struct b.c" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2332: (152-169): Type "function ()[984770902183611232881]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2072: (152-180): Unused local variable. diff --git a/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol b/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol index b32815028..902ad73bc 100644 --- a/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol +++ b/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol @@ -3,4 +3,4 @@ contract C { function f(S storage) internal {} } // ---- -// Warning 2332: (64-65): Type "C.S" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2332: (64-65): Type "struct C.S" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. From c3eff6ed50f0ffec75133ef02f23cb6d3321dd70 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 22 Jul 2020 16:20:59 +0200 Subject: [PATCH 092/139] [cmake] C++ version should not be mandated by the toolchain setting, as it conflicts with Windows (CI) builds using vcpkg --- .circleci/config.yml | 2 +- cmake/EthToolchains.cmake | 5 +++++ cmake/toolchains/cxx20.cmake | 4 ---- cmake/toolchains/default.cmake | 4 ---- 4 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 cmake/toolchains/cxx20.cmake diff --git a/.circleci/config.yml b/.circleci/config.yml index f5ab32ccc..fbe76d594 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -487,7 +487,7 @@ jobs: <<: *build_ubuntu2004 environment: CMAKE_BUILD_TYPE: Debug - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF + CMAKE_OPTIONS: -DCMAKE_CXX_STANDARD=20 -DUSE_CVC4=OFF MAKEFLAGS: -j 10 steps: - checkout diff --git a/cmake/EthToolchains.cmake b/cmake/EthToolchains.cmake index a4263b7df..adf0a20a5 100644 --- a/cmake/EthToolchains.cmake +++ b/cmake/EthToolchains.cmake @@ -1,3 +1,8 @@ +# Require C++17. +set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + if(NOT CMAKE_TOOLCHAIN_FILE) # Use default toolchain file if none is provided. set( diff --git a/cmake/toolchains/cxx20.cmake b/cmake/toolchains/cxx20.cmake deleted file mode 100644 index ad34e5749..000000000 --- a/cmake/toolchains/cxx20.cmake +++ /dev/null @@ -1,4 +0,0 @@ -# Require C++20. -set(CMAKE_CXX_STANDARD 20) # This requires at least CMake 3.12 to understand this C++20 flag -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/toolchains/default.cmake b/cmake/toolchains/default.cmake index 07fc80e8e..e69de29bb 100644 --- a/cmake/toolchains/default.cmake +++ b/cmake/toolchains/default.cmake @@ -1,4 +0,0 @@ -# Require C++17. -set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS OFF) From 7ff809dc78e9c00bfb0580dbab4653f9e61cea88 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Thu, 27 Aug 2020 17:25:13 +0200 Subject: [PATCH 093/139] Disallow assigning more than once to immutables during the vardecl --- Changelog.md | 1 + libsolidity/analysis/ImmutableValidator.cpp | 8 +++++--- .../immutable/variable_declaration_already.sol | 10 ++++++++++ .../immutable/variable_declaration_value.sol | 6 ++++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol create mode 100644 test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol diff --git a/Changelog.md b/Changelog.md index d5ff1204c..da14b7d16 100644 --- a/Changelog.md +++ b/Changelog.md @@ -25,6 +25,7 @@ Bugfixes: * Allow `type(Contract).name` for abstract contracts and interfaces. * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. * Type Checker: Disallow ``using for`` directive inside interfaces. + * Immutables: Disallow assigning immutables more than once during their declaration. ### 0.7.0 (2020-07-28) diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 5af640e58..f6fe498ea 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -34,10 +34,12 @@ void ImmutableValidator::analyze() for (ContractDefinition const* contract: linearizedContracts) for (VariableDeclaration const* stateVar: contract->stateVariables()) if (stateVar->value()) - { + m_initializedStateVariables.emplace(stateVar); + + for (ContractDefinition const* contract: linearizedContracts) + for (VariableDeclaration const* stateVar: contract->stateVariables()) + if (stateVar->value()) stateVar->value()->accept(*this); - solAssert(m_initializedStateVariables.emplace(stateVar).second, ""); - } for (ContractDefinition const* contract: linearizedContracts) if (contract->constructor()) diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol new file mode 100644 index 000000000..6c204d05e --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable z = 2; + uint immutable x = z = y = 3; + uint immutable y = 5; +} +// ---- +// TypeError 1581: (62-63): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1574: (62-63): Immutable state variable already initialized. +// TypeError 1581: (66-67): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1574: (66-67): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol new file mode 100644 index 000000000..db8dd6494 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol @@ -0,0 +1,6 @@ +contract C { + int immutable x = x = 5; +} +// ---- +// TypeError 1581: (35-36): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1574: (35-36): Immutable state variable already initialized. From 8b564a7be71088e98fd6ef572e96262a8da9f42f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 4 Aug 2020 13:18:41 +0200 Subject: [PATCH 094/139] Fix reads checks for immutable variables. --- Changelog.md | 1 + libsolidity/analysis/ImmutableValidator.cpp | 19 +++++++++++-------- .../syntaxTests/immutable/complex.sol | 8 ++++++++ .../immutable/conditionally_initialized.sol | 2 +- .../ctor_indirect_initialization.sol | 2 +- .../immutable/ctor_modifier_args.sol | 2 +- .../ctor_modifier_initialization.sol | 2 +- .../syntaxTests/immutable/decrement.sol | 4 ++-- .../syntaxTests/immutable/delete.sol | 4 ++-- .../immutable/delete_and_initialize.sol | 9 +++++++++ .../function_pointer_initializing.sol | 3 +-- .../syntaxTests/immutable/increment.sol | 4 ++-- .../immutable/increment_decrement.sol | 11 +++++++++++ .../immutable/inheritance_ctor_argument.sol | 2 +- ...e_ctor_inherit_specifier_argument_init.sol | 2 +- .../immutable/inheritance_wrong_ctor.sol | 3 +-- .../immutable/loop_initialized.sol | 2 +- .../syntaxTests/immutable/unary.sol | 8 ++++++++ .../writing_after_initialization.sol | 3 +-- .../writing_after_initialization_modifier.sol | 3 +-- 20 files changed, 65 insertions(+), 29 deletions(-) create mode 100644 test/libsolidity/syntaxTests/immutable/complex.sol create mode 100644 test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol create mode 100644 test/libsolidity/syntaxTests/immutable/increment_decrement.sol create mode 100644 test/libsolidity/syntaxTests/immutable/unary.sol diff --git a/Changelog.md b/Changelog.md index cc92dd025..3ea0f116e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,7 @@ Compiler Features: Bugfixes: * AST: Remove ``null`` member values also when the compiler is used in standard-json-mode. + * Immutables: Properly treat complex assignment and increment/decrement as both reading and writing and thus disallow it everywhere for immutable variables. * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on array implicit conversion. diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 5af640e58..3e89417cd 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -163,41 +163,44 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va if (!_variableReference.isStateVariable() || !_variableReference.immutable()) return; - if (_expression.annotation().willBeWrittenTo && _expression.annotation().lValueOfOrdinaryAssignment) + // If this is not an ordinary assignment, we write and read at the same time. + bool write = _expression.annotation().willBeWrittenTo; + bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment; + if (write) { if (!m_currentConstructor) m_errorReporter.typeError( 1581_error, _expression.location(), - "Immutable variables can only be initialized inline or assigned directly in the constructor." + "Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor." ); else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) m_errorReporter.typeError( 7484_error, _expression.location(), - "Immutable variables must be initialized in the constructor of the contract they are defined in." + "Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in." ); else if (m_inLoop) m_errorReporter.typeError( 6672_error, _expression.location(), - "Immutable variables can only be initialized once, not in a while statement." + "Cannot write to immutable here: Immutable variables cannot be initialized inside a loop." ); else if (m_inBranch) m_errorReporter.typeError( 4599_error, _expression.location(), - "Immutable variables must be initialized unconditionally, not in an if statement." + "Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement." ); - - if (!m_initializedStateVariables.emplace(&_variableReference).second) + else if (m_initializedStateVariables.count(&_variableReference)) m_errorReporter.typeError( 1574_error, _expression.location(), "Immutable state variable already initialized." ); + m_initializedStateVariables.emplace(&_variableReference); } - else if (m_inConstructionContext) + if (read && m_inConstructionContext) m_errorReporter.typeError( 7733_error, _expression.location(), diff --git a/test/libsolidity/syntaxTests/immutable/complex.sol b/test/libsolidity/syntaxTests/immutable/complex.sol new file mode 100644 index 000000000..b624430ea --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/complex.sol @@ -0,0 +1,8 @@ +contract A { + int immutable a; + constructor() { a = 5; } + function f() public { a += 7; } +} + +// ---- +// TypeError 1581: (83-84): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol index b4808b844..052a1b597 100644 --- a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol +++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 4599: (86-87): Immutable variables must be initialized unconditionally, not in an if statement. +// TypeError 4599: (86-87): Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol index f91e98333..e95bf5740 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// TypeError 1581: (119-120): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (119-120): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol index 3f2461456..0a966f7c3 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol @@ -9,4 +9,4 @@ contract C { function f(uint a) internal pure {} } // ---- -// TypeError 1581: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (59-60): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol index 14f47364a..5970ab247 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// TypeError 1581: (102-103): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (102-103): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol index 3c34dd3b7..5717f05c8 100644 --- a/test/libsolidity/syntaxTests/immutable/decrement.sol +++ b/test/libsolidity/syntaxTests/immutable/decrement.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { x--; } } // ---- -// TypeError 7733: (67-68): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (63-64): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete.sol b/test/libsolidity/syntaxTests/immutable/delete.sol index 58e9235f3..541fceb53 100644 --- a/test/libsolidity/syntaxTests/immutable/delete.sol +++ b/test/libsolidity/syntaxTests/immutable/delete.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { delete x; } } // ---- -// TypeError 7733: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (70-71): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol b/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol new file mode 100644 index 000000000..859a30a77 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x = 3; + constructor() { + delete x; + } +} +// ---- +// TypeError 1574: (74-75): Immutable state variable already initialized. +// TypeError 7733: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol index 629d80f66..cc175d083 100644 --- a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol @@ -10,5 +10,4 @@ contract C is B(C.f) { function f() internal returns(uint) { return x = 2; } } // ---- -// TypeError 1581: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (200-201): Immutable state variable already initialized. +// TypeError 1581: (200-201): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol index 00c20422c..11959ebc7 100644 --- a/test/libsolidity/syntaxTests/immutable/increment.sol +++ b/test/libsolidity/syntaxTests/immutable/increment.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { x++; } } // ---- -// TypeError 7733: (67-68): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (63-64): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/increment_decrement.sol b/test/libsolidity/syntaxTests/immutable/increment_decrement.sol new file mode 100644 index 000000000..ccfaf1920 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/increment_decrement.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + uint immutable y; + constructor() { + ++x; + --y; + } +} +// ---- +// TypeError 7733: (77-78): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (86-87): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol index 695319378..bc084400e 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol @@ -11,4 +11,4 @@ contract C is B { constructor() B(y = 3) { } } // ---- -// TypeError 1581: (148-149): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (148-149): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol index 8e3d67145..503f182d6 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol @@ -10,4 +10,4 @@ contract C is B(C.y = 3) { uint immutable y; } // ---- -// TypeError 1581: (104-107): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (104-107): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol index 4f431474b..748d9da55 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol @@ -8,5 +8,4 @@ contract C is B { } } // ---- -// TypeError 7484: (88-89): Immutable variables must be initialized in the constructor of the contract they are defined in. -// TypeError 1574: (88-89): Immutable state variable already initialized. +// TypeError 7484: (88-89): Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in. diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol index b831f0f0b..d8c8ca47f 100644 --- a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol +++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 6672: (88-89): Immutable variables can only be initialized once, not in a while statement. +// TypeError 6672: (88-89): Cannot write to immutable here: Immutable variables cannot be initialized inside a loop. diff --git a/test/libsolidity/syntaxTests/immutable/unary.sol b/test/libsolidity/syntaxTests/immutable/unary.sol new file mode 100644 index 000000000..f3c75c09b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/unary.sol @@ -0,0 +1,8 @@ +contract A { + int immutable a; + constructor() { a = 5; } + function f() public { --a; } +} + +// ---- +// TypeError 1581: (85-86): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol index 05d366ead..4b2b91a15 100644 --- a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol @@ -6,5 +6,4 @@ contract C { } } // ---- -// TypeError 1581: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (76-77): Immutable state variable already initialized. +// TypeError 1581: (76-77): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol index cdfd47443..d450a37d2 100644 --- a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol @@ -8,5 +8,4 @@ contract C { } } // ---- -// TypeError 1581: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (111-112): Immutable state variable already initialized. +// TypeError 1581: (111-112): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. From aa70758c32807134e2f53eb3b23300559630f09d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 28 Aug 2020 14:33:29 +0100 Subject: [PATCH 095/139] Add more scanner tests for hex and unicode literals --- test/liblangutil/Scanner.cpp | 52 ++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index c2f926389..755f59c2f 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -589,6 +589,30 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) // Unicode string literal +BOOST_AUTO_TEST_CASE(unicode_prefix_only) +{ + Scanner scanner(CharStream("{ unicode", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(unicode_invalid_space) +{ + Scanner scanner(CharStream("{ unicode ", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(unicode_invalid_token) +{ + Scanner scanner(CharStream("{ unicode test", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + BOOST_AUTO_TEST_CASE(valid_unicode_literal) { Scanner scanner(CharStream("{ unicode\"Hello 😃\"", "")); @@ -606,7 +630,31 @@ BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \x07\xf0\x9f\x98\x83", 11)); } -// HEX STRING LITERAL +// Hex string literal + +BOOST_AUTO_TEST_CASE(hex_prefix_only) +{ + Scanner scanner(CharStream("{ hex", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(hex_invalid_space) +{ + Scanner scanner(CharStream("{ hex ", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(hex_invalid_token) +{ + Scanner scanner(CharStream("{ hex test", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} BOOST_AUTO_TEST_CASE(valid_hex_literal) { @@ -648,7 +696,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } -// COMMENTS +// Comments BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { From cd3836f467ddaabb6f442075df94840537c99783 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 28 Aug 2020 14:46:29 +0200 Subject: [PATCH 096/139] Fixes compilation on MSVC wrt. narrowing conversion from cstr-ptr to bool. --- test/libsolidity/SolidityTypes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index b837b3878..49e6f6ac4 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(helper_bool_result) BoolResult r7{true}; // Attention: this will implicitly convert to bool. - BoolResult r8{"true"}; + BoolResult r8("true"); // We cannot use {} initializer here because this does not allow narrowing conversion (at least MSVC breaks) r7.merge(r8, logical_and()); BOOST_REQUIRE_EQUAL(r7.get(), true); BOOST_REQUIRE_EQUAL(r7.message(), ""); From a80e2c146238eefcdf07cff692cbaa7b89f8778e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 28 Aug 2020 19:06:34 +0100 Subject: [PATCH 097/139] Update tests for immutable variables --- .../syntaxTests/immutable/variable_declaration_already.sol | 6 ++---- .../syntaxTests/immutable/variable_declaration_value.sol | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol index 6c204d05e..73fe59801 100644 --- a/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol @@ -4,7 +4,5 @@ contract C { uint immutable y = 5; } // ---- -// TypeError 1581: (62-63): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (62-63): Immutable state variable already initialized. -// TypeError 1581: (66-67): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (66-67): Immutable state variable already initialized. +// TypeError 1581: (62-63): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (66-67): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol index db8dd6494..2c98ffd79 100644 --- a/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol @@ -2,5 +2,4 @@ contract C { int immutable x = x = 5; } // ---- -// TypeError 1581: (35-36): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (35-36): Immutable state variable already initialized. +// TypeError 1581: (35-36): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. From 64c73baee917e0c9e3682ae9ff62dd5a64b8d032 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 29 Jul 2020 14:20:59 +0100 Subject: [PATCH 098/139] Use only advance() and not with a mix of m_scanner->next() --- libyul/AsmParser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 83f17a0d3..1e3b563d7 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -116,14 +116,14 @@ Statement Parser::parseStatement() { Statement stmt{createWithLocation()}; checkBreakContinuePosition("break"); - m_scanner->next(); + advance(); return stmt; } case Token::Continue: { Statement stmt{createWithLocation()}; checkBreakContinuePosition("continue"); - m_scanner->next(); + advance(); return stmt; } case Token::Identifier: @@ -132,7 +132,7 @@ Statement Parser::parseStatement() Statement stmt{createWithLocation()}; if (!m_insideFunction) m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); - m_scanner->next(); + advance(); return stmt; } break; From bd7283ad22d51622c1c43bea2356748bf8277205 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 27 Jul 2020 18:34:24 +0100 Subject: [PATCH 099/139] Add scanner test for specially handled keywords in Yul --- test/liblangutil/Scanner.cpp | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index 755f59c2f..862b19c00 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -819,6 +819,71 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) } } +BOOST_AUTO_TEST_CASE(solidity_keywords) +{ + // These are tokens which have a different meaning in Yul. + string keywords = "return byte bool address var in true false leave switch case default"; + Scanner scanner(CharStream(keywords, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); + BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); + BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); + BOOST_CHECK_EQUAL(scanner.next(), Token::Address); + BOOST_CHECK_EQUAL(scanner.next(), Token::Var); + BOOST_CHECK_EQUAL(scanner.next(), Token::In); + BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); + BOOST_CHECK_EQUAL(scanner.next(), Token::Case); + BOOST_CHECK_EQUAL(scanner.next(), Token::Default); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(keywords, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); + BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); + BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); + BOOST_CHECK_EQUAL(scanner.next(), Token::Address); + BOOST_CHECK_EQUAL(scanner.next(), Token::Var); + BOOST_CHECK_EQUAL(scanner.next(), Token::In); + BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); + BOOST_CHECK_EQUAL(scanner.next(), Token::Case); + BOOST_CHECK_EQUAL(scanner.next(), Token::Default); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_keyword_like) +{ + Scanner scanner(CharStream("leave.function", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("leave.function", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) +{ + Scanner scanner(CharStream("mystorage.slot := 1", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("mystorage.slot := 1", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + BOOST_AUTO_TEST_CASE(yul_function) { string sig = "function f(a, b) -> x, y"; From caa329066e800680c1c70c781a57adfde1daf05e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Jul 2020 16:20:04 +0100 Subject: [PATCH 100/139] Do not emit Solidity-only tokens in the Scanner for Yul And simplfiy AsmParser greatly. --- liblangutil/Scanner.cpp | 13 +++++-- liblangutil/Token.h | 7 ++++ libyul/AsmParser.cpp | 26 ++------------ test/liblangutil/Scanner.cpp | 36 +++++++++++++++---- .../inlineAssembly/hex_assignment.sol | 2 +- .../inlineAssembly/hex_expression.sol | 2 +- .../inlineAssembly/hex_switch_case.sol | 2 +- test/libyul/yulSyntaxTests/hex_assignment.yul | 2 +- test/libyul/yulSyntaxTests/hex_expression.yul | 2 +- .../libyul/yulSyntaxTests/hex_switch_case.yul | 2 +- 10 files changed, 56 insertions(+), 38 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 0b17a0883..2fdb181ac 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -668,7 +668,7 @@ void Scanner::scanToken() tie(token, m, n) = scanIdentifierOrKeyword(); // Special case for hexadecimal literals - if (token == Token::Hex) + if (token == Token::Hex && m_kind != ScannerKind::Yul) { // reset m = 0; @@ -680,7 +680,7 @@ void Scanner::scanToken() else token = setError(ScannerError::IllegalToken); } - else if (token == Token::Unicode) + else if (token == Token::Unicode && m_kind != ScannerKind::Yul) { // reset m = 0; @@ -969,7 +969,14 @@ tuple Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char) || (m_char == '.' && m_kind == ScannerKind::Yul)) addLiteralCharAndAdvance(); literal.complete(); - return TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); + auto const token = TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); + if (m_kind == ScannerKind::Yul) + { + // Turn non-Yul keywords into identifiers. + if (!TokenTraits::isYulKeyword(std::get<0>(token))) + return std::make_tuple(Token::Identifier, 0, 0); + } + return token; } } // namespace solidity::langutil diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 53d8131f1..fecd3acbb 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -317,6 +317,13 @@ namespace TokenTraits constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } constexpr bool isReservedKeyword(Token op) { return (Token::After <= op && op <= Token::Unchecked); } + constexpr bool isYulKeyword(Token tok) + { + return tok == Token::Function || tok == Token::Let || tok == Token::If || tok == Token::Switch || tok == Token::Case || + tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue /* || tok == Token::Leave */ || + tok == Token::TrueLiteral || tok == Token::FalseLiteral; + } + inline Token AssignmentToBinaryOp(Token op) { solAssert(isAssignmentOp(op) && op != Token::Assign, ""); diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 1e3b563d7..43323c825 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -284,12 +284,6 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() switch (currentToken()) { case Token::Identifier: - case Token::Return: - case Token::Byte: - case Token::Bool: - case Token::Address: - case Token::Var: - case Token::In: { YulString literal{currentLiteral()}; if (m_dialect.builtin(literal)) @@ -472,24 +466,10 @@ TypedName Parser::parseTypedName() YulString Parser::expectAsmIdentifier() { YulString name{currentLiteral()}; - switch (currentToken()) - { - case Token::Return: - case Token::Byte: - case Token::Address: - case Token::Bool: - case Token::Identifier: - case Token::Var: - case Token::In: - break; - default: - expectToken(Token::Identifier); - break; - } - - if (m_dialect.builtin(name)) + if (currentToken() == Token::Identifier && m_dialect.builtin(name)) fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name."); - advance(); + // NOTE: We keep the expectation here to ensure the correct source location for the error above. + expectToken(Token::Identifier); return name; } diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index 862b19c00..c336d5e0b 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -595,6 +595,11 @@ BOOST_AUTO_TEST_CASE(unicode_prefix_only) BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ unicode", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); } BOOST_AUTO_TEST_CASE(unicode_invalid_space) @@ -611,6 +616,13 @@ BOOST_AUTO_TEST_CASE(unicode_invalid_token) BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ unicode test", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "test"); } BOOST_AUTO_TEST_CASE(valid_unicode_literal) @@ -638,6 +650,11 @@ BOOST_AUTO_TEST_CASE(hex_prefix_only) BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ hex", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "hex"); } BOOST_AUTO_TEST_CASE(hex_invalid_space) @@ -654,6 +671,13 @@ BOOST_AUTO_TEST_CASE(hex_invalid_token) BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ hex test", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "hex"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "test"); } BOOST_AUTO_TEST_CASE(valid_hex_literal) @@ -839,12 +863,12 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream(keywords, "")); scanner.setScannerMode(ScannerKind::Yul); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); - BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); - BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); - BOOST_CHECK_EQUAL(scanner.next(), Token::Address); - BOOST_CHECK_EQUAL(scanner.next(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::In); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol index 18107e044..3821a5073 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError 1856: (72-81): Literal or identifier expected. +// ParserError 6913: (86-87): Call or assignment expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol index 53cd23a29..d4cbe810b 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError 1856: (67-76): Literal or identifier expected. +// ParserError 2314: (70-76): Expected ',' but got 'StringLiteral' diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol index bc0ff56fe..38f1f9637 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// ParserError 1856: (92-99): Literal or identifier expected. +// ParserError 4805: (95-99): Literal expected. diff --git a/test/libyul/yulSyntaxTests/hex_assignment.yul b/test/libyul/yulSyntaxTests/hex_assignment.yul index c9af448c1..9305cd3f2 100644 --- a/test/libyul/yulSyntaxTests/hex_assignment.yul +++ b/test/libyul/yulSyntaxTests/hex_assignment.yul @@ -2,4 +2,4 @@ let x := hex"0011" } // ---- -// ParserError 1856: (15-24): Literal or identifier expected. +// ParserError 6913: (25-26): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/hex_expression.yul b/test/libyul/yulSyntaxTests/hex_expression.yul index f9c1cb52d..02c8d78e6 100644 --- a/test/libyul/yulSyntaxTests/hex_expression.yul +++ b/test/libyul/yulSyntaxTests/hex_expression.yul @@ -2,4 +2,4 @@ pop(hex"2233") } // ---- -// ParserError 1856: (10-19): Literal or identifier expected. +// ParserError 2314: (13-19): Expected ',' but got 'StringLiteral' diff --git a/test/libyul/yulSyntaxTests/hex_switch_case.yul b/test/libyul/yulSyntaxTests/hex_switch_case.yul index 0f3636b4f..4034b4797 100644 --- a/test/libyul/yulSyntaxTests/hex_switch_case.yul +++ b/test/libyul/yulSyntaxTests/hex_switch_case.yul @@ -4,4 +4,4 @@ case hex"1122" {} } // ---- -// ParserError 1856: (33-40): Literal or identifier expected. +// ParserError 4805: (36-40): Literal expected. From 4366ede889be684d62e9f2595d89b7ae9af82e83 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 27 Jul 2020 19:11:38 +0100 Subject: [PATCH 101/139] Introduce Leave as a keyword for Yul --- liblangutil/Scanner.cpp | 3 +++ liblangutil/Token.h | 5 ++++- libyul/AsmParser.cpp | 18 ++++++++---------- test/liblangutil/Scanner.cpp | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 2fdb181ac..b6011342d 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -972,6 +972,9 @@ tuple Scanner::scanIdentifierOrKeyword() auto const token = TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); if (m_kind == ScannerKind::Yul) { + // Turn Solidity identifier into a Yul keyword + if (m_tokens[NextNext].literal == "leave") + return std::make_tuple(Token::Leave, 0, 0); // Turn non-Yul keywords into identifiers. if (!TokenTraits::isYulKeyword(std::get<0>(token))) return std::make_tuple(Token::Identifier, 0, 0); diff --git a/liblangutil/Token.h b/liblangutil/Token.h index fecd3acbb..ecebe0cf5 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -269,6 +269,9 @@ namespace solidity::langutil K(Unchecked, "unchecked", 0) \ K(Var, "var", 0) \ \ + /* Yul-specific tokens, but not keywords. */ \ + T(Leave, "leave", 0) \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -320,7 +323,7 @@ namespace TokenTraits constexpr bool isYulKeyword(Token tok) { return tok == Token::Function || tok == Token::Let || tok == Token::If || tok == Token::Switch || tok == Token::Case || - tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue /* || tok == Token::Leave */ || + tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave || tok == Token::TrueLiteral || tok == Token::FalseLiteral; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 43323c825..e85bafb67 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -126,16 +126,14 @@ Statement Parser::parseStatement() advance(); return stmt; } - case Token::Identifier: - if (currentLiteral() == "leave") - { - Statement stmt{createWithLocation()}; - if (!m_insideFunction) - m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); - advance(); - return stmt; - } - break; + case Token::Leave: + { + Statement stmt{createWithLocation()}; + if (!m_insideFunction) + m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); + advance(); + return stmt; + } default: break; } diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index c336d5e0b..2bb934fee 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -871,7 +871,7 @@ BOOST_AUTO_TEST_CASE(solidity_keywords) BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Leave); BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); BOOST_CHECK_EQUAL(scanner.next(), Token::Case); BOOST_CHECK_EQUAL(scanner.next(), Token::Default); From 26a76c18d43f578c607c40ddf797e8abaf4a3698 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 29 Jul 2020 14:40:34 +0100 Subject: [PATCH 102/139] Add Yul syntax test for every Solidity keyword --- .../inlineAssembly/solidity_keywords.sol | 113 ++++++++++++++++++ .../yulSyntaxTests/solidity_keywords.yul | 109 +++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol create mode 100644 test/libyul/yulSyntaxTests/solidity_keywords.yul diff --git a/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol new file mode 100644 index 000000000..9b5e570be --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol @@ -0,0 +1,113 @@ +contract C { + function f() view public { + assembly { + // These are keywords of Solidity -- a copy from liblangutil/Token.h. + let abstract := 1 + let anonymous := 1 + let as := 1 + let assembly := 1 + // break is Yul keyword + let catch := 1 + let constant := 1 + let constructor := 1 + // continue is Yul keyword + let contract := 1 + let do := 1 + let else := 1 + let enum := 1 + let emit := 1 + let event := 1 + let external := 1 + let fallback := 1 + // for is a Yul keyword + // function is a Yul keyword + let hex := 1 + // if is a Yul keyword + let indexed := 1 + let interface := 1 + let internal := 1 + let immutable := 1 + let import := 1 + let is := 1 + let library := 1 + let mapping := 1 + let memory := 1 + let modifier := 1 + let new := 1 + let override := 1 + let payable := 1 + let public := 1 + let pragma := 1 + let private := 1 + let pure := 1 + let receive := 1 + // return is a builtin in EVMDialect + return(0, 0) + let returns := 1 + let storage := 1 + let calldata := 1 + let struct := 1 + let throw := 1 + let try := 1 + // type shadows the Solidity function + let unicode := 1 + let using := 1 + let view := 1 + let virtual := 1 + let while := 1 + let wei := 1 + let gwei := 1 + let ether := 1 + let seconds := 1 + let minutes := 1 + let hours := 1 + let days := 1 + let weeks := 1 + let years := 1 + let int := 1 + let uint := 1 + let bytes := 1 + // byte is a builtin in EVMDialect + pop(byte(1, 1)) + let string := 1 + // address is a builtin in EVMDialect + pop(address()) + let bool := 1 + let fixed := 1 + let ufixed := 1 + let after := 1 + let alias := 1 + let apply := 1 + let auto := 1 + // case is a Yul keyword + let copyof := 1 + // default is a Yul keyword + let define := 1 + let final := 1 + let implements := 1 + let in := 1 + let inline := 1 + // let is a Yul keyword + let macro := 1 + let match := 1 + let mutable := 1 + let null := 1 + let of := 1 + let partial := 1 + let promise := 1 + let reference := 1 + let relocatable := 1 + let sealed := 1 + let sizeof := 1 + let static := 1 + let supports := 1 + // switch is a Yul keyword + let typedef := 1 + let typeof := 1 + let unchecked := 1 + let var := 1 + } + } +} +// ---- +// Warning 5740: (944-2157): Unreachable code. diff --git a/test/libyul/yulSyntaxTests/solidity_keywords.yul b/test/libyul/yulSyntaxTests/solidity_keywords.yul new file mode 100644 index 000000000..4868fadad --- /dev/null +++ b/test/libyul/yulSyntaxTests/solidity_keywords.yul @@ -0,0 +1,109 @@ +{ + // These are keywords of Solidity -- a copy from liblangutil/Token.h. + let abstract := 1 + let anonymous := 1 + let as := 1 + let assembly := 1 + // break is Yul keyword + let catch := 1 + let constant := 1 + let constructor := 1 + // continue is Yul keyword + let contract := 1 + let do := 1 + let else := 1 + let enum := 1 + let emit := 1 + let event := 1 + let external := 1 + let fallback := 1 + // for is a Yul keyword + // function is a Yul keyword + let hex := 1 + // if is a Yul keyword + let indexed := 1 + let interface := 1 + let internal := 1 + let immutable := 1 + let import := 1 + let is := 1 + let library := 1 + let mapping := 1 + let memory := 1 + let modifier := 1 + let new := 1 + let override := 1 + let payable := 1 + let public := 1 + let pragma := 1 + let private := 1 + let pure := 1 + let receive := 1 + // return is a builtin in EVMDialect + return(0, 0) + let returns := 1 + let storage := 1 + let calldata := 1 + let struct := 1 + let throw := 1 + let try := 1 + let type := 1 + let unicode := 1 + let using := 1 + let view := 1 + let virtual := 1 + let while := 1 + let wei := 1 + let gwei := 1 + let ether := 1 + let seconds := 1 + let minutes := 1 + let hours := 1 + let days := 1 + let weeks := 1 + let years := 1 + let int := 1 + let uint := 1 + let bytes := 1 + // byte is a builtin in EVMDialect + pop(byte(1, 1)) + let string := 1 + // address is a builtin in EVMDialect + pop(address()) + let bool := 1 + let fixed := 1 + let ufixed := 1 + let after := 1 + let alias := 1 + let apply := 1 + let auto := 1 + // case is a Yul keyword + let copyof := 1 + // default is a Yul keyword + let define := 1 + let final := 1 + let implements := 1 + let in := 1 + let inline := 1 + // let is a Yul keyword + let macro := 1 + let match := 1 + let mutable := 1 + let null := 1 + let of := 1 + let partial := 1 + let promise := 1 + let reference := 1 + let relocatable := 1 + let sealed := 1 + let sizeof := 1 + let static := 1 + let supports := 1 + // switch is a Yul keyword + let typedef := 1 + let typeof := 1 + let unchecked := 1 + let var := 1 +} +// ==== +// dialect: evm From 7ef9591e645c5780f82d676418e6257f0b0c09b2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Aug 2020 15:53:45 +0100 Subject: [PATCH 103/139] Expose hex literal to the Yul parser This allows nicer error messages. --- liblangutil/Scanner.cpp | 2 +- liblangutil/Token.h | 2 +- libyul/AsmParser.cpp | 3 +++ test/liblangutil/Scanner.cpp | 11 ++++------- .../syntaxTests/inlineAssembly/hex_assignment.sol | 2 +- .../syntaxTests/inlineAssembly/hex_expression.sol | 2 +- .../syntaxTests/inlineAssembly/hex_switch_case.sol | 2 +- .../syntaxTests/inlineAssembly/solidity_keywords.sol | 4 ++-- test/libyul/yulSyntaxTests/hex_assignment.yul | 2 +- test/libyul/yulSyntaxTests/hex_expression.yul | 2 +- test/libyul/yulSyntaxTests/hex_switch_case.yul | 2 +- test/libyul/yulSyntaxTests/solidity_keywords.yul | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index b6011342d..1a3903d5c 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -668,7 +668,7 @@ void Scanner::scanToken() tie(token, m, n) = scanIdentifierOrKeyword(); // Special case for hexadecimal literals - if (token == Token::Hex && m_kind != ScannerKind::Yul) + if (token == Token::Hex) { // reset m = 0; diff --git a/liblangutil/Token.h b/liblangutil/Token.h index ecebe0cf5..2bfc58e44 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -324,7 +324,7 @@ namespace TokenTraits { return tok == Token::Function || tok == Token::Let || tok == Token::If || tok == Token::Switch || tok == Token::Case || tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave || - tok == Token::TrueLiteral || tok == Token::FalseLiteral; + tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; } inline Token AssignmentToBinaryOp(Token op) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index e85bafb67..2baefa16f 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -337,6 +337,9 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() ret = std::move(literal); break; } + case Token::HexStringLiteral: + fatalParserError(3772_error, "Hex literals are not valid in this context."); + break; default: fatalParserError(1856_error, "Literal or identifier expected."); } diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index 2bb934fee..2110cb2b6 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -652,9 +652,8 @@ BOOST_AUTO_TEST_CASE(hex_prefix_only) BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); scanner.reset(CharStream("{ hex", "")); scanner.setScannerMode(ScannerKind::Yul); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "hex"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); } BOOST_AUTO_TEST_CASE(hex_invalid_space) @@ -674,10 +673,8 @@ BOOST_AUTO_TEST_CASE(hex_invalid_token) scanner.reset(CharStream("{ hex test", "")); scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "hex"); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "test"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); } BOOST_AUTO_TEST_CASE(valid_hex_literal) diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol index 3821a5073..b51dd8604 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError 6913: (86-87): Call or assignment expected. +// ParserError 3772: (72-81): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol index d4cbe810b..2f4f69174 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError 2314: (70-76): Expected ',' but got 'StringLiteral' +// ParserError 3772: (67-76): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol index 38f1f9637..de64567d7 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// ParserError 4805: (95-99): Literal expected. +// ParserError 3772: (92-99): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol index 9b5e570be..5467d045a 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol @@ -21,7 +21,7 @@ contract C { let fallback := 1 // for is a Yul keyword // function is a Yul keyword - let hex := 1 + // hex is a Yul keyword // if is a Yul keyword let indexed := 1 let interface := 1 @@ -110,4 +110,4 @@ contract C { } } // ---- -// Warning 5740: (944-2157): Unreachable code. +// Warning 5740: (955-2168): Unreachable code. diff --git a/test/libyul/yulSyntaxTests/hex_assignment.yul b/test/libyul/yulSyntaxTests/hex_assignment.yul index 9305cd3f2..213163916 100644 --- a/test/libyul/yulSyntaxTests/hex_assignment.yul +++ b/test/libyul/yulSyntaxTests/hex_assignment.yul @@ -2,4 +2,4 @@ let x := hex"0011" } // ---- -// ParserError 6913: (25-26): Call or assignment expected. +// ParserError 3772: (15-24): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/hex_expression.yul b/test/libyul/yulSyntaxTests/hex_expression.yul index 02c8d78e6..191fdf085 100644 --- a/test/libyul/yulSyntaxTests/hex_expression.yul +++ b/test/libyul/yulSyntaxTests/hex_expression.yul @@ -2,4 +2,4 @@ pop(hex"2233") } // ---- -// ParserError 2314: (13-19): Expected ',' but got 'StringLiteral' +// ParserError 3772: (10-19): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/hex_switch_case.yul b/test/libyul/yulSyntaxTests/hex_switch_case.yul index 4034b4797..87ba6a4a9 100644 --- a/test/libyul/yulSyntaxTests/hex_switch_case.yul +++ b/test/libyul/yulSyntaxTests/hex_switch_case.yul @@ -4,4 +4,4 @@ case hex"1122" {} } // ---- -// ParserError 4805: (36-40): Literal expected. +// ParserError 3772: (33-40): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/solidity_keywords.yul b/test/libyul/yulSyntaxTests/solidity_keywords.yul index 4868fadad..1f1dafc53 100644 --- a/test/libyul/yulSyntaxTests/solidity_keywords.yul +++ b/test/libyul/yulSyntaxTests/solidity_keywords.yul @@ -19,7 +19,7 @@ let fallback := 1 // for is a Yul keyword // function is a Yul keyword - let hex := 1 + // hex is a Yul keyword // if is a Yul keyword let indexed := 1 let interface := 1 From 8c05db88c004cbf0a4798f7ad64c7eb68c3fa906 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 28 Aug 2020 11:59:15 +0200 Subject: [PATCH 104/139] [SMTChecker] Fix soundness of array pop --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 8 ++++---- .../array_members/array_pop_length_1.sol | 11 +++++++++++ .../array_members/array_pop_length_2.sol | 11 +++++++++++ .../array_members/array_pop_length_3.sol | 12 ++++++++++++ .../array_members/array_pop_length_4.sol | 11 +++++++++++ .../array_members/array_pop_length_5.sol | 14 ++++++++++++++ .../array_members/array_pop_length_6.sol | 14 ++++++++++++++ .../array_members/array_pop_length_7.sol | 9 +++++++++ .../array_members/array_pop_length_8.sol | 16 ++++++++++++++++ .../array_members/pop_2d_unsafe.sol | 2 +- 11 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol create mode 100644 test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol diff --git a/Changelog.md b/Changelog.md index d670209ad..413507fb6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. + * SMTChecker: Fix soundness of array ``pop``. * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. * Type Checker: Disallow signed literals as exponent in exponentiation operator. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 7088f485d..340825838 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1111,11 +1111,11 @@ void SMTEncoder::arrayPop(FunctionCall const& _funCall) symbArray->increaseIndex(); m_context.addAssertion(symbArray->elements() == oldElements); auto newLength = smtutil::Expression::ite( - oldLength == 0, - smt::maxValue(*TypeProvider::uint256()), - oldLength - 1 + oldLength > 0, + oldLength - 1, + 0 ); - m_context.addAssertion(symbArray->length() == oldLength - 1); + m_context.addAssertion(symbArray->length() == newLength); arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue()); } diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol new file mode 100644 index 000000000..28d671780 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.push(); + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol new file mode 100644 index 000000000..5567afa87 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.length; + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol new file mode 100644 index 000000000..0cbb15ea0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.pop(); + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here +// Warning 2529: (93-100): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol new file mode 100644 index 000000000..241d02c79 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.length; + a.pop(); + } +} +// ---- +// Warning 2529: (94-101): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol new file mode 100644 index 000000000..6a05d3af8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function g() internal { + a.push(); + } + function f() public { + a.pop(); + g(); + } +} +// ---- +// Warning 2529: (122-129): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol new file mode 100644 index 000000000..40e081d30 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function g() internal view { + a.length; + } + function f() public { + a.pop(); + g(); + } +} +// ---- +// Warning 2529: (127-134): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol new file mode 100644 index 000000000..34663d931 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.push(); + a.pop(); + } +} diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol new file mode 100644 index 000000000..517755aca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.push(); + a.push(); + a.push(); + a.pop(); + a.pop(); + a.pop(); + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol index 2795dfe51..5d4bd3a97 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 2529: (111-121): Empty array "pop" detected here. +// Warning 2529: (111-121): Empty array "pop" detected here From 192954e8fa1ce6d376f74049ed392cf986e20e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 12:56:17 +0200 Subject: [PATCH 105/139] docs: Fix broken internal links using wrong syntax - These links were using external link syntax instead of `:ref:`. What's worse, Sphinx can't detect when they're broken. - Added missing anchors in some cases. --- docs/contracts/constant-state-variables.rst | 2 +- docs/contracts/inheritance.rst | 2 +- docs/examples/micropayment.rst | 4 ++-- docs/layout-of-source-files.rst | 2 +- docs/style-guide.rst | 6 +++--- docs/units-and-global-variables.rst | 2 ++ 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 33204f58d..06bc4189e 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -21,7 +21,7 @@ is copied to all the places in the code where they are accessed. For these value can sometimes be cheaper than immutable values. Not all types for constants and immutables are implemented at this time. The only supported types are -`strings `_ (only for constants) and `value types `_. +:ref:`strings ` (only for constants) and :ref:`value types `. :: diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index f628d49fb..533f74da4 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -331,7 +331,7 @@ Modifier Overriding =================== Function modifiers can override each other. This works in the same way as -`function overriding `_ (except that there is no overloading for modifiers). The +:ref:`function overriding ` (except that there is no overloading for modifiers). The ``virtual`` keyword must be used on the overridden modifier and the ``override`` keyword must be used in the overriding modifier: diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index b68de3cad..2ff293f48 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -114,7 +114,7 @@ In general, ECDSA signatures consist of two parameters, parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in -function `ecrecover `_ that +function :ref:`ecrecover ` that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message. @@ -127,7 +127,7 @@ apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into its constituent parts is a mess, so we use -`inline assembly `_ to do the job in the ``splitSignature`` +:doc:`inline assembly ` to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section). Computing the Message Hash diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index d80af49f7..6c0dd0a16 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -22,7 +22,7 @@ Every source file should start with a comment indicating its license: The compiler does not validate that the license is part of the `list allowed by SPDX `_, but -it does include the supplied string in the `bytecode metadata `_. +it does include the supplied string in the :ref:`bytecode metadata `. If you do not want to specify a license or if the source code is not open-source, please use the special value ``UNLICENSED``. diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 30ceec3a9..3b52d7c47 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -1149,7 +1149,7 @@ Add comments above functions or contracts following `doxygen `_ with the comments +For example, the contract from :ref:`a simple smart contract ` with the comments added looks like the one below:: // SPDX-License-Identifier: GPL-3.0 @@ -1176,6 +1176,6 @@ added looks like the one below:: } } -It is recommended that Solidity contracts are fully annotated using `NatSpec `_ for all public interfaces (everything in the ABI). +It is recommended that Solidity contracts are fully annotated using :ref:`NatSpec ` for all public interfaces (everything in the ABI). -Please see the section about `NatSpec `_ for a detailed explanation. +Please see the section about :ref:`NatSpec ` for a detailed explanation. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a32900a50..a84cc555e 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -160,6 +160,8 @@ more details on error handling and when to use which function. .. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, +.. _mathematical-and-cryptographic-functions: + Mathematical and Cryptographic Functions ---------------------------------------- From 9325c4ee99f2d85abbef96cd8863e660917a59a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 13:57:38 +0200 Subject: [PATCH 106/139] docs: Update broken external links to point at new locations --- docs/contracts/events.rst | 2 +- docs/installing-solidity.rst | 2 +- docs/natspec-format.rst | 2 +- docs/units-and-global-variables.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 3183d3c3f..820039707 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -160,6 +160,6 @@ where the long hexadecimal number is equal to Additional Resources for Understanding Events ============================================== -- `Javascript documentation `_ +- `Javascript documentation `_ - `Example usage of events `_ - `How to access them in js `_ diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index e992e9491..38739e2ff 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -246,7 +246,7 @@ command-line builds: Our OS X build script uses `the Homebrew `_ package manager for installing external dependencies. Here's how to `uninstall Homebrew -`_, +`_, if you ever want to start again from scratch. Prerequisites - Windows diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index acb7a35d3..ad41fe579 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -36,7 +36,7 @@ for the purposes of NatSpec. - For Vyper, use ``"""`` indented to the inner contents with bare comments. See `Vyper - documentation `__. + documentation `__. The following example shows a contract and a function using all available tags. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a84cc555e..a6a434987 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -205,7 +205,7 @@ Mathematical and Cryptographic Functions the ecrecover function remained unchanged. This is usually not a problem unless you require signatures to be unique or - use them to identify items. OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. + use them to identify items. OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. .. note:: From 57a22a27a404a2fca8a5210eef77495c1337fabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 14:47:21 +0200 Subject: [PATCH 107/139] docs: Don't use link shortening --- docs/units-and-global-variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a6a434987..ab5d35baf 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -195,7 +195,7 @@ Mathematical and Cryptographic Functions ``ecrecover`` returns an ``address``, and not an ``address payable``. See :ref:`address payable
` for conversion, in case you need to transfer funds to the recovered address. - For further details, read `example usage `_. + For further details, read `example usage `_. .. warning:: From 46524cf1642c9b153edde43b5b4f1d8a11a2d639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 14:37:22 +0200 Subject: [PATCH 108/139] docs: Update external links to github repos that were moved --- docs/contracts/events.rst | 4 ++-- docs/metadata.rst | 2 +- docs/resources.rst | 4 ++-- docs/security-considerations.rst | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 820039707..b7289e784 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -161,5 +161,5 @@ Additional Resources for Understanding Events ============================================== - `Javascript documentation `_ -- `Example usage of events `_ -- `How to access them in js `_ +- `Example usage of events `_ +- `How to access them in js `_ diff --git a/docs/metadata.rst b/docs/metadata.rst index 240814adc..bd827a0d8 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -202,6 +202,6 @@ This automatically verifies the metadata since its hash is part of the bytecode. Excess data corresponds to the constructor input data, which should be decoded according to the interface and presented to the user. -In the repository `source-verify `_ +In the repository `sourcify `_ (`npm package `_) you can see example code that shows how to use this feature. diff --git a/docs/resources.rst b/docs/resources.rst index 02c1930aa..157f7d066 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -75,7 +75,7 @@ Solidity Integrations * `Vim Solidity `_ Plugin for the Vim editor providing syntax highlighting. - * `Vim Syntastic `_ + * `Vim Syntastic `_ Plugin for the Vim editor providing compile checking. * Visual Studio Code: @@ -145,5 +145,5 @@ Third-Party Solidity Parsers and Grammars * `solidity-parser `_ Solidity parser for JavaScript -* `Solidity Grammar for ANTLR 4 `_ +* `Solidity Grammar for ANTLR 4 `_ Solidity grammar for the ANTLR 4 parser generator diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index a471fdd00..3a6d5dfd6 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -259,7 +259,7 @@ more special edge cases for signed numbers. Try to use ``require`` to limit the size of inputs to a reasonable range and use the :ref:`SMT checker` to find potential overflows, or use a library like -`SafeMath `_ +`SafeMath `_ if you want all overflows to cause a revert. Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect. From 26ccb982228c79360c204dac3aa7a95caf943cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 14:39:34 +0200 Subject: [PATCH 109/139] docs: Update old external links to use HTTPS where possible --- docs/brand-guide.rst | 2 +- docs/contributing.rst | 4 ++-- docs/index.rst | 4 ++-- docs/installing-solidity.rst | 4 ++-- docs/introduction-to-smart-contracts.rst | 2 +- docs/resources.rst | 2 +- docs/style-guide.rst | 2 +- docs/units-and-global-variables.rst | 2 +- scripts/install_deps.sh | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/brand-guide.rst b/docs/brand-guide.rst index 0e6400bed..dda438533 100644 --- a/docs/brand-guide.rst +++ b/docs/brand-guide.rst @@ -43,7 +43,7 @@ Solidity Logo License :alt: Creative Commons License The Solidity logo is distributed and licensed under a `Creative Commons -Attribution 4.0 International License `_. +Attribution 4.0 International License `_. This is the most permissive Creative Commons license and allows reuse and modifications for any purpose. diff --git a/docs/contributing.rst b/docs/contributing.rst index 5d7dc9591..a5932c8fe 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -254,7 +254,7 @@ inside the input. We have a specialized binary called ``solfuzzer`` which takes and fails whenever it encounters an internal compiler error, segmentation fault or similar, but does not fail if e.g., the code contains an error. This way, fuzzing tools can find internal problems in the compiler. -We mainly use `AFL `_ for fuzzing. You need to download and +We mainly use `AFL `_ for fuzzing. You need to download and install the AFL packages from your repositories (afl, afl-clang) or build them manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: @@ -388,7 +388,7 @@ local slang and references, making your language as clear to all readers as poss Title Case for Headings ----------------------- -Use `title case `_ for headings. This means capitalise all principal words in +Use `title case `_ for headings. This means capitalise all principal words in titles, but not articles, conjunctions, and prepositions unless they start the title. diff --git a/docs/index.rst b/docs/index.rst index 470fc56fe..2468c4867 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -72,10 +72,10 @@ Community volunteers help translate this documentation into several languages. They have varying degrees of completeness and up-to-dateness. The English version stands as a reference. -* `French `_ (in progress) +* `French `_ (in progress) * `Italian `_ (in progress) * `Japanese `_ -* `Korean `_ (in progress) +* `Korean `_ (in progress) * `Russian `_ (rather outdated) * `Simplified Chinese `_ (in progress) * `Spanish `_ diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 38739e2ff..7a5c52d71 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -205,7 +205,7 @@ The following are dependencies for all builds of Solidity: | `cvc4`_ (Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ -.. _cvc4: http://cvc4.cs.stanford.edu/web/ +.. _cvc4: https://cvc4.cs.stanford.edu/web/ .. _Git: https://git-scm.com/download .. _Boost: https://www.boost.org .. _CMake: https://cmake.org/download/ @@ -243,7 +243,7 @@ command-line builds: sudo xcodebuild -license accept -Our OS X build script uses `the Homebrew `_ +Our OS X build script uses `the Homebrew `_ package manager for installing external dependencies. Here's how to `uninstall Homebrew `_, diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 39454ece1..f2b12e930 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -285,7 +285,7 @@ likely it will be. since it is not up to the submitter of a transaction, but up to the miners to determine in which block the transaction is included. If you want to schedule future calls of your contract, you can use - the `alarm clock `_ or a similar oracle service. + the `alarm clock `_ or a similar oracle service. .. _the-ethereum-virtual-machine: diff --git a/docs/resources.rst b/docs/resources.rst index 157f7d066..a4eecd34e 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -80,7 +80,7 @@ Solidity Integrations * Visual Studio Code: - * `Visual Studio Code extension `_ + * `Visual Studio Code extension `_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. Discontinued: diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 3b52d7c47..73a65df08 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -1145,7 +1145,7 @@ NatSpec Solidity contracts can have a form of comments that are the basis of the Ethereum Natural Language Specification Format. -Add comments above functions or contracts following `doxygen `_ notation +Add comments above functions or contracts following `doxygen `_ notation of one or multiple lines starting with ``///`` or a multiline comment starting with ``/**`` and ending with ``*/``. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index ab5d35baf..b67f84739 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -201,7 +201,7 @@ Mathematical and Cryptographic Functions If you use ``ecrecover``, be aware that a valid signature can be turned into a different valid signature without requiring knowledge of the corresponding private key. In the Homestead hard fork, this issue was fixed - for _transaction_ signatures (see `EIP-2 `_), but + for _transaction_ signatures (see `EIP-2 `_), but the ecrecover function remained unchanged. This is usually not a problem unless you require signatures to be unique or diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index a825a6d84..87f889e8f 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -101,7 +101,7 @@ case $(uname -s) in esac # Check for Homebrew install and abort if it is not installed. - brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See http://brew.sh."; exit 1; } + brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See https://brew.sh."; exit 1; } brew update brew install boost brew install cmake From 961e3f3bccf2f433ecf9dc03e35cbcdf76ad1dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 6 Aug 2020 15:24:35 +0200 Subject: [PATCH 110/139] docs: Remove/update obsolete links to Solidity-related resources --- docs/resources.rst | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/docs/resources.rst b/docs/resources.rst index a4eecd34e..01c4715a1 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -83,17 +83,6 @@ Solidity Integrations * `Visual Studio Code extension `_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. -Discontinued: - -* `Mix IDE `_ - Qt based IDE for designing, debugging and testing solidity smart contracts. - -* `Ethereum Studio `_ - Specialized web IDE that also provides shell access to a complete Ethereum environment. - -* `Visual Studio Extension `_ - Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. - Solidity Tools ~~~~~~~~~~~~~~ @@ -142,8 +131,5 @@ Solidity Tools Third-Party Solidity Parsers and Grammars ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* `solidity-parser `_ - Solidity parser for JavaScript - -* `Solidity Grammar for ANTLR 4 `_ - Solidity grammar for the ANTLR 4 parser generator +* `Solidity Parser for JavaScript `_ + A Solidity parser for JS built on top of a robust ANTLR4 grammar. From 6336aa5abac4dfd3ab5e4c3c36f48ff374ef264b Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 29 Aug 2020 21:57:37 +0200 Subject: [PATCH 111/139] Fix test expectation update --- test/libsolidity/util/TestFunctionCall.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index f961c615c..b614d17b9 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -300,7 +300,8 @@ string TestFunctionCall::formatRawParameters( { if (param.format.newline) os << endl << _linePrefix << "// "; - os << param.rawString; + for (auto const c: param.rawString) + os << (c >= ' ' ? string(1, c) : "\\x" + toHex(static_cast(c))); if (¶m != &_params.back()) os << ", "; } From 2ebc4bb9a702e79513621ed5223119fe38745e12 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Jul 2020 18:19:08 +0100 Subject: [PATCH 112/139] Introduce TokenTraits::isYulKeyword helper --- liblangutil/Token.cpp | 5 +++++ liblangutil/Token.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/liblangutil/Token.cpp b/liblangutil/Token.cpp index 70b645992..bbcfe1590 100644 --- a/liblangutil/Token.cpp +++ b/liblangutil/Token.cpp @@ -152,6 +152,11 @@ static Token keywordByName(string const& _name) return it == keywords.end() ? Token::Identifier : it->second; } +bool isYulKeyword(string const& _literal) +{ + return _literal == "leave" || isYulKeyword(keywordByName(_literal)); +} + tuple fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 2bfc58e44..3d9c71ab3 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -327,6 +327,8 @@ namespace TokenTraits tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; } + bool isYulKeyword(std::string const& _literal); + inline Token AssignmentToBinaryOp(Token op) { solAssert(isAssignmentOp(op) && op != Token::Assign, ""); From 242661685969fc50e7162cd7583941a730a34f13 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Jul 2020 18:25:12 +0100 Subject: [PATCH 113/139] Disallow language keywords to be used as identifiers by NameDispenser and VarNameCleaner Also introduce isRestrictedIdentifier helper in OptimizerUtilities --- Changelog.md | 1 + libyul/optimiser/NameDispenser.cpp | 6 +++--- libyul/optimiser/OptimizerUtilities.cpp | 9 +++++++++ libyul/optimiser/OptimizerUtilities.h | 6 ++++++ libyul/optimiser/VarNameCleaner.cpp | 5 ++--- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index d670209ad..6126ea6d4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ Bugfixes: * SMTChecker: Fix internal error on fixed bytes index access. * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. + * Yul Optimizer: Ensure that Yul keywords are not mistakenly used by the NameDispenser and VarNameCleaners. The bug would manifest as uncompilable code. * Type Checker: Disallow signed literals as exponent in exponentiation operator. * Allow `type(Contract).name` for abstract contracts and interfaces. * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index ec091b2d7..eaf9b8b4b 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -22,8 +22,10 @@ #include #include +#include #include #include +#include #include @@ -57,7 +59,5 @@ YulString NameDispenser::newName(YulString _nameHint) bool NameDispenser::illegalName(YulString _name) { - if (_name.empty() || m_usedNames.count(_name) || m_dialect.builtin(_name)) - return true; - return false; + return isRestrictedIdentifier(m_dialect, _name) || m_usedNames.count(_name); } diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp index 3cb66d689..ad46f7cb1 100644 --- a/libyul/optimiser/OptimizerUtilities.cpp +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -21,15 +21,19 @@ #include +#include #include +#include #include #include using namespace std; using namespace solidity; +using namespace solidity::langutil; using namespace solidity::util; +using namespace solidity::yul; void yul::removeEmptyBlocks(Block& _block) { @@ -38,3 +42,8 @@ void yul::removeEmptyBlocks(Block& _block) }; boost::range::remove_erase_if(_block.statements, isEmptyBlock); } + +bool yul::isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifier) +{ + return _identifier.empty() || TokenTraits::isYulKeyword(_identifier.str()) || _dialect.builtin(_identifier); +} diff --git a/libyul/optimiser/OptimizerUtilities.h b/libyul/optimiser/OptimizerUtilities.h index 61d970877..829d10b55 100644 --- a/libyul/optimiser/OptimizerUtilities.h +++ b/libyul/optimiser/OptimizerUtilities.h @@ -23,6 +23,8 @@ #include #include +#include +#include namespace solidity::yul { @@ -30,4 +32,8 @@ namespace solidity::yul /// Removes statements that are just empty blocks (non-recursive). void removeEmptyBlocks(Block& _block); +/// Returns true if a given literal can not be used as an identifier. +/// This includes Yul keywords and builtins of the given dialect. +bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifier); + } diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index edea33d2b..85ecece46 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include +#include #include #include @@ -110,9 +111,7 @@ YulString VarNameCleaner::findCleanName(YulString const& _name) const bool VarNameCleaner::isUsedName(YulString const& _name) const { - if (_name.empty() || m_dialect.builtin(_name) || m_usedNames.count(_name)) - return true; - return false; + return isRestrictedIdentifier(m_dialect, _name) || m_usedNames.count(_name); } YulString VarNameCleaner::stripSuffix(YulString const& _name) const From af482558cf4e3b1d355a87b3120637f88442c17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 31 Aug 2020 13:54:22 +0200 Subject: [PATCH 114/139] natspec-format.rst: Update broken link to "Contract ABI" --- docs/natspec-format.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index ad41fe579..21e78f2df 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -195,8 +195,8 @@ JSON file as output: } Note that the key by which to find the methods is the function's -canonical signature as defined in the `Contract -ABI `__ and not simply the function's +canonical signature as defined in the :ref:`Contract +ABI ` and not simply the function's name. .. _header-developer-doc: From 24b3e22ffbe7a1347c06c58a6274e1ccac895544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 31 Aug 2020 18:44:06 +0200 Subject: [PATCH 115/139] Release checklist: add dead link check --- ReleaseChecklist.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 8cdc00591..069098f20 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -6,6 +6,9 @@ - [ ] Readthedocs account, access to the Solidity project - [ ] Write access to https://github.com/ethereum/homebrew-ethereum +### Documentation check + - [ ] Run `make linkcheck` from within `docs/` and fix any broken links it finds. Ignore false positives caused by `href` anchors and dummy links not meant to work. + ### Blog Post - [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts. From 8faa5243b5e78d25d78d6635bc80f8bc0afdbd9e Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 22 Aug 2020 00:01:29 +0200 Subject: [PATCH 116/139] Fix MSVC ADL --- cmake/EthCompilerSettings.cmake | 3 ++- libsolutil/CommonData.h | 7 +------ test/ExecutionFramework.cpp | 7 +++++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 3bcdad47b..653c5fdc3 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -157,9 +157,10 @@ elseif (DEFINED MSVC) add_compile_options(/wd4800) # disable forcing value to bool 'true' or 'false' (performance warning) (4800) add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions - add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) + add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) add_compile_options(-DBOOST_REGEX_NO_LIB) # disable automatic boost::regex library selection add_compile_options(-D_REGEX_MAX_STACK_COUNT=200000L) # increase std::regex recursion depth limit + add_compile_options(/permissive-) # specify standards conformance mode to the compiler # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index 5fe0c2bfb..b01e24c38 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -37,10 +37,7 @@ #include #include -namespace std -{ - -/// Operator overloads for STL containers should be in std namespace for ADL to work properly. +/// Operators need to stay in the global namespace. /// Concatenate the contents of a container onto a vector template std::vector& operator+=(std::vector& _a, U& _b) @@ -147,8 +144,6 @@ inline std::multiset& operator-=(std::multiset& _a, C const& _b) return _a; } -} // end namespace std - namespace solidity::util { diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index b3841f6b4..7f5dfcf74 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -97,12 +97,15 @@ u256 ExecutionFramework::gasLimit() const u256 ExecutionFramework::gasPrice() const { - return {EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; + // here and below we use "return u256{....}" instead of just "return {....}" + // to please MSVC and avoid unexpected + // warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied + return u256{EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; } u256 ExecutionFramework::blockHash(u256 const& _number) const { - return {EVMHost::convertFromEVMC( + return u256{EVMHost::convertFromEVMC( m_evmHost->get_block_hash(static_cast(_number & numeric_limits::max())) )}; } From 5f7b4a2e059d616e7c721c3576483b6a30e9590b Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 31 Aug 2020 19:04:38 -0500 Subject: [PATCH 117/139] OSX: Fix readlink issues and that ASTImportTest.sh silently ignores errors. --- scripts/ASTImportTest.sh | 35 ++++++++++++++++++++++---- scripts/splitSources.py | 46 ++++++++++++++++++++++++++--------- scripts/test_antlr_grammar.sh | 8 ++++-- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 6f8d8b06e..02125c921 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +set -e + # Bash script to test the ast-import option of the compiler by # first exporting a .sol file to JSON, then loading it into the compiler # and exporting it again. The second JSON should be identical to the first - -REPO_ROOT=$(readlink -f "$(dirname "$0")"/..) +READLINK=readlink +if [[ "$OSTYPE" == "darwin"* ]]; then + READLINK=greadlink +fi +REPO_ROOT=$(${READLINK} -f "$(dirname "$0")"/..) SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build} SOLC=${SOLIDITY_BUILD_DIR}/solc/solc SPLITSOURCES=${REPO_ROOT}/scripts/splitSources.py @@ -83,8 +88,11 @@ do FILETMP=$(mktemp -d) cd $FILETMP + set +e OUTPUT=$($SPLITSOURCES $solfile) - if [ $? != 1 ] + SPLITSOURCES_RC=$? + set -e + if [ ${SPLITSOURCES_RC} == 0 ] then # echo $OUTPUT NSOURCES=$((NSOURCES - 1)) @@ -93,9 +101,26 @@ do testImportExportEquivalence $i $OUTPUT NSOURCES=$((NSOURCES + 1)) done - - else + elif [ ${SPLITSOURCES_RC} == 1 ] + then testImportExportEquivalence $solfile + elif [ ${SPLITSOURCES_RC} == 2 ] + then + # The script will exit with return code 2, if an UnicodeDecodeError occurred. + # This is the case if e.g. some tests are using invalid utf-8 sequences. We will ignore + # these errors, but print the actual output of the script. + echo -e "\n${OUTPUT}\n" + testImportExportEquivalence $solfile + else + # All other return codes will be treated as critical errors. The script will exit. + echo -e "\nGot unexpected return code ${SPLITSOURCES_RC} from ${SPLITSOURCES}. Aborting." + echo -e "\n${OUTPUT}\n" + + cd $WORKINGDIR + # Delete temporary files + rm -rf $FILETMP + + exit 1 fi cd $WORKINGDIR diff --git a/scripts/splitSources.py b/scripts/splitSources.py index 01a9f9278..54ddb2f38 100755 --- a/scripts/splitSources.py +++ b/scripts/splitSources.py @@ -11,10 +11,20 @@ import sys import os +import traceback hasMultipleSources = False createdSources = [] + +def uncaught_exception_hook(exc_type, exc_value, exc_traceback): + # The script `scripts/ASTImportTest.sh` will interpret return code 3 + # as a critical error (because of the uncaught exception) and will + # terminate further execution. + print("Unhandled exception: %s", "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))) + sys.exit(3) + + def extractSourceName(line): if line.find("/") > -1: filePath = line[13: line.rindex("/")] @@ -23,6 +33,7 @@ def extractSourceName(line): return filePath, srcName return False, line[line.find(":")+2 : line.find(" ====")] + # expects the first line of lines to be "==== Source: sourceName ====" # writes the following source into a file named sourceName def writeSourceToFile(lines): @@ -45,18 +56,29 @@ def writeSourceToFile(lines): writeSourceToFile(lines[1+idx:]) break + if __name__ == '__main__': filePath = sys.argv[1] - # decide if file has multiple sources - lines = open(filePath, mode='r', encoding='utf8').read().splitlines() - if lines[0][:12] == "==== Source:": - hasMultipleSources = True - writeSourceToFile(lines) + sys.excepthook = uncaught_exception_hook - if hasMultipleSources: - srcString = "" - for src in createdSources: - srcString += src + ' ' - print(srcString) - else: - sys.exit(1) + try: + # decide if file has multiple sources + lines = open(filePath, mode='r', encoding='utf8').read().splitlines() + if lines[0][:12] == "==== Source:": + hasMultipleSources = True + writeSourceToFile(lines) + + if hasMultipleSources: + srcString = "" + for src in createdSources: + srcString += src + ' ' + print(srcString) + sys.exit(0) + else: + sys.exit(1) + + except UnicodeDecodeError as ude: + print("UnicodeDecodeError in '" + filePath + "': " + str(ude)) + print("This is expected for some tests containing invalid utf8 sequences. " + "Exception will be ignored.") + sys.exit(2) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 846cecf80..a41387ee9 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -2,7 +2,11 @@ set -e -ROOT_DIR=$(readlink -f "$(dirname "$0")"/..) +READLINK=readlink +if [[ "$OSTYPE" == "darwin"* ]]; then + READLINK=greadlink +fi +ROOT_DIR=$(${READLINK} -f "$(dirname "$0")"/..) WORKDIR="${ROOT_DIR}/build/antlr" ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" @@ -54,7 +58,7 @@ failed_count=0 test_file() { local SOL_FILE - SOL_FILE="$(readlink -m "${1}")" + SOL_FILE="$(${READLINK} -m "${1}")" local cur=${2} local max=${3} local solOrYul=${4} From 50e0ada77dbfe68ba68fe3cb5d40c1a588027b05 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 31 Aug 2020 12:59:11 +0200 Subject: [PATCH 118/139] [SMTChecker] Fix unary operator on lvalue tuple --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 45 +++++++++---------- libsolidity/formal/SMTEncoder.h | 5 ++- .../operators/unary_operators_tuple_1.sol | 11 +++++ .../operators/unary_operators_tuple_2.sol | 11 +++++ .../operators/unary_operators_tuple_3.sol | 12 +++++ 6 files changed, 60 insertions(+), 25 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol diff --git a/Changelog.md b/Changelog.md index 8410eea46..29498feac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. + * SMTChecker: Fix internal error on lvalue unary operators with tuples. * SMTChecker: Fix soundness of array ``pop``. * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 340825838..2f7313bc6 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -463,12 +463,14 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) createExpr(_op); + auto const* subExpr = innermostTuple(_op.subExpression()); + switch (_op.getOperator()) { case Token::Not: // ! { solAssert(smt::isBool(_op.annotation().type->category()), ""); - defineExpr(_op, !expr(_op.subExpression())); + defineExpr(_op, !expr(*subExpr)); break; } case Token::Inc: // ++ (pre- or postfix) @@ -476,8 +478,8 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) { auto cat = _op.annotation().type->category(); solAssert(smt::isInteger(cat) || smt::isFixedPoint(cat), ""); - solAssert(_op.subExpression().annotation().willBeWrittenTo, ""); - if (auto identifier = dynamic_cast(&_op.subExpression())) + solAssert(subExpr->annotation().willBeWrittenTo, ""); + if (auto identifier = dynamic_cast(subExpr)) { auto decl = identifierToVariable(*identifier); solAssert(decl, ""); @@ -486,12 +488,12 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); assignment(*decl, newValue); } - else if (dynamic_cast(&_op.subExpression())) + else if (dynamic_cast(subExpr)) { - auto innerValue = expr(_op.subExpression()); + auto innerValue = expr(*subExpr); auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); - arrayIndexAssignment(_op.subExpression(), newValue); + arrayIndexAssignment(*subExpr, newValue); } else m_errorReporter.warning( @@ -504,25 +506,24 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) } case Token::Sub: // - { - defineExpr(_op, 0 - expr(_op.subExpression())); + defineExpr(_op, 0 - expr(*subExpr)); break; } case Token::Delete: { - auto const& subExpr = _op.subExpression(); - if (auto decl = identifierToVariable(subExpr)) + if (auto decl = identifierToVariable(*subExpr)) { m_context.newValue(*decl); m_context.setZeroValue(*decl); } else { - solAssert(m_context.knownExpression(subExpr), ""); - auto const& symbVar = m_context.expression(subExpr); + solAssert(m_context.knownExpression(*subExpr), ""); + auto const& symbVar = m_context.expression(*subExpr); symbVar->increaseIndex(); m_context.setZeroValue(*symbVar); - if (dynamic_cast(&_op.subExpression())) - arrayIndexAssignment(_op.subExpression(), symbVar->currentValue()); + if (dynamic_cast(subExpr)) + arrayIndexAssignment(*subExpr, symbVar->currentValue()); else m_errorReporter.warning( 2683_error, @@ -1122,9 +1123,7 @@ void SMTEncoder::arrayPop(FunctionCall const& _funCall) void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression const& _array) { - Expression const* expr = &_expr; - if (auto const* tupleExpr = dynamic_cast(expr)) - expr = innermostTuple(*tupleExpr); + Expression const* expr = innermostTuple(_expr); if (auto const* id = dynamic_cast(expr)) { @@ -1461,9 +1460,7 @@ void SMTEncoder::assignment( "Tuple assignments should be handled by tupleAssignment." ); - Expression const* left = &_left; - if (auto const* tuple = dynamic_cast(left)) - left = innermostTuple(*tuple); + Expression const* left = innermostTuple(_left); if (!smt::isSupportedType(_type->category())) { @@ -1491,7 +1488,7 @@ void SMTEncoder::assignment( void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right) { - auto lTuple = dynamic_cast(innermostTuple(dynamic_cast(_left))); + auto lTuple = dynamic_cast(innermostTuple(_left)); solAssert(lTuple, ""); auto const& lComponents = lTuple->components(); @@ -1917,10 +1914,12 @@ Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) return base; } -Expression const* SMTEncoder::innermostTuple(TupleExpression const& _tuple) +Expression const* SMTEncoder::innermostTuple(Expression const& _expr) { - solAssert(!_tuple.isInlineArray(), ""); - TupleExpression const* tuple = &_tuple; + auto const* tuple = dynamic_cast(&_expr); + if (!tuple || tuple->isInlineArray()) + return &_expr; + Expression const* expr = tuple; while (tuple && !tuple->isInlineArray() && tuple->components().size() == 1) { diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 122c8544f..fb5dce405 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -54,8 +54,9 @@ public: /// @returns the leftmost identifier in a multi-d IndexAccess. static Expression const* leftmostBase(IndexAccess const& _indexAccess); - /// @returns the innermost element in a chain of 1-tuples. - static Expression const* innermostTuple(TupleExpression const& _tuple); + /// @returns the innermost element in a chain of 1-tuples if applicable, + /// otherwise _expr. + static Expression const* innermostTuple(Expression const& _expr); /// @returns the FunctionDefinition of a FunctionCall /// if possible or nullptr. diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol new file mode 100644 index 000000000..69411e3c2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++(x); + if (b) --(x); + if (b) delete(b); + assert(x == 0); + assert(!b); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol new file mode 100644 index 000000000..c16f01402 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++((((((x)))))); + if (b) --((((((x)))))); + if (b) delete((((((b)))))); + assert(x == 0); + assert(!b); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol new file mode 100644 index 000000000..6ab61f0ee --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++(x); + else x += 1; + assert(x == 1); + assert(!b); + } +} +// ---- +// Warning 6328: (140-150): Assertion violation happens here From 7ca335adde39ec1aea44b0518919d11b6377b1b5 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 31 Aug 2020 17:10:37 +0200 Subject: [PATCH 119/139] Decrease rlimit --- libsmtutil/Z3Interface.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libsmtutil/Z3Interface.h b/libsmtutil/Z3Interface.h index 22f793337..23534fd75 100644 --- a/libsmtutil/Z3Interface.h +++ b/libsmtutil/Z3Interface.h @@ -49,9 +49,7 @@ public: // Z3 "basic resources" limit. // This is used to make the runs more deterministic and platform/machine independent. - // The tests start failing for Z3 with less than 10000000, - // so using double that. - static int const resourceLimit = 20000000; + static int const resourceLimit = 12500000; private: void declareFunction(std::string const& _name, Sort const& _sort); From 5cafbeebecf7e4f526191ca01abffbe3b0bc1e5b Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 31 Aug 2020 16:12:38 +0200 Subject: [PATCH 120/139] [SMTChecker] Fix ICE on tuple assignment --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 7 ++++--- .../types/tuple_different_count_assignment_1.sol | 11 +++++++++++ .../types/tuple_different_count_assignment_2.sol | 11 +++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol create mode 100644 test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol diff --git a/Changelog.md b/Changelog.md index 29498feac..c3ffc0ed9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,7 @@ Bugfixes: * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. * SMTChecker: Fix internal error on lvalue unary operators with tuples. + * SMTChecker: Fix internal error on tuple assignment. * SMTChecker: Fix soundness of array ``pop``. * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 2f7313bc6..7cb8c0367 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1490,13 +1490,14 @@ void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _rig { auto lTuple = dynamic_cast(innermostTuple(_left)); solAssert(lTuple, ""); + Expression const* right = innermostTuple(_right); auto const& lComponents = lTuple->components(); // If both sides are tuple expressions, we individually and potentially // recursively assign each pair of components. // This is because of potential type conversion. - if (auto rTuple = dynamic_cast(&_right)) + if (auto rTuple = dynamic_cast(right)) { auto const& rComponents = rTuple->components(); solAssert(lComponents.size() == rComponents.size(), ""); @@ -1517,13 +1518,13 @@ void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _rig } else { - auto rType = dynamic_cast(_right.annotation().type); + auto rType = dynamic_cast(right->annotation().type); solAssert(rType, ""); auto const& rComponents = rType->components(); solAssert(lComponents.size() == rComponents.size(), ""); - auto symbRight = expr(_right); + auto symbRight = expr(*right); solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, ""); for (unsigned i = 0; i < lComponents.size(); ++i) diff --git a/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol new file mode 100644 index 000000000..b0861b3b5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns(int) { + int a; + (,, a) = ((((((1, 3, (((((2))))))))))); + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning 6328: (157-171): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol new file mode 100644 index 000000000..02a14c1b4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns(int) { + int a; + ((,, a)) = ((((((1, 3, (((((2))))))))))); + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning 6328: (159-173): Assertion violation happens here From 238b8a929e5a6502e59ac95443350c6675e2e06a Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 31 Aug 2020 16:45:52 +0200 Subject: [PATCH 121/139] [SMTChecker] Fix ICE on tuples of one element that actually have tuple type --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 12 ++++-------- .../smtCheckerTests/types/tuple_extra_parens_7.sol | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol diff --git a/Changelog.md b/Changelog.md index c3ffc0ed9..9c54eabe5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ Bugfixes: * SMTChecker: Fix internal error on fixed bytes index access. * SMTChecker: Fix internal error on lvalue unary operators with tuples. * SMTChecker: Fix internal error on tuple assignment. + * SMTChecker: Fix internal error on tuples of one element that have tuple type. * SMTChecker: Fix soundness of array ``pop``. * References Resolver: Fix internal bug when using constructor for library. * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 7cb8c0367..8cbed6712 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -420,8 +420,11 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) _tuple.location(), "Assertion checker does not yet implement inline arrays." ); - else if (_tuple.annotation().type->category() == Type::Category::Tuple) + else if (_tuple.components().size() == 1) + defineExpr(_tuple, expr(*_tuple.components().front())); + else { + solAssert(_tuple.annotation().type->category() == Type::Category::Tuple, ""); auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_tuple)); solAssert(symbTuple, ""); auto const& symbComponents = symbTuple->components(); @@ -445,13 +448,6 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) } } } - else - { - /// Parenthesized expressions are also TupleExpression regardless their type. - auto const& components = _tuple.components(); - solAssert(components.size() == 1, ""); - defineExpr(_tuple, expr(*components.front())); - } } void SMTEncoder::endVisit(UnaryOperation const& _op) diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol new file mode 100644 index 000000000..824d84c3e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C { + function g() internal pure returns (uint, uint) { + return (2, 3); + } + function f() public { + (address(1).call("")); + (uint x, uint y) = ((g())); + assert(x == 2); + assert(y == 3); + } +} +// ---- +// Warning 5084: (142-152): Type conversion is not yet fully supported and might yield false positives. From c6dff971cba77a5043d7ffefba398b6539069f69 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 1 Sep 2020 14:29:44 +0200 Subject: [PATCH 122/139] Do not generally override CXX standard. --- cmake/EthToolchains.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/EthToolchains.cmake b/cmake/EthToolchains.cmake index adf0a20a5..c2306bd75 100644 --- a/cmake/EthToolchains.cmake +++ b/cmake/EthToolchains.cmake @@ -1,5 +1,7 @@ # Require C++17. -set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. +if (NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. +endif () set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_EXTENSIONS OFF) From 016b9b83a86ebcc3cf1c68448a28421eb9846ae6 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 13 Aug 2020 14:00:33 +0200 Subject: [PATCH 123/139] Refactor predicates --- libsolidity/CMakeLists.txt | 2 + libsolidity/formal/CHC.cpp | 116 +++++++++--------- libsolidity/formal/CHC.h | 33 ++--- libsolidity/formal/Predicate.cpp | 92 ++++++++++++++ libsolidity/formal/Predicate.h | 86 +++++++++++++ .../types/array_aliasing_memory_1.sol | 2 +- 6 files changed, 253 insertions(+), 78 deletions(-) create mode 100644 libsolidity/formal/Predicate.cpp create mode 100644 libsolidity/formal/Predicate.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 74f4a9a21..5e823d8ee 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -100,6 +100,8 @@ set(sources formal/EncodingContext.h formal/ModelChecker.cpp formal/ModelChecker.h + formal/Predicate.cpp + formal/Predicate.h formal/SMTEncoder.cpp formal/SMTEncoder.h formal/SSAVariable.cpp diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 65df7e203..ea8ad765a 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -85,11 +85,7 @@ void CHC::analyze(SourceUnit const& _source) resetSourceAnalysis(); - auto genesisSort = make_shared( - vector(), - smtutil::SortProvider::boolSort - ); - m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); + m_genesisPredicate = createSymbolicBlock(arity0FunctionSort(), "genesis"); addRule(genesis(), "genesis"); set sources; @@ -124,10 +120,8 @@ bool CHC::visit(ContractDefinition const& _contract) clearIndices(&_contract); string suffix = _contract.name() + "_" + to_string(_contract.id()); - m_errorPredicate = createSymbolicBlock(arity0FunctionSort(), "error_" + suffix); - m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix); - m_symbolFunction[m_constructorSummaryPredicate->currentFunctionValue().name] = &_contract; - m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix); + m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix, &_contract); + m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix, &_contract); auto stateExprs = currentStateVariables(); setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); @@ -233,7 +227,7 @@ void CHC::endVisit(FunctionDefinition const& _function) if (_function.isConstructor()) { string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id()); - auto constructorExit = createSymbolicBlock(constructorSort(), "constructor_exit_" + suffix); + auto constructorExit = createSymbolicBlock(constructorSort(), "constructor_exit_" + suffix, m_currentContract); connectBlocks(m_currentBlock, predicate(*constructorExit, currentFunctionVariables(*m_currentContract))); clearIndices(m_currentContract, m_currentFunction); @@ -326,8 +320,8 @@ bool CHC::visit(WhileStatement const& _while) auto outerBreakDest = m_breakDest; auto outerContinueDest = m_continueDest; - m_breakDest = afterLoopBlock.get(); - m_continueDest = loopHeaderBlock.get(); + m_breakDest = afterLoopBlock; + m_continueDest = loopHeaderBlock; if (_while.isDoWhile()) _while.body().accept(*this); @@ -377,8 +371,8 @@ bool CHC::visit(ForStatement const& _for) auto outerBreakDest = m_breakDest; auto outerContinueDest = m_continueDest; - m_breakDest = afterLoopBlock.get(); - m_continueDest = postLoop ? postLoopBlock.get() : loopHeaderBlock.get(); + m_breakDest = afterLoopBlock; + m_continueDest = postLoop ? postLoopBlock : loopHeaderBlock; if (auto init = _for.initializationExpression()) init->accept(*this); @@ -571,7 +565,6 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) m_context.variable(*var)->increaseIndex(); auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + currentStateVariables()); - m_symbolFunction[nondet.name] = &_funCall; m_context.addAssertion(nondet); m_context.addAssertion(m_error.currentValue() == 0); @@ -681,7 +674,9 @@ void CHC::resetSourceAnalysis() m_errorIds.clear(); m_callGraph.clear(); m_summaries.clear(); - m_symbolFunction.clear(); + m_interfaces.clear(); + m_nondetInterfaces.clear(); + Predicate::reset(); } void CHC::resetContractAnalysis() @@ -717,7 +712,7 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c } void CHC::setCurrentBlock( - smt::SymbolicFunctionVariable const& _block, + Predicate const& _block, vector const* _arguments ) { @@ -806,7 +801,7 @@ smtutil::SortPointer CHC::nondetInterfaceSort(ContractDefinition const& _contrac ); } -smtutil::SortPointer CHC::arity0FunctionSort() +smtutil::SortPointer CHC::arity0FunctionSort() const { return make_shared( vector(), @@ -870,14 +865,10 @@ smtutil::SortPointer CHC::summarySort(FunctionDefinition const& _function, Contr ); } -unique_ptr CHC::createSymbolicBlock(smtutil::SortPointer _sort, string const& _name) +Predicate const* CHC::createSymbolicBlock(SortPointer _sort, string const& _name, ASTNode const* _node) { - auto block = make_unique( - _sort, - _name, - m_context - ); - m_interface->registerRelation(block->currentFunctionValue()); + auto const* block = Predicate::create(_sort, _name, m_context, _node); + m_interface->registerRelation(block->functor()); return block; } @@ -887,20 +878,24 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) 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); - m_nondetInterfaces[base] = createSymbolicBlock(nondetInterfaceSort(*base), "nondet_interface_" + suffix); - for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*base)) if (!m_context.knownVariable(*var)) createVariable(*var); - /// Base nondeterministic interface that allows - /// 0 steps to be taken, used as base for the inductive - /// rule for each function. - auto const& iface = *m_nondetInterfaces.at(base); - auto state0 = stateVariablesAtIndex(0, *base); - addRule(iface(state0 + state0), "base_nondet"); + if (!m_interfaces.count(base)) + { + solAssert(!m_nondetInterfaces.count(base), ""); + string suffix = base->name() + "_" + to_string(base->id()); + m_interfaces.emplace(base, createSymbolicBlock(interfaceSort(*base), "interface_" + suffix, base)); + m_nondetInterfaces.emplace(base, createSymbolicBlock(nondetInterfaceSort(*base), "nondet_interface_" + suffix, base)); + + /// Base nondeterministic interface that allows + /// 0 steps to be taken, used as base for the inductive + /// rule for each function. + auto const* iface = m_nondetInterfaces.at(base); + auto state0 = stateVariablesAtIndex(0, *base); + addRule((*iface)(state0 + state0), "base_nondet"); + } for (auto const* function: base->definedFunctions()) { @@ -923,8 +918,10 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) auto state1 = stateVariablesAtIndex(1, *base); auto state2 = stateVariablesAtIndex(2, *base); - auto nondetPre = iface(state0 + state1); - auto nondetPost = iface(state0 + state2); + auto const* iface = m_nondetInterfaces.at(base); + auto state0 = stateVariablesAtIndex(0, *base); + auto nondetPre = (*iface)(state0 + state1); + auto nondetPost = (*iface)(state0 + state2); vector args{m_error.currentValue()}; args += state1 + @@ -960,7 +957,7 @@ smtutil::Expression CHC::error() smtutil::Expression CHC::error(unsigned _idx) { - return m_errorPredicate->functionValueAtIndex(_idx)({}); + return m_errorPredicate->functor(_idx)({}); } smtutil::Expression CHC::summary(ContractDefinition const& _contract) @@ -994,37 +991,33 @@ smtutil::Expression CHC::summary(FunctionDefinition const& _function) return summary(_function, *m_currentContract); } -unique_ptr CHC::createBlock(ASTNode const* _node, string const& _prefix) +Predicate const* CHC::createBlock(ASTNode const* _node, string const& _prefix) { - auto block = createSymbolicBlock(sort(_node), - "block_" + - uniquePrefix() + - "_" + - _prefix + - predicateName(_node)); + auto block = createSymbolicBlock( + sort(_node), + "block_" + uniquePrefix() + "_" + _prefix + predicateName(_node), + _node + ); solAssert(m_currentFunction, ""); - m_symbolFunction[block->currentFunctionValue().name] = m_currentFunction; return block; } -unique_ptr CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) +Predicate const* CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) { - auto block = createSymbolicBlock(summarySort(_function, _contract), - "summary_" + - uniquePrefix() + - "_" + - predicateName(&_function, &_contract)); + auto block = createSymbolicBlock( + summarySort(_function, _contract), + "summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract), + &_function + ); - m_symbolFunction[block->currentFunctionValue().name] = &_function; return block; } void CHC::createErrorBlock() { - solAssert(m_errorPredicate, ""); - m_errorPredicate->increaseIndex(); - m_interface->registerRelation(m_errorPredicate->currentFunctionValue()); + m_errorPredicate = createSymbolicBlock(arity0FunctionSort(), "error_target_" + to_string(m_context.newUniqueId())); + m_interface->registerRelation(m_errorPredicate->functor()); } void CHC::connectBlocks(smtutil::Expression const& _from, smtutil::Expression const& _to, smtutil::Expression const& _constraints) @@ -1128,13 +1121,13 @@ string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contr return prefix + "_" + to_string(_node->id()) + "_" + to_string(contract->id()); } -smtutil::Expression CHC::predicate(smt::SymbolicFunctionVariable const& _block) +smtutil::Expression CHC::predicate(Predicate const& _block) { return _block(currentBlockVariables()); } smtutil::Expression CHC::predicate( - smt::SymbolicFunctionVariable const& _block, + Predicate const& _block, vector const& _arguments ) { @@ -1441,18 +1434,19 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& /// At this point property 2 from the function description is verified for this node. auto const& summaryNode = _graph.nodes.at(summaryId); - solAssert(m_symbolFunction.count(summaryNode.first), ""); + Predicate const* summaryPredicate = Predicate::predicate(summaryNode.first); + solAssert(summaryPredicate, ""); FunctionDefinition const* calledFun = nullptr; ContractDefinition const* calledContract = nullptr; - if (auto const* contract = dynamic_cast(m_symbolFunction.at(summaryNode.first))) + if (auto const* contract = dynamic_cast(summaryPredicate->programNode())) { if (auto const* constructor = contract->constructor()) calledFun = constructor; else calledContract = contract; } - else if (auto const* fun = dynamic_cast(m_symbolFunction.at(summaryNode.first))) + else if (auto const* fun = dynamic_cast(summaryPredicate->programNode())) calledFun = fun; else solAssert(false, ""); diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index ac1315317..8b1c09a2b 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -31,6 +31,7 @@ #pragma once +#include #include #include @@ -108,7 +109,7 @@ private: void resetContractAnalysis(); void eraseKnowledge(); void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; - void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector const* _arguments = nullptr); + void setCurrentBlock(Predicate const& _block, std::vector const* _arguments = nullptr); std::set transactionAssertions(ASTNode const* _txRoot); static std::vector stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); static std::vector stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); @@ -122,7 +123,7 @@ private: smtutil::SortPointer nondetInterfaceSort(); static smtutil::SortPointer interfaceSort(ContractDefinition const& _const); static smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _const); - smtutil::SortPointer arity0FunctionSort(); + smtutil::SortPointer arity0FunctionSort() const; smtutil::SortPointer sort(FunctionDefinition const& _function); smtutil::SortPointer sort(ASTNode const* _block); /// @returns the sort of a predicate that represents the summary of _function in the scope of _contract. @@ -134,7 +135,7 @@ private: /// Predicate helpers. //@{ /// @returns a new block of given _sort and _name. - std::unique_ptr createSymbolicBlock(smtutil::SortPointer _sort, std::string const& _name); + Predicate const* createSymbolicBlock(smtutil::SortPointer _sort, std::string const& _name, ASTNode const* _node = nullptr); /// Creates summary predicates for all functions of all contracts /// in a given _source. @@ -150,10 +151,10 @@ private: smtutil::Expression error(unsigned _idx); /// Creates a block for the given _node. - std::unique_ptr createBlock(ASTNode const* _node, std::string const& _prefix = ""); + Predicate const* 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); + Predicate const* createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract); /// Creates a new error block to be used by an assertion. /// Also registers the predicate. @@ -184,9 +185,9 @@ private: /// @returns the predicate name for a given node. std::string predicateName(ASTNode const* _node, ContractDefinition const* _contract = nullptr); /// @returns a predicate application over the current scoped variables. - smtutil::Expression predicate(smt::SymbolicFunctionVariable const& _block); + smtutil::Expression predicate(Predicate const& _block); /// @returns a predicate application over @param _arguments. - smtutil::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); + smtutil::Expression predicate(Predicate const& _block, std::vector const& _arguments); /// @returns the summary predicate for the called function. smtutil::Expression predicate(FunctionCall const& _funCall); /// @returns a predicate that defines a constructor summary. @@ -251,32 +252,32 @@ private: /// Predicates. //@{ /// Genesis predicate. - std::unique_ptr m_genesisPredicate; + Predicate const* m_genesisPredicate = nullptr; /// Implicit constructor predicate. /// Explicit constructors are handled as functions. - std::unique_ptr m_implicitConstructorPredicate; + Predicate const* m_implicitConstructorPredicate = nullptr; /// Constructor summary predicate, exists after the constructor /// (implicit or explicit) and before the interface. - std::unique_ptr m_constructorSummaryPredicate; + Predicate const* m_constructorSummaryPredicate = nullptr; /// Artificial Interface predicate. /// Single entry block for all functions. - std::map> m_interfaces; + std::map m_interfaces; /// Nondeterministic interfaces. /// These are used when the analyzed contract makes external calls to unknown code, /// which means that the analyzed contract can potentially be called /// nondeterministically. - std::map> m_nondetInterfaces; + std::map m_nondetInterfaces; /// Artificial Error predicate. /// Single error block for all assertions. - std::unique_ptr m_errorPredicate; + Predicate const* m_errorPredicate = nullptr; /// Function predicates. - std::map>> m_summaries; + std::map> m_summaries; smt::SymbolicIntVariable m_error{ TypeProvider::uint256(), @@ -337,9 +338,9 @@ private: bool m_unknownFunctionCallSeen = false; /// Block where a loop break should go to. - smt::SymbolicFunctionVariable const* m_breakDest = nullptr; + Predicate const* m_breakDest; /// Block where a loop continue should go to. - smt::SymbolicFunctionVariable const* m_continueDest = nullptr; + Predicate const* m_continueDest; //@} /// CHC solver. diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp new file mode 100644 index 000000000..b7d2a9945 --- /dev/null +++ b/libsolidity/formal/Predicate.cpp @@ -0,0 +1,92 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::smtutil; +using namespace solidity::frontend; +using namespace solidity::frontend::smt; + +map Predicate::m_predicates; + +Predicate const* Predicate::create( + SortPointer _sort, + string _name, + EncodingContext& _context, + ASTNode const* _node +) +{ + smt::SymbolicFunctionVariable predicate{_sort, move(_name), _context}; + string functorName = predicate.currentName(); + solAssert(!m_predicates.count(functorName), ""); + return &m_predicates.emplace( + std::piecewise_construct, + std::forward_as_tuple(functorName), + std::forward_as_tuple(move(predicate), _node) + ).first->second; +} + +Predicate::Predicate( + smt::SymbolicFunctionVariable&& _predicate, + ASTNode const* _node +): + m_predicate(move(_predicate)), + m_node(_node) +{ +} + +Predicate const* Predicate::predicate(string const& _name) +{ + return &m_predicates.at(_name); +} + +void Predicate::reset() +{ + m_predicates.clear(); +} + +smtutil::Expression Predicate::operator()(vector const& _args) const +{ + return m_predicate(_args); +} + +smtutil::Expression Predicate::functor() const +{ + return m_predicate.currentFunctionValue(); +} + +smtutil::Expression Predicate::functor(unsigned _idx) const +{ + return m_predicate.functionValueAtIndex(_idx); +} + +void Predicate::newFunctor() +{ + m_predicate.increaseIndex(); +} + +ASTNode const* Predicate::programNode() const { + return m_node; +} diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h new file mode 100644 index 000000000..fb3255617 --- /dev/null +++ b/libsolidity/formal/Predicate.h @@ -0,0 +1,86 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace solidity::frontend +{ + +/** + * Represents a predicate used by the CHC engine. + */ +class Predicate +{ +public: + static Predicate const* create( + smtutil::SortPointer _sort, + std::string _name, + smt::EncodingContext& _context, + ASTNode const* _node = nullptr + ); + + Predicate( + smt::SymbolicFunctionVariable&& _predicate, + ASTNode const* _node = nullptr + ); + + /// Predicate should not be copiable. + Predicate(Predicate const&) = delete; + Predicate& operator=(Predicate const&) = delete; + + /// @returns the Predicate associated with _name. + static Predicate const* predicate(std::string const& _name); + + /// Resets all the allocated predicates. + static void reset(); + + /// @returns a function application of the predicate over _args. + smtutil::Expression operator()(std::vector const& _args) const; + + /// @returns the function declaration of the predicate. + smtutil::Expression functor() const; + /// @returns the function declaration of the predicate with index _idx. + smtutil::Expression functor(unsigned _idx) const; + /// Increases the index of the function declaration of the predicate. + void newFunctor(); + + /// @returns the program node this predicate represents. + ASTNode const* programNode() const; + +private: + /// The actual SMT expression. + smt::SymbolicFunctionVariable m_predicate; + + /// The ASTNode that this predicate represents. + /// nullptr if this predicate is not associated with a specific program AST node. + ASTNode const* m_node = nullptr; + + /// Maps the name of the predicate to the actual Predicate. + /// Used in counterexample generation. + static std::map m_predicates; +}; + +} diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol index f267f0b99..6b09f780d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -26,4 +26,4 @@ contract C } } // ---- -// Warning 6328: (400-457): Assertion violation happens here. +// Warning 6328: (400-457): Assertion violation happens here From 5bbb20d3cbde836f20096fe190fc0ae23a9cd667 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 19 Aug 2020 13:51:58 +0200 Subject: [PATCH 124/139] Move stateVariablesIncludingInheritedAndPrivate from CHC to SMTEncoder --- libsolidity/formal/CHC.cpp | 26 ++++++-------------------- libsolidity/formal/CHC.h | 2 -- libsolidity/formal/SMTEncoder.cpp | 14 ++++++++++++++ libsolidity/formal/SMTEncoder.h | 3 +++ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index ea8ad765a..155b30b42 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -114,7 +114,7 @@ bool CHC::visit(ContractDefinition const& _contract) initContract(_contract); - m_stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); + m_stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); m_stateSorts = stateSorts(_contract); clearIndices(&_contract); @@ -738,24 +738,10 @@ set CHC::transactionAssertions(ASTN return assertions; } -vector CHC::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) -{ - return fold( - _contract.annotation().linearizedBaseContracts, - vector{}, - [](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); } - ); -} - -vector CHC::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) -{ - return stateVariablesIncludingInheritedAndPrivate(dynamic_cast(*_function.scope())); -} - vector CHC::stateSorts(ContractDefinition const& _contract) { return applyMap( - stateVariablesIncludingInheritedAndPrivate(_contract), + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); } ); } @@ -848,7 +834,7 @@ smtutil::SortPointer CHC::sort(ASTNode const* _node) smtutil::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract) { - auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); + auto stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); auto sorts = stateSorts(_contract); auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; @@ -878,7 +864,7 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) if (auto const* contract = dynamic_cast(node.get())) for (auto const* base: contract->annotation().linearizedBaseContracts) { - for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*base)) + for (auto const* var: SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*base)) if (!m_context.knownVariable(*var)) createVariable(*var); @@ -1048,7 +1034,7 @@ vector CHC::stateVariablesAtIndex(unsigned _index) vector CHC::stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract) { return applyMap( - stateVariablesIncludingInheritedAndPrivate(_contract), + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [&](auto _var) { return valueAtIndex(*_var, _index); } ); } @@ -1061,7 +1047,7 @@ vector CHC::currentStateVariables() vector CHC::currentStateVariables(ContractDefinition const& _contract) { - return applyMap(stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); }); + return applyMap(SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); }); } vector CHC::currentFunctionVariables() diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8b1c09a2b..619223388 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -111,8 +111,6 @@ private: void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; void setCurrentBlock(Predicate const& _block, std::vector const* _arguments = nullptr); std::set transactionAssertions(ASTNode const* _txRoot); - static std::vector stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); - static std::vector stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); //@} /// Sort helpers. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 8cbed6712..306440e63 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1982,6 +1982,20 @@ FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall cons return funDef; } +vector SMTEncoder::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) +{ + return fold( + _contract.annotation().linearizedBaseContracts, + vector{}, + [](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); } + ); +} + +vector SMTEncoder::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) +{ + return stateVariablesIncludingInheritedAndPrivate(dynamic_cast(*_function.scope())); +} + void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) { FunctionDefinition const* funDef = functionCallToDefinition(_funCall); diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index fb5dce405..6e7388155 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -62,6 +62,9 @@ public: /// if possible or nullptr. static FunctionDefinition const* functionCallToDefinition(FunctionCall const& _funCall); + static std::vector stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); + static std::vector stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); + protected: // TODO: Check that we do not have concurrent reads and writes to a variable, // because the order of expression evaluation is undefined From e3a8c94acea0c167edc9cd45608422bd16e16176 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 19 Aug 2020 13:53:16 +0200 Subject: [PATCH 125/139] Move formatFunctionCallCounterexample from CHC to Predicate --- libsolidity/formal/CHC.cpp | 54 +++--------------- libsolidity/formal/CHC.h | 4 -- libsolidity/formal/Predicate.cpp | 94 +++++++++++++++++++++++++++++++- libsolidity/formal/Predicate.h | 22 ++++++++ 4 files changed, 123 insertions(+), 51 deletions(-) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 155b30b42..56e43b3da 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -1423,24 +1423,12 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& Predicate const* summaryPredicate = Predicate::predicate(summaryNode.first); solAssert(summaryPredicate, ""); - FunctionDefinition const* calledFun = nullptr; - ContractDefinition const* calledContract = nullptr; - if (auto const* contract = dynamic_cast(summaryPredicate->programNode())) - { - if (auto const* constructor = contract->constructor()) - calledFun = constructor; - else - calledContract = contract; - } - else if (auto const* fun = dynamic_cast(summaryPredicate->programNode())) - calledFun = fun; - else - solAssert(false, ""); + FunctionDefinition const* calledFun = summaryPredicate->programFunction(); + ContractDefinition const* calledContract = summaryPredicate->programContract(); solAssert((calledFun && !calledContract) || (!calledFun && calledContract), ""); - auto const& stateVars = calledFun ? stateVariablesIncludingInheritedAndPrivate(*calledFun) : stateVariablesIncludingInheritedAndPrivate(*calledContract); - /// calledContract != nullptr implies that the constructor of the analyzed contract is implicit and - /// therefore takes no parameters. + auto stateVars = summaryPredicate->stateVariables(); + solAssert(stateVars.has_value(), ""); /// This summary node is the end of a tx. /// If it is the first summary node seen in this loop, it is the summary @@ -1450,12 +1438,12 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& { lastTxSeen = true; /// Generate counterexample message local to the failed target. - localState = formatStateCounterexample(stateVars, calledFun, summaryNode.second) + "\n"; + localState = formatStateCounterexample(*stateVars, calledFun, summaryNode.second) + "\n"; if (calledFun) { /// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). auto const& inParams = calledFun->parameters(); - unsigned initLocals = stateVars.size() * 2 + 1 + inParams.size(); + unsigned initLocals = stateVars->size() * 2 + 1 + inParams.size(); /// In this loop we are interested in postInputVars. for (unsigned i = initLocals; i < initLocals + inParams.size(); ++i) { @@ -1477,9 +1465,9 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& else /// We report the state after every tx in the trace except for the last, which is reported /// first in the code above. - path.emplace_back("State: " + formatStateCounterexample(stateVars, calledFun, summaryNode.second)); + path.emplace_back("State: " + formatStateCounterexample(*stateVars, calledFun, summaryNode.second)); - string txCex = calledContract ? "constructor()" : formatFunctionCallCounterexample(stateVars, *calledFun, summaryNode.second); + string txCex = summaryPredicate->formatSummaryCall(summaryNode.second); path.emplace_back(txCex); /// Recurse on the next interface node which represents the previous transaction @@ -1527,32 +1515,6 @@ string CHC::formatStateCounterexample(vector const& return boost::algorithm::join(stateCex, ", "); } -string CHC::formatFunctionCallCounterexample(vector const& _stateVars, FunctionDefinition const& _function, vector const& _summaryValues) -{ - /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). - /// Here we are interested in preInputVars. - vector::const_iterator first = _summaryValues.begin() + static_cast(_stateVars.size()) + 1; - vector::const_iterator last = first + static_cast(_function.parameters().size()); - solAssert(first >= _summaryValues.begin() && first <= _summaryValues.end(), ""); - solAssert(last >= _summaryValues.begin() && last <= _summaryValues.end(), ""); - vector functionArgsCex(first, last); - vector functionArgs; - - auto const& params = _function.parameters(); - solAssert(params.size() == functionArgsCex.size(), ""); - for (unsigned i = 0; i < params.size(); ++i) - if (params[i]->type()->isValueType()) - functionArgs.emplace_back(functionArgsCex[i]); - else - functionArgs.emplace_back(params[i]->name()); - - string fName = _function.isConstructor() ? "constructor" : - _function.isFallback() ? "fallback" : - _function.isReceive() ? "receive" : - _function.name(); - return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")"; -} - string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex) { string dot = "digraph {\n"; diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 619223388..f28f97d67 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -226,10 +226,6 @@ private: /// _function = nullptr means the transaction was the deployment of a /// contract without an explicit constructor. std::string formatStateCounterexample(std::vector const& _stateVariables, FunctionDefinition const* _function, std::vector const& _summaryValues); - /// @returns a formatted text representing a call to _function - /// with the concrete values for value type parameters and - /// the parameter name for reference types. - std::string formatFunctionCallCounterexample(std::vector const& _stateVariables, FunctionDefinition const& _function, std::vector const& _summaryValues); /// @returns a DAG in the dot format. /// Used for debugging purposes. diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index b7d2a9945..316104b41 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -18,6 +18,8 @@ #include +#include + #include #include @@ -87,6 +89,96 @@ void Predicate::newFunctor() m_predicate.increaseIndex(); } -ASTNode const* Predicate::programNode() const { +ASTNode const* Predicate::programNode() const +{ return m_node; } + +ContractDefinition const* Predicate::programContract() const +{ + if (auto const* contract = dynamic_cast(m_node)) + if (!contract->constructor()) + return contract; + + return nullptr; +} + +FunctionDefinition const* Predicate::programFunction() const +{ + if (auto const* contract = dynamic_cast(m_node)) + { + if (contract->constructor()) + return contract->constructor(); + return nullptr; + } + + if (auto const* fun = dynamic_cast(m_node)) + return fun; + + return nullptr; +} + +optional> Predicate::stateVariables() const +{ + if (auto const* fun = programFunction()) + return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun); + if (auto const* contract = programContract()) + return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract); + + auto const* node = m_node; + while (auto const* scopable = dynamic_cast(node)) + { + node = scopable->scope(); + if (auto const* fun = dynamic_cast(node)) + return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun); + } + + return nullopt; +} + +bool Predicate::isSummary() const +{ + return functor().name.rfind("summary", 0) == 0; +} + +bool Predicate::isInterface() const +{ + return functor().name.rfind("interface", 0) == 0; +} + +string Predicate::formatSummaryCall(vector const& _args) const +{ + if (programContract()) + return "constructor()"; + + solAssert(isSummary(), ""); + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + auto const* fun = programFunction(); + solAssert(fun, ""); + + /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars). + /// Here we are interested in preInputVars. + vector::const_iterator first = _args.begin() + static_cast(stateVars->size()) + 1; + vector::const_iterator last = first + static_cast(fun->parameters().size()); + solAssert(first >= _args.begin() && first <= _args.end(), ""); + solAssert(last >= _args.begin() && last <= _args.end(), ""); + vector functionArgsCex(first, last); + vector functionArgs; + + auto const& params = fun->parameters(); + solAssert(params.size() == functionArgsCex.size(), ""); + for (unsigned i = 0; i < params.size(); ++i) + if (params[i]->type()->isValueType()) + functionArgs.emplace_back(functionArgsCex[i]); + else + functionArgs.emplace_back(params[i]->name()); + + string fName = fun->isConstructor() ? "constructor" : + fun->isFallback() ? "fallback" : + fun->isReceive() ? "receive" : + fun->name(); + return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")"; + +} diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index fb3255617..4ce3446b2 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -24,6 +24,7 @@ #include #include +#include #include namespace solidity::frontend @@ -70,6 +71,27 @@ public: /// @returns the program node this predicate represents. ASTNode const* programNode() const; + /// @returns the ContractDefinition that this predicate represents + /// or nullptr otherwise. + ContractDefinition const* programContract() const; + + /// @returns the FunctionDefinition that this predicate represents + /// or nullptr otherwise. + FunctionDefinition const* programFunction() const; + + /// @returns the program state variables in the scope of this predicate. + std::optional> stateVariables() const; + + /// @returns true if this predicate represents a summary. + bool isSummary() const; + + /// @returns true if this predicate represents an interface. + bool isInterface() const; + + /// @returns a formatted string representing a call to this predicate + /// with _args. + std::string formatSummaryCall(std::vector const& _args) const; + private: /// The actual SMT expression. smt::SymbolicFunctionVariable m_predicate; From 2e2e96cc93392eecd9ba76b3492ed486d5e8ce36 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 19 Aug 2020 18:38:56 +0200 Subject: [PATCH 126/139] Move state model filtering from CHC to Predicate --- libsolidity/formal/CHC.cpp | 33 ++++++++------------------------ libsolidity/formal/CHC.h | 2 +- libsolidity/formal/Predicate.cpp | 32 +++++++++++++++++++++++++++++++ libsolidity/formal/Predicate.h | 4 ++++ 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 56e43b3da..3961d6dd2 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -1429,6 +1429,8 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& solAssert((calledFun && !calledContract) || (!calledFun && calledContract), ""); auto stateVars = summaryPredicate->stateVariables(); solAssert(stateVars.has_value(), ""); + auto stateValues = summaryPredicate->summaryStateValues(summaryNode.second); + solAssert(stateValues.size() == stateVars->size(), ""); /// This summary node is the end of a tx. /// If it is the first summary node seen in this loop, it is the summary @@ -1438,7 +1440,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& { lastTxSeen = true; /// Generate counterexample message local to the failed target. - localState = formatStateCounterexample(*stateVars, calledFun, summaryNode.second) + "\n"; + localState = formatStateCounterexample(*stateVars, stateValues) + "\n"; if (calledFun) { /// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). @@ -1465,7 +1467,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& else /// We report the state after every tx in the trace except for the last, which is reported /// first in the code above. - path.emplace_back("State: " + formatStateCounterexample(*stateVars, calledFun, summaryNode.second)); + path.emplace_back("State: " + formatStateCounterexample(*stateVars, stateValues)); string txCex = summaryPredicate->formatSummaryCall(summaryNode.second); path.emplace_back(txCex); @@ -1481,35 +1483,16 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); } -string CHC::formatStateCounterexample(vector const& _stateVars, FunctionDefinition const* _function, vector const& _summaryValues) +string CHC::formatStateCounterexample(vector const& _stateVars, vector const& _values) { - /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). - /// The signature of an implicit constructor summary predicate is: summary(error, postStateVars). - /// Here we are interested in postStateVars. - vector::const_iterator stateFirst; - vector::const_iterator stateLast; - if (_function) - { - stateFirst = _summaryValues.begin() + 1 + static_cast(_stateVars.size()) + static_cast(_function->parameters().size()); - stateLast = stateFirst + static_cast(_stateVars.size()); - } - else - { - stateFirst = _summaryValues.begin() + 1; - stateLast = stateFirst + static_cast(_stateVars.size()); - } - - solAssert(stateFirst >= _summaryValues.begin() && stateFirst <= _summaryValues.end(), ""); - solAssert(stateLast >= _summaryValues.begin() && stateLast <= _summaryValues.end(), ""); - vector stateArgs(stateFirst, stateLast); - solAssert(stateArgs.size() == _stateVars.size(), ""); + solAssert(_stateVars.size() == _values.size(), ""); vector stateCex; - for (unsigned i = 0; i < stateArgs.size(); ++i) + for (unsigned i = 0; i < _values.size(); ++i) { auto var = _stateVars.at(i); if (var->type()->isValueType()) - stateCex.emplace_back(var->name() + " = " + stateArgs.at(i)); + stateCex.emplace_back(var->name() + " = " + _values.at(i)); } return boost::algorithm::join(stateCex, ", "); diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index f28f97d67..1dce128fe 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -225,7 +225,7 @@ private: /// _function was executed. /// _function = nullptr means the transaction was the deployment of a /// contract without an explicit constructor. - std::string formatStateCounterexample(std::vector const& _stateVariables, FunctionDefinition const* _function, std::vector const& _summaryValues); + std::string formatStateCounterexample(std::vector const& _stateVariables, std::vector const& _values); /// @returns a DAG in the dot format. /// Used for debugging purposes. diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 316104b41..7426adeb3 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -182,3 +182,35 @@ string Predicate::formatSummaryCall(vector const& _args) const return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")"; } + +vector Predicate::summaryStateValues(vector const& _args) const +{ + /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars). + /// The signature of an implicit constructor summary predicate is: summary(error, postStateVars). + /// Here we are interested in postStateVars. + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + + vector::const_iterator stateFirst; + vector::const_iterator stateLast; + if (auto const* function = programFunction()) + { + stateFirst = _args.begin() + 1 + static_cast(stateVars->size()) + static_cast(function->parameters().size()); + stateLast = stateFirst + static_cast(stateVars->size()); + } + else if (programContract()) + { + stateFirst = _args.begin() + 1; + stateLast = stateFirst + static_cast(stateVars->size()); + } + else + solAssert(false, ""); + + solAssert(stateFirst >= _args.begin() && stateFirst <= _args.end(), ""); + solAssert(stateLast >= _args.begin() && stateLast <= _args.end(), ""); + + vector stateArgs(stateFirst, stateLast); + solAssert(stateArgs.size() == stateVars->size(), ""); + return stateArgs; +} diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index 4ce3446b2..daf685d61 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -92,6 +92,10 @@ public: /// with _args. std::string formatSummaryCall(std::vector const& _args) const; + /// @returns the values of the state variables from _args at the point + /// where this summary was reached. + std::vector summaryStateValues(std::vector const& _args) const; + private: /// The actual SMT expression. smt::SymbolicFunctionVariable m_predicate; From a3b6019131bb022cb2636d30c81f33d174698bd9 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 20 Aug 2020 10:43:50 +0200 Subject: [PATCH 127/139] Move post input and post output filtering from CHC to Predicate --- libsolidity/formal/CHC.cpp | 59 ++++++++------------------------ libsolidity/formal/CHC.h | 24 ++++++++++--- libsolidity/formal/Predicate.cpp | 44 ++++++++++++++++++++++++ libsolidity/formal/Predicate.h | 8 +++++ 4 files changed, 85 insertions(+), 50 deletions(-) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 3961d6dd2..f6a3b5aae 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -1404,24 +1403,22 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& solAssert(edges.size() <= 2, ""); unsigned summaryId = edges.at(0); - optional interfaceId; if (edges.size() == 2) { interfaceId = edges.at(1); - if (_graph.nodes.at(summaryId).first.rfind("summary", 0) != 0) + if (!Predicate::predicate(_graph.nodes.at(summaryId).first)->isSummary()) swap(summaryId, *interfaceId); - solAssert(_graph.nodes.at(*interfaceId).first.rfind("interface", 0) == 0, ""); + auto interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first); + solAssert(interfacePredicate && interfacePredicate->isInterface(), ""); } /// The children are unordered, so we need to check which is the summary and /// which is the interface. - solAssert(_graph.nodes.at(summaryId).first.rfind("summary", 0) == 0, ""); + Predicate const* summaryPredicate = Predicate::predicate(_graph.nodes.at(summaryId).first); + solAssert(summaryPredicate && summaryPredicate->isSummary(), ""); /// At this point property 2 from the function description is verified for this node. - - auto const& summaryNode = _graph.nodes.at(summaryId); - Predicate const* summaryPredicate = Predicate::predicate(summaryNode.first); - solAssert(summaryPredicate, ""); + auto summaryArgs = _graph.nodes.at(summaryId).second; FunctionDefinition const* calledFun = summaryPredicate->programFunction(); ContractDefinition const* calledContract = summaryPredicate->programContract(); @@ -1429,7 +1426,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& solAssert((calledFun && !calledContract) || (!calledFun && calledContract), ""); auto stateVars = summaryPredicate->stateVariables(); solAssert(stateVars.has_value(), ""); - auto stateValues = summaryPredicate->summaryStateValues(summaryNode.second); + auto stateValues = summaryPredicate->summaryStateValues(summaryArgs); solAssert(stateValues.size() == stateVars->size(), ""); /// This summary node is the end of a tx. @@ -1440,36 +1437,23 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& { lastTxSeen = true; /// Generate counterexample message local to the failed target. - localState = formatStateCounterexample(*stateVars, stateValues) + "\n"; + localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n"; if (calledFun) { - /// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). + auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs); auto const& inParams = calledFun->parameters(); - unsigned initLocals = stateVars->size() * 2 + 1 + inParams.size(); - /// In this loop we are interested in postInputVars. - for (unsigned i = initLocals; i < initLocals + inParams.size(); ++i) - { - auto param = inParams.at(i - initLocals); - if (param->type()->isValueType()) - localState += param->name() + " = " + summaryNode.second.at(i) + "\n"; - } + localState += formatVariableModel(inParams, inValues, "\n") + "\n"; + auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs); auto const& outParams = calledFun->returnParameters(); - initLocals += inParams.size(); - /// In this loop we are interested in outputVars. - for (unsigned i = initLocals; i < initLocals + outParams.size(); ++i) - { - auto param = outParams.at(i - initLocals); - if (param->type()->isValueType()) - localState += param->name() + " = " + summaryNode.second.at(i) + "\n"; - } + localState += formatVariableModel(outParams, outValues, "\n") + "\n"; } } else /// We report the state after every tx in the trace except for the last, which is reported /// first in the code above. - path.emplace_back("State: " + formatStateCounterexample(*stateVars, stateValues)); + path.emplace_back("State: " + formatVariableModel(*stateVars, stateValues, ", ")); - string txCex = summaryPredicate->formatSummaryCall(summaryNode.second); + string txCex = summaryPredicate->formatSummaryCall(summaryArgs); path.emplace_back(txCex); /// Recurse on the next interface node which represents the previous transaction @@ -1483,21 +1467,6 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); } -string CHC::formatStateCounterexample(vector const& _stateVars, vector const& _values) -{ - solAssert(_stateVars.size() == _values.size(), ""); - - vector stateCex; - for (unsigned i = 0; i < _values.size(); ++i) - { - auto var = _stateVars.at(i); - if (var->type()->isValueType()) - stateCex.emplace_back(var->name() + " = " + _values.at(i)); - } - - return boost::algorithm::join(stateCex, ", "); -} - string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex) { string dot = "digraph {\n"; diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 1dce128fe..d555f19e7 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -38,6 +38,8 @@ #include +#include + #include #include #include @@ -221,11 +223,23 @@ private: ); std::optional generateCounterexample(smtutil::CHCSolverInterface::CexGraph const& _graph, std::string const& _root); - /// @returns values for the _stateVariables after a transaction calling - /// _function was executed. - /// _function = nullptr means the transaction was the deployment of a - /// contract without an explicit constructor. - std::string formatStateCounterexample(std::vector const& _stateVariables, std::vector const& _values); + + /// @returns a set of pairs _var = _value separated by _separator. + template + std::string formatVariableModel(std::vector const& _variables, std::vector const& _values, std::string const& _separator) const + { + solAssert(_variables.size() == _values.size(), ""); + + std::vector assignments; + for (unsigned i = 0; i < _values.size(); ++i) + { + auto var = _variables.at(i); + if (var && var->type()->isValueType()) + assignments.emplace_back(var->name() + " = " + _values.at(i)); + } + + return boost::algorithm::join(assignments, _separator); + } /// @returns a DAG in the dot format. /// Used for debugging purposes. diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 7426adeb3..9f897d63f 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -214,3 +214,47 @@ vector Predicate::summaryStateValues(vector const& _args) const solAssert(stateArgs.size() == stateVars->size(), ""); return stateArgs; } + +vector Predicate::summaryPostInputValues(vector const& _args) const +{ + /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars). + /// Here we are interested in postInputVars. + auto const* function = programFunction(); + solAssert(function, ""); + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + + auto const& inParams = function->parameters(); + + vector::const_iterator first = _args.begin() + 1 + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()); + vector::const_iterator last = first + static_cast(inParams.size()); + + solAssert(first >= _args.begin() && first <= _args.end(), ""); + solAssert(last >= _args.begin() && last <= _args.end(), ""); + + vector inValues(first, last); + solAssert(inValues.size() == inParams.size(), ""); + return inValues; +} + +vector Predicate::summaryPostOutputValues(vector const& _args) const +{ + /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars). + /// Here we are interested in outputVars. + auto const* function = programFunction(); + solAssert(function, ""); + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + + auto const& inParams = function->parameters(); + + vector::const_iterator first = _args.begin() + 1 + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) * 2; + + solAssert(first >= _args.begin() && first <= _args.end(), ""); + + vector outValues(first, _args.end()); + solAssert(outValues.size() == function->returnParameters().size(), ""); + return outValues; +} diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index daf685d61..3da5ad453 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -96,6 +96,14 @@ public: /// where this summary was reached. std::vector summaryStateValues(std::vector const& _args) const; + /// @returns the values of the function input variables from _args at the point + /// where this summary was reached. + std::vector summaryPostInputValues(std::vector const& _args) const; + + /// @returns the values of the function output variables from _args at the point + /// where this summary was reached. + std::vector summaryPostOutputValues(std::vector const& _args) const; + private: /// The actual SMT expression. smt::SymbolicFunctionVariable m_predicate; From e23d96464b54ef78fdc7233e43c695daab7e6faa Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 20 Aug 2020 15:57:12 +0200 Subject: [PATCH 128/139] Adjust test --- .../modifiers/modifier_code_after_placeholder.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol index f543be8db..6f3701d41 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,5 +21,5 @@ contract C } } // ---- -// Warning 4984: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 6328: (136-149): Assertion violation happens here From 49d3804de443f9f6123b1e1b64b2dca1b49034ba Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 1 Sep 2020 11:35:25 +0200 Subject: [PATCH 129/139] [SMTChecker] Fix rational number short circuit --- libsolidity/formal/SMTEncoder.cpp | 7 +++++-- .../operators/bitwise_rational_1.sol | 12 ++++++++++++ .../operators/bitwise_rational_2.sol | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 8cbed6712..0418de737 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -452,11 +452,14 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) void SMTEncoder::endVisit(UnaryOperation const& _op) { - if (TokenTraits::isBitOp(_op.getOperator())) - return bitwiseNotOperation(_op); + /// We need to shortcut here due to potentially unknown + /// rational number sizes. if (_op.annotation().type->category() == Type::Category::RationalNumber) return; + if (TokenTraits::isBitOp(_op.getOperator())) + return bitwiseNotOperation(_op); + createExpr(_op); auto const* subExpr = innermostTuple(_op.subExpression()); diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol new file mode 100644 index 000000000..97343e235 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint x = uint(~1); + // This assertion fails because type conversion is still unsupported. + assert(x == 2**256 - 2); + assert(~1 == -2); + } +} +// ---- +// Warning 6328: (169-192): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol new file mode 100644 index 000000000..94bb5cc83 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + assert(~1 | (~0xff & 0) == -2); + int x = ~1 | (~0xff ^ 0); + /// Result is negative, assertion fails. + assert(x > 0); + int y = ~x | (0xff & 1); + assert(y > 0); + assert(y & (0xffffffffffffffffff & 1) == 1); + } +} +// ---- +// Warning 6328: (181-194): Assertion violation happens here From b52f334fff62fc180ad0ed69e37e65daf03b41dc Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 1 Sep 2020 10:39:15 +0200 Subject: [PATCH 130/139] Prepare release of 0.7.1. --- Changelog.md | 24 ++++++++++++------------ docs/bugs_by_version.json | 4 ++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index 29498feac..0861e22d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.7.1 (unreleased) +### 0.7.1 (2020-09-02) Language Features: * Allow function definitions outside of contracts, behaving much like internal library functions. @@ -6,30 +6,30 @@ Language Features: Compiler Features: * SMTChecker: Add underflow and overflow as verification conditions in the CHC engine. - * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. - * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. - * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. - * SMTChecker: Support conditional operator. * SMTChecker: Support bitwise or, xor and not operators. + * SMTChecker: Support conditional operator. + * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. + * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. + * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. Bugfixes: * AST: Remove ``null`` member values also when the compiler is used in standard-json-mode. + * General: Allow `type(Contract).name` for abstract contracts and interfaces. + * Immutables: Disallow assigning immutables more than once during their declaration. * Immutables: Properly treat complex assignment and increment/decrement as both reading and writing and thus disallow it everywhere for immutable variables. * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. + * References Resolver: Fix internal bug when using constructor for library. * Scanner: Fix bug where whitespace would be allowed within the ``->`` token (e.g. ``function f() - > x {}`` becomes invalid in inline assembly and Yul). * SMTChecker: Fix internal error in BMC function inlining. * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. * SMTChecker: Fix internal error on lvalue unary operators with tuples. * SMTChecker: Fix soundness of array ``pop``. - * References Resolver: Fix internal bug when using constructor for library. - * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. - * Yul Optimizer: Ensure that Yul keywords are not mistakenly used by the NameDispenser and VarNameCleaners. The bug would manifest as uncompilable code. - * Type Checker: Disallow signed literals as exponent in exponentiation operator. - * Allow `type(Contract).name` for abstract contracts and interfaces. - * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. * Type Checker: Disallow ``using for`` directive inside interfaces. - * Immutables: Disallow assigning immutables more than once during their declaration. + * Type Checker: Disallow signed literals as exponent in exponentiation operator. + * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. + * Yul Optimizer: Ensure that Yul keywords are not mistakenly used by the NameDispenser and VarNameCleaners. The bug would manifest as uncompilable code. + * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. ### 0.7.0 (2020-07-28) diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index fc7191322..4acdc7701 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1185,5 +1185,9 @@ "0.7.0": { "bugs": [], "released": "2020-07-28" + }, + "0.7.1": { + "bugs": [], + "released": "2020-09-02" } } \ No newline at end of file From e7a05324afad143b9859459e4e7cf1723c1d98d7 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 2 Sep 2020 09:36:20 +0200 Subject: [PATCH 131/139] Adding simple array copying tests --- .../array/array_copy_memory_to_storage.sol | 11 +++++++++++ .../array/array_copy_storage_to_memory.sol | 10 ++++++++++ .../semanticTests/array/bytes_memory_to_storage.sol | 10 ++++++++++ .../semanticTests/array/bytes_storage_to_memory.sol | 9 +++++++++ .../calldata/calldata_bytes_to_storage.sol | 9 +++++++++ 5 files changed, 49 insertions(+) create mode 100644 test/libsolidity/semanticTests/array/array_copy_memory_to_storage.sol create mode 100644 test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol create mode 100644 test/libsolidity/semanticTests/array/bytes_memory_to_storage.sol create mode 100644 test/libsolidity/semanticTests/array/bytes_storage_to_memory.sol create mode 100644 test/libsolidity/semanticTests/calldata/calldata_bytes_to_storage.sol diff --git a/test/libsolidity/semanticTests/array/array_copy_memory_to_storage.sol b/test/libsolidity/semanticTests/array/array_copy_memory_to_storage.sol new file mode 100644 index 000000000..3055b0fa8 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_memory_to_storage.sol @@ -0,0 +1,11 @@ +contract C { + uint[] a; + function f() public returns (uint, uint) { + uint[] memory b = new uint[](3); + b[0] = 1; + a = b; + return (a[0], a.length); + } +} +// ---- +// f() -> 1, 3 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol b/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol new file mode 100644 index 000000000..8bc6b86a2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_to_memory.sol @@ -0,0 +1,10 @@ +contract C { + uint[] a; + function f() public returns (uint, uint) { + a.push(1); a.push(0); a.push(0); + uint[] memory b = a; + return (b[0], b.length); + } +} +// ---- +// f() -> 1, 3 diff --git a/test/libsolidity/semanticTests/array/bytes_memory_to_storage.sol b/test/libsolidity/semanticTests/array/bytes_memory_to_storage.sol new file mode 100644 index 000000000..eebdb7e9b --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_memory_to_storage.sol @@ -0,0 +1,10 @@ +contract C { + bytes s; + function f() external returns (byte) { + bytes memory data = "abcd"; + s = data; + return s[0]; + } +} +// ---- +// f() -> "a" diff --git a/test/libsolidity/semanticTests/array/bytes_storage_to_memory.sol b/test/libsolidity/semanticTests/array/bytes_storage_to_memory.sol new file mode 100644 index 000000000..13932a2cc --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_storage_to_memory.sol @@ -0,0 +1,9 @@ +contract C { + bytes s = "abcd"; + function f() external returns (byte) { + bytes memory data = s; + return data[0]; + } +} +// ---- +// f() -> "a" diff --git a/test/libsolidity/semanticTests/calldata/calldata_bytes_to_storage.sol b/test/libsolidity/semanticTests/calldata/calldata_bytes_to_storage.sol new file mode 100644 index 000000000..07041910d --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_bytes_to_storage.sol @@ -0,0 +1,9 @@ +contract C { + bytes s; + function f(bytes calldata data) external returns (byte) { + s = data; + return s[0]; + } +} +// ---- +// f(bytes): 0x20, 0x08, "abcdefgh" -> "a" From 8f34ac472fff2b3504f76bb00266c4c2f8fe04a5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 1 Sep 2020 18:04:07 +0200 Subject: [PATCH 132/139] Implement ``super``. --- .../codegen/ir/IRGeneratorForStatements.cpp | 24 +++++++++++- .../intheritance/super_in_constructor.sol | 3 +- .../super_in_constructor_assignment.sol | 39 +++++++++++++++++++ .../intheritance/super_overload.sol | 2 + .../semanticTests/various/super.sol | 2 + .../various/super_parentheses.sol | 31 +++++++++++++++ 6 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol create mode 100644 test/libsolidity/semanticTests/various/super_parentheses.sol diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 3ab3c8888..c0ee13897 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -749,8 +749,18 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) if (identifier) functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); + else + { + ContractType const* type = dynamic_cast(memberAccess->expression().annotation().type); + if (type && type->isSuper()) + { + ContractDefinition const* super = type->contractDefinition().superContract(m_context.mostDerivedContract()); + solAssert(super, "Super contract not available."); + functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract(), super); + } + } - solAssert(functionDef->isImplemented(), ""); + solAssert(functionDef && functionDef->isImplemented(), ""); } solAssert(!functionType->takesArbitraryParameters(), ""); @@ -1397,7 +1407,17 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) ContractType const& type = dynamic_cast(*_memberAccess.expression().annotation().type); if (type.isSuper()) { - solUnimplementedAssert(false, ""); + solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + ContractDefinition const* super = type.contractDefinition().superContract(m_context.mostDerivedContract()); + solAssert(super, "Super contract not available."); + FunctionDefinition const& resolvedFunctionDef = dynamic_cast( + *_memberAccess.annotation().referencedDeclaration + ).resolveVirtual(m_context.mostDerivedContract(), super); + + define(_memberAccess) << to_string(resolvedFunctionDef.id()) << "\n"; + solAssert(resolvedFunctionDef.functionType(true), ""); + solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); + m_context.internalFunctionAccessed(_memberAccess, resolvedFunctionDef); } // ordinary contract type else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) diff --git a/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol index 5be2158a2..24f1f1e17 100644 --- a/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol +++ b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol @@ -30,6 +30,7 @@ contract D is B, C { return data; } } - +// ==== +// compileViaYul: also // ---- // f() -> 15 diff --git a/test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol b/test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol new file mode 100644 index 000000000..46acb36d0 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol @@ -0,0 +1,39 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + function() internal returns (uint) x = super.f; + return x() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + function() internal returns (uint) x = super.f; + return x() | 4; + } +} + + +contract D is B, C { + uint256 data; + + constructor() { + function() internal returns (uint) x = super.f; + data = x() | 8; + } + + function f() public override (B, C) returns (uint256 r) { + return data; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/intheritance/super_overload.sol b/test/libsolidity/semanticTests/intheritance/super_overload.sol index 6a1f6dc3d..b7cbf10b3 100644 --- a/test/libsolidity/semanticTests/intheritance/super_overload.sol +++ b/test/libsolidity/semanticTests/intheritance/super_overload.sol @@ -22,6 +22,8 @@ contract C is A, B { } } +// ==== +// compileViaYul: also // ---- // g() -> 10 // h() -> 2 diff --git a/test/libsolidity/semanticTests/various/super.sol b/test/libsolidity/semanticTests/various/super.sol index 16e4257e1..cfaa0204c 100644 --- a/test/libsolidity/semanticTests/various/super.sol +++ b/test/libsolidity/semanticTests/various/super.sol @@ -25,5 +25,7 @@ contract D is B, C { } } +// ==== +// compileViaYul: also // ---- // f() -> 15 diff --git a/test/libsolidity/semanticTests/various/super_parentheses.sol b/test/libsolidity/semanticTests/various/super_parentheses.sol new file mode 100644 index 000000000..e5d07f10d --- /dev/null +++ b/test/libsolidity/semanticTests/various/super_parentheses.sol @@ -0,0 +1,31 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + return ((super).f)() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + return ((super).f)() | 4; + } +} + + +contract D is B, C { + function f() public override(B, C) returns (uint256 r) { + return ((super).f)() | 8; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 15 From b89c863e11ade7459520b5ba6492d1baa421f850 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 27 Jul 2020 10:04:19 +0200 Subject: [PATCH 133/139] [Sol->Yul] Implementing struct copying from memory to storage --- libsolidity/codegen/YulUtilFunctions.cpp | 77 ++++++++++++++++--- libsolidity/codegen/YulUtilFunctions.h | 6 +- .../codegen/ir/IRGeneratorForStatements.cpp | 6 +- .../calldata/calldata_struct_to_memory.sol | 7 +- .../structs/struct_memory_to_storage.sol | 28 +++++++ .../structs/struct_named_constructor.sol | 2 + 6 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 542a67613..a2dc840e5 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1458,18 +1458,24 @@ string YulUtilFunctions::readFromCalldata(Type const& _type) return readFromMemoryOrCalldata(_type, true); } -string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::optional const& _offset) +string YulUtilFunctions::updateStorageValueFunction( + Type const& _toType, + Type const* _fromType, + std::optional const& _offset +) { string const functionName = "update_storage_value_" + (_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") + - _type.identifier(); + (_fromType ? "_from_" + _fromType->identifier() : "") + + "_to_" + + _toType.identifier(); return m_functionCollector.createFunction(functionName, [&] { - if (_type.isValueType()) + if (_toType.isValueType()) { - solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size."); - solAssert(_type.storageBytes() > 0, "Invalid storage bytes size."); + solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size."); + solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size."); return Whiskers(R"( function (slot, value) { @@ -1480,19 +1486,68 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::opti ("functionName", functionName) ("update", _offset.has_value() ? - updateByteSliceFunction(_type.storageBytes(), *_offset) : - updateByteSliceFunctionDynamic(_type.storageBytes()) + updateByteSliceFunction(_toType.storageBytes(), *_offset) : + updateByteSliceFunctionDynamic(_toType.storageBytes()) ) ("offset", _offset.has_value() ? "" : "offset, ") - ("prepare", prepareStoreFunction(_type)) + ("prepare", prepareStoreFunction(_toType)) .render(); } else { - if (_type.category() == Type::Category::Array) - solUnimplementedAssert(false, ""); - else if (_type.category() == Type::Category::Struct) + if (_toType.category() == Type::Category::Array) solUnimplementedAssert(false, ""); + else if (_toType.category() == Type::Category::Struct) + { + solAssert(_fromType, ""); + solAssert(_fromType->category() == Type::Category::Struct, ""); + auto const& fromStructType = dynamic_cast(*_fromType); + auto const& toStructType = dynamic_cast(_toType); + solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); + solUnimplementedAssert(fromStructType.location() == DataLocation::Memory, ""); + solUnimplementedAssert(_offset.has_value() && _offset.value() == 0, ""); + + Whiskers templ(R"( + function (slot, value) { + <#member> + { + + } + + } + )"); + templ("functionName", functionName); + + MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); + MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr); + + vector> memberParams(toStructMembers.size()); + for (size_t i = 0; i < toStructMembers.size(); ++i) + { + solAssert(toStructMembers[i].type->memoryHeadSize() == 32, ""); + bool isStruct = toStructMembers[i].type->category() == Type::Category::Struct; + auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(toStructMembers[i].name); + memberParams[i]["updateMemberCall"] = Whiskers(R"( + let memberValue := (add(valueMem, )) + (add(slot, ), , memberValue) + )") + ("hasOffset", !isStruct) + ( + "updateMember", + isStruct ? + updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type, offset) : + updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type) + ) + ("memberStorageSlotDiff", slotDiff.str()) + ("memberStorageOffset", to_string(offset)) + ("memberMemoryOffset", fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str()) + ("loadFromMemory", readFromMemory(*fromStructMembers[i].type)) + .render(); + } + templ("member", memberParams); + + return templ.render(); + } else solAssert(false, "Invalid non-value type for assignment."); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 7af910a6d..d25287589 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -248,7 +248,11 @@ public: /// the specified slot and offset. If offset is not given, it is expected as /// runtime parameter. /// signature: (slot, [offset,] value) - std::string updateStorageValueFunction(Type const& _type, std::optional const& _offset = std::optional()); + std::string updateStorageValueFunction( + Type const& _toType, + Type const* _fromType = nullptr, + std::optional const& _offset = std::optional() + ); /// Returns the name of a function that will write the given value to /// the specified address. diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 3ab3c8888..170b636fc 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -314,9 +314,9 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) writeToLValue(*m_currentLValue, value); - m_currentLValue.reset(); - if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) + if (m_currentLValue->type.category() != Type::Category::Struct && *_assignment.annotation().type != *TypeProvider::emptyTuple()) define(_assignment, value); + m_currentLValue.reset(); return false; } @@ -2486,7 +2486,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable offset = std::get(_storage.offset); m_code << - m_utils.updateStorageValueFunction(_lvalue.type, offset) << + m_utils.updateStorageValueFunction(_lvalue.type, &_value.type(), offset) << "(" << _storage.slot << ( diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol index 04d675371..1841d4fc1 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol @@ -4,15 +4,16 @@ contract C { struct S { uint256 a; uint256 b; + bytes2 c; } - function f(S calldata s) external pure returns (uint256, uint256) { + function f(S calldata s) external pure returns (uint256, uint256, byte) { S memory m = s; - return (m.a, m.b); + return (m.a, m.b, m.c[1]); } } // ==== // compileViaYul: also // ---- -// f((uint256,uint256)): 42, 23 -> 42, 23 +// f((uint256,uint256, bytes2)): 42, 23, "ab" -> 42, 23, "b" diff --git a/test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol b/test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol new file mode 100644 index 000000000..fd965f12b --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol @@ -0,0 +1,28 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + } + + struct X { + uint256 a; + S s; + } + + uint[79] r; + X x; + + function f() external returns (uint32, uint128, uint256) { + X memory m = X(12, S(42, 23, 34)); + x = m; + return (x.s.a, x.s.b, x.s.c); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 42, 23, 34 diff --git a/test/libsolidity/semanticTests/structs/struct_named_constructor.sol b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol index f74cc90bd..12e69bdfc 100644 --- a/test/libsolidity/semanticTests/structs/struct_named_constructor.sol +++ b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol @@ -10,5 +10,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // s() -> 1, true From 381784dd89087ad7f7852205cba43be124a0cbac Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Thu, 30 Jul 2020 11:33:34 +0200 Subject: [PATCH 134/139] [Sol->Yul] Implementing struct copying from storage to memory --- libsolidity/codegen/YulUtilFunctions.cpp | 70 +++++++++++++++++-- libsolidity/codegen/YulUtilFunctions.h | 1 + .../structs/struct_copy_via_local.sol | 3 + .../structs/struct_storage_to_memory.sol | 26 +++++++ 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index a2dc840e5..256ab238e 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1413,6 +1413,49 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool to_string(_offset) + "_" + _type.identifier(); + + if (_type.category() == Type::Category::Struct) + { + solAssert(_offset == 0, ""); + auto const& structType = dynamic_cast(_type); + solUnimplementedAssert(structType.location() == DataLocation::Memory, ""); + MemberList::MemberMap structMembers = structType.nativeMembers(nullptr); + vector> memberSetValues(structMembers.size()); + for (size_t i = 0; i < structMembers.size(); ++i) + { + auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name); + bool isStruct = structMembers[i].type->category() == Type::Category::Struct; + + memberSetValues[i]["setMember"] = Whiskers(R"( + mstore(add(value, ), (add(slot, ), )) + )") + ("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str()) + ("memberSlotDiff", memberSlotDiff.str()) + ("memberStorageOffset", to_string(memberStorageOffset)) + ("readFromStorage", + isStruct ? + readFromStorage(*structMembers[i].type, memberStorageOffset, true) : + readFromStorageDynamic(*structMembers[i].type, true) + ) + ("notStruct", !isStruct) + .render(); + } + + return m_functionCollector.createFunction(functionName, [&] { + return Whiskers(R"( + function (slot) -> value { + value := () + <#member> + + + } + )") + ("functionName", functionName) + ("allocStruct", allocateMemoryStructFunction(structType)) + ("member", memberSetValues) + .render(); + }); + } return m_functionCollector.createFunction(functionName, [&] { solAssert(_type.sizeOnStack() == 1, ""); return Whiskers(R"( @@ -1430,6 +1473,7 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu { if (_type.category() == Type::Category::Function) solUnimplementedAssert(!_splitFunctionTypes, ""); + string functionName = "read_from_storage_dynamic" + string(_splitFunctionTypes ? "split_" : "") + @@ -2107,13 +2151,27 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), ""); solUnimplementedAssert(toStructType.location() == DataLocation::Memory, ""); - solUnimplementedAssert(fromStructType.location() == DataLocation::CallData, ""); + solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, ""); + + if (fromStructType.location() == DataLocation::CallData) + { + body = Whiskers(R"( + converted := (value, calldatasize()) + )")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder( + {&toStructType} + )).render(); + } + else + { + solAssert(fromStructType.location() == DataLocation::Storage, ""); + + body = Whiskers(R"( + converted := (value) + )") + ("readFromStorage", readFromStorage(toStructType, 0, true)) + .render(); + } - body = Whiskers(R"( - converted := (value, calldatasize()) - )")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder( - {&toStructType} - )).render(); break; } case Type::Category::FixedBytes: diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index d25287589..b2bb6144b 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -222,6 +222,7 @@ public: std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType); /// @returns a function that reads a value type from storage. + /// Will allocate memory if return type is struct with location set to memory /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. /// @param _splitFunctionTypes if false, returns the address and function signature in a /// single variable. diff --git a/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol index a1cfcac05..bc2ade361 100644 --- a/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol +++ b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol @@ -3,6 +3,7 @@ contract c { uint256 a; uint256 b; } + uint[75] r; Struct data1; Struct data2; @@ -15,5 +16,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> true diff --git a/test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol b/test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol new file mode 100644 index 000000000..28c6284ee --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + } + struct X { + uint32 a; + S s; + } + + uint[79] arr; + X x = X(12, S(42, 23, 34)); + + function f() external returns (uint32, uint128, uint256) { + X memory m = x; + return (m.s.a, m.s.b, m.s.c); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 42, 23, 34 From 762e3f3cee05af8891c70435292de29085e0107b Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 17 Aug 2020 13:50:09 +0200 Subject: [PATCH 135/139] [Sol->Yul] Implementing struct copying from calldata to storage --- libsolidity/codegen/YulUtilFunctions.cpp | 17 ++++++++++++--- .../calldata/calldata_struct_to_storage.sol | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 256ab238e..459620909 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1427,7 +1427,7 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool bool isStruct = structMembers[i].type->category() == Type::Category::Struct; memberSetValues[i]["setMember"] = Whiskers(R"( - mstore(add(value, ), (add(slot, ), )) + mstore(add(value, ), (add(slot, ), )) )") ("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str()) ("memberSlotDiff", memberSlotDiff.str()) @@ -1437,7 +1437,7 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool readFromStorage(*structMembers[i].type, memberStorageOffset, true) : readFromStorageDynamic(*structMembers[i].type, true) ) - ("notStruct", !isStruct) + ("hasOffset", !isStruct) .render(); } @@ -1548,11 +1548,16 @@ string YulUtilFunctions::updateStorageValueFunction( auto const& fromStructType = dynamic_cast(*_fromType); auto const& toStructType = dynamic_cast(_toType); solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); - solUnimplementedAssert(fromStructType.location() == DataLocation::Memory, ""); + solAssert(fromStructType.location() != DataLocation::Storage, ""); solUnimplementedAssert(_offset.has_value() && _offset.value() == 0, ""); Whiskers templ(R"( function (slot, value) { + + let valueMem := (value) + + let valueMem := value + <#member> { @@ -1561,6 +1566,12 @@ string YulUtilFunctions::updateStorageValueFunction( } )"); templ("functionName", functionName); + templ("fromCalldata", fromStructType.location() == DataLocation::CallData); + if (fromStructType.location() == DataLocation::CallData) + templ("convertToMemory", conversionFunction( + fromStructType, + *TypeProvider::structType(toStructType.structDefinition(), DataLocation::Memory) + )); MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr); diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol new file mode 100644 index 000000000..9a443d97d --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol @@ -0,0 +1,21 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint256 a; + uint256 b; + bytes2 c; + } + + S s; + + function f(uint32 a, S calldata c, uint256 b) external returns (uint256, uint256, byte) { + s = c; + return (s.a, s.b, s.c[1]); + } +} + +// ==== +// compileViaYul: true +// ---- +// f(uint32, (uint256, uint256, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b" \ No newline at end of file From 23f6369a46dce987f856fcaa58683ea6c4f7d9cd Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 17 Aug 2020 14:47:54 +0200 Subject: [PATCH 136/139] Implementing struct copying from calldata to storage --- Changelog.md | 2 +- libsolidity/codegen/LValue.cpp | 65 +++++++++++-------- libsolidity/codegen/YulUtilFunctions.cpp | 22 +++---- .../calldata/calldata_struct_to_storage.sol | 7 +- 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9c54eabe5..8b93fcfc1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ Language Features: * Allow function definitions outside of contracts, behaving much like internal library functions. - + * Code generator: Implementing copying structs from calldata to storage. Compiler Features: * SMTChecker: Add underflow and overflow as verification conditions in the CHC engine. diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index f4fdd0cbb..40555b658 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -357,38 +357,47 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc "Struct assignment with conversion." ); solAssert(!structType.containsNestedMapping(), ""); - solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); - for (auto const& member: structType.members(nullptr)) + if (sourceType.location() == DataLocation::CallData) { - // assign each member that can live outside of storage - TypePointer const& memberType = member.type; - solAssert(memberType->nameable(), ""); - TypePointer sourceMemberType = sourceType.memberType(member.name); - if (sourceType.location() == DataLocation::Storage) + solAssert(sourceType.sizeOnStack() == 1, ""); + solAssert(structType.sizeOnStack() == 1, ""); + m_context << Instruction::DUP2 << Instruction::DUP2; + m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(structType, &sourceType, 0), 2, 0); + } + else + { + for (auto const& member: structType.members(nullptr)) { - // stack layout: source_ref target_ref - pair const& offsets = sourceType.storageOffsetsOfMember(member.name); - m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; + // assign each member that can live outside of storage + TypePointer const& memberType = member.type; + solAssert(memberType->nameable(), ""); + TypePointer sourceMemberType = sourceType.memberType(member.name); + if (sourceType.location() == DataLocation::Storage) + { + // stack layout: source_ref target_ref + pair const& offsets = sourceType.storageOffsetsOfMember(member.name); + m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; + m_context << u256(offsets.second); + // stack: source_ref target_ref source_member_ref source_member_off + StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); + // stack: source_ref target_ref source_value... + } + else + { + solAssert(sourceType.location() == DataLocation::Memory, ""); + // stack layout: source_ref target_ref + m_context << sourceType.memoryOffsetOfMember(member.name); + m_context << Instruction::DUP3 << Instruction::ADD; + MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); + // stack layout: source_ref target_ref source_value... + } + unsigned stackSize = sourceMemberType->sizeOnStack(); + pair const& offsets = structType.storageOffsetsOfMember(member.name); + m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD; m_context << u256(offsets.second); - // stack: source_ref target_ref source_member_ref source_member_off - StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); - // stack: source_ref target_ref source_value... + // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off + StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); } - else - { - solAssert(sourceType.location() == DataLocation::Memory, ""); - // stack layout: source_ref target_ref - m_context << sourceType.memoryOffsetOfMember(member.name); - m_context << Instruction::DUP3 << Instruction::ADD; - MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); - // stack layout: source_ref target_ref source_value... - } - unsigned stackSize = sourceMemberType->sizeOnStack(); - pair const& offsets = structType.storageOffsetsOfMember(member.name); - m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD; - m_context << u256(offsets.second); - // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off - StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); } // stack layout: source_ref target_ref solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size."); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 459620909..ad801d9f0 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1553,11 +1553,6 @@ string YulUtilFunctions::updateStorageValueFunction( Whiskers templ(R"( function (slot, value) { - - let valueMem := (value) - - let valueMem := value - <#member> { @@ -1566,12 +1561,6 @@ string YulUtilFunctions::updateStorageValueFunction( } )"); templ("functionName", functionName); - templ("fromCalldata", fromStructType.location() == DataLocation::CallData); - if (fromStructType.location() == DataLocation::CallData) - templ("convertToMemory", conversionFunction( - fromStructType, - *TypeProvider::structType(toStructType.structDefinition(), DataLocation::Memory) - )); MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr); @@ -1581,9 +1570,10 @@ string YulUtilFunctions::updateStorageValueFunction( { solAssert(toStructMembers[i].type->memoryHeadSize() == 32, ""); bool isStruct = toStructMembers[i].type->category() == Type::Category::Struct; + bool fromCalldata = fromStructType.location() == DataLocation::CallData; auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(toStructMembers[i].name); memberParams[i]["updateMemberCall"] = Whiskers(R"( - let memberValue := (add(valueMem, )) + let memberValue := (add(value, )) (add(slot, ), , memberValue) )") ("hasOffset", !isStruct) @@ -1595,8 +1585,12 @@ string YulUtilFunctions::updateStorageValueFunction( ) ("memberStorageSlotDiff", slotDiff.str()) ("memberStorageOffset", to_string(offset)) - ("memberMemoryOffset", fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str()) - ("loadFromMemory", readFromMemory(*fromStructMembers[i].type)) + ("memberOffset", + fromCalldata ? + to_string(fromStructType.calldataOffsetOfMember(fromStructMembers[i].name)) : + fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str() + ) + ("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*fromStructMembers[i].type, fromCalldata)) .render(); } templ("member", memberParams); diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol index 9a443d97d..b97ff80c4 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol @@ -3,10 +3,11 @@ pragma experimental ABIEncoderV2; contract C { struct S { uint256 a; - uint256 b; + uint64 b; bytes2 c; } + uint[153] r; S s; function f(uint32 a, S calldata c, uint256 b) external returns (uint256, uint256, byte) { @@ -16,6 +17,6 @@ contract C { } // ==== -// compileViaYul: true +// compileViaYul: also // ---- -// f(uint32, (uint256, uint256, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b" \ No newline at end of file +// f(uint32, (uint256, uint64, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b" \ No newline at end of file From 15163b2270e0c0d348716cb12ec66fa2fa34089e Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 19 Aug 2020 13:13:05 +0200 Subject: [PATCH 137/139] Refactoring readFromStorage util frunctions Co-authored-by: Daniel Kirchner --- libsolidity/codegen/YulUtilFunctions.cpp | 160 +++++++++++++---------- libsolidity/codegen/YulUtilFunctions.h | 14 +- 2 files changed, 103 insertions(+), 71 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index ad801d9f0..a93dd57a7 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1404,58 +1404,35 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes) { + if (_type.isValueType()) + return readFromStorageValueType(_type, _offset, _splitFunctionTypes); + else + { + solAssert(_offset == 0, ""); + return readFromStorageReferenceType(_type); + } +} + +string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes) +{ + solAssert(_type.isValueType(), ""); + return readFromStorageValueTypeDynamic(_type, _splitFunctionTypes); +} + +string YulUtilFunctions::readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes) +{ + solAssert(_type.isValueType(), ""); + if (_type.category() == Type::Category::Function) solUnimplementedAssert(!_splitFunctionTypes, ""); string functionName = - "read_from_storage_" + - string(_splitFunctionTypes ? "split_" : "") + - "offset_" + - to_string(_offset) + - "_" + - _type.identifier(); + "read_from_storage_" + + string(_splitFunctionTypes ? "split_" : "") + + "offset_" + + to_string(_offset) + + "_" + + _type.identifier(); - if (_type.category() == Type::Category::Struct) - { - solAssert(_offset == 0, ""); - auto const& structType = dynamic_cast(_type); - solUnimplementedAssert(structType.location() == DataLocation::Memory, ""); - MemberList::MemberMap structMembers = structType.nativeMembers(nullptr); - vector> memberSetValues(structMembers.size()); - for (size_t i = 0; i < structMembers.size(); ++i) - { - auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name); - bool isStruct = structMembers[i].type->category() == Type::Category::Struct; - - memberSetValues[i]["setMember"] = Whiskers(R"( - mstore(add(value, ), (add(slot, ), )) - )") - ("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str()) - ("memberSlotDiff", memberSlotDiff.str()) - ("memberStorageOffset", to_string(memberStorageOffset)) - ("readFromStorage", - isStruct ? - readFromStorage(*structMembers[i].type, memberStorageOffset, true) : - readFromStorageDynamic(*structMembers[i].type, true) - ) - ("hasOffset", !isStruct) - .render(); - } - - return m_functionCollector.createFunction(functionName, [&] { - return Whiskers(R"( - function (slot) -> value { - value := () - <#member> - - - } - )") - ("functionName", functionName) - ("allocStruct", allocateMemoryStructFunction(structType)) - ("member", memberSetValues) - .render(); - }); - } return m_functionCollector.createFunction(functionName, [&] { solAssert(_type.sizeOnStack() == 1, ""); return Whiskers(R"( @@ -1463,19 +1440,19 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool value := (sload(slot)) } )") - ("functionName", functionName) - ("extract", extractFromStorageValue(_type, _offset, false)) - .render(); + ("functionName", functionName) + ("extract", extractFromStorageValue(_type, _offset, false)) + .render(); }); } - -string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes) +string YulUtilFunctions::readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes) { + solAssert(_type.isValueType(), ""); if (_type.category() == Type::Category::Function) solUnimplementedAssert(!_splitFunctionTypes, ""); string functionName = - "read_from_storage_dynamic" + + "read_from_storage_value_type_dynamic" + string(_splitFunctionTypes ? "split_" : "") + "_" + _type.identifier(); @@ -1491,6 +1468,55 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu .render(); }); } +string YulUtilFunctions::readFromStorageReferenceType(Type const& _type) +{ + solUnimplementedAssert(_type.category() == Type::Category::Struct, ""); + + string functionName = "read_from_storage_reference_type_" + _type.identifier(); + + auto const& structType = dynamic_cast(_type); + solAssert(structType.location() == DataLocation::Memory, ""); + MemberList::MemberMap structMembers = structType.nativeMembers(nullptr); + vector> memberSetValues(structMembers.size()); + for (size_t i = 0; i < structMembers.size(); ++i) + { + auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name); + + memberSetValues[i]["setMember"] = Whiskers(R"( + { + let := (add(slot, ), ) + (add(value, ), ) + } + )") + ("memberValues", suffixedVariableNameList("memberValue_", 0, structMembers[i].type->stackItems().size())) + ("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str()) + ("memberSlotDiff", memberSlotDiff.str()) + ("memberStorageOffset", to_string(memberStorageOffset)) + ("readFromStorage", + structMembers[i].type->isValueType() ? + readFromStorageDynamic(*structMembers[i].type, true) : + readFromStorage(*structMembers[i].type, memberStorageOffset, true) + ) + ("writeToMemory", writeToMemoryFunction(*structMembers[i].type)) + ("hasOffset", structMembers[i].type->isValueType()) + .render(); + } + + return m_functionCollector.createFunction(functionName, [&] { + return Whiskers(R"( + function (slot) -> value { + value := () + <#member> + + + } + )") + ("functionName", functionName) + ("allocStruct", allocateMemoryStructFunction(structType)) + ("member", memberSetValues) + .render(); + }); +} string YulUtilFunctions::readFromMemory(Type const& _type) { @@ -1562,35 +1588,33 @@ string YulUtilFunctions::updateStorageValueFunction( )"); templ("functionName", functionName); - MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); - MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr); + MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); - vector> memberParams(toStructMembers.size()); - for (size_t i = 0; i < toStructMembers.size(); ++i) + vector> memberParams(structMembers.size()); + for (size_t i = 0; i < structMembers.size(); ++i) { - solAssert(toStructMembers[i].type->memoryHeadSize() == 32, ""); - bool isStruct = toStructMembers[i].type->category() == Type::Category::Struct; + solAssert(structMembers[i].type->memoryHeadSize() == 32, ""); bool fromCalldata = fromStructType.location() == DataLocation::CallData; - auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(toStructMembers[i].name); + auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); memberParams[i]["updateMemberCall"] = Whiskers(R"( let memberValue := (add(value, )) (add(slot, ), , memberValue) )") - ("hasOffset", !isStruct) + ("hasOffset", structMembers[i].type->isValueType()) ( "updateMember", - isStruct ? - updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type, offset) : - updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type) + structMembers[i].type->isValueType() ? + updateStorageValueFunction(*structMembers[i].type, structMembers[i].type) : + updateStorageValueFunction(*structMembers[i].type, structMembers[i].type, offset) ) ("memberStorageSlotDiff", slotDiff.str()) ("memberStorageOffset", to_string(offset)) ("memberOffset", fromCalldata ? - to_string(fromStructType.calldataOffsetOfMember(fromStructMembers[i].name)) : - fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str() + to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) : + fromStructType.memoryOffsetOfMember(structMembers[i].name).str() ) - ("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*fromStructMembers[i].type, fromCalldata)) + ("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata)) .render(); } templ("member", memberParams); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index b2bb6144b..96ac4dfd4 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -221,9 +221,7 @@ public: /// @param _keyType the type of the value provided std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType); - /// @returns a function that reads a value type from storage. - /// Will allocate memory if return type is struct with location set to memory - /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. + /// @returns a function that reads a type from storage. /// @param _splitFunctionTypes if false, returns the address and function signature in a /// single variable. std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes); @@ -406,6 +404,16 @@ private: std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata); + /// @returns a function that reads a value type from storage. + /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. + /// @param _splitFunctionTypes if false, returns the address and function signature in a + /// single variable. + std::string readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes); + std::string readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes); + + /// @returns a function that reads a reference type from storage to memory (performing a deep copy). + std::string readFromStorageReferenceType(Type const& _type); + langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; MultiUseYulFunctionCollector& m_functionCollector; From 1fab5b79fbc4c40340cea40120a42e90e16a8d13 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Tue, 1 Sep 2020 14:43:40 +0200 Subject: [PATCH 138/139] Refactoring signature for updateStorageValueFunction --- libsolidity/codegen/LValue.cpp | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 37 +++++++++++++------ libsolidity/codegen/YulUtilFunctions.h | 2 +- .../codegen/ir/IRGeneratorForStatements.cpp | 2 +- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 40555b658..576a2d4c0 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -362,7 +362,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc solAssert(sourceType.sizeOnStack() == 1, ""); solAssert(structType.sizeOnStack() == 1, ""); m_context << Instruction::DUP2 << Instruction::DUP2; - m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(structType, &sourceType, 0), 2, 0); + m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(sourceType, structType, 0), 2, 0); } else { diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index a93dd57a7..b2f3ad8dd 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -964,7 +964,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) ("dataAreaFunction", arrayDataAreaFunction(_type)) ("isByteArray", _type.isByteArray()) ("indexAccess", storageArrayIndexAccessFunction(_type)) - ("storeValue", updateStorageValueFunction(*_type.baseType())) + ("storeValue", updateStorageValueFunction(*_type.baseType(), *_type.baseType())) ("maxArrayLength", (u256(1) << 64).str()) ("shl", shiftLeftFunctionDynamic()) ("shr", shiftRightFunction(248)) @@ -994,7 +994,7 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) ("functionName", functionName) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) - ("storeValue", updateStorageValueFunction(*_type.baseType())) + ("storeValue", updateStorageValueFunction(*_type.baseType(), *_type.baseType())) ("maxArrayLength", (u256(1) << 64).str()) ("zeroValueFunction", zeroValueFunction(*_type.baseType())) .render(); @@ -1529,21 +1529,22 @@ string YulUtilFunctions::readFromCalldata(Type const& _type) } string YulUtilFunctions::updateStorageValueFunction( + Type const& _fromType, Type const& _toType, - Type const* _fromType, std::optional const& _offset ) { string const functionName = "update_storage_value_" + (_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") + - (_fromType ? "_from_" + _fromType->identifier() : "") + + _fromType.identifier() + "_to_" + _toType.identifier(); return m_functionCollector.createFunction(functionName, [&] { if (_toType.isValueType()) { + solAssert(_fromType.isImplicitlyConvertibleTo(_toType), ""); solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size."); solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size."); @@ -1565,13 +1566,20 @@ string YulUtilFunctions::updateStorageValueFunction( } else { + auto const* toReferenceType = dynamic_cast(&_toType); + auto const* fromReferenceType = dynamic_cast(&_toType); + solAssert(fromReferenceType && toReferenceType, ""); + solAssert(*toReferenceType->copyForLocation( + fromReferenceType->location(), + fromReferenceType->isPointer() + ).get() == *fromReferenceType, ""); + if (_toType.category() == Type::Category::Array) solUnimplementedAssert(false, ""); else if (_toType.category() == Type::Category::Struct) { - solAssert(_fromType, ""); - solAssert(_fromType->category() == Type::Category::Struct, ""); - auto const& fromStructType = dynamic_cast(*_fromType); + solAssert(_fromType.category() == Type::Category::Struct, ""); + auto const& fromStructType = dynamic_cast(_fromType); auto const& toStructType = dynamic_cast(_toType); solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); solAssert(fromStructType.location() != DataLocation::Storage, ""); @@ -1597,15 +1605,20 @@ string YulUtilFunctions::updateStorageValueFunction( bool fromCalldata = fromStructType.location() == DataLocation::CallData; auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); memberParams[i]["updateMemberCall"] = Whiskers(R"( - let memberValue := (add(value, )) - (add(slot, ), , memberValue) + let := (add(value, )) + (add(slot, ), , ) )") + ("memberValues", suffixedVariableNameList( + "memberValue_", + 0, + structMembers[i].type->stackItems().size() + )) ("hasOffset", structMembers[i].type->isValueType()) ( "updateMember", structMembers[i].type->isValueType() ? - updateStorageValueFunction(*structMembers[i].type, structMembers[i].type) : - updateStorageValueFunction(*structMembers[i].type, structMembers[i].type, offset) + updateStorageValueFunction(*structMembers[i].type, *structMembers[i].type) : + updateStorageValueFunction(*structMembers[i].type, *structMembers[i].type, offset) ) ("memberStorageSlotDiff", slotDiff.str()) ("memberStorageOffset", to_string(offset)) @@ -2632,7 +2645,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type) } )") ("functionName", functionName) - ("store", updateStorageValueFunction(_type)) + ("store", updateStorageValueFunction(_type, _type)) ("zeroValue", zeroValueFunction(_type)) .render(); else if (_type.category() == Type::Category::Array) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 96ac4dfd4..8394c33af 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -248,8 +248,8 @@ public: /// runtime parameter. /// signature: (slot, [offset,] value) std::string updateStorageValueFunction( + Type const& _fromType, Type const& _toType, - Type const* _fromType = nullptr, std::optional const& _offset = std::optional() ); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 170b636fc..d5d3f19fc 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2486,7 +2486,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable offset = std::get(_storage.offset); m_code << - m_utils.updateStorageValueFunction(_lvalue.type, &_value.type(), offset) << + m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offset) << "(" << _storage.slot << ( From a740cb619be84b00616aa04f722edc5d0a0856cb Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Tue, 1 Sep 2020 15:56:55 +0200 Subject: [PATCH 139/139] Adding tests for copying structs with function pointers between storage and memory --- .../struct_memory_to_storage_function_ptr.sol | 33 +++++++++++++++++++ .../struct_storage_to_memory_function_ptr.sol | 32 ++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol create mode 100644 test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol diff --git a/test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol b/test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol new file mode 100644 index 000000000..2a8b36f36 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol @@ -0,0 +1,33 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + function() internal returns (uint32) f; + } + + struct X { + uint256 a; + S s; + } + + uint[79] r; + X x; + + function f() external returns (uint32, uint128, uint256, uint32, uint32) { + X memory m = X(12, S(42, 23, 34, g)); + x = m; + return (x.s.a, x.s.b, x.s.c, x.s.f(), m.s.f()); + } + + function g() internal returns (uint32) { + return x.s.a; + } +} + +// ==== +// compileViaYul: false +// ---- +// f() -> 42, 23, 34, 42, 42 diff --git a/test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol b/test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol new file mode 100644 index 000000000..12ba289ea --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol @@ -0,0 +1,32 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + function() internal returns (uint32) f; + } + + struct X { + uint256 a; + S s; + } + + uint[79] arr; + X x = X(12, S(42, 23, 34, g)); + + function f() external returns (uint32, uint128, uint256, uint32, uint32) { + X memory m = x; + return (m.s.a, m.s.b, m.s.c, m.s.f(), x.s.f()); + } + + function g() internal returns (uint32) { + return x.s.a; + } +} + +// ==== +// compileViaYul: false +// ---- +// f() -> 42, 23, 34, 42, 42