Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsmtutil/SolverInterface.h
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
19
#pragma once
20
21
#include <libsmtutil/Exceptions.h>
22
#include <libsmtutil/Sorts.h>
23
24
#include <libsolutil/Common.h>
25
#include <libsolutil/Numeric.h>
26
#include <libsolutil/CommonData.h>
27
28
#include <range/v3/algorithm/all_of.hpp>
29
#include <range/v3/range/conversion.hpp>
30
#include <range/v3/view/split.hpp>
31
32
#include <cstdio>
33
#include <map>
34
#include <memory>
35
#include <optional>
36
#include <set>
37
#include <string>
38
#include <vector>
39
40
namespace solidity::smtutil
41
{
42
43
struct SMTSolverChoice
44
{
45
  bool cvc5 = false;
46
  bool eld = false;
47
  bool smtlib2 = false;
48
  bool z3 = false;
49
50
24.8k
  static constexpr SMTSolverChoice All() noexcept { return {true, true, true, true}; }
51
0
  static constexpr SMTSolverChoice CVC5() noexcept { return {true, false, false, false}; }
52
0
  static constexpr SMTSolverChoice ELD() noexcept { return {false, true, false, false}; }
53
11.9k
  static constexpr SMTSolverChoice SMTLIB2() noexcept { return {false, false, true, false}; }
54
25.5k
  static constexpr SMTSolverChoice Z3() noexcept { return {false, false, false, true}; }
55
0
  static constexpr SMTSolverChoice None() noexcept { return {false, false, false, false}; }
56
57
  static std::optional<SMTSolverChoice> fromString(std::string const& _solvers)
58
0
  {
59
0
    SMTSolverChoice solvers;
60
0
    for (auto&& s: _solvers | ranges::views::split(',') | ranges::to<std::vector<std::string>>())
61
0
      if (!solvers.setSolver(s))
62
0
        return {};
63
0
64
0
    return solvers;
65
0
  }
66
67
  SMTSolverChoice& operator&=(SMTSolverChoice const& _other)
68
0
  {
69
0
    cvc5 &= _other.cvc5;
70
0
    eld &= _other.eld;
71
0
    smtlib2 &= _other.smtlib2;
72
0
    z3 &= _other.z3;
73
0
    return *this;
74
0
  }
75
76
  SMTSolverChoice operator&(SMTSolverChoice _other) const noexcept
77
0
  {
78
0
    _other &= *this;
79
0
    return _other;
80
0
  }
81
82
0
  bool operator!=(SMTSolverChoice const& _other) const noexcept { return !(*this == _other); }
83
84
  bool operator==(SMTSolverChoice const& _other) const noexcept
85
0
  {
86
0
    return cvc5 == _other.cvc5 &&
87
0
      eld == _other.eld &&
88
0
      smtlib2 == _other.smtlib2 &&
89
0
      z3 == _other.z3;
90
0
  }
91
92
  bool setSolver(std::string const& _solver)
93
0
  {
94
0
    static std::set<std::string> const solvers{"cvc5", "eld", "smtlib2", "z3"};
95
0
    if (!solvers.count(_solver))
96
0
      return false;
97
0
    if (_solver == "cvc5")
98
0
      cvc5 = true;
99
0
    if (_solver == "eld")
100
0
      eld = true;
101
0
    else if (_solver == "smtlib2")
102
0
      smtlib2 = true;
103
0
    else if (_solver == "z3")
104
0
      z3 = true;
105
0
    return true;
106
0
  }
107
108
0
  bool none() const noexcept { return !some(); }
109
0
  bool some() const noexcept { return cvc5 || eld || smtlib2 || z3; }
110
0
  bool all() const noexcept { return cvc5 && eld && smtlib2 && z3; }
111
};
112
113
enum class CheckResult
114
{
115
  SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR
116
};
117
118
/// C++ representation of an SMTLIB2 expression.
119
class Expression
120
{
121
  friend class SolverInterface;
122
public:
123
712k
  explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {}
124
95.0k
  explicit Expression(std::shared_ptr<SortSort> _sort, std::string _name = ""): Expression(std::move(_name), {}, _sort) {}
125
  explicit Expression(std::string _name, std::vector<Expression> _arguments, SortPointer _sort):
126
22.6M
    name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {}
127
2.13M
  Expression(size_t _number): Expression(std::to_string(_number), {}, SortProvider::uintSort) {}
128
230k
  Expression(u256 const& _number): Expression(_number.str(), {}, SortProvider::uintSort) {}
129
6.13k
  Expression(s256 const& _number): Expression(
130
6.13k
    _number >= 0 ? _number.str() : "-",
131
6.13k
    _number >= 0 ?
132
3.53k
      std::vector<Expression>{} :
133
6.13k
      std::vector<Expression>{Expression(size_t(0)), bigint(-_number)},
134
6.13k
    SortProvider::sintSort
135
6.13k
  ) {}
136
1.36M
  Expression(bigint const& _number): Expression(
137
1.36M
    _number >= 0 ? _number.str() : "-",
138
1.36M
    _number >= 0 ?
139
1.34M
      std::vector<Expression>{} :
140
1.36M
      std::vector<Expression>{Expression(size_t(0)), bigint(-_number)},
141
1.36M
    SortProvider::sintSort
142
1.36M
  ) {}
143
144
628M
  Expression(Expression const&) = default;
145
34.3M
  Expression(Expression&&) = default;
146
16.8k
  Expression& operator=(Expression const&) = default;
147
1.72M
  Expression& operator=(Expression&&) = default;
148
149
  bool hasCorrectArity() const
150
0
  {
151
0
    if (name == "tuple_constructor")
152
0
    {
153
0
      auto tupleSort = std::dynamic_pointer_cast<TupleSort>(sort);
154
0
      smtAssert(tupleSort, "");
155
0
      return arguments.size() == tupleSort->components.size();
156
0
    }
157
0
158
0
    static std::map<std::string, unsigned> const operatorsArity{
159
0
      {"ite", 3},
160
0
      {"not", 1},
161
0
      {"and", 2},
162
0
      {"or", 2},
163
0
      {"=>", 2},
164
0
      {"=", 2},
165
0
      {"<", 2},
166
0
      {"<=", 2},
167
0
      {">", 2},
168
0
      {">=", 2},
169
0
      {"+", 2},
170
0
      {"-", 2},
171
0
      {"*", 2},
172
0
      {"div", 2},
173
0
      {"mod", 2},
174
0
      {"bvnot", 1},
175
0
      {"bvand", 2},
176
0
      {"bvor", 2},
177
0
      {"bvxor", 2},
178
0
      {"bvshl", 2},
179
0
      {"bvlshr", 2},
180
0
      {"bvashr", 2},
181
0
      {"int2bv", 2},
182
0
      {"bv2int", 1},
183
0
      {"select", 2},
184
0
      {"store", 3},
185
0
      {"const_array", 2},
186
0
      {"tuple_get", 2}
187
0
    };
188
0
    return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size();
189
0
  }
190
191
  static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue)
192
32.9k
  {
193
32.9k
    smtAssert(areCompatible(*_trueValue.sort, *_falseValue.sort));
194
32.9k
    SortPointer sort = _trueValue.sort;
195
32.9k
    return Expression("ite", std::vector<Expression>{
196
32.9k
      std::move(_condition), std::move(_trueValue), std::move(_falseValue)
197
32.9k
    }, std::move(sort));
198
32.9k
  }
199
200
  static Expression implies(Expression _a, Expression _b)
201
441k
  {
202
441k
    return Expression(
203
441k
      "=>",
204
441k
      std::move(_a),
205
441k
      std::move(_b),
206
441k
      Kind::Bool
207
441k
    );
208
441k
  }
209
210
  /// select is the SMT representation of an array index access.
211
  static Expression select(Expression _array, Expression _index)
212
167k
  {
213
167k
    smtAssert(_array.sort->kind == Kind::Array, "");
214
167k
    std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
215
167k
    smtAssert(arraySort, "");
216
167k
    smtAssert(_index.sort, "");
217
167k
    smtAssert(areCompatible(*arraySort->domain, *_index.sort));
218
167k
    return Expression(
219
167k
      "select",
220
167k
      std::vector<Expression>{std::move(_array), std::move(_index)},
221
167k
      arraySort->range
222
167k
    );
223
167k
  }
224
225
  /// store is the SMT representation of an assignment to array index.
226
  /// The function is pure and returns the modified array.
227
  static Expression store(Expression _array, Expression _index, Expression _element)
228
291k
  {
229
291k
    auto arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
230
291k
    smtAssert(arraySort, "");
231
291k
    smtAssert(_index.sort, "");
232
291k
    smtAssert(_element.sort, "");
233
291k
    smtAssert(areCompatible(*arraySort->domain, *_index.sort));
234
291k
    smtAssert(areCompatible(*arraySort->range, *_element.sort));
235
291k
    return Expression(
236
291k
      "store",
237
291k
      std::vector<Expression>{std::move(_array), std::move(_index), std::move(_element)},
238
291k
      arraySort
239
291k
    );
240
291k
  }
241
242
  static Expression const_array(Expression _sort, Expression _value)
243
42.0k
  {
244
42.0k
    smtAssert(_sort.sort->kind == Kind::Sort, "");
245
42.0k
    auto sortSort = std::dynamic_pointer_cast<SortSort>(_sort.sort);
246
42.0k
    auto arraySort = std::dynamic_pointer_cast<ArraySort>(sortSort->inner);
247
42.0k
    smtAssert(sortSort && arraySort, "");
248
42.0k
    smtAssert(_value.sort, "");
249
42.0k
    smtAssert(areCompatible(*arraySort->range, *_value.sort));
250
42.0k
    return Expression(
251
42.0k
      "const_array",
252
42.0k
      std::vector<Expression>{std::move(_sort), std::move(_value)},
253
42.0k
      arraySort
254
42.0k
    );
255
42.0k
  }
256
257
  static Expression tuple_get(Expression _tuple, size_t _index)
258
942k
  {
259
942k
    smtAssert(_tuple.sort->kind == Kind::Tuple, "");
260
942k
    std::shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_tuple.sort);
261
942k
    smtAssert(tupleSort, "");
262
942k
    smtAssert(_index < tupleSort->components.size(), "");
263
942k
    return Expression(
264
942k
      "tuple_get",
265
942k
      std::vector<Expression>{std::move(_tuple), Expression(_index)},
266
942k
      tupleSort->components.at(_index)
267
942k
    );
268
942k
  }
