aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/CMakeLists.txt65
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp3
-rw-r--r--libsolidity/analysis/ContractLevelChecker.cpp13
-rw-r--r--libsolidity/analysis/ContractLevelChecker.h1
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.cpp185
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.h9
-rw-r--r--libsolidity/analysis/ControlFlowBuilder.cpp262
-rw-r--r--libsolidity/analysis/ControlFlowBuilder.h31
-rw-r--r--libsolidity/analysis/ControlFlowGraph.cpp91
-rw-r--r--libsolidity/analysis/ControlFlowGraph.h104
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp1
-rw-r--r--libsolidity/analysis/DeclarationContainer.h5
-rw-r--r--libsolidity/analysis/DocStringAnalyser.cpp3
-rw-r--r--libsolidity/analysis/GlobalContext.cpp3
-rw-r--r--libsolidity/analysis/GlobalContext.h8
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp3
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h10
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp7
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp14
-rw-r--r--libsolidity/analysis/ReferencesResolver.h7
-rw-r--r--libsolidity/analysis/SemVerHandler.cpp1
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp29
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp13
-rw-r--r--libsolidity/analysis/TypeChecker.cpp87
-rw-r--r--libsolidity/analysis/TypeChecker.h2
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp7
-rw-r--r--libsolidity/ast/AST.cpp5
-rw-r--r--libsolidity/ast/AST.h8
-rw-r--r--libsolidity/ast/ASTAnnotations.h6
-rw-r--r--libsolidity/ast/ASTForward.h2
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp19
-rw-r--r--libsolidity/ast/ASTJsonConverter.h7
-rw-r--r--libsolidity/ast/ASTPrinter.cpp5
-rw-r--r--libsolidity/ast/ASTPrinter.h2
-rw-r--r--libsolidity/ast/ASTVisitor.h4
-rw-r--r--libsolidity/ast/Types.cpp233
-rw-r--r--libsolidity/ast/Types.h120
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp5
-rw-r--r--libsolidity/codegen/ABIFunctions.h7
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp8
-rw-r--r--libsolidity/codegen/AsmCodeGen.cpp197
-rw-r--r--libsolidity/codegen/AsmCodeGen.h92
-rw-r--r--libsolidity/codegen/Compiler.cpp7
-rw-r--r--libsolidity/codegen/Compiler.h4
-rw-r--r--libsolidity/codegen/CompilerContext.cpp19
-rw-r--r--libsolidity/codegen/CompilerContext.h13
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp6
-rw-r--r--libsolidity/codegen/CompilerUtils.h2
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp137
-rw-r--r--libsolidity/codegen/ContractCompiler.h16
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp28
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h13
-rw-r--r--libsolidity/codegen/LValue.cpp5
-rw-r--r--libsolidity/codegen/LValue.h6
-rw-r--r--libsolidity/formal/CVC4Interface.cpp1
-rw-r--r--libsolidity/formal/CVC4Interface.h1
-rw-r--r--libsolidity/formal/SMTChecker.cpp319
-rw-r--r--libsolidity/formal/SMTChecker.h43
-rw-r--r--libsolidity/formal/SMTLib2Interface.cpp8
-rw-r--r--libsolidity/formal/SMTLib2Interface.h11
-rw-r--r--libsolidity/formal/SMTPortfolio.h3
-rw-r--r--libsolidity/formal/SolverInterface.h21
-rw-r--r--libsolidity/formal/SymbolicTypes.cpp56
-rw-r--r--libsolidity/formal/SymbolicTypes.h8
-rw-r--r--libsolidity/formal/SymbolicVariables.cpp65
-rw-r--r--libsolidity/formal/SymbolicVariables.h63
-rw-r--r--libsolidity/formal/Z3Interface.cpp1
-rw-r--r--libsolidity/formal/Z3Interface.h2
-rw-r--r--libsolidity/interface/ABI.cpp1
-rw-r--r--libsolidity/interface/ABI.h4
-rw-r--r--libsolidity/interface/AssemblyStack.cpp82
-rw-r--r--libsolidity/interface/AssemblyStack.h14
-rw-r--r--libsolidity/interface/CompilerStack.cpp99
-rw-r--r--libsolidity/interface/CompilerStack.h15
-rw-r--r--libsolidity/interface/GasEstimator.cpp19
-rw-r--r--libsolidity/interface/GasEstimator.h6
-rw-r--r--libsolidity/interface/Natspec.cpp3
-rw-r--r--libsolidity/interface/Natspec.h4
-rw-r--r--libsolidity/interface/ReadFile.h4
-rw-r--r--libsolidity/interface/StandardCompiler.cpp225
-rw-r--r--libsolidity/interface/Version.cpp5
-rw-r--r--libsolidity/interface/Version.h2
-rw-r--r--libsolidity/parsing/DocStringParser.cpp20
-rw-r--r--libsolidity/parsing/DocStringParser.h2
-rw-r--r--libsolidity/parsing/Parser.cpp43
-rw-r--r--libsolidity/parsing/Parser.h8
86 files changed, 2089 insertions, 1009 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index dc4c6d15..8c2ab347 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -1,68 +1,121 @@
# Until we have a clear separation, libyul has to be included here
set(sources
analysis/ConstantEvaluator.cpp
+ analysis/ConstantEvaluator.h
analysis/ContractLevelChecker.cpp
+ analysis/ContractLevelChecker.h
analysis/ControlFlowAnalyzer.cpp
+ analysis/ControlFlowAnalyzer.h
analysis/ControlFlowBuilder.cpp
+ analysis/ControlFlowBuilder.h
analysis/ControlFlowGraph.cpp
+ analysis/ControlFlowGraph.h
analysis/DeclarationContainer.cpp
+ analysis/DeclarationContainer.h
analysis/DocStringAnalyser.cpp
+ analysis/DocStringAnalyser.h
analysis/GlobalContext.cpp
+ analysis/GlobalContext.h
analysis/NameAndTypeResolver.cpp
+ analysis/NameAndTypeResolver.h
analysis/PostTypeChecker.cpp
+ analysis/PostTypeChecker.h
analysis/ReferencesResolver.cpp
+ analysis/ReferencesResolver.h
analysis/SemVerHandler.cpp
+ analysis/SemVerHandler.h
analysis/StaticAnalyzer.cpp
+ analysis/StaticAnalyzer.h
analysis/SyntaxChecker.cpp
+ analysis/SyntaxChecker.h
analysis/TypeChecker.cpp
+ analysis/TypeChecker.h
analysis/ViewPureChecker.cpp
+ analysis/ViewPureChecker.h
ast/AST.cpp
+ ast/AST.h
+ ast/AST_accept.h
ast/ASTAnnotations.cpp
+ ast/ASTAnnotations.h
+ ast/ASTEnums.h
+ ast/ASTForward.h
ast/ASTJsonConverter.cpp
+ ast/ASTJsonConverter.h
ast/ASTPrinter.cpp
+ ast/ASTPrinter.h
+ ast/ASTVisitor.h
+ ast/ExperimentalFeatures.h
ast/Types.cpp
+ ast/Types.h
codegen/ABIFunctions.cpp
+ codegen/ABIFunctions.h
codegen/ArrayUtils.cpp
+ codegen/ArrayUtils.h
+ codegen/AsmCodeGen.cpp
+ codegen/AsmCodeGen.h
codegen/Compiler.cpp
+ codegen/Compiler.h
codegen/CompilerContext.cpp
+ codegen/CompilerContext.h
codegen/CompilerUtils.cpp
+ codegen/CompilerUtils.h
codegen/ContractCompiler.cpp
+ codegen/ContractCompiler.h
codegen/ExpressionCompiler.cpp
+ codegen/ExpressionCompiler.h
codegen/LValue.cpp
+ codegen/LValue.h
formal/SMTChecker.cpp
+ formal/SMTChecker.h
formal/SMTLib2Interface.cpp
+ formal/SMTLib2Interface.h
formal/SMTPortfolio.cpp
+ formal/SMTPortfolio.h
+ formal/SolverInterface.h
formal/SSAVariable.cpp
+ formal/SSAVariable.h
formal/SymbolicTypes.cpp
+ formal/SymbolicTypes.h
formal/SymbolicVariables.cpp
+ formal/SymbolicVariables.h
formal/VariableUsage.cpp
+ formal/VariableUsage.h
interface/ABI.cpp
+ interface/ABI.h
interface/AssemblyStack.cpp
+ interface/AssemblyStack.h
interface/CompilerStack.cpp
+ interface/CompilerStack.h
interface/GasEstimator.cpp
+ interface/GasEstimator.h
interface/Natspec.cpp
+ interface/Natspec.h
+ interface/ReadFile.h
interface/StandardCompiler.cpp
+ interface/StandardCompiler.h
interface/Version.cpp
+ interface/Version.h
parsing/DocStringParser.cpp
+ parsing/DocStringParser.h
parsing/Parser.cpp
+ parsing/Parser.h
+ parsing/Token.h
)
find_package(Z3 QUIET)
if (${Z3_FOUND})
- include_directories(${Z3_INCLUDE_DIR})
add_definitions(-DHAVE_Z3)
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
- set(z3_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
+ set(z3_SRCS formal/Z3Interface.cpp formal/Z3Interface.h)
else()
set(z3_SRCS)
endif()
find_package(CVC4 QUIET)
if (${CVC4_FOUND})
- include_directories(${CVC4_INCLUDE_DIR})
add_definitions(-DHAVE_CVC4)
message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
- set(cvc4_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
+ set(cvc4_SRCS formal/CVC4Interface.cpp formal/CVC4Interface.h)
else()
set(cvc4_SRCS)
endif()
@@ -76,9 +129,9 @@ add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
if (${Z3_FOUND})
- target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
+ target_link_libraries(solidity PUBLIC Z3::Z3)
endif()
if (${CVC4_FOUND})
- target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES})
+ target_link_libraries(solidity PUBLIC CVC4::CVC4)
endif()
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index 9d041ce5..e637795a 100644
--- a/libsolidity/analysis/ConstantEvaluator.cpp
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/ConstantEvaluator.h>
+
#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
@@ -41,7 +42,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
auto right = type(_operation.rightExpression());
if (left && right)
{
- auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
+ TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
if (!commonType)
m_errorReporter.fatalTypeError(
_operation.location(),
diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp
index 6dc564de..96b9611e 100644
--- a/libsolidity/analysis/ContractLevelChecker.cpp
+++ b/libsolidity/analysis/ContractLevelChecker.cpp
@@ -20,10 +20,9 @@
*/
#include <libsolidity/analysis/ContractLevelChecker.h>
-#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
-
#include <boost/range/adaptor/reversed.hpp>
@@ -227,7 +226,7 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con
return _type->hasEqualParameterTypes(*_funAndFlag.first);
});
if (it == overloads.end())
- overloads.push_back(make_pair(_type, _implemented));
+ overloads.emplace_back(_type, _implemented);
else if (it->second)
{
if (!_implemented)
@@ -409,8 +408,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
auto functionType = make_shared<FunctionType>(*f);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
- externalDeclarations[functionType->externalSignature()].push_back(
- make_pair(f, functionType->asCallableFunction(false))
+ externalDeclarations[functionType->externalSignature()].emplace_back(
+ f, functionType->asCallableFunction(false)
);
}
for (VariableDeclaration const* v: contract->stateVariables())
@@ -419,8 +418,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
auto functionType = make_shared<FunctionType>(*v);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
- externalDeclarations[functionType->externalSignature()].push_back(
- make_pair(v, functionType->asCallableFunction(false))
+ externalDeclarations[functionType->externalSignature()].emplace_back(
+ v, functionType->asCallableFunction(false)
);
}
}
diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h
index 15cbf45d..d754687a 100644
--- a/libsolidity/analysis/ContractLevelChecker.h
+++ b/libsolidity/analysis/ContractLevelChecker.h
@@ -22,7 +22,6 @@
#pragma once
#include <libsolidity/ast/ASTForward.h>
-
#include <map>
namespace langutil
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp
index fe58f0aa..3adf6318 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.cpp
+++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp
@@ -16,7 +16,9 @@
*/
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
+
#include <liblangutil/SourceLocation.h>
+#include <boost/range/algorithm/sort.hpp>
using namespace std;
using namespace langutil;
@@ -33,131 +35,112 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
if (_function.isImplemented())
{
auto const& functionFlow = m_cfg.functionFlow(_function);
- checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit);
+ checkUninitializedAccess(functionFlow.entry, functionFlow.exit);
}
return false;
}
-set<VariableDeclaration const*> ControlFlowAnalyzer::variablesAssignedInNode(CFGNode const *node)
+void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const
{
- set<VariableDeclaration const*> result;
- for (auto expression: node->block.expressions)
+ struct NodeInfo
{
- if (auto const* assignment = dynamic_cast<Assignment const*>(expression))
+ set<VariableDeclaration const*> unassignedVariablesAtEntry;
+ set<VariableDeclaration const*> unassignedVariablesAtExit;
+ set<VariableOccurrence const*> uninitializedVariableAccesses;
+ /// Propagate the information from another node to this node.
+ /// To be used to propagate information from a node to its exit nodes.
+ /// Returns true, if new variables were added and thus the current node has
+ /// to be traversed again.
+ bool propagateFrom(NodeInfo const& _entryNode)
{
- stack<Expression const*> expressions;
- expressions.push(&assignment->leftHandSide());
- while (!expressions.empty())
- {
- Expression const* expression = expressions.top();
- expressions.pop();
-
- if (auto const *tuple = dynamic_cast<TupleExpression const*>(expression))
- for (auto const& component: tuple->components())
- expressions.push(component.get());
- else if (auto const* identifier = dynamic_cast<Identifier const*>(expression))
- if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(
- identifier->annotation().referencedDeclaration
- ))
- result.insert(variableDeclaration);
- }
+ size_t previousUnassignedVariablesAtEntry = unassignedVariablesAtEntry.size();
+ size_t previousUninitializedVariableAccessess = uninitializedVariableAccesses.size();
+ unassignedVariablesAtEntry += _entryNode.unassignedVariablesAtExit;
+ uninitializedVariableAccesses += _entryNode.uninitializedVariableAccesses;
+ return
+ unassignedVariablesAtEntry.size() > previousUnassignedVariablesAtEntry ||
+ uninitializedVariableAccesses.size() > previousUninitializedVariableAccessess
+ ;
}
- }
- return result;
-}
-
-void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
- FunctionDefinition const& _function,
- CFGNode const* _functionEntry,
- CFGNode const* _functionExit
-) const
-{
- if (_function.returnParameterList()->parameters().empty())
- return;
-
- map<CFGNode const*, set<VariableDeclaration const*>> unassigned;
-
- {
- auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
- for (auto const& returnParameter: _function.returnParameterList()->parameters())
- if (
- returnParameter->type()->dataStoredIn(DataLocation::Storage) ||
- returnParameter->type()->category() == Type::Category::Mapping
- )
- unassignedAtFunctionEntry.insert(returnParameter.get());
- }
-
- stack<CFGNode const*> nodesToTraverse;
- nodesToTraverse.push(_functionEntry);
-
- // walk all paths from entry with maximal set of unassigned return values
+ };
+ map<CFGNode const*, NodeInfo> nodeInfos;
+ set<CFGNode const*> nodesToTraverse;
+ nodesToTraverse.insert(_entry);
+
+ // Walk all paths starting from the nodes in ``nodesToTraverse`` until ``NodeInfo::propagateFrom``
+ // returns false for all exits, i.e. until all paths have been walked with maximal sets of unassigned
+ // variables and accesses.
while (!nodesToTraverse.empty())
{
- auto node = nodesToTraverse.top();
- nodesToTraverse.pop();
-
- auto& unassignedAtNode = unassigned[node];
+ CFGNode const* currentNode = *nodesToTraverse.begin();
+ nodesToTraverse.erase(nodesToTraverse.begin());
- if (node->block.returnStatement != nullptr)
- if (node->block.returnStatement->expression())
- unassignedAtNode.clear();
- if (!unassignedAtNode.empty())
+ auto& nodeInfo = nodeInfos[currentNode];
+ auto unassignedVariables = nodeInfo.unassignedVariablesAtEntry;
+ for (auto const& variableOccurrence: currentNode->variableOccurrences)
{
- // kill all return values to which a value is assigned
- for (auto const* variableDeclaration: variablesAssignedInNode(node))
- unassignedAtNode.erase(variableDeclaration);
-
- // kill all return values referenced in inline assembly
- // a reference is enough, checking whether there actually was an assignment might be overkill
- for (auto assembly: node->block.inlineAssemblyStatements)
- for (auto const& ref: assembly->annotation().externalReferences)
- if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
- unassignedAtNode.erase(variableDeclaration);
+ switch (variableOccurrence.kind())
+ {
+ case VariableOccurrence::Kind::Assignment:
+ unassignedVariables.erase(&variableOccurrence.declaration());
+ break;
+ case VariableOccurrence::Kind::InlineAssembly:
+ // We consider all variables referenced in inline assembly as accessed.
+ // So far any reference is enough, but we might want to actually analyze
+ // the control flow in the assembly at some point.
+ case VariableOccurrence::Kind::Access:
+ case VariableOccurrence::Kind::Return:
+ if (unassignedVariables.count(&variableOccurrence.declaration()))
+ {
+ if (variableOccurrence.declaration().type()->dataStoredIn(DataLocation::Storage))
+ // Merely store the unassigned access. We do not generate an error right away, since this
+ // path might still always revert. It is only an error if this is propagated to the exit
+ // node of the function (i.e. there is a path with an uninitialized access).
+ nodeInfo.uninitializedVariableAccesses.insert(&variableOccurrence);
+ }
+ break;
+ case VariableOccurrence::Kind::Declaration:
+ unassignedVariables.insert(&variableOccurrence.declaration());
+ break;
+ }
}
+ nodeInfo.unassignedVariablesAtExit = std::move(unassignedVariables);
- for (auto const& exit: node->exits)
- {
- auto& unassignedAtExit = unassigned[exit];
- auto oldSize = unassignedAtExit.size();
- unassignedAtExit.insert(unassignedAtNode.begin(), unassignedAtNode.end());
- // (re)traverse an exit, if we are on a path with new unassigned return values to consider
- // this will terminate, since there is only a finite number of unassigned return values
- if (unassignedAtExit.size() > oldSize)
- nodesToTraverse.push(exit);
- }
+ // Propagate changes to all exits and queue them for traversal, if needed.
+ for (auto const& exit: currentNode->exits)
+ if (nodeInfos[exit].propagateFrom(nodeInfo))
+ nodesToTraverse.insert(exit);
}
- if (!unassigned[_functionExit].empty())
+ auto const& exitInfo = nodeInfos[_exit];
+ if (!exitInfo.uninitializedVariableAccesses.empty())
{
- vector<VariableDeclaration const*> unassignedOrdered(
- unassigned[_functionExit].begin(),
- unassigned[_functionExit].end()
- );
- sort(
- unassignedOrdered.begin(),
- unassignedOrdered.end(),
- [](VariableDeclaration const* lhs, VariableDeclaration const* rhs) -> bool {
- return lhs->id() < rhs->id();
+ vector<VariableOccurrence const*> uninitializedAccessesOrdered(
+ exitInfo.uninitializedVariableAccesses.begin(),
+ exitInfo.uninitializedVariableAccesses.end()
+ );
+ boost::range::sort(
+ uninitializedAccessesOrdered,
+ [](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool
+ {
+ return *lhs < *rhs;
}
);
- for (auto const* returnVal: unassignedOrdered)
+
+ for (auto const* variableOccurrence: uninitializedAccessesOrdered)
{
SecondarySourceLocation ssl;
- for (CFGNode* lastNodeBeforeExit: _functionExit->entries)
- if (unassigned[lastNodeBeforeExit].count(returnVal))
- {
- if (!!lastNodeBeforeExit->block.returnStatement)
- ssl.append("Problematic return:", lastNodeBeforeExit->block.returnStatement->location());
- else
- ssl.append("Problematic end of function:", _function.location());
- }
+ if (variableOccurrence->occurrence())
+ ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
m_errorReporter.typeError(
- returnVal->location(),
+ variableOccurrence->occurrence() ?
+ variableOccurrence->occurrence()->location() :
+ variableOccurrence->declaration().location(),
ssl,
- "This variable is of storage pointer type and might be returned without assignment and "
- "could be used uninitialized. Assign the variable (potentially from itself) "
- "to fix this error."
+ string("This variable is of storage pointer type and can be ") +
+ (variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") +
+ " without prior assignment."
);
}
}
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h
index 411d57ff..7761817a 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.h
+++ b/libsolidity/analysis/ControlFlowAnalyzer.h
@@ -18,7 +18,6 @@
#pragma once
#include <libsolidity/analysis/ControlFlowGraph.h>
-
#include <set>
namespace dev
@@ -37,12 +36,8 @@ public:
bool visit(FunctionDefinition const& _function) override;
private:
- static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node);
- void checkUnassignedStorageReturnValues(
- FunctionDefinition const& _function,
- CFGNode const* _functionEntry,
- CFGNode const* _functionExit
- ) const;
+ /// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit.
+ void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const;
CFG const& m_cfg;
langutil::ErrorReporter& m_errorReporter;
diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp
index 5bd39da3..3dab8b16 100644
--- a/libsolidity/analysis/ControlFlowBuilder.cpp
+++ b/libsolidity/analysis/ControlFlowBuilder.cpp
@@ -22,7 +22,10 @@ using namespace solidity;
using namespace std;
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
- m_nodeContainer(_nodeContainer), m_currentFunctionFlow(_functionFlow), m_currentNode(_functionFlow.entry)
+ m_nodeContainer(_nodeContainer),
+ m_currentNode(_functionFlow.entry),
+ m_returnNode(_functionFlow.exit),
+ m_revertNode(_functionFlow.revert)
{
}
@@ -37,26 +40,8 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
functionFlow->revert = _nodeContainer.newNode();
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
builder.appendControlFlow(_function);
- connect(builder.m_currentNode, functionFlow->exit);
- return functionFlow;
-}
-
-unique_ptr<ModifierFlow> ControlFlowBuilder::createModifierFlow(
- CFG::NodeContainer& _nodeContainer,
- ModifierDefinition const& _modifier
-)
-{
- auto modifierFlow = unique_ptr<ModifierFlow>(new ModifierFlow());
- modifierFlow->entry = _nodeContainer.newNode();
- modifierFlow->exit = _nodeContainer.newNode();
- modifierFlow->revert = _nodeContainer.newNode();
- modifierFlow->placeholderEntry = _nodeContainer.newNode();
- modifierFlow->placeholderExit = _nodeContainer.newNode();
- ControlFlowBuilder builder(_nodeContainer, *modifierFlow);
- builder.appendControlFlow(_modifier);
- connect(builder.m_currentNode, modifierFlow->exit);
- return modifierFlow;
+ return functionFlow;
}
bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
@@ -219,64 +204,24 @@ bool ControlFlowBuilder::visit(Continue const&)
bool ControlFlowBuilder::visit(Throw const&)
{
solAssert(!!m_currentNode, "");
- solAssert(!!m_currentFunctionFlow.revert, "");
- connect(m_currentNode, m_currentFunctionFlow.revert);
+ solAssert(!!m_revertNode, "");
+ connect(m_currentNode, m_revertNode);
m_currentNode = newLabel();
return false;
}
-bool ControlFlowBuilder::visit(Block const&)
-{
- solAssert(!!m_currentNode, "");
- createLabelHere();
- return true;
-}
-
-void ControlFlowBuilder::endVisit(Block const&)
-{
- solAssert(!!m_currentNode, "");
- createLabelHere();
-}
-
-bool ControlFlowBuilder::visit(Return const& _return)
-{
- solAssert(!!m_currentNode, "");
- solAssert(!!m_currentFunctionFlow.exit, "");
- solAssert(!m_currentNode->block.returnStatement, "");
- m_currentNode->block.returnStatement = &_return;
- connect(m_currentNode, m_currentFunctionFlow.exit);
- m_currentNode = newLabel();
- return true;
-}
-
-
bool ControlFlowBuilder::visit(PlaceholderStatement const&)
{
solAssert(!!m_currentNode, "");
- auto modifierFlow = dynamic_cast<ModifierFlow const*>(&m_currentFunctionFlow);
- solAssert(!!modifierFlow, "");
-
- connect(m_currentNode, modifierFlow->placeholderEntry);
+ solAssert(!!m_placeholderEntry, "");
+ solAssert(!!m_placeholderExit, "");
+ connect(m_currentNode, m_placeholderEntry);
m_currentNode = newLabel();
-
- connect(modifierFlow->placeholderExit, m_currentNode);
+ connect(m_placeholderExit, m_currentNode);
return false;
}
-bool ControlFlowBuilder::visitNode(ASTNode const& node)
-{
- solAssert(!!m_currentNode, "");
- if (auto const* expression = dynamic_cast<Expression const*>(&node))
- m_currentNode->block.expressions.emplace_back(expression);
- else if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(&node))
- m_currentNode->block.variableDeclarations.emplace_back(variableDeclaration);
- else if (auto const* assembly = dynamic_cast<InlineAssembly const*>(&node))
- m_currentNode->block.inlineAssemblyStatements.emplace_back(assembly);
-
- return true;
-}
-
bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
{
solAssert(!!m_currentNode, "");
@@ -286,19 +231,19 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
switch (functionType->kind())
{
case FunctionType::Kind::Revert:
- solAssert(!!m_currentFunctionFlow.revert, "");
+ solAssert(!!m_revertNode, "");
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
- connect(m_currentNode, m_currentFunctionFlow.revert);
+ connect(m_currentNode, m_revertNode);
m_currentNode = newLabel();
return false;
case FunctionType::Kind::Require:
case FunctionType::Kind::Assert:
{
- solAssert(!!m_currentFunctionFlow.revert, "");
+ solAssert(!!m_revertNode, "");
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
- connect(m_currentNode, m_currentFunctionFlow.revert);
+ connect(m_currentNode, m_revertNode);
auto nextNode = newLabel();
connect(m_currentNode, nextNode);
m_currentNode = nextNode;
@@ -310,6 +255,183 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
return ASTConstVisitor::visit(_functionCall);
}
+bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
+{
+ if (auto arguments = _modifierInvocation.arguments())
+ for (auto& argument: *arguments)
+ appendControlFlow(*argument);
+
+ auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
+ _modifierInvocation.name()->annotation().referencedDeclaration
+ );
+ if (!modifierDefinition) return false;
+ solAssert(!!modifierDefinition, "");
+ solAssert(!!m_returnNode, "");
+
+ m_placeholderEntry = newLabel();
+ m_placeholderExit = newLabel();
+
+ appendControlFlow(*modifierDefinition);
+ connect(m_currentNode, m_returnNode);
+
+ m_currentNode = m_placeholderEntry;
+ m_returnNode = m_placeholderExit;
+
+ m_placeholderEntry = nullptr;
+ m_placeholderExit = nullptr;
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
+{
+ for (auto const& parameter: _functionDefinition.parameters())
+ appendControlFlow(*parameter);
+
+ for (auto const& returnParameter: _functionDefinition.returnParameters())
+ {
+ appendControlFlow(*returnParameter);
+ m_returnNode->variableOccurrences.emplace_back(
+ *returnParameter,
+ VariableOccurrence::Kind::Return,
+ nullptr
+ );
+
+ }
+
+ for (auto const& modifier: _functionDefinition.modifiers())
+ appendControlFlow(*modifier);
+
+ appendControlFlow(_functionDefinition.body());
+
+ connect(m_currentNode, m_returnNode);
+ m_currentNode = nullptr;
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Return const& _return)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!m_returnNode, "");
+ if (_return.expression())
+ {
+ appendControlFlow(*_return.expression());
+ // Returns with return expression are considered to be assignments to the return parameters.
+ for (auto returnParameter: _return.annotation().functionReturnParameters->parameters())
+ m_currentNode->variableOccurrences.emplace_back(
+ *returnParameter,
+ VariableOccurrence::Kind::Assignment,
+ &_return
+ );
+ }
+ connect(m_currentNode, m_returnNode);
+ m_currentNode = newLabel();
+ return true;
+}
+
+bool ControlFlowBuilder::visit(FunctionTypeName const&)
+{
+ // Do not visit the parameters and return values of a function type name.
+ // We do not want to consider them as variable declarations for the control flow graph.
+ return false;
+}
+
+bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly)
+{
+ solAssert(!!m_currentNode, "");
+ for (auto const& ref: _inlineAssembly.annotation().externalReferences)
+ {
+ if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
+ m_currentNode->variableOccurrences.emplace_back(
+ *variableDeclaration,
+ VariableOccurrence::Kind::InlineAssembly,
+ &_inlineAssembly
+ );
+ }
+ return true;
+}
+
+bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
+{
+ solAssert(!!m_currentNode, "");
+
+ m_currentNode->variableOccurrences.emplace_back(
+ _variableDeclaration,
+ VariableOccurrence::Kind::Declaration,
+ nullptr
+ );
+
+ // Handle declaration with immediate assignment.
+ if (_variableDeclaration.value())
+ m_currentNode->variableOccurrences.emplace_back(
+ _variableDeclaration,
+ VariableOccurrence::Kind::Assignment,
+ _variableDeclaration.value().get()
+ );
+ // Function arguments are considered to be immediately assigned as well (they are "externally assigned").
+ else if (_variableDeclaration.isCallableParameter() && !_variableDeclaration.isReturnParameter())
+ m_currentNode->variableOccurrences.emplace_back(
+ _variableDeclaration,
+ VariableOccurrence::Kind::Assignment,
+ nullptr
+ );
+ return true;
+}
+
+bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
+{
+ solAssert(!!m_currentNode, "");
+
+ for (auto const& var: _variableDeclarationStatement.declarations())
+ if (var)
+ var->accept(*this);
+ if (_variableDeclarationStatement.initialValue())
+ {
+ _variableDeclarationStatement.initialValue()->accept(*this);
+ for (size_t i = 0; i < _variableDeclarationStatement.declarations().size(); i++)
+ if (auto const& var = _variableDeclarationStatement.declarations()[i])
+ {
+ auto expression = _variableDeclarationStatement.initialValue();
+ if (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
+ if (tupleExpression->components().size() > 1)
+ {
+ solAssert(tupleExpression->components().size() > i, "");
+ expression = tupleExpression->components()[i].get();
+ }
+ while (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
+ if (tupleExpression->components().size() == 1)
+ expression = tupleExpression->components().front().get();
+ else
+ break;
+ m_currentNode->variableOccurrences.emplace_back(
+ *var,
+ VariableOccurrence::Kind::Assignment,
+ expression
+ );
+ }
+ }
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Identifier const& _identifier)
+{
+ solAssert(!!m_currentNode, "");
+
+ if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
+ m_currentNode->variableOccurrences.emplace_back(
+ *variableDeclaration,
+ static_cast<Expression const&>(_identifier).annotation().lValueRequested ?
+ VariableOccurrence::Kind::Assignment :
+ VariableOccurrence::Kind::Access,
+ &_identifier
+ );
+
+ return true;
+}
+
+
+
void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
{
_node.accept(*this);
diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h
index 40605e00..f196e5fc 100644
--- a/libsolidity/analysis/ControlFlowBuilder.h
+++ b/libsolidity/analysis/ControlFlowBuilder.h
@@ -38,14 +38,11 @@ public:
CFG::NodeContainer& _nodeContainer,
FunctionDefinition const& _function
);
- static std::unique_ptr<ModifierFlow> createModifierFlow(
- CFG::NodeContainer& _nodeContainer,
- ModifierDefinition const& _modifier
- );
private:
explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
+ // Visits for constructing the control flow.
bool visit(BinaryOperation const& _operation) override;
bool visit(Conditional const& _conditional) override;
bool visit(IfStatement const& _ifStatement) override;
@@ -54,12 +51,20 @@ private:
bool visit(Break const&) override;
bool visit(Continue const&) override;
bool visit(Throw const&) override;
- bool visit(Block const&) override;
- void endVisit(Block const&) override;
- bool visit(Return const& _return) override;
bool visit(PlaceholderStatement const&) override;
bool visit(FunctionCall const& _functionCall) override;
+ bool visit(ModifierInvocation const& _modifierInvocation) override;
+
+ // Visits for constructing the control flow as well as filling variable occurrences.
+ bool visit(FunctionDefinition const& _functionDefinition) override;
+ bool visit(Return const& _return) override;
+ // Visits for filling variable occurrences.
+ bool visit(FunctionTypeName const& _functionTypeName) override;
+ bool visit(InlineAssembly const& _inlineAssembly) override;
+ bool visit(VariableDeclaration const& _variableDeclaration) override;
+ bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
+ bool visit(Identifier const& _identifier) override;
/// Appends the control flow of @a _node to the current control flow.
void appendControlFlow(ASTNode const& _node);
@@ -73,9 +78,6 @@ private:
static void connect(CFGNode* _from, CFGNode* _to);
-protected:
- bool visitNode(ASTNode const& node) override;
-
private:
/// Splits the control flow starting at the current node into n paths.
@@ -114,17 +116,18 @@ private:
CFG::NodeContainer& m_nodeContainer;
- /// The control flow of the function that is currently parsed.
- /// Note: this can also be a ModifierFlow
- FunctionFlow const& m_currentFunctionFlow;
-
CFGNode* m_currentNode = nullptr;
+ CFGNode* m_returnNode = nullptr;
+ CFGNode* m_revertNode = nullptr;
/// The current jump destination of break Statements.
CFGNode* m_breakJump = nullptr;
/// The current jump destination of continue Statements.
CFGNode* m_continueJump = nullptr;
+ CFGNode* m_placeholderEntry = nullptr;
+ CFGNode* m_placeholderExit = nullptr;
+
/// Helper class that replaces the break and continue jump destinations for the
/// current scope and restores the originals at the end of the scope.
class BreakContinueScope
diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp
index b8860158..8960166a 100644
--- a/libsolidity/analysis/ControlFlowGraph.cpp
+++ b/libsolidity/analysis/ControlFlowGraph.cpp
@@ -16,10 +16,9 @@
*/
#include <libsolidity/analysis/ControlFlowGraph.h>
-#include <libsolidity/analysis/ControlFlowBuilder.h>
+#include <libsolidity/analysis/ControlFlowBuilder.h>
#include <boost/range/adaptor/reversed.hpp>
-
#include <algorithm>
using namespace std;
@@ -29,20 +28,14 @@ using namespace dev::solidity;
bool CFG::constructFlow(ASTNode const& _astRoot)
{
_astRoot.accept(*this);
- applyModifiers();
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
-bool CFG::visit(ModifierDefinition const& _modifier)
-{
- m_modifierControlFlow[&_modifier] = ControlFlowBuilder::createModifierFlow(m_nodeContainer, _modifier);
- return false;
-}
-
bool CFG::visit(FunctionDefinition const& _function)
{
- m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
+ if (_function.isImplemented())
+ m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
return false;
}
@@ -57,81 +50,3 @@ CFGNode* CFG::NodeContainer::newNode()
m_nodes.emplace_back(new CFGNode());
return m_nodes.back().get();
}
-
-void CFG::applyModifiers()
-{
- for (auto const& function: m_functionControlFlow)
- {
- for (auto const& modifierInvocation: boost::adaptors::reverse(function.first->modifiers()))
- {
- if (auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
- modifierInvocation->name()->annotation().referencedDeclaration
- ))
- {
- solAssert(m_modifierControlFlow.count(modifierDefinition), "");
- applyModifierFlowToFunctionFlow(*m_modifierControlFlow[modifierDefinition], function.second.get());
- }
- }
- }
-}
-
-void CFG::applyModifierFlowToFunctionFlow(
- ModifierFlow const& _modifierFlow,
- FunctionFlow* _functionFlow
-)
-{
- solAssert(!!_functionFlow, "");
-
- map<CFGNode*, CFGNode*> copySrcToCopyDst;
-
- // inherit the revert node of the function
- copySrcToCopyDst[_modifierFlow.revert] = _functionFlow->revert;
-
- // replace the placeholder nodes by the function entry and exit
- copySrcToCopyDst[_modifierFlow.placeholderEntry] = _functionFlow->entry;
- copySrcToCopyDst[_modifierFlow.placeholderExit] = _functionFlow->exit;
-
- stack<CFGNode*> nodesToCopy;
- nodesToCopy.push(_modifierFlow.entry);
-
- // map the modifier entry to a new node that will become the new function entry
- copySrcToCopyDst[_modifierFlow.entry] = m_nodeContainer.newNode();
-
- while (!nodesToCopy.empty())
- {
- CFGNode* copySrcNode = nodesToCopy.top();
- nodesToCopy.pop();
-
- solAssert(copySrcToCopyDst.count(copySrcNode), "");
-
- CFGNode* copyDstNode = copySrcToCopyDst[copySrcNode];
-
- copyDstNode->block = copySrcNode->block;
- for (auto const& entry: copySrcNode->entries)
- {
- if (!copySrcToCopyDst.count(entry))
- {
- copySrcToCopyDst[entry] = m_nodeContainer.newNode();
- nodesToCopy.push(entry);
- }
- copyDstNode->entries.emplace_back(copySrcToCopyDst[entry]);
- }
- for (auto const& exit: copySrcNode->exits)
- {
- if (!copySrcToCopyDst.count(exit))
- {
- copySrcToCopyDst[exit] = m_nodeContainer.newNode();
- nodesToCopy.push(exit);
- }
- copyDstNode->exits.emplace_back(copySrcToCopyDst[exit]);
- }
- }
-
- // if the modifier control flow never reached its exit node,
- // we need to create a new (disconnected) exit node now
- if (!copySrcToCopyDst.count(_modifierFlow.exit))
- copySrcToCopyDst[_modifierFlow.exit] = m_nodeContainer.newNode();
-
- _functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry];
- _functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit];
-}
diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h
index 8fe9fe8e..cc0113d8 100644
--- a/libsolidity/analysis/ControlFlowGraph.h
+++ b/libsolidity/analysis/ControlFlowGraph.h
@@ -31,25 +31,57 @@ namespace dev
namespace solidity
{
-/** Basic Control Flow Block.
- * Basic block of control flow. Consists of a set of (unordered) AST nodes
- * for which control flow is always linear. A basic control flow block
- * encompasses at most one scope. Reverts are considered to break the control
- * flow.
- * @todo Handle function calls correctly. So far function calls are not considered
- * to change the control flow.
- */
-struct ControlFlowBlock
+/** Occurrence of a variable in a block of control flow.
+ * Stores the declaration of the referenced variable, the
+ * kind of the occurrence and possibly the node at which
+ * it occurred.
+ */
+class VariableOccurrence
{
- /// All variable declarations inside this control flow block.
- std::vector<VariableDeclaration const*> variableDeclarations;
- /// All expressions inside this control flow block (this includes all subexpressions!).
- std::vector<Expression const*> expressions;
- /// All inline assembly statements inside in this control flow block.
- std::vector<InlineAssembly const*> inlineAssemblyStatements;
- /// If control flow returns in this node, the return statement is stored in returnStatement,
- /// otherwise returnStatement is nullptr.
- Return const* returnStatement = nullptr;
+public:
+ enum class Kind
+ {
+ Declaration,
+ Access,
+ Return,
+ Assignment,
+ InlineAssembly
+ };
+ VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, ASTNode const* _occurrence):
+ m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence)
+ {
+ }
+
+ /// Defines a deterministic order on variable occurrences.
+ bool operator<(VariableOccurrence const& _rhs) const
+ {
+ if (m_occurrence && _rhs.m_occurrence)
+ {
+ if (m_occurrence->id() < _rhs.m_occurrence->id()) return true;
+ if (_rhs.m_occurrence->id() < m_occurrence->id()) return false;
+ }
+ else if (_rhs.m_occurrence)
+ return true;
+ else if (m_occurrence)
+ return false;
+
+ using KindCompareType = std::underlying_type<VariableOccurrence::Kind>::type;
+ return
+ std::make_pair(m_declaration.id(), static_cast<KindCompareType>(m_occurrenceKind)) <
+ std::make_pair(_rhs.m_declaration.id(), static_cast<KindCompareType>(_rhs.m_occurrenceKind))
+ ;
+ }
+
+ VariableDeclaration const& declaration() const { return m_declaration; }
+ Kind kind() const { return m_occurrenceKind; };
+ ASTNode const* occurrence() const { return m_occurrence; }
+private:
+ /// Declaration of the occurring variable.
+ VariableDeclaration const& m_declaration;
+ /// Kind of occurrence.
+ Kind m_occurrenceKind = Kind::Access;
+ /// AST node at which the variable occurred, if available (may be nullptr).
+ ASTNode const* m_occurrence = nullptr;
};
/** Node of the Control Flow Graph.
@@ -64,14 +96,15 @@ struct CFGNode
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
std::vector<CFGNode*> exits;
- /// Control flow in the node.
- ControlFlowBlock block;
+ /// Variable occurrences in the node.
+ std::vector<VariableOccurrence> variableOccurrences;
};
/** Describes the control flow of a function. */
struct FunctionFlow
{
- virtual ~FunctionFlow() {}
+ virtual ~FunctionFlow() = default;
+
/// Entry node. Control flow of the function starts here.
/// This node is empty and does not have any entries.
CFGNode* entry = nullptr;
@@ -85,19 +118,6 @@ struct FunctionFlow
CFGNode* revert = nullptr;
};
-/** Describes the control flow of a modifier.
- * Every placeholder breaks the control flow. The node preceding the
- * placeholder is assigned placeholderEntry as exit and the node
- * following the placeholder is assigned placeholderExit as entry.
- */
-struct ModifierFlow: FunctionFlow
-{
- /// Control flow leading towards a placeholder exit in placeholderEntry.
- CFGNode* placeholderEntry = nullptr;
- /// Control flow coming from a placeholder enter from placeholderExit.
- CFGNode* placeholderExit = nullptr;
-};
-
class CFG: private ASTConstVisitor
{
public:
@@ -105,7 +125,6 @@ public:
bool constructFlow(ASTNode const& _astRoot);
- bool visit(ModifierDefinition const& _modifier) override;
bool visit(FunctionDefinition const& _function) override;
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
@@ -118,20 +137,6 @@ public:
std::vector<std::unique_ptr<CFGNode>> m_nodes;
};
private:
- /// Initially the control flow for all functions *ignoring* modifiers and for
- /// all modifiers is constructed. Afterwards the control flow of functions
- /// is adjusted by applying all modifiers.
- void applyModifiers();
-
- /// Creates a copy of the modifier flow @a _modifierFlow, while replacing the
- /// placeholder entry and exit with the function entry and exit, as well as
- /// replacing the modifier revert node with the function's revert node.
- /// The resulting control flow is the new function flow with the modifier applied.
- /// @a _functionFlow is updated in-place.
- void applyModifierFlowToFunctionFlow(
- ModifierFlow const& _modifierFlow,
- FunctionFlow* _functionFlow
- );
langutil::ErrorReporter& m_errorReporter;
@@ -141,7 +146,6 @@ private:
NodeContainer m_nodeContainer;
std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
- std::map<ModifierDefinition const*, std::unique_ptr<ModifierFlow>> m_modifierControlFlow;
};
}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index cf12a49d..d0657898 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/DeclarationContainer.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
#include <libdevcore/StringUtils.h>
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 9d7a17a3..e26f5891 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -22,11 +22,10 @@
#pragma once
+#include <libsolidity/ast/ASTForward.h>
+#include <boost/noncopyable.hpp>
#include <map>
#include <set>
-#include <boost/noncopyable.hpp>
-
-#include <libsolidity/ast/ASTForward.h>
namespace dev
{
diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp
index 69a7a43c..0878b550 100644
--- a/libsolidity/analysis/DocStringAnalyser.cpp
+++ b/libsolidity/analysis/DocStringAnalyser.cpp
@@ -22,9 +22,10 @@
*/
#include <libsolidity/analysis/DocStringAnalyser.h>
+
#include <libsolidity/ast/AST.h>
-#include <liblangutil/ErrorReporter.h>
#include <libsolidity/parsing/DocStringParser.h>
+#include <liblangutil/ErrorReporter.h>
using namespace std;
using namespace dev;
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index cba2655c..cd5fe07d 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -21,10 +21,11 @@
* Container of the (implicit and explicit) global objects.
*/
-#include <memory>
#include <libsolidity/analysis/GlobalContext.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
+#include <memory>
using namespace std;
diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h
index 4ed08711..09611c41 100644
--- a/libsolidity/analysis/GlobalContext.h
+++ b/libsolidity/analysis/GlobalContext.h
@@ -22,12 +22,12 @@
#pragma once
-#include <string>
-#include <vector>
+#include <libsolidity/ast/ASTForward.h>
+#include <boost/noncopyable.hpp>
#include <map>
#include <memory>
-#include <boost/noncopyable.hpp>
-#include <libsolidity/ast/ASTForward.h>
+#include <string>
+#include <vector>
namespace dev
{
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 0528a200..95bc69fe 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -22,11 +22,10 @@
#include <libsolidity/analysis/NameAndTypeResolver.h>
-#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/StringUtils.h>
-
#include <boost/algorithm/string.hpp>
using namespace std;
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 1b034ef4..89c53932 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -22,13 +22,15 @@
#pragma once
-#include <map>
-#include <list>
-#include <boost/noncopyable.hpp>
#include <libsolidity/analysis/DeclarationContainer.h>
#include <libsolidity/analysis/ReferencesResolver.h>
-#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <list>
+#include <map>
namespace langutil
{
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index 27cbcd45..6a7e5c7e 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -16,15 +16,14 @@
*/
#include <libsolidity/analysis/PostTypeChecker.h>
-#include <libsolidity/ast/AST.h>
+
#include <libsolidity/analysis/SemVerHandler.h>
-#include <liblangutil/ErrorReporter.h>
+#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/Version.h>
-
+#include <liblangutil/ErrorReporter.h>
#include <libdevcore/Algorithms.h>
#include <boost/range/adaptor/map.hpp>
-
#include <memory>
using namespace std;
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index c4931d98..4e6d7f59 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -21,12 +21,15 @@
*/
#include <libsolidity/analysis/ReferencesResolver.h>
-#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
+#include <libsolidity/ast/AST.h>
+
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
+#include <libyul/backends/evm/EVMDialect.h>
+
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
@@ -316,7 +319,14 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// We use the latest EVM version because we will re-run it anyway.
yul::AsmAnalysisInfo analysisInfo;
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
- yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
+ yul::AsmAnalyzer(
+ analysisInfo,
+ errorsIgnored,
+ EVMVersion(),
+ errorTypeForLoose,
+ yul::EVMDialect::looseAssemblyForEVM(),
+ resolver
+ ).analyze(_inlineAssembly.operations());
return false;
}
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
index 32c0553f..b3de9458 100644
--- a/libsolidity/analysis/ReferencesResolver.h
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -22,12 +22,13 @@
#pragma once
-#include <map>
-#include <list>
-#include <boost/noncopyable.hpp>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <boost/noncopyable.hpp>
+#include <list>
+#include <map>
+
namespace langutil
{
class ErrorReporter;
diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp
index 64fa17b3..7c6ba91f 100644
--- a/libsolidity/analysis/SemVerHandler.cpp
+++ b/libsolidity/analysis/SemVerHandler.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/SemVerHandler.h>
+
#include <functional>
using namespace std;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 38391841..aaaa4f9f 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/StaticAnalyzer.h>
+
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
@@ -63,21 +64,21 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
- m_currentFunction = nullptr;
- m_constructor = false;
- for (auto const& var: m_localVarUseCount)
- if (var.second == 0)
- {
- if (var.first.second->isCallableParameter())
- m_errorReporter.warning(
- var.first.second->location(),
- "Unused function parameter. Remove or comment out the variable name to silence this warning."
- );
- else
- m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
- }
-
+ if (m_currentFunction && !m_currentFunction->body().statements().empty())
+ for (auto const& var: m_localVarUseCount)
+ if (var.second == 0)
+ {
+ if (var.first.second->isCallableParameter())
+ m_errorReporter.warning(
+ var.first.second->location(),
+ "Unused function parameter. Remove or comment out the variable name to silence this warning."
+ );
+ else
+ m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
+ }
m_localVarUseCount.clear();
+ m_constructor = false;
+ m_currentFunction = nullptr;
}
bool StaticAnalyzer::visit(Identifier const& _identifier)
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index a73d7e5c..066b5004 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -16,15 +16,18 @@
*/
#include <libsolidity/analysis/SyntaxChecker.h>
-#include <memory>
+
+#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ExperimentalFeatures.h>
-#include <libsolidity/analysis/SemVerHandler.h>
-#include <liblangutil/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
-#include <boost/algorithm/cxx11/all_of.hpp>
+#include <liblangutil/ErrorReporter.h>
+
+#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string.hpp>
+
+#include <memory>
#include <string>
using namespace std;
@@ -111,7 +114,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
SemVerMatchExpressionParser parser(tokens, literals);
auto matchExpression = parser.parse();
- SemVerVersion currentVersion{string(VersionString)};
+ static SemVerVersion const currentVersion{string(VersionString)};
if (!matchExpression.matches(currentVersion))
m_errorReporter.syntaxError(
_pragma.location(),
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 9350df05..507a2c94 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -26,6 +26,7 @@
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/ErrorReporter.h>
@@ -33,8 +34,8 @@
#include <libdevcore/StringUtils.h>
#include <boost/algorithm/cxx11/all_of.hpp>
-#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
#include <memory>
#include <vector>
@@ -658,7 +659,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter,
m_evmVersion,
Error::Type::SyntaxError,
- yul::AsmFlavour::Loose,
+ yul::EVMDialect::looseAssemblyForEVM(),
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
@@ -935,30 +936,32 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
var.accept(*this);
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
{
+ auto errorMsg = "Type " +
+ valueComponentType->toString() +
+ " is not implicitly convertible to expected type " +
+ var.annotation().type->toString();
if (
valueComponentType->category() == Type::Category::RationalNumber &&
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
valueComponentType->mobileType()
)
- m_errorReporter.typeError(
- _statement.location(),
- "Type " +
- valueComponentType->toString() +
- " is not implicitly convertible to expected type " +
- var.annotation().type->toString() +
- ". Try converting to type " +
- valueComponentType->mobileType()->toString() +
- " or use an explicit conversion."
- );
+ {
+ if (var.annotation().type->operator==(*valueComponentType->mobileType()))
+ m_errorReporter.typeError(
+ _statement.location(),
+ errorMsg + ", but it can be explicitly converted."
+ );
+ else
+ m_errorReporter.typeError(
+ _statement.location(),
+ errorMsg +
+ ". Try converting to type " +
+ valueComponentType->mobileType()->toString() +
+ " or use an explicit conversion."
+ );
+ }
else
- m_errorReporter.typeError(
- _statement.location(),
- "Type " +
- valueComponentType->toString() +
- " is not implicitly convertible to expected type " +
- var.annotation().type->toString() +
- "."
- );
+ m_errorReporter.typeError(_statement.location(), errorMsg + ".");
}
}
}
@@ -1252,7 +1255,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
{
TypePointer const& leftType = type(_operation.leftExpression());
TypePointer const& rightType = type(_operation.rightExpression());
- TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ TypeResult result = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ TypePointer commonType = result.get();
if (!commonType)
{
m_errorReporter.typeError(
@@ -1262,7 +1266,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
" not compatible with types " +
leftType->toString() +
" and " +
- rightType->toString()
+ rightType->toString() +
+ (!result.message().empty() ? ". " + result.message() : "")
);
commonType = leftType;
}
@@ -2329,30 +2334,32 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
_expression.accept(*this);
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
{
+ auto errorMsg = "Type " +
+ type(_expression)->toString() +
+ " is not implicitly convertible to expected type " +
+ _expectedType.toString();
if (
type(_expression)->category() == Type::Category::RationalNumber &&
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
type(_expression)->mobileType()
)
- m_errorReporter.typeError(
- _expression.location(),
- "Type " +
- type(_expression)->toString() +
- " is not implicitly convertible to expected type " +
- _expectedType.toString() +
- ". Try converting to type " +
- type(_expression)->mobileType()->toString() +
- " or use an explicit conversion."
- );
+ {
+ if (_expectedType.operator==(*type(_expression)->mobileType()))
+ m_errorReporter.typeError(
+ _expression.location(),
+ errorMsg + ", but it can be explicitly converted."
+ );
+ else
+ m_errorReporter.typeError(
+ _expression.location(),
+ errorMsg +
+ ". Try converting to type " +
+ type(_expression)->mobileType()->toString() +
+ " or use an explicit conversion."
+ );
+ }
else
- m_errorReporter.typeError(
- _expression.location(),
- "Type " +
- type(_expression)->toString() +
- " is not implicitly convertible to expected type " +
- _expectedType.toString() +
- "."
- );
+ m_errorReporter.typeError(_expression.location(), errorMsg + ".");
return false;
}
return true;
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index ebfcdadc..b60c571a 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -24,10 +24,10 @@
#include <liblangutil/EVMVersion.h>
-#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/ast/Types.h>
namespace langutil
{
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 1112d682..eb019481 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -16,14 +16,10 @@
*/
#include <libsolidity/analysis/ViewPureChecker.h>
-
-#include <libevmasm/SemanticInformation.h>
-
#include <libsolidity/ast/ExperimentalFeatures.h>
#include <libyul/AsmData.h>
-
#include <liblangutil/ErrorReporter.h>
-
+#include <libevmasm/SemanticInformation.h>
#include <functional>
using namespace std;
@@ -156,6 +152,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
_funDef.stateMutability() != StateMutability::Payable &&
_funDef.isImplemented() &&
+ !_funDef.body().statements().empty() &&
!_funDef.isConstructor() &&
!_funDef.isFallback() &&
!_funDef.annotation().superFunction
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 3ae6bd6d..f379d758 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -21,13 +21,12 @@
*/
#include <libsolidity/ast/AST.h>
+
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/AST_accept.h>
-
#include <libdevcore/Keccak256.h>
#include <boost/algorithm/string.hpp>
-
#include <algorithm>
#include <functional>
@@ -198,7 +197,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
{
signaturesSeen.insert(functionSignature);
FixedHash<4> hash(dev::keccak256(functionSignature));
- m_interfaceFunctionList->push_back(make_pair(hash, fun));
+ m_interfaceFunctionList->emplace_back(hash, fun);
}
}
}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 2f418b09..9ac065ea 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -22,24 +22,22 @@
#pragma once
-
-#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTEnums.h>
+#include <libsolidity/parsing/Token.h>
#include <liblangutil/SourceLocation.h>
#include <libevmasm/Instruction.h>
-
#include <libdevcore/FixedHash.h>
-#include <json/json.h>
#include <boost/noncopyable.hpp>
+#include <json/json.h>
+#include <memory>
#include <string>
#include <vector>
-#include <memory>
namespace yul
{
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index e9cc905e..d1acf90b 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -27,8 +27,8 @@
#include <map>
#include <memory>
-#include <vector>
#include <set>
+#include <vector>
namespace yul
{
@@ -46,7 +46,7 @@ using TypePointer = std::shared_ptr<Type const>;
struct ASTAnnotation
{
- virtual ~ASTAnnotation() {}
+ virtual ~ASTAnnotation() = default;
};
struct DocTag
@@ -57,7 +57,7 @@ struct DocTag
struct DocumentedAnnotation
{
- virtual ~DocumentedAnnotation() {}
+ virtual ~DocumentedAnnotation() = default;
/// Mapping docstring tag name -> content.
std::multimap<std::string, DocTag> docTags;
};
diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h
index 992fe4cd..f61acad9 100644
--- a/libsolidity/ast/ASTForward.h
+++ b/libsolidity/ast/ASTForward.h
@@ -22,8 +22,8 @@
#pragma once
-#include <string>
#include <memory>
+#include <string>
#include <vector>
// Forward-declare all AST node types
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index cfb13271..f8222598 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -20,11 +20,12 @@
*/
#include <libsolidity/ast/ASTJsonConverter.h>
-#include <boost/algorithm/string/join.hpp>
-#include <libdevcore/UTF8.h>
+
#include <libsolidity/ast/AST.h>
#include <libyul/AsmData.h>
#include <libyul/AsmPrinter.h>
+#include <libdevcore/UTF8.h>
+#include <boost/algorithm/string/join.hpp>
using namespace std;
using namespace langutil;
@@ -234,7 +235,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)),
make_pair("scope", idOrNull(_node.scope()))
};
- attributes.push_back(make_pair("unitAlias", _node.name()));
+ attributes.emplace_back("unitAlias", _node.name());
Json::Value symbolAliases(Json::arrayValue);
for (auto const& symbolAlias: _node.symbolAliases())
{
@@ -244,7 +245,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue;
symbolAliases.append(tuple);
}
- attributes.push_back(make_pair("symbolAliases", std::move(symbolAliases)));
+ attributes.emplace_back("symbolAliases", std::move(symbolAliases));
setJsonNode(_node, "ImportDirective", std::move(attributes));
return false;
}
@@ -357,7 +358,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
};
if (m_inEvent)
- attributes.push_back(make_pair("indexed", _node.isIndexed()));
+ attributes.emplace_back("indexed", _node.isIndexed());
setJsonNode(_node, "VariableDeclaration", std::move(attributes));
return false;
}
@@ -647,11 +648,11 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
};
if (m_legacy)
{
- attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall));
- attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion));
+ attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
+ attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
}
else
- attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind)));
+ attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
appendExpressionAttributes(attributes, _node.annotation());
setJsonNode(_node, "FunctionCall", std::move(attributes));
return false;
@@ -724,7 +725,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
std::vector<pair<string, Json::Value>> attributes = {
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
make_pair("value", value),
- make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(_node.value())),
+ make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(asBytes(_node.value()))),
make_pair(
"subdenomination",
subdenomination == Token::Illegal ?
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index ef0a217a..770e3d9d 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -22,12 +22,13 @@
#pragma once
-#include <ostream>
-#include <stack>
+#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/Exceptions.h>
-#include <libsolidity/ast/ASTAnnotations.h>
+
#include <json/json.h>
+#include <ostream>
+#include <stack>
namespace langutil
{
diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
index cdc6ae7d..d8bafa2c 100644
--- a/libsolidity/ast/ASTPrinter.cpp
+++ b/libsolidity/ast/ASTPrinter.cpp
@@ -21,11 +21,10 @@
*/
#include <libsolidity/ast/ASTPrinter.h>
-#include <libsolidity/ast/AST.h>
-
-#include <json/json.h>
+#include <libsolidity/ast/AST.h>
#include <boost/algorithm/string/join.hpp>
+#include <json/json.h>
using namespace std;
using namespace langutil;
diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h
index de3bf8a2..d762af47 100644
--- a/libsolidity/ast/ASTPrinter.h
+++ b/libsolidity/ast/ASTPrinter.h
@@ -22,9 +22,9 @@
#pragma once
-#include <ostream>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/GasEstimator.h>
+#include <ostream>
namespace dev
{
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
index 1a761032..8cb94e05 100644
--- a/libsolidity/ast/ASTVisitor.h
+++ b/libsolidity/ast/ASTVisitor.h
@@ -22,10 +22,10 @@
#pragma once
-#include <string>
+#include <libsolidity/ast/AST.h>
#include <functional>
+#include <string>
#include <vector>
-#include <libsolidity/ast/AST.h>
namespace dev
{
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 6cadb5f3..cc978b4a 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -24,22 +24,22 @@
#include <libsolidity/ast/AST.h>
-#include <libdevcore/CommonIO.h>
+#include <libdevcore/Algorithms.h>
#include <libdevcore/CommonData.h>
+#include <libdevcore/CommonIO.h>
#include <libdevcore/Keccak256.h>
#include <libdevcore/UTF8.h>
-#include <libdevcore/Algorithms.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/join.hpp>
-#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/range/adaptor/reversed.hpp>
-#include <boost/range/algorithm/copy.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/range/adaptor/transformed.hpp>
-#include <boost/algorithm/string.hpp>
+#include <boost/range/algorithm/copy.hpp>
#include <limits>
@@ -125,6 +125,22 @@ bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2)
return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
}
+/// Checks whether _value fits into IntegerType _type.
+bool fitsIntegerType(bigint const& _value, IntegerType const& _type)
+{
+ return (_type.minValue() <= _value) && (_value <= _type.maxValue());
+}
+
+/// Checks whether _value fits into _bits bits when having 1 bit as the sign bit
+/// if _signed is true.
+bool fitsIntoBits(bigint const& _value, unsigned _bits, bool _signed)
+{
+ return fitsIntegerType(_value, IntegerType(
+ _bits,
+ _signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
+ ));
+}
+
}
void StorageOffsets::computeOffsets(TypePointers const& _types)
@@ -446,7 +462,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
continue;
FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true);
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
- members.push_back(MemberList::Member(function->name(), fun, function));
+ members.emplace_back(function->name(), fun, function);
}
}
return members;
@@ -466,7 +482,7 @@ string AddressType::richIdentifier() const
return "t_address";
}
-bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
+BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _other) const
{
if (_other.category() != category())
return false;
@@ -475,7 +491,7 @@ bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
return other.m_stateMutability <= m_stateMutability;
}
-bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo))
return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
@@ -504,17 +520,16 @@ u256 AddressType::literalValue(Literal const* _literal) const
return u256(_literal->valueWithoutUnderscores());
}
-TypePointer AddressType::unaryOperatorResult(Token _operator) const
+TypeResult AddressType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
- // Addresses can only be compared.
if (!TokenTraits::isCompareOp(_operator))
- return TypePointer();
+ return TypeResult{"Arithmetic operations on addresses are not supported. Convert to integer first before using them."};
return Type::commonType(shared_from_this(), _other);
}
@@ -576,7 +591,7 @@ string IntegerType::richIdentifier() const
return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
}
-bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() == category())
{
@@ -597,7 +612,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() ||
_convertTo.category() == Category::Address ||
@@ -607,18 +622,17 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == Category::FixedPoint;
}
-TypePointer IntegerType::unaryOperatorResult(Token _operator) const
+TypeResult IntegerType::unaryOperatorResult(Token _operator) const
{
// "delete" is ok for all integer types
if (_operator == Token::Delete)
- return make_shared<TupleType>();
- // we allow +, -, ++ and --
- else if (_operator == Token::Add || _operator == Token::Sub ||
- _operator == Token::Inc || _operator == Token::Dec ||
- _operator == Token::BitNot)
- return shared_from_this();
+ return TypeResult{make_shared<TupleType>()};
+ // we allow -, ++ and --
+ else if (_operator == Token::Sub || _operator == Token::Inc ||
+ _operator == Token::Dec || _operator == Token::BitNot)
+ return TypeResult{shared_from_this()};
else
- return TypePointer();
+ return TypeResult{""};
}
bool IntegerType::operator==(Type const& _other) const
@@ -651,7 +665,7 @@ bigint IntegerType::maxValue() const
return (bigint(1) << m_bits) - 1;
}
-TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (
_other->category() != Category::RationalNumber &&
@@ -679,9 +693,8 @@ TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const
return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
{
- // Signed EXP is not allowed
if (Token::Exp == _operator && intType->isSigned())
- return TypePointer();
+ return TypeResult{"Exponentiation is not allowed for signed integer types."};
}
else if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
if (Token::Exp == _operator)
@@ -704,7 +717,7 @@ string FixedPointType::richIdentifier() const
return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits);
}
-bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() == category())
{
@@ -717,18 +730,18 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
}
-TypePointer FixedPointType::unaryOperatorResult(Token _operator) const
+TypeResult FixedPointType::unaryOperatorResult(Token _operator) const
{
switch(_operator)
{
case Token::Delete:
// "delete" is ok for all fixed types
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
case Token::Add:
case Token::Sub:
case Token::Inc:
@@ -771,7 +784,7 @@ bigint FixedPointType::minIntegerValue() const
return bigint(0);
}
-TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
auto commonType = Type::commonType(shared_from_this(), _other);
@@ -957,7 +970,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
return make_tuple(true, value);
}
-bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
switch (_convertTo.category())
{
@@ -966,27 +979,21 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (isFractional())
return false;
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
- if (m_value == rational(0))
- return true;
- unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
- if (m_value > rational(0))
- {
- if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit)))
- return true;
- return false;
- }
- if (targetType.isSigned())
- {
- if (-m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit)))
- return true;
- }
- return false;
+ return fitsIntegerType(m_value.numerator(), targetType);
}
case Category::FixedPoint:
{
- if (auto fixed = fixedPointType())
- return fixed->isImplicitlyConvertibleTo(_convertTo);
- return false;
+ FixedPointType const& targetType = dynamic_cast<FixedPointType const&>(_convertTo);
+ // Store a negative number into an unsigned.
+ if (isNegative() && !targetType.isSigned())
+ return false;
+ if (!isFractional())
+ return (targetType.minIntegerValue() <= m_value) && (m_value <= targetType.maxIntegerValue());
+ rational value = m_value * pow(bigint(10), targetType.fractionalDigits());
+ // Need explicit conversion since truncation will occur.
+ if (value.denominator() != 1)
+ return false;
+ return fitsIntoBits(value.numerator(), targetType.numBits(), targetType.isSigned());
}
case Category::FixedBytes:
return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
@@ -995,7 +1002,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
}
}
-bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (isImplicitlyConvertibleTo(_convertTo))
return true;
@@ -1008,7 +1015,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
+TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const
{
rational value;
switch (_operator)
@@ -1029,10 +1036,10 @@ TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
default:
return TypePointer();
}
- return make_shared<RationalNumberType>(value);
+ return TypeResult(make_shared<RationalNumberType>(value));
}
-TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{
@@ -1129,9 +1136,8 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
- // Limit size to 4096 bits
if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp))
- return TypePointer();
+ return TypeResult{"Precision of rational constants is limited to 4096 bits."};
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
if (_base == 1)
@@ -1212,9 +1218,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
// verify that numerator and denominator fit into 4096 bit after every operation
if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096)
- return TypePointer();
+ return TypeResult{"Precision of rational constants is limited to 4096 bits."};
- return make_shared<RationalNumberType>(value);
+ return TypeResult(make_shared<RationalNumberType>(value));
}
}
@@ -1354,7 +1360,7 @@ StringLiteralType::StringLiteralType(Literal const& _literal):
{
}
-bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
return size_t(fixedBytes->numBytes()) >= m_value.size();
@@ -1409,7 +1415,7 @@ FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes)
);
}
-bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -1417,7 +1423,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return convertTo.m_bytes >= m_bytes;
}
-bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) ||
(_convertTo.category() == Category::Address && numBytes() == 20) ||
@@ -1425,18 +1431,18 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == category();
}
-TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const
+TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const
{
// "delete" and "~" is okay for FixedBytesType
if (_operator == Token::Delete)
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
else if (_operator == Token::BitNot)
return shared_from_this();
return TypePointer();
}
-TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (TokenTraits::isShiftOp(_operator))
{
@@ -1452,7 +1458,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer co
// FixedBytes can be compared and have bitwise operators applied to them
if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
- return commonType;
+ return TypeResult(commonType);
return TypePointer();
}
@@ -1486,14 +1492,14 @@ u256 BoolType::literalValue(Literal const* _literal) const
solAssert(false, "Bool type constructed from non-boolean literal.");
}
-TypePointer BoolType::unaryOperatorResult(Token _operator) const
+TypeResult BoolType::unaryOperatorResult(Token _operator) const
{
if (_operator == Token::Delete)
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
}
-TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (category() != _other->category())
return TypePointer();
@@ -1503,7 +1509,7 @@ TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _
return TypePointer();
}
-bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (*this == _convertTo)
return true;
@@ -1520,7 +1526,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
@@ -1533,14 +1539,14 @@ bool ContractType::isPayable() const
return fallbackFunction && fallbackFunction->isPayable();
}
-TypePointer ContractType::unaryOperatorResult(Token _operator) const
+TypeResult ContractType::unaryOperatorResult(Token _operator) const
{
if (isSuper())
return TypePointer{};
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
+TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
{
if (_operator != Token::Delete)
return TypePointer();
@@ -1551,7 +1557,7 @@ TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
case DataLocation::CallData:
return TypePointer();
case DataLocation::Memory:
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
case DataLocation::Storage:
return m_isPointer ? TypePointer() : make_shared<TupleType>();
}
@@ -1605,7 +1611,7 @@ string ReferenceType::identifierLocationSuffix() const
return id;
}
-bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -1645,7 +1651,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
}
}
-bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
+BoolResult ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
{
if (isImplicitlyConvertibleTo(_convertTo))
return true;
@@ -1815,23 +1821,23 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap members;
if (!isString())
{
- members.push_back({"length", make_shared<IntegerType>(256)});
+ members.emplace_back("length", make_shared<IntegerType>(256));
if (isDynamicallySized() && location() == DataLocation::Storage)
{
- members.push_back({"push", make_shared<FunctionType>(
+ members.emplace_back("push", make_shared<FunctionType>(
TypePointers{baseType()},
TypePointers{make_shared<IntegerType>(256)},
strings{string()},
strings{string()},
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
- )});
- members.push_back({"pop", make_shared<FunctionType>(
+ ));
+ members.emplace_back("pop", make_shared<FunctionType>(
TypePointers{},
TypePointers{},
strings{string()},
strings{string()},
FunctionType::Kind::ArrayPop
- )});
+ ));
}
}
return members;
@@ -1960,21 +1966,17 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
break;
}
if (!functionWithEqualArgumentsFound)
- members.push_back(MemberList::Member(
- function->name(),
- functionType,
- function
- ));
+ members.emplace_back(function->name(), functionType, function);
}
}
else if (!m_contract.isLibrary())
{
for (auto const& it: m_contract.interfaceFunctions())
- members.push_back(MemberList::Member(
+ members.emplace_back(
it.second->declaration().name(),
it.second->asCallableFunction(m_contract.isLibrary()),
&it.second->declaration()
- ));
+ );
}
return members;
}
@@ -2002,11 +2004,11 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
for (size_t index = 0; index < variables.size(); ++index)
if (auto const* offset = offsets.offset(index))
- variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second));
+ variablesAndOffsets.emplace_back(variables[index], offset->first, offset->second);
return variablesAndOffsets;
}
-bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -2092,10 +2094,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
// Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
- members.push_back(MemberList::Member(
+ members.emplace_back(
variable->name(),
copyForLocationIfReference(type),
- variable.get())
+ variable.get()
);
}
return members;
@@ -2249,7 +2251,7 @@ bool StructType::recursive() const
return *m_recursive;
}
-TypePointer EnumType::unaryOperatorResult(Token _operator) const
+TypeResult EnumType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
@@ -2291,7 +2293,7 @@ size_t EnumType::numberOfMembers() const
return m_enum.members().size();
};
-bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo == *this || _convertTo.category() == Category::Integer;
}
@@ -2308,7 +2310,7 @@ unsigned EnumType::memberValue(ASTString const& _member) const
solAssert(false, "Requested unknown enum value " + _member);
}
-bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
+BoolResult TupleType::isImplicitlyConvertibleTo(Type const& _other) const
{
if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
{
@@ -2432,7 +2434,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{
m_parameterTypes.push_back(mappingType->keyType());
- m_parameterNames.push_back("");
+ m_parameterNames.emplace_back("");
returnType = mappingType->valueType();
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
@@ -2441,7 +2443,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
// Return byte arrays as whole.
break;
returnType = arrayType->baseType();
- m_parameterNames.push_back("");
+ m_parameterNames.emplace_back("");
m_parameterTypes.push_back(make_shared<IntegerType>(256));
}
else
@@ -2472,7 +2474,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
DataLocation::Memory,
returnType
));
- m_returnParameterNames.push_back("");
+ m_returnParameterNames.emplace_back("");
}
}
@@ -2648,14 +2650,14 @@ bool FunctionType::operator==(Type const& _other) const
return true;
}
-bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (m_kind == Kind::External && _convertTo == AddressType::address())
return true;
return _convertTo.category() == category();
}
-bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -2680,14 +2682,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true;
}
-TypePointer FunctionType::unaryOperatorResult(Token _operator) const
+TypeResult FunctionType::unaryOperatorResult(Token _operator) const
{
if (_operator == Token::Delete)
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
return TypePointer();
}
-TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
return TypePointer();
@@ -2841,14 +2843,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
- members.push_back(MemberList::Member(
- "selector",
- make_shared<FixedBytesType>(4)
- ));
+ members.emplace_back("selector", make_shared<FixedBytesType>(4));
if (m_kind != Kind::BareDelegateCall)
{
if (isPayable())
- members.push_back(MemberList::Member(
+ members.emplace_back(
"value",
make_shared<FunctionType>(
parseElementaryTypeVector({"uint"}),
@@ -2862,10 +2861,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
m_gasSet,
m_valueSet
)
- ));
+ );
}
if (m_kind != Kind::Creation)
- members.push_back(MemberList::Member(
+ members.emplace_back(
"gas",
make_shared<FunctionType>(
parseElementaryTypeVector({"uint"}),
@@ -2879,7 +2878,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
m_gasSet,
m_valueSet
)
- ));
+ );
return members;
}
default:
@@ -3207,24 +3206,24 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
if (contract.isLibrary())
for (FunctionDefinition const* function: contract.definedFunctions())
if (function->isVisibleAsLibraryMember())
- members.push_back(MemberList::Member(
+ members.emplace_back(
function->name(),
FunctionType(*function).asCallableFunction(true),
function
- ));
+ );
if (isBase)
{
// We are accessing the type of a base contract, so add all public and protected
// members. Note that this does not add inherited functions on purpose.
for (Declaration const* decl: contract.inheritableMembers())
- members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
+ members.emplace_back(decl->name(), decl->type(), decl);
}
else
{
for (auto const& stru: contract.definedStructs())
- members.push_back(MemberList::Member(stru->name(), stru->type(), stru));
+ members.emplace_back(stru->name(), stru->type(), stru);
for (auto const& enu: contract.definedEnums())
- members.push_back(MemberList::Member(enu->name(), enu->type(), enu));
+ members.emplace_back(enu->name(), enu->type(), enu);
}
}
else if (m_actualType->category() == Category::Enum)
@@ -3232,7 +3231,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
auto enumType = make_shared<EnumType>(enumDef);
for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
- members.push_back(MemberList::Member(enumValue->name(), enumType));
+ members.emplace_back(enumValue->name(), enumType);
}
return members;
}
@@ -3297,7 +3296,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap symbols;
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
for (Declaration const* symbol: symbolName.second)
- symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
+ symbols.emplace_back(symbolName.first, symbol->type(), symbol);
return symbols;
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 0f0548d3..bee00661 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -22,22 +22,23 @@
#pragma once
-#include <liblangutil/Exceptions.h>
-#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTEnums.h>
+#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
+#include <libdevcore/Result.h>
+#include <boost/optional.hpp>
#include <boost/noncopyable.hpp>
#include <boost/rational.hpp>
-#include <boost/optional.hpp>
-#include <memory>
-#include <string>
#include <map>
+#include <memory>
#include <set>
+#include <string>
namespace dev
{
@@ -50,6 +51,8 @@ using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
using rational = boost::rational<dev::bigint>;
+using TypeResult = Result<TypePointer>;
+using BoolResult = Result<bool>;
inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
{
@@ -63,6 +66,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato
enum class DataLocation { Storage, CallData, Memory };
+
/**
* Helper class to compute storage offsets of members of structs and contracts.
*/
@@ -189,19 +193,19 @@ public:
/// @returns an escaped identifier (will not contain any parenthesis or commas)
static std::string escapeIdentifier(std::string const& _identifier);
- virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
- virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
+ virtual BoolResult isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
+ virtual BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return isImplicitlyConvertibleTo(_convertTo);
}
/// @returns the resulting type of applying the given unary operator or an empty pointer if
/// this is not possible.
/// The default implementation does not allow any unary operator.
- virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); }
+ virtual TypeResult unaryOperatorResult(Token) const { return TypePointer(); }
/// @returns the resulting type of applying the given binary operator or an empty pointer if
/// this is not possible.
/// The default implementation allows comparison operators if a common type exists
- virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const
+ virtual TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
}
@@ -336,10 +340,10 @@ public:
explicit AddressType(StateMutability _stateMutability);
std::string richIdentifier() const override;
- bool isImplicitlyConvertibleTo(Type const& _other) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
bool operator==(Type const& _other) const override;
@@ -381,10 +385,10 @@ public:
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
std::string richIdentifier() const override;
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
bool operator==(Type const& _other) const override;
@@ -423,10 +427,10 @@ public:
explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
std::string richIdentifier() const override;
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
bool operator==(Type const& _other) const override;
@@ -476,11 +480,10 @@ public:
explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
-
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
@@ -536,8 +539,8 @@ public:
explicit StringLiteralType(Literal const& _literal);
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -570,12 +573,12 @@ public:
explicit FixedBytesType(unsigned _bytes);
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
unsigned storageBytes() const override { return m_bytes; }
@@ -598,11 +601,10 @@ private:
class BoolType: public Type
{
public:
- BoolType() {}
Category category() const override { return Category::Bool; }
std::string richIdentifier() const override { return "t_bool"; }
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
unsigned storageBytes() const override { return 1; }
@@ -624,8 +626,8 @@ public:
explicit ReferenceType(DataLocation _location): m_location(_location) {}
DataLocation location() const { return m_location; }
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -702,8 +704,8 @@ public:
m_length(_length)
{}
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() const override;
bool operator==(const Type& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override;
@@ -757,10 +759,10 @@ public:
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
m_contract(_contract), m_super(_super) {}
/// Contracts can be implicitly converted only to base contracts.
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can only be explicitly converted to address types and base contracts.
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded ) const override
@@ -821,7 +823,7 @@ public:
Category category() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
ReferenceType(_location), m_struct(_struct) {}
- bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override;
@@ -876,7 +878,7 @@ class EnumType: public Type
public:
Category category() const override { return Category::Enum; }
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
- TypePointer unaryOperatorResult(Token _operator) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override
@@ -889,7 +891,7 @@ public:
std::string canonicalName() const override;
bool isValueType() const override { return true; }
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(8 * int(storageBytes()));
@@ -917,10 +919,10 @@ class TupleType: public Type
public:
Category category() const override { return Category::Tuple; }
explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
- bool isImplicitlyConvertibleTo(Type const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
std::string toString(bool) const override;
bool canBeStored() const override { return false; }
u256 storageSize() const override;
@@ -1065,10 +1067,10 @@ public:
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override;
std::string canonicalName() const override;
std::string toString(bool _short) const override;
unsigned calldataEncodedSize(bool _padded) const override;
@@ -1197,7 +1199,7 @@ public:
std::string toString(bool _short) const override;
std::string canonicalName() const override;
bool canLiveOutsideStorage() const override { return false; }
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(256);
@@ -1230,7 +1232,7 @@ public:
explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
TypePointer const& actualType() const { return m_actualType; }
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; }
@@ -1255,7 +1257,7 @@ public:
Category category() const override { return Category::Modifier; }
explicit ModifierType(ModifierDefinition const& _modifier);
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
@@ -1281,7 +1283,7 @@ public:
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; }
@@ -1308,7 +1310,7 @@ public:
explicit MagicType(Kind _kind): m_kind(_kind) {}
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -1339,9 +1341,9 @@ public:
Category category() const override { return Category::InaccessibleDynamic; }
std::string richIdentifier() const override { return "t_inaccessible"; }
- bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
- bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ BoolResult isImplicitlyConvertibleTo(Type const&) const override { return false; }
+ BoolResult isExplicitlyConvertibleTo(Type const&) const override { return false; }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index b02623de..c1ab03e3 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -24,7 +24,6 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerUtils.h>
-
#include <libdevcore/Whiskers.h>
#include <boost/algorithm/string/join.hpp>
@@ -141,8 +140,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
vector<string> valueNamesLocal;
for (size_t j = 0; j < sizeOnStack; j++)
{
- valueNamesLocal.push_back("value" + to_string(stackPos));
- valueReturnParams.push_back("value" + to_string(stackPos));
+ valueNamesLocal.emplace_back("value" + to_string(stackPos));
+ valueReturnParams.emplace_back("value" + to_string(stackPos));
stackPos++;
}
bool dynamic = decodingTypes[i]->isDynamicallyEncoded();
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index d2132258..1e0cf7fa 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -22,14 +22,13 @@
#pragma once
-#include <liblangutil/EVMVersion.h>
-
#include <libsolidity/ast/ASTForward.h>
+#include <liblangutil/EVMVersion.h>
-#include <vector>
#include <functional>
-#include <set>
#include <map>
+#include <set>
+#include <vector>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index 4878f9f3..f9678f7c 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -21,13 +21,15 @@
*/
#include <libsolidity/codegen/ArrayUtils.h>
-#include <libevmasm/Instruction.h>
+
+#include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h>
-#include <libsolidity/ast/Types.h>
-#include <liblangutil/Exceptions.h>
#include <libsolidity/codegen/LValue.h>
+#include <libevmasm/Instruction.h>
+#include <liblangutil/Exceptions.h>
+
using namespace std;
using namespace dev;
using namespace langutil;
diff --git a/libsolidity/codegen/AsmCodeGen.cpp b/libsolidity/codegen/AsmCodeGen.cpp
new file mode 100644
index 00000000..c04c1c34
--- /dev/null
+++ b/libsolidity/codegen/AsmCodeGen.cpp
@@ -0,0 +1,197 @@
+/*
+ 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/>.
+*/
+/**
+ * Adaptor between the abstract assembly and eth assembly.
+ */
+
+#include <libsolidity/codegen/AsmCodeGen.h>
+
+#include <libyul/AsmData.h>
+#include <libyul/AsmAnalysisInfo.h>
+
+#include <libyul/backends/evm/AbstractAssembly.h>
+#include <libyul/backends/evm/EVMCodeTransform.h>
+
+#include <libevmasm/Assembly.h>
+#include <libevmasm/AssemblyItem.h>
+#include <libevmasm/Instruction.h>
+
+#include <liblangutil/SourceLocation.h>
+
+#include <libdevcore/FixedHash.h>
+
+#include <memory>
+#include <functional>
+
+using namespace std;
+using namespace dev;
+using namespace langutil;
+using namespace yul;
+using namespace dev::solidity;
+
+EthAssemblyAdapter::EthAssemblyAdapter(eth::Assembly& _assembly):
+ m_assembly(_assembly)
+{
+}
+
+void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
+{
+ m_assembly.setSourceLocation(_location);
+}
+
+int EthAssemblyAdapter::stackHeight() const
+{
+ return m_assembly.deposit();
+}
+
+void EthAssemblyAdapter::appendInstruction(solidity::Instruction _instruction)
+{
+ m_assembly.append(_instruction);
+}
+
+void EthAssemblyAdapter::appendConstant(u256 const& _constant)
+{
+ m_assembly.append(_constant);
+}
+
+void EthAssemblyAdapter::appendLabel(LabelID _labelId)
+{
+ m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
+}
+
+void EthAssemblyAdapter::appendLabelReference(LabelID _labelId)
+{
+ m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
+}
+
+size_t EthAssemblyAdapter::newLabelId()
+{
+ return assemblyTagToIdentifier(m_assembly.newTag());
+}
+
+size_t EthAssemblyAdapter::namedLabel(std::string const& _name)
+{
+ return assemblyTagToIdentifier(m_assembly.namedTag(_name));
+}
+
+void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol)
+{
+ m_assembly.appendLibraryAddress(_linkerSymbol);
+}
+
+void EthAssemblyAdapter::appendJump(int _stackDiffAfter)
+{
+ appendInstruction(solidity::Instruction::JUMP);
+ m_assembly.adjustDeposit(_stackDiffAfter);
+}
+
+void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter)
+{
+ appendLabelReference(_labelId);
+ appendJump(_stackDiffAfter);
+}
+
+void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId)
+{
+ appendLabelReference(_labelId);
+ appendInstruction(solidity::Instruction::JUMPI);
+}
+
+void EthAssemblyAdapter::appendBeginsub(LabelID, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "BEGINSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendJumpsub(LabelID, int, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "JUMPSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendReturnsub(int, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "RETURNSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendAssemblySize()
+{
+ m_assembly.appendProgramSize();
+}
+
+pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly()
+{
+ shared_ptr<eth::Assembly> assembly{make_shared<eth::Assembly>()};
+ auto sub = m_assembly.newSub(assembly);
+ return {make_shared<EthAssemblyAdapter>(*assembly), size_t(sub.data())};
+}
+
+void EthAssemblyAdapter::appendDataOffset(AbstractAssembly::SubID _sub)
+{
+ auto it = m_dataHashBySubId.find(_sub);
+ if (it == m_dataHashBySubId.end())
+ m_assembly.pushSubroutineOffset(size_t(_sub));
+ else
+ m_assembly << eth::AssemblyItem(eth::PushData, it->second);
+}
+
+void EthAssemblyAdapter::appendDataSize(AbstractAssembly::SubID _sub)
+{
+ auto it = m_dataHashBySubId.find(_sub);
+ if (it == m_dataHashBySubId.end())
+ m_assembly.pushSubroutineSize(size_t(_sub));
+ else
+ m_assembly << u256(m_assembly.data(h256(it->second)).size());
+}
+
+AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data)
+{
+ eth::AssemblyItem pushData = m_assembly.newData(_data);
+ SubID subID = m_nextDataCounter++;
+ m_dataHashBySubId[subID] = pushData.data();
+ return subID;
+}
+
+EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
+{
+ u256 id = _tag.data();
+ solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
+ return LabelID(id);
+}
+
+void CodeGenerator::assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ eth::Assembly& _assembly,
+ ExternalIdentifierAccess const& _identifierAccess,
+ bool _useNamedLabelsForFunctions,
+ bool _optimize
+)
+{
+ EthAssemblyAdapter assemblyAdapter(_assembly);
+ CodeTransform(
+ assemblyAdapter,
+ _analysisInfo,
+ _parsedData,
+ *EVMDialect::strictAssemblyForEVM(),
+ _optimize,
+ false,
+ _identifierAccess,
+ _useNamedLabelsForFunctions
+ )(_parsedData);
+}
diff --git a/libsolidity/codegen/AsmCodeGen.h b/libsolidity/codegen/AsmCodeGen.h
new file mode 100644
index 00000000..516b0a36
--- /dev/null
+++ b/libsolidity/codegen/AsmCodeGen.h
@@ -0,0 +1,92 @@
+/*
+ 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/>.
+*/
+/**
+ * Adaptor between the abstract assembly and eth assembly.
+ */
+
+#pragma once
+
+#include <libyul/AsmAnalysis.h>
+#include <libyul/backends/evm/AbstractAssembly.h>
+#include <liblangutil/SourceLocation.h>
+#include <functional>
+
+namespace yul
+{
+struct Block;
+}
+
+namespace dev
+{
+namespace eth
+{
+class Assembly;
+class AssemblyItem;
+}
+
+namespace solidity
+{
+
+class EthAssemblyAdapter: public yul::AbstractAssembly
+{
+public:
+ explicit EthAssemblyAdapter(eth::Assembly& _assembly);
+ void setSourceLocation(langutil::SourceLocation const& _location) override;
+ int stackHeight() const override;
+ void appendInstruction(solidity::Instruction _instruction) override;
+ void appendConstant(u256 const& _constant) override;
+ void appendLabel(LabelID _labelId) override;
+ void appendLabelReference(LabelID _labelId) override;
+ size_t newLabelId() override;
+ size_t namedLabel(std::string const& _name) override;
+ void appendLinkerSymbol(std::string const& _linkerSymbol) override;
+ void appendJump(int _stackDiffAfter) override;
+ void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override;
+ void appendJumpToIf(LabelID _labelId) override;
+ void appendBeginsub(LabelID, int) override;
+ void appendJumpsub(LabelID, int, int) override;
+ void appendReturnsub(int, int) override;
+ void appendAssemblySize() override;
+ std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
+ void appendDataOffset(SubID _sub) override;
+ void appendDataSize(SubID _sub) override;
+ SubID appendData(dev::bytes const& _data) override;
+
+private:
+ static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag);
+
+ eth::Assembly& m_assembly;
+ std::map<SubID, dev::u256> m_dataHashBySubId;
+ size_t m_nextDataCounter = std::numeric_limits<size_t>::max() / 2;
+};
+
+class CodeGenerator
+{
+public:
+ /// Performs code generation and appends generated to _assembly.
+ static void assemble(
+ yul::Block const& _parsedData,
+ yul::AsmAnalysisInfo& _analysisInfo,
+ dev::eth::Assembly& _assembly,
+ yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(),
+ bool _useNamedLabelsForFunctions = false,
+ bool _optimize = false
+ );
+};
+
+}
+}
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index 55f1d252..a22e6e9d 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -21,8 +21,9 @@
*/
#include <libsolidity/codegen/Compiler.h>
-#include <libevmasm/Assembly.h>
+
#include <libsolidity/codegen/ContractCompiler.h>
+#include <libevmasm/Assembly.h>
using namespace std;
using namespace dev;
@@ -34,13 +35,13 @@ void Compiler::compileContract(
bytes const& _metadata
)
{
- ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
+ ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
runtimeCompiler.compileContract(_contract, _contracts);
m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at
// creation time.
- ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
+ ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1);
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
m_context.optimise(m_optimize, m_optimizeRuns);
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index 48d9e9d6..784d7f8c 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -24,11 +24,9 @@
#include <libsolidity/codegen/CompilerContext.h>
#include <liblangutil/EVMVersion.h>
-
#include <libevmasm/Assembly.h>
-
-#include <ostream>
#include <functional>
+#include <ostream>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 5a3a233c..be681b2e 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -21,18 +21,22 @@
*/
#include <libsolidity/codegen/CompilerContext.h>
-#include <libsolidity/codegen/CompilerUtils.h>
+
#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/AsmCodeGen.h>
#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/interface/Version.h>
-#include <liblangutil/SourceReferenceFormatter.h>
+
#include <libyul/AsmParser.h>
-#include <libyul/AsmCodeGen.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/YulString.h>
+
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h>
+#include <liblangutil/SourceReferenceFormatter.h>
#include <boost/algorithm/string/replace.hpp>
@@ -361,7 +365,7 @@ void CompilerContext::appendInlineAssembly(
ErrorList errors;
ErrorReporter errorReporter(errors);
auto scanner = make_shared<langutil::Scanner>(langutil::CharStream(_assembly, "--CODEGEN--"));
- auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false);
+ auto parserResult = yul::Parser(errorReporter, yul::EVMDialect::strictAssemblyForEVM()).parse(scanner, false);
#ifdef SOL_OUTPUT_ASM
cout << yul::AsmPrinter()(*parserResult) << endl;
#endif
@@ -373,7 +377,7 @@ void CompilerContext::appendInlineAssembly(
errorReporter,
m_evmVersion,
boost::none,
- yul::AsmFlavour::Strict,
+ yul::EVMDialect::strictAssemblyForEVM(),
identifierAccess.resolve
).analyze(*parserResult);
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
@@ -386,8 +390,7 @@ void CompilerContext::appendInlineAssembly(
for (auto const& error: errorReporter.errors())
message += SourceReferenceFormatter::formatExceptionInformation(
*error,
- (error->type() == Error::Type::Warning) ? "Warning" : "Error",
- [&](string const&) -> Scanner const& { return *scanner; }
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error"
);
message += "-------------------------------------------\n";
@@ -395,7 +398,7 @@ void CompilerContext::appendInlineAssembly(
}
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
- yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
+ CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
// Reset the source location to the one of the node (instead of the CODEGEN source location)
updateSourceLocation();
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 02369813..dedcd95f 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -22,24 +22,21 @@
#pragma once
-#include <libsolidity/codegen/ABIFunctions.h>
-
-#include <liblangutil/EVMVersion.h>
-
+#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
-#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/codegen/ABIFunctions.h>
-#include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h>
-
+#include <libevmasm/Instruction.h>
+#include <liblangutil/EVMVersion.h>
#include <libdevcore/Common.h>
+#include <functional>
#include <ostream>
#include <stack>
#include <queue>
#include <utility>
-#include <functional>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 7d2ad9d2..bbc703c7 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -23,12 +23,10 @@
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/codegen/ArrayUtils.h>
#include <libsolidity/codegen/LValue.h>
-#include <libsolidity/codegen/ABIFunctions.h>
-
#include <libevmasm/Instruction.h>
-
#include <libdevcore/Whiskers.h>
using namespace std;
@@ -1205,7 +1203,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
{
//@todo provide both alternatives to the optimiser
// stack: mempos
- if (_data.size() <= 128)
+ if (_data.size() <= 32)
{
for (unsigned i = 0; i < _data.size(); i += 32)
{
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 5f7dce22..7e4f47ba 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -22,8 +22,8 @@
#pragma once
-#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/codegen/CompilerContext.h>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index aabdbb79..b051d260 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -20,19 +20,18 @@
* Solidity compiler.
*/
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/AsmCodeGen.h>
+#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ContractCompiler.h>
#include <libsolidity/codegen/ExpressionCompiler.h>
-#include <libsolidity/codegen/CompilerUtils.h>
-#include <libsolidity/ast/AST.h>
-#include <libyul/AsmCodeGen.h>
-#include <liblangutil/ErrorReporter.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/GasMeter.h>
+#include <liblangutil/ErrorReporter.h>
#include <boost/range/adaptor/reversed.hpp>
-
#include <algorithm>
using namespace std;
@@ -268,6 +267,89 @@ void ContractCompiler::appendDelegatecallCheck()
// "We have not been called via DELEGATECALL".
}
+void ContractCompiler::appendInternalSelector(
+ map<FixedHash<4>, eth::AssemblyItem const> const& _entryPoints,
+ vector<FixedHash<4>> const& _ids,
+ eth::AssemblyItem const& _notFoundTag,
+ size_t _runs
+)
+{
+ // Code for selecting from n functions without split:
+ // n times: dup1, push4 <id_i>, eq, push2/3 <tag_i>, jumpi
+ // push2/3 <notfound> jump
+ // (called SELECT[n])
+ // Code for selecting from n functions with split:
+ // dup1, push4 <pivot>, gt, push2/3<tag_less>, jumpi
+ // SELECT[n/2]
+ // tag_less:
+ // SELECT[n/2]
+ //
+ // This means each split adds 16-18 bytes of additional code (note the additional jump out!)
+ // The average execution cost if we do not split at all are:
+ // (3 + 3 + 3 + 3 + 10) * n/2 = 24 * n/2 = 12 * n
+ // If we split once:
+ // (3 + 3 + 3 + 3 + 10) + 24 * n/4 = 24 * (n/4 + 1) = 6 * n + 24;
+ //
+ // We should split if
+ // _runs * 12 * n > _runs * (6 * n + 24) + 17 * createDataGas
+ // <=> _runs * 6 * (n - 4) > 17 * createDataGas
+ //
+ // Which also means that the execution itself is not profitable
+ // unless we have at least 5 functions.
+
+ // Start with some comparisons to avoid overflow, then do the actual comparison.
+ bool split = false;
+ if (_ids.size() <= 4)
+ split = false;
+ else if (_runs > (17 * eth::GasCosts::createDataGas) / 6)
+ split = true;
+ else
+ split = (_runs * 6 * (_ids.size() - 4) > 17 * eth::GasCosts::createDataGas);
+
+ if (split)
+ {
+ size_t pivotIndex = _ids.size() / 2;
+ FixedHash<4> pivot{_ids.at(pivotIndex)};
+ m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(pivot)) << Instruction::GT;
+ eth::AssemblyItem lessTag{m_context.appendConditionalJump()};
+ // Here, we have funid >= pivot
+ vector<FixedHash<4>> larger{_ids.begin() + pivotIndex, _ids.end()};
+ appendInternalSelector(_entryPoints, larger, _notFoundTag, _runs);
+ m_context << lessTag;
+ // Here, we have funid < pivot
+ vector<FixedHash<4>> smaller{_ids.begin(), _ids.begin() + pivotIndex};
+ appendInternalSelector(_entryPoints, smaller, _notFoundTag, _runs);
+ }
+ else
+ {
+ for (auto const& id: _ids)
+ {
+ m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(id)) << Instruction::EQ;
+ m_context.appendConditionalJumpTo(_entryPoints.at(id));
+ }
+ m_context.appendJumpTo(_notFoundTag);
+ }
+}
+
+namespace
+{
+
+// Helper function to check if any function is payable
+bool hasPayableFunctions(ContractDefinition const& _contract)
+{
+ FunctionDefinition const* fallback = _contract.fallbackFunction();
+ if (fallback && fallback->isPayable())
+ return true;
+
+ for (auto const& it: _contract.interfaceFunctions())
+ if (it.second->isPayable())
+ return true;
+
+ return false;
+}
+
+}
+
void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions();
@@ -279,6 +361,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
}
FunctionDefinition const* fallback = _contract.fallbackFunction();
+ solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have fallback functions");
+
+ bool needToAddCallvalueCheck = true;
+ if (!hasPayableFunctions(_contract) && !interfaceFunctions.empty() && !_contract.isLibrary())
+ {
+ appendCallValueCheck();
+ needToAddCallvalueCheck = false;
+ }
+
eth::AssemblyItem notFound = m_context.newTag();
// directly jump to fallback if the data is too short to contain a function selector
// also guards against short data
@@ -287,22 +378,26 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
// retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty())
+ {
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
- // stack now is: <can-call-non-view-functions>? <funhash>
- for (auto const& it: interfaceFunctions)
- {
- callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
- m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << Instruction::EQ;
- m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
+ // stack now is: <can-call-non-view-functions>? <funhash>
+ vector<FixedHash<4>> sortedIDs;
+ for (auto const& it: interfaceFunctions)
+ {
+ callDataUnpackerEntryPoints.emplace(it.first, m_context.newTag());
+ sortedIDs.emplace_back(it.first);
+ }
+ std::sort(sortedIDs.begin(), sortedIDs.end());
+ appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs);
}
- m_context.appendJumpTo(notFound);
m_context << notFound;
+
if (fallback)
{
solAssert(!_contract.isLibrary(), "");
- if (!fallback->isPayable())
+ if (!fallback->isPayable() && needToAddCallvalueCheck)
appendCallValueCheck();
solAssert(fallback->isFallback(), "");
@@ -332,7 +427,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context.setStackOffset(0);
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
- if (!functionType->isPayable() && !_contract.isLibrary())
+ if (!functionType->isPayable() && !_contract.isLibrary() && needToAddCallvalueCheck)
appendCallValueCheck();
// Return tag is used to jump out of the function.
@@ -618,7 +713,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
}
};
solAssert(_inlineAssembly.annotation().analysisInfo, "");
- yul::CodeGenerator::assemble(
+ CodeGenerator::assemble(
_inlineAssembly.operations(),
*_inlineAssembly.annotation().analysisInfo,
m_context.nonConstAssembly(),
@@ -656,14 +751,14 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
- m_breakTags.push_back({loopEnd, m_context.stackHeight()});
+ m_breakTags.emplace_back(loopEnd, m_context.stackHeight());
m_context << loopStart;
if (_whileStatement.isDoWhile())
{
eth::AssemblyItem condition = m_context.newTag();
- m_continueTags.push_back({condition, m_context.stackHeight()});
+ m_continueTags.emplace_back(condition, m_context.stackHeight());
_whileStatement.body().accept(*this);
@@ -674,7 +769,7 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
}
else
{
- m_continueTags.push_back({loopStart, m_context.stackHeight()});
+ m_continueTags.emplace_back(loopStart, m_context.stackHeight());
compileExpression(_whileStatement.condition());
m_context << Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
@@ -705,8 +800,8 @@ bool ContractCompiler::visit(ForStatement const& _forStatement)
if (_forStatement.initializationExpression())
_forStatement.initializationExpression()->accept(*this);
- m_breakTags.push_back({loopEnd, m_context.stackHeight()});
- m_continueTags.push_back({loopNext, m_context.stackHeight()});
+ m_breakTags.emplace_back(loopEnd, m_context.stackHeight());
+ m_continueTags.emplace_back(loopNext, m_context.stackHeight());
m_context << loopStart;
// if there is no terminating condition in for, default is to always be true
@@ -932,7 +1027,7 @@ void ContractCompiler::appendModifierOrFunctionCode()
if (codeBlock)
{
- m_returnTags.push_back({m_context.newTag(), m_context.stackHeight()});
+ m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
codeBlock->accept(*this);
solAssert(!m_returnTags.empty(), "");
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 001aec7c..40871f0d 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -22,11 +22,11 @@
#pragma once
-#include <ostream>
-#include <functional>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/CompilerContext.h>
#include <libevmasm/Assembly.h>
+#include <functional>
+#include <ostream>
namespace dev {
namespace solidity {
@@ -38,8 +38,9 @@ namespace solidity {
class ContractCompiler: private ASTConstVisitor
{
public:
- explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise):
+ explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200):
m_optimise(_optimise),
+ m_optimise_runs(_optimise_runs),
m_runtimeCompiler(_runtimeCompiler),
m_context(_context)
{
@@ -81,6 +82,14 @@ private:
/// This is done by inserting a specific push constant as the first instruction
/// whose data will be modified in memory at deploy time.
void appendDelegatecallCheck();
+ /// Appends the function selector. Is called recursively to create a binary search tree.
+ /// @a _runs the number of intended executions of the contract to tune the split point.
+ void appendInternalSelector(
+ std::map<FixedHash<4>, eth::AssemblyItem const> const& _entryPoints,
+ std::vector<FixedHash<4>> const& _ids,
+ eth::AssemblyItem const& _notFoundTag,
+ size_t _runs
+ );
void appendFunctionSelector(ContractDefinition const& _contract);
void appendCallValueCheck();
void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary);
@@ -122,6 +131,7 @@ private:
void storeStackHeight(ASTNode const* _node);
bool const m_optimise;
+ size_t const m_optimise_runs = 200;
/// Pointer to the runtime compiler in case this is a creation compiler.
ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context;
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 121585d9..be2709ae 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -20,21 +20,23 @@
* Solidity AST to EVM bytecode compiler for expressions.
*/
-#include <utility>
-#include <numeric>
-#include <boost/range/adaptor/reversed.hpp>
-#include <boost/algorithm/string/replace.hpp>
-#include <libdevcore/Common.h>
-#include <libdevcore/Keccak256.h>
-#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/ExpressionCompiler.h>
+
+#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/LValue.h>
-#include <libevmasm/GasMeter.h>
+#include <libevmasm/GasMeter.h>
+#include <libdevcore/Common.h>
+#include <libdevcore/Keccak256.h>
#include <libdevcore/Whiskers.h>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/range/adaptor/reversed.hpp>
+#include <numeric>
+#include <utility>
+
using namespace std;
using namespace langutil;
@@ -833,10 +835,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::RIPEMD160:
{
_functionCall.expression().accept(*this);
- static const map<FunctionType::Kind, u256> contractAddresses{{FunctionType::Kind::ECRecover, 1},
- {FunctionType::Kind::SHA256, 2},
- {FunctionType::Kind::RIPEMD160, 3}};
- m_context << contractAddresses.find(function.kind())->second;
+ static map<FunctionType::Kind, u256> const contractAddresses{
+ {FunctionType::Kind::ECRecover, 1},
+ {FunctionType::Kind::SHA256, 2},
+ {FunctionType::Kind::RIPEMD160, 3}
+ };
+ m_context << contractAddresses.at(function.kind());
for (unsigned i = function.sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
appendExternalFunctionCall(function, arguments);
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index 2bfaab43..4a6e43ff 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -21,14 +21,17 @@
* Solidity AST to EVM bytecode compiler for expressions.
*/
-#include <functional>
-#include <memory>
-#include <boost/noncopyable.hpp>
-#include <libdevcore/Common.h>
-#include <liblangutil/SourceLocation.h>
+#pragma once
+
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/LValue.h>
#include <liblangutil/Exceptions.h>
+#include <liblangutil/SourceLocation.h>
+#include <libdevcore/Common.h>
+
+#include <boost/noncopyable.hpp>
+#include <functional>
+#include <memory>
namespace dev {
namespace eth
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index 6d71d36f..70dbee81 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -21,10 +21,11 @@
*/
#include <libsolidity/codegen/LValue.h>
-#include <libevmasm/Instruction.h>
-#include <libsolidity/ast/Types.h>
+
#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/CompilerUtils.h>
+#include <libevmasm/Instruction.h>
using namespace std;
using namespace dev;
diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h
index d854857b..3072ff11 100644
--- a/libsolidity/codegen/LValue.h
+++ b/libsolidity/codegen/LValue.h
@@ -22,10 +22,10 @@
#pragma once
+#include <libsolidity/codegen/ArrayUtils.h>
+#include <liblangutil/SourceLocation.h>
#include <memory>
#include <vector>
-#include <liblangutil/SourceLocation.h>
-#include <libsolidity/codegen/ArrayUtils.h>
namespace dev
{
@@ -49,7 +49,7 @@ protected:
m_context(_compilerContext), m_dataType(_dataType) {}
public:
- virtual ~LValue() {}
+ virtual ~LValue() = default;
/// @returns the number of stack slots occupied by the lvalue reference
virtual unsigned sizeOnStack() const { return 1; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp
index de5e4430..e7c8f015 100644
--- a/libsolidity/formal/CVC4Interface.cpp
+++ b/libsolidity/formal/CVC4Interface.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/CVC4Interface.h>
#include <liblangutil/Exceptions.h>
-
#include <libdevcore/CommonIO.h>
using namespace std;
diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h
index bbe23855..89792364 100644
--- a/libsolidity/formal/CVC4Interface.h
+++ b/libsolidity/formal/CVC4Interface.h
@@ -18,7 +18,6 @@
#pragma once
#include <libsolidity/formal/SolverInterface.h>
-
#include <boost/noncopyable.hpp>
#if defined(__GLIBC__)
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index ebb09f0a..35c1e2f1 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -18,11 +18,11 @@
#include <libsolidity/formal/SMTChecker.h>
#include <libsolidity/formal/SMTPortfolio.h>
-
#include <libsolidity/formal/VariableUsage.h>
#include <libsolidity/formal/SymbolicTypes.h>
#include <liblangutil/ErrorReporter.h>
+#include <libdevcore/StringUtils.h>
#include <boost/range/adaptor/map.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -58,8 +58,7 @@ void SMTChecker::analyze(SourceUnit const& _source, shared_ptr<Scanner> const& _
bool SMTChecker::visit(ContractDefinition const& _contract)
{
for (auto _var : _contract.stateVariables())
- if (_var->type()->isValueType())
- createVariable(*_var);
+ createVariable(*_var);
return true;
}
@@ -88,14 +87,14 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
m_interface->reset();
m_pathConditions.clear();
m_expressions.clear();
- m_specialVariables.clear();
- m_uninterpretedFunctions.clear();
+ m_globalContext.clear();
m_uninterpretedTerms.clear();
resetStateVariables();
initializeLocalVariables(_function);
+ m_loopExecutionHappened = false;
+ m_arrayAssignmentHappened = false;
}
- m_loopExecutionHappened = false;
return true;
}
@@ -136,13 +135,24 @@ bool SMTChecker::visit(IfStatement const& _node)
return false;
}
+// Here we consider the execution of two branches:
+// Branch 1 assumes the loop condition to be true and executes the loop once,
+// after resetting touched variables.
+// Branch 2 assumes the loop condition to be false and skips the loop after
+// visiting the condition (it might contain side-effects, they need to be considered)
+// and does not erase knowledge.
+// If the loop is a do-while, condition side-effects are lost since the body,
+// executed once before the condition, might reassign variables.
+// Variables touched by the loop are merged with Branch 2.
bool SMTChecker::visit(WhileStatement const& _node)
{
+ auto indicesBeforeLoop = copyVariableIndices();
auto touchedVariables = m_variableUsage->touchedVariables(_node);
resetVariables(touchedVariables);
+ decltype(indicesBeforeLoop) indicesAfterLoop;
if (_node.isDoWhile())
{
- visitBranch(_node.body());
+ indicesAfterLoop = visitBranch(_node.body());
// TODO the assertions generated in the body should still be active in the condition
_node.condition().accept(*this);
if (isRootFunction())
@@ -154,19 +164,31 @@ bool SMTChecker::visit(WhileStatement const& _node)
if (isRootFunction())
checkBooleanNotConstant(_node.condition(), "While loop condition is always $VALUE.");
- visitBranch(_node.body(), expr(_node.condition()));
+ indicesAfterLoop = visitBranch(_node.body(), expr(_node.condition()));
}
- m_loopExecutionHappened = true;
- resetVariables(touchedVariables);
+ // We reset the execution to before the loop
+ // and visit the condition in case it's not a do-while.
+ // A do-while's body might have non-precise information
+ // in its first run about variables that are touched.
+ resetVariableIndices(indicesBeforeLoop);
+ if (!_node.isDoWhile())
+ _node.condition().accept(*this);
+
+ mergeVariables(touchedVariables, expr(_node.condition()), indicesAfterLoop, copyVariableIndices());
+
+ m_loopExecutionHappened = true;
return false;
}
+// Here we consider the execution of two branches similar to WhileStatement.
bool SMTChecker::visit(ForStatement const& _node)
{
if (_node.initializationExpression())
_node.initializationExpression()->accept(*this);
+ auto indicesBeforeLoop = copyVariableIndices();
+
// Do not reset the init expression part.
auto touchedVariables =
m_variableUsage->touchedVariables(_node.body());
@@ -193,13 +215,19 @@ bool SMTChecker::visit(ForStatement const& _node)
_node.body().accept(*this);
if (_node.loopExpression())
_node.loopExpression()->accept(*this);
-
m_interface->pop();
- m_loopExecutionHappened = true;
+ auto indicesAfterLoop = copyVariableIndices();
+ // We reset the execution to before the loop
+ // and visit the condition.
+ resetVariableIndices(indicesBeforeLoop);
+ if (_node.condition())
+ _node.condition()->accept(*this);
- resetVariables(touchedVariables);
+ auto forCondition = _node.condition() ? expr(*_node.condition()) : smt::Expression(true);
+ mergeVariables(touchedVariables, forCondition, indicesAfterLoop, copyVariableIndices());
+ m_loopExecutionHappened = true;
return false;
}
@@ -237,16 +265,14 @@ void SMTChecker::endVisit(Assignment const& _assignment)
else if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_assignment.leftHandSide()))
{
VariableDeclaration const& decl = dynamic_cast<VariableDeclaration const&>(*identifier->annotation().referencedDeclaration);
- if (knownVariable(decl))
- {
- assignment(decl, _assignment.rightHandSide(), _assignment.location());
- defineExpr(_assignment, expr(_assignment.rightHandSide()));
- }
- else
- m_errorReporter.warning(
- _assignment.location(),
- "Assertion checker does not yet implement such assignments."
- );
+ solAssert(knownVariable(decl), "");
+ assignment(decl, _assignment.rightHandSide(), _assignment.location());
+ defineExpr(_assignment, expr(_assignment.rightHandSide()));
+ }
+ else if (dynamic_cast<IndexAccess const*>(&_assignment.leftHandSide()))
+ {
+ arrayIndexAssignment(_assignment);
+ defineExpr(_assignment, expr(_assignment.rightHandSide()));
}
else
m_errorReporter.warning(
@@ -257,7 +283,11 @@ void SMTChecker::endVisit(Assignment const& _assignment)
void SMTChecker::endVisit(TupleExpression const& _tuple)
{
- if (_tuple.isInlineArray() || _tuple.components().size() != 1)
+ if (
+ _tuple.isInlineArray() ||
+ _tuple.components().size() != 1 ||
+ !isSupportedType(_tuple.components()[0]->annotation().type->category())
+ )
m_errorReporter.warning(
_tuple.location(),
"Assertion checker does not yet implement tuples and inline arrays."
@@ -271,14 +301,14 @@ void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _
checkCondition(
_value < minValue(_type),
_location,
- "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")",
+ "Underflow (resulting value less than " + formatNumberReadable(_type.minValue()) + ")",
"<result>",
&_value
);
checkCondition(
_value > maxValue(_type),
_location,
- "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")",
+ "Overflow (resulting value larger than " + formatNumberReadable(_type.maxValue()) + ")",
"<result>",
&_value
);
@@ -368,18 +398,30 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
std::vector<ASTPointer<Expression const>> const args = _funCall.arguments();
- if (funType.kind() == FunctionType::Kind::Assert)
+ switch (funType.kind())
+ {
+ case FunctionType::Kind::Assert:
visitAssert(_funCall);
- else if (funType.kind() == FunctionType::Kind::Require)
+ break;
+ case FunctionType::Kind::Require:
visitRequire(_funCall);
- else if (funType.kind() == FunctionType::Kind::GasLeft)
+ break;
+ case FunctionType::Kind::GasLeft:
visitGasLeft(_funCall);
- else if (funType.kind() == FunctionType::Kind::BlockHash)
- visitBlockHash(_funCall);
- else if (funType.kind() == FunctionType::Kind::Internal)
+ break;
+ case FunctionType::Kind::Internal:
inlineFunctionCall(_funCall);
- else
- {
+ break;
+ case FunctionType::Kind::KECCAK256:
+ case FunctionType::Kind::ECRecover:
+ case FunctionType::Kind::SHA256:
+ case FunctionType::Kind::RIPEMD160:
+ case FunctionType::Kind::BlockHash:
+ case FunctionType::Kind::AddMod:
+ case FunctionType::Kind::MulMod:
+ abstractFunctionCall(_funCall);
+ break;
+ default:
m_errorReporter.warning(
_funCall.location(),
"Assertion checker does not yet implement this type of function call."
@@ -411,8 +453,8 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall)
string gasLeft = "gasleft()";
// We increase the variable index since gasleft changes
// inside a tx.
- defineSpecialVariable(gasLeft, _funCall, true);
- auto const& symbolicVar = m_specialVariables.at(gasLeft);
+ defineGlobalVariable(gasLeft, _funCall, true);
+ auto const& symbolicVar = m_globalContext.at(gasLeft);
unsigned index = symbolicVar->index();
// We set the current value to unknown anyway to add type constraints.
setUnknownValue(*symbolicVar);
@@ -420,19 +462,11 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall)
m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1));
}
-void SMTChecker::visitBlockHash(FunctionCall const& _funCall)
+void SMTChecker::eraseArrayKnowledge()
{
- string blockHash = "blockhash";
- auto const& arguments = _funCall.arguments();
- solAssert(arguments.size() == 1, "");
- smt::SortPointer paramSort = smtSort(*arguments.at(0)->annotation().type);
- smt::SortPointer returnSort = smtSort(*_funCall.annotation().type);
- defineUninterpretedFunction(
- blockHash,
- make_shared<smt::FunctionSort>(vector<smt::SortPointer>{paramSort}, returnSort)
- );
- defineExpr(_funCall, m_uninterpretedFunctions.at(blockHash)({expr(*arguments.at(0))}));
- m_uninterpretedTerms.push_back(&_funCall);
+ for (auto const& var: m_variables)
+ if (var.first->annotation().type->category() == Type::Category::Mapping)
+ newValue(*var.first);
}
void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
@@ -502,29 +536,32 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
}
}
+void SMTChecker::abstractFunctionCall(FunctionCall const& _funCall)
+{
+ vector<smt::Expression> smtArguments;
+ for (auto const& arg: _funCall.arguments())
+ smtArguments.push_back(expr(*arg));
+ defineExpr(_funCall, (*m_expressions.at(&_funCall.expression()))(smtArguments));
+ m_uninterpretedTerms.insert(&_funCall);
+ setSymbolicUnknownValue(expr(_funCall), _funCall.annotation().type, *m_interface);
+}
+
void SMTChecker::endVisit(Identifier const& _identifier)
{
if (_identifier.annotation().lValueRequested)
{
// Will be translated as part of the node that requested the lvalue.
}
- else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
+ else if (dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
{
- if (
- fun->kind() == FunctionType::Kind::Assert ||
- fun->kind() == FunctionType::Kind::Require ||
- fun->kind() == FunctionType::Kind::GasLeft ||
- fun->kind() == FunctionType::Kind::BlockHash
- )
- return;
- createExpr(_identifier);
+ visitFunctionIdentifier(_identifier);
}
else if (isSupportedType(_identifier.annotation().type->category()))
{
if (VariableDeclaration const* decl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
defineExpr(_identifier, currentValue(*decl));
else if (_identifier.name() == "now")
- defineSpecialVariable(_identifier.name(), _identifier);
+ defineGlobalVariable(_identifier.name(), _identifier);
else
// TODO: handle MagicVariableDeclaration here
m_errorReporter.warning(
@@ -534,6 +571,20 @@ void SMTChecker::endVisit(Identifier const& _identifier)
}
}
+void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier)
+{
+ auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type);
+ if (fType.returnParameterTypes().size() > 1)
+ {
+ m_errorReporter.warning(
+ _identifier.location(),
+ "Assertion checker does not yet support functions with more than one return parameter."
+ );
+ }
+ defineGlobalFunction(fType.richIdentifier(), _identifier);
+ m_expressions.emplace(&_identifier, m_globalContext.at(fType.richIdentifier()));
+}
+
void SMTChecker::endVisit(Literal const& _literal)
{
Type const& type = *_literal.annotation().type;
@@ -585,7 +636,7 @@ bool SMTChecker::visit(MemberAccess const& _memberAccess)
_memberAccess.location(),
"Assertion checker does not yet support this expression."
);
- defineSpecialVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess);
+ defineGlobalVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess);
return false;
}
else
@@ -597,30 +648,107 @@ bool SMTChecker::visit(MemberAccess const& _memberAccess)
return true;
}
-void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
+void SMTChecker::endVisit(IndexAccess const& _indexAccess)
{
- if (!knownSpecialVariable(_name))
+ shared_ptr<SymbolicVariable> array;
+ if (auto const& id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression()))
+ {
+ auto const& varDecl = dynamic_cast<VariableDeclaration const&>(*id->annotation().referencedDeclaration);
+ solAssert(knownVariable(varDecl), "");
+ array = m_variables[&varDecl];
+ }
+ else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
+ {
+ solAssert(knownExpr(*innerAccess), "");
+ array = m_expressions[innerAccess];
+ }
+ else
+ {
+ m_errorReporter.warning(
+ _indexAccess.location(),
+ "Assertion checker does not yet implement this expression."
+ );
+ return;
+ }
+
+ solAssert(array, "");
+ defineExpr(_indexAccess, smt::Expression::select(
+ array->currentValue(),
+ expr(*_indexAccess.indexExpression())
+ ));
+ setSymbolicUnknownValue(
+ expr(_indexAccess),
+ _indexAccess.annotation().type,
+ *m_interface
+ );
+ m_uninterpretedTerms.insert(&_indexAccess);
+}
+
+void SMTChecker::arrayAssignment()
+{
+ m_arrayAssignmentHappened = true;
+ eraseArrayKnowledge();
+}
+
+void SMTChecker::arrayIndexAssignment(Assignment const& _assignment)
+{
+ auto const& indexAccess = dynamic_cast<IndexAccess const&>(_assignment.leftHandSide());
+ if (auto const& id = dynamic_cast<Identifier const*>(&indexAccess.baseExpression()))
+ {
+ auto const& varDecl = dynamic_cast<VariableDeclaration const&>(*id->annotation().referencedDeclaration);
+ solAssert(knownVariable(varDecl), "");
+ smt::Expression store = smt::Expression::store(
+ m_variables[&varDecl]->currentValue(),
+ expr(*indexAccess.indexExpression()),
+ expr(_assignment.rightHandSide())
+ );
+ m_interface->addAssertion(newValue(varDecl) == store);
+ }
+ else if (dynamic_cast<IndexAccess const*>(&indexAccess.baseExpression()))
+ m_errorReporter.warning(
+ indexAccess.location(),
+ "Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays."
+ );
+ else
+ m_errorReporter.warning(
+ _assignment.location(),
+ "Assertion checker does not yet implement this expression."
+ );
+}
+
+void SMTChecker::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
+{
+ if (!knownGlobalSymbol(_name))
{
auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface);
- m_specialVariables.emplace(_name, result.second);
+ m_globalContext.emplace(_name, result.second);
setUnknownValue(*result.second);
if (result.first)
m_errorReporter.warning(
_expr.location(),
- "Assertion checker does not yet support this special variable."
+ "Assertion checker does not yet support this global variable."
);
}
else if (_increaseIndex)
- m_specialVariables.at(_name)->increaseIndex();
+ m_globalContext.at(_name)->increaseIndex();
// The default behavior is not to increase the index since
- // most of the special values stay the same throughout a tx.
- defineExpr(_expr, m_specialVariables.at(_name)->currentValue());
+ // most of the global values stay the same throughout a tx.
+ if (isSupportedType(_expr.annotation().type->category()))
+ defineExpr(_expr, m_globalContext.at(_name)->currentValue());
}
-void SMTChecker::defineUninterpretedFunction(string const& _name, smt::SortPointer _sort)
+void SMTChecker::defineGlobalFunction(string const& _name, Expression const& _expr)
{
- if (!m_uninterpretedFunctions.count(_name))
- m_uninterpretedFunctions.emplace(_name, m_interface->newVariable(_name, _sort));
+ if (!knownGlobalSymbol(_name))
+ {
+ auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface);
+ m_globalContext.emplace(_name, result.second);
+ if (result.first)
+ m_errorReporter.warning(
+ _expr.location(),
+ "Assertion checker does not yet support the type of this function."
+ );
+ }
}
void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
@@ -753,6 +881,8 @@ void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expressio
checkUnderOverflow(_value, *intType, _location);
else if (dynamic_cast<AddressType const*>(type.get()))
checkUnderOverflow(_value, IntegerType(160), _location);
+ else if (dynamic_cast<MappingType const*>(type.get()))
+ arrayAssignment();
m_interface->addAssertion(newValue(_variable) == _value);
}
@@ -797,18 +927,31 @@ void SMTChecker::checkCondition(
}
for (auto const& var: m_variables)
{
- expressionsToEvaluate.emplace_back(currentValue(*var.first));
- expressionNames.push_back(var.first->name());
+ if (var.first->type()->isValueType())
+ {
+ expressionsToEvaluate.emplace_back(currentValue(*var.first));
+ expressionNames.push_back(var.first->name());
+ }
}
- for (auto const& var: m_specialVariables)
+ for (auto const& var: m_globalContext)
{
- expressionsToEvaluate.emplace_back(var.second->currentValue());
- expressionNames.push_back(var.first);
+ auto const& type = var.second->type();
+ if (
+ type->isValueType() &&
+ smtKind(type->category()) != smt::Kind::Function
+ )
+ {
+ expressionsToEvaluate.emplace_back(var.second->currentValue());
+ expressionNames.push_back(var.first);
+ }
}
for (auto const& uf: m_uninterpretedTerms)
{
- expressionsToEvaluate.emplace_back(expr(*uf));
- expressionNames.push_back(m_scanner->sourceAt(uf->location()));
+ if (uf->annotation().type->isValueType())
+ {
+ expressionsToEvaluate.emplace_back(expr(*uf));
+ expressionNames.push_back(m_scanner->sourceAt(uf->location()));
+ }
}
}
smt::CheckResult result;
@@ -820,6 +963,13 @@ void SMTChecker::checkCondition(
loopComment =
"\nNote that some information is erased after the execution of loops.\n"
"You can re-introduce information using require().";
+ if (m_arrayAssignmentHappened)
+ loopComment +=
+ "\nNote that array aliasing is not supported,"
+ " therefore all mapping information is erased after"
+ " a mapping local variable/parameter is assigned.\n"
+ "You can re-introduce information using require().";
+
switch (result)
{
case smt::CheckResult::SATISFIABLE:
@@ -838,19 +988,19 @@ void SMTChecker::checkCondition(
for (auto const& eval: sortedModel)
modelMessage << " " << eval.first << " = " << eval.second << "\n";
- m_errorReporter.warning(_location, message.str() + loopComment, SecondarySourceLocation().append(modelMessage.str(), SourceLocation()));
+ m_errorReporter.warning(_location, message.str(), SecondarySourceLocation().append(modelMessage.str(), SourceLocation()).append(loopComment, SourceLocation()));
}
else
{
message << ".";
- m_errorReporter.warning(_location, message.str() + loopComment);
+ m_errorReporter.warning(_location, message.str(), SecondarySourceLocation().append(loopComment, SourceLocation()));
}
break;
}
case smt::CheckResult::UNSATISFIABLE:
break;
case smt::CheckResult::UNKNOWN:
- m_errorReporter.warning(_location, _description + " might happen here." + loopComment);
+ m_errorReporter.warning(_location, _description + " might happen here.", SecondarySourceLocation().append(loopComment, SourceLocation()));
break;
case smt::CheckResult::CONFLICTING:
m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
@@ -933,7 +1083,7 @@ SMTChecker::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _exp
try
{
// Parse and re-format nicely
- value = formatNumber(bigint(value));
+ value = formatNumberReadable(bigint(value));
}
catch (...) { }
}
@@ -952,7 +1102,11 @@ void SMTChecker::initializeFunctionCallParameters(FunctionDefinition const& _fun
solAssert(funParams.size() == _callArgs.size(), "");
for (unsigned i = 0; i < funParams.size(); ++i)
if (createVariable(*funParams[i]))
+ {
m_interface->addAssertion(_callArgs[i] == newValue(*funParams[i]));
+ if (funParams[i]->annotation().type->category() == Type::Category::Mapping)
+ m_arrayAssignmentHappened = true;
+ }
for (auto const& variable: _function.localVariables())
if (createVariable(*variable))
@@ -1115,9 +1269,9 @@ bool SMTChecker::knownExpr(Expression const& _e) const
return m_expressions.count(&_e);
}
-bool SMTChecker::knownSpecialVariable(string const& _var) const
+bool SMTChecker::knownGlobalSymbol(string const& _var) const
{
- return m_specialVariables.count(_var);
+ return m_globalContext.count(_var);
}
void SMTChecker::createExpr(Expression const& _e)
@@ -1140,6 +1294,7 @@ void SMTChecker::createExpr(Expression const& _e)
void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value)
{
createExpr(_e);
+ solAssert(isSupportedType(*_e.annotation().type), "Equality operator applied to type that is not fully supported");
m_interface->addAssertion(expr(_e) == _value);
}
diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h
index 34724848..f14d2ac0 100644
--- a/libsolidity/formal/SMTChecker.h
+++ b/libsolidity/formal/SMTChecker.h
@@ -22,13 +22,11 @@
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/ast/ASTVisitor.h>
-
#include <libsolidity/interface/ReadFile.h>
-
#include <liblangutil/Scanner.h>
-#include <unordered_map>
#include <string>
+#include <unordered_map>
#include <vector>
namespace langutil
@@ -79,21 +77,32 @@ private:
void endVisit(Literal const& _node) override;
void endVisit(Return const& _node) override;
bool visit(MemberAccess const& _node) override;
+ void endVisit(IndexAccess const& _node) override;
void arithmeticOperation(BinaryOperation const& _op);
void compareOperation(BinaryOperation const& _op);
void booleanOperation(BinaryOperation const& _op);
- void visitAssert(FunctionCall const&);
- void visitRequire(FunctionCall const&);
- void visitGasLeft(FunctionCall const&);
- void visitBlockHash(FunctionCall const&);
+ void visitAssert(FunctionCall const& _funCall);
+ void visitRequire(FunctionCall const& _funCall);
+ void visitGasLeft(FunctionCall const& _funCall);
/// Visits the FunctionDefinition of the called function
/// if available and inlines the return value.
- void inlineFunctionCall(FunctionCall const&);
-
- void defineSpecialVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false);
- void defineUninterpretedFunction(std::string const& _name, smt::SortPointer _sort);
+ void inlineFunctionCall(FunctionCall const& _funCall);
+ /// Creates an uninterpreted function call.
+ void abstractFunctionCall(FunctionCall const& _funCall);
+ void visitFunctionIdentifier(Identifier const& _identifier);
+
+ void defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false);
+ void defineGlobalFunction(std::string const& _name, Expression const& _expr);
+ /// Handles the side effects of assignment
+ /// to variable of some SMT array type
+ /// while aliasing is not supported.
+ void arrayAssignment();
+ /// Handles assignment to SMT array index.
+ void arrayIndexAssignment(Assignment const& _assignment);
+ /// Erases information about SMT arrays.
+ void eraseArrayKnowledge();
/// Division expression in the given type. Requires special treatment because
/// of rounding for signed division.
@@ -176,8 +185,8 @@ private:
/// Creates the expression and sets its value.
void defineExpr(Expression const& _e, smt::Expression _value);
- /// Checks if special variable was seen.
- bool knownSpecialVariable(std::string const& _var) const;
+ /// Checks if special variable or function was seen.
+ bool knownGlobalSymbol(std::string const& _var) const;
/// Adds a new path condition
void pushPathCondition(smt::Expression const& _e);
@@ -201,16 +210,16 @@ private:
std::shared_ptr<smt::SolverInterface> m_interface;
std::shared_ptr<VariableUsage> m_variableUsage;
bool m_loopExecutionHappened = false;
+ bool m_arrayAssignmentHappened = false;
/// An Expression may have multiple smt::Expression due to
/// repeated calls to the same function.
std::unordered_map<Expression const*, std::shared_ptr<SymbolicVariable>> m_expressions;
std::unordered_map<VariableDeclaration const*, std::shared_ptr<SymbolicVariable>> m_variables;
- std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_specialVariables;
- /// Stores the declaration of an Uninterpreted Function.
- std::unordered_map<std::string, smt::Expression> m_uninterpretedFunctions;
+ std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_globalContext;
/// Stores the instances of an Uninterpreted Function applied to arguments.
+ /// These may be direct application of UFs or Array index access.
/// Used to retrieve models.
- std::vector<Expression const*> m_uninterpretedTerms;
+ std::set<Expression const*> m_uninterpretedTerms;
std::vector<smt::Expression> m_pathConditions;
langutil::ErrorReporter& m_errorReporter;
std::shared_ptr<langutil::Scanner> m_scanner;
diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp
index 3cfa01b1..a23dbe55 100644
--- a/libsolidity/formal/SMTLib2Interface.cpp
+++ b/libsolidity/formal/SMTLib2Interface.cpp
@@ -17,22 +17,20 @@
#include <libsolidity/formal/SMTLib2Interface.h>
-#include <liblangutil/Exceptions.h>
#include <libsolidity/interface/ReadFile.h>
-
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Keccak256.h>
-#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
-#include <cstdio>
+#include <array>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
-#include <array>
using namespace std;
using namespace dev;
diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h
index 55fc4096..d0bf4702 100644
--- a/libsolidity/formal/SMTLib2Interface.h
+++ b/libsolidity/formal/SMTLib2Interface.h
@@ -19,20 +19,17 @@
#include <libsolidity/formal/SolverInterface.h>
-#include <liblangutil/Exceptions.h>
#include <libsolidity/interface/ReadFile.h>
-
-#include <libdevcore/FixedHash.h>
-
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Common.h>
+#include <libdevcore/FixedHash.h>
#include <boost/noncopyable.hpp>
-
+#include <cstdio>
#include <map>
+#include <set>
#include <string>
#include <vector>
-#include <cstdio>
-#include <set>
namespace dev
{
diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h
index 7f5ba37e..8c38bd2e 100644
--- a/libsolidity/formal/SMTPortfolio.h
+++ b/libsolidity/formal/SMTPortfolio.h
@@ -19,13 +19,10 @@
#include <libsolidity/formal/SolverInterface.h>
-
#include <libsolidity/interface/ReadFile.h>
-
#include <libdevcore/FixedHash.h>
#include <boost/noncopyable.hpp>
-
#include <map>
#include <vector>
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index 4a4b3fb1..6e0b17ac 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -17,18 +17,16 @@
#pragma once
-#include <liblangutil/Exceptions.h>
#include <libsolidity/interface/ReadFile.h>
-
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
#include <boost/noncopyable.hpp>
-
+#include <cstdio>
#include <map>
#include <string>
#include <vector>
-#include <cstdio>
namespace dev
{
@@ -80,6 +78,8 @@ struct FunctionSort: public Sort
[&](SortPointer _a, SortPointer _b) { return *_a == *_b; }
))
return false;
+ solAssert(codomain, "");
+ solAssert(_otherFunction->codomain, "");
return *codomain == *_otherFunction->codomain;
}
@@ -99,6 +99,10 @@ struct ArraySort: public Sort
return false;
auto _otherArray = dynamic_cast<ArraySort const*>(&_other);
solAssert(_otherArray, "");
+ solAssert(_otherArray->domain, "");
+ solAssert(_otherArray->range, "");
+ solAssert(domain, "");
+ solAssert(range, "");
return *domain == *_otherArray->domain && *range == *_otherArray->range;
}
@@ -161,8 +165,9 @@ public:
static Expression select(Expression _array, Expression _index)
{
solAssert(_array.sort->kind == Kind::Array, "");
- auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get());
+ std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
solAssert(arraySort, "");
+ solAssert(_index.sort, "");
solAssert(*arraySort->domain == *_index.sort, "");
return Expression(
"select",
@@ -176,14 +181,16 @@ public:
static Expression store(Expression _array, Expression _index, Expression _element)
{
solAssert(_array.sort->kind == Kind::Array, "");
- auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get());
+ std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
solAssert(arraySort, "");
+ solAssert(_index.sort, "");
+ solAssert(_element.sort, "");
solAssert(*arraySort->domain == *_index.sort, "");
solAssert(*arraySort->range == *_element.sort, "");
return Expression(
"store",
std::vector<Expression>{std::move(_array), std::move(_index), std::move(_element)},
- _array.sort
+ arraySort
);
}
diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp
index c297c807..269bff73 100644
--- a/libsolidity/formal/SymbolicTypes.cpp
+++ b/libsolidity/formal/SymbolicTypes.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/SymbolicTypes.h>
#include <libsolidity/ast/Types.h>
-
#include <memory>
using namespace std;
@@ -38,17 +37,33 @@ smt::SortPointer dev::solidity::smtSort(Type const& _type)
solAssert(fType, "");
vector<smt::SortPointer> parameterSorts = smtSort(fType->parameterTypes());
auto returnTypes = fType->returnParameterTypes();
- // TODO remove this when we support tuples.
- solAssert(returnTypes.size() == 1, "");
- smt::SortPointer returnSort = smtSort(*returnTypes.at(0));
+ smt::SortPointer returnSort;
+ // TODO change this when we support tuples.
+ if (returnTypes.size() == 0)
+ // We cannot declare functions without a return sort, so we use the smallest.
+ returnSort = make_shared<smt::Sort>(smt::Kind::Bool);
+ else if (returnTypes.size() > 1)
+ // Abstract sort.
+ returnSort = make_shared<smt::Sort>(smt::Kind::Int);
+ else
+ returnSort = smtSort(*returnTypes.at(0));
return make_shared<smt::FunctionSort>(parameterSorts, returnSort);
}
case smt::Kind::Array:
{
- solUnimplementedAssert(false, "Invalid type");
+ if (isMapping(_type.category()))
+ {
+ auto mapType = dynamic_cast<MappingType const*>(&_type);
+ solAssert(mapType, "");
+ return make_shared<smt::ArraySort>(smtSort(*mapType->keyType()), smtSort(*mapType->valueType()));
+ }
+ // TODO Solidity array
+ return make_shared<smt::Sort>(smt::Kind::Int);
}
+ default:
+ // Abstract case.
+ return make_shared<smt::Sort>(smt::Kind::Int);
}
- solAssert(false, "Invalid type");
}
vector<smt::SortPointer> dev::solidity::smtSort(vector<TypePointer> const& _types)
@@ -65,13 +80,24 @@ smt::Kind dev::solidity::smtKind(Type::Category _category)
return smt::Kind::Int;
else if (isBool(_category))
return smt::Kind::Bool;
- solAssert(false, "Invalid type");
+ else if (isFunction(_category))
+ return smt::Kind::Function;
+ else if (isMapping(_category))
+ return smt::Kind::Array;
+ // Abstract case.
+ return smt::Kind::Int;
}
bool dev::solidity::isSupportedType(Type::Category _category)
{
return isNumber(_category) ||
isBool(_category) ||
+ isMapping(_category);
+}
+
+bool dev::solidity::isSupportedTypeDeclaration(Type::Category _category)
+{
+ return isSupportedType(_category) ||
isFunction(_category);
}
@@ -84,7 +110,7 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
bool abstract = false;
shared_ptr<SymbolicVariable> var;
TypePointer type = _type.shared_from_this();
- if (!isSupportedType(_type))
+ if (!isSupportedTypeDeclaration(_type))
{
abstract = true;
var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver);
@@ -92,7 +118,7 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
else if (isBool(_type.category()))
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _solver);
else if (isFunction(_type.category()))
- var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver);
+ var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _solver);
else if (isInteger(_type.category()))
var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver);
else if (isFixedBytes(_type.category()))
@@ -112,6 +138,8 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
else
var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver);
}
+ else if (isMapping(_type.category()))
+ var = make_shared<SymbolicMappingVariable>(type, _uniqueName, _solver);
else
solAssert(false, "");
return make_pair(abstract, var);
@@ -122,6 +150,11 @@ bool dev::solidity::isSupportedType(Type const& _type)
return isSupportedType(_type.category());
}
+bool dev::solidity::isSupportedTypeDeclaration(Type const& _type)
+{
+ return isSupportedTypeDeclaration(_type.category());
+}
+
bool dev::solidity::isInteger(Type::Category _category)
{
return _category == Type::Category::Integer;
@@ -160,6 +193,11 @@ bool dev::solidity::isFunction(Type::Category _category)
return _category == Type::Category::Function;
}
+bool dev::solidity::isMapping(Type::Category _category)
+{
+ return _category == Type::Category::Mapping;
+}
+
smt::Expression dev::solidity::minValue(IntegerType const& _type)
{
return smt::Expression(_type.minValue());
diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h
index 984653b3..35c7bb8d 100644
--- a/libsolidity/formal/SymbolicTypes.h
+++ b/libsolidity/formal/SymbolicTypes.h
@@ -19,7 +19,6 @@
#include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/formal/SymbolicVariables.h>
-
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
@@ -34,10 +33,12 @@ std::vector<smt::SortPointer> smtSort(std::vector<TypePointer> const& _types);
/// Returns the SMT kind that models the Solidity type type category _category.
smt::Kind smtKind(Type::Category _category);
-/// So far int, bool and address are supported.
-/// Returns true if type is supported.
+/// Returns true if type is fully supported (declaration and operations).
bool isSupportedType(Type::Category _category);
bool isSupportedType(Type const& _type);
+/// Returns true if type is partially supported (declaration).
+bool isSupportedTypeDeclaration(Type::Category _category);
+bool isSupportedTypeDeclaration(Type const& _type);
bool isInteger(Type::Category _category);
bool isRational(Type::Category _category);
@@ -46,6 +47,7 @@ bool isAddress(Type::Category _category);
bool isNumber(Type::Category _category);
bool isBool(Type::Category _category);
bool isFunction(Type::Category _category);
+bool isMapping(Type::Category _category);
/// Returns a new symbolic variable, according to _type.
/// Also returns whether the type is abstract or not,
diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp
index efaeb97a..c4fc81da 100644
--- a/libsolidity/formal/SymbolicVariables.cpp
+++ b/libsolidity/formal/SymbolicVariables.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/formal/SymbolicTypes.h>
-
#include <libsolidity/ast/AST.h>
using namespace std;
@@ -37,16 +36,32 @@ SymbolicVariable::SymbolicVariable(
{
}
+smt::Expression SymbolicVariable::currentValue() const
+{
+ return valueAtIndex(m_ssa->index());
+}
+
string SymbolicVariable::currentName() const
{
return uniqueSymbol(m_ssa->index());
}
+smt::Expression SymbolicVariable::valueAtIndex(int _index) const
+{
+ return m_interface.newVariable(uniqueSymbol(_index), smtSort(*m_type));
+}
+
string SymbolicVariable::uniqueSymbol(unsigned _index) const
{
return m_uniqueName + "_" + to_string(_index);
}
+smt::Expression SymbolicVariable::increaseIndex()
+{
+ ++(*m_ssa);
+ return currentValue();
+}
+
SymbolicBoolVariable::SymbolicBoolVariable(
TypePointer _type,
string const& _uniqueName,
@@ -57,11 +72,6 @@ SymbolicBoolVariable::SymbolicBoolVariable(
solAssert(m_type->category() == Type::Category::Bool, "");
}
-smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const
-{
- return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Bool));
-}
-
SymbolicIntVariable::SymbolicIntVariable(
TypePointer _type,
string const& _uniqueName,
@@ -72,11 +82,6 @@ SymbolicIntVariable::SymbolicIntVariable(
solAssert(isNumber(m_type->category()), "");
}
-smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const
-{
- return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Int));
-}
-
SymbolicAddressVariable::SymbolicAddressVariable(
string const& _uniqueName,
smt::SolverInterface& _interface
@@ -93,3 +98,41 @@ SymbolicFixedBytesVariable::SymbolicFixedBytesVariable(
SymbolicIntVariable(make_shared<IntegerType>(_numBytes * 8), _uniqueName, _interface)
{
}
+
+SymbolicFunctionVariable::SymbolicFunctionVariable(
+ TypePointer _type,
+ string const& _uniqueName,
+ smt::SolverInterface&_interface
+):
+ SymbolicVariable(move(_type), _uniqueName, _interface),
+ m_declaration(m_interface.newVariable(currentName(), smtSort(*m_type)))
+{
+ solAssert(m_type->category() == Type::Category::Function, "");
+}
+
+void SymbolicFunctionVariable::resetDeclaration()
+{
+ m_declaration = m_interface.newVariable(currentName(), smtSort(*m_type));
+}
+
+smt::Expression SymbolicFunctionVariable::increaseIndex()
+{
+ ++(*m_ssa);
+ resetDeclaration();
+ return currentValue();
+}
+
+smt::Expression SymbolicFunctionVariable::operator()(vector<smt::Expression> _arguments) const
+{
+ return m_declaration(_arguments);
+}
+
+SymbolicMappingVariable::SymbolicMappingVariable(
+ TypePointer _type,
+ string const& _uniqueName,
+ smt::SolverInterface& _interface
+):
+ SymbolicVariable(move(_type), _uniqueName, _interface)
+{
+ solAssert(isMapping(m_type->category()), "");
+}
diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h
index fcf32760..86abf4f1 100644
--- a/libsolidity/formal/SymbolicVariables.h
+++ b/libsolidity/formal/SymbolicVariables.h
@@ -17,12 +17,9 @@
#pragma once
-#include <libsolidity/formal/SSAVariable.h>
-
#include <libsolidity/formal/SolverInterface.h>
-
+#include <libsolidity/formal/SSAVariable.h>
#include <libsolidity/ast/Types.h>
-
#include <memory>
namespace dev
@@ -46,19 +43,13 @@ public:
virtual ~SymbolicVariable() = default;
- smt::Expression currentValue() const
- {
- return valueAtIndex(m_ssa->index());
- }
-
+ smt::Expression currentValue() const;
std::string currentName() const;
-
- virtual smt::Expression valueAtIndex(int _index) const = 0;
-
- smt::Expression increaseIndex()
+ virtual smt::Expression valueAtIndex(int _index) const;
+ virtual smt::Expression increaseIndex();
+ virtual smt::Expression operator()(std::vector<smt::Expression> /*_arguments*/) const
{
- ++(*m_ssa);
- return currentValue();
+ solAssert(false, "Function application to non-function.");
}
unsigned index() const { return m_ssa->index(); }
@@ -86,9 +77,6 @@ public:
std::string const& _uniqueName,
smt::SolverInterface& _interface
);
-
-protected:
- smt::Expression valueAtIndex(int _index) const;
};
/**
@@ -102,9 +90,6 @@ public:
std::string const& _uniqueName,
smt::SolverInterface& _interface
);
-
-protected:
- smt::Expression valueAtIndex(int _index) const;
};
/**
@@ -132,5 +117,41 @@ public:
);
};
+/**
+ * Specialization of SymbolicVariable for FunctionType
+ */
+class SymbolicFunctionVariable: public SymbolicVariable
+{
+public:
+ SymbolicFunctionVariable(
+ TypePointer _type,
+ std::string const& _uniqueName,
+ smt::SolverInterface& _interface
+ );
+
+ smt::Expression increaseIndex();
+ smt::Expression operator()(std::vector<smt::Expression> _arguments) const;
+
+private:
+ /// Creates a new function declaration.
+ void resetDeclaration();
+
+ /// Stores the current function declaration.
+ smt::Expression m_declaration;
+};
+
+/**
+ * Specialization of SymbolicVariable for Mapping
+ */
+class SymbolicMappingVariable: public SymbolicVariable
+{
+public:
+ SymbolicMappingVariable(
+ TypePointer _type,
+ std::string const& _uniqueName,
+ smt::SolverInterface& _interface
+ );
+};
+
}
}
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
index cb01dc61..4cbc3271 100644
--- a/libsolidity/formal/Z3Interface.cpp
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/Z3Interface.h>
#include <liblangutil/Exceptions.h>
-
#include <libdevcore/CommonIO.h>
using namespace std;
diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h
index 86e1badd..ee4d1551 100644
--- a/libsolidity/formal/Z3Interface.h
+++ b/libsolidity/formal/Z3Interface.h
@@ -18,9 +18,7 @@
#pragma once
#include <libsolidity/formal/SolverInterface.h>
-
#include <boost/noncopyable.hpp>
-
#include <z3++.h>
namespace dev
diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp
index aefb34af..0d27109e 100644
--- a/libsolidity/interface/ABI.cpp
+++ b/libsolidity/interface/ABI.cpp
@@ -19,6 +19,7 @@
*/
#include <libsolidity/interface/ABI.h>
+
#include <libsolidity/ast/AST.h>
using namespace std;
diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h
index db70729d..082f3900 100644
--- a/libsolidity/interface/ABI.h
+++ b/libsolidity/interface/ABI.h
@@ -20,9 +20,9 @@
#pragma once
-#include <string>
-#include <memory>
#include <json/json.h>
+#include <memory>
+#include <string>
namespace dev
{
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index f5eb7e41..69bceefc 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -22,18 +22,19 @@
#include <libsolidity/interface/AssemblyStack.h>
+#include <libsolidity/codegen/AsmCodeGen.h>
+#include <libevmasm/Assembly.h>
#include <liblangutil/Scanner.h>
-#include <libyul/AsmPrinter.h>
-#include <libyul/AsmParser.h>
+
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
-#include <libyul/AsmCodeGen.h>
-#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/AsmParser.h>
+#include <libyul/AsmPrinter.h>
#include <libyul/backends/evm/EVMAssembly.h>
+#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/backends/evm/EVMDialect.h>
+#include <libyul/backends/evm/EVMObjectCompiler.h>
#include <libyul/ObjectParser.h>
-
-#include <libevmasm/Assembly.h>
-
#include <libyul/optimiser/Suite.h>
using namespace std;
@@ -43,19 +44,19 @@ using namespace dev::solidity;
namespace
{
-yul::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language)
+shared_ptr<yul::Dialect> languageToDialect(AssemblyStack::Language _language)
{
switch (_language)
{
case AssemblyStack::Language::Assembly:
- return yul::AsmFlavour::Loose;
+ return yul::EVMDialect::looseAssemblyForEVM();
case AssemblyStack::Language::StrictAssembly:
- return yul::AsmFlavour::Strict;
+ return yul::EVMDialect::strictAssemblyForEVMObjects();
case AssemblyStack::Language::Yul:
- return yul::AsmFlavour::Yul;
+ return yul::Dialect::yul();
}
solAssert(false, "");
- return yul::AsmFlavour::Yul;
+ return yul::Dialect::yul();
}
}
@@ -72,7 +73,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
m_errors.clear();
m_analysisSuccessful = false;
m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName));
- m_parserResult = yul::ObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
+ m_parserResult = yul::ObjectParser(m_errorReporter, languageToDialect(m_language)).parse(m_scanner, false);
if (!m_errorReporter.errors().empty())
return false;
solAssert(m_parserResult, "");
@@ -84,21 +85,59 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
void AssemblyStack::optimize()
{
solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly.");
- yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo);
+ solAssert(m_analysisSuccessful, "Analysis was not successful.");
+ m_analysisSuccessful = false;
+ optimize(*m_parserResult);
solAssert(analyzeParsed(), "Invalid source code after optimization.");
}
bool AssemblyStack::analyzeParsed()
{
solAssert(m_parserResult, "");
- solAssert(m_parserResult->code, "");
- m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>();
- yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language));
- m_analysisSuccessful = analyzer.analyze(*m_parserResult->code);
+ m_analysisSuccessful = analyzeParsed(*m_parserResult);
return m_analysisSuccessful;
}
-MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
+bool AssemblyStack::analyzeParsed(yul::Object& _object)
+{
+ solAssert(_object.code, "");
+ _object.analysisInfo = make_shared<yul::AsmAnalysisInfo>();
+ yul::AsmAnalyzer analyzer(*_object.analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToDialect(m_language));
+ bool success = analyzer.analyze(*_object.code);
+ for (auto& subNode: _object.subObjects)
+ if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
+ if (!analyzeParsed(*subObject))
+ success = false;
+ return success;
+}
+
+void AssemblyStack::compileEVM(yul::AbstractAssembly& _assembly, bool _evm15, bool _optimize) const
+{
+ shared_ptr<yul::EVMDialect> dialect;
+
+ if (m_language == Language::Assembly)
+ dialect = yul::EVMDialect::looseAssemblyForEVM();
+ else if (m_language == AssemblyStack::Language::StrictAssembly)
+ dialect = yul::EVMDialect::strictAssemblyForEVMObjects();
+ else if (m_language == AssemblyStack::Language::Yul)
+ dialect = yul::EVMDialect::yulForEVM();
+ else
+ solAssert(false, "Invalid language.");
+
+ yul::EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _evm15, _optimize);
+}
+
+void AssemblyStack::optimize(yul::Object& _object)
+{
+ solAssert(_object.code, "");
+ solAssert(_object.analysisInfo, "");
+ for (auto& subNode: _object.subObjects)
+ if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
+ optimize(*subObject);
+ yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo);
+}
+
+MachineAssemblyObject AssemblyStack::assemble(Machine _machine, bool _optimize) const
{
solAssert(m_analysisSuccessful, "");
solAssert(m_parserResult, "");
@@ -111,7 +150,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
MachineAssemblyObject object;
eth::Assembly assembly;
- yul::CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly);
+ EthAssemblyAdapter adapter(assembly);
+ compileEVM(adapter, false, _optimize);
object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
object.assembly = assembly.assemblyString();
return object;
@@ -120,7 +160,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
MachineAssemblyObject object;
yul::EVMAssembly assembly(true);
- yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code);
+ compileEVM(assembly, true, _optimize);
object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
/// TODO: fill out text representation
return object;
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index 0d04ffec..01db6b61 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -29,13 +29,17 @@
#include <libevmasm/LinkerObject.h>
-#include <string>
#include <memory>
+#include <string>
namespace langutil
{
class Scanner;
}
+namespace yul
+{
+class AbstractAssembly;
+}
namespace dev
{
@@ -73,7 +77,8 @@ public:
void optimize();
/// Run the assembly step (should only be called after parseAndAnalyze).
- MachineAssemblyObject assemble(Machine _machine) const;
+ /// @param _optimize does not run the optimizer but performs optimized code generation.
+ MachineAssemblyObject assemble(Machine _machine, bool _optimize = false) const;
/// @returns the errors generated during parsing, analysis (and potentially assembly).
langutil::ErrorList const& errors() const { return m_errors; }
@@ -83,6 +88,11 @@ public:
private:
bool analyzeParsed();
+ bool analyzeParsed(yul::Object& _object);
+
+ void compileEVM(yul::AbstractAssembly& _assembly, bool _evm15, bool _optimize) const;
+
+ void optimize(yul::Object& _object);
Language m_language = Language::Assembly;
EVMVersion m_evmVersion;
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 610caea1..f9d889e7 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -24,26 +24,27 @@
#include <libsolidity/interface/CompilerStack.h>
-#include <libsolidity/interface/Version.h>
-#include <libsolidity/analysis/SemVerHandler.h>
-#include <libsolidity/ast/AST.h>
-#include <libsolidity/parsing/Parser.h>
-#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <libsolidity/analysis/ControlFlowGraph.h>
+#include <libsolidity/analysis/ContractLevelChecker.h>
+#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
-#include <libsolidity/analysis/TypeChecker.h>
-#include <libsolidity/analysis/DocStringAnalyser.h>
-#include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/PostTypeChecker.h>
+#include <libsolidity/analysis/SemVerHandler.h>
+#include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/SyntaxChecker.h>
+#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/analysis/ViewPureChecker.h>
+
+#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/formal/SMTChecker.h>
#include <libsolidity/interface/ABI.h>
#include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h>
+#include <libsolidity/interface/Version.h>
+#include <libsolidity/parsing/Parser.h>
#include <libyul/YulString.h>
@@ -388,18 +389,27 @@ string const CompilerStack::lastContractName() const
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
return currentContract.compiler ? &contract(_contractName).compiler->assemblyItems() : nullptr;
}
eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
}
string const* CompilerStack::sourceMapping(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& c = contract(_contractName);
if (!c.sourceMapping)
{
@@ -411,6 +421,9 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const
string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& c = contract(_contractName);
if (!c.runtimeSourceMapping)
{
@@ -446,17 +459,26 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
return contract(_contractName).object;
}
eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
return contract(_contractName).runtimeObject;
}
/// FIXME: cache this string
string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
if (currentContract.compiler)
return currentContract.compiler->assemblyString(_sourceCodes);
@@ -467,6 +489,9 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap _sou
/// FIXME: cache the JSON
Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap _sourceCodes) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
if (currentContract.compiler)
return currentContract.compiler->assemblyJSON(_sourceCodes);
@@ -493,13 +518,16 @@ map<string, unsigned> CompilerStack::sourceIndices() const
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
return contractABI(contract(_contractName));
}
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
@@ -512,13 +540,16 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
return natspecUser(contract(_contractName));
}
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
@@ -531,13 +562,16 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
return natspecDev(contract(_contractName));
}
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
@@ -550,9 +584,12 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
Json::Value methodIdentifiers(Json::objectValue);
for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
- methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
+ methodIdentifiers[it.second->externalSignature()] = it.first.hex();
return methodIdentifiers;
}
@@ -582,8 +619,8 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
{
- if (m_stackState != CompilationSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
return *contract(_contractName).contract;
}
@@ -593,6 +630,9 @@ size_t CompilerStack::functionEntryPoint(
FunctionDefinition const& _function
) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
shared_ptr<Compiler> const& compiler = contract(_contractName).compiler;
if (!compiler)
return 0;
@@ -618,6 +658,22 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
}
+
+h256 const& CompilerStack::Source::keccak256() const
+{
+ if (keccak256HashCached == h256{})
+ keccak256HashCached = dev::keccak256(scanner->source());
+ return keccak256HashCached;
+}
+
+h256 const& CompilerStack::Source::swarmHash() const
+{
+ if (swarmHashCached == h256{})
+ swarmHashCached = dev::swarmHash(scanner->source());
+ return swarmHashCached;
+}
+
+
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
{
solAssert(m_stackState < ParsingSuccessful, "");
@@ -859,16 +915,13 @@ string CompilerStack::createMetadata(Contract const& _contract) const
continue;
solAssert(s.second.scanner, "Scanner not available");
- meta["sources"][s.first]["keccak256"] =
- "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes());
+ meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes());
if (m_metadataLiteralSources)
meta["sources"][s.first]["content"] = s.second.scanner->source();
else
{
meta["sources"][s.first]["urls"] = Json::arrayValue;
- meta["sources"][s.first]["urls"].append(
- "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes())
- );
+ meta["sources"][s.first]["urls"].append("bzzr://" + toHex(s.second.swarmHash().asBytes()));
}
}
meta["settings"]["optimizer"]["enabled"] = m_optimize;
@@ -895,7 +948,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
return jsonCompactPrint(meta);
}
-bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode)
+bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimentalMode)
{
bytes cborEncodedHash =
// CBOR-encoding of the key "bzzr0"
@@ -922,6 +975,9 @@ bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode
string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
string ret;
map<string, unsigned> sourceIndicesMap = sourceIndices();
int prevStart = -1;
@@ -1008,6 +1064,9 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
return Json::Value();
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 2c7add3b..81d5009f 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -34,15 +34,14 @@
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
-#include <json/json.h>
-
#include <boost/noncopyable.hpp>
+#include <json/json.h>
+#include <functional>
+#include <memory>
#include <ostream>
#include <string>
-#include <memory>
#include <vector>
-#include <functional>
namespace langutil
{
@@ -262,7 +261,11 @@ private:
std::shared_ptr<langutil::Scanner> scanner;
std::shared_ptr<SourceUnit> ast;
bool isLibrary = false;
- void reset() { scanner.reset(); ast.reset(); }
+ h256 mutable keccak256HashCached;
+ h256 mutable swarmHashCached;
+ void reset() { *this = Source(); }
+ h256 const& keccak256() const;
+ h256 const& swarmHash() const;
};
/// The state per contract. Filled gradually during compilation.
@@ -316,7 +319,7 @@ private:
std::string createMetadata(Contract const& _contract) const;
/// @returns the metadata CBOR for the given serialised metadata JSON.
- static bytes createCBORMetadata(std::string _metadata, bool _experimentalMode);
+ static bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode);
/// @returns the computer source mapping string.
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index de6b2ce5..8ffcf951 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -20,18 +20,21 @@
* Gas consumption estimator working alongside the AST.
*/
-#include "GasEstimator.h"
-#include <map>
-#include <functional>
-#include <memory>
-#include <libdevcore/Keccak256.h>
-#include <libevmasm/ControlFlowGraph.h>
-#include <libevmasm/KnownState.h>
-#include <libevmasm/PathGasMeter.h>
+#include <libsolidity/interface/GasEstimator.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/CompilerUtils.h>
+#include <libevmasm/ControlFlowGraph.h>
+#include <libevmasm/KnownState.h>
+#include <libevmasm/PathGasMeter.h>
+#include <libdevcore/Keccak256.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+
using namespace std;
using namespace dev;
using namespace dev::eth;
diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h
index 214a3e58..f40cffeb 100644
--- a/libsolidity/interface/GasEstimator.h
+++ b/libsolidity/interface/GasEstimator.h
@@ -24,12 +24,12 @@
#include <liblangutil/EVMVersion.h>
-#include <libevmasm/GasMeter.h>
#include <libevmasm/Assembly.h>
+#include <libevmasm/GasMeter.h>
-#include <vector>
-#include <map>
#include <array>
+#include <map>
+#include <vector>
namespace dev
{
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
index 11dde349..7a89abae 100644
--- a/libsolidity/interface/Natspec.cpp
+++ b/libsolidity/interface/Natspec.cpp
@@ -24,8 +24,9 @@
*/
#include <libsolidity/interface/Natspec.h>
-#include <boost/range/irange.hpp>
+
#include <libsolidity/ast/AST.h>
+#include <boost/range/irange.hpp>
using namespace std;
using namespace dev;
diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h
index 0be4dda2..fbaa6d4d 100644
--- a/libsolidity/interface/Natspec.h
+++ b/libsolidity/interface/Natspec.h
@@ -25,9 +25,9 @@
#pragma once
-#include <string>
-#include <memory>
#include <json/json.h>
+#include <memory>
+#include <string>
namespace dev
{
diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h
index 7068629d..3b3d747e 100644
--- a/libsolidity/interface/ReadFile.h
+++ b/libsolidity/interface/ReadFile.h
@@ -17,9 +17,9 @@
#pragma once
-#include <string>
-#include <functional>
#include <boost/noncopyable.hpp>
+#include <functional>
+#include <string>
namespace dev
{
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 0eef50d2..137a4439 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -21,13 +21,17 @@
*/
#include <libsolidity/interface/StandardCompiler.h>
-#include <liblangutil/SourceReferenceFormatter.h>
+
#include <libsolidity/ast/ASTJsonConverter.h>
+#include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>
#include <libdevcore/Keccak256.h>
+#include <boost/algorithm/cxx11/any_of.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
using namespace std;
using namespace dev;
@@ -69,12 +73,11 @@ Json::Value formatErrorWithException(
bool const& _warning,
string const& _type,
string const& _component,
- string const& _message,
- function<Scanner const&(string const&)> const& _scannerFromSourceName
+ string const& _message
)
{
string message;
- string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type, _scannerFromSourceName);
+ string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type);
// NOTE: the below is partially a copy from SourceReferenceFormatter
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
@@ -189,6 +192,31 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
return false;
}
+/// @returns true if any binary was requested, i.e. we actually have to perform compilation.
+bool isBinaryRequested(Json::Value const& _outputSelection)
+{
+ if (!_outputSelection.isObject())
+ return false;
+
+ // This does not inculde "evm.methodIdentifiers" on purpose!
+ static vector<string> const outputsThatRequireBinaries{
+ "*",
+ "metadata", // This is only generated at the end of compilation, but could be generated earlier.
+ "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes",
+ "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences",
+ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap",
+ "evm.bytecode.linkReferences",
+ "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
+ };
+
+ for (auto const& fileRequests: _outputSelection)
+ for (auto const& requests: fileRequests)
+ for (auto const& output: outputsThatRequireBinaries)
+ if (isArtifactRequested(requests, output))
+ return true;
+ return false;
+}
+
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
{
Json::Value ret(Json::objectValue);
@@ -226,6 +254,99 @@ Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _so
return output;
}
+boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
+{
+ if (!!_input && !_input.isObject())
+ return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
+
+ for (auto const& member: _input.getMemberNames())
+ if (!_keys.count(member))
+ return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
+
+ return boost::none;
+}
+
+boost::optional<Json::Value> checkRootKeys(Json::Value const& _input)
+{
+ static set<string> keys{"auxiliaryInput", "language", "settings", "sources"};
+ return checkKeys(_input, keys, "root");
+}
+
+boost::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
+{
+ static set<string> keys{"content", "keccak256", "urls"};
+ return checkKeys(_input, keys, "sources." + _name);
+}
+
+boost::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
+{
+ static set<string> keys{"smtlib2responses"};
+ return checkKeys(_input, keys, "auxiliaryInput");
+}
+
+boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
+{
+ static set<string> keys{"evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"};
+ return checkKeys(_input, keys, "settings");
+}
+
+boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
+{
+ static set<string> keys{"enabled", "runs"};
+ return checkKeys(_input, keys, "settings.optimizer");
+}
+
+boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
+{
+ static set<string> keys{"useLiteralContent"};
+ return checkKeys(_input, keys, "settings.metadata");
+}
+
+boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
+{
+ if (!!_outputSelection && !_outputSelection.isObject())
+ return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
+
+ for (auto const& sourceName: _outputSelection.getMemberNames())
+ {
+ auto const& sourceVal = _outputSelection[sourceName];
+
+ if (!sourceVal.isObject())
+ return formatFatalError(
+ "JSONError",
+ "\"settings.outputSelection." + sourceName + "\" must be an object"
+ );
+
+ for (auto const& contractName: sourceVal.getMemberNames())
+ {
+ auto const& contractVal = sourceVal[contractName];
+
+ if (!contractVal.isArray())
+ return formatFatalError(
+ "JSONError",
+ "\"settings.outputSelection." +
+ sourceName +
+ "." +
+ contractName +
+ "\" must be a string array"
+ );
+
+ for (auto const& output: contractVal)
+ if (!output.isString())
+ return formatFatalError(
+ "JSONError",
+ "\"settings.outputSelection." +
+ sourceName +
+ "." +
+ contractName +
+ "\" must be a string array"
+ );
+ }
+ }
+
+ return boost::none;
+}
+
}
Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
@@ -235,6 +356,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (!_input.isObject())
return formatFatalError("JSONError", "Input is not a JSON object.");
+ if (auto result = checkRootKeys(_input))
+ return *result;
+
if (_input["language"] != "Solidity")
return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
@@ -252,8 +376,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
string hash;
- if (!sources[sourceName].isObject())
- return formatFatalError("JSONError", "Source input is not a JSON object.");
+ if (auto result = checkSourceKeys(sources[sourceName], sourceName))
+ return *result;
if (sources[sourceName]["keccak256"].isString())
hash = sources[sourceName]["keccak256"].asString();
@@ -320,10 +444,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
}
Json::Value const& auxInputs = _input["auxiliaryInput"];
+
+ if (auto result = checkAuxiliaryInputKeys(auxInputs))
+ return *result;
+
if (!!auxInputs)
{
Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"];
if (!!smtlib2Responses)
+ {
+ if (!smtlib2Responses.isObject())
+ return formatFatalError("JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object.");
+
for (auto const& hashString: smtlib2Responses.getMemberNames())
{
h256 hash;
@@ -336,12 +468,22 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input.");
}
+ if (!smtlib2Responses[hashString].isString())
+ return formatFatalError(
+ "JSONError",
+ "\"smtlib2Responses." + hashString + "\" must be a string."
+ );
+
m_compilerStack.addSMTLib2Response(hash, smtlib2Responses[hashString].asString());
}
+ }
}
Json::Value const& settings = _input.get("settings", Json::Value());
+ if (auto result = checkSettingsKeys(settings))
+ return *result;
+
if (settings.isMember("evmVersion"))
{
if (!settings["evmVersion"].isString())
@@ -352,11 +494,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setEVMVersion(*version);
}
+ if (settings.isMember("remappings") && !settings["remappings"].isArray())
+ return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings.");
+
vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
{
if (!remapping.isString())
- return formatFatalError("JSONError", "Remapping entry must be a string.");
+ return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings");
if (auto r = CompilerStack::parseRemapping(remapping.asString()))
remappings.emplace_back(std::move(*r));
else
@@ -367,6 +512,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (settings.isMember("optimizer"))
{
Json::Value optimizerSettings = settings["optimizer"];
+
+ if (auto result = checkOptimizerKeys(optimizerSettings))
+ return *result;
+
if (optimizerSettings.isMember("enabled"))
{
if (!optimizerSettings["enabled"].isBool())
@@ -428,16 +577,27 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setLibraries(libraries);
Json::Value metadataSettings = settings.get("metadata", Json::Value());
+
+ if (auto result = checkMetadataKeys(metadataSettings))
+ return *result;
+
m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());
Json::Value outputSelection = settings.get("outputSelection", Json::Value());
+
+ if (auto jsonError = checkOutputSelection(outputSelection))
+ return *jsonError;
+
m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection));
- auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compilerStack.scanner(_sourceName); };
+ bool const binariesRequested = isBinaryRequested(outputSelection);
try
{
- m_compilerStack.compile();
+ if (binariesRequested)
+ m_compilerStack.compile();
+ else
+ m_compilerStack.parseAndAnalyze();
for (auto const& error: m_compilerStack.errors())
{
@@ -448,8 +608,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
err.type() == Error::Type::Warning,
err.typeName(),
"general",
- "",
- scannerFromSourceName
+ ""
));
}
}
@@ -461,8 +620,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
_error.typeName(),
"general",
- "Uncaught error: ",
- scannerFromSourceName
+ "Uncaught error: "
));
}
/// This should not be leaked from compile().
@@ -482,8 +640,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"CompilerError",
"general",
- "Compiler error (" + _exception.lineInfo() + ")",
- scannerFromSourceName
+ "Compiler error (" + _exception.lineInfo() + ")"
));
}
catch (InternalCompilerError const& _exception)
@@ -493,8 +650,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"InternalCompilerError",
"general",
- "Internal compiler error (" + _exception.lineInfo() + ")",
- scannerFromSourceName
+ "Internal compiler error (" + _exception.lineInfo() + ")"
));
}
catch (UnimplementedFeatureError const& _exception)
@@ -504,8 +660,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"UnimplementedFeatureError",
"general",
- "Unimplemented feature (" + _exception.lineInfo() + ")",
- scannerFromSourceName
+ "Unimplemented feature (" + _exception.lineInfo() + ")"
));
}
catch (Exception const& _exception)
@@ -531,7 +686,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
/// Inconsistent state - stop here to receive error reports from users
- if (!compilationSuccess && errors.empty())
+ if (((binariesRequested && !compilationSuccess) || !analysisSuccess) && errors.empty())
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
Json::Value output = Json::objectValue;
@@ -557,7 +712,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
}
Json::Value contractsOutput = Json::objectValue;
- for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector<string>())
+ for (string const& contractName: analysisSuccess ? m_compilerStack.contractNames() : vector<string>())
{
size_t colon = contractName.rfind(':');
solAssert(colon != string::npos, "");
@@ -568,7 +723,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
Json::Value contractData(Json::objectValue);
if (isArtifactRequested(outputSelection, file, name, "abi"))
contractData["abi"] = m_compilerStack.contractABI(contractName);
- if (isArtifactRequested(outputSelection, file, name, "metadata"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "metadata"))
contractData["metadata"] = m_compilerStack.metadata(contractName);
if (isArtifactRequested(outputSelection, file, name, "userdoc"))
contractData["userdoc"] = m_compilerStack.natspecUser(contractName);
@@ -578,16 +733,16 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// EVM
Json::Value evmData(Json::objectValue);
// @TODO: add ir
- if (isArtifactRequested(outputSelection, file, name, "evm.assembly"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.assembly"))
evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input));
- if (isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly"))
evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input));
if (isArtifactRequested(outputSelection, file, name, "evm.methodIdentifiers"))
evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName);
- if (isArtifactRequested(outputSelection, file, name, "evm.gasEstimates"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.gasEstimates"))
evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
- if (isArtifactRequested(
+ if (compilationSuccess && isArtifactRequested(
outputSelection,
file,
name,
@@ -598,7 +753,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.sourceMapping(contractName)
);
- if (isArtifactRequested(
+ if (compilationSuccess && isArtifactRequested(
outputSelection,
file,
name,
@@ -609,14 +764,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.runtimeSourceMapping(contractName)
);
- contractData["evm"] = evmData;
-
- if (!contractsOutput.isMember(file))
- contractsOutput[file] = Json::objectValue;
+ if (!evmData.empty())
+ contractData["evm"] = evmData;
- contractsOutput[file][name] = contractData;
+ if (!contractData.empty())
+ {
+ if (!contractsOutput.isMember(file))
+ contractsOutput[file] = Json::objectValue;
+ contractsOutput[file][name] = contractData;
+ }
}
- output["contracts"] = contractsOutput;
+ if (!contractsOutput.empty())
+ output["contracts"] = contractsOutput;
return output;
}
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
index b785d557..efd46d40 100644
--- a/libsolidity/interface/Version.cpp
+++ b/libsolidity/interface/Version.cpp
@@ -21,11 +21,12 @@
*/
#include <libsolidity/interface/Version.h>
-#include <string>
+
+#include <liblangutil/Exceptions.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Common.h>
-#include <liblangutil/Exceptions.h>
#include <solidity/BuildInfo.h>
+#include <string>
using namespace dev;
using namespace dev::solidity;
diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h
index 24c3555d..38d63ec6 100644
--- a/libsolidity/interface/Version.h
+++ b/libsolidity/interface/Version.h
@@ -22,8 +22,8 @@
#pragma once
-#include <string>
#include <libdevcore/Common.h>
+#include <string>
namespace dev
{
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
index d8927fea..d1d45150 100644
--- a/libsolidity/parsing/DocStringParser.cpp
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -1,17 +1,33 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
#include <libsolidity/parsing/DocStringParser.h>
+
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
-#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
+#include <boost/range/irange.hpp>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
-
namespace
{
diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h
index c83b416d..671a2f34 100644
--- a/libsolidity/parsing/DocStringParser.h
+++ b/libsolidity/parsing/DocStringParser.h
@@ -22,8 +22,8 @@
#pragma once
-#include <string>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <string>
namespace langutil
{
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 6cab7be3..8a6bc343 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -20,13 +20,17 @@
* Solidity parser.
*/
-#include <cctype>
-#include <vector>
#include <libsolidity/parsing/Parser.h>
+
+#include <libsolidity/analysis/SemVerHandler.h>
+#include <libsolidity/interface/Version.h>
#include <libyul/AsmParser.h>
-#include <liblangutil/SourceLocation.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h>
+#include <liblangutil/SourceLocation.h>
+#include <cctype>
+#include <vector>
using namespace std;
using namespace langutil;
@@ -42,9 +46,9 @@ class Parser::ASTNodeFactory
{
public:
explicit ASTNodeFactory(Parser const& _parser):
- m_parser(_parser), m_location(_parser.position(), -1, _parser.source()) {}
+ m_parser(_parser), m_location{_parser.position(), -1, _parser.source()} {}
ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
- m_parser(_parser), m_location(_childNode->location()) {}
+ m_parser(_parser), m_location{_childNode->location()} {}
void markEndPosition() { m_location.end = m_parser.endPosition(); }
void setLocation(SourceLocation const& _location) { m_location = _location; }
@@ -104,6 +108,20 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
}
}
+void Parser::parsePragmaVersion(vector<Token> const& tokens, vector<string> const& literals)
+{
+ SemVerMatchExpressionParser parser(tokens, literals);
+ auto matchExpression = parser.parse();
+ static SemVerVersion const currentVersion{string(VersionString)};
+ // FIXME: only match for major version incompatibility
+ if (!matchExpression.matches(currentVersion))
+ fatalParserError(
+ "Source file requires different compiler version (current compiler is " +
+ string(VersionString) + " - note that nightly builds are considered to be "
+ "strictly less than the released version"
+ );
+}
+
ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
{
RecursionGuard recursionGuard(*this);
@@ -132,6 +150,15 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
while (m_scanner->currentToken() != Token::Semicolon && m_scanner->currentToken() != Token::EOS);
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
+
+ if (literals.size() >= 2 && literals[0] == "solidity")
+ {
+ parsePragmaVersion(
+ vector<Token>(tokens.begin() + 1, tokens.end()),
+ vector<string>(literals.begin() + 1, literals.end())
+ );
+ }
+
return nodeFactory.createNode<PragmaDirective>(tokens, literals);
}
@@ -170,7 +197,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
expectToken(Token::As);
alias = expectIdentifierToken();
}
- symbolAliases.push_back(make_pair(move(id), move(alias)));
+ symbolAliases.emplace_back(move(id), move(alias));
if (m_scanner->currentToken() != Token::Comma)
break;
m_scanner->next();
@@ -1012,7 +1039,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
m_scanner->next();
}
- yul::Parser asmParser(m_errorReporter);
+ yul::Parser asmParser(m_errorReporter, yul::EVMDialect::looseAssemblyForEVM());
shared_ptr<yul::Block> block = asmParser.parse(m_scanner, true);
nodeFactory.markEndPosition();
return nodeFactory.createNode<InlineAssembly>(_docString, block);
@@ -1690,7 +1717,7 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
index = parseExpression();
SourceLocation indexLocation = iap.path.front()->location();
indexLocation.end = endPosition();
- iap.indices.push_back(make_pair(index, indexLocation));
+ iap.indices.emplace_back(index, indexLocation);
expectToken(Token::RBrack);
}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 15852096..b8d0e9a8 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -47,7 +47,10 @@ private:
struct VarDeclParserOptions
{
+ // This is actually not needed, but due to a defect in the C++ standard, we have to.
+ // https://stackoverflow.com/questions/17430377
VarDeclParserOptions() {}
+
bool allowVar = false;
bool isStateVariable = false;
bool allowIndexed = false;
@@ -70,6 +73,7 @@ private:
///@{
///@name Parsing functions for the AST nodes
+ void parsePragmaVersion(std::vector<Token> const& tokens, std::vector<std::string> const& literals);
ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<ImportDirective> parseImportDirective();
ContractDefinition::ContractKind parseContractKind();
@@ -84,7 +88,7 @@ private:
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(
- VarDeclParserOptions const& _options = VarDeclParserOptions(),
+ VarDeclParserOptions const& _options = {},
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
);
ASTPointer<ModifierDefinition> parseModifierDefinition();
@@ -98,7 +102,7 @@ private:
ASTPointer<FunctionTypeName> parseFunctionType();
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(
- VarDeclParserOptions const& _options,
+ VarDeclParserOptions const& _options = {},
bool _allowEmpty = true
);
ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {});