#pragma once
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/formal/VariableUsage.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/ReadFile.h>
#include <liblangutil/UniqueErrorReporter.h>
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>
namespace solidity::langutil
{
class ErrorReporter;
struct SourceLocation;
class CharStreamProvider;
}
namespace solidity::frontend
{
class SMTEncoder: public ASTConstVisitor
{
public:
SMTEncoder(
smt::EncodingContext& _context,
ModelCheckerSettings const& _settings,
langutil::UniqueErrorReporter& _errorReporter,
langutil::CharStreamProvider const& _charStreamProvider
);
static Expression const* leftmostBase(IndexAccess const& _indexAccess);
static Type const* keyType(Type const* _type);
static Expression const* innermostTuple(Expression const& _expr);
static Type const* underlyingType(Type const* _type);
static TypePointers replaceUserTypes(TypePointers const& _types);
static std::pair<Expression const*, FunctionCallOptions const*> functionCallExpression(FunctionCall const& _funCall);
static Expression const* cleanExpression(Expression const& _expr);
static FunctionDefinition const* functionCallToDefinition(
FunctionCall const& _funCall,
ContractDefinition const* _scopeContract,
ContractDefinition const* _contextContract
);
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract);
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function);
static std::vector<VariableDeclaration const*> localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract);
static std::vector<VariableDeclaration const*> modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract);
static std::vector<VariableDeclaration const*> tryCatchVariables(FunctionDefinition const& _function);
static ModifierDefinition const* resolveModifierInvocation(ModifierInvocation const& _invocation, ContractDefinition const* _contract);
static SourceUnit const* sourceUnitContaining(Scopable const& _scopable);
std::map<ContractDefinition const*, std::vector<ASTPointer<frontend::Expression>>> baseArguments(ContractDefinition const& _contract);
static RationalNumberType const* isConstant(Expression const& _expr);
static std::set<FunctionCall const*> collectABICalls(ASTNode const* _node);
static std::set<SourceUnit const*, ASTNode::CompareByID> sourceDependencies(SourceUnit const& _source);
protected:
void resetSourceAnalysis();
bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition const& _node) override;
void endVisit(ContractDefinition const& _node) override;
void endVisit(VariableDeclaration const& _node) override;
bool visit(ModifierDefinition const& _node) override;
bool visit(FunctionDefinition const& _node) override;
void endVisit(FunctionDefinition const& _node) override;
bool visit(Block const& _node) override;
void endVisit(Block const& _node) override;
bool visit(PlaceholderStatement const& _node) override;
bool visit(IfStatement const&) override { return false; }
bool visit(WhileStatement const&) override { return false; }
bool visit(ForStatement const&) override { return false; }
void endVisit(ForStatement const&) override {}
void endVisit(VariableDeclarationStatement const& _node) override;
bool visit(Assignment const& _node) override;
void endVisit(Assignment const& _node) override;
void endVisit(TupleExpression const& _node) override;
bool visit(UnaryOperation const& _node) override;
void endVisit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override;
void endVisit(BinaryOperation const& _node) override;
bool visit(Conditional const& _node) override;
bool visit(FunctionCall const& _node) override;
void endVisit(FunctionCall const& _node) override;
bool visit(ModifierInvocation const& _node) override;
void endVisit(Identifier const& _node) override;
void endVisit(ElementaryTypeNameExpression const& _node) override;
void endVisit(Literal const& _node) override;
void endVisit(Return const& _node) override;
bool visit(MemberAccess const& _node) override;
void endVisit(IndexAccess const& _node) override;
void endVisit(IndexRangeAccess const& _node) override;
bool visit(InlineAssembly const& _node) override;
void endVisit(Break const&) override {}
void endVisit(Continue const&) override {}
bool visit(TryCatchClause const&) override { return true; }
void endVisit(TryCatchClause const&) override {}
bool visit(TryStatement const&) override { return false; }
virtual void pushInlineFrame(CallableDeclaration const&);
virtual void popInlineFrame(CallableDeclaration const&);
bool shortcutRationalNumber(Expression const& _expr);
void arithmeticOperation(BinaryOperation const& _op);
virtual std::pair<smtutil::Expression, smtutil::Expression> arithmeticOperation(
Token _op,
smtutil::Expression const& _left,
smtutil::Expression const& _right,
Type const* _commonType,
Expression const& _expression
);
smtutil::Expression bitwiseOperation(
Token _op,
smtutil::Expression const& _left,
smtutil::Expression const& _right,
Type const* _commonType
);
void compareOperation(BinaryOperation const& _op);
void booleanOperation(BinaryOperation const& _op);
void bitwiseOperation(BinaryOperation const& _op);
void bitwiseNotOperation(UnaryOperation const& _op);
void initContract(ContractDefinition const& _contract);
void initFunction(FunctionDefinition const& _function);
void visitAssert(FunctionCall const& _funCall);
void visitRequire(FunctionCall const& _funCall);
void visitABIFunction(FunctionCall const& _funCall);
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);
void visitFunctionIdentifier(Identifier const& _identifier);
void visitPublicGetter(FunctionCall const& _funCall);
bool shouldAnalyze(ContractDefinition const& _contract) const;
bool shouldAnalyze(SourceUnit const& _source) const;
bool isPublicGetter(Expression const& _expr);
void visitFunctionOrModifier();
void inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition);
void inlineConstructorHierarchy(ContractDefinition const& _contract);
void defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false);
void arrayAssignment();
void indexOrMemberAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide);
void arrayPush(FunctionCall const& _funCall);
void arrayPop(FunctionCall const& _funCall);
virtual void makeArrayPopVerificationTarget(FunctionCall const&) {}
virtual void makeOutOfBoundsVerificationTarget(IndexAccess const&) {}
void addArrayLiteralAssertions(
smt::SymbolicArrayVariable& _symArray,
std::vector<smtutil::Expression> const& _elementValues
);
void bytesToFixedBytesAssertions(
smt::SymbolicArrayVariable& _symArray,
Expression const& _fixedBytes
);
std::pair<smtutil::Expression, smtutil::Expression> divModWithSlacks(
smtutil::Expression _left,
smtutil::Expression _right,
IntegerType const& _type
);
virtual void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value);
void assignment(VariableDeclaration const& _variable, Expression const& _value);
void assignment(VariableDeclaration const& _variable, smtutil::Expression const& _value);
void assignment(Expression const& _left, smtutil::Expression const& _right);
void assignment(
Expression const& _left,
smtutil::Expression const& _right,
Type const* _type
);
void tupleAssignment(Expression const& _left, Expression const& _right);
smtutil::Expression compoundAssignment(Assignment const& _assignment);
void expressionToTupleAssignment(std::vector<std::shared_ptr<VariableDeclaration>> const& _variables, Expression const& _rhs);
using VariableIndices = std::unordered_map<VariableDeclaration const*, unsigned>;
std::pair<VariableIndices, smtutil::Expression> visitBranch(ASTNode const* _statement, smtutil::Expression const* _condition = nullptr);
std::pair<VariableIndices, smtutil::Expression> visitBranch(ASTNode const* _statement, smtutil::Expression _condition);
using CallStackEntry = std::pair<CallableDeclaration const*, ASTNode const*>;
void createStateVariables(ContractDefinition const& _contract);
void initializeStateVariables(ContractDefinition const& _contract);
void createLocalVariables(FunctionDefinition const& _function);
void initializeLocalVariables(FunctionDefinition const& _function);
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs);
void resetStateVariables();
void resetStorageVariables();
void resetMemoryVariables();
void resetBalances();
void resetReferences(VariableDeclaration const& _varDecl);
void resetReferences(Type const* _type);
Type const* typeWithoutPointer(Type const* _type);
bool sameTypeOrSubtype(Type const* _a, Type const* _b);
bool isSupportedType(Type const& _type) const;
void mergeVariables(smtutil::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse);
bool createVariable(VariableDeclaration const& _varDecl);
smtutil::Expression currentValue(VariableDeclaration const& _decl) const;
smtutil::Expression valueAtIndex(VariableDeclaration const& _decl, unsigned _index) const;
smtutil::Expression expr(Expression const& _e, Type const* _targetType = nullptr);
void createExpr(Expression const& _e);
void defineExpr(Expression const& _e, smtutil::Expression _value);
void defineExpr(Expression const& _e, std::vector<std::optional<smtutil::Expression>> const& _values);
void setPathCondition(smtutil::Expression const& _e);
void pushPathCondition(smtutil::Expression const& _e);
void popPathCondition();
smtutil::Expression currentPathConditions();
langutil::SecondarySourceLocation callStackMessage(std::vector<CallStackEntry> const& _callStack);
CallStackEntry popCallStack();
void pushCallStack(CallStackEntry _entry);
void addPathImpliedExpression(smtutil::Expression const& _e);
VariableIndices copyVariableIndices();
void resetVariableIndices(VariableIndices const& _indices);
virtual void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr);
std::set<VariableDeclaration const*> touchedVariables(ASTNode const& _node);
Declaration const* expressionToDeclaration(Expression const& _expr) const;
VariableDeclaration const* identifierToVariable(Expression const& _expr) const;
MemberAccess const* isEmptyPush(Expression const& _expr) const;
static bool isTrustedExternalCall(Expression const* _expr);
void createReturnedExpressions(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
std::vector<smtutil::Expression> symbolicArguments(FunctionCall const& _funCall, ContractDefinition const* _contextContract);
smtutil::Expression constantExpr(Expression const& _expr, VariableDeclaration const& _var);
void collectFreeFunctions(std::set<SourceUnit const*, ASTNode::CompareByID> const& _sources);
std::set<FunctionDefinition const*, ASTNode::CompareByID> const& allFreeFunctions() const { return m_freeFunctions; }
void createFreeConstants(std::set<SourceUnit const*, ASTNode::CompareByID> const& _sources);
std::string extraComment();
struct VerificationTarget
{
VerificationTargetType type;
smtutil::Expression value;
smtutil::Expression constraints;
};
smt::VariableUsage m_variableUsage;
bool m_arrayAssignmentHappened = false;
bool m_noSolverWarning = false;
std::set<Expression const*> m_uninterpretedTerms;
std::vector<smtutil::Expression> m_pathConditions;
bool m_checked = true;
langutil::UniqueErrorReporter& m_errorReporter;
std::vector<CallStackEntry> m_callStack;
std::vector<ScopeOpener const*> m_scopes;
bool isRootFunction();
bool visitedFunction(FunctionDefinition const* _funDef);
ContractDefinition const* currentScopeContract();
std::set<FunctionDefinition const*, ASTNode::CompareByID> const& contractFunctions(ContractDefinition const& _contract);
std::map<ContractDefinition const*, std::set<FunctionDefinition const*, ASTNode::CompareByID>> m_contractFunctions;
std::set<FunctionDefinition const*, ASTNode::CompareByID> const& contractFunctionsWithoutVirtual(ContractDefinition const& _contract);
std::map<ContractDefinition const*, std::set<FunctionDefinition const*, ASTNode::CompareByID>> m_contractFunctionsWithoutVirtual;
std::vector<int> m_modifierDepthStack;
std::map<ContractDefinition const*, ModifierInvocation const*> m_baseConstructorCalls;
ContractDefinition const* m_currentContract = nullptr;
std::set<FunctionDefinition const*, ASTNode::CompareByID> m_freeFunctions;
smt::EncodingContext& m_context;
ModelCheckerSettings const& m_settings;
langutil::CharStreamProvider const& m_charStreamProvider;
smt::SymbolicState& state();
};
}