/src/solidity/libsmtutil/SMTLib2Context.cpp
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 | | #include <libsmtutil/SMTLib2Context.h> |
20 | | |
21 | | #include <boost/functional/hash.hpp> |
22 | | |
23 | | #include <range/v3/algorithm/find_if.hpp> |
24 | | |
25 | | namespace solidity::smtutil |
26 | | { |
27 | | |
28 | | std::size_t SortPairHash::operator()(std::pair<SortId, SortId> const& _pair) const |
29 | 683k | { |
30 | 683k | std::size_t seed = 0; |
31 | 683k | boost::hash_combine(seed, _pair.first); |
32 | 683k | boost::hash_combine(seed, _pair.second); |
33 | 683k | return seed; |
34 | 683k | } |
35 | | |
36 | | SMTLib2Context::SMTLib2Context() |
37 | 30.8k | { |
38 | 30.8k | clear(); |
39 | 30.8k | } |
40 | | |
41 | | bool SMTLib2Context::isDeclared(std::string const& _name) const |
42 | 33.9M | { |
43 | 33.9M | return m_functions.count(_name) > 0; |
44 | 33.9M | } |
45 | | |
46 | | void SMTLib2Context::declare(std::string const& _name, SortPointer const& _sort) |
47 | 1.30M | { |
48 | 1.30M | auto [_, inserted] = m_functions.insert({_name, _sort}); |
49 | 1.30M | smtAssert(inserted, "Trying to redeclare SMT function!"); |
50 | 1.30M | } |
51 | | |
52 | | SortPointer SMTLib2Context::getDeclaredSort(std::string const& _name) const |
53 | 4.56M | { |
54 | 4.56M | smtAssert(isDeclared(_name)); |
55 | 4.56M | return m_functions.at(_name); |
56 | 4.56M | } |
57 | | |
58 | 78.3k | void SMTLib2Context::clear() { |
59 | 78.3k | m_functions.clear(); |
60 | 78.3k | m_knownTypes.clear(); |
61 | 78.3k | m_arraySorts.clear(); |
62 | 78.3k | m_tupleSorts.clear(); |
63 | 78.3k | m_bitVectorSorts.clear(); |
64 | 78.3k | m_callback = {}; |
65 | 78.3k | m_knownTypes.emplace_back(std::make_unique<SMTLibSort>(Kind::Bool, std::string("Bool"), std::vector<SortId>{}, SortId{0u})); |
66 | 78.3k | m_knownTypes.emplace_back(std::make_unique<SMTLibSort>(Kind::Int, std::string("Int"), std::vector<SortId>{}, SortId{1u})); |
67 | 78.3k | assert(m_boolSort == m_knownTypes[0]->id); |
68 | 78.3k | assert(m_intSort == m_knownTypes[1]->id); |
69 | 78.3k | } |
70 | | |
71 | | SortId SMTLib2Context::resolve(SortPointer const& _sort) |
72 | 10.7M | { |
73 | 10.7M | switch (_sort->kind) |
74 | 10.7M | { |
75 | 5.22M | case Kind::Int: |
76 | 5.22M | return m_intSort; |
77 | 389k | case Kind::Bool: |
78 | 389k | return m_boolSort; |
79 | 0 | case Kind::BitVector: |
80 | 0 | return resolveBitVectorSort(dynamic_cast<BitVectorSort const&>(*_sort)); |
81 | 607k | case Kind::Array: |
82 | 607k | return resolveArraySort(dynamic_cast<ArraySort const&>(*_sort)); |
83 | 4.54M | case Kind::Tuple: |
84 | 4.54M | return resolveTupleSort(dynamic_cast<TupleSort const&>(*_sort)); |
85 | 0 | default: |
86 | 0 | smtAssert(false, "Invalid SMT sort"); |
87 | 10.7M | } |
88 | 10.7M | } |
89 | | |
90 | | SortPointer SMTLib2Context::unresolve(SortId _sortId) const |
91 | 0 | { |
92 | 0 | smtAssert(_sortId < m_knownTypes.size()); |
93 | 0 | auto const& type = *m_knownTypes[_sortId]; |
94 | 0 | switch (type.kind) |
95 | 0 | { |
96 | 0 | case Kind::Int: |
97 | 0 | return SortProvider::sintSort; |
98 | 0 | case Kind::Bool: |
99 | 0 | return SortProvider::boolSort; |
100 | 0 | case Kind::BitVector: |
101 | 0 | { |
102 | 0 | auto it = ranges::find_if(m_bitVectorSorts, [&](auto const& entry) { return entry.second == _sortId; }); |
103 | 0 | smtAssert(it != m_bitVectorSorts.end()); |
104 | 0 | return std::make_shared<BitVectorSort>(it->first); |
105 | 0 | } |
106 | 0 | case Kind::Array: |
107 | 0 | { |
108 | 0 | auto it = ranges::find_if(m_arraySorts, [&](auto const& entry) { return entry.second == _sortId; }); |
109 | 0 | smtAssert(it != m_arraySorts.end()); |
110 | 0 | return std::make_shared<ArraySort>(unresolve(it->first.first), unresolve(it->first.second)); |
111 | 0 | } |
112 | 0 | case Kind::Tuple: |
113 | 0 | { |
114 | 0 | auto const& tupleType = dynamic_cast<TupleType const&>(type); |
115 | 0 | std::vector<std::string> memberNames; |
116 | 0 | std::vector<SortPointer> memberTypes; |
117 | 0 | for (auto&& [name, sortId] : tupleType.accessors) |
118 | 0 | { |
119 | 0 | memberNames.push_back(name); |
120 | 0 | memberTypes.push_back(unresolve(sortId)); |
121 | 0 | } |
122 | 0 | return std::make_shared<TupleSort>(tupleType.name, std::move(memberNames), std::move(memberTypes)); |
123 | 0 | } |
124 | 0 | default: |
125 | 0 | smtAssert(false, "Invalid SMT sort"); |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | | SortId SMTLib2Context::resolveBitVectorSort(BitVectorSort const& _sort) |
130 | 0 | { |
131 | 0 | auto size = _sort.size; |
132 | 0 | auto it = m_bitVectorSorts.find(size); |
133 | 0 | if (it == m_bitVectorSorts.end()) |
134 | 0 | { |
135 | 0 | auto newId = static_cast<uint32_t>(m_knownTypes.size()); |
136 | 0 | m_knownTypes.emplace_back(std::make_unique<SMTLibSort>(Kind::BitVector, "(_ BitVec " + std::to_string(size) + ')', std::vector<SortId>{}, SortId{newId})); |
137 | 0 | auto&& [newIt, inserted] = m_bitVectorSorts.emplace(size, SortId{newId}); |
138 | 0 | smtAssert(inserted); |
139 | 0 | return newIt->second; |
140 | 0 | } |
141 | 0 | return it->second; |
142 | 0 | } |
143 | | |
144 | | SortId SMTLib2Context::resolveArraySort(ArraySort const& _sort) |
145 | 607k | { |
146 | 607k | smtAssert(_sort.domain && _sort.range); |
147 | 607k | auto domainSort = resolve(_sort.domain); |
148 | 607k | auto rangeSort = resolve(_sort.range); |
149 | 607k | auto pair = std::make_pair(domainSort, rangeSort); |
150 | 607k | auto it = m_arraySorts.find(pair); |
151 | 607k | if (it == m_arraySorts.end()) |
152 | 106k | { |
153 | 106k | auto newId = static_cast<uint32_t>(m_knownTypes.size()); |
154 | 106k | m_knownTypes.emplace_back(std::make_unique<SMTLibSort>(Kind::Array, "Array", std::vector<SortId>{domainSort, rangeSort}, SortId{newId})); |
155 | 106k | auto&& [newIt, inserted] = m_arraySorts.emplace(pair, SortId{newId}); |
156 | 106k | smtAssert(inserted); |
157 | 106k | return newIt->second; |
158 | 106k | } |
159 | 501k | return it->second; |
160 | 607k | } |
161 | | |
162 | | SortId SMTLib2Context::resolveTupleSort(TupleSort const& _sort) |
163 | 4.54M | { |
164 | 4.54M | auto const& tupleName = _sort.name; |
165 | 4.54M | auto it = m_tupleSorts.find(tupleName); |
166 | 4.54M | if (it == m_tupleSorts.end()) |
167 | 211k | { |
168 | 211k | std::vector<std::pair<std::string, SortId>> accessors; |
169 | 211k | smtAssert(_sort.members.size() == _sort.components.size()); |
170 | 1.11M | for (std::size_t i = 0u; i < _sort.members.size(); ++i) |
171 | 899k | accessors.emplace_back(_sort.members[i], resolve(_sort.components[i])); |
172 | 211k | auto newId = static_cast<uint32_t>(m_knownTypes.size()); |
173 | 211k | m_knownTypes.emplace_back(std::make_unique<TupleType>(tupleName, std::move(accessors), SortId{newId})); |
174 | 211k | auto&& [newIt, inserted] = m_tupleSorts.emplace(tupleName, SortId{newId}); |
175 | 211k | smtAssert(inserted); |
176 | 211k | if (m_callback) |
177 | 211k | m_callback(_sort); |
178 | 211k | return newIt->second; |
179 | 211k | } |
180 | 4.33M | return it->second; |
181 | 4.54M | } |
182 | | |
183 | | std::string SMTLib2Context::toString(SortId _id) |
184 | 9.34M | { |
185 | 9.34M | auto const& sort = m_knownTypes.at(_id); |
186 | 9.34M | switch (sort->kind) |
187 | 9.34M | { |
188 | 4.23M | case Kind::Int: |
189 | 4.23M | return "Int"; |
190 | 388k | case Kind::Bool: |
191 | 388k | return "Bool"; |
192 | 0 | case Kind::BitVector: |
193 | 0 | return dynamic_cast<SMTLibSort const&>(*sort).name; |
194 | 344k | case Kind::Array: |
195 | 344k | { |
196 | 344k | auto const& arraySort = dynamic_cast<SMTLibSort const&>(*sort); |
197 | 344k | smtAssert(arraySort.args.size() == 2); |
198 | 344k | return "(Array " + toString(arraySort.args.at(0)) + ' ' + toString(arraySort.args.at(1)) + ')'; |
199 | 0 | } |
200 | 4.37M | case Kind::Tuple: |
201 | 4.37M | { |
202 | 4.37M | auto const& tupleType = dynamic_cast<TupleType const&>(*sort); |
203 | 4.37M | return '|' + tupleType.name + '|'; |
204 | 0 | } |
205 | 0 | default: |
206 | 0 | smtAssert(false, "Invalid SMT sort"); |
207 | 9.34M | } |
208 | 9.34M | } |
209 | | |
210 | | std::string SMTLib2Context::toSmtLibSort(solidity::smtutil::SortPointer const& _sort) |
211 | 8.65M | { |
212 | 8.65M | return toString(resolve(_sort)); |
213 | 8.65M | } |
214 | | |
215 | | std::string SMTLib2Context::toSExpr(Expression const& _expr) |
216 | 30.6M | { |
217 | 30.6M | if (_expr.arguments.empty()) |
218 | 17.0M | return _expr.name; |
219 | | |
220 | 13.5M | std::string sexpr = "("; |
221 | 13.5M | if (_expr.name == "int2bv") |
222 | 11.6k | { |
223 | 11.6k | size_t size = std::stoul(_expr.arguments[1].name); |
224 | 11.6k | auto arg = toSExpr(_expr.arguments.front()); |
225 | 11.6k | auto int2bv = "(_ int2bv " + std::to_string(size) + ")"; |
226 | | // Some solvers treat all BVs as unsigned, so we need to manually apply 2's complement if needed. |
227 | 11.6k | sexpr += std::string("ite ") + |
228 | 11.6k | "(>= " + arg + " 0) " + |
229 | 11.6k | "(" + int2bv + " " + arg + ") " + |
230 | 11.6k | "(bvneg (" + int2bv + " (- " + arg + ")))"; |
231 | 11.6k | } |
232 | 13.5M | else if (_expr.name == "bv2int") |
233 | 6.74k | { |
234 | 6.74k | auto intSort = std::dynamic_pointer_cast<IntSort>(_expr.sort); |
235 | 6.74k | smtAssert(intSort, ""); |
236 | | |
237 | 6.74k | auto arg = toSExpr(_expr.arguments.front()); |
238 | 6.74k | auto nat = "(bv2nat " + arg + ")"; |
239 | | |
240 | 6.74k | if (!intSort->isSigned) |
241 | 6.11k | return nat; |
242 | | |
243 | 624 | auto bvSort = std::dynamic_pointer_cast<BitVectorSort>(_expr.arguments.front().sort); |
244 | 624 | smtAssert(bvSort, ""); |
245 | 624 | auto size = std::to_string(bvSort->size); |
246 | 624 | auto pos = std::to_string(bvSort->size - 1); |
247 | | |
248 | | // Some solvers treat all BVs as unsigned, so we need to manually apply 2's complement if needed. |
249 | 624 | sexpr += std::string("ite ") + |
250 | 624 | "(= ((_ extract " + pos + " " + pos + ")" + arg + ") #b0) " + |
251 | 624 | nat + " " + |
252 | 624 | "(- (bv2nat (bvneg " + arg + ")))"; |
253 | 624 | } |
254 | 13.5M | else if (_expr.name == "const_array") |
255 | 77.8k | { |
256 | 77.8k | smtAssert(_expr.arguments.size() == 2, ""); |
257 | 77.8k | auto sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments.at(0).sort); |
258 | 77.8k | smtAssert(sortSort, ""); |
259 | 77.8k | auto arraySort = std::dynamic_pointer_cast<ArraySort>(sortSort->inner); |
260 | 77.8k | smtAssert(arraySort, ""); |
261 | 77.8k | sexpr += "(as const " + toSmtLibSort(arraySort) + ") "; |
262 | 77.8k | sexpr += toSExpr(_expr.arguments.at(1)); |
263 | 77.8k | } |
264 | 13.5M | else if (_expr.name == "tuple_get") |
265 | 2.02M | { |
266 | 2.02M | smtAssert(_expr.arguments.size() == 2, ""); |
267 | 2.02M | auto tupleSort = std::dynamic_pointer_cast<TupleSort>(_expr.arguments.at(0).sort); |
268 | 2.02M | size_t index = std::stoul(_expr.arguments.at(1).name); |
269 | 2.02M | smtAssert(index < tupleSort->members.size(), ""); |
270 | 2.02M | sexpr += "|" + tupleSort->members.at(index) + "| " + toSExpr(_expr.arguments.at(0)); |
271 | 2.02M | } |
272 | 11.4M | else if (_expr.name == "tuple_constructor") |
273 | 116k | { |
274 | 116k | auto tupleSort = std::dynamic_pointer_cast<TupleSort>(_expr.sort); |
275 | 116k | smtAssert(tupleSort, ""); |
276 | 116k | sexpr += "|" + tupleSort->name + "|"; |
277 | 116k | for (auto const& arg: _expr.arguments) |
278 | 218k | sexpr += " " + toSExpr(arg); |
279 | 116k | } |
280 | 11.3M | else |
281 | 11.3M | { |
282 | 11.3M | sexpr += _expr.name; |
283 | 11.3M | for (auto const& arg: _expr.arguments) |
284 | 27.8M | sexpr += " " + toSExpr(arg); |
285 | 11.3M | } |
286 | 13.5M | sexpr += ")"; |
287 | 13.5M | return sexpr; |
288 | 13.5M | } |
289 | | |
290 | | std::optional<SortPointer> SMTLib2Context::getTupleType(std::string const& _name) const |
291 | 0 | { |
292 | 0 | auto it = m_tupleSorts.find(_name); |
293 | 0 | return it == m_tupleSorts.end() ? std::nullopt : std::optional<SortPointer>(unresolve(it->second)); |
294 | 0 | } |
295 | | |
296 | | std::optional<std::pair<std::string, SortPointer>> SMTLib2Context::getTupleAccessor(std::string const& _name) const |
297 | 0 | { |
298 | 0 | for (auto&& [_, sortId] : m_tupleSorts) |
299 | 0 | { |
300 | 0 | auto const& type = m_knownTypes.at(sortId); |
301 | 0 | smtAssert(type->kind == Kind::Tuple); |
302 | 0 | auto const& tupleType = dynamic_cast<TupleType const&>(*type); |
303 | 0 | for (auto&& [memberName, memberSort] : tupleType.accessors) |
304 | 0 | if (memberName == _name) |
305 | 0 | return std::make_pair(memberName, unresolve(memberSort)); |
306 | 0 | } |
307 | 0 | return std::nullopt; |
308 | 0 | } |
309 | | |
310 | | void SMTLib2Context::setTupleDeclarationCallback(TupleDeclarationCallback _callback) |
311 | 47.4k | { |
312 | 47.4k | m_callback = std::move(_callback); |
313 | 47.4k | } |
314 | | } // namespace solidity::smtutil |