ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
type_handler.h
Go to the documentation of this file.
1#pragma once
2
3#include <util/lang/c_types.h>
7#include <util/irep/migrate.h>
8#include <irep2/irep2_utils.h>
9#include <nlohmann/json.hpp>
10#include <optional>
11
13
14// P26: Cached double type — avoids constructing a new floatbv_typet on every
15// call. Initialized once on first use; thread-safe (C++11 static local
16// semantics).
18{
19 static const typet instance = double_type();
20 return instance;
21}
22
23// P4: Static-local struct type — eliminates repeated heap allocations.
24// Returns a const reference to the single global instance.
26{
27 static const struct_typet instance = []() {
29 t.tag("complex");
30 t.components().push_back(
31 struct_typet::componentt("real", "real", double_type()));
32 t.components().push_back(
33 struct_typet::componentt("imag", "imag", double_type()));
34 return t;
35 }();
36 return instance;
37}
38
39inline bool is_complex_type(const typet &type)
40{
41 if (type.id() == "symbol")
42 return to_symbol_type(type).get_identifier().as_string() == "tag-complex";
43
44 if (!type.is_struct())
45 return false;
46
47 const struct_typet &struct_type = to_struct_type(type);
48 const std::string tag = struct_type.tag().as_string();
49 return tag == "complex" || tag == "tag-complex";
50}
51
52inline exprt make_complex(const exprt &real, const exprt &imag)
53{
54 // V.3: build the complex struct literal in IREP2, back-migrating once. Each
55 // non-double operand's int/bool/float -> double conversion is exactly the
56 // typecast2t migrate_expr builds for the legacy typecast_exprt (ieee cast,
57 // rounding mode implicit), so the back-migrated struct is byte-identical to
58 // the old struct_exprt. Mirrors complex_typecast / complex_to_bool_expr.
59 const typet &dt = cached_double_type();
60 const type2tc dt2 = migrate_type(dt);
61
62 auto to_double2 = [&](const exprt &v) -> expr2tc {
63 expr2tc v2;
64 migrate_expr(v, v2);
65 return v.type() == dt ? v2 : typecast2tc(dt2, v2);
66 };
67
68 std::vector<expr2tc> members{to_double2(real), to_double2(imag)};
69 return migrate_expr_back(
70 constant_struct2tc(migrate_type(get_complex_struct_type()), members));
71}
72
74{
75 if (value.statement() == "cpp-throw")
76 return value;
77
78 if (is_complex_type(value.type()))
79 return value;
80
81 // Bool cannot be directly typecast to float64 in the SMT encoder.
82 // Convert bool → int first; make_complex will then typecast int → double.
83 exprt real = value;
84 if (real.type().is_bool())
85 real = typecast_exprt(real, long_long_int_type());
86
87 return make_complex(real, from_double(0.0, cached_double_type()));
88}
89
90inline exprt complex_to_bool_expr(const exprt &complex_expr)
91{
92 const typet &dt = cached_double_type();
93 // V.3: build `z.real != 0.0 || z.imag != 0.0` in IREP2, back-migrating once.
94 // member2t over a complex source is exactly the node migrate_expr builds for
95 // the legacy member access at goto-convert (util/migrate.cpp:1580), and the
96 // not(equal) shape mirrors the legacy
97 // or_exprt(not_exprt(equality_exprt(...))) verbatim, so the back-migrated
98 // tree is byte-identical to the old one.
99 const type2tc dt2 = migrate_type(dt);
100 expr2tc complex2;
101 migrate_expr(complex_expr, complex2);
102 expr2tc zero2;
103 migrate_expr(from_double(0.0, dt), zero2);
104
105 expr2tc real_nz =
106 not2tc(equality2tc(member2tc(dt2, complex2, "real"), zero2));
107 expr2tc imag_nz =
108 not2tc(equality2tc(member2tc(dt2, complex2, "imag"), zero2));
109 return migrate_expr_back(or2tc(real_nz, imag_nz));
110}
111
113{
114public:
115 type_handler(const python_converter &converter);
116
117 /*
118 * Checks if the AST node represents a constructor call.
119 * @param json AST node in JSON format corresponding to a function call.
120 * @return true if the node is a constructor call, false otherwise.
121 */
122 bool is_constructor_call(const nlohmann::json &json) const;
123
124 /*
125 * True iff `t`'s storage has no pointer fields (primitives, char arrays,
126 * or structs whose components are recursively pointer-free). Used to gate
127 * uint64 fast paths in the list operational model — a struct payload with
128 * a pointer field cannot be reinterpreted as a uint64 array under
129 * ESBMC's byte-encoding.
130 */
131 bool is_pointer_free(const typet &t) const;
132
133 /*
134 * True iff `t` is a struct whose every component is a pointer.
135 */
136 bool is_pointer_record(const typet &t) const;
137
138 /*
139 * The `ptr_free` argument of a list-model store whose payload argument is
140 * `payload` and whose element has type `elem_type`. 2 when the payload is
141 * the address of a pointer -- a nested list or an object reference -- so
142 * the model copies it as a pointer and the stored element still names its
143 * object; 1 when is_pointer_free(elem_type); 3 when
144 * is_pointer_record(elem_type); otherwise 0.
145 */
146 exprt ptr_free_arg(const exprt &payload, const typet &elem_type) const;
147
148 /*
149 * Converts a typet to its string representation.
150 * @param t The typet to convert.
151 * @return A string containing the type name.
152 */
153 std::string type_to_string(const typet &t) const;
154
160 std::string get_python_type_name(const typet &t) const;
161
162 /*
163 * Returns the detected type for a variable.
164 * @param var_name The name of the variable.
165 * @return A string representing the variable's type.
166 */
167 std::string get_var_type(const std::string &var_name) const;
168 std::string get_var_classname(const nlohmann::json &value_node) const;
169
170 /*
171 * Creates an array_typet.
172 * @param sub_type The type of elements in the array.
173 * @param size The number of elements in the array.
174 * @return The constructed array typet.
175 */
176 typet build_array(const typet &sub_type, const size_t size) const;
177
178 std::vector<int> get_array_type_shape(const typet &array_type) const;
179
180 /*
181 * Creates a typet based on a Python type.
182 * @param ast_type The name of the Python type (e.g., "int", "str").
183 * @param type_size The size used for container types like arrays and lists
184 * (default is 0).
185 * @return The corresponding typet.
186 */
187 typet get_typet(const std::string &ast_type, size_t type_size = 0) const;
188
192 static typet class_value_type();
193 static bool is_class_value_type(const typet &type);
194
203 static typet python_int_typet();
204
207 static unsigned python_int_width();
208
209 /*
210 * Creates a typet directly from a JSON value.
211 * @param elem A JSON node representing a value.
212 * @return The corresponding typet.
213 */
214 typet get_typet(const nlohmann::json &elem) const;
215
216 // The element type of `a[i]` when `a` names an array of non-char scalars or
217 // a pointer to one (a NumPy view).
218 std::optional<typet>
219 scalar_array_element_type(const nlohmann::json &elem) const;
220
221 /*
222 * Checks if a container contains elements of different types.
223 * @param container The JSON node representing the container.
224 * @return true if the container contains multiple types, false otherwise.
225 */
226 bool has_multiple_types(const nlohmann::json &container) const;
227
228 /*
229 * Builds an array_typet from a list of JSON elements by detecting the
230 * elements' subtypes and size.
231 * @param list_value The list of elements of an array.
232 * @return The array_typet capable of holding the list's values.
233 */
234 typet get_list_type(const nlohmann::json &list_value) const;
235
236 const typet get_list_type() const;
237
239
241
242 bool is_tagged_scalar_type(const typet &t) const;
243
244 exprt tagged_scalar_type_id(const typet &type) const;
245
247 const exprt &tagged_type_id,
248 const typet &literal_type) const;
249
251
252 // A Python str is a char array or char*.
253 bool is_string_type(const typet &t) const;
254
255 // A Python numeric scalar is an int (>=16-bit bitvector, excluding the
256 // 8-bit char that backs a 1-character string), float, or bool.
257 bool is_numeric_scalar_type(const typet &t) const;
258
259 /*
260 * Gets the generic dictionary type from the symbol table.
261 * @return A pointer to the generic __python_dict__ struct type.
262 */
263 const typet get_dict_type() const;
264
265 /*
266 * Infers the specific dictionary type from a JSON value.
267 * @param dict_value The JSON node representing the dict value.
268 * @return The inferred dictionary type based on the value's structure.
269 */
270 typet get_dict_type(const nlohmann::json &dict_value) const;
271
279 typet get_slice_type() const;
280
281 /*
282 * Determines the type of an operand in binary operations.
283 * @param operand The JSON node representing the operand.
284 * @return A string representing the operand's type.
285 */
286 std::string get_operand_type(const nlohmann::json &operand) const;
287
288 /*
289 * Checks whether the given JSON object represents a 2D array (list of lists).
290 * @param arr The JSON object to check.
291 * @return true if it's a 2D array, false otherwise.
292 */
293 bool is_2d_array(const nlohmann::json &arr) const;
294
295 int get_array_dimensions(const nlohmann::json &arr) const;
296
297 /*
298 * Determines the numeric width (in bits) of a given type.
299 * @param type The type object to analyze for width determination.
300 * @return The width of the type in bits as a size_t value.
301 */
302 size_t get_type_width(const typet &type) const;
303
305 const std::string &ast_type,
306 size_t bracket_pos,
307 size_t type_size) const;
309
310 /*
311 * Returns true if `class_name` is the same as, or derives (directly or
312 * indirectly) from, `expected_base` in the current AST.
313 */
315 const std::string &class_name,
316 const std::string &expected_base) const;
317
320 std::string resolve_builtin_alias(const std::string &name) const;
321
322private:
324 exprt get_expr_helper(const nlohmann::json &json) const;
325
327 bool are_types_compatible(const typet &t1, const typet &t2) const;
328
330 typet get_canonical_string_type(const typet &t) const;
331
333 typet get_typet_from_call_func(const nlohmann::json &func) const;
334
335 const python_converter &converter_;
336};
exprt from_double(double val, const typet &type)
Definition arith_tools.cpp:147
void base_type(type2tc &type, const namespacet &ns)
Definition base_type.cpp:6
typet long_long_int_type()
Definition c_types.cpp:101
typet double_type()
Definition c_types.cpp:240
Definition expr.h:34
const std::string & as_string() const
Definition irep_idt.h:138
const irep_idt & tag() const
Definition irep.h:192
typet & type()
Definition irep.h:153
bool is_bool() const
Definition irep.h:1129
Main converter for transforming Python AST into ESBMC's intermediate representation.
Definition python_converter.h:76
Definition std_types.h:232
Definition std_types.h:87
const componentst & components() const
Definition std_types.h:184
const irep_idt & get_identifier() const
Definition std_types.h:44
typet build_optional_type(const typet &base_type) const
Definition type_handler.cpp:1624
typet build_array(const typet &sub_type, const size_t size) const
This method creates a typet representing a statically sized array.
Definition type_handler.cpp:439
exprt ptr_free_arg(const exprt &payload, const typet &elem_type) const
Definition type_handler.cpp:119
exprt tagged_scalar_byte_size(const exprt &value) const
Definition type_handler.cpp:1317
static typet python_int_typet()
Lowering for Python int — arbitrarily large in the language spec.
Definition type_handler.cpp:69
exprt tagged_scalar_type_matches(const exprt &tagged_type_id, const typet &literal_type) const
Definition type_handler.cpp:1300
std::optional< typet > scalar_array_element_type(const nlohmann::json &elem) const
Definition type_handler.cpp:896
typet get_typet(const std::string &ast_type, size_t type_size=0) const
Definition type_handler.cpp:523
size_t get_type_width(const typet &type) const
Definition type_handler.cpp:1564
static typet class_value_type()
A class used as a value that is not known statically, as a variable bound to one of several classes i...
Definition type_handler.cpp:1691
typet get_tagged_object_type() const
Definition type_handler.cpp:1277
static bool is_class_value_type(const typet &type)
Definition type_handler.cpp:1699
exprt tagged_scalar_type_id(const typet &type) const
Definition type_handler.cpp:1291
typet get_generic_typet(const std::string &ast_type, size_t bracket_pos, size_t type_size) const
Convert a Python AST type to an ESBMC internal irep type.
Definition type_handler.cpp:509
typet get_list_element_type() const
Definition type_handler.cpp:1348
bool is_string_type(const typet &t) const
Definition type_handler.cpp:1334
std::string resolve_builtin_alias(const std::string &name) const
Resolves Alias = builtin (e.g.
Definition type_handler.cpp:841
std::string get_operand_type(const nlohmann::json &operand) const
This method inspects the JSON representation of a Python operand node and attempts to infer its type ...
Definition type_handler.cpp:1366
bool is_2d_array(const nlohmann::json &arr) const
Definition type_handler.cpp:1530
bool is_pointer_record(const typet &t) const
Definition type_handler.cpp:106
bool has_multiple_types(const nlohmann::json &container) const
Definition type_handler.cpp:1071
std::string type_to_string(const typet &t) const
This utility maps internal ESBMC types to their corresponding Python type strings.
Definition type_handler.cpp:195
std::vector< int > get_array_type_shape(const typet &array_type) const
Definition type_handler.cpp:452
std::string get_var_type(const std::string &var_name) const
Definition type_handler.cpp:267
typet get_slice_type() const
Returns the registered struct type used for Python slice objects.
Definition type_handler.cpp:1353
bool is_numeric_scalar_type(const typet &t) const
Definition type_handler.cpp:1339
bool class_derives_from(const std::string &class_name, const std::string &expected_base) const
Definition type_handler.cpp:1651
const typet get_list_type() const
Definition type_handler.cpp:1266
std::string get_var_classname(const nlohmann::json &value_node) const
Definition type_handler.cpp:351
static unsigned python_int_width()
Bit width of python_int_typet() — exposed for callsites that need to compute representable ranges (e....
Definition type_handler.cpp:62
type_handler(const python_converter &converter)
Definition type_handler.cpp:76
bool is_pointer_free(const typet &t) const
Definition type_handler.cpp:87
std::string get_python_type_name(const typet &t) const
Returns the Python type name for an ESBMC type, as produced by type().
Definition type_handler.cpp:239
const typet get_dict_type() const
Definition type_handler.cpp:1676
bool is_constructor_call(const nlohmann::json &json) const
Definition type_handler.cpp:134
bool is_tagged_scalar_type(const typet &t) const
Definition type_handler.cpp:1286
int get_array_dimensions(const nlohmann::json &arr) const
Definition type_handler.cpp:1538
Definition std_expr.h:650
Definition type.h:9
Definition toml.hpp:1531
@ instance
Definition converter_compare.cpp:886
irep_container< type2t > type2tc
Definition irep2.h:430
irep_container< expr2t > expr2tc
Definition irep2.h:431
nlohmann::json json
Definition json.cpp:12
exprt migrate_expr_back(const expr2tc &ref)
Definition migrate.cpp:3949
void migrate_expr(const exprt &expr, expr2tc &new_expr_ref)
Definition migrate.cpp:1512
type2tc migrate_type(const typet &type)
Definition migrate.cpp:557
const struct_typet & to_struct_type(const typet &type)
Definition std_types.h:241
const symbol_typet & to_symbol_type(const typet &type)
Cast a generic typet to a symbol_typet.
Definition std_types.h:60
const struct_typet & get_complex_struct_type()
Definition type_handler.h:25
const typet & cached_double_type()
Definition type_handler.h:17
exprt make_complex(const exprt &real, const exprt &imag)
Definition type_handler.h:52
exprt promote_to_complex(const exprt &value)
Definition type_handler.h:73
bool is_complex_type(const typet &type)
Definition type_handler.h:39
exprt complex_to_bool_expr(const exprt &complex_expr)
Definition type_handler.h:90