mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6055 from ethereum/smt_arrays
[SMTChecker] Add support to arrays
This commit is contained in:
@@ -535,13 +535,6 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall)
|
||||
m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1));
|
||||
}
|
||||
|
||||
void SMTChecker::eraseArrayKnowledge()
|
||||
{
|
||||
for (auto const& var: m_variables)
|
||||
if (var.first->annotation().type->category() == Type::Category::Mapping)
|
||||
newValue(*var.first);
|
||||
}
|
||||
|
||||
void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
|
||||
{
|
||||
FunctionDefinition const* _funDef = nullptr;
|
||||
@@ -698,17 +691,26 @@ void SMTChecker::endVisit(Literal const& _literal)
|
||||
solAssert(_literal.annotation().type, "Expected type for AST node");
|
||||
Type const& type = *_literal.annotation().type;
|
||||
if (isNumber(type.category()))
|
||||
|
||||
defineExpr(_literal, smt::Expression(type.literalValue(&_literal)));
|
||||
else if (isBool(type.category()))
|
||||
defineExpr(_literal, smt::Expression(_literal.token() == Token::TrueLiteral ? true : false));
|
||||
else
|
||||
{
|
||||
if (type.category() == Type::Category::StringLiteral)
|
||||
{
|
||||
auto stringType = make_shared<ArrayType>(DataLocation::Memory, true);
|
||||
auto stringLit = dynamic_cast<StringLiteralType const*>(_literal.annotation().type.get());
|
||||
solAssert(stringLit, "");
|
||||
auto result = newSymbolicVariable(*stringType, stringLit->richIdentifier(), *m_interface);
|
||||
m_expressions.emplace(&_literal, result.second);
|
||||
}
|
||||
m_errorReporter.warning(
|
||||
_literal.location(),
|
||||
"Assertion checker does not yet support the type of this literal (" +
|
||||
_literal.annotation().type->toString() +
|
||||
")."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void SMTChecker::endVisit(Return const& _return)
|
||||
@@ -796,7 +798,6 @@ void SMTChecker::endVisit(IndexAccess const& _indexAccess)
|
||||
void SMTChecker::arrayAssignment()
|
||||
{
|
||||
m_arrayAssignmentHappened = true;
|
||||
eraseArrayKnowledge();
|
||||
}
|
||||
|
||||
void SMTChecker::arrayIndexAssignment(Assignment const& _assignment)
|
||||
@@ -806,12 +807,48 @@ void SMTChecker::arrayIndexAssignment(Assignment const& _assignment)
|
||||
{
|
||||
auto const& varDecl = dynamic_cast<VariableDeclaration const&>(*id->annotation().referencedDeclaration);
|
||||
solAssert(knownVariable(varDecl), "");
|
||||
|
||||
if (varDecl.hasReferenceOrMappingType())
|
||||
resetVariables([&](VariableDeclaration const& _var) {
|
||||
if (_var == varDecl)
|
||||
return false;
|
||||
TypePointer prefix = _var.type();
|
||||
TypePointer originalType = typeWithoutPointer(varDecl.type());
|
||||
while (
|
||||
prefix->category() == Type::Category::Mapping ||
|
||||
prefix->category() == Type::Category::Array
|
||||
)
|
||||
{
|
||||
if (*originalType == *typeWithoutPointer(prefix))
|
||||
return true;
|
||||
if (prefix->category() == Type::Category::Mapping)
|
||||
{
|
||||
auto mapPrefix = dynamic_cast<MappingType const*>(prefix.get());
|
||||
solAssert(mapPrefix, "");
|
||||
prefix = mapPrefix->valueType();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix.get());
|
||||
solAssert(arrayPrefix, "");
|
||||
prefix = arrayPrefix->baseType();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
smt::Expression store = smt::Expression::store(
|
||||
m_variables[&varDecl]->currentValue(),
|
||||
expr(*indexAccess.indexExpression()),
|
||||
expr(_assignment.rightHandSide())
|
||||
);
|
||||
m_interface->addAssertion(newValue(varDecl) == store);
|
||||
// Update the SMT select value after the assignment,
|
||||
// necessary for sound models.
|
||||
defineExpr(indexAccess, smt::Expression::select(
|
||||
m_variables[&varDecl]->currentValue(),
|
||||
expr(*indexAccess.indexExpression())
|
||||
));
|
||||
}
|
||||
else if (dynamic_cast<IndexAccess const*>(&indexAccess.baseExpression()))
|
||||
m_errorReporter.warning(
|
||||
@@ -1310,6 +1347,13 @@ void SMTChecker::resetVariables(function<bool(VariableDeclaration const&)> const
|
||||
});
|
||||
}
|
||||
|
||||
TypePointer SMTChecker::typeWithoutPointer(TypePointer const& _type)
|
||||
{
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(_type.get()))
|
||||
return ReferenceType::copyForLocationIfReference(refType->location(), _type);
|
||||
return _type;
|
||||
}
|
||||
|
||||
void SMTChecker::mergeVariables(vector<VariableDeclaration const*> const& _variables, smt::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse)
|
||||
{
|
||||
set<VariableDeclaration const*> uniqueVars(_variables.begin(), _variables.end());
|
||||
|
||||
@@ -103,8 +103,6 @@ private:
|
||||
void arrayAssignment();
|
||||
/// Handles assignment to SMT array index.
|
||||
void arrayIndexAssignment(Assignment const& _assignment);
|
||||
/// Erases information about SMT arrays.
|
||||
void eraseArrayKnowledge();
|
||||
|
||||
/// Division expression in the given type. Requires special treatment because
|
||||
/// of rounding for signed division.
|
||||
@@ -177,6 +175,9 @@ private:
|
||||
void resetStorageReferences();
|
||||
void resetVariables(std::vector<VariableDeclaration const*> _variables);
|
||||
void resetVariables(std::function<bool(VariableDeclaration const&)> const& _filter);
|
||||
/// @returns the type without storage pointer information if it has it.
|
||||
TypePointer typeWithoutPointer(TypePointer const& _type);
|
||||
|
||||
/// Given two different branches and the touched variables,
|
||||
/// merge the touched variables into after-branch ite variables
|
||||
/// using the branch condition as guard.
|
||||
|
||||
@@ -57,8 +57,13 @@ smt::SortPointer dev::solidity::smtSort(Type const& _type)
|
||||
solAssert(mapType, "");
|
||||
return make_shared<smt::ArraySort>(smtSort(*mapType->keyType()), smtSort(*mapType->valueType()));
|
||||
}
|
||||
// TODO Solidity array
|
||||
return make_shared<smt::Sort>(smt::Kind::Int);
|
||||
else
|
||||
{
|
||||
solAssert(isArray(_type.category()), "");
|
||||
auto arrayType = dynamic_cast<ArrayType const*>(&_type);
|
||||
solAssert(arrayType, "");
|
||||
return make_shared<smt::ArraySort>(make_shared<smt::Sort>(smt::Kind::Int), smtSort(*arrayType->baseType()));
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Abstract case.
|
||||
@@ -82,7 +87,7 @@ smt::Kind dev::solidity::smtKind(Type::Category _category)
|
||||
return smt::Kind::Bool;
|
||||
else if (isFunction(_category))
|
||||
return smt::Kind::Function;
|
||||
else if (isMapping(_category))
|
||||
else if (isMapping(_category) || isArray(_category))
|
||||
return smt::Kind::Array;
|
||||
// Abstract case.
|
||||
return smt::Kind::Int;
|
||||
@@ -92,7 +97,8 @@ bool dev::solidity::isSupportedType(Type::Category _category)
|
||||
{
|
||||
return isNumber(_category) ||
|
||||
isBool(_category) ||
|
||||
isMapping(_category);
|
||||
isMapping(_category) ||
|
||||
isArray(_category);
|
||||
}
|
||||
|
||||
bool dev::solidity::isSupportedTypeDeclaration(Type::Category _category)
|
||||
@@ -140,6 +146,8 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
|
||||
}
|
||||
else if (isMapping(_type.category()))
|
||||
var = make_shared<SymbolicMappingVariable>(type, _uniqueName, _solver);
|
||||
else if (isArray(_type.category()))
|
||||
var = make_shared<SymbolicArrayVariable>(type, _uniqueName, _solver);
|
||||
else
|
||||
solAssert(false, "");
|
||||
return make_pair(abstract, var);
|
||||
@@ -198,6 +206,11 @@ bool dev::solidity::isMapping(Type::Category _category)
|
||||
return _category == Type::Category::Mapping;
|
||||
}
|
||||
|
||||
bool dev::solidity::isArray(Type::Category _category)
|
||||
{
|
||||
return _category == Type::Category::Array;
|
||||
}
|
||||
|
||||
smt::Expression dev::solidity::minValue(IntegerType const& _type)
|
||||
{
|
||||
return smt::Expression(_type.minValue());
|
||||
|
||||
@@ -48,6 +48,7 @@ bool isNumber(Type::Category _category);
|
||||
bool isBool(Type::Category _category);
|
||||
bool isFunction(Type::Category _category);
|
||||
bool isMapping(Type::Category _category);
|
||||
bool isArray(Type::Category _category);
|
||||
|
||||
/// Returns a new symbolic variable, according to _type.
|
||||
/// Also returns whether the type is abstract or not,
|
||||
|
||||
@@ -136,3 +136,13 @@ SymbolicMappingVariable::SymbolicMappingVariable(
|
||||
{
|
||||
solAssert(isMapping(m_type->category()), "");
|
||||
}
|
||||
|
||||
SymbolicArrayVariable::SymbolicArrayVariable(
|
||||
TypePointer _type,
|
||||
string const& _uniqueName,
|
||||
smt::SolverInterface& _interface
|
||||
):
|
||||
SymbolicVariable(move(_type), _uniqueName, _interface)
|
||||
{
|
||||
solAssert(isArray(m_type->category()), "");
|
||||
}
|
||||
|
||||
@@ -153,5 +153,18 @@ public:
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Specialization of SymbolicVariable for Array
|
||||
*/
|
||||
class SymbolicArrayVariable: public SymbolicVariable
|
||||
{
|
||||
public:
|
||||
SymbolicArrayVariable(
|
||||
TypePointer _type,
|
||||
std::string const& _uniqueName,
|
||||
smt::SolverInterface& _interface
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user