mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6679 from ethereum/smt_fix_ssa_reset
[SMTChecker] Fix ICE in touched vars
This commit is contained in:
commit
9e09b5d33b
@ -16,6 +16,7 @@ Bugfixes:
|
|||||||
* SMTChecker: Fix internal error when visiting state variable inherited from base class.
|
* SMTChecker: Fix internal error when visiting state variable inherited from base class.
|
||||||
* SMTChecker: Fix internal error in fixed point operations.
|
* SMTChecker: Fix internal error in fixed point operations.
|
||||||
* SMTChecker: Fix internal error in assignment to unsupported type.
|
* SMTChecker: Fix internal error in assignment to unsupported type.
|
||||||
|
* SMTChecker: Fix internal error in branching when inlining function calls that modify local variables.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -400,6 +400,21 @@ SourceUnit const& Scopable::sourceUnit() const
|
|||||||
return dynamic_cast<SourceUnit const&>(*s);
|
return dynamic_cast<SourceUnit const&>(*s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallableDeclaration const* Scopable::functionOrModifierDefinition() const
|
||||||
|
{
|
||||||
|
ASTNode const* s = scope();
|
||||||
|
solAssert(s, "");
|
||||||
|
while (dynamic_cast<Scopable const*>(s))
|
||||||
|
{
|
||||||
|
if (auto funDef = dynamic_cast<FunctionDefinition const*>(s))
|
||||||
|
return funDef;
|
||||||
|
if (auto modDef = dynamic_cast<ModifierDefinition const*>(s))
|
||||||
|
return modDef;
|
||||||
|
s = dynamic_cast<Scopable const*>(s)->scope();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
string Scopable::sourceUnitName() const
|
string Scopable::sourceUnitName() const
|
||||||
{
|
{
|
||||||
return sourceUnit().annotation().path;
|
return sourceUnit().annotation().path;
|
||||||
|
@ -161,6 +161,9 @@ public:
|
|||||||
/// @returns the source unit this scopable is present in.
|
/// @returns the source unit this scopable is present in.
|
||||||
SourceUnit const& sourceUnit() const;
|
SourceUnit const& sourceUnit() const;
|
||||||
|
|
||||||
|
/// @returns the function or modifier definition this scopable is present in or nullptr.
|
||||||
|
CallableDeclaration const* functionOrModifierDefinition() const;
|
||||||
|
|
||||||
/// @returns the source name this scopable is present in.
|
/// @returns the source name this scopable is present in.
|
||||||
/// Can be combined with annotation().canonicalName (if present) to form a globally unique name.
|
/// Can be combined with annotation().canonicalName (if present) to form a globally unique name.
|
||||||
std::string sourceUnitName() const;
|
std::string sourceUnitName() const;
|
||||||
|
@ -38,6 +38,7 @@ class SourceUnit;
|
|||||||
class PragmaDirective;
|
class PragmaDirective;
|
||||||
class ImportDirective;
|
class ImportDirective;
|
||||||
class Declaration;
|
class Declaration;
|
||||||
|
class CallableDeclaration;
|
||||||
class ContractDefinition;
|
class ContractDefinition;
|
||||||
class InheritanceSpecifier;
|
class InheritanceSpecifier;
|
||||||
class UsingForDirective;
|
class UsingForDirective;
|
||||||
|
@ -103,25 +103,14 @@ bool SMTChecker::visit(ModifierDefinition const&)
|
|||||||
|
|
||||||
bool SMTChecker::visit(FunctionDefinition const& _function)
|
bool SMTChecker::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
m_functionPath.push_back(&_function);
|
|
||||||
m_modifierDepthStack.push_back(-1);
|
|
||||||
|
|
||||||
if (_function.isConstructor())
|
|
||||||
{
|
|
||||||
m_errorReporter.warning(
|
|
||||||
_function.location(),
|
|
||||||
"Assertion checker does not yet support constructors."
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not visited by a function call
|
// Not visited by a function call
|
||||||
if (isRootFunction())
|
if (m_callStack.empty())
|
||||||
{
|
{
|
||||||
m_interface->reset();
|
m_interface->reset();
|
||||||
m_context.reset();
|
m_context.reset();
|
||||||
m_pathConditions.clear();
|
m_pathConditions.clear();
|
||||||
m_callStack.clear();
|
m_callStack.clear();
|
||||||
|
pushCallStack({&_function, nullptr});
|
||||||
m_expressions.clear();
|
m_expressions.clear();
|
||||||
m_globalContext.clear();
|
m_globalContext.clear();
|
||||||
m_uninterpretedTerms.clear();
|
m_uninterpretedTerms.clear();
|
||||||
@ -132,20 +121,31 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
|
|||||||
m_arrayAssignmentHappened = false;
|
m_arrayAssignmentHappened = false;
|
||||||
m_externalFunctionCallHappened = false;
|
m_externalFunctionCallHappened = false;
|
||||||
}
|
}
|
||||||
_function.parameterList().accept(*this);
|
m_modifierDepthStack.push_back(-1);
|
||||||
if (_function.returnParameterList())
|
if (_function.isConstructor())
|
||||||
_function.returnParameterList()->accept(*this);
|
{
|
||||||
visitFunctionOrModifier();
|
m_errorReporter.warning(
|
||||||
|
_function.location(),
|
||||||
|
"Assertion checker does not yet support constructors."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_function.parameterList().accept(*this);
|
||||||
|
if (_function.returnParameterList())
|
||||||
|
_function.returnParameterList()->accept(*this);
|
||||||
|
visitFunctionOrModifier();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTChecker::visitFunctionOrModifier()
|
void SMTChecker::visitFunctionOrModifier()
|
||||||
{
|
{
|
||||||
solAssert(!m_functionPath.empty(), "");
|
solAssert(!m_callStack.empty(), "");
|
||||||
solAssert(!m_modifierDepthStack.empty(), "");
|
solAssert(!m_modifierDepthStack.empty(), "");
|
||||||
|
|
||||||
++m_modifierDepthStack.back();
|
++m_modifierDepthStack.back();
|
||||||
FunctionDefinition const& function = *m_functionPath.back();
|
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(*m_callStack.back().first);
|
||||||
|
|
||||||
if (m_modifierDepthStack.back() == int(function.modifiers().size()))
|
if (m_modifierDepthStack.back() == int(function.modifiers().size()))
|
||||||
{
|
{
|
||||||
@ -166,7 +166,7 @@ void SMTChecker::visitFunctionOrModifier()
|
|||||||
for (auto arg: *modifierInvocation->arguments())
|
for (auto arg: *modifierInvocation->arguments())
|
||||||
modifierArgsExpr.push_back(expr(*arg));
|
modifierArgsExpr.push_back(expr(*arg));
|
||||||
initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
|
initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
|
||||||
pushCallStack(modifierInvocation.get());
|
pushCallStack({&modifierDef, modifierInvocation.get()});
|
||||||
modifierDef.body().accept(*this);
|
modifierDef.body().accept(*this);
|
||||||
popCallStack();
|
popCallStack();
|
||||||
}
|
}
|
||||||
@ -176,8 +176,8 @@ void SMTChecker::visitFunctionOrModifier()
|
|||||||
|
|
||||||
bool SMTChecker::visit(PlaceholderStatement const&)
|
bool SMTChecker::visit(PlaceholderStatement const&)
|
||||||
{
|
{
|
||||||
solAssert(!m_functionPath.empty(), "");
|
solAssert(!m_callStack.empty(), "");
|
||||||
ASTNode const* lastCall = popCallStack();
|
auto lastCall = popCallStack();
|
||||||
visitFunctionOrModifier();
|
visitFunctionOrModifier();
|
||||||
pushCallStack(lastCall);
|
pushCallStack(lastCall);
|
||||||
return true;
|
return true;
|
||||||
@ -185,20 +185,20 @@ bool SMTChecker::visit(PlaceholderStatement const&)
|
|||||||
|
|
||||||
void SMTChecker::endVisit(FunctionDefinition const&)
|
void SMTChecker::endVisit(FunctionDefinition const&)
|
||||||
{
|
{
|
||||||
|
m_callStack.pop_back();
|
||||||
|
solAssert(m_modifierDepthStack.back() == -1, "");
|
||||||
|
m_modifierDepthStack.pop_back();
|
||||||
// If _function was visited from a function call we don't remove
|
// If _function was visited from a function call we don't remove
|
||||||
// the local variables just yet, since we might need them for
|
// the local variables just yet, since we might need them for
|
||||||
// future calls.
|
// future calls.
|
||||||
// Otherwise we remove any local variables from the context and
|
// Otherwise we remove any local variables from the context and
|
||||||
// keep the state variables.
|
// keep the state variables.
|
||||||
if (isRootFunction())
|
if (m_callStack.empty())
|
||||||
{
|
{
|
||||||
checkUnderOverflow();
|
checkUnderOverflow();
|
||||||
removeLocalVariables();
|
removeLocalVariables();
|
||||||
solAssert(m_callStack.empty(), "");
|
solAssert(m_callStack.empty(), "");
|
||||||
}
|
}
|
||||||
m_functionPath.pop_back();
|
|
||||||
solAssert(m_modifierDepthStack.back() == -1, "");
|
|
||||||
m_modifierDepthStack.pop_back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SMTChecker::visit(InlineAssembly const& _inlineAsm)
|
bool SMTChecker::visit(InlineAssembly const& _inlineAsm)
|
||||||
@ -391,7 +391,7 @@ void SMTChecker::endVisit(Assignment const& _assignment)
|
|||||||
vector<smt::Expression> rightArguments;
|
vector<smt::Expression> rightArguments;
|
||||||
if (_assignment.rightHandSide().annotation().type->category() == Type::Category::Tuple)
|
if (_assignment.rightHandSide().annotation().type->category() == Type::Category::Tuple)
|
||||||
{
|
{
|
||||||
auto const& symbTuple = dynamic_pointer_cast<SymbolicTupleVariable>(m_expressions[&_assignment.rightHandSide()]);
|
auto symbTuple = dynamic_pointer_cast<SymbolicTupleVariable>(m_expressions[&_assignment.rightHandSide()]);
|
||||||
solAssert(symbTuple, "");
|
solAssert(symbTuple, "");
|
||||||
for (auto const& component: symbTuple->components())
|
for (auto const& component: symbTuple->components())
|
||||||
{
|
{
|
||||||
@ -666,9 +666,7 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
|
|||||||
visitGasLeft(_funCall);
|
visitGasLeft(_funCall);
|
||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
pushCallStack(&_funCall);
|
|
||||||
inlineFunctionCall(_funCall);
|
inlineFunctionCall(_funCall);
|
||||||
popCallStack();
|
|
||||||
break;
|
break;
|
||||||
case FunctionType::Kind::External:
|
case FunctionType::Kind::External:
|
||||||
case FunctionType::Kind::DelegateCall:
|
case FunctionType::Kind::DelegateCall:
|
||||||
@ -756,10 +754,8 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
|
|||||||
_funCall.location(),
|
_funCall.location(),
|
||||||
"Assertion checker does not yet implement this type of function call."
|
"Assertion checker does not yet implement this type of function call."
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (visitedFunction(funDef))
|
||||||
if (visitedFunction(funDef))
|
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
_funCall.location(),
|
_funCall.location(),
|
||||||
"Assertion checker does not support recursive function calls.",
|
"Assertion checker does not support recursive function calls.",
|
||||||
@ -783,7 +779,12 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
|
|||||||
funArgs.push_back(expr(*arg));
|
funArgs.push_back(expr(*arg));
|
||||||
initializeFunctionCallParameters(*funDef, funArgs);
|
initializeFunctionCallParameters(*funDef, funArgs);
|
||||||
|
|
||||||
|
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)
|
||||||
|
// is that there we don't have `_funCall`.
|
||||||
|
pushCallStack({funDef, &_funCall});
|
||||||
funDef->accept(*this);
|
funDef->accept(*this);
|
||||||
|
// The callstack entry is popped only in endVisit(FunctionDefinition) instead of here
|
||||||
|
// as well to avoid code duplication (not all entries are from inlined function calls).
|
||||||
|
|
||||||
createExpr(_funCall);
|
createExpr(_funCall);
|
||||||
auto const& returnParams = funDef->returnParameters();
|
auto const& returnParams = funDef->returnParameters();
|
||||||
@ -920,7 +921,7 @@ void SMTChecker::endVisit(Return const& _return)
|
|||||||
{
|
{
|
||||||
if (_return.expression() && knownExpr(*_return.expression()))
|
if (_return.expression() && knownExpr(*_return.expression()))
|
||||||
{
|
{
|
||||||
auto returnParams = m_functionPath.back()->returnParameters();
|
auto returnParams = m_callStack.back().first->returnParameters();
|
||||||
if (returnParams.size() > 1)
|
if (returnParams.size() > 1)
|
||||||
{
|
{
|
||||||
auto tuple = dynamic_cast<TupleExpression const*>(_return.expression());
|
auto tuple = dynamic_cast<TupleExpression const*>(_return.expression());
|
||||||
@ -1423,7 +1424,7 @@ void SMTChecker::checkCondition(
|
|||||||
|
|
||||||
vector<smt::Expression> expressionsToEvaluate;
|
vector<smt::Expression> expressionsToEvaluate;
|
||||||
vector<string> expressionNames;
|
vector<string> expressionNames;
|
||||||
if (m_functionPath.size())
|
if (m_callStack.size())
|
||||||
{
|
{
|
||||||
solAssert(m_scanner, "");
|
solAssert(m_scanner, "");
|
||||||
if (_additionalValue)
|
if (_additionalValue)
|
||||||
@ -1491,7 +1492,7 @@ void SMTChecker::checkCondition(
|
|||||||
{
|
{
|
||||||
std::ostringstream message;
|
std::ostringstream message;
|
||||||
message << _description << " happens here";
|
message << _description << " happens here";
|
||||||
if (m_functionPath.size())
|
if (m_callStack.size())
|
||||||
{
|
{
|
||||||
std::ostringstream modelMessage;
|
std::ostringstream modelMessage;
|
||||||
modelMessage << " for:\n";
|
modelMessage << " for:\n";
|
||||||
@ -1864,25 +1865,27 @@ smt::Expression SMTChecker::currentPathConditions()
|
|||||||
SecondarySourceLocation SMTChecker::currentCallStack()
|
SecondarySourceLocation SMTChecker::currentCallStack()
|
||||||
{
|
{
|
||||||
SecondarySourceLocation callStackLocation;
|
SecondarySourceLocation callStackLocation;
|
||||||
if (m_callStack.empty())
|
solAssert(!m_callStack.empty(), "");
|
||||||
return callStackLocation;
|
|
||||||
callStackLocation.append("Callstack: ", SourceLocation());
|
callStackLocation.append("Callstack: ", SourceLocation());
|
||||||
for (auto const& call: m_callStack | boost::adaptors::reversed)
|
for (auto const& call: m_callStack | boost::adaptors::reversed)
|
||||||
callStackLocation.append("", call->location());
|
if (call.second)
|
||||||
|
callStackLocation.append("", call.second->location());
|
||||||
|
// The first function in the tx has no FunctionCall.
|
||||||
|
solAssert(m_callStack.front().second == nullptr, "");
|
||||||
return callStackLocation;
|
return callStackLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode const* SMTChecker::popCallStack()
|
pair<CallableDeclaration const*, ASTNode const*> SMTChecker::popCallStack()
|
||||||
{
|
{
|
||||||
solAssert(!m_callStack.empty(), "");
|
solAssert(!m_callStack.empty(), "");
|
||||||
ASTNode const* lastCalled = m_callStack.back();
|
auto lastCalled = m_callStack.back();
|
||||||
m_callStack.pop_back();
|
m_callStack.pop_back();
|
||||||
return lastCalled;
|
return lastCalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTChecker::pushCallStack(ASTNode const* _node)
|
void SMTChecker::pushCallStack(CallStackEntry _entry)
|
||||||
{
|
{
|
||||||
m_callStack.push_back(_node);
|
m_callStack.push_back(_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTChecker::addPathConjoinedExpression(smt::Expression const& _e)
|
void SMTChecker::addPathConjoinedExpression(smt::Expression const& _e)
|
||||||
@ -1897,12 +1900,15 @@ void SMTChecker::addPathImpliedExpression(smt::Expression const& _e)
|
|||||||
|
|
||||||
bool SMTChecker::isRootFunction()
|
bool SMTChecker::isRootFunction()
|
||||||
{
|
{
|
||||||
return m_functionPath.size() == 1;
|
return m_callStack.size() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SMTChecker::visitedFunction(FunctionDefinition const* _funDef)
|
bool SMTChecker::visitedFunction(FunctionDefinition const* _funDef)
|
||||||
{
|
{
|
||||||
return contains(m_functionPath, _funDef);
|
for (auto const& call: m_callStack)
|
||||||
|
if (call.first == _funDef)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SMTChecker::VariableIndices SMTChecker::copyVariableIndices()
|
SMTChecker::VariableIndices SMTChecker::copyVariableIndices()
|
||||||
@ -1950,8 +1956,11 @@ FunctionDefinition const* SMTChecker::inlinedFunctionCallToDefinition(FunctionCa
|
|||||||
|
|
||||||
set<VariableDeclaration const*> SMTChecker::touchedVariables(ASTNode const& _node)
|
set<VariableDeclaration const*> SMTChecker::touchedVariables(ASTNode const& _node)
|
||||||
{
|
{
|
||||||
solAssert(!m_functionPath.empty(), "");
|
solAssert(!m_callStack.empty(), "");
|
||||||
return m_variableUsage.touchedVariables(_node, m_functionPath);
|
vector<CallableDeclaration const*> callStack;
|
||||||
|
for (auto const& call: m_callStack)
|
||||||
|
callStack.push_back(call.first);
|
||||||
|
return m_variableUsage.touchedVariables(_node, callStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableDeclaration const* SMTChecker::identifierToVariable(Expression const& _expr)
|
VariableDeclaration const* SMTChecker::identifierToVariable(Expression const& _expr)
|
||||||
|
@ -173,6 +173,8 @@ private:
|
|||||||
std::string const& _description
|
std::string const& _description
|
||||||
);
|
);
|
||||||
|
|
||||||
|
using CallStackEntry = std::pair<CallableDeclaration const*, ASTNode const*>;
|
||||||
|
|
||||||
struct OverflowTarget
|
struct OverflowTarget
|
||||||
{
|
{
|
||||||
enum class Type { Underflow, Overflow, All } type;
|
enum class Type { Underflow, Overflow, All } type;
|
||||||
@ -180,9 +182,9 @@ private:
|
|||||||
smt::Expression value;
|
smt::Expression value;
|
||||||
smt::Expression path;
|
smt::Expression path;
|
||||||
langutil::SourceLocation const& location;
|
langutil::SourceLocation const& location;
|
||||||
std::vector<ASTNode const*> callStack;
|
std::vector<CallStackEntry> callStack;
|
||||||
|
|
||||||
OverflowTarget(Type _type, TypePointer _intType, smt::Expression _value, smt::Expression _path, langutil::SourceLocation const& _location, std::vector<ASTNode const*> _callStack):
|
OverflowTarget(Type _type, TypePointer _intType, smt::Expression _value, smt::Expression _path, langutil::SourceLocation const& _location, std::vector<CallStackEntry> _callStack):
|
||||||
type(_type),
|
type(_type),
|
||||||
intType(_intType),
|
intType(_intType),
|
||||||
value(_value),
|
value(_value),
|
||||||
@ -266,9 +268,9 @@ private:
|
|||||||
/// Returns the current callstack. Used for models.
|
/// Returns the current callstack. Used for models.
|
||||||
langutil::SecondarySourceLocation currentCallStack();
|
langutil::SecondarySourceLocation currentCallStack();
|
||||||
/// Copies and pops the last called node.
|
/// Copies and pops the last called node.
|
||||||
ASTNode const* popCallStack();
|
CallStackEntry popCallStack();
|
||||||
/// Adds @param _node to the callstack.
|
/// Adds (_definition, _node) to the callstack.
|
||||||
void pushCallStack(ASTNode const* _node);
|
void pushCallStack(CallStackEntry _entry);
|
||||||
/// Conjoin the current path conditions with the given parameter and add to the solver
|
/// Conjoin the current path conditions with the given parameter and add to the solver
|
||||||
void addPathConjoinedExpression(smt::Expression const& _e);
|
void addPathConjoinedExpression(smt::Expression const& _e);
|
||||||
/// Add to the solver: the given expression implied by the current path conditions
|
/// Add to the solver: the given expression implied by the current path conditions
|
||||||
@ -315,10 +317,8 @@ private:
|
|||||||
langutil::ErrorList m_smtErrors;
|
langutil::ErrorList m_smtErrors;
|
||||||
std::shared_ptr<langutil::Scanner> m_scanner;
|
std::shared_ptr<langutil::Scanner> m_scanner;
|
||||||
|
|
||||||
/// Stores the current path of function calls.
|
/// Stores the current function/modifier call/invocation path.
|
||||||
std::vector<FunctionDefinition const*> m_functionPath;
|
std::vector<CallStackEntry> m_callStack;
|
||||||
/// Stores the current call/invocation path.
|
|
||||||
std::vector<ASTNode const*> m_callStack;
|
|
||||||
/// Returns true if the current function was not visited by
|
/// Returns true if the current function was not visited by
|
||||||
/// a function call.
|
/// a function call.
|
||||||
bool isRootFunction();
|
bool isRootFunction();
|
||||||
|
@ -31,26 +31,30 @@ void VariableUsage::endVisit(Identifier const& _identifier)
|
|||||||
solAssert(declaration, "");
|
solAssert(declaration, "");
|
||||||
if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
if (_identifier.annotation().lValueRequested)
|
if (_identifier.annotation().lValueRequested)
|
||||||
m_touchedVariables.insert(varDecl);
|
{
|
||||||
|
solAssert(m_lastCall, "");
|
||||||
|
if (!varDecl->isLocalVariable() || varDecl->functionOrModifierDefinition() == m_lastCall)
|
||||||
|
m_touchedVariables.insert(varDecl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariableUsage::endVisit(FunctionCall const& _funCall)
|
void VariableUsage::endVisit(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
if (auto const& funDef = SMTChecker::inlinedFunctionCallToDefinition(_funCall))
|
if (auto const& funDef = SMTChecker::inlinedFunctionCallToDefinition(_funCall))
|
||||||
if (find(m_functionPath.begin(), m_functionPath.end(), funDef) == m_functionPath.end())
|
if (find(m_callStack.begin(), m_callStack.end(), funDef) == m_callStack.end())
|
||||||
funDef->accept(*this);
|
funDef->accept(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VariableUsage::visit(FunctionDefinition const& _function)
|
bool VariableUsage::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
m_functionPath.push_back(&_function);
|
m_callStack.push_back(&_function);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariableUsage::endVisit(FunctionDefinition const&)
|
void VariableUsage::endVisit(FunctionDefinition const&)
|
||||||
{
|
{
|
||||||
solAssert(!m_functionPath.empty(), "");
|
solAssert(!m_callStack.empty(), "");
|
||||||
m_functionPath.pop_back();
|
m_callStack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariableUsage::endVisit(ModifierInvocation const& _modifierInv)
|
void VariableUsage::endVisit(ModifierInvocation const& _modifierInv)
|
||||||
@ -62,18 +66,21 @@ void VariableUsage::endVisit(ModifierInvocation const& _modifierInv)
|
|||||||
|
|
||||||
void VariableUsage::endVisit(PlaceholderStatement const&)
|
void VariableUsage::endVisit(PlaceholderStatement const&)
|
||||||
{
|
{
|
||||||
solAssert(!m_functionPath.empty(), "");
|
solAssert(!m_callStack.empty(), "");
|
||||||
FunctionDefinition const* function = m_functionPath.back();
|
FunctionDefinition const* funDef = nullptr;
|
||||||
solAssert(function, "");
|
for (auto it = m_callStack.rbegin(); it != m_callStack.rend() && !funDef; ++it)
|
||||||
if (function->isImplemented())
|
funDef = dynamic_cast<FunctionDefinition const*>(*it);
|
||||||
function->body().accept(*this);
|
solAssert(funDef, "");
|
||||||
|
if (funDef->isImplemented())
|
||||||
|
funDef->body().accept(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
set<VariableDeclaration const*> VariableUsage::touchedVariables(ASTNode const& _node, vector<FunctionDefinition const*> const& _outerCallstack)
|
set<VariableDeclaration const*> VariableUsage::touchedVariables(ASTNode const& _node, vector<CallableDeclaration const*> const& _outerCallstack)
|
||||||
{
|
{
|
||||||
m_touchedVariables.clear();
|
m_touchedVariables.clear();
|
||||||
m_functionPath.clear();
|
m_callStack.clear();
|
||||||
m_functionPath += _outerCallstack;
|
m_callStack += _outerCallstack;
|
||||||
|
m_lastCall = m_callStack.back();
|
||||||
_node.accept(*this);
|
_node.accept(*this);
|
||||||
return m_touchedVariables;
|
return m_touchedVariables;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class VariableUsage: private ASTConstVisitor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param _outerCallstack the current callstack in the callers context.
|
/// @param _outerCallstack the current callstack in the callers context.
|
||||||
std::set<VariableDeclaration const*> touchedVariables(ASTNode const& _node, std::vector<FunctionDefinition const*> const& _outerCallstack);
|
std::set<VariableDeclaration const*> touchedVariables(ASTNode const& _node, std::vector<CallableDeclaration const*> const& _outerCallstack);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void endVisit(Identifier const& _node) override;
|
void endVisit(Identifier const& _node) override;
|
||||||
@ -45,7 +45,8 @@ private:
|
|||||||
void endVisit(PlaceholderStatement const& _node) override;
|
void endVisit(PlaceholderStatement const& _node) override;
|
||||||
|
|
||||||
std::set<VariableDeclaration const*> m_touchedVariables;
|
std::set<VariableDeclaration const*> m_touchedVariables;
|
||||||
std::vector<FunctionDefinition const*> m_functionPath;
|
std::vector<CallableDeclaration const*> m_callStack;
|
||||||
|
CallableDeclaration const* m_lastCall = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract ContractWithFunctionCalled {
|
||||||
|
function funcCalled() external {
|
||||||
|
uint256 i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ContractWithFunctionCalledSuper is ContractWithFunctionCalled {
|
||||||
|
function callWithSuper() public {
|
||||||
|
uint256 i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ContractWithFunctionNotCalled {
|
||||||
|
|
||||||
|
function funcNotCalled3() public {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function funcNotCalled2() public {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function funcNotCalled() public {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function my_func() internal returns(bool){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ContractWithFunctionNotCalled2 is ContractWithFunctionCalledSuper {
|
||||||
|
function funcNotCalled() public {
|
||||||
|
uint256 i = 0;
|
||||||
|
address three = address(new ContractWithFunctionNotCalled());
|
||||||
|
three.call(abi.encode(bytes4(keccak256("helloTwo()"))));
|
||||||
|
super.callWithSuper();
|
||||||
|
ContractWithFunctionCalled c = new ContractWithFunctionCalled();
|
||||||
|
c.funcCalled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract InternalCall {
|
||||||
|
|
||||||
|
function() returns(uint) ptr;
|
||||||
|
|
||||||
|
function set_test1() external{
|
||||||
|
ptr = test1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_test2() external{
|
||||||
|
ptr = test2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test1() public returns(uint){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test2() public returns(uint){
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test3() public returns(uint){
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec() external returns(uint){
|
||||||
|
return ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (760-815): Return value of low-level calls not used.
|
||||||
|
// Warning: (117-126): Unused local variable.
|
||||||
|
// Warning: (260-269): Unused local variable.
|
||||||
|
// Warning: (667-676): Unused local variable.
|
||||||
|
// Warning: (75-137): Function state mutability can be restricted to pure
|
||||||
|
// Warning: (218-280): Function state mutability can be restricted to pure
|
||||||
|
// Warning: (470-539): Function state mutability can be restricted to pure
|
||||||
|
// Warning: (1144-1206): Function state mutability can be restricted to pure
|
||||||
|
// Warning: (1212-1274): Function state mutability can be restricted to pure
|
||||||
|
// Warning: (1280-1342): Function state mutability can be restricted to pure
|
||||||
|
// Warning: (714-749): Internal error: Expression undefined for SMT solver.
|
||||||
|
// Warning: (799-811): Assertion checker does not yet support the type of this literal (literal_string "helloTwo()").
|
||||||
|
// Warning: (782-813): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (771-814): Assertion checker does not yet implement this type of function call.
|
||||||
|
// Warning: (825-830): Assertion checker does not yet support the type of this variable.
|
||||||
|
// Warning: (887-919): Internal error: Expression undefined for SMT solver.
|
||||||
|
// Warning: (690-750): Underflow (resulting value less than 0) happens here
|
||||||
|
// Warning: (690-750): Overflow (resulting value larger than 2**160 - 1) happens here
|
||||||
|
// Warning: (1057-1068): Assertion checker does not yet implement type function () returns (uint256)
|
||||||
|
// Warning: (1120-1131): Assertion checker does not yet implement type function () returns (uint256)
|
||||||
|
// Warning: (1403-1408): Assertion checker does not yet implement this type of function call.
|
@ -0,0 +1,20 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
function f() public pure {
|
||||||
|
if (true) {
|
||||||
|
address a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (208-218): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (123-133): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (208-218): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,27 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
function f() public pure {
|
||||||
|
if (true) {
|
||||||
|
address a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
address b = g();
|
||||||
|
assert(b == address(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (271-281): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (123-133): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (271-281): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (186-196): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (271-281): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,27 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
function f() public pure {
|
||||||
|
if (true) {
|
||||||
|
address a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
address a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (275-285): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (123-133): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (275-285): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (189-199): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (275-285): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,31 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
function f() public pure {
|
||||||
|
if (true) {
|
||||||
|
address a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
address a = h();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
function h() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (275-285): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (123-133): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (189-199): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (275-285): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,21 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
function f() public pure {
|
||||||
|
if (true) {
|
||||||
|
} else {
|
||||||
|
address a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address x;
|
||||||
|
x = address(0);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (219-229): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (134-144): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (219-229): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,24 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
modifier m(address a) {
|
||||||
|
if (true) {
|
||||||
|
a = g();
|
||||||
|
_;
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(address a) m(a) public pure {
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (249-259): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (118-128): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (249-259): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,25 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
modifier m {
|
||||||
|
if (true)
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(address a) m public pure {
|
||||||
|
if (true) {
|
||||||
|
a = g();
|
||||||
|
assert(a == address(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (247-257): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (162-172): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (247-257): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,20 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
function f(uint _x) public pure returns (uint) {
|
||||||
|
return _x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract D
|
||||||
|
{
|
||||||
|
C c;
|
||||||
|
function g(uint _y) public view {
|
||||||
|
uint z = c.f(_y);
|
||||||
|
assert(z == _y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (180-187): Internal error: Expression undefined for SMT solver.
|
||||||
|
// Warning: (191-206): Assertion violation happens here
|
@ -0,0 +1,21 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C
|
||||||
|
{
|
||||||
|
address owner;
|
||||||
|
modifier m {
|
||||||
|
if (true)
|
||||||
|
owner = g();
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
function f() m public {
|
||||||
|
}
|
||||||
|
function g() public pure returns (address) {
|
||||||
|
address a;
|
||||||
|
a = address(0);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (205-215): Type conversion is not yet fully supported and might yield false positives.
|
||||||
|
// Warning: (205-215): Type conversion is not yet fully supported and might yield false positives.
|
@ -22,5 +22,4 @@ contract C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning: (86-93): Condition is always true.
|
|
||||||
// Warning: (311-324): Assertion violation happens here
|
// Warning: (311-324): Assertion violation happens here
|
||||||
|
@ -15,4 +15,3 @@ contract C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning: (127-132): Condition is always true.
|
|
||||||
|
@ -18,4 +18,3 @@ contract C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning: (138-144): Condition is always false.
|
|
||||||
|
@ -17,5 +17,3 @@ contract C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning: (137-142): Condition is always true.
|
|
||||||
// Warning: (155-164): Condition is always true.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user