aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md1
-rw-r--r--libjulia/ASTDataForward.h (renamed from libjulia/AsmDataForward.h)0
-rw-r--r--libjulia/backends/evm/EVMCodeTransform.cpp25
-rw-r--r--libjulia/backends/evm/EVMCodeTransform.h2
-rw-r--r--libjulia/optimiser/ASTCopier.cpp175
-rw-r--r--libjulia/optimiser/ASTCopier.h94
-rw-r--r--libjulia/optimiser/ASTWalker.cpp149
-rw-r--r--libjulia/optimiser/ASTWalker.h104
-rw-r--r--libjulia/optimiser/Disambiguator.cpp85
-rw-r--r--libjulia/optimiser/Disambiguator.h68
-rw-r--r--libjulia/optimiser/NameCollector.cpp44
-rw-r--r--libjulia/optimiser/NameCollector.h52
-rw-r--r--libjulia/optimiser/Substitution.cpp39
-rw-r--r--libjulia/optimiser/Substitution.h51
-rw-r--r--libsolidity/analysis/TypeChecker.cpp2
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp14
-rw-r--r--libsolidity/formal/SMTChecker.cpp176
-rw-r--r--libsolidity/formal/SMTChecker.h18
-rw-r--r--libsolidity/formal/SolverInterface.h4
-rw-r--r--libsolidity/formal/Z3Interface.cpp5
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp18
-rw-r--r--libsolidity/inlineasm/AsmData.h4
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp11
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp8
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp6
-rwxr-xr-xscripts/build.sh7
-rw-r--r--test/libjulia/Common.cpp86
-rw-r--r--test/libjulia/Common.h55
-rw-r--r--test/libjulia/Disambiguator.cpp105
-rw-r--r--test/libsolidity/InlineAssembly.cpp5
-rw-r--r--test/libsolidity/SMTChecker.cpp98
31 files changed, 1425 insertions, 86 deletions
diff --git a/Changelog.md b/Changelog.md
index fd256af8..248165bb 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,7 @@
### 0.4.20 (unreleased)
Features:
+ * Inline Assembly: Issue warning for using jump labels (already existed for jump instructions).
Bugfixes:
diff --git a/libjulia/AsmDataForward.h b/libjulia/ASTDataForward.h
index 3806e321..3806e321 100644
--- a/libjulia/AsmDataForward.h
+++ b/libjulia/ASTDataForward.h
diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp
index 099b83fe..f92939be 100644
--- a/libjulia/backends/evm/EVMCodeTransform.cpp
+++ b/libjulia/backends/evm/EVMCodeTransform.cpp
@@ -125,11 +125,11 @@ void CodeTransform::operator()(FunctionCall const& _call)
void CodeTransform::operator()(FunctionalInstruction const& _instruction)
{
if (m_evm15 && (
- _instruction.instruction.instruction == solidity::Instruction::JUMP ||
- _instruction.instruction.instruction == solidity::Instruction::JUMPI
+ _instruction.instruction == solidity::Instruction::JUMP ||
+ _instruction.instruction == solidity::Instruction::JUMPI
))
{
- bool const isJumpI = _instruction.instruction.instruction == solidity::Instruction::JUMPI;
+ bool const isJumpI = _instruction.instruction == solidity::Instruction::JUMPI;
if (isJumpI)
{
solAssert(_instruction.arguments.size() == 2, "");
@@ -150,7 +150,8 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction)
{
for (auto const& arg: _instruction.arguments | boost::adaptors::reversed)
visitExpression(arg);
- (*this)(_instruction.instruction);
+ m_assembly.setSourceLocation(_instruction.location);
+ m_assembly.appendInstruction(_instruction.instruction);
}
checkStackHeight(&_instruction);
}
@@ -290,7 +291,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
solAssert(m_info.scopes.at(&_function.body), "");
Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
solAssert(varScope, "");
- for (auto const& v: _function.arguments | boost::adaptors::reversed)
+ for (auto const& v: _function.parameters | boost::adaptors::reversed)
{
auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name));
m_context->variableStackHeights[&var] = height++;
@@ -303,7 +304,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
if (m_evm15)
{
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore);
- m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.arguments.size());
+ m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.parameters.size());
}
else
{
@@ -312,7 +313,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
}
m_stackAdjustment += localStackAdjustment;
- for (auto const& v: _function.returns)
+ for (auto const& v: _function.returnVariables)
{
auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name));
m_context->variableStackHeights[&var] = height++;
@@ -342,9 +343,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
// modified parallel to the actual stack.
vector<int> stackLayout;
if (!m_evm15)
- stackLayout.push_back(_function.returns.size()); // Move return label to the top
- stackLayout += vector<int>(_function.arguments.size(), -1); // discard all arguments
- for (size_t i = 0; i < _function.returns.size(); ++i)
+ stackLayout.push_back(_function.returnVariables.size()); // Move return label to the top
+ stackLayout += vector<int>(_function.parameters.size(), -1); // discard all arguments
+ for (size_t i = 0; i < _function.returnVariables.size(); ++i)
stackLayout.push_back(i); // Move return values down, but keep order.
solAssert(stackLayout.size() <= 17, "Stack too deep");
@@ -364,9 +365,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
}
if (m_evm15)
- m_assembly.appendReturnsub(_function.returns.size(), stackHeightBefore);
+ m_assembly.appendReturnsub(_function.returnVariables.size(), stackHeightBefore);
else
- m_assembly.appendJump(stackHeightBefore - _function.returns.size());
+ m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size());
m_stackAdjustment -= localStackAdjustment;
m_assembly.appendLabel(afterFunction);
checkStackHeight(&_function);
diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h
index e4cb20ed..577cc8ba 100644
--- a/libjulia/backends/evm/EVMCodeTransform.h
+++ b/libjulia/backends/evm/EVMCodeTransform.h
@@ -20,7 +20,7 @@
#include <libjulia/backends/evm/EVMAssembly.h>
-#include <libjulia/AsmDataForward.h>
+#include <libjulia/ASTDataForward.h>
#include <libsolidity/inlineasm/AsmScope.h>
diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp
new file mode 100644
index 00000000..a461f434
--- /dev/null
+++ b/libjulia/optimiser/ASTCopier.cpp
@@ -0,0 +1,175 @@
+/*
+ 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/>.
+*/
+/**
+ * Creates an independent copy of an AST, renaming identifiers to be unique.
+ */
+
+#include <libjulia/optimiser/ASTCopier.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <libdevcore/Common.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+
+
+Statement ASTCopier::operator()(Instruction const&)
+{
+ solAssert(false, "Invalid operation.");
+ return {};
+}
+
+Statement ASTCopier::operator()(VariableDeclaration const& _varDecl)
+{
+ return VariableDeclaration{
+ _varDecl.location,
+ translateVector(_varDecl.variables),
+ translate(_varDecl.value)
+ };
+}
+
+Statement ASTCopier::operator()(Assignment const& _assignment)
+{
+ return Assignment{
+ _assignment.location,
+ translateVector(_assignment.variableNames),
+ translate(_assignment.value)
+ };
+}
+
+Statement ASTCopier::operator()(StackAssignment const&)
+{
+ solAssert(false, "Invalid operation.");
+ return {};
+}
+
+Statement ASTCopier::operator()(Label const&)
+{
+ solAssert(false, "Invalid operation.");
+ return {};
+}
+
+Statement ASTCopier::operator()(FunctionCall const& _call)
+{
+ return FunctionCall{
+ _call.location,
+ translate(_call.functionName),
+ translateVector(_call.arguments)
+ };
+}
+
+Statement ASTCopier::operator()(FunctionalInstruction const& _instruction)
+{
+ return FunctionalInstruction{
+ _instruction.location,
+ _instruction.instruction,
+ translateVector(_instruction.arguments)
+ };
+}
+
+Statement ASTCopier::operator()(Identifier const& _identifier)
+{
+ return Identifier{_identifier.location, translateIdentifier(_identifier.name)};
+}
+
+Statement ASTCopier::operator()(Literal const& _literal)
+{
+ return translate(_literal);
+}
+
+Statement ASTCopier::operator()(If const& _if)
+{
+ return If{_if.location, translate(_if.condition), translate(_if.body)};
+}
+
+Statement ASTCopier::operator()(Switch const& _switch)
+{
+ return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)};
+}
+
+Statement ASTCopier::operator()(FunctionDefinition const& _function)
+{
+ string translatedName = translateIdentifier(_function.name);
+
+ enterFunction(_function);
+ ScopeGuard g([&]() { this->leaveFunction(_function); });
+
+ return FunctionDefinition{
+ _function.location,
+ move(translatedName),
+ translateVector(_function.parameters),
+ translateVector(_function.returnVariables),
+ translate(_function.body)
+ };
+}
+
+Statement ASTCopier::operator()(ForLoop const& _forLoop)
+{
+ enterScope(_forLoop.pre);
+ ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); });
+
+ return ForLoop{
+ _forLoop.location,
+ translate(_forLoop.pre),
+ translate(_forLoop.condition),
+ translate(_forLoop.post),
+ translate(_forLoop.body)
+ };
+}
+
+Statement ASTCopier::operator ()(Block const& _block)
+{
+ return translate(_block);
+}
+
+Statement ASTCopier::translate(Statement const& _statement)
+{
+ return boost::apply_visitor(*this, _statement);
+}
+
+Block ASTCopier::translate(Block const& _block)
+{
+ enterScope(_block);
+ ScopeGuard g([&]() { this->leaveScope(_block); });
+
+ return Block{_block.location, translateVector(_block.statements)};
+}
+
+Case ASTCopier::translate(Case const& _case)
+{
+ return Case{_case.location, translate(_case.value), translate(_case.body)};
+}
+
+Identifier ASTCopier::translate(Identifier const& _identifier)
+{
+ return Identifier{_identifier.location, translateIdentifier(_identifier.name)};
+}
+
+Literal ASTCopier::translate(Literal const& _literal)
+{
+ return _literal;
+}
+
+TypedName ASTCopier::translate(TypedName const& _typedName)
+{
+ return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type};
+}
+
diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h
new file mode 100644
index 00000000..5dde2ce9
--- /dev/null
+++ b/libjulia/optimiser/ASTCopier.h
@@ -0,0 +1,94 @@
+/*
+ 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/>.
+*/
+/**
+ * Creates an independent copy of an AST, renaming identifiers to be unique.
+ */
+
+#pragma once
+
+#include <libjulia/ASTDataForward.h>
+
+#include <boost/variant.hpp>
+#include <boost/optional.hpp>
+
+#include <vector>
+#include <set>
+#include <memory>
+
+namespace dev
+{
+namespace julia
+{
+
+/**
+ * Creates a copy of a iulia AST potentially replacing identifier names.
+ * Base class to be extended.
+ */
+class ASTCopier: public boost::static_visitor<Statement>
+{
+public:
+ Statement operator()(Literal const& _literal);
+ Statement operator()(Instruction const& _instruction);
+ Statement operator()(Identifier const& _identifier);
+ Statement operator()(FunctionalInstruction const& _instr);
+ Statement operator()(FunctionCall const&);
+ Statement operator()(Label const& _label);
+ Statement operator()(StackAssignment const& _assignment);
+ Statement operator()(Assignment const& _assignment);
+ Statement operator()(VariableDeclaration const& _varDecl);
+ Statement operator()(If const& _if);
+ Statement operator()(Switch const& _switch);
+ Statement operator()(FunctionDefinition const&);
+ Statement operator()(ForLoop const&);
+ Statement operator()(Block const& _block);
+
+ virtual Statement translate(Statement const& _statement);
+
+protected:
+ template <typename T>
+ std::vector<T> translateVector(std::vector<T> const& _values);
+
+ template <typename T>
+ std::shared_ptr<T> translate(std::shared_ptr<T> const& _v)
+ {
+ return _v ? std::make_shared<T>(translate(*_v)) : nullptr;
+ }
+ Block translate(Block const& _block);
+ Case translate(Case const& _case);
+ Identifier translate(Identifier const& _identifier);
+ Literal translate(Literal const& _literal);
+ TypedName translate(TypedName const& _typedName);
+
+ virtual void enterScope(Block const&) { }
+ virtual void leaveScope(Block const&) { }
+ virtual void enterFunction(FunctionDefinition const&) { }
+ virtual void leaveFunction(FunctionDefinition const&) { }
+ virtual std::string translateIdentifier(std::string const& _name) { return _name; }
+};
+
+template <typename T>
+std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values)
+{
+ std::vector<T> translated;
+ for (auto const& v: _values)
+ translated.emplace_back(translate(v));
+ return translated;
+}
+
+
+}
+}
diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp
new file mode 100644
index 00000000..0caef04e
--- /dev/null
+++ b/libjulia/optimiser/ASTWalker.cpp
@@ -0,0 +1,149 @@
+/*
+ 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/>.
+*/
+/**
+ * Generic AST walker.
+ */
+
+#include <libjulia/optimiser/ASTWalker.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+using namespace dev::solidity;
+
+
+void ASTWalker::operator()(FunctionalInstruction const& _instr)
+{
+ walkVector(_instr.arguments | boost::adaptors::reversed);
+}
+
+void ASTWalker::operator()(FunctionCall const& _funCall)
+{
+ walkVector(_funCall.arguments | boost::adaptors::reversed);
+}
+
+void ASTWalker::operator()(Assignment const& _assignment)
+{
+ for (auto const& name: _assignment.variableNames)
+ (*this)(name);
+ boost::apply_visitor(*this, *_assignment.value);
+}
+
+void ASTWalker::operator()(VariableDeclaration const& _varDecl)
+{
+ if (_varDecl.value)
+ boost::apply_visitor(*this, *_varDecl.value);
+}
+
+void ASTWalker::operator()(If const& _if)
+{
+ boost::apply_visitor(*this, *_if.condition);
+ (*this)(_if.body);
+}
+
+void ASTWalker::operator()(Switch const& _switch)
+{
+ boost::apply_visitor(*this, *_switch.expression);
+ for (auto const& _case: _switch.cases)
+ {
+ if (_case.value)
+ (*this)(*_case.value);
+ (*this)(_case.body);
+ }
+}
+
+void ASTWalker::operator()(FunctionDefinition const& _fun)
+{
+ (*this)(_fun.body);
+}
+
+void ASTWalker::operator()(ForLoop const& _for)
+{
+ (*this)(_for.pre);
+ boost::apply_visitor(*this, *_for.condition);
+ (*this)(_for.post);
+ (*this)(_for.body);
+}
+
+void ASTWalker::operator()(Block const& _block)
+{
+ walkVector(_block.statements);
+}
+
+void ASTModifier::operator()(FunctionalInstruction& _instr)
+{
+ walkVector(_instr.arguments | boost::adaptors::reversed);
+}
+
+void ASTModifier::operator()(FunctionCall& _funCall)
+{
+ walkVector(_funCall.arguments | boost::adaptors::reversed);
+}
+
+void ASTModifier::operator()(Assignment& _assignment)
+{
+ for (auto& name: _assignment.variableNames)
+ (*this)(name);
+ visit(*_assignment.value);
+}
+
+void ASTModifier::operator()(VariableDeclaration& _varDecl)
+{
+ if (_varDecl.value)
+ visit(*_varDecl.value);
+}
+
+void ASTModifier::operator()(If& _if)
+{
+ visit(*_if.condition);
+ (*this)(_if.body);
+}
+
+void ASTModifier::operator()(Switch& _switch)
+{
+ visit(*_switch.expression);
+ for (auto& _case: _switch.cases)
+ {
+ if (_case.value)
+ (*this)(*_case.value);
+ (*this)(_case.body);
+ }
+}
+
+void ASTModifier::operator()(FunctionDefinition& _fun)
+{
+ (*this)(_fun.body);
+}
+
+void ASTModifier::operator()(ForLoop& _for)
+{
+ (*this)(_for.pre);
+ visit(*_for.condition);
+ (*this)(_for.post);
+ (*this)(_for.body);
+}
+
+void ASTModifier::operator()(Block& _block)
+{
+ walkVector(_block.statements);
+}
diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h
new file mode 100644
index 00000000..8bd867d5
--- /dev/null
+++ b/libjulia/optimiser/ASTWalker.h
@@ -0,0 +1,104 @@
+/*
+ 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/>.
+*/
+/**
+ * Generic AST walker.
+ */
+
+#pragma once
+
+#include <libjulia/ASTDataForward.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/variant.hpp>
+#include <boost/optional.hpp>
+
+#include <vector>
+#include <set>
+#include <map>
+
+namespace dev
+{
+namespace julia
+{
+
+/**
+ * Generic AST walker.
+ */
+class ASTWalker: public boost::static_visitor<>
+{
+public:
+ virtual void operator()(Literal const&) {}
+ virtual void operator()(Instruction const&) { solAssert(false, ""); }
+ virtual void operator()(Identifier const&) {}
+ virtual void operator()(FunctionalInstruction const& _instr);
+ virtual void operator()(FunctionCall const& _funCall);
+ virtual void operator()(Label const&) { solAssert(false, ""); }
+ virtual void operator()(StackAssignment const&) { solAssert(false, ""); }
+ virtual void operator()(Assignment const& _assignment);
+ virtual void operator()(VariableDeclaration const& _varDecl);
+ virtual void operator()(If const& _if);
+ virtual void operator()(Switch const& _switch);
+ virtual void operator()(FunctionDefinition const&);
+ virtual void operator()(ForLoop const&);
+ virtual void operator()(Block const& _block);
+
+protected:
+ template <class T>
+ void walkVector(T const& _statements)
+ {
+ for (auto const& st: _statements)
+ boost::apply_visitor(*this, st);
+ }
+};
+
+/**
+ * Generic AST modifier (i.e. non-const version of ASTWalker).
+ */
+class ASTModifier: public boost::static_visitor<>
+{
+public:
+ virtual void operator()(Literal&) {}
+ virtual void operator()(Instruction&) { solAssert(false, ""); }
+ virtual void operator()(Identifier&) {}
+ virtual void operator()(FunctionalInstruction& _instr);
+ virtual void operator()(FunctionCall& _funCall);
+ virtual void operator()(Label&) { solAssert(false, ""); }
+ virtual void operator()(StackAssignment&) { solAssert(false, ""); }
+ virtual void operator()(Assignment& _assignment);
+ virtual void operator()(VariableDeclaration& _varDecl);
+ virtual void operator()(If& _if);
+ virtual void operator()(Switch& _switch);
+ virtual void operator()(FunctionDefinition&);
+ virtual void operator()(ForLoop&);
+ virtual void operator()(Block& _block);
+
+protected:
+ template <class T>
+ void walkVector(T&& _statements)
+ {
+ for (auto& st: _statements)
+ visit(st);
+ }
+ virtual void visit(Statement& _st)
+ {
+ boost::apply_visitor(*this, _st);
+ }
+};
+
+}
+}
diff --git a/libjulia/optimiser/Disambiguator.cpp b/libjulia/optimiser/Disambiguator.cpp
new file mode 100644
index 00000000..df2984e5
--- /dev/null
+++ b/libjulia/optimiser/Disambiguator.cpp
@@ -0,0 +1,85 @@
+/*
+ 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 makes all identifiers unique.
+ */
+
+#include <libjulia/optimiser/Disambiguator.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmScope.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+using namespace dev::solidity;
+
+using Scope = dev::solidity::assembly::Scope;
+
+string Disambiguator::translateIdentifier(string const& _originalName)
+{
+ solAssert(!m_scopes.empty() && m_scopes.back(), "");
+ 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;
+ }
+ return m_translations.at(id);
+}
+
+void Disambiguator::enterScope(Block const& _block)
+{
+ enterScopeInternal(*m_info.scopes.at(&_block));
+}
+
+void Disambiguator::leaveScope(Block const& _block)
+{
+ leaveScopeInternal(*m_info.scopes.at(&_block));
+}
+
+void Disambiguator::enterFunction(FunctionDefinition const& _function)
+{
+ enterScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()));
+}
+
+void Disambiguator::leaveFunction(FunctionDefinition const& _function)
+{
+ leaveScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()));
+}
+
+void Disambiguator::enterScopeInternal(Scope& _scope)
+{
+ m_scopes.push_back(&_scope);
+}
+
+void Disambiguator::leaveScopeInternal(Scope& _scope)
+{
+ solAssert(!m_scopes.empty(), "");
+ solAssert(m_scopes.back() == &_scope, "");
+ m_scopes.pop_back();
+}
diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h
new file mode 100644
index 00000000..cc9488d5
--- /dev/null
+++ b/libjulia/optimiser/Disambiguator.h
@@ -0,0 +1,68 @@
+/*
+ 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 makes all identifiers unique.
+ */
+
+#pragma once
+
+#include <libjulia/ASTDataForward.h>
+
+#include <libjulia/optimiser/ASTCopier.h>
+
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
+
+#include <boost/variant.hpp>
+#include <boost/optional.hpp>
+
+#include <set>
+
+namespace dev
+{
+namespace julia
+{
+class EVMAssembly;
+
+/**
+ * Creates a copy of a iulia AST replacing all identifiers by unique names.
+ */
+class Disambiguator: public ASTCopier
+{
+public:
+ Disambiguator(solidity::assembly::AsmAnalysisInfo const& _analysisInfo):
+ m_info(_analysisInfo)
+ {}
+
+protected:
+ virtual void enterScope(Block const& _block) override;
+ virtual void leaveScope(Block const& _block) override;
+ virtual void enterFunction(FunctionDefinition const& _function) override;
+ virtual void leaveFunction(FunctionDefinition const& _function) override;
+ virtual std::string translateIdentifier(std::string const& _name) override;
+
+ void enterScopeInternal(solidity::assembly::Scope& _scope);
+ void leaveScopeInternal(solidity::assembly::Scope& _scope);
+
+ solidity::assembly::AsmAnalysisInfo const& m_info;
+
+ std::vector<solidity::assembly::Scope*> m_scopes;
+ std::map<void const*, std::string> m_translations;
+ std::set<std::string> m_usedNames;
+};
+
+}
+}
diff --git a/libjulia/optimiser/NameCollector.cpp b/libjulia/optimiser/NameCollector.cpp
new file mode 100644
index 00000000..7b4c4793
--- /dev/null
+++ b/libjulia/optimiser/NameCollector.cpp
@@ -0,0 +1,44 @@
+/*
+ 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/>.
+*/
+/**
+ * Specific AST walker that collects all defined names.
+ */
+
+#include <libjulia/optimiser/NameCollector.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+
+void NameCollector::operator()(VariableDeclaration const& _varDecl)
+{
+ for (auto const& var: _varDecl.variables)
+ m_names.insert(var.name);
+}
+
+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)
+ m_names.insert(ret.name);
+ ASTWalker::operator ()(_funDef);
+}
diff --git a/libjulia/optimiser/NameCollector.h b/libjulia/optimiser/NameCollector.h
new file mode 100644
index 00000000..b7e38f46
--- /dev/null
+++ b/libjulia/optimiser/NameCollector.h
@@ -0,0 +1,52 @@
+/*
+ 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/>.
+*/
+/**
+ * Specific AST walker that collects all defined names.
+ */
+
+#pragma once
+
+#include <libjulia/optimiser/ASTWalker.h>
+
+#include <string>
+#include <map>
+#include <set>
+
+namespace dev
+{
+namespace julia
+{
+
+/**
+ * Specific AST walker that collects all defined names.
+ */
+class NameCollector: public ASTWalker
+{
+public:
+ 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; }
+private:
+ std::set<std::string> m_names;
+ std::map<std::string, FunctionDefinition const*> m_functions;
+};
+
+}
+}
diff --git a/libjulia/optimiser/Substitution.cpp b/libjulia/optimiser/Substitution.cpp
new file mode 100644
index 00000000..a49f1f7a
--- /dev/null
+++ b/libjulia/optimiser/Substitution.cpp
@@ -0,0 +1,39 @@
+/*
+ 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/>.
+*/
+/**
+ * Specific AST copier that replaces certain identifiers with expressions.
+ */
+
+#include <libjulia/optimiser/Substitution.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+
+Statement Substitution::translate(Statement const& _statement)
+{
+ if (_statement.type() == typeid(Identifier))
+ {
+ string const& name = boost::get<Identifier>(_statement).name;
+ if (m_substitutions.count(name))
+ // No recursive substitution
+ return ASTCopier().translate(*m_substitutions.at(name));
+ }
+ return ASTCopier::translate(_statement);
+}
diff --git a/libjulia/optimiser/Substitution.h b/libjulia/optimiser/Substitution.h
new file mode 100644
index 00000000..10bdf32e
--- /dev/null
+++ b/libjulia/optimiser/Substitution.h
@@ -0,0 +1,51 @@
+/*
+ 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/>.
+*/
+/**
+ * Specific AST copier that replaces certain identifiers with expressions.
+ */
+
+#pragma once
+
+#include <libjulia/optimiser/ASTCopier.h>
+
+#include <string>
+#include <map>
+#include <set>
+
+namespace dev
+{
+namespace julia
+{
+
+/**
+ * Specific AST copier that replaces certain identifiers with expressions.
+ * Only works on ASTs that are expressions.
+ */
+class Substitution: public ASTCopier
+{
+public:
+ Substitution(std::map<std::string, Statement const*> const& _substitutions):
+ m_substitutions(_substitutions)
+ {}
+ virtual Statement translate(Statement const& _statement) override;
+
+private:
+ std::map<std::string, Statement const*> const& m_substitutions;
+};
+
+}
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 73047e76..96160a75 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1060,7 +1060,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
_statement.initialValue()->location(),
"Invalid rational " +
valueComponentType->toString() +
- " (absolute value too large or divison by zero)."
+ " (absolute value too large or division by zero)."
);
else
solAssert(false, "");
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 7e41fc16..6788cb05 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -40,16 +40,13 @@ public:
void operator()(assembly::Label const&) { }
void operator()(assembly::Instruction const& _instruction)
{
- if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction))
- m_reportMutability(StateMutability::NonPayable, _instruction.location);
- else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction))
- m_reportMutability(StateMutability::View, _instruction.location);
+ checkInstruction(_instruction.location, _instruction.instruction);
}
void operator()(assembly::Literal const&) {}
void operator()(assembly::Identifier const&) {}
void operator()(assembly::FunctionalInstruction const& _instr)
{
- (*this)(_instr.instruction);
+ checkInstruction(_instr.location, _instr.instruction);
for (auto const& arg: _instr.arguments)
boost::apply_visitor(*this, arg);
}
@@ -102,6 +99,13 @@ public:
private:
std::function<void(StateMutability, SourceLocation const&)> m_reportMutability;
+ void checkInstruction(SourceLocation _location, solidity::Instruction _instruction)
+ {
+ if (eth::SemanticInformation::invalidInViewFunctions(_instruction))
+ m_reportMutability(StateMutability::NonPayable, _location);
+ else if (eth::SemanticInformation::invalidInPureFunctions(_instruction))
+ m_reportMutability(StateMutability::View, _location);
+ }
};
}
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 1050621e..a22e35d6 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -55,7 +55,7 @@ void SMTChecker::analyze(SourceUnit const& _source)
void SMTChecker::endVisit(VariableDeclaration const& _varDecl)
{
if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value())
- assignment(_varDecl, *_varDecl.value());
+ assignment(_varDecl, *_varDecl.value(), _varDecl.location());
}
bool SMTChecker::visit(FunctionDefinition const& _function)
@@ -178,8 +178,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl)
else if (knownVariable(*_varDecl.declarations()[0]))
{
if (_varDecl.initialValue())
- // TODO more checks?
- assignment(*_varDecl.declarations()[0], *_varDecl.initialValue());
+ assignment(*_varDecl.declarations()[0], *_varDecl.initialValue(), _varDecl.location());
}
else
m_errorReporter.warning(
@@ -208,7 +207,10 @@ void SMTChecker::endVisit(Assignment const& _assignment)
{
Declaration const* decl = identifier->annotation().referencedDeclaration;
if (knownVariable(*decl))
- assignment(*decl, _assignment.rightHandSide());
+ {
+ assignment(*decl, _assignment.rightHandSide(), _assignment.location());
+ defineExpr(_assignment, expr(_assignment.rightHandSide()));
+ }
else
m_errorReporter.warning(
_assignment.location(),
@@ -230,7 +232,81 @@ void SMTChecker::endVisit(TupleExpression const& _tuple)
"Assertion checker does not yet implement tules and inline arrays."
);
else
- m_interface->addAssertion(expr(_tuple) == expr(*_tuple.components()[0]));
+ defineExpr(_tuple, expr(*_tuple.components()[0]));
+}
+
+void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location)
+{
+ checkCondition(
+ _value < minValue(_type),
+ _location,
+ "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")",
+ "value",
+ &_value
+ );
+ checkCondition(
+ _value > maxValue(_type),
+ _location,
+ "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")",
+ "value",
+ &_value
+ );
+}
+
+void SMTChecker::endVisit(UnaryOperation const& _op)
+{
+ switch (_op.getOperator())
+ {
+ case Token::Not: // !
+ {
+ solAssert(_op.annotation().type->category() == Type::Category::Bool, "");
+ defineExpr(_op, !expr(_op.subExpression()));
+ break;
+ }
+ case Token::Inc: // ++ (pre- or postfix)
+ case Token::Dec: // -- (pre- or postfix)
+ {
+ solAssert(_op.annotation().type->category() == Type::Category::Integer, "");
+ solAssert(_op.subExpression().annotation().lValueRequested, "");
+ if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_op.subExpression()))
+ {
+ Declaration const* decl = identifier->annotation().referencedDeclaration;
+ if (knownVariable(*decl))
+ {
+ auto innerValue = currentValue(*decl);
+ auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1;
+ assignment(*decl, newValue, _op.location());
+ defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
+ }
+ else
+ m_errorReporter.warning(
+ _op.location(),
+ "Assertion checker does not yet implement such assignments."
+ );
+ }
+ else
+ m_errorReporter.warning(
+ _op.location(),
+ "Assertion checker does not yet implement such increments / decrements."
+ );
+ break;
+ }
+ case Token::Add: // +
+ defineExpr(_op, expr(_op.subExpression()));
+ break;
+ case Token::Sub: // -
+ {
+ defineExpr(_op, 0 - expr(_op.subExpression()));
+ if (auto intType = dynamic_cast<IntegerType const*>(_op.annotation().type.get()))
+ checkUnderOverflow(expr(_op), *intType, _op.location());
+ break;
+ }
+ default:
+ m_errorReporter.warning(
+ _op.location(),
+ "Assertion checker does not yet implement this operator."
+ );
+ }
}
void SMTChecker::endVisit(BinaryOperation const& _op)
@@ -274,10 +350,8 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
{
solAssert(args.size() == 1, "");
solAssert(args[0]->annotation().type->category() == Type::Category::Bool, "");
+ checkBooleanNotConstant(*args[0], "Condition is always $VALUE.");
m_interface->addAssertion(expr(*args[0]));
- checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code");
- // TODO is there something meaningful we can check here?
- // We can check whether the condition is always fulfilled or never fulfilled.
}
}
@@ -290,7 +364,7 @@ void SMTChecker::endVisit(Identifier const& _identifier)
// Will be translated as part of the node that requested the lvalue.
}
else if (dynamic_cast<IntegerType const*>(_identifier.annotation().type.get()))
- m_interface->addAssertion(expr(_identifier) == currentValue(*decl));
+ defineExpr(_identifier, currentValue(*decl));
else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
{
if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require)
@@ -306,10 +380,10 @@ void SMTChecker::endVisit(Literal const& _literal)
if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&type))
solAssert(!rational->isFractional(), "");
- m_interface->addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal)));
+ defineExpr(_literal, smt::Expression(type.literalValue(&_literal)));
}
else if (type.category() == Type::Category::Bool)
- m_interface->addAssertion(expr(_literal) == smt::Expression(_literal.token() == Token::TrueLiteral ? true : false));
+ defineExpr(_literal, smt::Expression(_literal.token() == Token::TrueLiteral ? true : false));
else
m_errorReporter.warning(
_literal.location(),
@@ -326,36 +400,30 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
case Token::Add:
case Token::Sub:
case Token::Mul:
+ case Token::Div:
{
solAssert(_op.annotation().commonType, "");
solAssert(_op.annotation().commonType->category() == Type::Category::Integer, "");
+ auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType);
smt::Expression left(expr(_op.leftExpression()));
smt::Expression right(expr(_op.rightExpression()));
Token::Value op = _op.getOperator();
smt::Expression value(
op == Token::Add ? left + right :
op == Token::Sub ? left - right :
+ op == Token::Div ? division(left, right, intType) :
/*op == Token::Mul*/ left * right
);
- // Overflow check
- auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType);
- checkCondition(
- value < minValue(intType),
- _op.location(),
- "Underflow (resulting value less than " + formatNumber(intType.minValue()) + ")",
- "value",
- &value
- );
- checkCondition(
- value > maxValue(intType),
- _op.location(),
- "Overflow (resulting value larger than " + formatNumber(intType.maxValue()) + ")",
- "value",
- &value
- );
+ if (_op.getOperator() == Token::Div)
+ {
+ checkCondition(right == 0, _op.location(), "Division by zero", "value", &right);
+ m_interface->addAssertion(right != 0);
+ }
+
+ checkUnderOverflow(value, intType, _op.location());
- m_interface->addAssertion(expr(_op) == value);
+ defineExpr(_op, value);
break;
}
default:
@@ -383,7 +451,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op)
/*op == Token::GreaterThanOrEqual*/ (left >= right)
);
// TODO: check that other values for op are not possible.
- m_interface->addAssertion(expr(_op) == value);
+ defineExpr(_op, value);
}
else
m_errorReporter.warning(
@@ -400,9 +468,9 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op)
{
// @TODO check that both of them are not constant
if (_op.getOperator() == Token::And)
- m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression()));
+ defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression()));
else
- m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression()));
+ defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression()));
}
else
m_errorReporter.warning(
@@ -411,11 +479,30 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op)
);
}
-void SMTChecker::assignment(Declaration const& _variable, Expression const& _value)
+smt::Expression SMTChecker::division(smt::Expression _left, smt::Expression _right, IntegerType const& _type)
+{
+ // Signed division in SMTLIB2 rounds differently for negative division.
+ if (_type.isSigned())
+ return (smt::Expression::ite(
+ _left >= 0,
+ smt::Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))),
+ smt::Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right))
+ ));
+ else
+ return _left / _right;
+}
+
+void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location)
+{
+ assignment(_variable, expr(_value), _location);
+}
+
+void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location)
{
- // TODO more checks?
- // TODO add restrictions about type (might be assignment from smaller type)
- m_interface->addAssertion(newValue(_variable) == expr(_value));
+ TypePointer type = _variable.type();
+ if (auto const* intType = dynamic_cast<IntegerType const*>(type.get()))
+ checkUnderOverflow(_value, *intType, _location);
+ m_interface->addAssertion(newValue(_variable) == _value);
}
void SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition)
@@ -696,6 +783,18 @@ smt::Expression SMTChecker::expr(Expression const& _e)
{
if (!m_expressions.count(&_e))
{
+ m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." );
+ createExpr(_e);
+ }
+ return m_expressions.at(&_e);
+}
+
+void SMTChecker::createExpr(Expression const& _e)
+{
+ if (m_expressions.count(&_e))
+ m_errorReporter.warning(_e.location(), "Internal error: Expression created twice in SMT solver." );
+ else
+ {
solAssert(_e.annotation().type, "");
switch (_e.annotation().type->category())
{
@@ -716,7 +815,12 @@ smt::Expression SMTChecker::expr(Expression const& _e)
solAssert(false, "Type not implemented.");
}
}
- return m_expressions.at(&_e);
+}
+
+void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value)
+{
+ createExpr(_e);
+ m_interface->addAssertion(expr(_e) == _value);
}
smt::Expression SMTChecker::var(Declaration const& _decl)
diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h
index 8e07d74d..e7481cca 100644
--- a/libsolidity/formal/SMTChecker.h
+++ b/libsolidity/formal/SMTChecker.h
@@ -57,6 +57,7 @@ private:
virtual void endVisit(ExpressionStatement const& _node) override;
virtual void endVisit(Assignment const& _node) override;
virtual void endVisit(TupleExpression const& _node) override;
+ virtual void endVisit(UnaryOperation const& _node) override;
virtual void endVisit(BinaryOperation const& _node) override;
virtual void endVisit(FunctionCall const& _node) override;
virtual void endVisit(Identifier const& _node) override;
@@ -66,7 +67,12 @@ private:
void compareOperation(BinaryOperation const& _op);
void booleanOperation(BinaryOperation const& _op);
- void assignment(Declaration const& _variable, Expression const& _value);
+ /// Division expression in the given type. Requires special treatment because
+ /// of rounding for signed division.
+ smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type);
+
+ void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location);
+ void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location);
// Visits the branch given by the statement, pushes and pops the SMT checker.
// @param _condition if present, asserts that this condition is true within the branch.
@@ -88,6 +94,9 @@ private:
Expression const& _condition,
std::string const& _description
);
+ /// Checks that the value is in the range given by the type.
+ void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, SourceLocation const& _location);
+
std::pair<smt::CheckResult, std::vector<std::string>>
checkSatisifableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
@@ -126,9 +135,12 @@ private:
using VariableSequenceCounters = std::map<Declaration const*, int>;
- /// Returns the expression corresponding to the AST node. Creates a new expression
- /// if it does not exist yet.
+ /// Returns the expression corresponding to the AST node. Throws if the expression does not exist.
smt::Expression expr(Expression const& _e);
+ /// Creates the expression (value can be arbitrary)
+ void createExpr(Expression const& _e);
+ /// Creates the expression and sets its value.
+ void defineExpr(Expression const& _e, smt::Expression _value);
/// Returns the function declaration corresponding to the given variable.
/// The function takes one argument which is the "sequence number".
smt::Expression var(Declaration const& _decl);
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index c9adf863..74c993e8 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -120,6 +120,10 @@ public:
{
return Expression("*", std::move(_a), std::move(_b), Sort::Int);
}
+ friend Expression operator/(Expression _a, Expression _b)
+ {
+ return Expression("/", std::move(_a), std::move(_b), Sort::Int);
+ }
Expression operator()(Expression _a) const
{
solAssert(
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
index e5c1aef4..769e6edb 100644
--- a/libsolidity/formal/Z3Interface.cpp
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -127,7 +127,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
{">=", 2},
{"+", 2},
{"-", 2},
- {"*", 2}
+ {"*", 2},
+ {"/", 2}
};
string const& n = _expr.name;
if (m_functions.count(n))
@@ -173,6 +174,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
return arguments[0] - arguments[1];
else if (n == "*")
return arguments[0] * arguments[1];
+ else if (n == "/")
+ return arguments[0] / arguments[1];
// Cannot reach here.
solAssert(false, "");
return arguments[0];
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 2804ddfc..049af65f 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -56,6 +56,7 @@ bool AsmAnalyzer::operator()(Label const& _label)
{
solAssert(!m_julia, "");
m_info.stackHeightInfo[&_label] = m_stackHeight;
+ warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location);
return true;
}
@@ -146,10 +147,11 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
if (!expectExpression(arg))
success = false;
// Parser already checks that the number of arguments is correct.
- solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), "");
- if (!(*this)(_instr.instruction))
- success = false;
+ auto const& info = instructionInfo(_instr.instruction);
+ solAssert(info.args == int(_instr.arguments.size()), "");
+ m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instr] = m_stackHeight;
+ warnOnInstructions(_instr.instruction, _instr.location);
return success;
}
@@ -217,14 +219,14 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
solAssert(virtualBlock, "");
Scope& varScope = scope(virtualBlock);
- for (auto const& var: _funDef.arguments + _funDef.returns)
+ for (auto const& var: _funDef.parameters + _funDef.returnVariables)
{
expectValidType(var.type, var.location);
m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
}
int const stackHeight = m_stackHeight;
- m_stackHeight = _funDef.arguments.size() + _funDef.returns.size();
+ m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size();
bool success = (*this)(_funDef.body);
@@ -522,11 +524,11 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
"the Metropolis hard fork. Before that it acts as an invalid instruction."
);
- if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI)
+ if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
m_errorReporter.warning(
_location,
- "Jump instructions are low-level EVM features that can lead to "
+ "Jump instructions and labels are low-level EVM features that can lead to "
"incorrect stack access. Because of that they are discouraged. "
- "Please consider using \"switch\" or \"for\" statements instead."
+ "Please consider using \"switch\", \"if\" or \"for\" statements instead."
);
}
diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h
index a792a1b8..11e56fae 100644
--- a/libsolidity/inlineasm/AsmData.h
+++ b/libsolidity/inlineasm/AsmData.h
@@ -60,14 +60,14 @@ struct StackAssignment { SourceLocation location; Identifier variableName; };
/// the same amount of items as the number of variables.
struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Statement> value; };
/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))"
-struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
+struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Statement> arguments; };
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Statement> value; };
/// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }")
-struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; };
+struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; };
/// Conditional execution without "else" part.
struct If { SourceLocation location; std::shared_ptr<Statement> condition; Block body; };
/// Switch case or default case
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 8f171005..4f8802a0 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -419,7 +419,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::LParen);
while (currentToken() != Token::RParen)
{
- funDef.arguments.emplace_back(parseTypedName());
+ funDef.parameters.emplace_back(parseTypedName());
if (currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
@@ -431,7 +431,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::GreaterThan);
while (true)
{
- funDef.returns.emplace_back(parseTypedName());
+ funDef.returnVariables.emplace_back(parseTypedName());
if (currentToken() == Token::LBrace)
break;
expectToken(Token::Comma);
@@ -448,10 +448,11 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
if (_instruction.type() == typeid(Instruction))
{
solAssert(!m_julia, "Instructions are invalid in JULIA");
+ Instruction const& instruction = std::move(boost::get<Instruction>(_instruction));
FunctionalInstruction ret;
- ret.instruction = std::move(boost::get<Instruction>(_instruction));
- ret.location = ret.instruction.location;
- solidity::Instruction instr = ret.instruction.instruction;
+ ret.instruction = instruction.instruction;
+ ret.location = std::move(instruction.location);
+ solidity::Instruction instr = ret.instruction;
InstructionInfo instrInfo = instructionInfo(instr);
if (solidity::isDupInstruction(instr))
fatalParserError("DUPi instructions not allowed for functional notation");
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 0f183244..c72586cb 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -94,7 +94,7 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional
{
solAssert(!m_julia, "");
return
- (*this)(_functionalInstruction.instruction) +
+ boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) +
"(" +
boost::algorithm::join(
_functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
@@ -144,17 +144,17 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin
{
string out = "function " + _functionDefinition.name + "(";
out += boost::algorithm::join(
- _functionDefinition.arguments | boost::adaptors::transformed(
+ _functionDefinition.parameters | boost::adaptors::transformed(
[this](TypedName argument) { return argument.name + appendTypeName(argument.type); }
),
", "
);
out += ")";
- if (!_functionDefinition.returns.empty())
+ if (!_functionDefinition.returnVariables.empty())
{
out += " -> ";
out += boost::algorithm::join(
- _functionDefinition.returns | boost::adaptors::transformed(
+ _functionDefinition.returnVariables | boost::adaptors::transformed(
[this](TypedName argument) { return argument.name + appendTypeName(argument.type); }
),
", "
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index 77ae9102..0984e7d2 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -71,10 +71,10 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
{
bool success = true;
vector<Scope::JuliaType> arguments;
- for (auto const& _argument: _funDef.arguments)
+ for (auto const& _argument: _funDef.parameters)
arguments.push_back(_argument.type);
vector<Scope::JuliaType> returns;
- for (auto const& _return: _funDef.returns)
+ for (auto const& _return: _funDef.returnVariables)
returns.push_back(_return.type);
if (!m_currentScope->registerFunction(_funDef.name, arguments, returns))
{
@@ -91,7 +91,7 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
varScope.superScope = m_currentScope;
m_currentScope = &varScope;
varScope.functionScope = true;
- for (auto const& var: _funDef.arguments + _funDef.returns)
+ for (auto const& var: _funDef.parameters + _funDef.returnVariables)
if (!registerVariable(var, _funDef.location, varScope))
success = false;
diff --git a/scripts/build.sh b/scripts/build.sh
index 3785e1c1..bddbb97a 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -7,6 +7,11 @@ else
fi
cd $(dirname "$0")/.. &&
+
+if [[ "$(git tag --points-at HEAD 2>/dev/null)" == v* ]]; then
+ touch prerelease.txt
+fi
+
mkdir -p build &&
cd build &&
cmake .. -DCMAKE_BUILD_TYPE="$BUILD_TYPE" &&
@@ -20,4 +25,4 @@ fi
if [ -z $CI ]; then
echo "Installing solc and soltest"
install solc/solc /usr/local/bin && install test/soltest /usr/local/bin
-fi \ No newline at end of file
+fi
diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp
new file mode 100644
index 00000000..da1538f3
--- /dev/null
+++ b/test/libjulia/Common.cpp
@@ -0,0 +1,86 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @date 2017
+ * Common functions the iulia tests.
+ */
+
+#include <test/libjulia/Common.h>
+
+#include <libjulia/optimiser/Disambiguator.h>
+
+#include <libsolidity/parsing/Scanner.h>
+
+#include <libsolidity/inlineasm/AsmParser.h>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmPrinter.h>
+
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/interface/ErrorReporter.h>
+
+#include <boost/test/unit_test.hpp>
+
+using namespace std;
+using namespace dev::julia;
+using namespace dev::solidity;
+
+void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner)
+{
+ for (auto const& error: _errors)
+ SourceReferenceFormatter::printExceptionInformation(
+ cout,
+ *error,
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error",
+ [&](std::string const&) -> Scanner const& { return _scanner; }
+ );
+}
+
+
+pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test::parse(string const& _source, bool _julia)
+{
+ ErrorList errors;
+ ErrorReporter errorReporter(errors);
+ auto scanner = make_shared<Scanner>(CharStream(_source), "");
+ auto parserResult = assembly::Parser(errorReporter, _julia).parse(scanner);
+ if (parserResult)
+ {
+ BOOST_REQUIRE(errorReporter.errors().empty());
+ auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
+ assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, _julia);
+ if (analyzer.analyze(*parserResult))
+ {
+ BOOST_REQUIRE(errorReporter.errors().empty());
+ return make_pair(parserResult, analysisInfo);
+ }
+ }
+ printErrors(errors, *scanner);
+ BOOST_FAIL("Invalid source.");
+
+ // Unreachable.
+ return {};
+}
+
+assembly::Block dev::julia::test::disambiguate(string const& _source, bool _julia)
+{
+ auto result = parse(_source, _julia);
+ return boost::get<Block>(Disambiguator(*result.second)(*result.first));
+}
+
+string dev::julia::test::format(string const& _source, bool _julia)
+{
+ return assembly::AsmPrinter(_julia)(*parse(_source, _julia).first);
+}
diff --git a/test/libjulia/Common.h b/test/libjulia/Common.h
new file mode 100644
index 00000000..1371101c
--- /dev/null
+++ b/test/libjulia/Common.h
@@ -0,0 +1,55 @@
+/*
+ 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/>.
+*/
+/**
+ * @date 2017
+ * Common functions the iulia tests.
+ */
+
+#pragma once
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+class Scanner;
+class Error;
+using ErrorList = std::vector<std::shared_ptr<Error const>>;
+namespace assembly
+{
+struct AsmAnalysisInfo;
+}
+}
+namespace julia
+{
+namespace test
+{
+
+void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner);
+std::pair<std::shared_ptr<solidity::assembly::Block>, std::shared_ptr<solidity::assembly::AsmAnalysisInfo>>
+parse(std::string const& _source, bool _julia = true);
+solidity::assembly::Block disambiguate(std::string const& _source, bool _julia = true);
+std::string format(std::string const& _source, bool _julia = true);
+
+}
+}
+}
diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp
new file mode 100644
index 00000000..a6338449
--- /dev/null
+++ b/test/libjulia/Disambiguator.cpp
@@ -0,0 +1,105 @@
+/*
+ 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/>.
+*/
+/**
+ * @date 2017
+ * Unit tests for the iulia name disambiguator.
+ */
+
+#include <test/libjulia/Common.h>
+
+#include <libsolidity/inlineasm/AsmPrinter.h>
+
+#include <boost/test/unit_test.hpp>
+
+using namespace std;
+using namespace dev::julia::test;
+using namespace dev::solidity;
+
+#define CHECK(_original, _expectation)\
+do\
+{\
+ assembly::AsmPrinter p(true);\
+ string result = p(disambiguate(_original));\
+ BOOST_CHECK_EQUAL(result, format(_expectation));\
+ BOOST_CHECK_EQUAL(result, p(disambiguate(result)));\
+}\
+while(false)
+
+BOOST_AUTO_TEST_SUITE(IuliaDisambiguator)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ CHECK("{ }", "{ }");
+}
+
+BOOST_AUTO_TEST_CASE(variables)
+{
+ CHECK(
+ "{ { let a:u256 } { let a:u256 } }",
+ "{ { let a:u256 } { let a_1:u256 } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(variables_clash)
+{
+ CHECK(
+ "{ { let a:u256 let a_1:u256 } { let a:u256 } }",
+ "{ { let a:u256 let a_1:u256 } { let a_2:u256 } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(variables_inside_functions)
+{
+ CHECK(
+ "{ { let c:u256 let b:u256 } function f(a:u256, c:u256) -> b:u256 { let x:u256 } { let a:u256 let x:u256 } }",
+ "{ { let c:u256 let b:u256 } function f(a:u256, c_1:u256) -> b_1:u256 { let x:u256 } { let a_1:u256 let x_1:u256 } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_call)
+{
+ CHECK(
+ "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f(a:u256) -> c:u256, d:u256 { let b:u256, c_1:u256 := f(a) } } }",
+ "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f_1(a_1:u256) -> c_1:u256, d_1:u256 { let b_1:u256, c_1_1:u256 := f_1(a_1) } } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(for_statement)
+{
+ CHECK(
+ "{ { let a:u256, b:u256 } { for { let a:u256 } a { a := a } { let b:u256 := a } } }",
+ "{ { let a:u256, b:u256 } { for { let a_1:u256 } a_1 { a_1 := a_1 } { let b_1:u256 := a_1 } } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(switch_statement)
+{
+ CHECK(
+ "{ { let a:u256, b:u256, c:u256 } { let a:u256 switch a case 0:u256 { let b:u256 := a } default { let c:u256 := a } } }",
+ "{ { let a:u256, b:u256, c:u256 } { let a_1:u256 switch a_1 case 0:u256 { let b_1:u256 := a_1 } default { let c_1:u256 := a_1 } } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(if_statement)
+{
+ CHECK(
+ "{ { let a:u256, b:u256, c:u256 } { let a:bool if a { let b:bool := a } } }",
+ "{ { let a:u256, b:u256, c:u256 } { let a_1:bool if a_1 { let b_1:bool := a_1 } } }"
+ );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index e9fb8431..82bafd49 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -712,8 +712,9 @@ BOOST_AUTO_TEST_CASE(jump_warning)
{
CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions");
CHECK_PARSE_WARNING("{ 1 2 jumpi }", Warning, "Jump instructions");
- CHECK_PARSE_WARNING("{ a: jump(a) }", Warning, "Jump instructions");
- CHECK_PARSE_WARNING("{ a: jumpi(a, 2) }", Warning, "Jump instructions");
+ CHECK_PARSE_WARNING("{ jump(44) }", Warning, "Jump instructions");
+ CHECK_PARSE_WARNING("{ jumpi(44, 2) }", Warning, "Jump instructions");
+ CHECK_PARSE_WARNING("{ a: }", Warning, "Jump instructions");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp
index 667d666b..38d02601 100644
--- a/test/libsolidity/SMTChecker.cpp
+++ b/test/libsolidity/SMTChecker.cpp
@@ -351,9 +351,9 @@ BOOST_AUTO_TEST_CASE(while_loop_simple)
// Check that side-effects of condition are taken into account
text = R"(
contract C {
- function f(uint x) public pure {
+ function f(uint x, uint y) public pure {
x = 7;
- while ((x = 5) > 0) {
+ while ((x = y) > 0) {
}
assert(x == 7);
}
@@ -458,6 +458,100 @@ BOOST_AUTO_TEST_CASE(for_loop)
CHECK_WARNING(text, "Assertion violation");
}
+BOOST_AUTO_TEST_CASE(division)
+{
+ string text = R"(
+ contract C {
+ function f(uint x, uint y) public pure returns (uint) {
+ return x / y;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Division by zero");
+ text = R"(
+ contract C {
+ function f(uint x, uint y) public pure returns (uint) {
+ require(y != 0);
+ return x / y;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(int x, int y) public pure returns (int) {
+ require(y != 0);
+ return x / y;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Overflow");
+ text = R"(
+ contract C {
+ function f(int x, int y) public pure returns (int) {
+ require(y != 0);
+ require(y != -1);
+ return x / y;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(division_truncates_correctly)
+{
+ string text = R"(
+ contract C {
+ function f(uint x, uint y) public pure {
+ x = 7;
+ y = 2;
+ assert(x / y == 3);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(int x, int y) public pure {
+ x = 7;
+ y = 2;
+ assert(x / y == 3);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(int x, int y) public pure {
+ x = -7;
+ y = 2;
+ assert(x / y == -3);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(int x, int y) public pure {
+ x = 7;
+ y = -2;
+ assert(x / y == -3);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(int x, int y) public pure {
+ x = -7;
+ y = -2;
+ assert(x / y == 3);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}