ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
type_utils.h
Go to the documentation of this file.
1#pragma once
2
3#include <util/lang/c_types.h>
4#include <util/irep/expr.h>
7#include <util/irep/type.h>
9
10#include <nlohmann/json.hpp>
11
12#include <cctype>
13#include <map>
14#include <set>
15#include <string>
16
41
59
61{
62 bool has_float = false;
63 bool has_int = false;
64 bool has_bool = false;
65 bool has_none = false;
70 bool has_other = false;
71};
72
74{
75public:
79 static const irep_idt &
81 {
82 return argument.get_identifier().empty() ? argument.identifier()
83 : argument.get_identifier();
84 }
85
86 static bool is_builtin_type(const std::string &name)
87 {
88 return (
89 name == "int" || name == "float" || name == "bool" || name == "str" ||
90 name == "chr" || name == "hex" || name == "oct" || name == "bin" ||
91 name == "ord" || name == "tuple" || name == "list" || name == "dict" ||
92 name == "set" || name == "frozenset" || name == "bytes" ||
93 name == "bytearray" || name == "range" || name == "complex" ||
94 name == "type" || name == "object" || name == "abs" || name == "None" ||
95 name == "divmod" || name == "slice");
96 }
97
100 static bool is_str_only_method(const std::string &name)
101 {
102 static const std::set<std::string> str_only_methods = {
103 "capitalize", "casefold", "center", "count", "encode",
104 "endswith", "expandtabs", "find", "format", "format_map",
105 "index", "isalnum", "isalpha", "isascii", "isdecimal",
106 "isdigit", "isidentifier", "islower", "isnumeric", "isprintable",
107 "isspace", "istitle", "isupper", "join", "ljust",
108 "lower", "lstrip", "maketrans", "partition", "removeprefix",
109 "removesuffix", "replace", "rfind", "rindex", "rjust",
110 "rpartition", "rsplit", "rstrip", "split", "splitlines",
111 "startswith", "strip", "swapcase", "title", "translate",
112 "upper", "zfill"};
113 return str_only_methods.count(name) > 0;
114 }
115
116 static bool is_consensus_type(const std::string &name)
117 {
118 return (
119 name == "uint64" || name == "uint256" || name == "Epoch" ||
120 name == "Gwei" || name == "BLSFieldElement" || name == "Slot" ||
121 name == "GeneralizedIndex");
122 }
123
134 static bool is_typeddict(const std::string &name)
135 {
136 return name == "TypedDict";
137 }
138
139 static bool is_consensus_func(const std::string &name)
140 {
141 return consensus_func_to_type().find(name) !=
142 consensus_func_to_type().end();
143 }
144
145 static std::string get_type_from_consensus_func(const std::string &name)
146 {
147 if (!is_consensus_func(name))
148 return std::string();
149 return consensus_func_to_type().at(name);
150 }
151
155 static bool is_nondet_collection_builder(const std::string &name)
156 {
157 return name.rfind("_nondet_list_", 0) == 0 ||
158 (name.rfind("_nondet_dict_", 0) == 0 && name != "_nondet_dict_size");
159 }
160
161 static bool is_python_model_func(const std::string &name)
162 {
163 return (
164 name == "ESBMC_range_next_" || name == "ESBMC_range_has_next_" ||
165 name == "bit_length" || name == "conjugate" || name == "from_bytes" ||
166 name == "to_bytes" || name == "randint" || name == "random" ||
167 name == "all" || is_nondet_collection_builder(name));
168 }
169
170 static bool is_python_exceptions(const std::string &name)
171 {
172 static const std::unordered_set<std::string> exceptions = {
173 "BaseException", "Exception",
174 "ValueError", "TypeError",
175 "AttributeError", "IndexError",
176 "KeyError", "ZeroDivisionError",
177 "OverflowError", "AssertionError",
178 "NameError", "OSError",
179 "FileNotFoundError", "FileExistsError",
180 "PermissionError", "NotImplementedError",
181 "ImportError", "ModuleNotFoundError",
182 "RuntimeError", "StopIteration",
183 "EOFError", "UnboundLocalError",
184 "ArithmeticError", "FloatingPointError",
185 "LookupError"};
186 return exceptions.count(name) != 0;
187 }
188
189 static bool is_c_model_func(const std::string &func_name)
190 {
191 return func_name == "ceil" || func_name == "floor" || func_name == "fabs" ||
192 func_name == "sin" || func_name == "cos" || func_name == "exp" ||
193 func_name == "fmod" || func_name == "sqrt" || func_name == "fmin" ||
194 func_name == "fmax" || func_name == "trunc" ||
195 func_name == "round" || func_name == "copysign" ||
196 func_name == "arctan" || func_name == "arccos" ||
197 func_name == "dot" || func_name == "add" ||
198 func_name == "subtract" || func_name == "multiply" ||
199 func_name == "divide" || func_name == "transpose" ||
200 func_name == "det" || func_name == "matmul" || func_name == "pow" ||
201 func_name == "log" || func_name == "pow_by_squaring" ||
202 func_name == "log2" || func_name == "log1p_taylor" ||
203 func_name == "ldexp" || func_name == "__ESBMC_sin" ||
204 func_name == "__ESBMC_cos" || func_name == "__ESBMC_sqrt" ||
205 func_name == "__ESBMC_exp" || func_name == "__ESBMC_log";
206 }
207
208 static bool is_ordered_comparison(const std::string &op)
209 {
210 return op == "Lt" || op == "Gt" || op == "LtE" || op == "GtE";
211 }
212
213 static bool is_relational_op(const std::string &op)
214 {
215 return (
216 op == "Eq" || op == "Lt" || op == "LtE" || op == "NotEq" || op == "Gt" ||
217 op == "GtE" || op == "And" || op == "Or");
218 }
219
220 // Encapsulated accessors for the irep string attributes the Python
221 // frontend attaches to legacy typet nodes. These attributes are read by
222 // shared/downstream passes — "#cpp_type" by clang_cpp_adjust, the C/C++
223 // pretty-printers and goto2c; "#member_name" by the shared clang-cpp pass
224 // — so they must stay on the legacy node at the migrate seam. Funnelling
225 // every raw .set/.get("#…") through one seam each is the Phase 4.1
226 // hardening step of the IREP2 migration (docs/roadmap/irep2-migration.md Part
227 // IV, F-P5 / §15). Keep the keys and values byte-identical: this is a
228 // behaviour-preserving relocation, not a semantic change.
229 static void set_cpp_type(typet &t, const irep_idt &value)
230 {
231 t.cpp_type(value);
232 }
233
234 static irep_idt get_cpp_type(const typet &t)
235 {
236 return t.cpp_type();
237 }
238
239 static void set_member_name(typet &t, const irep_idt &value)
240 {
242 }
243
244 static void remove_member_name(typet &t)
245 {
247 }
248
249 // A scalar T whose `Optional[T]` is the Optional<T> struct, not a T*.
250 static bool is_optional_scalar(const typet &t)
251 {
252 return t == long_long_int_type() || t == long_long_uint_type() ||
253 t == double_type() || t == bool_type();
254 }
255
256 // The Optional<T> struct type_handler::build_optional_type makes.
257 static bool is_optional_struct(const typet &t)
258 {
259 return t.is_struct() &&
260 to_struct_type(t).tag().as_string().starts_with("tag-Optional_");
261 }
262
263 // Optional[str]: a pointer to an unsized char array, NULL for None.
264 static bool is_optional_str_pointer(const typet &t)
265 {
266 return t.is_pointer() && t.subtype().is_array() &&
267 t.subtype().subtype() == char_type() &&
269 }
270
271 static bool is_char_type(const typet &t)
272 {
273 return (t.is_signedbv() || t.is_unsignedbv()) && get_cpp_type(t) == "char";
274 }
275
276 // Distinguishes a `bytes` value from a numpy-style numeric array, so `+`
277 // routes to concatenation only for the former. Both share the same legacy
278 // `array of long_long_int_type` representation here
279 // (type_handler::get_typet's "bytes" branch).
280 static bool is_bytes_array(const typet &t)
281 {
282 return t.is_array() && get_cpp_type(t) == "bytes";
283 }
284
285 static bool is_float_vs_char(const exprt &a, const exprt &b)
286 {
287 const auto &type_a = a.type();
288 const auto &type_b = b.type();
289 return (type_a.is_floatbv() && is_char_type(type_b)) ||
290 (type_b.is_floatbv() && is_char_type(type_a));
291 }
292
293 static std::string remove_quotes(const std::string &str)
294 {
295 if (str.length() < 2)
296 return str;
297
298 // Check for single quotes
299 if (str.front() == '\'' && str.back() == '\'')
300 return str.substr(1, str.length() - 2);
301
302 // Check for double quotes
303 if (str.front() == '"' && str.back() == '"')
304 return str.substr(1, str.length() - 2);
305
306 // No quotes found, return original string
307 return str;
308 }
309
310 // Select widest type from flags based on hierarchy: float > int > bool
311 static typet
312 select_widest_type(const TypeFlags &flags, const typet &default_type)
313 {
314 // A member outside the float/int/bool hierarchy has no place in it, so
315 // widening would pick a scalar for a union that is not one (#7872).
316 if (flags.has_other)
317 return default_type;
318
319 if (flags.has_float)
320 return double_type();
321 if (flags.has_int)
322 return long_long_int_type();
323 if (flags.has_bool)
324 return bool_type();
325
326 return default_type;
327 }
328
329 // Extract type flags from Union annotation slice
330 static TypeFlags extract_union_types(const nlohmann::json &slice)
331 {
332 TypeFlags flags;
333
334 if (slice["_type"] == "Tuple" && slice.contains("elts"))
335 {
336 for (const auto &elem : slice["elts"])
337 update_type_flags_from_node(elem, flags);
338 }
339
340 return flags;
341 }
342
343 // Extract type flags from PEP 604 union syntax: int | bool
344 static TypeFlags extract_binop_union_types(const nlohmann::json &binop_node)
345 {
346 TypeFlags flags;
347
348 // Extract from left operand. `|` is left-associative, so a chained union
349 // nests on the left: `int | bool | float` is
350 // BinOp(BinOp(int, bool), float).
351 if (binop_node.contains("left"))
352 {
353 const auto &left = binop_node["left"];
354 if (left["_type"] == "BinOp")
355 merge_type_flags(flags, extract_binop_union_types(left));
356 else
357 update_type_flags_from_node(left, flags);
358 }
359
360 // Extract from right operand (may be nested BinOp for chained unions)
361 if (binop_node.contains("right"))
362 {
363 const auto &right = binop_node["right"];
364 if (right["_type"] == "BinOp")
365 {
366 // Recursively handle chained unions: int | bool | float
367 TypeFlags right_flags = extract_binop_union_types(right);
368 merge_type_flags(flags, right_flags);
369 }
370 else
371 update_type_flags_from_node(right, flags);
372 }
373
374 return flags;
375 }
376
377 // Whether an annotation spells a union with None (`T | None`,
378 // `Union[T, None]`).
379 static bool annotation_admits_none(const nlohmann::json &ann)
380 {
381 if (!ann.is_object())
382 return false;
383 if (ann.value("_type", "") == "BinOp")
385 if (
386 ann.value("_type", "") != "Subscript" ||
387 ann["value"].value("id", "") != "Union")
388 return false;
389 const nlohmann::json &slice = ann["slice"];
390 for (const auto &elt : slice.value("elts", nlohmann::json::array()))
391 if (elt.value("_type", "") == "Constant" && elt["value"].is_null())
392 return true;
393 return false;
394 }
395
396 static bool is_integer_type(const typet &type)
397 {
398 return type.is_signedbv() || type.is_unsignedbv();
399 }
400
402 static bool is_numpy_string_array(const typet &type)
403 {
404 if (!type.is_array() || !type.subtype().is_array())
405 return false;
406 return type.subtype().subtype() == char_type() ||
407 is_numpy_string_array(type.subtype());
408 }
409
411 static typet array_scalar_type(const typet &type)
412 {
413 typet scalar = type;
414 while (scalar.is_array())
415 scalar = scalar.subtype();
416 return scalar;
417 }
418
420 static bool is_numpy_complex_array(const typet &type)
421 {
422 return type.is_array() && is_complex_type(array_scalar_type(type));
423 }
424
425 static bool is_string_type(const typet &type)
426 {
427 // String types are represented as arrays or pointers to char
428 if (type.is_array() && type.subtype() == char_type())
429 return true;
430
431 if (type.is_pointer())
432 {
433 const typet &subtype = type.subtype();
434 // Direct pointer to char: char*
435 if (subtype == char_type())
436 return true;
437 // Pointer to char array: char(*)[N] (string literals)
438 if (subtype.is_array() && subtype.subtype() == char_type())
439 return true;
440 }
441
442 return false;
443 }
444
445 static bool is_dict_subscript(const nlohmann::json &element)
446 {
447 return element.contains("_type") && element["_type"] == "Subscript" &&
448 element.contains("value");
449 }
450
451 static inline bool is_type_identifier(const std::string &name)
452 {
453 static const std::unordered_set<std::string> type_identifiers = {
454 "int",
455 "float",
456 "str",
457 "bool",
458 "bytes",
459 "list",
460 "set",
461 "tuple",
462 "type",
463 "object",
464 "complex",
465 "frozenset",
466 "NoneType"};
467 return type_identifiers.find(name) != type_identifiers.end();
468 }
469
473 static bool strip_pep515_underscores(const std::string &s, std::string &out)
474 {
475 out.clear();
476 out.reserve(s.size());
477 for (std::size_t i = 0; i < s.size(); i++)
478 {
479 if (s[i] != '_')
480 {
481 out.push_back(s[i]);
482 continue;
483 }
484 const bool between_digits =
485 i > 0 && i + 1 < s.size() &&
486 isdigit(static_cast<unsigned char>(s[i - 1])) &&
487 isdigit(static_cast<unsigned char>(s[i + 1]));
488 if (!between_digits)
489 return false;
490 }
491 return true;
492 }
493
494private:
495 static void
496 update_type_flags_from_node(const nlohmann::json &node, TypeFlags &flags)
497 {
498 if (node["_type"] == "Name" && node.contains("id"))
499 {
500 const std::string type_str = node["id"].get<std::string>();
501 if (type_str == "float")
502 flags.has_float = true;
503 else if (type_str == "int")
504 flags.has_int = true;
505 else if (type_str == "bool")
506 flags.has_bool = true;
507 else if (type_str == "None" || type_str == "NoneType")
508 flags.has_none = true;
509 else
510 flags.has_other = true;
511 }
512 else if (
513 node["_type"] == "Constant" && node.contains("value") &&
514 node["value"].is_null())
515 {
516 flags.has_none = true;
517 }
518 else
519 flags.has_other = true;
520 }
521
522 static void merge_type_flags(TypeFlags &dest, const TypeFlags &src)
523 {
524 dest.has_float = dest.has_float || src.has_float;
525 dest.has_int = dest.has_int || src.has_int;
526 dest.has_bool = dest.has_bool || src.has_bool;
527 dest.has_none = dest.has_none || src.has_none;
528 dest.has_other = dest.has_other || src.has_other;
529 }
530
531 static const std::map<std::string, std::string> &consensus_func_to_type()
532 {
533 // hash() -> bytes (Bytes32), matching models/consensus.py's real
534 // signature -- not the real Python builtin's int.
535 static const std::map<std::string, std::string> func_to_type = {
536 {"hash", "bytes"}};
537 return func_to_type;
538 }
539};
typet long_long_uint_type()
Definition c_types.cpp:146
typet long_long_int_type()
Definition c_types.cpp:101
typet double_type()
Definition c_types.cpp:240
typet char_type()
Definition c_types.cpp:166
typet bool_type()
Definition c_types.cpp:85
const exprt & size() const
Definition std_types.h:414
Definition std_types.h:297
const irep_idt & get_identifier() const
Definition std_types.h:332
Definition expr.h:34
bool is_zero() const
Definition expr.cpp:158
Definition irep_idt.h:12
const std::string & as_string() const
Definition irep_idt.h:138
bool empty() const
Definition irep_idt.h:54
bool is_pointer() const
Definition irep.h:1181
bool is_array() const
Definition irep.h:1125
const irep_idt & identifier() const
Definition irep.h:167
const irep_idt & member_name() const
Owning class tag of a member function type, e.g.
Definition irep.h:489
const irep_idt & tag() const
Definition irep.h:192
bool is_struct() const
Definition irep.h:1189
bool is_unsignedbv() const
Definition irep.h:1205
const irep_idt & cpp_type() const
Source-level spelling of a type, e.g.
Definition irep.h:503
typet & type()
Definition irep.h:153
bool is_signedbv() const
Definition irep.h:1185
void remove_member_name()
Definition irep.h:758
Definition type_utils.h:74
static bool is_optional_str_pointer(const typet &t)
Definition type_utils.h:264
static TypeFlags extract_union_types(const nlohmann::json &slice)
Definition type_utils.h:330
static bool is_dict_subscript(const nlohmann::json &element)
Definition type_utils.h:445
static TypeFlags extract_binop_union_types(const nlohmann::json &binop_node)
Definition type_utils.h:344
static bool is_type_identifier(const std::string &name)
Definition type_utils.h:451
static void set_cpp_type(typet &t, const irep_idt &value)
Definition type_utils.h:229
static typet array_scalar_type(const typet &type)
The scalar type at the leaves of the (possibly nested) array type.
Definition type_utils.h:411
static bool is_python_exceptions(const std::string &name)
Definition type_utils.h:170
static bool is_numpy_complex_array(const typet &type)
A numpy array of complex numbers.
Definition type_utils.h:420
static bool is_optional_scalar(const typet &t)
Definition type_utils.h:250
static std::string remove_quotes(const std::string &str)
Definition type_utils.h:293
static bool is_python_model_func(const std::string &name)
Definition type_utils.h:161
static bool is_c_model_func(const std::string &func_name)
Definition type_utils.h:189
static void remove_member_name(typet &t)
Definition type_utils.h:244
static bool is_char_type(const typet &t)
Definition type_utils.h:271
static irep_idt get_cpp_type(const typet &t)
Definition type_utils.h:234
static std::string get_type_from_consensus_func(const std::string &name)
Definition type_utils.h:145
static bool annotation_admits_none(const nlohmann::json &ann)
Definition type_utils.h:379
static void set_member_name(typet &t, const irep_idt &value)
Definition type_utils.h:239
static typet select_widest_type(const TypeFlags &flags, const typet &default_type)
Definition type_utils.h:312
static bool is_ordered_comparison(const std::string &op)
Definition type_utils.h:208
static bool is_nondet_collection_builder(const std::string &name)
True for the monomorphic collection builders in models/nondet.py (_nondet_list_int,...
Definition type_utils.h:155
static bool strip_pep515_underscores(const std::string &s, std::string &out)
Copies s to out with PEP 515 underscore separators removed.
Definition type_utils.h:473
static bool is_string_type(const typet &type)
Definition type_utils.h:425
static bool is_numpy_string_array(const typet &type)
A numpy array of fixed-width strings: arrays of char arrays.
Definition type_utils.h:402
static bool is_str_only_method(const std::string &name)
A method of str that int and float lack: set(dir(str)) - set(dir(int)).
Definition type_utils.h:100
static bool is_typeddict(const std::string &name)
Check if a name is TypedDict from typing module.
Definition type_utils.h:134
static bool is_bytes_array(const typet &t)
Definition type_utils.h:280
static bool is_consensus_type(const std::string &name)
Definition type_utils.h:116
static bool is_relational_op(const std::string &op)
Definition type_utils.h:213
static const irep_idt & argument_identifier(const code_typet::argumentt &argument)
A parameter's id, preferring #identifier: frontends disagree on which key holds it and only #identifi...
Definition type_utils.h:80
static bool is_consensus_func(const std::string &name)
Definition type_utils.h:139
static bool is_float_vs_char(const exprt &a, const exprt &b)
Definition type_utils.h:285
static bool is_builtin_type(const std::string &name)
Definition type_utils.h:86
static bool is_integer_type(const typet &type)
Definition type_utils.h:396
static bool is_optional_struct(const typet &t)
Definition type_utils.h:257
Definition type.h:9
const typet & subtype() const
Definition type.h:17
Definition toml.hpp:1531
int isdigit(int c)
Definition ctype.c:44
@ RETURN
Definition goto_program.h:39
@ ASSERT
Definition goto_program.h:32
Definition exceptions.py:1
const struct_typet & to_struct_type(const typet &type)
Definition std_types.h:241
const array_typet & to_array_type(const typet &type)
Definition std_types.h:425
Definition type_utils.h:61
bool has_none
Definition type_utils.h:65
bool has_bool
Definition type_utils.h:64
bool has_int
Definition type_utils.h:63
bool has_other
A union member this tracker cannot represent – a list, dict, class or any other non-scalar.
Definition type_utils.h:70
bool has_float
Definition type_utils.h:62
bool is_complex_type(const typet &type)
Definition type_handler.h:39
ExpressionType
Definition type_utils.h:43
@ SLICE
Definition type_utils.h:56
@ CLASS_MRO
Definition type_utils.h:57
@ LIST
Definition type_utils.h:52
@ BINARY_OPERATION
Definition type_utils.h:44
@ TUPLE
Definition type_utils.h:55
@ VARIABLE_REF
Definition type_utils.h:51
@ LITERAL
Definition type_utils.h:49
@ SUBSCRIPT
Definition type_utils.h:50
@ FUNC_CALL
Definition type_utils.h:46
@ UNARY_OPERATION
Definition type_utils.h:45
@ IF_EXPR
Definition type_utils.h:47
@ LOGICAL_OPERATION
Definition type_utils.h:48
@ FSTRING
Definition type_utils.h:54
StatementType
Definition type_utils.h:18
@ BREAK
Definition type_utils.h:31
@ RAISE
Definition type_utils.h:33
@ CONTINUE
Definition type_utils.h:32
@ FOR_STATEMENT
Definition type_utils.h:24
@ EXCEPTHANDLER
Definition type_utils.h:38
@ NONLOCAL
Definition type_utils.h:36
@ WHILE_STATEMENT
Definition type_utils.h:23
@ IF_STATEMENT
Definition type_utils.h:22
@ VARIABLE_ASSIGN
Definition type_utils.h:19
@ TRY
Definition type_utils.h:37
@ UNKNOWN
Definition type_utils.h:34
@ GLOBAL
Definition type_utils.h:35
@ DELETE_STATEMENT
Definition type_utils.h:39
@ PASS
Definition type_utils.h:29
@ COMPOUND_ASSIGN
Definition type_utils.h:20
@ CLASS_DEFINITION
Definition type_utils.h:28
@ EXPR
Definition type_utils.h:25
@ IMPORT
Definition type_utils.h:30
@ FUNC_DEFINITION
Definition type_utils.h:21