diff options
25 files changed, 383 insertions, 79 deletions
diff --git a/Changelog.md b/Changelog.md index d6860bdf..3b8cba1d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. + * Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * Inheritance: Error when using empty parenthesis for base class constructors that require arguments as experimental 0.5.0 feature. @@ -27,6 +28,7 @@ Bugfixes: * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. + * Static Analyzer: Fix non-deterministic order of unused variable warnings. ### 0.4.21 (2018-03-07) diff --git a/docs/contracts.rst b/docs/contracts.rst index f8a44fb3..0dd9845c 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1034,9 +1034,12 @@ the base constructors. This can be done in two ways:: constructor(uint _x) public { x = _x; } } - contract Derived is Base(7) { - constructor(uint _y) Base(_y * _y) public { - } + contract Derived1 is Base(7) { + constructor(uint _y) public {} + } + + contract Derived2 is Base { + constructor(uint _y) Base(_y * _y) public {} } One way is directly in the inheritance list (``is Base(7)``). The other is in @@ -1046,8 +1049,9 @@ do it is more convenient if the constructor argument is a constant and defines the behaviour of the contract or describes it. The second way has to be used if the constructor arguments of the base depend on those of the -derived contract. If, as in this silly example, both places -are used, the modifier-style argument takes precedence. +derived contract. Arguments have to be given either in the +inheritance list or in modifier-style in the derived constuctor. +Specifying arguments in both places is an error. .. index:: ! inheritance;multiple, ! linearization, ! C3 linearization diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 2571f20a..e7f41ed1 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -169,6 +169,13 @@ For more information, see the section on :ref:`address`. Use a pattern where the recipient withdraws the money. .. note:: + If storage variables are accessed via a low-level delegatecall, the storage layout of the two contracts + must align in order for the called contract to correctly access the storage variables of the calling contract by name. + This is of course not the case if storage pointers are passed as function arguments as in the case for + the high-level libraries. + + +.. note:: The use of ``callcode`` is discouraged and will be removed in the future. .. index:: this, selfdestruct diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.cpp b/libjulia/optimiser/CommonSubexpressionEliminator.cpp new file mode 100644 index 00000000..229bd35e --- /dev/null +++ b/libjulia/optimiser/CommonSubexpressionEliminator.cpp @@ -0,0 +1,48 @@ +/*( + 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/>. +*/ +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + */ + +#include <libjulia/optimiser/CommonSubexpressionEliminator.h> + +#include <libjulia/optimiser/Metrics.h> +#include <libjulia/optimiser/SyntacticalEquality.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::julia; + +void CommonSubexpressionEliminator::visit(Expression& _e) +{ + // Single exception for substitution: We do not substitute one variable for another. + if (_e.type() != typeid(Identifier)) + // TODO this search rather inefficient. + for (auto const& var: m_value) + { + solAssert(var.second, ""); + if (SyntacticalEqualityChecker::equal(_e, *var.second)) + { + _e = Identifier{locationOf(_e), var.first}; + break; + } + } + DataFlowAnalyzer::visit(_e); +} diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.h b/libjulia/optimiser/CommonSubexpressionEliminator.h new file mode 100644 index 00000000..a8ca3abb --- /dev/null +++ b/libjulia/optimiser/CommonSubexpressionEliminator.h @@ -0,0 +1,45 @@ +/* + 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/>. +*/ +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + */ + +#pragma once + +#include <libjulia/optimiser/DataFlowAnalyzer.h> + +namespace dev +{ +namespace julia +{ + +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + * + * Prerequisite: Disambiguator + */ +class CommonSubexpressionEliminator: public DataFlowAnalyzer +{ +protected: + using ASTModifier::visit; + virtual void visit(Expression& _e) override; +}; + +} +} diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d96f8748..33b0e296 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -78,13 +78,13 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) for (auto const& var: m_localVarUseCount) if (var.second == 0) { - if (var.first->isCallableParameter()) + if (var.first.second->isCallableParameter()) m_errorReporter.warning( - var.first->location(), + var.first.second->location(), "Unused function parameter. Remove or comment out the variable name to silence this warning." ); else - m_errorReporter.warning(var.first->location(), "Unused local variable."); + m_errorReporter.warning(var.first.second->location(), "Unused local variable."); } m_localVarUseCount.clear(); @@ -97,7 +97,7 @@ bool StaticAnalyzer::visit(Identifier const& _identifier) { solAssert(!var->name().empty(), ""); if (var->isLocalVariable()) - m_localVarUseCount[var] += 1; + m_localVarUseCount[make_pair(var->id(), var)] += 1; } return true; } @@ -109,7 +109,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) solAssert(_variable.isLocalVariable(), ""); if (_variable.name() != "") // This is not a no-op, the entry might pre-exist. - m_localVarUseCount[&_variable] += 0; + m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0; } else if (_variable.isStateVariable()) { @@ -132,7 +132,7 @@ bool StaticAnalyzer::visit(Return const& _return) if (m_currentFunction && _return.expression()) for (auto const& var: m_currentFunction->returnParameters()) if (!var->name().empty()) - m_localVarUseCount[var.get()] += 1; + m_localVarUseCount[make_pair(var->id(), var.get())] += 1; return true; } @@ -224,7 +224,7 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) { solAssert(!var->name().empty(), ""); if (var->isLocalVariable()) - m_localVarUseCount[var] += 1; + m_localVarUseCount[make_pair(var->id(), var)] += 1; } } diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 124c4e7c..0a806bbd 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -77,7 +77,9 @@ private: bool m_nonPayablePublic = false; /// Number of uses of each (named) local variable in a function, counter is initialized with zero. - std::map<VariableDeclaration const*, int> m_localVarUseCount; + /// Pairs of AST ids and pointers are used as keys to ensure a deterministic order + /// when traversing. + std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a252742d..1d8fd82d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -101,7 +101,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract) checkContractDuplicateEvents(_contract); checkContractIllegalOverrides(_contract); checkContractAbstractFunctions(_contract); - checkContractAbstractConstructors(_contract); + checkContractBaseConstructorArguments(_contract); FunctionDefinition const* function = _contract.constructor(); if (function) @@ -291,42 +291,90 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont } } -void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract) +void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) { - set<ContractDefinition const*> argumentsNeeded; - // check that we get arguments for all base constructors that need it. - // If not mark the contract as abstract (not fully implemented) - vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; - for (ContractDefinition const* contract: bases) - if (FunctionDefinition const* constructor = contract->constructor()) - if (contract != &_contract && !constructor->parameters().empty()) - argumentsNeeded.insert(contract); + // Determine the arguments that are used for the base constructors. for (ContractDefinition const* contract: bases) { if (FunctionDefinition const* constructor = contract->constructor()) for (auto const& modifier: constructor->modifiers()) { - auto baseContract = dynamic_cast<ContractDefinition const*>( - &dereference(*modifier->name()) - ); - if (baseContract) - argumentsNeeded.erase(baseContract); + auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name())); + if (baseContract && baseContract->constructor() && !modifier->arguments().empty()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); } - for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) { auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name())); solAssert(baseContract, ""); - if (base->arguments() && !base->arguments()->empty()) - argumentsNeeded.erase(baseContract); + + if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); } } - if (!argumentsNeeded.empty()) - for (ContractDefinition const* contract: argumentsNeeded) - _contract.annotation().unimplementedFunctions.push_back(contract->constructor()); + + // check that we get arguments for all base constructors that need it. + // If not mark the contract as abstract (not fully implemented) + for (ContractDefinition const* contract: bases) + if (FunctionDefinition const* constructor = contract->constructor()) + if (contract != &_contract && !constructor->parameters().empty()) + if (!_contract.annotation().baseConstructorArguments.count(constructor)) + _contract.annotation().unimplementedFunctions.push_back(constructor); +} + +void TypeChecker::annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode +) +{ + bool const v050 = _currentContract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + solAssert(_baseConstructor, ""); + solAssert(_argumentNode, ""); + + auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert( + std::make_pair(_baseConstructor, _argumentNode) + ); + if (!insertionResult.second) + { + ASTNode const* previousNode = insertionResult.first->second; + + SourceLocation const* mainLocation = nullptr; + SecondarySourceLocation ssl; + + if ( + _currentContract.location().contains(previousNode->location()) || + _currentContract.location().contains(_argumentNode->location()) + ) + { + mainLocation = &previousNode->location(); + ssl.append("Second constructor call is here:", _argumentNode->location()); + } + else + { + mainLocation = &_currentContract.location(); + ssl.append("First constructor call is here: ", _argumentNode->location()); + ssl.append("Second constructor call is here: ", previousNode->location()); + } + + if (v050) + m_errorReporter.declarationError( + *mainLocation, + ssl, + "Base constructor arguments given twice." + ); + else + m_errorReporter.warning( + *mainLocation, + "Base constructor arguments given twice.", + ssl + ); + } + } void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 2ba31232..2245abd6 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -73,7 +73,12 @@ private: void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); void checkContractAbstractFunctions(ContractDefinition const& _contract); - void checkContractAbstractConstructors(ContractDefinition const& _contract); + void checkContractBaseConstructorArguments(ContractDefinition const& _contract); + void annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode + ); /// Checks that different functions with external visibility end up having different /// external argument types (i.e. different signature). void checkContractExternalTypeClashes(ContractDefinition const& _contract); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 3d4236cc..5cbe42bd 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -90,6 +90,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota /// List of contracts this contract creates, i.e. which need to be compiled first. /// Also includes all contracts from @a linearizedBaseContracts. std::set<ContractDefinition const*> contractDependencies; + /// Mapping containing the nodes that define the arguments for base constructors. + /// These can either be inheritance specifiers or modifier invocations. + std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments; }; struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index aa46520f..05f506f1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -403,7 +403,7 @@ private: }; /** - * Integer and fixed point constants either literals or computed. + * Integer and fixed point constants either literals or computed. * Example expressions: 2, 3.14, 2+10.2, ~10. * There is one distinct type per value. */ @@ -415,7 +415,7 @@ public: /// @returns true if the literal is a valid integer. static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); - + explicit RationalNumberType(rational const& _value): m_value(_value) {} @@ -436,7 +436,7 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; - /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. + /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. /// If the integer part does not fit, returns an empty pointer. std::shared_ptr<FixedPointType const> fixedPointType() const; @@ -778,7 +778,7 @@ public: virtual std::string canonicalName() const override; virtual std::string signatureInExternalFunction(bool _structsByName) const override; - /// @returns a function that peforms the type conversion between a list of struct members + /// @returns a function that performs the type conversion between a list of struct members /// and a memory struct of this type. FunctionTypePointer constructorType() const; @@ -1039,7 +1039,7 @@ public: return *m_declaration; } bool hasDeclaration() const { return !!m_declaration; } - /// @returns true if the the result of this function only depends on its arguments + /// @returns true if the result of this function only depends on its arguments /// and it does not modify the state. /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; @@ -1056,7 +1056,7 @@ public: bool bound() const { return m_bound; } /// @returns a copy of this type, where gas or value are set manually. This will never set one - /// of the parameters to fals. + /// of the parameters to false. TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; /// @returns a copy of this function type where all return parameters of dynamic size are diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d3a7e4ea..3ca0b69d 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -135,34 +135,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c { solAssert(!_contract.isLibrary(), "Tried to initialize library."); CompilerContext::LocationSetter locationSetter(m_context, _contract); - // Determine the arguments that are used for the base constructors. - std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; - for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->constructor()) - for (auto const& modifier: constructor->modifiers()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>( - modifier->name()->annotation().referencedDeclaration - ); - if (baseContract && !modifier->arguments().empty()) - if (m_baseArguments.count(baseContract->constructor()) == 0) - m_baseArguments[baseContract->constructor()] = &modifier->arguments(); - } - for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) - { - ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( - base->name().annotation().referencedDeclaration - ); - solAssert(baseContract, ""); + m_baseArguments = &_contract.annotation().baseConstructorArguments; - if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty()) - m_baseArguments[baseContract->constructor()] = base->arguments(); - } - } // Initialization of state variables in base-to-derived order. - for (ContractDefinition const* contract: boost::adaptors::reverse(bases)) + for (ContractDefinition const* contract: boost::adaptors::reverse( + _contract.annotation().linearizedBaseContracts + )) initializeStateVariables(*contract); if (FunctionDefinition const* constructor = _contract.constructor()) @@ -236,8 +215,14 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc FunctionType constructorType(_constructor); if (!constructorType.parameterTypes().empty()) { - solAssert(m_baseArguments.count(&_constructor), ""); - std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor]; + solAssert(m_baseArguments, ""); + solAssert(m_baseArguments->count(&_constructor), ""); + std::vector<ASTPointer<Expression>> const* arguments = nullptr; + ASTNode const* baseArgumentNode = m_baseArguments->at(&_constructor); + if (auto inheritanceSpecifier = dynamic_cast<InheritanceSpecifier const*>(baseArgumentNode)) + arguments = inheritanceSpecifier->arguments(); + else if (auto modifierInvocation = dynamic_cast<ModifierInvocation const*>(baseArgumentNode)) + arguments = &modifierInvocation->arguments(); solAssert(arguments, ""); solAssert(arguments->size() == constructorType.parameterTypes().size(), ""); for (unsigned i = 0; i < arguments->size(); ++i) diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index e04a56fb..02a3452f 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -135,7 +135,7 @@ private: FunctionDefinition const* m_currentFunction = nullptr; unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag // arguments for base constructors, filled in derived-to-base order - std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments; + std::map<FunctionDefinition const*, ASTNode const*> const* m_baseArguments; }; } diff --git a/scripts/tests.sh b/scripts/tests.sh index 37ffafc2..2e40f8cb 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -61,13 +61,13 @@ function download_eth() mkdir -p /tmp/test if grep -i trusty /etc/lsb-release >/dev/null 2>&1 then - # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06 - ETH_BINARY=eth_byzantium_trusty - ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b" + # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05 + ETH_BINARY=eth_2018-04-05_trusty + ETH_HASH="1e5e178b005e5b51f9d347df4452875ba9b53cc6" else - # built from ?? at 2018-02-13 ? - ETH_BINARY=eth_byzantium_artful - ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05 + ETH_BINARY=eth_2018-04-05_artful + ETH_HASH="eb2d0df022753bb2b442ba73e565a9babf6828d6" fi wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" diff --git a/test/libjulia/CommonSubexpression.cpp b/test/libjulia/CommonSubexpression.cpp new file mode 100644 index 00000000..8a575c48 --- /dev/null +++ b/test/libjulia/CommonSubexpression.cpp @@ -0,0 +1,102 @@ +/* + 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/>. +*/ +/** + * Unit tests for the common subexpression eliminator optimizer stage. + */ + +#include <test/libjulia/Common.h> + +#include <libjulia/optimiser/CommonSubexpressionEliminator.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +#include <boost/range/adaptors.hpp> +#include <boost/algorithm/string/join.hpp> + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::julia::test; +using namespace dev::solidity; + + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p;\ + Block b = disambiguate(_original, false);\ + (CommonSubexpressionEliminator{})(b);\ + string result = p(b);\ + BOOST_CHECK_EQUAL(result, format(_expectation, false));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaCSE) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(trivial) +{ + CHECK( + "{ let a := mul(1, codesize()) let b := mul(1, codesize()) }", + "{ let a := mul(1, codesize()) let b := a }" + ); +} + +BOOST_AUTO_TEST_CASE(non_movable_instr) +{ + CHECK( + "{ let a := mload(1) let b := mload(1) }", + "{ let a := mload(1) let b := mload(1) }" + ); +} + +BOOST_AUTO_TEST_CASE(non_movable_instr2) +{ + CHECK( + "{ let a := gas() let b := gas() }", + "{ let a := gas() let b := gas() }" + ); +} + +BOOST_AUTO_TEST_CASE(branches_if) +{ + CHECK( + "{ let b := 1 if b { b := 1 } let c := 1 }", + "{ let b := 1 if b { b := b } let c := 1 }" + ); +} + +BOOST_AUTO_TEST_CASE(branches_for) +{ + CHECK( + "{ let a := 1 let b := codesize()" + "for { } lt(1, codesize()) { mstore(1, codesize()) a := add(a, codesize()) }" + "{ mstore(1, codesize()) } mstore(1, codesize()) }", + + "{ let a := 1 let b := codesize()" + "for { } lt(1, b) { mstore(1, b) a := add(a, b) }" + "{ mstore(1, b) } mstore(1, b) }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index beeae786..5ed53a2f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5191,7 +5191,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base) } uint public m_i; } - contract Derived is Base(2) { + contract Derived is Base { function Derived(uint i) Base(i) {} } @@ -5211,10 +5211,10 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base) } uint public m_i; } - contract Base1 is Base(3) { + contract Base1 is Base { function Base1(uint k) Base(k*k) {} } - contract Derived is Base(3), Base1(2) { + contract Derived is Base, Base1 { function Derived(uint i) Base(i) Base1(i) {} } @@ -5235,7 +5235,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap) uint public m_i; } contract Base1 is Base(3) {} - contract Derived is Base(2), Base1 { + contract Derived is Base, Base1 { function Derived(uint i) Base(i) {} } contract Final is Derived(4) { diff --git a/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol new file mode 100644 index 00000000..1f580b1d --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol @@ -0,0 +1,3 @@ +contract A { constructor() public { } } +contract B1 is A { constructor() A() public { } } +contract B2 is A { constructor() A public { } } diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol new file mode 100644 index 00000000..5483d5d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol @@ -0,0 +1,9 @@ +contract Base { + constructor(uint) public { } +} +contract Base1 is Base(3) {} +contract Derived is Base, Base1 { + constructor(uint i) Base(i) public {} +} +// ---- +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol new file mode 100644 index 00000000..8b1af245 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol @@ -0,0 +1,5 @@ +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() public { } } +contract C is B { constructor() A(3) public { } } +// ---- +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol new file mode 100644 index 00000000..6616c9a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; + +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() public { } } +contract C is B { constructor() A(3) public { } } +// ---- +// DeclarationError: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol new file mode 100644 index 00000000..1fb504fe --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol @@ -0,0 +1,4 @@ +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() A(3) public { } } +// ---- +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol new file mode 100644 index 00000000..96eb1bb1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol @@ -0,0 +1,6 @@ +pragma experimental "v0.5.0"; + +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() A(3) public { } } +// ---- +// DeclarationError: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol new file mode 100644 index 00000000..db9ffc85 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol @@ -0,0 +1,7 @@ +contract C { constructor(uint) public {} } +contract A is C(2) {} +contract B is C(2) {} +contract D is A, B { constructor() C(3) public {} } +// ---- +// Warning: Base constructor arguments given twice. +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol new file mode 100644 index 00000000..fe280ad5 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol @@ -0,0 +1,6 @@ +contract C { constructor(uint) public {} } +contract A is C(2) {} +contract B is C(2) {} +contract D is A, B {} +// ---- +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol new file mode 100644 index 00000000..ea85aae7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol @@ -0,0 +1,6 @@ +contract C { constructor(uint) public {} } +contract A is C { constructor() C(2) public {} } +contract B is C { constructor() C(2) public {} } +contract D is A, B { } +// ---- +// Warning: Base constructor arguments given twice. |