269
270
  static Expression tuple_constructor(Expression _tuple, std::vector<Expression> _arguments)
271
52.9k
  {
272
52.9k
    smtAssert(_tuple.sort->kind == Kind::Sort, "");
273
52.9k
    auto sortSort = std::dynamic_pointer_cast<SortSort>(_tuple.sort);
274
52.9k
    auto tupleSort = std::dynamic_pointer_cast<TupleSort>(sortSort->inner);
275
52.9k
    smtAssert(tupleSort, "");
276
52.9k
    smtAssert(_arguments.size() == tupleSort->components.size(), "");
277
52.9k
    return Expression(
278
52.9k
      "tuple_constructor",
279
52.9k
      std::move(_arguments),
280
52.9k
      tupleSort
281
52.9k
    );
282
52.9k
  }
283
284
  static Expression int2bv(Expression _n, size_t _size)
285
5.49k
  {
286
5.49k
    smtAssert(_n.sort->kind == Kind::Int, "");
287
5.49k
    std::shared_ptr<IntSort> intSort = std::dynamic_pointer_cast<IntSort>(_n.sort);
288
5.49k
    smtAssert(intSort, "");
289
5.49k
    smtAssert(_size <= 256, "");
290
5.49k
    return Expression(
291
5.49k
      "int2bv",
292
5.49k
      std::vector<Expression>{std::move(_n), Expression(_size)},
293
5.49k
      std::make_shared<BitVectorSort>(_size)
294
5.49k
    );
295
5.49k
  }
