diff --git a/Changelog.md b/Changelog.md index 80b202c44..6a89ab1dd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -47,6 +47,7 @@ Compiler Features: Language Features: * Allow to obtain the selector of public or external library functions via a member ``.selector``. * Parser: Allow splitting string and hexadecimal string literals into multiple parts. + * Inline Assembly: Support referencing other constants. Compiler Features: @@ -66,7 +67,7 @@ Bugfixes: * SMTChecker: Fix internal error when using arrays or mappings of functions. * SMTChecker: Fix internal error in array of structs type. * Yul: Consider infinite loops and recursion to be not removable. - + * Version Checker: 0.5.x-prerelease will match `pragma solidity ^0.5`. ### 0.5.13 (2019-11-14) diff --git a/docs/contributing.rst b/docs/contributing.rst index 6220d7d7f..1b50ccaaa 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -86,7 +86,7 @@ but for quicker feedback, you might want to run specific tests. Solidity includes different types of tests, most of them bundled into the `Boost C++ Test Framework `_ application ``soltest``. -Running ``build/test/soltest` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. +Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. Some tests require the ``evmone`` library, others require ``libz3``. @@ -113,7 +113,7 @@ See especially: If you are running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-smt``. To run a subset of tests, you can use filters: -``./scripts/soltest.sh -t TestSuite/TestName, +``./scripts/soltest.sh -t TestSuite/TestName``, where ``TestName`` can be a wildcard ``*``. For example, here is an example test you might run; diff --git a/liblangutil/SemVerHandler.cpp b/liblangutil/SemVerHandler.cpp index 378923420..611f058ae 100644 --- a/liblangutil/SemVerHandler.cpp +++ b/liblangutil/SemVerHandler.cpp @@ -105,8 +105,19 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio didCompare = true; cmp = _version.numbers[i] - version.numbers[i]; } + if (cmp == 0 && !_version.prerelease.empty() && didCompare) + { cmp = -1; + for (unsigned i = levelsPresent; i < 3; i++) + { + if (_version.numbers[i] > 0) + { + cmp = 0; + break; + } + } + } switch (prefix) { diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index a5936c947..00f10ea5c 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -39,6 +39,8 @@ set(sources ast/ASTForward.h ast/ASTJsonConverter.cpp ast/ASTJsonConverter.h + ast/ASTUtils.cpp + ast/ASTUtils.h ast/ASTVisitor.h ast/ExperimentalFeatures.h ast/Types.cpp diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 960458f24..a171bcdb0 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -658,6 +659,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(var->type(), "Expected variable type!"); if (var->isConstant()) { + var = rootVariableDeclaration(*var); + if (!var->value()) { m_errorReporter.typeError(_identifier.location, "Constant has no value."); @@ -668,7 +671,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) type(*var->value())->category() != Type::Category::RationalNumber )) { - m_errorReporter.typeError(_identifier.location, "Only direct number constants are supported by inline assembly."); + m_errorReporter.typeError(_identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); return size_t(-1); } else if (_context == yul::IdentifierContext::LValue) diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp new file mode 100644 index 000000000..991083dee --- /dev/null +++ b/libsolidity/ast/ASTUtils.cpp @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#include +#include + +namespace dev +{ +namespace solidity +{ + +VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl) +{ + solAssert(_varDecl.isConstant(), "Constant variable expected"); + + VariableDeclaration const* rootDecl = &_varDecl; + Identifier const* identifier; + while ((identifier = dynamic_cast(rootDecl->value().get()))) + { + auto referencedVarDecl = dynamic_cast(identifier->annotation().referencedDeclaration); + solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration"); + rootDecl = referencedVarDecl; + } + return rootDecl; +} + +} +} diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h new file mode 100644 index 000000000..67841f1ce --- /dev/null +++ b/libsolidity/ast/ASTUtils.h @@ -0,0 +1,30 @@ +/* + 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 + +namespace dev +{ +namespace solidity +{ + +/// Find the topmost referenced variable declaration when the given variable +/// declaration value is an identifier. Works only for constant variable declarations. +VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl); + +} +} diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 2ff92510c..1fe96bbac 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -668,6 +669,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { if (variable->isConstant()) { + variable = rootVariableDeclaration(*variable); u256 value; if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) { diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 1fc594978..dc1be1035 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -29,21 +29,24 @@ using namespace dev::solidity; BMC::BMC( smt::EncodingContext& _context, - ErrorReporter& _errorReporter, map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ErrorReporter& _errorReporter, + map const& _smtlib2Responses, + ReadCallback::Callback const& _smtCallback, + smt::SMTSolverChoice _enabledSolvers ): SMTEncoder(_context), m_outerErrorReporter(_errorReporter), - m_interface(make_shared(_smtlib2Responses, _smtCallback)) + m_interface(make_shared(_smtlib2Responses, _smtCallback, _enabledSolvers)) { #if defined (HAVE_Z3) || defined (HAVE_CVC4) - if (!_smtlib2Responses.empty()) - m_errorReporter.warning( - "SMT-LIB2 query responses were given in the auxiliary input, " - "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." - "These responses will be ignored." - "Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." - ); + if (_enabledSolvers.some()) + if (!_smtlib2Responses.empty()) + m_errorReporter.warning( + "SMT-LIB2 query responses were given in the auxiliary input, " + "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." + "These responses will be ignored." + "Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." + ); #endif } diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index d5b9d88d1..473b5dfee 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -57,7 +57,8 @@ public: smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback const& _smtCallback, + smt::SMTSolverChoice _enabledSolvers ); void analyze(SourceUnit const& _sources, std::set _safeAssertions); diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 3a142616e..42e20320e 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -36,17 +36,23 @@ CHC::CHC( smt::EncodingContext& _context, ErrorReporter& _errorReporter, map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback const& _smtCallback, + smt::SMTSolverChoice _enabledSolvers ): SMTEncoder(_context), #ifdef HAVE_Z3 - m_interface(make_shared()), + m_interface( + _enabledSolvers.z3 ? + dynamic_pointer_cast(make_shared()) : + dynamic_pointer_cast(make_shared(_smtlib2Responses, _smtCallback)) + ), #else m_interface(make_shared(_smtlib2Responses, _smtCallback)), #endif m_outerErrorReporter(_errorReporter) { (void)_smtlib2Responses; + (void)_enabledSolvers; (void)_smtCallback; } diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8c3e322ce..c695545da 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -50,7 +50,8 @@ public: smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback const& _smtCallback, + smt::SMTSolverChoice _enabledSolvers ); void analyze(SourceUnit const& _sources); diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index 20bc527d3..570d220bb 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -25,10 +25,11 @@ using namespace dev::solidity; ModelChecker::ModelChecker( ErrorReporter& _errorReporter, map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback const& _smtCallback, + smt::SMTSolverChoice _enabledSolvers ): - m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback), - m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback), + m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers), + m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers), m_context() { } diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index 764803141..4c23bda39 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,13 @@ namespace solidity class ModelChecker { public: + /// @param _enabledSolvers represents a runtime choice of which SMT solvers + /// should be used, even if all are available. The default choice is to use all. ModelChecker( langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback = ReadCallback::Callback() + ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(), + smt::SMTSolverChoice _enabledSolvers = smt::SMTSolverChoice::All() ); void analyze(SourceUnit const& _sources); diff --git a/libsolidity/formal/SMTPortfolio.cpp b/libsolidity/formal/SMTPortfolio.cpp index 96e758b10..26d0f4b4e 100644 --- a/libsolidity/formal/SMTPortfolio.cpp +++ b/libsolidity/formal/SMTPortfolio.cpp @@ -32,16 +32,20 @@ using namespace dev::solidity::smt; SMTPortfolio::SMTPortfolio( map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback const& _smtCallback, + SMTSolverChoice _enabledSolvers ) { m_solvers.emplace_back(make_unique(_smtlib2Responses, _smtCallback)); #ifdef HAVE_Z3 - m_solvers.emplace_back(make_unique()); + if (_enabledSolvers.z3) + m_solvers.emplace_back(make_unique()); #endif #ifdef HAVE_CVC4 - m_solvers.emplace_back(make_unique()); + if (_enabledSolvers.cvc4) + m_solvers.emplace_back(make_unique()); #endif + (void)_enabledSolvers; } void SMTPortfolio::reset() diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h index 62b084ee7..47691dff5 100644 --- a/libsolidity/formal/SMTPortfolio.h +++ b/libsolidity/formal/SMTPortfolio.h @@ -44,7 +44,8 @@ class SMTPortfolio: public SolverInterface, public boost::noncopyable public: SMTPortfolio( std::map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback const& _smtCallback, + SMTSolverChoice _enabledSolvers ); void reset() override; diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index a39e699af..81f6ea497 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -36,6 +36,21 @@ namespace solidity namespace smt { +struct SMTSolverChoice +{ + bool cvc4 = false; + bool z3 = false; + + static constexpr SMTSolverChoice All() { return {true, true}; } + static constexpr SMTSolverChoice CVC4() { return {true, false}; } + static constexpr SMTSolverChoice Z3() { return {false, true}; } + static constexpr SMTSolverChoice None() { return {false, false}; } + + bool none() { return !some(); } + bool some() { return cvc4 || z3; } + bool all() { return cvc4 && z3; } +}; + enum class CheckResult { SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 4a6c123ef..7dae613a8 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -70,6 +70,8 @@ BOOST_AUTO_TEST_CASE(positive_range) {"*", "1.2.3-foo"}, {"1.0.0 - 2.0.0", "1.2.3"}, {"1.0.0", "1.0.0"}, + {"1.0", "1.0.0"}, + {"1", "1.0.0"}, {">=*", "0.2.4"}, {"*", "1.2.3"}, {">=1.0.0", "1.0.0"}, @@ -82,6 +84,8 @@ BOOST_AUTO_TEST_CASE(positive_range) {"<=2.0.0", "0.2.9"}, {"<2.0.0", "1.9999.9999"}, {"<2.0.0", "0.2.9"}, + {"<1.0", "1.0.0-pre"}, + {"<1", "1.0.0-pre"}, {">= 1.0.0", "1.0.0"}, {">= 1.0.0", "1.0.1"}, {">= 1.0.0", "1.1.0"}, @@ -137,6 +141,10 @@ BOOST_AUTO_TEST_CASE(positive_range) {"^0.1.2", "0.1.2"}, {"^0.1", "0.1.2"}, {"^1.2", "1.4.2"}, + {"^1.2", "1.2.1-pre"}, + {"^1.2", "1.2.0"}, + {"^1", "1.2.0-pre"}, + {"^1", "1.2.0"}, {"<=1.2.3", "1.2.3-beta"}, {">1.2", "1.3.0-beta"}, {"<1.2.3", "1.2.3-beta"}, @@ -158,6 +166,8 @@ BOOST_AUTO_TEST_CASE(negative_range) // Positive range tests vector> tests = { {"1.0.0 - 2.0.0", "2.2.3"}, + {"1.0", "1.0.0-pre"}, + {"1", "1.0.0-pre"}, {"^1.2.3", "1.2.3-pre"}, {"^1.2", "1.2.0-pre"}, {"^1.2.3", "1.2.3-beta"}, diff --git a/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol new file mode 100644 index 000000000..d2e24a70e --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol @@ -0,0 +1,26 @@ +contract C { + uint constant a = 2; + uint constant aa = a; + uint constant aaa = aa; + bytes2 constant b = 0xabcd; + bytes2 constant bb = b; + bytes3 constant c = "abc"; + bytes3 constant cc = c; + bytes3 constant ccc = cc; + bytes3 constant cccc = ccc; + bool constant d = true; + bool constant dd = d; + address payable constant e = 0x1212121212121212121212121212121212121212; + address payable constant ee = e; + function f() public pure returns (uint w, bytes2 x, bytes3 y, bool z, address t) { + assembly { + w := aaa + x := bb + y := cccc + z := dd + t := ee + } + } +} +// ---- +// f() -> 2, left(0xabcd), left(0x616263), true, 0x1212121212121212121212121212121212121212 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol b/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol index 04cb37421..0711c5368 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// TypeError: (115-116): Only direct number constants are supported by inline assembly. +// TypeError: (115-116): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol b/test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol new file mode 100644 index 000000000..dba00a588 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol @@ -0,0 +1,11 @@ +contract C { + bytes32 constant x = keccak256("abc"); + bytes32 constant y = x; + function f() public pure returns (uint t) { + assembly { + t := y + } + } +} +// ---- +// TypeError: (168-169): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol b/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol index eba70edce..dbde5cfa3 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol @@ -8,4 +8,3 @@ contract C { } } // ---- -// TypeError: (134-135): Only direct number constants are supported by inline assembly.