Basic support to free functions

This commit is contained in:
Leonardo Alt
2021-04-19 19:23:18 +02:00
parent 6a0a51110d
commit 4e34359063
12 changed files with 90 additions and 67 deletions
+13 -4
View File
@@ -149,11 +149,13 @@ void BMC::endVisit(ContractDefinition const& _contract)
bool BMC::visit(FunctionDefinition const& _function)
{
// Free functions need to be visited in the context of a contract.
if (!m_currentContract)
return false;
auto contract = dynamic_cast<ContractDefinition const*>(_function.scope());
solAssert(contract, "");
solAssert(m_currentContract, "");
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
if (find(hierarchy.begin(), hierarchy.end(), contract) == hierarchy.end())
if (contract && find(hierarchy.begin(), hierarchy.end(), contract) == hierarchy.end())
createStateVariables(*contract);
if (m_callStack.empty())
@@ -165,7 +167,10 @@ bool BMC::visit(FunctionDefinition const& _function)
}
if (_function.isConstructor())
inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope()));
{
solAssert(contract, "");
inlineConstructorHierarchy(*contract);
}
/// Already visits the children.
SMTEncoder::visit(_function);
@@ -175,6 +180,10 @@ bool BMC::visit(FunctionDefinition const& _function)
void BMC::endVisit(FunctionDefinition const& _function)
{
// Free functions need to be visited in the context of a contract.
if (!m_currentContract)
return;
if (isRootFunction())
{
checkVerificationTargets();
+21 -26
View File
@@ -82,10 +82,11 @@ void CHC::analyze(SourceUnit const& _source)
{
resetSourceAnalysis();
set<SourceUnit const*, EncodingContext::IdCompare> sources;
set<SourceUnit const*, ASTNode::CompareByID> sources;
sources.insert(&_source);
for (auto const& source: _source.referencedSourceUnits(true))
sources.insert(source);
collectFreeFunctions(sources);
for (auto const* source: sources)
defineInterfacesAndSummaries(*source);
for (auto const* source: sources)
@@ -219,6 +220,10 @@ void CHC::endVisit(ContractDefinition const& _contract)
bool CHC::visit(FunctionDefinition const& _function)
{
// Free functions need to be visited in the context of a contract.
if (!m_currentContract)
return false;
if (!_function.isImplemented())
{
addRule(summary(_function), "summary_function_" + to_string(_function.id()));
@@ -258,6 +263,10 @@ bool CHC::visit(FunctionDefinition const& _function)
void CHC::endVisit(FunctionDefinition const& _function)
{
// Free functions need to be visited in the context of a contract.
if (!m_currentContract)
return;
if (!_function.isImplemented())
return;
@@ -677,19 +686,13 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall)
{
solAssert(m_currentContract, "");
auto scopeContract = currentScopeContract();
auto function = functionCallToDefinition(_funCall, scopeContract, m_currentContract);
auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
if (function)
{
if (m_currentFunction && !m_currentFunction->isConstructor())
m_callGraph[m_currentFunction].insert(function);
else
m_callGraph[m_currentContract].insert(function);
// Libraries can have constants as their "state" variables,
// so we need to ensure they were constructed correctly.
if (function->annotation().contract->isLibrary())
m_context.addAssertion(interface(*scopeContract));
}
m_context.addAssertion(predicate(_funCall));
@@ -1045,7 +1048,7 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
addRule(smtutil::Expression::implies(errorFlag().currentValue() == 0, smt::nondetInterface(iface, *contract, m_context, 0, 0)), "base_nondet");
auto const& resolved = contractFunctions(*contract);
for (auto const* function: contractFunctionsWithoutVirtual(*contract))
for (auto const* function: contractFunctionsWithoutVirtual(*contract) + allFreeFunctions())
{
for (auto var: function->parameters())
createVariable(*var);
@@ -1310,8 +1313,7 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
solAssert(kind == FunctionType::Kind::Internal || kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
solAssert(m_currentContract, "");
auto scopeContract = currentScopeContract();
auto function = functionCallToDefinition(_funCall, scopeContract, m_currentContract);
auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
if (!function)
return smtutil::Expression(true);
@@ -1328,27 +1330,20 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
auto const* contract = function->annotation().contract;
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
solAssert(kind != FunctionType::Kind::Internal || contract->isLibrary() || contains(hierarchy, contract), "");
/// If the call is to a library, we use that library as the called contract.
/// If the call is to a contract not in the inheritance hierarchy, we also use that as the called contract.
/// Otherwise, the call is to some contract in the inheritance hierarchy of the current contract.
/// In this case we use current contract as the called one since the interfaces/predicates are different.
auto const* calledContract = contains(hierarchy, contract) ? m_currentContract : contract;
solAssert(calledContract, "");
solAssert(kind != FunctionType::Kind::Internal || function->isFree() || (contract && contract->isLibrary()) || contains(hierarchy, contract), "");
bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View;
args += currentStateVariables(*calledContract);
args += currentStateVariables(*m_currentContract);
args += symbolicArguments(_funCall, m_currentContract);
if (!calledContract->isLibrary() && !usesStaticCall)
if (!m_currentContract->isLibrary() && !usesStaticCall)
{
state().newState();
for (auto const& var: m_stateVariables)
m_context.variable(*var)->increaseIndex();
}
args += vector<smtutil::Expression>{state().state()};
args += currentStateVariables(*calledContract);
args += currentStateVariables(*m_currentContract);
for (auto var: function->parameters() + function->returnParameters())
{
@@ -1359,14 +1354,14 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
args.push_back(currentValue(*var));
}
Predicate const& summary = *m_summaries.at(calledContract).at(function);
auto from = smt::function(summary, calledContract, m_context);
Predicate const& summary = *m_summaries.at(m_currentContract).at(function);
auto from = smt::function(summary, m_currentContract, m_context);
Predicate const& callPredicate = *createSummaryBlock(
*function,
*calledContract,
*m_currentContract,
kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCallTrusted
);
auto to = smt::function(callPredicate, calledContract, m_context);
auto to = smt::function(callPredicate, m_currentContract, m_context);
addRule(smtutil::Expression::implies(from, to), to.name);
return callPredicate(args);
+9 -2
View File
@@ -243,9 +243,16 @@ string Predicate::formatSummaryCall(vector<smtutil::Expression> const& _args) co
fun->isFallback() ? "fallback" :
fun->isReceive() ? "receive" :
fun->name();
solAssert(fun->annotation().contract, "");
return fun->annotation().contract->name() + "." + fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")" + value;
string prefix;
if (fun->isFree())
prefix = !fun->sourceUnitName().empty() ? (fun->sourceUnitName() + ":") : "";
else
{
solAssert(fun->annotation().contract, "");
prefix = fun->annotation().contract->name() + ".";
}
return prefix + fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")" + value;
}
vector<optional<string>> Predicate::summaryStateValues(vector<smtutil::Expression> const& _args) const
+26 -11
View File
@@ -59,16 +59,7 @@ bool SMTEncoder::analyze(SourceUnit const& _source)
bool analysis = true;
for (auto source: sources)
for (auto node: source->nodes())
if (auto function = dynamic_pointer_cast<FunctionDefinition>(node))
{
m_errorReporter.warning(
6660_error,
function->location(),
"Model checker analysis was not possible because file level functions are not supported."
);
analysis = false;
}
else if (auto var = dynamic_pointer_cast<VariableDeclaration>(node))
if (auto var = dynamic_pointer_cast<VariableDeclaration>(node))
{
m_errorReporter.warning(
8195_error,
@@ -116,7 +107,7 @@ bool SMTEncoder::visit(ContractDefinition const& _contract)
// the constructor.
// Constructors are visited as part of the constructor
// hierarchy inlining.
for (auto const* function: contractFunctionsWithoutVirtual(_contract))
for (auto const* function: contractFunctionsWithoutVirtual(_contract) + allFreeFunctions())
if (!function->isConstructor())
function->accept(*this);
@@ -152,6 +143,8 @@ bool SMTEncoder::visit(ModifierDefinition const&)
bool SMTEncoder::visit(FunctionDefinition const& _function)
{
solAssert(m_currentContract, "");
m_modifierDepthStack.push_back(-1);
initializeLocalVariables(_function);
@@ -288,6 +281,8 @@ bool SMTEncoder::visit(PlaceholderStatement const&)
void SMTEncoder::endVisit(FunctionDefinition const&)
{
solAssert(m_currentContract, "");
popCallStack();
solAssert(m_modifierDepthStack.back() == -1, "");
m_modifierDepthStack.pop_back();
@@ -2963,6 +2958,26 @@ vector<smtutil::Expression> SMTEncoder::symbolicArguments(FunctionCall const& _f
return args;
}
void SMTEncoder::collectFreeFunctions(set<SourceUnit const*, ASTNode::CompareByID> const& _sources)
{
if (!m_freeFunctions.empty())
return;
for (auto source: _sources)
for (auto node: source->nodes())
if (auto function = dynamic_cast<FunctionDefinition const*>(node.get()))
m_freeFunctions.insert(function);
else if (
auto contract = dynamic_cast<ContractDefinition const*>(node.get());
contract && contract->isLibrary()
)
{
for (auto function: contract->definedFunctions())
if (!function->isPublic())
m_freeFunctions.insert(function);
}
}
smt::SymbolicState& SMTEncoder::state()
{
return m_context.state();
+9
View File
@@ -369,6 +369,11 @@ protected:
/// type conversion.
std::vector<smtutil::Expression> symbolicArguments(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
/// Traverses all source units available collecting free functions
/// and internal library functions in m_freeFunctions.
void collectFreeFunctions(std::set<SourceUnit const*, ASTNode::CompareByID> const& _sources);
std::set<FunctionDefinition const*, ASTNode::CompareByID> const& allFreeFunctions() const { return m_freeFunctions; }
/// @returns a note to be added to warnings.
std::string extraComment();
@@ -437,6 +442,10 @@ protected:
ContractDefinition const* m_currentContract = nullptr;
/// Stores the free functions and internal library functions.
/// Those need to be encoded repeatedely for every analyzed contract.
std::set<FunctionDefinition const*, ASTNode::CompareByID> m_freeFunctions;
/// Stores the context of the encoding.
smt::EncodingContext& m_context;