mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Move formatFunctionCallCounterexample from CHC to Predicate
This commit is contained in:
parent
5bbb20d3cb
commit
e3a8c94ace
@ -1423,24 +1423,12 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
|
||||
Predicate const* summaryPredicate = Predicate::predicate(summaryNode.first);
|
||||
solAssert(summaryPredicate, "");
|
||||
|
||||
FunctionDefinition const* calledFun = nullptr;
|
||||
ContractDefinition const* calledContract = nullptr;
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(summaryPredicate->programNode()))
|
||||
{
|
||||
if (auto const* constructor = contract->constructor())
|
||||
calledFun = constructor;
|
||||
else
|
||||
calledContract = contract;
|
||||
}
|
||||
else if (auto const* fun = dynamic_cast<FunctionDefinition const*>(summaryPredicate->programNode()))
|
||||
calledFun = fun;
|
||||
else
|
||||
solAssert(false, "");
|
||||
FunctionDefinition const* calledFun = summaryPredicate->programFunction();
|
||||
ContractDefinition const* calledContract = summaryPredicate->programContract();
|
||||
|
||||
solAssert((calledFun && !calledContract) || (!calledFun && calledContract), "");
|
||||
auto const& stateVars = calledFun ? stateVariablesIncludingInheritedAndPrivate(*calledFun) : stateVariablesIncludingInheritedAndPrivate(*calledContract);
|
||||
/// calledContract != nullptr implies that the constructor of the analyzed contract is implicit and
|
||||
/// therefore takes no parameters.
|
||||
auto stateVars = summaryPredicate->stateVariables();
|
||||
solAssert(stateVars.has_value(), "");
|
||||
|
||||
/// This summary node is the end of a tx.
|
||||
/// If it is the first summary node seen in this loop, it is the summary
|
||||
@ -1450,12 +1438,12 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
|
||||
{
|
||||
lastTxSeen = true;
|
||||
/// Generate counterexample message local to the failed target.
|
||||
localState = formatStateCounterexample(stateVars, calledFun, summaryNode.second) + "\n";
|
||||
localState = formatStateCounterexample(*stateVars, calledFun, summaryNode.second) + "\n";
|
||||
if (calledFun)
|
||||
{
|
||||
/// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
|
||||
auto const& inParams = calledFun->parameters();
|
||||
unsigned initLocals = stateVars.size() * 2 + 1 + inParams.size();
|
||||
unsigned initLocals = stateVars->size() * 2 + 1 + inParams.size();
|
||||
/// In this loop we are interested in postInputVars.
|
||||
for (unsigned i = initLocals; i < initLocals + inParams.size(); ++i)
|
||||
{
|
||||
@ -1477,9 +1465,9 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
|
||||
else
|
||||
/// We report the state after every tx in the trace except for the last, which is reported
|
||||
/// first in the code above.
|
||||
path.emplace_back("State: " + formatStateCounterexample(stateVars, calledFun, summaryNode.second));
|
||||
path.emplace_back("State: " + formatStateCounterexample(*stateVars, calledFun, summaryNode.second));
|
||||
|
||||
string txCex = calledContract ? "constructor()" : formatFunctionCallCounterexample(stateVars, *calledFun, summaryNode.second);
|
||||
string txCex = summaryPredicate->formatSummaryCall(summaryNode.second);
|
||||
path.emplace_back(txCex);
|
||||
|
||||
/// Recurse on the next interface node which represents the previous transaction
|
||||
@ -1527,32 +1515,6 @@ string CHC::formatStateCounterexample(vector<VariableDeclaration const*> const&
|
||||
return boost::algorithm::join(stateCex, ", ");
|
||||
}
|
||||
|
||||
string CHC::formatFunctionCallCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const& _function, vector<string> const& _summaryValues)
|
||||
{
|
||||
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
|
||||
/// Here we are interested in preInputVars.
|
||||
vector<string>::const_iterator first = _summaryValues.begin() + static_cast<int>(_stateVars.size()) + 1;
|
||||
vector<string>::const_iterator last = first + static_cast<int>(_function.parameters().size());
|
||||
solAssert(first >= _summaryValues.begin() && first <= _summaryValues.end(), "");
|
||||
solAssert(last >= _summaryValues.begin() && last <= _summaryValues.end(), "");
|
||||
vector<string> functionArgsCex(first, last);
|
||||
vector<string> functionArgs;
|
||||
|
||||
auto const& params = _function.parameters();
|
||||
solAssert(params.size() == functionArgsCex.size(), "");
|
||||
for (unsigned i = 0; i < params.size(); ++i)
|
||||
if (params[i]->type()->isValueType())
|
||||
functionArgs.emplace_back(functionArgsCex[i]);
|
||||
else
|
||||
functionArgs.emplace_back(params[i]->name());
|
||||
|
||||
string fName = _function.isConstructor() ? "constructor" :
|
||||
_function.isFallback() ? "fallback" :
|
||||
_function.isReceive() ? "receive" :
|
||||
_function.name();
|
||||
return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")";
|
||||
}
|
||||
|
||||
string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex)
|
||||
{
|
||||
string dot = "digraph {\n";
|
||||
|
@ -226,10 +226,6 @@ private:
|
||||
/// _function = nullptr means the transaction was the deployment of a
|
||||
/// contract without an explicit constructor.
|
||||
std::string formatStateCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const* _function, std::vector<std::string> const& _summaryValues);
|
||||
/// @returns a formatted text representing a call to _function
|
||||
/// with the concrete values for value type parameters and
|
||||
/// the parameter name for reference types.
|
||||
std::string formatFunctionCallCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const& _function, std::vector<std::string> const& _summaryValues);
|
||||
|
||||
/// @returns a DAG in the dot format.
|
||||
/// Used for debugging purposes.
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include <libsolidity/formal/Predicate.h>
|
||||
|
||||
#include <libsolidity/formal/SMTEncoder.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@ -87,6 +89,96 @@ void Predicate::newFunctor()
|
||||
m_predicate.increaseIndex();
|
||||
}
|
||||
|
||||
ASTNode const* Predicate::programNode() const {
|
||||
ASTNode const* Predicate::programNode() const
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
ContractDefinition const* Predicate::programContract() const
|
||||
{
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_node))
|
||||
if (!contract->constructor())
|
||||
return contract;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionDefinition const* Predicate::programFunction() const
|
||||
{
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_node))
|
||||
{
|
||||
if (contract->constructor())
|
||||
return contract->constructor();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto const* fun = dynamic_cast<FunctionDefinition const*>(m_node))
|
||||
return fun;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<vector<VariableDeclaration const*>> Predicate::stateVariables() const
|
||||
{
|
||||
if (auto const* fun = programFunction())
|
||||
return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun);
|
||||
if (auto const* contract = programContract())
|
||||
return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract);
|
||||
|
||||
auto const* node = m_node;
|
||||
while (auto const* scopable = dynamic_cast<Scopable const*>(node))
|
||||
{
|
||||
node = scopable->scope();
|
||||
if (auto const* fun = dynamic_cast<FunctionDefinition const*>(node))
|
||||
return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun);
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
bool Predicate::isSummary() const
|
||||
{
|
||||
return functor().name.rfind("summary", 0) == 0;
|
||||
}
|
||||
|
||||
bool Predicate::isInterface() const
|
||||
{
|
||||
return functor().name.rfind("interface", 0) == 0;
|
||||
}
|
||||
|
||||
string Predicate::formatSummaryCall(vector<string> const& _args) const
|
||||
{
|
||||
if (programContract())
|
||||
return "constructor()";
|
||||
|
||||
solAssert(isSummary(), "");
|
||||
|
||||
auto stateVars = stateVariables();
|
||||
solAssert(stateVars.has_value(), "");
|
||||
auto const* fun = programFunction();
|
||||
solAssert(fun, "");
|
||||
|
||||
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
|
||||
/// Here we are interested in preInputVars.
|
||||
vector<string>::const_iterator first = _args.begin() + static_cast<int>(stateVars->size()) + 1;
|
||||
vector<string>::const_iterator last = first + static_cast<int>(fun->parameters().size());
|
||||
solAssert(first >= _args.begin() && first <= _args.end(), "");
|
||||
solAssert(last >= _args.begin() && last <= _args.end(), "");
|
||||
vector<string> functionArgsCex(first, last);
|
||||
vector<string> functionArgs;
|
||||
|
||||
auto const& params = fun->parameters();
|
||||
solAssert(params.size() == functionArgsCex.size(), "");
|
||||
for (unsigned i = 0; i < params.size(); ++i)
|
||||
if (params[i]->type()->isValueType())
|
||||
functionArgs.emplace_back(functionArgsCex[i]);
|
||||
else
|
||||
functionArgs.emplace_back(params[i]->name());
|
||||
|
||||
string fName = fun->isConstructor() ? "constructor" :
|
||||
fun->isFallback() ? "fallback" :
|
||||
fun->isReceive() ? "receive" :
|
||||
fun->name();
|
||||
return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")";
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <libsmtutil/Sorts.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend
|
||||
@ -70,6 +71,27 @@ public:
|
||||
/// @returns the program node this predicate represents.
|
||||
ASTNode const* programNode() const;
|
||||
|
||||
/// @returns the ContractDefinition that this predicate represents
|
||||
/// or nullptr otherwise.
|
||||
ContractDefinition const* programContract() const;
|
||||
|
||||
/// @returns the FunctionDefinition that this predicate represents
|
||||
/// or nullptr otherwise.
|
||||
FunctionDefinition const* programFunction() const;
|
||||
|
||||
/// @returns the program state variables in the scope of this predicate.
|
||||
std::optional<std::vector<VariableDeclaration const*>> stateVariables() const;
|
||||
|
||||
/// @returns true if this predicate represents a summary.
|
||||
bool isSummary() const;
|
||||
|
||||
/// @returns true if this predicate represents an interface.
|
||||
bool isInterface() const;
|
||||
|
||||
/// @returns a formatted string representing a call to this predicate
|
||||
/// with _args.
|
||||
std::string formatSummaryCall(std::vector<std::string> const& _args) const;
|
||||
|
||||
private:
|
||||
/// The actual SMT expression.
|
||||
smt::SymbolicFunctionVariable m_predicate;
|
||||
|
Loading…
Reference in New Issue
Block a user