diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index fb766eea9..6774b6e38 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -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, ""); diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index b17483954..91b112eb5 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -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 splitFlow(size_t n) + { + std::vector 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 - void mergeFlow(std::array const& _nodes, CFGNode* _endNode = nullptr) + template + void mergeFlow(C const& _nodes, CFGNode* _endNode = nullptr) { CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode; for (auto& node: _nodes) diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_err.sol new file mode 100644 index 000000000..ee9cb2f2a --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_err.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_fine.sol new file mode 100644 index 000000000..94d4ec1ee --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_fine.sol @@ -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 \ No newline at end of file