mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Directly restart processing calling functions.
This commit is contained in:
parent
a2afe8baeb
commit
5b4a2f66e1
@ -7,6 +7,7 @@ Compiler Features:
|
|||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* Control Flow Graph: Fix incorrectly reported unreachable code.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,9 @@ namespace solidity::frontend
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Find the right scope for the called function: When calling a base function, we keep the most derived, but we use the called contract in case it is a library function or nullptr for a free function
|
/// Find the right scope for the called function: When calling a base function,
|
||||||
|
/// we keep the most derived, but we use the called contract in case it is a
|
||||||
|
/// library function or nullptr for a free function.
|
||||||
ContractDefinition const* findScopeContract(FunctionDefinition const& _function, ContractDefinition const* _callingContract)
|
ContractDefinition const* findScopeContract(FunctionDefinition const& _function, ContractDefinition const* _callingContract)
|
||||||
{
|
{
|
||||||
if (auto const* functionContract = _function.annotation().contract)
|
if (auto const* functionContract = _function.annotation().contract)
|
||||||
@ -46,9 +48,8 @@ ContractDefinition const* findScopeContract(FunctionDefinition const& _function,
|
|||||||
|
|
||||||
void ControlFlowRevertPruner::run()
|
void ControlFlowRevertPruner::run()
|
||||||
{
|
{
|
||||||
// build a lookup table for function calls / callers
|
|
||||||
for (auto& [pair, flow]: m_cfg.allFunctionFlows())
|
for (auto& [pair, flow]: m_cfg.allFunctionFlows())
|
||||||
collectCalls(*pair.function, pair.contract);
|
m_functions[pair] = RevertState::Unknown;
|
||||||
|
|
||||||
findRevertStates();
|
findRevertStates();
|
||||||
modifyFunctionFlows();
|
modifyFunctionFlows();
|
||||||
@ -57,6 +58,10 @@ void ControlFlowRevertPruner::run()
|
|||||||
void ControlFlowRevertPruner::findRevertStates()
|
void ControlFlowRevertPruner::findRevertStates()
|
||||||
{
|
{
|
||||||
std::set<CFG::FunctionContractTuple> pendingFunctions = keys(m_functions);
|
std::set<CFG::FunctionContractTuple> pendingFunctions = keys(m_functions);
|
||||||
|
// We interrupt the search whenever we encounter a call to a function with (yet) unknown
|
||||||
|
// revert behaviour. The ``wakeUp`` data structure contains information about which
|
||||||
|
// searches to restart once we know about the behaviour.
|
||||||
|
std::map<CFG::FunctionContractTuple, std::set<CFG::FunctionContractTuple>> wakeUp;
|
||||||
|
|
||||||
while (!pendingFunctions.empty())
|
while (!pendingFunctions.empty())
|
||||||
{
|
{
|
||||||
@ -83,21 +88,27 @@ void ControlFlowRevertPruner::findRevertStates()
|
|||||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.contract), resolvedFunction}))
|
CFG::FunctionContractTuple calledFunctionTuple{
|
||||||
|
findScopeContract(*resolvedFunction, item.contract),
|
||||||
|
resolvedFunction
|
||||||
|
};
|
||||||
|
switch (m_functions.at(calledFunctionTuple))
|
||||||
{
|
{
|
||||||
case RevertState::Unknown:
|
case RevertState::Unknown:
|
||||||
|
wakeUp[calledFunctionTuple].insert(item);
|
||||||
foundUnknown = true;
|
foundUnknown = true;
|
||||||
return;
|
return;
|
||||||
case RevertState::AllPathsRevert:
|
case RevertState::AllPathsRevert:
|
||||||
return;
|
return;
|
||||||
default:
|
case RevertState::HasNonRevertingPath:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CFGNode* exit: _node->exits)
|
for (CFGNode* exit: _node->exits)
|
||||||
_addChild(exit);
|
_addChild(exit);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
auto& revertState = m_functions[item];
|
auto& revertState = m_functions[item];
|
||||||
|
|
||||||
@ -106,16 +117,14 @@ void ControlFlowRevertPruner::findRevertStates()
|
|||||||
else if (!foundUnknown)
|
else if (!foundUnknown)
|
||||||
revertState = RevertState::AllPathsRevert;
|
revertState = RevertState::AllPathsRevert;
|
||||||
|
|
||||||
// Mark all functions depending on this one as modified again
|
if (revertState != RevertState::Unknown && wakeUp.count(item))
|
||||||
if (revertState != RevertState::Unknown)
|
{
|
||||||
for (auto& nextItem: m_calledBy[item.function])
|
// Restart all searches blocked by this function.
|
||||||
// Ignore different most derived contracts in dependent callees
|
for (CFG::FunctionContractTuple const& nextItem: wakeUp[item])
|
||||||
if (
|
if (m_functions.at(nextItem) == RevertState::Unknown)
|
||||||
item.contract == nullptr ||
|
|
||||||
nextItem.contract == nullptr ||
|
|
||||||
nextItem.contract == item.contract
|
|
||||||
)
|
|
||||||
pendingFunctions.insert(nextItem);
|
pendingFunctions.insert(nextItem);
|
||||||
|
wakeUp.erase(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,27 +168,4 @@ void ControlFlowRevertPruner::modifyFunctionFlows()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlFlowRevertPruner::collectCalls(FunctionDefinition const& _function, ContractDefinition const* _mostDerivedContract)
|
|
||||||
{
|
|
||||||
FunctionFlow const& functionFlow = m_cfg.functionFlow(_function, _mostDerivedContract);
|
|
||||||
|
|
||||||
CFG::FunctionContractTuple pair{_mostDerivedContract, &_function};
|
|
||||||
|
|
||||||
solAssert(m_functions.count(pair) == 0, "");
|
|
||||||
m_functions[pair] = RevertState::Unknown;
|
|
||||||
|
|
||||||
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
|
||||||
[&](CFGNode* _node, auto&& _addChild) {
|
|
||||||
for (auto const* functionCall: _node->functionCalls)
|
|
||||||
{
|
|
||||||
auto const* funcDef = ASTNode::resolveFunctionCall(*functionCall, _mostDerivedContract);
|
|
||||||
if (funcDef && funcDef->isImplemented())
|
|
||||||
m_calledBy[funcDef].insert(pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (CFGNode* exit: _node->exits)
|
|
||||||
_addChild(exit);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,24 +51,12 @@ private:
|
|||||||
/// Modify function flows so that edges with reverting function calls are removed
|
/// Modify function flows so that edges with reverting function calls are removed
|
||||||
void modifyFunctionFlows();
|
void modifyFunctionFlows();
|
||||||
|
|
||||||
|
|
||||||
/// Collect all function calls made by `_function` for later analysis
|
|
||||||
/// @param _function function to analyse
|
|
||||||
/// @param _mostDerivedContract most derived contract used in the calls
|
|
||||||
void collectCalls(FunctionDefinition const& _function, ContractDefinition const* _mostDerivedContract);
|
|
||||||
|
|
||||||
/// Control Flow Graph object.
|
/// Control Flow Graph object.
|
||||||
CFG& m_cfg;
|
CFG& m_cfg;
|
||||||
|
|
||||||
/// function/contract pairs mapped to their according revert state
|
/// function/contract pairs mapped to their according revert state
|
||||||
std::map<CFG::FunctionContractTuple, RevertState> m_functions;
|
std::map<CFG::FunctionContractTuple, RevertState> m_functions;
|
||||||
|
|
||||||
/// Called function mapped to the set of function/contract pairs calling them
|
|
||||||
std::map<
|
|
||||||
FunctionDefinition const*,
|
|
||||||
std::set<CFG::FunctionContractTuple>
|
|
||||||
> m_calledBy;
|
|
||||||
|
|
||||||
std::map<
|
std::map<
|
||||||
std::tuple<FunctionCall const*, ContractDefinition const*>,
|
std::tuple<FunctionCall const*, ContractDefinition const*>,
|
||||||
FunctionDefinition const*
|
FunctionDefinition const*
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
==== Source: a ====
|
||||||
|
import "b";
|
||||||
|
contract Test is Bar {}
|
||||||
|
==== Source: b ====
|
||||||
|
library Foo {
|
||||||
|
function nop() internal {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Bar {
|
||||||
|
function example() public returns (uint256) {
|
||||||
|
foo();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo() public {
|
||||||
|
Foo.nop();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
contract Bar {
|
||||||
|
function example() public {
|
||||||
|
foo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo() internal {
|
||||||
|
Foo.nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Y is Bar {}
|
||||||
|
|
||||||
|
library Foo {
|
||||||
|
function nop() internal {}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user