296
297
  static Expression bv2int(Expression _bv, bool _signed = false)
298
3.84k
  {
299
3.84k
    smtAssert(_bv.sort->kind == Kind::BitVector, "");
300
3.84k
    std::shared_ptr<BitVectorSort> bvSort = std::dynamic_pointer_cast<BitVectorSort>(_bv.sort);
301
3.84k
    smtAssert(bvSort, "");
302
3.84k
    smtAssert(bvSort->size <= 256, "");
303
3.84k
    return Expression(
304
3.84k
      "bv2int",
305
3.84k
      std::vector<Expression>{std::move(_bv)},
306
3.84k
      SortProvider::intSort(_signed)
307
3.84k
    );
308
3.84k
  }
309
310
  static bool sameSort(std::vector<Expression> const& _args)
311
0
  {
312
0
    if (_args.empty())
313
0
      return true;
314
0
315
0
    auto sort = _args.front().sort;
316
0
    return ranges::all_of(
317
0
      _args,
318
0
      [&](auto const& _expr){ return _expr.sort->kind == sort->kind; }
319
0
    );
320
0
  }
321
322
  static Expression mkAnd(std::vector<Expression> _args)
323
0
  {
324
0
    smtAssert(!_args.empty(), "");
325
0
    smtAssert(sameSort(_args), "");
326
0
327
0
    auto sort = _args.front().sort;
328
0
    if (sort->kind == Kind::BitVector)
329
0
      return Expression("bvand", std::move(_args), sort);
330
0
331
0
    smtAssert(sort->kind == Kind::Bool, "");
332
0
    return Expression("and", std::move(_args), Kind::Bool);
333
0
  }
