diff options
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | docs/control-structures.rst | 3 | ||||
-rw-r--r-- | docs/index.rst | 3 | ||||
-rw-r--r-- | docs/miscellaneous.rst | 3 | ||||
-rw-r--r-- | docs/utils/SolidityLexer.py | 2 | ||||
-rw-r--r-- | libsolidity/analysis/GlobalContext.cpp | 5 | ||||
-rw-r--r-- | libsolidity/analysis/PostTypeChecker.cpp | 108 | ||||
-rw-r--r-- | libsolidity/analysis/PostTypeChecker.h | 69 | ||||
-rw-r--r-- | libsolidity/analysis/StaticAnalyzer.h | 3 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 13 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 5 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 9 | ||||
-rw-r--r-- | libsolidity/interface/Exceptions.cpp | 8 | ||||
-rw-r--r-- | libsolidity/interface/Exceptions.h | 6 | ||||
-rw-r--r-- | test/RPCSession.cpp | 2 | ||||
-rw-r--r-- | test/RPCSession.h | 1 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 36 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 73 |
18 files changed, 308 insertions, 42 deletions
diff --git a/Changelog.md b/Changelog.md index 5eb1e401..8e11775b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Bugfixes: * Commandline interface: Always escape filenames (replace ``/``, ``:`` and ``.`` with ``_``). * Commandline interface: Do not try creating paths ``.`` and ``..``. * Type system: Fix a crash caused by continuing on fatal errors in the code. + * Type system: Detect cyclic dependencies between constants. * Type system: Disallow arrays with negative length. * Type system: Fix a crash related to invalid binary operators. * Type system: Disallow ``var`` declaration with empty tuple type. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index ebc45965..83d3eac9 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -399,10 +399,9 @@ Currently, Solidity automatically generates a runtime exception in the following While a user-provided exception is generated in the following situations: #. Calling ``throw``. -#. The condition of ``assert(condition)`` is not met. Internally, Solidity performs a revert operation (instruction ``0xfd``) when a user-provided exception is thrown. In contrast, it performs an invalid operation (instruction ``0xfe``) if a runtime exception is encountered. In both cases, this causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction -(or at least call) without effect.
\ No newline at end of file +(or at least call) without effect. diff --git a/docs/index.rst b/docs/index.rst index 904c3a54..fc1a4231 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -39,6 +39,9 @@ Available Solidity Integrations * `Ethereum Studio <https://live.ether.camp/>`_ Specialized web IDE that also provides shell access to a complete Ethereum environment. +* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_ + Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs) + * `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_ Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 7b0305d5..e3ec0efb 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -435,7 +435,7 @@ The following is the order of precedence for operators, listed in order of evalu | *16* | Comma operator | ``,`` | +------------+-------------------------------------+--------------------------------------------+ -.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, assert, revert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send +.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, revert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send Global Variables ================ @@ -461,7 +461,6 @@ Global Variables - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error - ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` -- ``assert(bool condition)``: throws if the condition is false - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py index f5220c8b..1dc99159 100644 --- a/docs/utils/SolidityLexer.py +++ b/docs/utils/SolidityLexer.py @@ -54,7 +54,7 @@ class SolidityLexer(RegexLexer): r'(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', Operator, 'slashstartsregex'), (r'[{(\[;,]', Punctuation, 'slashstartsregex'), (r'[})\].]', Punctuation), - (r'(anonymous|as|assembly|break|constant|continue|do|else|external|hex|if|' + (r'(anonymous|as|assembly|break|constant|continue|do|delete|else|external|for|hex|if|' r'indexed|internal|import|is|mapping|memory|new|payable|public|pragma|' r'private|return|returns|storage|super|this|throw|using|while)\b', Keyword, 'slashstartsregex'), (r'(var|function|event|modifier|struct|enum|contract|library)\b', Keyword.Declaration, 'slashstartsregex'), diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 4f100cd0..069d10f5 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -66,8 +66,9 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared< make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true)), - make_shared<MagicVariableDeclaration>("assert", - make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert)), +// Disabled until decision about semantics of assert is made. +// make_shared<MagicVariableDeclaration>("assert", +// make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert)), make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Revert))}) { diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp new file mode 100644 index 00000000..cae77c74 --- /dev/null +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -0,0 +1,108 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <libsolidity/analysis/PostTypeChecker.h> +#include <libsolidity/ast/AST.h> +#include <libsolidity/analysis/SemVerHandler.h> +#include <libsolidity/interface/Version.h> + +#include <boost/range/adaptor/map.hpp> + +#include <memory> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +bool PostTypeChecker::check(ASTNode const& _astRoot) +{ + _astRoot.accept(*this); + return Error::containsOnlyWarnings(m_errors); +} + +void PostTypeChecker::typeError(SourceLocation const& _location, std::string const& _description) +{ + auto err = make_shared<Error>(Error::Type::TypeError); + *err << + errinfo_sourceLocation(_location) << + errinfo_comment(_description); + + m_errors.push_back(err); +} + +bool PostTypeChecker::visit(ContractDefinition const&) +{ + solAssert(!m_currentConstVariable, ""); + solAssert(m_constVariableDependencies.empty(), ""); + return true; +} + +void PostTypeChecker::endVisit(ContractDefinition const&) +{ + solAssert(!m_currentConstVariable, ""); + for (auto declaration: m_constVariables) + if (auto identifier = findCycle(declaration)) + typeError(declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + "."); +} + +bool PostTypeChecker::visit(VariableDeclaration const& _variable) +{ + solAssert(!m_currentConstVariable, ""); + if (_variable.isConstant()) + { + m_currentConstVariable = &_variable; + m_constVariables.push_back(&_variable); + } + return true; +} + +void PostTypeChecker::endVisit(VariableDeclaration const& _variable) +{ + if (_variable.isConstant()) + { + solAssert(m_currentConstVariable == &_variable, ""); + m_currentConstVariable = nullptr; + } +} + +bool PostTypeChecker::visit(Identifier const& _identifier) +{ + if (m_currentConstVariable) + if (auto var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) + if (var->isConstant()) + m_constVariableDependencies[m_currentConstVariable].insert(var); + return true; +} + +VariableDeclaration const* PostTypeChecker::findCycle( + VariableDeclaration const* _startingFrom, + set<VariableDeclaration const*> const& _seen +) +{ + if (_seen.count(_startingFrom)) + return _startingFrom; + else if (m_constVariableDependencies.count(_startingFrom)) + { + set<VariableDeclaration const*> seen(_seen); + seen.insert(_startingFrom); + for (auto v: m_constVariableDependencies[_startingFrom]) + if (findCycle(v, seen)) + return v; + } + return nullptr; +} diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h new file mode 100644 index 00000000..8774f413 --- /dev/null +++ b/libsolidity/analysis/PostTypeChecker.h @@ -0,0 +1,69 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <libsolidity/analysis/TypeChecker.h> +#include <libsolidity/ast/Types.h> +#include <libsolidity/ast/ASTAnnotations.h> +#include <libsolidity/ast/ASTForward.h> +#include <libsolidity/ast/ASTVisitor.h> + +namespace dev +{ +namespace solidity +{ + +/** + * This module performs analyses on the AST that are done after type checking and assignments of types: + * - whether there are circular references in constant state variables + * @TODO factor out each use-case into an individual class (but do the traversal only once) + */ +class PostTypeChecker: private ASTConstVisitor +{ +public: + /// @param _errors the reference to the list of errors and warnings to add them found during type checking. + PostTypeChecker(ErrorList& _errors): m_errors(_errors) {} + + bool check(ASTNode const& _astRoot); + +private: + /// Adds a new error to the list of errors. + void typeError(SourceLocation const& _location, std::string const& _description); + + virtual bool visit(ContractDefinition const& _contract) override; + virtual void endVisit(ContractDefinition const& _contract) override; + + virtual bool visit(VariableDeclaration const& _declaration) override; + virtual void endVisit(VariableDeclaration const& _declaration) override; + + virtual bool visit(Identifier const& _identifier) override; + + VariableDeclaration const* findCycle( + VariableDeclaration const* _startingFrom, + std::set<VariableDeclaration const*> const& _seen = {} + ); + + ErrorList& m_errors; + + VariableDeclaration const* m_currentConstVariable = nullptr; + std::vector<VariableDeclaration const*> m_constVariables; ///< Required for determinism. + std::map<VariableDeclaration const*, std::set<VariableDeclaration const*>> m_constVariableDependencies; +}; + +} +} diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index b6cf783e..0cb961bd 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -36,6 +36,9 @@ namespace solidity /** * The module that performs static analysis on the AST. + * In this context, static analysis is anything that can produce warnings which can help + * programmers write cleaner code. For every warning generated eher, it has to be possible to write + * equivalent code that does generate the warning. */ class StaticAnalyzer: private ASTConstVisitor { diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 10406400..93b183a2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -737,13 +737,16 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get())) { if (ref->dataStoredIn(DataLocation::Storage)) - { warning( varDecl.location(), "Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?" ); - } } + else if (dynamic_cast<MappingType const*>(type(varDecl).get())) + typeError( + varDecl.location(), + "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." + ); varDecl.accept(*this); return false; } @@ -1587,7 +1590,11 @@ void TypeChecker::endVisit(Literal const& _literal) return; } else - warning(_literal.location(), "This looks like an address but has an invalid checksum."); + warning( + _literal.location(), + "This looks like an address but has an invalid checksum. " + "If this is not used as an address, please prepend '00'." + ); } _literal.annotation().type = Type::forLiteral(_literal); if (!_literal.annotation().type) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index aac4c0c6..5192ffa6 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -885,9 +885,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // jump if condition was met m_context << Instruction::ISZERO << Instruction::ISZERO; auto success = m_context.appendConditionalJump(); - // condition was not met, abort - m_context << u256(0) << u256(0); - m_context << Instruction::REVERT; + // condition was not met, flag an error + m_context << Instruction::INVALID; // the success branch m_context << success; break; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9d8d872f..594efb1e 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -34,6 +34,7 @@ #include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/analysis/StaticAnalyzer.h> +#include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/interface/InterfaceHandler.h> @@ -219,6 +220,14 @@ bool CompilerStack::parse() if (noErrors) { + PostTypeChecker postTypeChecker(m_errors); + for (Source const* source: m_sourceOrder) + if (!postTypeChecker.check(*source->ast)) + noErrors = false; + } + + if (noErrors) + { StaticAnalyzer staticAnalyzer(m_errors); for (Source const* source: m_sourceOrder) if (!staticAnalyzer.analyze(*source->ast)) diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp index 20f9dd75..968a24ad 100644 --- a/libsolidity/interface/Exceptions.cpp +++ b/libsolidity/interface/Exceptions.cpp @@ -27,7 +27,8 @@ using namespace std; using namespace dev; using namespace dev::solidity; -Error::Error(Type _type): m_type(_type) +Error::Error(Type _type, SourceLocation const& _location, string const& _description): + m_type(_type) { switch(m_type) { @@ -56,6 +57,11 @@ Error::Error(Type _type): m_type(_type) solAssert(false, ""); break; } + + if (!_location.isEmpty()) + *this << errinfo_sourceLocation(_location); + if (!_description.empty()) + *this << errinfo_comment(_description); } Error::Error(Error::Type _type, const std::string& _description, const SourceLocation& _location): diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h index 71699da8..0803d8cc 100644 --- a/libsolidity/interface/Exceptions.h +++ b/libsolidity/interface/Exceptions.h @@ -53,7 +53,11 @@ public: Warning }; - explicit Error(Type _type); + explicit Error( + Type _type, + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string() + ); Error(Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation()); diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index be8774bc..f8b364d1 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -207,7 +207,7 @@ void RPCSession::personal_unlockAccount(string const& _address, string const& _p string RPCSession::personal_newAccount(string const& _password) { string addr = rpcCall("personal_newAccount", { quote(_password) }).asString(); - BOOST_MESSAGE("Created account " + addr); + BOOST_TEST_MESSAGE("Created account " + addr); return addr; } diff --git a/test/RPCSession.h b/test/RPCSession.h index 843036e1..b37cc322 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -30,6 +30,7 @@ #include <json/value.h> +#include <boost/noncopyable.hpp> #include <boost/test/unit_test.hpp> #include <string> diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index cb0cc168..130b0d3a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9119,24 +9119,24 @@ BOOST_AUTO_TEST_CASE(invalid_instruction) BOOST_CHECK(callContractFunction("f()") == encodeArgs()); } -BOOST_AUTO_TEST_CASE(assert) -{ - char const* sourceCode = R"( - contract C { - function f() { - assert(false); - } - function g(bool val) returns (bool) { - assert(val == true); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs()); - BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs()); - BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(true)); -} +//BOOST_AUTO_TEST_CASE(assert) +//{ +// char const* sourceCode = R"( +// contract C { +// function f() { +// assert(false); +// } +// function g(bool val) returns (bool) { +// assert(val == true); +// return true; +// } +// } +// )"; +// compileAndRun(sourceCode, 0, "C"); +// BOOST_CHECK(callContractFunction("f()") == encodeArgs()); +// BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs()); +// BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(true)); +//} BOOST_AUTO_TEST_CASE(revert) { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 3b137572..3d82fc70 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -20,19 +20,23 @@ * Unit tests for the name and type resolution of the solidity parser. */ -#include <string> +#include <test/libsolidity/ErrorCheck.h> + +#include <test/TestHelper.h> -#include <libdevcore/SHA3.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> #include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/StaticAnalyzer.h> +#include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/analysis/GlobalContext.h> #include <libsolidity/analysis/TypeChecker.h> -#include "../TestHelper.h" -#include "ErrorCheck.h" + +#include <libdevcore/SHA3.h> + +#include <string> using namespace std; @@ -93,10 +97,11 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, BOOST_CHECK(success || !errors.empty()); } if (success) - { - StaticAnalyzer staticAnalyzer(errors); - staticAnalyzer.analyze(*sourceUnit); - } + if (!PostTypeChecker(errors).check(*sourceUnit)) + success = false; + if (success) + if (!StaticAnalyzer(errors).analyze(*sourceUnit)) + success = false; if (errors.size() > 1 && !_allowMultipleErrors) BOOST_FAIL("Multiple errors found"); for (auto const& currentError: errors) @@ -2501,6 +2506,30 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) CHECK_ERROR(sourceCode, TypeError, ""); } +BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable) +{ + char const* sourceCode = R"( + contract C { + function f() { + mapping(uint => uint) x; + } + } + )"; + CHECK_ERROR(sourceCode, TypeError, "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable"); +} + +BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) +{ + char const* sourceCode = R"( + contract C { + function f() { + mapping(uint => uint)[] x; + } + } + )"; + CHECK_WARNING(sourceCode, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) { char const* sourceCode = R"( @@ -5156,6 +5185,34 @@ BOOST_AUTO_TEST_CASE(address_methods) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants) +{ + char const* text = R"( + contract C { + uint constant a = a; + } + )"; + CHECK_ERROR(text, TypeError, "cyclic dependency via a"); + text = R"( + contract C { + uint constant a = b * c; + uint constant b = 7; + uint constant c = b + uint(sha3(d)); + uint constant d = 2 + a; + } + )"; + CHECK_ERROR_ALLOW_MULTI(text, TypeError, "a has a cyclic dependency via c"); + text = R"( + contract C { + uint constant a = b * c; + uint constant b = 7; + uint constant c = 4 + uint(sha3(d)); + uint constant d = 2 + b; + } + )"; + CHECK_SUCCESS(text); +} + BOOST_AUTO_TEST_SUITE_END() } |