[SMTChecker] Support try-catch in CHC engine

This commit is contained in:
Martin Blicha 2021-01-04 19:33:07 +01:00
parent f03245d473
commit 0f3924186e
4 changed files with 94 additions and 16 deletions

View File

@ -541,6 +541,74 @@ void CHC::endVisit(Return const& _return)
m_currentBlock = predicate(*returnGhost); m_currentBlock = predicate(*returnGhost);
} }
bool CHC::visit(TryStatement const& _tryStatement)
{
FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
solAssert(externalCall && externalCall->annotation().tryCall, "");
solAssert(m_currentFunction, "");
auto tryHeaderBlock = createBlock(&_tryStatement, PredicateType::FunctionBlock, "try_header_");
auto tryAfterCallSuccessBlock = createBlock(&_tryStatement, PredicateType::FunctionBlock, "try_after_call_success_");
auto tryAfterCallFailBlock = createBlock(&_tryStatement, PredicateType::FunctionBlock, "try_after_call_fail_");
auto afterTryBlock = createBlock(&m_currentFunction->body(), PredicateType::FunctionBlock);
connectBlocks(m_currentBlock, predicate(*tryHeaderBlock));
setCurrentBlock(*tryHeaderBlock);
// fine-grained control over how the external call is visited
// cannot use "_tryStatement.externalCall().accept(*this);" since we need to insert the "connectBlocks"
// visit(*externalCall); currently there is nothing to do here, so it's commented out
externalCall->expression().accept(*this);
ASTNode::listAccept(externalCall->arguments(), *this);
connectBlocks(m_currentBlock, predicate(*tryAfterCallFailBlock));
endVisit(*externalCall);
if (_tryStatement.successClause()->parameters())
{
auto const& params = _tryStatement.successClause()->parameters()->parameters();
if (params.size() > 1)
{
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*externalCall));
solAssert(symbTuple, "");
auto const& symbComponents = symbTuple->components();
solAssert(symbComponents.size() == params.size(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i)
{
auto param = params.at(i);
solAssert(param, "");
solAssert(m_context.knownVariable(*param), "");
assignment(*param, symbTuple->component(i));
}
}
else if (params.size() == 1)
assignment(*params.front(), expr(*externalCall));
}
connectBlocks(m_currentBlock, predicate(*tryAfterCallSuccessBlock));
auto const& clauses = _tryStatement.clauses();
solAssert(clauses[0].get() == _tryStatement.successClause(), "First clause of TryStatement should be the success clause");
auto clauseBlocks = applyMap(clauses, [this](ASTPointer<TryCatchClause> clause) {
return createBlock(clause.get(), PredicateType::FunctionBlock, "try_clause_" + std::to_string(clause->id()));
});
setCurrentBlock(*tryAfterCallSuccessBlock);
connectBlocks(m_currentBlock, predicate(*clauseBlocks[0]));
setCurrentBlock(*tryAfterCallFailBlock);
for (size_t i = 1; i < clauseBlocks.size(); ++i)
connectBlocks(m_currentBlock, predicate(*clauseBlocks[i]));
for (size_t i = 0; i < clauses.size(); ++i)
{
setCurrentBlock(*clauseBlocks[i]);
clauses[i]->accept(*this);
connectBlocks(m_currentBlock, predicate(*afterTryBlock));
}
setCurrentBlock(*afterTryBlock);
return false;
}
void CHC::pushInlineFrame(CallableDeclaration const& _callable) void CHC::pushInlineFrame(CallableDeclaration const& _callable)
{ {
m_returnDests.push_back(createBlock(&_callable, PredicateType::FunctionBlock, "return_")); m_returnDests.push_back(createBlock(&_callable, PredicateType::FunctionBlock, "return_"));

View File

@ -84,6 +84,7 @@ private:
void endVisit(Continue const& _node) override; void endVisit(Continue const& _node) override;
void endVisit(IndexRangeAccess const& _node) override; void endVisit(IndexRangeAccess const& _node) override;
void endVisit(Return const& _node) override; void endVisit(Return const& _node) override;
bool visit(TryStatement const& _node) override;
void pushInlineFrame(CallableDeclaration const& _callable) override; void pushInlineFrame(CallableDeclaration const& _callable) override;
void popInlineFrame(CallableDeclaration const& _callable) override; void popInlineFrame(CallableDeclaration const& _callable) override;

View File

@ -312,20 +312,6 @@ bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
return false; return false;
} }
bool SMTEncoder::visit(TryCatchClause const& _clause)
{
if (auto params = _clause.parameters())
for (auto const& var: params->parameters())
createVariable(*var);
m_errorReporter.warning(
7645_error,
_clause.location(),
"Assertion checker does not support try/catch clauses."
);
return false;
}
void SMTEncoder::pushInlineFrame(CallableDeclaration const&) void SMTEncoder::pushInlineFrame(CallableDeclaration const&)
{ {
pushPathCondition(currentPathConditions()); pushPathCondition(currentPathConditions());
@ -2688,7 +2674,29 @@ vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedA
vector<VariableDeclaration const*> SMTEncoder::localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract) vector<VariableDeclaration const*> SMTEncoder::localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract)
{ {
return _function.localVariables() + modifiersVariables(_function, _contract); return _function.localVariables() + tryCatchVariables(_function) + modifiersVariables(_function, _contract);
}
vector<VariableDeclaration const*> SMTEncoder::tryCatchVariables(FunctionDefinition const& _function)
{
struct TryCatchVarsVisitor : public ASTConstVisitor
{
bool visit(TryCatchClause const& _catchClause) override
{
if (_catchClause.parameters())
{
auto const& params = _catchClause.parameters()->parameters();
for (auto param: params)
vars.push_back(param.get());
}
return false;
}
vector<VariableDeclaration const*> vars;
} tryCatchVarsVisitor;
_function.accept(tryCatchVarsVisitor);
return tryCatchVarsVisitor.vars;
} }
vector<VariableDeclaration const*> SMTEncoder::modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract) vector<VariableDeclaration const*> SMTEncoder::modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract)

View File

@ -76,6 +76,7 @@ public:
static std::vector<VariableDeclaration const*> localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract); static std::vector<VariableDeclaration const*> localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract);
static std::vector<VariableDeclaration const*> modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract); static std::vector<VariableDeclaration const*> modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract);
static std::vector<VariableDeclaration const*> tryCatchVariables(FunctionDefinition const& _function);
/// @returns the ModifierDefinition of a ModifierInvocation if possible, or nullptr. /// @returns the ModifierDefinition of a ModifierInvocation if possible, or nullptr.
static ModifierDefinition const* resolveModifierInvocation(ModifierInvocation const& _invocation, ContractDefinition const* _contract); static ModifierDefinition const* resolveModifierInvocation(ModifierInvocation const& _invocation, ContractDefinition const* _contract);
@ -130,7 +131,7 @@ protected:
bool visit(InlineAssembly const& _node) override; bool visit(InlineAssembly const& _node) override;
void endVisit(Break const&) override {} void endVisit(Break const&) override {}
void endVisit(Continue const&) override {} void endVisit(Continue const&) override {}
bool visit(TryCatchClause const& _node) override; bool visit(TryStatement const&) override { return false; }
virtual void pushInlineFrame(CallableDeclaration const&); virtual void pushInlineFrame(CallableDeclaration const&);
virtual void popInlineFrame(CallableDeclaration const&); virtual void popInlineFrame(CallableDeclaration const&);