mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[SMTChecker] Support user types
This commit is contained in:
@@ -483,6 +483,8 @@ void BMC::endVisit(FunctionCall const& _funCall)
|
||||
case FunctionType::Kind::BlockHash:
|
||||
case FunctionType::Kind::AddMod:
|
||||
case FunctionType::Kind::MulMod:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
case FunctionType::Kind::Wrap:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
SMTEncoder::endVisit(_funCall);
|
||||
|
||||
@@ -553,6 +553,8 @@ void CHC::endVisit(FunctionCall const& _funCall)
|
||||
case FunctionType::Kind::BlockHash:
|
||||
case FunctionType::Kind::AddMod:
|
||||
case FunctionType::Kind::MulMod:
|
||||
case FunctionType::Kind::Unwrap:
|
||||
case FunctionType::Kind::Wrap:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
SMTEncoder::endVisit(_funCall);
|
||||
|
||||
@@ -237,7 +237,7 @@ string Predicate::formatSummaryCall(
|
||||
auto last = first + static_cast<int>(fun->parameters().size());
|
||||
solAssert(first >= _args.begin() && first <= _args.end(), "");
|
||||
solAssert(last >= _args.begin() && last <= _args.end(), "");
|
||||
auto inTypes = FunctionType(*fun).parameterTypes();
|
||||
auto inTypes = SMTEncoder::replaceUserTypes(FunctionType(*fun).parameterTypes());
|
||||
vector<optional<string>> functionArgsCex = formatExpressions(vector<smtutil::Expression>(first, last), inTypes);
|
||||
vector<string> functionArgs;
|
||||
|
||||
@@ -317,7 +317,7 @@ vector<optional<string>> Predicate::summaryPostInputValues(vector<smtutil::Expre
|
||||
|
||||
vector<smtutil::Expression> inValues(first, last);
|
||||
solAssert(inValues.size() == inParams.size(), "");
|
||||
auto inTypes = FunctionType(*function).parameterTypes();
|
||||
auto inTypes = SMTEncoder::replaceUserTypes(FunctionType(*function).parameterTypes());
|
||||
return formatExpressions(inValues, inTypes);
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ vector<optional<string>> Predicate::summaryPostOutputValues(vector<smtutil::Expr
|
||||
|
||||
vector<smtutil::Expression> outValues(first, _args.end());
|
||||
solAssert(outValues.size() == function->returnParameters().size(), "");
|
||||
auto outTypes = FunctionType(*function).returnParameterTypes();
|
||||
auto outTypes = SMTEncoder::replaceUserTypes(FunctionType(*function).returnParameterTypes());
|
||||
return formatExpressions(outValues, outTypes);
|
||||
}
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||
Token op = _assignment.assignmentOperator();
|
||||
solAssert(TokenTraits::isAssignmentOp(op), "");
|
||||
|
||||
if (!smt::isSupportedType(*_assignment.annotation().type))
|
||||
if (!isSupportedType(*_assignment.annotation().type))
|
||||
{
|
||||
// Give it a new index anyway to keep the SSA scheme sound.
|
||||
|
||||
@@ -406,6 +406,9 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
|
||||
if (_tuple.annotation().type->category() == Type::Category::Function)
|
||||
return;
|
||||
|
||||
if (_tuple.annotation().type->category() == Type::Category::TypeType)
|
||||
return;
|
||||
|
||||
createExpr(_tuple);
|
||||
|
||||
if (_tuple.isInlineArray())
|
||||
@@ -575,6 +578,17 @@ bool SMTEncoder::visit(FunctionCall const& _funCall)
|
||||
arg->accept(*this);
|
||||
return false;
|
||||
}
|
||||
// We do not really need to visit the expression in a wrap/unwrap no-op call,
|
||||
// so we just ignore the function call expression to avoid "unsupported" warnings.
|
||||
else if (
|
||||
funType.kind() == FunctionType::Kind::Wrap ||
|
||||
funType.kind() == FunctionType::Kind::Unwrap
|
||||
)
|
||||
{
|
||||
if (auto arg = _funCall.arguments().front())
|
||||
arg->accept(*this);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -641,6 +655,10 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
case FunctionType::Kind::MulMod:
|
||||
visitAddMulMod(_funCall);
|
||||
break;
|
||||
case FunctionType::Kind::Unwrap:
|
||||
case FunctionType::Kind::Wrap:
|
||||
visitWrapUnwrap(_funCall);
|
||||
break;
|
||||
case FunctionType::Kind::Send:
|
||||
case FunctionType::Kind::Transfer:
|
||||
{
|
||||
@@ -846,6 +864,13 @@ void SMTEncoder::visitAddMulMod(FunctionCall const& _funCall)
|
||||
defineExpr(_funCall, divModWithSlacks(x * y, k, intType).second);
|
||||
}
|
||||
|
||||
void SMTEncoder::visitWrapUnwrap(FunctionCall const& _funCall)
|
||||
{
|
||||
auto const& args = _funCall.arguments();
|
||||
solAssert(args.size() == 1, "");
|
||||
defineExpr(_funCall, expr(*args.front()));
|
||||
}
|
||||
|
||||
void SMTEncoder::visitObjectCreation(FunctionCall const& _funCall)
|
||||
{
|
||||
auto const& args = _funCall.arguments();
|
||||
@@ -889,6 +914,9 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
|
||||
// Ignore module identifiers
|
||||
else if (dynamic_cast<ModuleType const*>(_identifier.annotation().type))
|
||||
return;
|
||||
// Ignore user defined value type identifiers
|
||||
else if (dynamic_cast<UserDefinedValueType const*>(_identifier.annotation().type))
|
||||
return;
|
||||
// Ignore the builtin abi, it is handled in FunctionCall.
|
||||
// TODO: ignore MagicType in general (abi, block, msg, tx, type)
|
||||
else if (auto magicType = dynamic_cast<MagicType const*>(_identifier.annotation().type); magicType && magicType->kind() == MagicType::Kind::ABI)
|
||||
@@ -944,7 +972,7 @@ void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall)
|
||||
auto var = dynamic_cast<VariableDeclaration const*>(access.annotation().referencedDeclaration);
|
||||
solAssert(var, "");
|
||||
solAssert(m_context.knownExpression(_funCall), "");
|
||||
auto paramExpectedTypes = FunctionType(*var).parameterTypes();
|
||||
auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes());
|
||||
auto actualArguments = _funCall.arguments();
|
||||
solAssert(actualArguments.size() == paramExpectedTypes.size(), "");
|
||||
deque<smtutil::Expression> symbArguments;
|
||||
@@ -1164,7 +1192,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
||||
void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier)
|
||||
{
|
||||
auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type);
|
||||
if (fType.returnParameterTypes().size() == 1)
|
||||
if (replaceUserTypes(fType.returnParameterTypes()).size() == 1)
|
||||
{
|
||||
defineGlobalVariable(fType.identifier(), _identifier);
|
||||
m_context.createExpression(_identifier, m_context.globalSymbol(fType.identifier()));
|
||||
@@ -1647,7 +1675,7 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex
|
||||
m_context.globalSymbol(_name)->increaseIndex();
|
||||
// The default behavior is not to increase the index since
|
||||
// most of the global values stay the same throughout a tx.
|
||||
if (smt::isSupportedType(*_expr.annotation().type))
|
||||
if (isSupportedType(*_expr.annotation().type))
|
||||
defineExpr(_expr, m_context.globalSymbol(_name)->currentValue());
|
||||
}
|
||||
|
||||
@@ -1843,9 +1871,10 @@ smtutil::Expression SMTEncoder::bitwiseOperation(
|
||||
|
||||
void SMTEncoder::compareOperation(BinaryOperation const& _op)
|
||||
{
|
||||
auto const& commonType = _op.annotation().commonType;
|
||||
auto commonType = _op.annotation().commonType;
|
||||
solAssert(commonType, "");
|
||||
if (smt::isSupportedType(*commonType))
|
||||
|
||||
if (isSupportedType(*commonType))
|
||||
{
|
||||
smtutil::Expression left(expr(_op.leftExpression(), commonType));
|
||||
smtutil::Expression right(expr(_op.rightExpression(), commonType));
|
||||
@@ -1978,7 +2007,7 @@ void SMTEncoder::assignment(
|
||||
|
||||
Expression const* left = cleanExpression(_left);
|
||||
|
||||
if (!smt::isSupportedType(*_type))
|
||||
if (!isSupportedType(*_type))
|
||||
{
|
||||
// Give it a new index anyway to keep the SSA scheme sound.
|
||||
if (auto varDecl = identifierToVariable(*left))
|
||||
@@ -2019,7 +2048,7 @@ void SMTEncoder::assignment(
|
||||
}
|
||||
else if (funType->kind() == FunctionType::Kind::Internal)
|
||||
{
|
||||
for (auto type: funType->returnParameterTypes())
|
||||
for (auto type: replaceUserTypes(funType->returnParameterTypes()))
|
||||
if (type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type))
|
||||
resetReferences(type);
|
||||
}
|
||||
@@ -2356,6 +2385,11 @@ bool SMTEncoder::sameTypeOrSubtype(Type const* _a, Type const* _b)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SMTEncoder::isSupportedType(Type const& _type) const
|
||||
{
|
||||
return smt::isSupportedType(*underlyingType(&_type));
|
||||
}
|
||||
|
||||
Type const* SMTEncoder::typeWithoutPointer(Type const* _type)
|
||||
{
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
|
||||
@@ -2417,7 +2451,7 @@ smtutil::Expression SMTEncoder::expr(Expression const& _e, Type const* _targetTy
|
||||
createExpr(_e);
|
||||
}
|
||||
|
||||
return m_context.expression(_e)->currentValue(_targetType);
|
||||
return m_context.expression(_e)->currentValue(underlyingType(_targetType));
|
||||
}
|
||||
|
||||
void SMTEncoder::createExpr(Expression const& _e)
|
||||
@@ -2608,6 +2642,22 @@ Expression const* SMTEncoder::innermostTuple(Expression const& _expr)
|
||||
return expr;
|
||||
}
|
||||
|
||||
Type const* SMTEncoder::underlyingType(Type const* _type)
|
||||
{
|
||||
if (auto userType = dynamic_cast<UserDefinedValueType const*>(_type))
|
||||
_type = &userType->underlyingType();
|
||||
return _type;
|
||||
}
|
||||
|
||||
TypePointers SMTEncoder::replaceUserTypes(TypePointers const& _types)
|
||||
{
|
||||
return applyMap(_types, [](auto _type) {
|
||||
if (auto userType = dynamic_cast<UserDefinedValueType const*>(_type))
|
||||
return &userType->underlyingType();
|
||||
return _type;
|
||||
});
|
||||
}
|
||||
|
||||
pair<Expression const*, FunctionCallOptions const*> SMTEncoder::functionCallExpression(FunctionCall const& _funCall)
|
||||
{
|
||||
Expression const* callExpr = &_funCall.expression();
|
||||
|
||||
@@ -71,6 +71,12 @@ public:
|
||||
/// otherwise _expr.
|
||||
static Expression const* innermostTuple(Expression const& _expr);
|
||||
|
||||
/// @returns the underlying type if _type is UserDefinedValueType,
|
||||
/// and _type otherwise.
|
||||
static Type const* underlyingType(Type const* _type);
|
||||
|
||||
static TypePointers replaceUserTypes(TypePointers const& _types);
|
||||
|
||||
/// @returns {_funCall.expression(), nullptr} if function call option values are not given, and
|
||||
/// {_funCall.expression().expression(), _funCall.expression()} if they are.
|
||||
static std::pair<Expression const*, FunctionCallOptions const*> functionCallExpression(FunctionCall const& _funCall);
|
||||
@@ -207,6 +213,7 @@ protected:
|
||||
void visitCryptoFunction(FunctionCall const& _funCall);
|
||||
void visitGasLeft(FunctionCall const& _funCall);
|
||||
virtual void visitAddMulMod(FunctionCall const& _funCall);
|
||||
void visitWrapUnwrap(FunctionCall const& _funCall);
|
||||
void visitObjectCreation(FunctionCall const& _funCall);
|
||||
void visitTypeConversion(FunctionCall const& _funCall);
|
||||
void visitStructConstructorCall(FunctionCall const& _funCall);
|
||||
@@ -319,6 +326,8 @@ protected:
|
||||
/// @returns whether _a or a subtype of _a is the same as _b.
|
||||
bool sameTypeOrSubtype(Type const* _a, Type const* _b);
|
||||
|
||||
bool isSupportedType(Type const& _type) const;
|
||||
|
||||
/// Given the state of the symbolic variables at the end of two different branches,
|
||||
/// create a merged state using the given branch condition.
|
||||
void mergeVariables(smtutil::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse);
|
||||
|
||||
@@ -270,6 +270,8 @@ void SymbolicState::buildABIFunctions(set<FunctionCall const*> const& _abiFuncti
|
||||
t = TypeProvider::uint256();
|
||||
else if (t->category() == frontend::Type::Category::StringLiteral)
|
||||
t = TypeProvider::bytesMemory();
|
||||
else if (auto userType = dynamic_cast<UserDefinedValueType const*>(t))
|
||||
t = &userType->underlyingType();
|
||||
};
|
||||
replaceTypes(inTypes);
|
||||
replaceTypes(outTypes);
|
||||
|
||||
@@ -233,6 +233,10 @@ pair<bool, shared_ptr<SymbolicVariable>> newSymbolicVariable(
|
||||
bool abstract = false;
|
||||
shared_ptr<SymbolicVariable> var;
|
||||
frontend::Type const* type = &_type;
|
||||
|
||||
if (auto userType = dynamic_cast<UserDefinedValueType const*>(type))
|
||||
return newSymbolicVariable(userType->underlyingType(), _uniqueName, _context);
|
||||
|
||||
if (!isSupportedTypeDeclaration(_type))
|
||||
{
|
||||
abstract = true;
|
||||
|
||||
Reference in New Issue
Block a user