Control flow for try statements.

This commit is contained in:
chriseth 2019-09-05 20:02:09 +02:00
parent 644a402166
commit b5bc52f2a7
4 changed files with 88 additions and 2 deletions

View File

@ -85,6 +85,18 @@ bool ControlFlowBuilder::visit(Conditional const& _conditional)
return false;
}
bool ControlFlowBuilder::visit(TryStatement const& _tryStatement)
{
appendControlFlow(_tryStatement.externalCall());
auto nodes = splitFlow(_tryStatement.clauses().size());
for (size_t i = 0; i < _tryStatement.clauses().size(); ++i)
nodes[i] = createFlow(nodes[i], _tryStatement.clauses()[i]->block());
mergeFlow(nodes);
return false;
}
bool ControlFlowBuilder::visit(IfStatement const& _ifStatement)
{
solAssert(!!m_currentNode, "");

View File

@ -45,6 +45,7 @@ private:
// Visits for constructing the control flow.
bool visit(BinaryOperation const& _operation) override;
bool visit(Conditional const& _conditional) override;
bool visit(TryStatement const& _tryStatement) override;
bool visit(IfStatement const& _ifStatement) override;
bool visit(ForStatement const& _forStatement) override;
bool visit(WhileStatement const& _whileStatement) override;
@ -98,12 +99,27 @@ private:
return result;
}
/// Splits the control flow starting at the current node into @a _n paths.
/// m_currentNode is set to nullptr and has to be set manually or
/// using mergeFlow later.
std::vector<CFGNode*> splitFlow(size_t n)
{
std::vector<CFGNode*> result(n);
for (auto& node: result)
{
node = m_nodeContainer.newNode();
connect(m_currentNode, node);
}
m_currentNode = nullptr;
return result;
}
/// Merges the control flow of @a _nodes to @a _endNode.
/// If @a _endNode is nullptr, a new node is creates and used as end node.
/// Sets the merge destination as current node.
/// Note: @a _endNode may be one of the nodes in @a _nodes.
template<size_t n>
void mergeFlow(std::array<CFGNode*, n> const& _nodes, CFGNode* _endNode = nullptr)
template<typename C>
void mergeFlow(C const& _nodes, CFGNode* _endNode = nullptr)
{
CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode;
for (auto& node: _nodes)

View File

@ -0,0 +1,34 @@
contract C {
struct S { bool f; }
S s;
function ext() external {}
function f() internal returns (S storage r)
{
try this.ext() { }
catch (bytes memory) { r = s; }
}
function g() internal returns (S storage r)
{
try this.ext() { r = s; }
catch (bytes memory) { }
}
function h() internal returns (S storage r)
{
try this.ext() {}
catch Error (string memory) { r = s; }
catch (bytes memory) { r = s; }
}
function i() internal returns (S storage r)
{
try this.ext() { r = s; }
catch (bytes memory) { return r; }
r = s;
}
}
// ====
// EVMVersion: >=byzantium
// ----
// TypeError: (113-124): This variable is of storage pointer type and can be returned without prior assignment.
// TypeError: (240-251): This variable is of storage pointer type and can be returned without prior assignment.
// TypeError: (367-378): This variable is of storage pointer type and can be returned without prior assignment.
// TypeError: (631-632): This variable is of storage pointer type and can be accessed without prior assignment.

View File

@ -0,0 +1,24 @@
contract C {
struct S { bool f; }
S s;
function ext() external { }
function f() internal returns (S storage r)
{
try this.ext() { r = s; }
catch (bytes memory) { r = s; }
}
function g() internal returns (S storage r)
{
try this.ext() { r = s; }
catch Error (string memory) { r = s; }
catch (bytes memory) { r = s; }
}
function h() internal returns (S storage r)
{
try this.ext() { }
catch (bytes memory) { }
r = s;
}
}
// ====
// EVMVersion: >=byzantium