334
335
  static Expression mkOr(std::vector<Expression> _args)
336
0
  {
337
0
    smtAssert(!_args.empty(), "");
338
0
    smtAssert(sameSort(_args), "");
339
0
340
0
    auto sort = _args.front().sort;
341
0
    if (sort->kind == Kind::BitVector)
342
0
      return Expression("bvor", std::move(_args), sort);
343
0
344
0
    smtAssert(sort->kind == Kind::Bool, "");
345
0
    return Expression("or", std::move(_args), Kind::Bool);
346
0
  }
347
348
  static Expression mkPlus(std::vector<Expression> _args)
349
0
  {
350
0
    smtAssert(!_args.empty(), "");
351
0
    smtAssert(sameSort(_args), "");
352
0
353
0
    auto sort = _args.front().sort;
354
0
    smtAssert(sort->kind == Kind::BitVector || sort->kind == Kind::Int, "");
355
0
    return Expression("+", std::move(_args), sort);
356
0
  }
357
358
  static Expression mkMul(std::vector<Expression> _args)
359
0
  {
360
0
    smtAssert(!_args.empty(), "");
361
0
    smtAssert(sameSort(_args), "");
362
0
363
0
    auto sort = _args.front().sort;
364
0
    smtAssert(sort->kind == Kind::BitVector || sort->kind == Kind::Int, "");
365
0
    return Expression("*", std::move(_args), sort);
366
0
  }
367
368
  friend Expression operator!(Expression _a)
369
15.5k
  {
370
15.5k
    if (_a.sort->kind == Kind::BitVector)
371
0
      return ~_a;
372
15.5k
    return Expression("not", std::move(_a), Kind::Bool);
373
15.5k
  }
374
  friend Expression operator&&(Expression _a, Expression _b)
375
3.07M
  {
376
3.07M
    if (_a.sort->kind == Kind::BitVector)
377
0
    {
378
0
      smtAssert(_b.sort->kind == Kind::BitVector, "");
379
0
      return _a & _b;
380
0
    }
381
3.07M
    return Expression("and", std::move(_a), std::move(_b), Kind::Bool);
382
3.07M
  }
383
  friend Expression operator||(Expression _a, Expression _b)
384
17.7k
  {
385
17.7k
    if (_a.sort->kind == Kind::BitVector)
386
0
    {
387
0
      smtAssert(_b.sort->kind == Kind::BitVector, "");
388
0
      return _a | _b;
389
0
    }
390
17.7k
    return Expression("or", std::move(_a), std::move(_b), Kind::Bool);
391
17.7k
  }
392
  friend Expression operator==(Expression _a, Expression _b)
393
910k
  {
394
910k
    smtAssert(_a.sort->kind == _b.sort->kind, "Trying to create an 'equal' expression with different sorts");
395
910k
    return Expression("=", std::move(_a), std::move(_b), Kind::Bool);
396
910k
  }
397
  friend Expression operator!=(Expression _a, Expression _b)
398
724
  {
399
724
    return !(std::move(_a) == std::move(_b));
400
724
  }
401
  friend Expression operator<(Expression _a, Expression _b)
402
21.2k
  {
403
21.2k
    return Expression("<", std::move(_a), std::move(_b), Kind::Bool);
404
21.2k
  }
405
  friend Expression operator<=(Expression _a, Expression _b)
406
715k
  {
407
715k
    return Expression("<=", std::move(_a), std::move(_b), Kind::Bool);
408
715k
  }
409
  friend Expression operator>(Expression _a, Expression _b)
410
66.1k
  {
411
66.1k
    return Expression(">", std::move(_a), std::move(_b), Kind::Bool);
412
66.1k
  }
413
  friend Expression operator>=(Expression _a, Expression _b)
414
832k
  {
415
832k
    return Expression(">=", std::move(_a), std::move(_b), Kind::Bool);
416
832k
  }
417
  friend Expression operator+(Expression _a, Expression _b)
418
34.7k
  {
419
34.7k
    auto intSort = _a.sort;
420
34.7k
    return Expression("+", {std::move(_a), std::move(_b)}, intSort);
421
34.7k
  }
422
  friend Expression operator-(Expression _a, Expression _b)
423
11.0k
  {
424
11.0k
    auto intSort = _a.sort;
425
11.0k
    return Expression("-", {std::move(_a), std::move(_b)}, intSort);
426
11.0k
  }
427
  friend Expression operator*(Expression _a, Expression _b)
428
5.70k
  {
429
5.70k
    auto intSort = _a.sort;
430
5.70k
    return Expression("*", {std::move(_a), std::move(_b)}, intSort);
431
5.70k
  }
