aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md3
-rw-r--r--appveyor.yml9
-rw-r--r--docs/abi-spec.rst14
-rw-r--r--docs/contracts.rst8
-rw-r--r--docs/control-structures.rst12
-rw-r--r--libevmasm/PathGasMeter.cpp19
-rw-r--r--libevmasm/PathGasMeter.h10
-rw-r--r--libjulia/optimiser/Disambiguator.cpp12
-rw-r--r--libjulia/optimiser/Disambiguator.h3
-rw-r--r--libjulia/optimiser/FullInliner.cpp262
-rw-r--r--libjulia/optimiser/FullInliner.h178
-rw-r--r--libjulia/optimiser/NameCollector.cpp1
-rw-r--r--libjulia/optimiser/NameCollector.h9
-rw-r--r--libjulia/optimiser/NameDispenser.cpp38
-rw-r--r--libjulia/optimiser/NameDispenser.h37
-rw-r--r--libsolidity/analysis/TypeChecker.cpp58
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp6
-rw-r--r--scripts/release.bat14
-rw-r--r--test/libjulia/Inliner.cpp166
-rw-r--r--test/libsolidity/GasMeter.cpp40
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp76
-rw-r--r--test/libsolidity/syntaxTests/emit_non_event.sol10
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol12
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol24
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol8
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol8
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol10
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol11
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol11
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol9
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol10
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol10
32 files changed, 959 insertions, 139 deletions
diff --git a/Changelog.md b/Changelog.md
index 737a11f1..a87c8dd4 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -3,12 +3,15 @@
Features:
* Build System: Update internal dependency of jsoncpp to 1.8.4, which introduces more strictness and reduces memory usage.
* Code Generator: Use native shift instructions on target Constantinople.
+ * Gas Estimator: Only explore paths with higher gas costs. This reduces accuracy but greatly improves the speed of gas estimation.
* Optimizer: Remove unnecessary masking of the result of known short instructions (``ADDRESS``, ``CALLER``, ``ORIGIN`` and ``COINBASE``).
* Parser: Display nicer error messages by showing the actual tokens and not internal names.
* Type Checker: Deprecate the ``years`` unit denomination and raise a warning for it (or an error as experimental 0.5.0 feature).
* Type Checker: Make literals (without explicit type casting) an error for tight packing as experimental 0.5.0 feature.
+ * Type Checker: Warn about wildcard tuple assignments (this will turn into an error with version 0.5.0).
Bugfixes:
+ * Type Checker: Show proper error when trying to ``emit`` a non-event.
* Type Checker: Warn about empty tuple components (this will turn into an error with version 0.5.0).
diff --git a/appveyor.yml b/appveyor.yml
index 5fd85482..81ec711d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -31,10 +31,12 @@ branches:
only:
- release
- develop
-os: Visual Studio 2015
configuration:
- RelWithDebInfo
environment:
+ matrix:
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
# This is used for pushing to solidity-test-bytecodes
priv_key:
secure: yYGwg4rhCdHfwuv2mFjaNEDwAx3IKUbp0D5fMGpaKefnfk+BiMS5bqSHRiOj91PZ91P9pUk2Vu+eNuS4hTFCf1zFGfrOhlJ4Ij0xSyU5m/LQr590Mo+f7W94Xc8ubgo6j2hp9qH/szTqTzmAkmxKO5TLlWjVzVny2t/s5o5UprLS1/MdzDNLjpVNXR03oKfdWUV9a2l6+PejXCbqyUCagh6BByZqeAPbDcil6eAfxu4EPX83Fuurof+KqFzIWycBG5qK1pTipn2pxiA0QKuUrD8y8VNL0S23NTgxoxSp7nPVMd3K0qRSzPM5lrqS7Z8i3evkVwPbuhu0gSiV08jGVahH2snQ3JGYsH2D4KmVn/xiVBeJ0lRplYlfZF0GUu7iJ+DDxi6wBPhW9A25/NyD/mx7Ub2dLheyWi8AjdSCzhfRD+4We8FQQeHRo3Q0kAohFmlCXdXhrcwOOloId8r6xYwg+hWxHTt2Oe9CKwXfmiPjgl/Gd6lYgLpyyfJ8drQ6tjO/pybLEa10v74qYNdVW5LaLIsRUM9Jm/FDVTrOGYtPndi87mF+/tBJIaXXNz0EMl5xvsKW0SBfUMV49zoDDKZZgWyO9U/cfViEUi7Sdn9QLsBWLZfSgBQNkq3WGZVKPq58OxEWT9dUghQHlSVh2qWF/NUx0TRBjiJl9JM56ENTMD00y18eDcXNCeLLVYB+R1axabUPdXivrO+BrWQK94IWxKEJ+YYN8WVJWAO5T/EBDKwgiXGneePwJ75WP7XCLtuYxqjC+CeW3xBVCzCEeZB/VVBvt7fhmtcoeZZ6tAS10h0yY5WWZ/EUVorj+c/FrMm7Nlpcrd1p4hciffePSLVg+yvy9/xTuM9trYWMgj4xcDQbYsaeItHO2Z3EiUoCgNdUw6rONiNwS/XBApWhCcklWm0/g62h2gOa7/hnKG6p2omQzYw+cOzWbF9+DBzoTSXXZXqbUshVee+CD+iYJKleGYSdbMdM89HW4HyskHk6HgM1ggE8CsgD1pMhXtqLTYZBlvsZCBkHPkD9NhGD2DtrNOmJOW8xwkL2/Il6roDF4n856XNdsjvd++rvQoKr58SkyApCJeCo3sfVres0W22g+7If2b2kWC4/DphrFkeaceFzJOctBUrwstvQBXIVOcadU978A3E7jvTaMR4JL9kC/iPOUVNjNRNM/gNvTlf3CIyMMszFeftjEBGnCZaSpht2RtNapRQQb6QPkOP88nufQVZq/TP1ECmvdTUWJ7kSnAupu6u8oH2x2IIm/KKeIwSYU5rGxjRb36DwgXCHcwfRYo3VNorwTeZGj4q1TSM9PuvgzNg//gKZW6VRa+HdNm/40ZGpDsOrr55tOBqfpq9k5RmevqW/OMZS3xUuArKdYLQY75t9eWcbHSgFN2ZY1KEdyEEvVKgs6Q4lEnSSulGxroRxTU5BOoA0V4tCeCUoSPD3FB93WsO9fBPzNsqOuBtDdIkApefzc1pT38uKpmVfggKUsoWUdqMXAWqCDWr2uw9EE900RJpEY6mIEWhkcro5LAMwaqByOGpqFFUkH+UWTC102eVHEmjxKpC6c6cSzoKKU6Ckd+jVRFO7TvmVe1MKCwjXj8lcAfAM2gQ+XehtrQdIBhAmCrnzurfz2u9tKVdpiADC1ig+kMs1/HX2713LYVXzDKdk+duQ94SVtGv9F2Iv+KN5oq4UFgll6VGt7GHsJOrYYf/wrOfB09IkpmjNygvcpmmSdcXXF8ulDD6KHTGEGUlFwLOpEwKx+zX2ZvviStHhN8KsoTKSVSueDmSSI63HdTS7FxfrHJc1yAzsdqEN5g5eV/z2Fn34qy64mdFSAZMF5zsbWZYFpc9ef3llF5aRcuD90JWT2VC7rB2jeGEtiwGkDlqKzxqRvJk06wTK6+n5RncN66bDaksulOPJMAR/bRW7dinV8T6yIvybuhqDetxJQP6eyAnW4xr1YxIAG4BXGZV6XAPTgOG2oGvMdncxkcLQHXVu07x39ySqP/m2MBxn0zF3DmaqrSPIRMhS8gG3d/23Jux3YHDEOBHjdJSdwqs5F5+QBFPV2rmJnpcSoW4d3M119XI20L914c62R7wY4e6+qmi3ydQU9g6p8psZgaE3TuMsyzX3k4C30nC/3gWT+zl253NjZwfbzIdHu5LWNDY9kEHtKzLP
@@ -62,11 +64,12 @@ install:
before_build:
- if not exist build mkdir build
- cd build
- - cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" ( cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On )
+ else ( cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On )
build_script:
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
- cd %APPVEYOR_BUILD_FOLDER%
- - scripts\release.bat %CONFIGURATION%
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" ( scripts\release.bat %CONFIGURATION% 2017 )
- ps: $bytecodedir = git show -s --format="%cd-%H" --date=short
test_script:
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index b0b16e28..8591a07f 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -62,7 +62,7 @@ The following elementary types exist:
The following (fixed-size) array type exists:
-- ``<type>[M]``: a fixed-length array of ``M`` elements, ``M > 0``, of the given type.
+- ``<type>[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type.
The following non-fixed-size types exist:
@@ -101,8 +101,8 @@ We distinguish static and dynamic types. Static types are encoded in-place and d
* ``bytes``
* ``string``
* ``T[]`` for any ``T``
-* ``T[k]`` for any dynamic ``T`` and any ``k > 0``
-* ``(T1,...,Tk)`` if any ``Ti`` is dynamic for ``1 <= i <= k``
+* ``T[k]`` for any dynamic ``T`` and any ``k >= 0``
+* ``(T1,...,Tk)`` if ``Ti`` is dynamic for some ``1 <= i <= k``
All other types are called "static".
@@ -119,14 +119,14 @@ on the type of ``X`` being
``enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))``
- where ``X(i)`` is the ``ith`` component of the value, and
+ where ``X = (X(1), ..., X(k))`` and
``head`` and ``tail`` are defined for ``Ti`` being a static type as
``head(X(i)) = enc(X(i))`` and ``tail(X(i)) = ""`` (the empty string)
and as
- ``head(X(i)) = enc(len( head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1)) ))``
+ ``head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1)) ))``
``tail(X(i)) = enc(X(i))``
otherwise, i.e. if ``Ti`` is a dynamic type.
@@ -137,14 +137,14 @@ on the type of ``X`` being
- ``T[k]`` for any ``T`` and ``k``:
- ``enc(X) = enc((X[1], ..., X[k]))``
+ ``enc(X) = enc((X[0], ..., X[k-1]))``
i.e. it is encoded as if it were a tuple with ``k`` elements
of the same type.
- ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``):
- ``enc(X) = enc(k) enc([X[1], ..., X[k]])``
+ ``enc(X) = enc(k) enc([X[0], ..., X[k-1]])``
i.e. it is encoded as if it were an array of static size ``k``, prefixed with
the number of elements.
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 5c298274..9d47b543 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -24,8 +24,8 @@ Creating contracts programatically on Ethereum is best done via using the JavaSc
As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
to facilitate contract creation.
-When a contract is created, its constructor (a function with the same
-name as the contract) is executed once.
+When a contract is created, its constructor (a function declared with the
+``constructor`` keyword) is executed once.
A constructor is optional. Only one constructor is allowed, and this means
overloading is not supported.
@@ -982,7 +982,7 @@ virtual method lookup.
Constructors
============
-A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
+A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the
default constructor: ``contructor() public {}``.
@@ -1139,7 +1139,7 @@ Example of a Function Type (a variable declaration, where the variable is of typ
function(address) external returns (address) foo;
-Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
+Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
facilitating patterns like the `Template method <https://en.wikipedia.org/wiki/Template_method_pattern>`_ and removing code duplication.
.. index:: ! contract;interface, ! interface contract
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index f3c351dd..f18e1e10 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -293,12 +293,7 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di
// Common trick to swap values -- does not work for non-value storage types.
(x, y) = (y, x);
// Components can be left out (also for variable declarations).
- // If the tuple ends in an empty component,
- // the rest of the values are discarded.
- (data.length,) = f(); // Sets the length to 7
- // The same can be done on the left side.
- // If the tuple begins in an empty component, the beginning values are discarded.
- (,data[3]) = f(); // Sets data[3] to 2
+ (data.length,,) = f(); // Sets the length to 7
// Components can only be left out at the left-hand-side of assignments, with
// one exception:
(x,) = (1,);
@@ -307,6 +302,11 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di
}
}
+.. note::
+ Prior to version 0.4.24 it was possible to assign to tuples of smaller size, either
+ filling up on the left or on the right side (which ever was empty). This is
+ now deprecated, both sides have to have the same number of components.
+
Complications for Arrays and Structs
------------------------------------
diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp
index 3fe682b7..cdadba76 100644
--- a/libevmasm/PathGasMeter.cpp
+++ b/libevmasm/PathGasMeter.cpp
@@ -43,7 +43,7 @@ GasMeter::GasConsumption PathGasMeter::estimateMax(
auto path = unique_ptr<GasPath>(new GasPath());
path->index = _startIndex;
path->state = _state->copy();
- m_queue.push_back(move(path));
+ queue(move(path));
GasMeter::GasConsumption gas;
while (!m_queue.empty() && !gas.isInfinite)
@@ -51,12 +51,23 @@ GasMeter::GasConsumption PathGasMeter::estimateMax(
return gas;
}
+void PathGasMeter::queue(std::unique_ptr<GasPath>&& _newPath)
+{
+ if (
+ m_highestGasUsagePerJumpdest.count(_newPath->index) &&
+ _newPath->gas < m_highestGasUsagePerJumpdest.at(_newPath->index)
+ )
+ return;
+ m_highestGasUsagePerJumpdest[_newPath->index] = _newPath->gas;
+ m_queue[_newPath->index] = move(_newPath);
+}
+
GasMeter::GasConsumption PathGasMeter::handleQueueItem()
{
assertThrow(!m_queue.empty(), OptimizerException, "");
- unique_ptr<GasPath> path = move(m_queue.back());
- m_queue.pop_back();
+ unique_ptr<GasPath> path = move(m_queue.rbegin()->second);
+ m_queue.erase(--m_queue.end());
shared_ptr<KnownState> state = path->state;
GasMeter meter(state, m_evmVersion, path->largestMemoryAccess);
@@ -117,7 +128,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
newPath->largestMemoryAccess = meter.largestMemoryAccess();
newPath->state = state->copy();
newPath->visitedJumpdests = path->visitedJumpdests;
- m_queue.push_back(move(newPath));
+ queue(move(newPath));
}
if (branchStops)
diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h
index 2527d7fb..9537b176 100644
--- a/libevmasm/PathGasMeter.h
+++ b/libevmasm/PathGasMeter.h
@@ -58,9 +58,17 @@ public:
GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
private:
+ /// Adds a new path item to the queue, but only if we do not already have
+ /// a higher gas usage at that point.
+ /// This is not exact as different state might influence higher gas costs at a later
+ /// point in time, but it greatly reduces computational overhead.
+ void queue(std::unique_ptr<GasPath>&& _newPath);
GasMeter::GasConsumption handleQueueItem();
- std::vector<std::unique_ptr<GasPath>> m_queue;
+ /// Map of jumpdest -> gas path, so not really a queue. We only have one queued up
+ /// item per jumpdest, because of the behaviour of `queue` above.
+ std::map<size_t, std::unique_ptr<GasPath>> m_queue;
+ std::map<size_t, GasMeter::GasConsumption> m_highestGasUsagePerJumpdest;
std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items;
solidity::EVMVersion m_evmVersion;
diff --git a/libjulia/optimiser/Disambiguator.cpp b/libjulia/optimiser/Disambiguator.cpp
index df2984e5..b988dba7 100644
--- a/libjulia/optimiser/Disambiguator.cpp
+++ b/libjulia/optimiser/Disambiguator.cpp
@@ -38,17 +38,7 @@ string Disambiguator::translateIdentifier(string const& _originalName)
Scope::Identifier const* id = m_scopes.back()->lookup(_originalName);
solAssert(id, "");
if (!m_translations.count(id))
- {
- string translated = _originalName;
- size_t suffix = 0;
- while (m_usedNames.count(translated))
- {
- suffix++;
- translated = _originalName + "_" + std::to_string(suffix);
- }
- m_usedNames.insert(translated);
- m_translations[id] = translated;
- }
+ m_translations[id] = m_nameDispenser.newName(_originalName);
return m_translations.at(id);
}
diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h
index 18ffd157..6fc8a615 100644
--- a/libjulia/optimiser/Disambiguator.h
+++ b/libjulia/optimiser/Disambiguator.h
@@ -23,6 +23,7 @@
#include <libjulia/ASTDataForward.h>
#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/optimiser/NameDispenser.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
@@ -60,7 +61,7 @@ protected:
std::vector<solidity::assembly::Scope*> m_scopes;
std::map<void const*, std::string> m_translations;
- std::set<std::string> m_usedNames;
+ NameDispenser m_nameDispenser;
};
}
diff --git a/libjulia/optimiser/FullInliner.cpp b/libjulia/optimiser/FullInliner.cpp
new file mode 100644
index 00000000..05d70729
--- /dev/null
+++ b/libjulia/optimiser/FullInliner.cpp
@@ -0,0 +1,262 @@
+/*
+ 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/>.
+*/
+/**
+ * Optimiser component that performs function inlining for arbitrary functions.
+ */
+
+#include <libjulia/optimiser/FullInliner.h>
+
+#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/optimiser/ASTWalker.h>
+#include <libjulia/optimiser/NameCollector.h>
+#include <libjulia/optimiser/Semantics.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <libdevcore/CommonData.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+using namespace dev::solidity;
+
+
+
+FullInliner::FullInliner(Block& _ast):
+ m_ast(_ast)
+{
+ solAssert(m_ast.statements.size() >= 1, "");
+ solAssert(m_ast.statements.front().type() == typeid(Block), "");
+ m_nameDispenser.m_usedNames = NameCollector(m_ast).names();
+
+ for (size_t i = 1; i < m_ast.statements.size(); ++i)
+ {
+ solAssert(m_ast.statements.at(i).type() == typeid(FunctionDefinition), "");
+ FunctionDefinition& fun = boost::get<FunctionDefinition>(m_ast.statements.at(i));
+ m_functions[fun.name] = &fun;
+ m_functionsToVisit.insert(&fun);
+ }
+}
+
+void FullInliner::run()
+{
+ solAssert(m_ast.statements[0].type() == typeid(Block), "");
+ InlineModifier(*this, m_nameDispenser, "").visit(m_ast.statements[0]);
+ while (!m_functionsToVisit.empty())
+ handleFunction(**m_functionsToVisit.begin());
+}
+
+void FullInliner::handleFunction(FunctionDefinition& _fun)
+{
+ if (!m_functionsToVisit.count(&_fun))
+ return;
+ m_functionsToVisit.erase(&_fun);
+ (InlineModifier(*this, m_nameDispenser, _fun.name))(_fun.body);
+}
+
+void InlineModifier::operator()(FunctionalInstruction& _instruction)
+{
+ visitArguments(_instruction.arguments);
+}
+
+void InlineModifier::operator()(FunctionCall&)
+{
+ solAssert(false, "Should be handled in visit() instead.");
+}
+
+void InlineModifier::operator()(ForLoop& _loop)
+{
+ (*this)(_loop.pre);
+ // Do not visit the condition because we cannot inline there.
+ (*this)(_loop.post);
+ (*this)(_loop.body);
+}
+
+void InlineModifier::operator()(Block& _block)
+{
+ // This is only used if needed to minimize the number of move operations.
+ vector<Statement> modifiedStatements;
+ for (size_t i = 0; i < _block.statements.size(); ++i)
+ {
+ visit(_block.statements.at(i));
+ if (!m_statementsToPrefix.empty())
+ {
+ if (modifiedStatements.empty())
+ std::move(
+ _block.statements.begin(),
+ _block.statements.begin() + i,
+ back_inserter(modifiedStatements)
+ );
+ modifiedStatements += std::move(m_statementsToPrefix);
+ m_statementsToPrefix.clear();
+ }
+ if (!modifiedStatements.empty())
+ modifiedStatements.emplace_back(std::move(_block.statements[i]));
+ }
+ if (!modifiedStatements.empty())
+ _block.statements = std::move(modifiedStatements);
+}
+
+void InlineModifier::visit(Expression& _expression)
+{
+ if (_expression.type() != typeid(FunctionCall))
+ return ASTModifier::visit(_expression);
+
+ FunctionCall& funCall = boost::get<FunctionCall>(_expression);
+ FunctionDefinition& fun = m_driver.function(funCall.functionName.name);
+
+ m_driver.handleFunction(fun);
+
+ // TODO: Insert good heuristic here. Perhaps implement that inside the driver.
+ bool doInline = funCall.functionName.name != m_currentFunction;
+
+ if (fun.returnVariables.size() > 1)
+ doInline = false;
+
+ {
+ vector<string> argNames;
+ vector<string> argTypes;
+ for (auto const& arg: fun.parameters)
+ {
+ argNames.push_back(fun.name + "_" + arg.name);
+ argTypes.push_back(arg.type);
+ }
+ visitArguments(funCall.arguments, argNames, argTypes, doInline);
+ }
+
+ if (!doInline)
+ return;
+
+ map<string, string> variableReplacements;
+ for (size_t i = 0; i < funCall.arguments.size(); ++i)
+ variableReplacements[fun.parameters[i].name] = boost::get<Identifier>(funCall.arguments[i]).name;
+ if (fun.returnVariables.empty())
+ _expression = noop(funCall.location);
+ else
+ {
+ string returnVariable = fun.returnVariables[0].name;
+ variableReplacements[returnVariable] = newName(fun.name + "_" + returnVariable);
+
+ m_statementsToPrefix.emplace_back(VariableDeclaration{
+ funCall.location,
+ {{funCall.location, variableReplacements[returnVariable], fun.returnVariables[0].type}},
+ {}
+ });
+ _expression = Identifier{funCall.location, variableReplacements[returnVariable]};
+ }
+ m_statementsToPrefix.emplace_back(BodyCopier(m_nameDispenser, fun.name + "_", variableReplacements)(fun.body));
+}
+
+void InlineModifier::visit(Statement& _statement)
+{
+ ASTModifier::visit(_statement);
+ // Replace pop(0) expression statemets (and others) by empty blocks.
+ if (_statement.type() == typeid(ExpressionStatement))
+ {
+ ExpressionStatement& expSt = boost::get<ExpressionStatement&>(_statement);
+ if (expSt.expression.type() == typeid(FunctionalInstruction))
+ {
+ FunctionalInstruction& funInstr = boost::get<FunctionalInstruction&>(expSt.expression);
+ if (funInstr.instruction == solidity::Instruction::POP)
+ if (MovableChecker(funInstr.arguments.at(0)).movable())
+ _statement = Block{expSt.location, {}};
+ }
+ }
+}
+
+void InlineModifier::visitArguments(
+ vector<Expression>& _arguments,
+ vector<string> const& _nameHints,
+ vector<string> const& _types,
+ bool _moveToFront
+)
+{
+ // If one of the elements moves parts to the front, all other elements right of it
+ // also have to be moved to the front to keep the order of evaluation.
+ vector<Statement> prefix;
+ for (size_t i = 0; i < _arguments.size(); ++i)
+ {
+ auto& arg = _arguments[i];
+ // TODO optimize vector operations, check that it actually moves
+ auto internalPrefix = visitRecursively(arg);
+ if (!internalPrefix.empty())
+ {
+ _moveToFront = true;
+ // We go through the arguments left to right, so we have to invert
+ // the prefixes.
+ prefix = std::move(internalPrefix) + std::move(prefix);
+ }
+ else if (_moveToFront)
+ {
+ auto location = locationOf(arg);
+ string var = newName(i < _nameHints.size() ? _nameHints[i] : "");
+ prefix.emplace(prefix.begin(), VariableDeclaration{
+ location,
+ {{TypedName{location, var, i < _types.size() ? _types[i] : ""}}},
+ make_shared<Expression>(std::move(arg))
+ });
+ arg = Identifier{location, var};
+ }
+ }
+ m_statementsToPrefix += std::move(prefix);
+}
+
+vector<Statement> InlineModifier::visitRecursively(Expression& _expression)
+{
+ vector<Statement> saved;
+ saved.swap(m_statementsToPrefix);
+ visit(_expression);
+ saved.swap(m_statementsToPrefix);
+ return saved;
+}
+
+string InlineModifier::newName(string const& _prefix)
+{
+ return m_nameDispenser.newName(_prefix);
+}
+
+Expression InlineModifier::noop(SourceLocation const& _location)
+{
+ return FunctionalInstruction{_location, solidity::Instruction::POP, {
+ Literal{_location, assembly::LiteralKind::Number, "0", ""}
+ }};
+}
+
+Statement BodyCopier::operator()(VariableDeclaration const& _varDecl)
+{
+ for (auto const& var: _varDecl.variables)
+ m_variableReplacements[var.name] = m_nameDispenser.newName(m_varNamePrefix + var.name);
+ return ASTCopier::operator()(_varDecl);
+}
+
+Statement BodyCopier::operator()(FunctionDefinition const& _funDef)
+{
+ solAssert(false, "Function hoisting has to be done before function inlining.");
+ return _funDef;
+}
+
+string BodyCopier::translateIdentifier(string const& _name)
+{
+ if (m_variableReplacements.count(_name))
+ return m_variableReplacements.at(_name);
+ else
+ return _name;
+}
diff --git a/libjulia/optimiser/FullInliner.h b/libjulia/optimiser/FullInliner.h
new file mode 100644
index 00000000..d3628e1a
--- /dev/null
+++ b/libjulia/optimiser/FullInliner.h
@@ -0,0 +1,178 @@
+/*
+ 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/>.
+*/
+/**
+ * Optimiser component that performs function inlining for arbitrary functions.
+ */
+#pragma once
+
+#include <libjulia/ASTDataForward.h>
+
+#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/optimiser/ASTWalker.h>
+#include <libjulia/optimiser/NameDispenser.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/variant.hpp>
+#include <boost/optional.hpp>
+
+#include <set>
+
+namespace dev
+{
+namespace julia
+{
+
+class NameCollector;
+
+
+/**
+ * Optimiser component that modifies an AST in place, inlining arbitrary functions.
+ *
+ * Code of the form
+ *
+ * function f(a, b) -> c { ... }
+ * h(g(x(...), f(arg1(...), arg2(...)), y(...)), z(...))
+ *
+ * is transformed into
+ *
+ * function f(a, b) -> c { ... }
+ *
+ * let z1 := z(...) let y1 := y(...) let a2 := arg2(...) let a1 := arg1(...)
+ * let c1 := 0
+ * { code of f, with replacements: a -> a1, b -> a2, c -> c1, d -> d1 }
+ * h(g(x(...), c1, y1), z1)
+ *
+ * No temporary variable is created for expressions that are "movable"
+ * (i.e. they are "pure", have no side-effects and also do not depend on other code
+ * that might have side-effects).
+ *
+ * This component can only be used on sources with unique names and with hoisted functions,
+ * i.e. the root node has to be a block that itself contains a single block followed by all
+ * function definitions.
+ */
+class FullInliner: public ASTModifier
+{
+public:
+ explicit FullInliner(Block& _ast);
+
+ void run();
+
+ /// Perform inlining operations inside the given function.
+ void handleFunction(FunctionDefinition& _function);
+
+ FunctionDefinition& function(std::string _name) { return *m_functions.at(_name); }
+
+private:
+ /// The AST to be modified. The root block itself will not be modified, because
+ /// we store pointers to functions.
+ Block& m_ast;
+ std::map<std::string, FunctionDefinition*> m_functions;
+ std::set<FunctionDefinition*> m_functionsToVisit;
+ NameDispenser m_nameDispenser;
+};
+
+/**
+ * Class that walks the AST of a block that does not contain function definitions and perform
+ * the actual code modifications.
+ */
+class InlineModifier: public ASTModifier
+{
+public:
+ InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, std::string _functionName):
+ m_currentFunction(std::move(_functionName)),
+ m_driver(_driver),
+ m_nameDispenser(_nameDispenser)
+ { }
+ ~InlineModifier()
+ {
+ solAssert(m_statementsToPrefix.empty(), "");
+ }
+
+ virtual void operator()(FunctionalInstruction&) override;
+ virtual void operator()(FunctionCall&) override;
+ virtual void operator()(ForLoop&) override;
+ virtual void operator()(Block& _block) override;
+
+ using ASTModifier::visit;
+ virtual void visit(Expression& _expression) override;
+ virtual void visit(Statement& _st) override;
+
+private:
+
+ /// Visits a list of expressions (usually an argument list to a function call) and tries
+ /// to inline them. If one of them is inlined, all right of it have to be moved to the front
+ /// (to keep the order of evaluation). If @a _moveToFront is true, all elements are moved
+ /// to the front. @a _nameHints and @_types are used for the newly created variables, but
+ /// both can be empty.
+ void visitArguments(
+ std::vector<Expression>& _arguments,
+ std::vector<std::string> const& _nameHints = {},
+ std::vector<std::string> const& _types = {},
+ bool _moveToFront = false
+ );
+
+ /// Visits an expression, but saves and restores the current statements to prefix and returns
+ /// the statements that should be prefixed for @a _expression.
+ std::vector<Statement> visitRecursively(Expression& _expression);
+
+ std::string newName(std::string const& _prefix);
+
+ /// @returns an expression returning nothing.
+ Expression noop(SourceLocation const& _location);
+
+ /// List of statements that should go in front of the currently visited AST element,
+ /// at the statement level.
+ std::vector<Statement> m_statementsToPrefix;
+ std::string m_currentFunction;
+ FullInliner& m_driver;
+ NameDispenser& m_nameDispenser;
+};
+
+/**
+ * Creates a copy of a block that is supposed to be the body of a function.
+ * Applies replacements to referenced variables and creates new names for
+ * variable declarations.
+ */
+class BodyCopier: public ASTCopier
+{
+public:
+ BodyCopier(
+ NameDispenser& _nameDispenser,
+ std::string const& _varNamePrefix,
+ std::map<std::string, std::string> const& _variableReplacements
+ ):
+ m_nameDispenser(_nameDispenser),
+ m_varNamePrefix(_varNamePrefix),
+ m_variableReplacements(_variableReplacements)
+ {}
+
+ using ASTCopier::operator ();
+
+ virtual Statement operator()(VariableDeclaration const& _varDecl) override;
+ virtual Statement operator()(FunctionDefinition const& _funDef) override;
+
+ virtual std::string translateIdentifier(std::string const& _name) override;
+
+ NameDispenser& m_nameDispenser;
+ std::string const& m_varNamePrefix;
+ std::map<std::string, std::string> m_variableReplacements;
+};
+
+
+}
+}
diff --git a/libjulia/optimiser/NameCollector.cpp b/libjulia/optimiser/NameCollector.cpp
index 510ee289..c0d0b707 100644
--- a/libjulia/optimiser/NameCollector.cpp
+++ b/libjulia/optimiser/NameCollector.cpp
@@ -35,7 +35,6 @@ void NameCollector::operator()(VariableDeclaration const& _varDecl)
void NameCollector::operator ()(FunctionDefinition const& _funDef)
{
m_names.insert(_funDef.name);
- m_functions[_funDef.name] = &_funDef;
for (auto const arg: _funDef.parameters)
m_names.insert(arg.name);
for (auto const ret: _funDef.returnVariables)
diff --git a/libjulia/optimiser/NameCollector.h b/libjulia/optimiser/NameCollector.h
index 2d4a1d4b..29856172 100644
--- a/libjulia/optimiser/NameCollector.h
+++ b/libjulia/optimiser/NameCollector.h
@@ -37,15 +37,18 @@ namespace julia
class NameCollector: public ASTWalker
{
public:
+ explicit NameCollector(Block const& _block)
+ {
+ (*this)(_block);
+ }
+
using ASTWalker::operator ();
virtual void operator()(VariableDeclaration const& _varDecl) override;
virtual void operator()(FunctionDefinition const& _funDef) override;
- std::set<std::string> const& names() const { return m_names; }
- std::map<std::string, FunctionDefinition const*> const& functions() const { return m_functions; }
+ std::set<std::string> names() const { return m_names; }
private:
std::set<std::string> m_names;
- std::map<std::string, FunctionDefinition const*> m_functions;
};
/**
diff --git a/libjulia/optimiser/NameDispenser.cpp b/libjulia/optimiser/NameDispenser.cpp
new file mode 100644
index 00000000..e4f0e4f6
--- /dev/null
+++ b/libjulia/optimiser/NameDispenser.cpp
@@ -0,0 +1,38 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Optimiser component that can create new unique names.
+ */
+
+#include <libjulia/optimiser/NameDispenser.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+
+string NameDispenser::newName(string const& _prefix)
+{
+ string name = _prefix;
+ size_t suffix = 0;
+ while (name.empty() || m_usedNames.count(name))
+ {
+ suffix++;
+ name = _prefix + "_" + std::to_string(suffix);
+ }
+ m_usedNames.insert(name);
+ return name;
+}
diff --git a/libjulia/optimiser/NameDispenser.h b/libjulia/optimiser/NameDispenser.h
new file mode 100644
index 00000000..91c43d54
--- /dev/null
+++ b/libjulia/optimiser/NameDispenser.h
@@ -0,0 +1,37 @@
+/*
+ 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/>.
+*/
+/**
+ * Optimiser component that can create new unique names.
+ */
+#pragma once
+
+#include <set>
+#include <string>
+
+namespace dev
+{
+namespace julia
+{
+
+struct NameDispenser
+{
+ std::string newName(std::string const& _prefix);
+ std::set<std::string> m_usedNames;
+};
+
+}
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 82421749..a222bdf0 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1067,6 +1067,7 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
{
if (
_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
+ type(_emit.eventCall().expression())->category() != Type::Category::Function ||
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
)
m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation.");
@@ -1075,6 +1076,7 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
if (!_statement.initialValue())
{
// No initial value is only permitted for single variables with specified type.
@@ -1091,7 +1093,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
solAssert(m_scope, "");
- if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
+ if (v050)
m_errorReporter.declarationError(varDecl.location(), errorText);
else
m_errorReporter.warning(varDecl.location(), errorText);
@@ -1131,12 +1133,33 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
") in value for variable assignment (0) needed"
);
}
- else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back())
- m_errorReporter.fatalTypeError(
- _statement.location(),
- "Wildcard both at beginning and end of variable declaration list is only allowed "
- "if the number of components is equal."
- );
+ else if (valueTypes.size() != variables.size())
+ {
+ if (v050)
+ m_errorReporter.fatalTypeError(
+ _statement.location(),
+ "Different number of components on the left hand side (" +
+ toString(variables.size()) +
+ ") than on the right hand side (" +
+ toString(valueTypes.size()) +
+ ")."
+ );
+ else if (!variables.front() && !variables.back())
+ m_errorReporter.fatalTypeError(
+ _statement.location(),
+ "Wildcard both at beginning and end of variable declaration list is only allowed "
+ "if the number of components is equal."
+ );
+ else
+ m_errorReporter.warning(
+ _statement.location(),
+ "Different number of components on the left hand side (" +
+ toString(variables.size()) +
+ ") than on the right hand side (" +
+ toString(valueTypes.size()) +
+ ")."
+ );
+ }
size_t minNumValues = variables.size();
if (!variables.empty() && (!variables.back() || !variables.front()))
--minNumValues;
@@ -1334,6 +1357,7 @@ bool TypeChecker::visit(Conditional const& _conditional)
bool TypeChecker::visit(Assignment const& _assignment)
{
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
requireLValue(_assignment.leftHandSide());
TypePointer t = type(_assignment.leftHandSide());
_assignment.annotation().type = t;
@@ -1346,11 +1370,29 @@ bool TypeChecker::visit(Assignment const& _assignment)
);
// Sequenced assignments of tuples is not valid, make the result a "void" type.
_assignment.annotation().type = make_shared<TupleType>();
+
expectType(_assignment.rightHandSide(), *tupleType);
// expectType does not cause fatal errors, so we have to check again here.
- if (dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
+ if (TupleType const* rhsType = dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
+ {
checkDoubleStorageAssignment(_assignment);
+ // @todo For 0.5.0, this code shoud move to TupleType::isImplicitlyConvertibleTo,
+ // but we cannot do it right now.
+ if (rhsType->components().size() != tupleType->components().size())
+ {
+ string message =
+ "Different number of components on the left hand side (" +
+ toString(tupleType->components().size()) +
+ ") than on the right hand side (" +
+ toString(rhsType->components().size()) +
+ ").";
+ if (v050)
+ m_errorReporter.typeError(_assignment.location(), message);
+ else
+ m_errorReporter.warning(_assignment.location(), message);
+ }
+ }
}
else if (t->category() == Type::Category::Mapping)
{
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index fc1ff0eb..a39e799c 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -110,7 +110,7 @@ void CompilerUtils::loadFromMemoryDynamic(
bool _padToWordBoundaries,
bool _keepUpdatedMemoryOffset
)
-{
+{
if (_keepUpdatedMemoryOffset)
m_context << Instruction::DUP1;
@@ -396,7 +396,7 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++;
- solAssert((argSize + dynPointers) < 16, "Stack too deep, try using less variables.");
+ solAssert((argSize + dynPointers) < 16, "Stack too deep, try using fewer variables.");
}
else
{
@@ -741,7 +741,7 @@ void CompilerUtils::convertType(
else if (targetTypeCategory == Type::Category::FixedPoint)
{
solAssert(
- stackTypeCategory == Type::Category::Integer ||
+ stackTypeCategory == Type::Category::Integer ||
stackTypeCategory == Type::Category::RationalNumber ||
stackTypeCategory == Type::Category::FixedPoint,
"Invalid conversion to FixedMxNType requested."
diff --git a/scripts/release.bat b/scripts/release.bat
index 54d85862..be95b35e 100644
--- a/scripts/release.bat
+++ b/scripts/release.bat
@@ -27,7 +27,19 @@ REM Copyright (c) 2016 solidity contributors.
REM ---------------------------------------------------------------------------
set CONFIGURATION=%1
+set VERSION=%2
+
+IF "%VERSION%"=="2015" (
+ set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll"
+) ELSE (
+
+ IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\" (
+ set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll"
+ ) ELSE (
+ set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll"
+ )
+)
7z a solidity-windows.zip ^
.\build\solc\%CONFIGURATION%\solc.exe .\build\test\%CONFIGURATION%\soltest.exe ^
- "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll"
+ "%DLLS%"
diff --git a/test/libjulia/Inliner.cpp b/test/libjulia/Inliner.cpp
index 88b51f28..464dcd93 100644
--- a/test/libjulia/Inliner.cpp
+++ b/test/libjulia/Inliner.cpp
@@ -1,18 +1,18 @@
/*
- This file is part of solidity.
+ 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 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.
+ 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/>.
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @date 2017
@@ -23,6 +23,9 @@
#include <libjulia/optimiser/ExpressionInliner.h>
#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h>
+#include <libjulia/optimiser/FullInliner.h>
+#include <libjulia/optimiser/FunctionHoister.h>
+#include <libjulia/optimiser/FunctionGrouper.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
@@ -58,8 +61,17 @@ string inlineFunctions(string const& _source, bool _julia = true)
ExpressionInliner(ast).run();
return assembly::AsmPrinter(_julia)(ast);
}
+string fullInline(string const& _source, bool _julia = true)
+{
+ Block ast = disambiguate(_source, _julia);
+ (FunctionHoister{})(ast);
+ (FunctionGrouper{})(ast);\
+ FullInliner(ast).run();
+ return assembly::AsmPrinter(_julia)(ast);
+}
}
+
BOOST_AUTO_TEST_SUITE(IuliaInlinableFunctionFilter)
BOOST_AUTO_TEST_CASE(smoke_test)
@@ -197,3 +209,135 @@ BOOST_AUTO_TEST_CASE(double_recursive_calls)
}
BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(IuliaFullInliner)
+
+BOOST_AUTO_TEST_CASE(simple)
+{
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a) -> x { let r := mul(a, a) x := add(r, r) }"
+ "let y := add(f(sload(mload(2))), mload(7))"
+ "}", false),
+ format("{"
+ "{"
+ "let _1 := mload(7)"
+ "let f_a := sload(mload(2))"
+ "let f_x"
+ "{"
+ "let f_r := mul(f_a, f_a)"
+ "f_x := add(f_r, f_r)"
+ "}"
+ "let y := add(f_x, _1)"
+ "}"
+ "function f(a) -> x"
+ "{"
+ "let r := mul(a, a)"
+ "x := add(r, r)"
+ "}"
+ "}", false)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(multi_fun)
+{
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a) -> x { x := add(a, a) }"
+ "function g(b, c) -> y { y := mul(mload(c), f(b)) }"
+ "let y := g(f(3), 7)"
+ "}", false),
+ format("{"
+ "{"
+ "let g_c := 7 "
+ "let f_a_1 := 3 "
+ "let f_x_1 "
+ "{ f_x_1 := add(f_a_1, f_a_1) } "
+ "let g_y "
+ "{"
+ "let g_f_a := f_x_1 "
+ "let g_f_x "
+ "{"
+ "g_f_x := add(g_f_a, g_f_a)"
+ "}"
+ "g_y := mul(mload(g_c), g_f_x)"
+ "}"
+ "let y_1 := g_y"
+ "}"
+ "function f(a) -> x"
+ "{"
+ "x := add(a, a)"
+ "}"
+ "function g(b, c) -> y"
+ "{"
+ "let f_a := b "
+ "let f_x "
+ "{"
+ "f_x := add(f_a, f_a)"
+ "}"
+ "y := mul(mload(c), f_x)"
+ "}"
+ "}", false)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(move_up_rightwards_arguments)
+{
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a, b, c) -> x { x := add(a, b) x := mul(x, c) }"
+ "let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5)))"
+ "}", false),
+ format("{"
+ "{"
+ "let _1 := mload(5)"
+ "let f_c := mload(4)"
+ "let f_b := mload(3)"
+ "let f_a := mload(2)"
+ "let f_x"
+ "{"
+ "f_x := add(f_a, f_b)"
+ "f_x := mul(f_x, f_c)"
+ "}"
+ "let y := add(mload(1), add(f_x, _1))"
+ "}"
+ "function f(a, b, c) -> x"
+ "{"
+ "x := add(a, b)"
+ "x := mul(x, c)"
+ "}"
+ "}", false)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(pop_result)
+{
+ // This tests that `pop(r)` is removed.
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a) -> x { let r := mul(a, a) x := add(r, r) }"
+ "pop(add(f(7), 2))"
+ "}", false),
+ format("{"
+ "{"
+ "let _1 := 2 "
+ "let f_a := 7 "
+ "let f_x "
+ "{"
+ "let f_r := mul(f_a, f_a) "
+ "f_x := add(f_r, f_r)"
+ "}"
+ "{"
+ "}"
+ "}"
+ "function f(a) -> x"
+ "{"
+ "let r := mul(a, a) "
+ "x := add(r, r)"
+ "}"
+ "}", false)
+ );
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp
index 0d66456c..f16d9abe 100644
--- a/test/libsolidity/GasMeter.cpp
+++ b/test/libsolidity/GasMeter.cpp
@@ -307,6 +307,46 @@ BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback)
testCreationTimeGas(sourceCode);
testRunTimeGas("x()", vector<bytes>{encodeArgs()});
}
+
+BOOST_AUTO_TEST_CASE(complex_control_flow)
+{
+ // This crashed the gas estimator previously (or took a very long time).
+ // Now we do not follow branches if they start out with lower gas costs than the ones
+ // we previously considered. This of course reduces accuracy.
+ char const* sourceCode = R"(
+ contract log {
+ function ln(int128 x) constant returns (int128 result) {
+ int128 t = x / 256;
+ int128 y = 5545177;
+ x = t;
+ t = x * 16; if (t <= 1000000) { x = t; y = y - 2772588; }
+ t = x * 4; if (t <= 1000000) { x = t; y = y - 1386294; }
+ t = x * 2; if (t <= 1000000) { x = t; y = y - 693147; }
+ t = x + x / 2; if (t <= 1000000) { x = t; y = y - 405465; }
+ t = x + x / 4; if (t <= 1000000) { x = t; y = y - 223144; }
+ t = x + x / 8; if (t <= 1000000) { x = t; y = y - 117783; }
+ t = x + x / 16; if (t <= 1000000) { x = t; y = y - 60624; }
+ t = x + x / 32; if (t <= 1000000) { x = t; y = y - 30771; }
+ t = x + x / 64; if (t <= 1000000) { x = t; y = y - 15504; }
+ t = x + x / 128; if (t <= 1000000) { x = t; y = y - 7782; }
+ t = x + x / 256; if (t <= 1000000) { x = t; y = y - 3898; }
+ t = x + x / 512; if (t <= 1000000) { x = t; y = y - 1951; }
+ t = x + x / 1024; if (t <= 1000000) { x = t; y = y - 976; }
+ t = x + x / 2048; if (t <= 1000000) { x = t; y = y - 488; }
+ t = x + x / 4096; if (t <= 1000000) { x = t; y = y - 244; }
+ t = x + x / 8192; if (t <= 1000000) { x = t; y = y - 122; }
+ t = x + x / 16384; if (t <= 1000000) { x = t; y = y - 61; }
+ t = x + x / 32768; if (t <= 1000000) { x = t; y = y - 31; }
+ t = x + x / 65536; if (t <= 1000000) { y = y - 15; }
+ return y;
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+ // max gas is used for small x
+ testRunTimeGas("ln(int128)", vector<bytes>{encodeArgs(0), encodeArgs(10), encodeArgs(105), encodeArgs(30000)});
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index a2540302..91fd1fff 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -2894,7 +2894,7 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
contract C {
function f(uint) public returns (string);
function g() public {
- var (x,) = this.f(2);
+ var x = this.f(2);
// we can assign to x but it is not usable.
bytes(x).length;
}
@@ -5833,80 +5833,6 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- (x, y) = (y, x);
- }
- }
- )";
- CHECK_WARNING(text, "This assignment performs two copies to storage.");
-}
-
-BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_right)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- (x, y, ) = (y, x, 1, 2);
- }
- }
- )";
- CHECK_WARNING(text, "This assignment performs two copies to storage.");
-}
-
-BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_left)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- (,x, y) = (1, 2, y, x);
- }
- }
- )";
- CHECK_WARNING(text, "This assignment performs two copies to storage.");
-}
-
-BOOST_AUTO_TEST_CASE(nowarn_swap_memory)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- function f() pure public {
- S memory x;
- S memory y;
- (x, y) = (y, x);
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(nowarn_swap_storage_pointers)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- S storage x_local = x;
- S storage y_local = y;
- S storage z_local = x;
- (x, y_local, x_local, z_local) = (y, x_local, y_local, y);
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
BOOST_AUTO_TEST_CASE(warn_unused_local)
{
char const* text = R"(
diff --git a/test/libsolidity/syntaxTests/emit_non_event.sol b/test/libsolidity/syntaxTests/emit_non_event.sol
new file mode 100644
index 00000000..1df6990d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/emit_non_event.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint256 Test;
+
+ function f() {
+ emit Test();
+ }
+}
+// ----
+// TypeError: (56-62): Type is not callable
+// TypeError: (56-60): Expression has to be an event invocation.
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol b/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol
new file mode 100644
index 00000000..5b7f870b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol
@@ -0,0 +1,12 @@
+pragma experimental "v0.5.0";
+contract C {
+ function f() public pure returns (uint, uint, bytes32) {
+ uint a;
+ bytes32 b;
+ (a,) = f();
+ (,b) = f();
+ }
+}
+// ----
+// TypeError: (126-136): Different number of components on the left hand side (2) than on the right hand side (3).
+// TypeError: (140-150): Different number of components on the left hand side (2) than on the right hand side (3).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol
new file mode 100644
index 00000000..bbf21d7e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol
@@ -0,0 +1,24 @@
+pragma experimental "v0.5.0";
+contract C {
+ function g() public pure returns (
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint
+ ) { }
+ function f() public pure returns (uint, uint, bytes32) {
+ uint a;
+ uint b;
+ (,,,,a,,,,b,,,,) = g();
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol
new file mode 100644
index 00000000..3262781b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol
@@ -0,0 +1,8 @@
+contract C {
+ function f() public pure {
+ uint a;
+ (a,) = (uint(1),);
+ }
+}
+// ----
+// Warning: (53-70): Different number of components on the left hand side (2) than on the right hand side (1).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol
new file mode 100644
index 00000000..b20bbf90
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol
@@ -0,0 +1,8 @@
+contract C {
+ struct S { uint a; uint b; }
+ function f() pure public {
+ S memory x;
+ S memory y;
+ (x, y) = (y, x);
+ }
+}
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol
new file mode 100644
index 00000000..5f7a18f7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol
@@ -0,0 +1,10 @@
+ contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ S storage x_local = x;
+ S storage y_local = y;
+ S storage z_local = x;
+ (x, y_local, x_local, z_local) = (y, x_local, y_local, y);
+ }
+ }
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol
new file mode 100644
index 00000000..a079a509
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() public pure returns (uint, uint, bytes32) {
+ uint a;
+ bytes32 b;
+ (a,) = f();
+ (,b) = f();
+ }
+}
+// ----
+// Warning: (96-106): Different number of components on the left hand side (2) than on the right hand side (3).
+// Warning: (110-120): Different number of components on the left hand side (2) than on the right hand side (3).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol
new file mode 100644
index 00000000..1d243c7c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() public pure returns (uint, uint, uint, uint) {
+ // Can later be replaced by (uint a, uint b,) = f();
+ var (a,b,) = f();
+ a; b;
+ }
+}
+// ----
+// Warning: (136-137): Use of the "var" keyword is deprecated.
+// Warning: (138-139): Use of the "var" keyword is deprecated.
+// Warning: (131-147): Different number of components on the left hand side (3) than on the right hand side (4).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol
new file mode 100644
index 00000000..e4c3e694
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol
@@ -0,0 +1,9 @@
+contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ (x, y) = (y, x);
+ }
+}
+// ----
+// Warning: (79-94): This assignment performs two copies to storage. Since storage copies do not first copy to a temporary location, one of them might be overwritten before the second is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol
new file mode 100644
index 00000000..b2979804
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol
@@ -0,0 +1,10 @@
+contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ (,x, y) = (1, 2, y, x);
+ }
+}
+// ----
+// Warning: (79-101): This assignment performs two copies to storage. Since storage copies do not first copy to a temporary location, one of them might be overwritten before the second is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
+// Warning: (79-101): Different number of components on the left hand side (3) than on the right hand side (4).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol
new file mode 100644
index 00000000..aa35d7d4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol
@@ -0,0 +1,10 @@
+contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ (x, y, ) = (y, x, 1, 2);
+ }
+}
+// ----
+// Warning: (79-102): This assignment performs two copies to storage. Since storage copies do not first copy to a temporary location, one of them might be overwritten before the second is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
+// Warning: (79-102): Different number of components on the left hand side (3) than on the right hand side (4).