432
  friend Expression operator/(Expression _a, Expression _b)
433
1.62k
  {
434
1.62k
    auto intSort = _a.sort;
435
1.62k
    return Expression("div", {std::move(_a), std::move(_b)}, intSort);
436
1.62k
  }
437
  friend Expression operator%(Expression _a, Expression _b)
438
1.62k
  {
439
1.62k
    auto intSort = _a.sort;
440
1.62k
    return Expression("mod", {std::move(_a), std::move(_b)}, intSort);
441
1.62k
  }
442
  friend Expression operator~(Expression _a)
443
2.15k
  {
444
2.15k
    auto bvSort = _a.sort;
445
2.15k
    return Expression("bvnot", {std::move(_a)}, bvSort);
446
2.15k
  }
447
  friend Expression operator&(Expression _a, Expression _b)
448
498
  {
449
498
    auto bvSort = _a.sort;
450
498
    return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort);
451
498
  }
452
  friend Expression operator|(Expression _a, Expression _b)
453
215
  {
454
215
    auto bvSort = _a.sort;
455
215
    return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort);
456
215
  }
457
  friend Expression operator^(Expression _a, Expression _b)
458
302
  {
459
302
    auto bvSort = _a.sort;
460
302
    return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort);
461
302
  }
462
  friend Expression operator<<(Expression _a, Expression _b)
463
322
  {
464
322
    auto bvSort = _a.sort;
465
322
    return Expression("bvshl", {std::move(_a), std::move(_b)}, bvSort);
466
322
  }
467
  friend Expression operator>>(Expression _a, Expression _b)
468
305
  {
469
305
    auto bvSort = _a.sort;
470
305
    return Expression("bvlshr", {std::move(_a), std::move(_b)}, bvSort);
471
305
  }
472
  static Expression ashr(Expression _a, Expression _b)
473
12
  {
474
12
    auto bvSort = _a.sort;
475
12
    return Expression("bvashr", {std::move(_a), std::move(_b)}, bvSort);
476
12
  }
477
  Expression operator()(std::vector<Expression> _arguments) const
478
0
  {
479
0
    smtAssert(
480
0
      sort->kind == Kind::Function,
481
0
      "Attempted function application to non-function."
482
0
    );
483
0
    auto fSort = dynamic_cast<FunctionSort const*>(sort.get());
484
0
    smtAssert(fSort, "");
485
0
    return Expression(name, std::move(_arguments), fSort->codomain);
486
0
  }
487
488
  std::string name;
489
  std::vector<Expression> arguments;
490
  SortPointer sort;
491
492
private:
493
  /// Helper method for checking sort compatibility when creating expressions
494
  /// Signed and unsigned Int sorts are compatible even though they are not same
495
  static bool areCompatible(Sort const& s1, Sort const& s2)
496
826k
  {
497
826k
    return s1.kind == Kind::Int ? s1.kind == s2.kind : s1 == s2;
498
826k
  }
499
  /// Manual constructors, should only be used by SolverInterface and this class itself.
500
  Expression(std::string _name, std::vector<Expression> _arguments, Kind _kind):
501
6.80M
    Expression(std::move(_name), std::move(_arguments), std::make_shared<Sort>(_kind)) {}
502
503
  explicit Expression(std::string _name, Kind _kind):
504
712k
    Expression(std::move(_name), std::vector<Expression>{}, _kind) {}
505
  Expression(std::string _name, Expression _arg, Kind _kind):
506
15.5k
    Expression(std::move(_name), std::vector<Expression>{std::move(_arg)}, _kind) {}
507
  Expression(std::string _name, Expression _arg1, Expression _arg2, Kind _kind):
508
6.07M
    Expression(std::move(_name), std::vector<Expression>{std::move(_arg1), std::move(_arg2)}, _kind) {}
509
};
510
511
DEV_SIMPLE_EXCEPTION(SolverError);
512
513
class SolverInterface
514
{
515
public:
516
37.0k
  SolverInterface() = default;
517
518
37.0k
  virtual ~SolverInterface() = default;
519
520
  virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
521
  Expression newVariable(std::string _name, SortPointer const& _sort)
522
9.69M
  {
523
    // Subclasses should do something here
524
9.69M
    smtAssert(_sort, "");
525
9.69M
    declareVariable(_name, _sort);
526
9.69M
    return Expression(std::move(_name), {}, _sort);
527
9.69M
  }
528
529
  /// @returns how many SMT solvers this interface has.
530
0
  virtual size_t solvers() { return 1; }
531
};
532
533
}