ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
expr.h
Go to the documentation of this file.
1#pragma once
2
3#include <nlohmann/json.hpp>
7#include <util/irep/expr.h>
8#include <functional>
9#include <map>
10#include <set>
11#include <unordered_map>
12
20
21class symbol_id;
23class locationt;
24
26{
27public:
36 exprt arg,
37 const nlohmann::json &node,
38 const symbolt &func_symbol,
39 const code_typet::argumentst &params,
40 std::size_t param_idx,
41 const nlohmann::json &module);
42
44 const symbol_id &function_id,
45 const nlohmann::json &call,
46 python_converter &converter);
47
48 virtual ~function_call_expr() = default;
49
50 /*
51 * Converts the function from the AST into an exprt.
52 */
53 virtual exprt get();
54
57 std::optional<exprt> check_explicit_super() const;
58
60 {
61 return function_id_;
62 }
63
64 /*
65 * Removes temporary $ctor_self$ parameters from constructor call arguments.
66 * Used when constructors are passed as arguments (e.g., foo(Positive(2))).
67 * The correct self parameter will be added by goto_sideeffects later.
68 */
69 static exprt::operandst
71
72private:
74
77 bool eval_const_int(const exprt &e, BigInt &out) const;
78
80 bool eval_const_str(const exprt &e, std::string &out) const;
81
82 /*
83 * Build the receiver for a method called on a constructor temporary
84 * (`A().f()`), running A's __init__ when it has one.
85 */
86 exprt build_temporary_receiver(const nlohmann::json &ctor_call) const;
87
88 /*
89 * Check if the current function call is to math.comb() function
90 * Returns true if this is a call to math.comb
91 */
92 bool is_math_comb_call() const;
93
94 /*
95 * Handles math.comb() function calls with type checking.
96 * Validates that both arguments are integers (not floats).
97 * Returns TypeError exception if arguments are not integers.
98 * Otherwise delegates to the comb implementation function.
99 */
100 exprt handle_math_comb() const;
101
102 /*
103 * Validates that function call arguments match expected parameter types.
104 * Returns TypeError exception if type mismatch is detected, nil_exprt
105 * otherwise.
106 */
109 exprt unbound_method_error(const symbolt &func_symbol) const;
110
112 exprt call_argument_error(const symbolt &func_symbol) const;
113
114 exprt check_argument_types(
115 const symbolt *func_symbol,
116 const nlohmann::json &args,
117 const nlohmann::json &keywords) const;
118
121 bool argument_matches_parameter(
122 const code_typet::argumentt &param,
123 const typet &actual) const;
124
125 // Helper methods for AttributeError detection
126 std::vector<std::string>
127 find_possible_class_types(const symbolt *obj_symbol) const;
128
129 bool method_exists_in_class_hierarchy(
130 const std::string &class_name,
131 const std::string &method_name) const;
132
133 exprt generate_attribute_error(
134 const std::string &method_name,
135 const std::vector<std::string> &possible_classes,
136 const typet &expected_type = typet()) const;
137
141 bool is_nondet_call() const;
142
143 bool is_introspection_call() const;
144
147 bool is_generic_hash_call() const;
148
151 exprt handle_generic_hash();
152
156 std::string
157 unimplemented_abstract_method(const nlohmann::json &ctor_call) const;
158
159 bool is_abstract_instantiation() const;
160
162 exprt abstract_instantiation_error(const nlohmann::json &ctor_call) const;
163
164 bool is_unmodelled_base_instantiation() const;
165
168 [[noreturn]] void
169 refuse_unmodelled_base_instance(const std::string &class_name) const;
170
171 static void collect_method_abstractness(
172 const python_converter &converter,
173 const std::string &class_name,
174 std::map<std::string, bool> &methods,
175 std::set<std::string> &visited,
176 bool &is_abc);
177
178 bool is_input_call() const;
179
180 bool is_print_call() const;
181
182 /* Processes Python print() statements by evaluating all arguments.
183 * @return nil_exprt since print has no return value
184 */
185 exprt handle_print() const;
186
187 // Create an expression that represents non-deterministic string input
188 exprt handle_input() const;
189
190 // Helper method for UTF-8 logic
191 int decode_utf8_codepoint(const std::string &utf8_str) const;
192
193 /*
194 * Creates an expression for a non-deterministic function call.
195 */
196 exprt build_nondet_call() const;
197
198 /*
199 * Creates a constant expression from function argument.
200 */
201 exprt build_constant_from_arg() const;
202
205 exprt str_of_object(exprt &value, const nlohmann::json &arg) const;
206
207 /*
208 * object(): a pointer to a fresh heap object, so each call has its own
209 * identity and is never None.
210 */
211 exprt build_fresh_object() const;
212
213 std::optional<BigInt>
214 try_fold_constant_arith_json(const nlohmann::json &node) const;
215
216 /*
217 * Folds bytes.fromhex("..") over a constant hex string into a byte array.
218 */
219 exprt handle_bytes_fromhex() const;
220
221 /*
222 * Sets the function_type_ attribute based on the call information.
223 */
224 void get_function_type();
225
226 /*
227 * The AST node of the named class, from the main module or, when the main
228 * module's body does not hold it, from the module that defines it (#7546).
229 */
230 nlohmann::json find_class_node(const std::string &name) const;
231 bool resolves_to_staticmethod(
232 const nlohmann::json &class_node,
233 const std::string &method) const;
234 const symbolt *
235 find_inherited_classmethod(const std::string &func_symbol_id) const;
236 std::optional<exprt>
237 build_post_init_forward_call(const std::string &func_symbol_id);
238
239 exprt forward_constant_return(const nlohmann::json &func_node) const;
240
241 /*
242 * Retrieves the object (caller) name from the AST.
243 */
244 std::string get_object_name() const;
245
246 /*
247 * Resolves the list symbol for a list method call.
248 * Handles both a plain name (e.g. mylist.append()) and a subscript of
249 * a nested list (e.g. nested[0].append()) by looking up the inner list
250 * symbol via the element-type registry. Returns nullptr when not found.
251 * On return, `display_name` holds a human-readable identifier suitable
252 * for error messages (e.g. "mylist" or "nested[0]").
253 */
254 const symbolt *get_object_list_symbol(std::string &display_name) const;
255
268 const symbolt *resolve_list_receiver_symbol(std::string &display_name) const;
269
278 std::optional<exprt> fold_constant_list_query() const;
279 void materialize_list_symbol(const symbolt *sym) const;
280
281 /*
282 * Handles int-to-str conversions (e.g., str(65)) by generating
283 * the appropriate cast expression.
284 */
285 exprt handle_int_to_str(nlohmann::json &arg) const;
286
287 exprt handle_int_to_bytes() const;
288
289 /*
290 * Folds a zero-argument int method on a constant literal receiver
291 * (e.g. (255).bit_length(), (7).bit_count(), (5).conjugate()). A Name or
292 * BinOp receiver already routes through the int operational model; a bare
293 * literal receiver is not classified as an int instance, so fold it here.
294 */
295 exprt handle_int_literal_method() const;
296
297 /*
298 * Folds float.is_integer() on a constant literal receiver
299 * (e.g. (2.0).is_integer()). A Name receiver already routes through the
300 * float operational model; a bare literal is not classified as a float
301 * instance, so fold it here to a Python bool.
302 */
303 exprt handle_float_is_integer_literal() const;
304
309 exprt handle_float_hex_literal() const;
310
315 exprt handle_float_fromhex() const;
316
317 /*
318 * Extracts a string representation from a symbol's constant value.
319 * Handles both character arrays (e.g., ['6', '5']) and single-character
320 * constants by decoding their binary-encoded bitvector representations.
321 */
322 std::optional<std::string>
323 extract_string_from_symbol(const symbolt *sym) const;
324
325 /*
326 * Looks up a Python variable's symbol using its identifier and the
327 * current filename to construct the full scoped symbol name.
328 */
329 const symbolt *lookup_python_symbol(const std::string &var_name) const;
330
331 void refuse_isinstance_on_path_dependent_number(
332 const nlohmann::json &obj_arg,
333 const std::vector<nlohmann::json> &types) const;
334 exprt handle_isinstance() const;
335
340 exprt bind_isinstance_call(exprt &obj_expr) const;
341 std::optional<exprt>
342 list_isinstance(const exprt &obj, const std::string &type_name) const;
343
347 exprt isinstance_operand(
348 const nlohmann::json &node,
349 const std::function<exprt(const std::string &)> &build,
350 const std::function<std::string(const nlohmann::json &)> &type_name) const;
351
354 exprt isinstance_without_type_name(
355 const nlohmann::json &obj_arg,
356 const exprt &obj_expr,
357 const std::vector<nlohmann::json> &types) const;
358
361 bool
362 is_class_object_operand(const nlohmann::json &arg, const exprt &obj) const;
363
366 exprt isinstance_str_as_type(const exprt &obj_expr) const;
367
368 exprt handle_hasattr() const;
369
373 bool is_getattr_call() const;
374 exprt handle_getattr();
375
376 /*
377 * hasattr()'s first argument when it names an imported module. A module is
378 * not a first-class value here, so it has no symbol of its own; resolve the
379 * attribute against the symbols the module contributed instead. Returns
380 * nullopt when the argument does not name an imported module.
381 */
382 std::optional<exprt> module_hasattr(
383 const nlohmann::json &obj_arg,
384 const std::string &attr_name) const;
385
386 /*
387 * Handles issubclass(cls, classinfo): resolves the class hierarchy from the
388 * AST at compile time. classinfo may be a single class or a tuple of classes.
389 */
390 exprt handle_issubclass() const;
391
392 /*
393 * Handles callable(obj): statically decides whether obj is callable
394 * (functions, classes, builtin type constructors, lambdas, or instances of
395 * a class defining __call__).
396 */
397 exprt handle_callable() const;
398
399 exprt handle_type_call() const;
400 exprt tagged_scalar_class(const exprt &tagged) const;
401 bool is_str_of_type_call() const;
402 exprt handle_str_of_type_call() const;
403 std::string
404 exact_instance_class(const nlohmann::json &arg, const exprt &expr) const;
405
406 /*
407 * Whether a string symbol's stored constant is the only value it can hold
408 * at this call: it is assigned exactly once in scope. The stored value is
409 * only its last static assignment, so a rebound name must not be folded.
410 */
411 bool is_single_assignment_str_symbol(
412 const symbolt *sym,
413 const std::string &name) const;
414
415 /*
416 * Folds int() of a single-assignment string symbol (e.g. s = '65';
417 * int(s)). Returns std::nullopt when the string is not a valid literal, so
418 * the caller emits the runtime conversion that raises ValueError.
419 */
420 std::optional<exprt> handle_str_symbol_to_int(const symbolt *sym) const;
421
422 /*
423 * Folds float() of a single-assignment string symbol. Returns std::nullopt
424 * when the string does not parse as a float (see handle_str_symbol_to_int).
425 */
426 std::optional<exprt> handle_str_symbol_to_float(const symbolt *sym) const;
427
428 /*
429 * Handles float-to-str conversions (e.g., str(5.5)) by converting
430 * the float value into a string representation and generating
431 * the corresponding constant character array expression.
432 */
433 exprt handle_float_to_str(nlohmann::json &arg) const;
434
435 /*
436 * Handles complex-to-str conversions (e.g., str(complex(1,2)) → "(1+2j)").
437 * Formats constant complex values using Python's repr rules.
438 */
439 exprt handle_complex_to_str() const;
440
441 /*
442 * Handles string arguments (e.g., str("abc")) by converting them
443 * into character array expressions.
444 */
445 size_t handle_str(nlohmann::json &arg) const;
446
447 /*
448 * Handles float-to-int conversions (e.g., int(3.14)) by generating
449 * the appropriate cast expression.
450 */
451 void handle_float_to_int(nlohmann::json &arg) const;
452
453 /*
454 * Raises OverflowError (infinity) or ValueError (NaN) before int() truncates
455 * a float @p operand given without a @p base, and TypeError when a pointer
456 * @p operand is None; returns the operand to convert.
457 */
458 exprt guard_nonfinite_float_to_int(exprt operand, const exprt &base) const;
459
462 exprt guard_none_to_float(exprt operand) const;
463
466 exprt
467 build_float_conversion(const exprt &operand, const typet &float_type) const;
468
469 /*
470 * Handles int-to-float conversions (e.g., float(3)) by generating
471 * the appropriate cast expression.
472 */
473 void handle_int_to_float(nlohmann::json &arg) const;
474
475 /*
476 * Handles chr(int) conversions by creating a single-character
477 * string expression from an integer.
478 */
479 exprt handle_chr(nlohmann::json &arg) const;
480
481 /*
482 * Handles hexadecimal string arguments (e.g., hex(255) -> "0xff")
483 * by building a constant expression representing the string.
484 */
485 exprt handle_hex(nlohmann::json &arg) const;
486
487 /*
488 * Handles octal string arguments (e.g., oct(8) -> "0o10")
489 * by building a constant expression representing the resulting
490 * string. Supports both positive and negative integers,
491 * following the Python 3 built-in `oct()` function semantics.
492 */
493 exprt handle_oct(nlohmann::json &arg) const;
494
495 /*
496 * Handles binary string arguments (e.g., bin(5) -> "0b101"), following
497 * the Python 3 built-in `bin()` semantics for positive and negative ints.
498 */
499 exprt handle_bin(nlohmann::json &arg) const;
500
501 /*
502 * Handles ascii(obj): like repr(obj) but escapes non-ASCII code points as
503 * \xNN / \uNNNN / \UNNNNNNNN, following Python 3 `ascii()` semantics.
504 */
505 exprt handle_ascii() const;
506
507 /*
508 * Handles the builtin format(value[, spec]) for a constant value with a bare
509 * presentation-type spec: the integer base specs ('d'/'x'/'X'/'o'/'b') and
510 * the default/empty spec (str(value)). Width/alignment/precision specs and
511 * non-constant values are not folded — they raise a clean error.
512 */
513 exprt handle_format() const;
514
515 /*
516 * Handles ord(str) conversions by extracting the Unicode code point
517 * (as an integer) from a single-character string expression.
518 */
519 exprt handle_ord(nlohmann::json &arg) const;
520
521 /*
522 * Rewrites the argument AST node into an integer Constant holding the given
523 * code point and returns the resulting int expression. Helper for handle_ord.
524 */
528 std::optional<int> folded_char_array_codepoint(const exprt &e) const;
529
530 exprt build_ord_constant(nlohmann::json &arg, int code_point) const;
531
532 /*
533 * Handles abs() function calls by computing the absolute value of the
534 * argument. The argument can be an integer, a floating-point number, or an
535 * object implementing the __abs__() method. The function returns an
536 * expression representing the absolute value.
537 */
538 exprt handle_abs(nlohmann::json &arg) const;
539
540 /*
541 * Handles round() function calls by rounding a numeric value.
542 * round(x) returns the nearest integer (as int).
543 * round(x, n) returns x rounded to n decimal places (as float).
544 */
545 exprt handle_round(nlohmann::json &arg) const;
546
547 /*
548 * Handles complex() constructor calls.
549 * Supported overloads:
550 * - complex() -> 0.0 + 0.0j
551 * - complex(x) -> float(x) + 0.0j
552 * - complex(x, y) -> float(x) + float(y)j
553 */
554 exprt handle_complex() const;
555
556 /*
557 * Checks if the current function call is a min() or max() built-in function.
558 * Returns true if the function name matches "min" or "max", false otherwise.
559 */
560 bool is_min_max_call() const;
561
562 /*
563 * Handles min() or max() function calls by generating conditional
564 * expressions. Accepts a single iterable/tuple argument or two or more
565 * positional arguments, building a comparison chain. For min(a, b),
566 * generates: a < b ? a : b For max(a, b), generates: a > b ? a : b Performs
567 * type compatibility checking with automatic int-to-float promotion.
568 */
569 exprt
570 handle_min_max(const std::string &func_name, irep_idt comparison_op) const;
571
572 // Tuple method detection and handling (count, index)
573 bool is_tuple_method_call() const;
574 exprt handle_tuple_method() const;
575
576 // Dict method detection and handling
577 bool is_dict_method_call() const;
578 exprt handle_dict_method() const;
579
580 // True when the Name receiver positively resolves to a non-dict object type
581 // (a class instance, identified by a struct tag other than
582 // "__python_dict__"). Used to stop dict-named methods (get/pop/keys/...) from
583 // shadowing a class's own same-named method (e.g. queue.Queue.get()).
584 bool receiver_is_non_dict_object() const;
585
586 // Dict class method detection (e.g. dict.fromkeys([1, 2, 3]))
587 bool is_dict_class_method_call() const;
588
589 // Set method detection and handling
590 bool is_set_method_call() const;
591 exprt handle_set_method() const;
592
593 // List method detection and handling
594 bool receiver_is_tracked_numpy_view(const std::string &recv_type) const;
595 bool receiver_is_binop_or_list_symbol() const;
596 bool receiver_class_defines_method(const std::string &method_name) const;
597 void reject_unresolved_user_method(const std::string &method_name) const;
598 bool is_list_method_call() const;
599 exprt handle_list_method() const;
600 exprt handle_list_append() const;
601 exprt handle_list_insert() const;
602 exprt handle_list_extend() const;
603 exprt handle_list_clear() const;
604 exprt handle_list_pop() const;
605 // deque front-end methods, modelled on the list backing store
606 // (collections.deque is a list in our model): popleft() == pop(0),
607 // appendleft(x) == insert(0, x).
608 exprt handle_list_popleft() const;
609 exprt handle_list_appendleft() const;
610 exprt handle_list_copy() const;
611 exprt handle_list_remove() const;
612 exprt handle_list_sort() const;
613 exprt handle_list_reverse() const;
614 exprt handle_list_count() const;
615 exprt handle_list_index() const;
616
617 // numpy ndarray.astype(dtype): runtime element-wise dtype cast.
618 bool is_numpy_astype_call() const;
619 exprt handle_numpy_astype() const;
620
621 /*
622 * Check if the current function call is to a regular expression module
623 * function Returns true if the function is match, search, or fullmatch from
624 * the re module
625 */
626 bool is_re_module_call() const;
627
628 /*
629 * Validate arguments for regular expression module functions
630 * Checks that pattern and string arguments are string types (array or pointer
631 * to char) Returns TypeError exception if validation fails, nil_exprt if
632 * validation passes
633 */
634 exprt validate_re_module_args() const;
635 void route_re_flags_call();
636
637 bool is_any_call() const;
638 bool is_all_call() const;
639
640 // Convert an IR expression to its Python truthiness value.
641 // Handles None, bool, int, float, complex, pointer types.
642 exprt compute_element_truthiness(const exprt &element) const;
643
644 enum class ReduceOp
645 {
646 Any,
647 All
648 };
649
650 /*
651 * Reduce a list or tuple literal by combining the truthiness of its
652 * elements. Used by both any() and all().
653 */
654 exprt reduce_iterable_literal_truthiness(
655 const nlohmann::json &iterable_arg,
656 ReduceOp op) const;
657
658 /*
659 * Reduce a tuple-typed expression (struct with tag-tuple* type) by
660 * combining the truthiness of its members. Used by both any() and
661 * all() when the argument is a Name bound to a tuple value rather
662 * than a tuple literal.
663 */
664 exprt
665 reduce_tuple_expr_truthiness(const exprt &tuple_expr, ReduceOp op) const;
666
667 // Combine two boolean truthiness expressions according to the reduction
668 // operator: any() folds with logical OR, all() with logical AND.
669 static exprt combine_truthiness(exprt acc, exprt next, ReduceOp op);
670
671 // Shared implementation for handle_any/handle_all.
672 exprt handle_any_all(ReduceOp op, const char *name);
673
678 exprt handle_base_conversion(
679 nlohmann::json &arg,
680 const std::string &func_name,
681 const std::string &prefix,
682 std::ios_base &(*base_formatter)(std::ios_base &)) const;
683
688 bool is_string_arg(const nlohmann::json &arg) const;
689
693 exprt handle_divmod() const;
694
700 exprt handle_pow() const;
701
702 // Handler function type for dispatch table
703 using HandlerFunction = std::function<exprt()>;
704 using PredicateFunction = std::function<bool()>;
705
706 struct FunctionHandler
707 {
708 PredicateFunction predicate;
709 HandlerFunction handler;
710 const char *description; // For debugging/documentation
711 };
712
713 // Initialize dispatch table
714 std::vector<FunctionHandler> get_dispatch_table();
715
716 // --- get_dispatch_table() decomposition helpers ---
717 // The dispatch table pairs a predicate with a handler for each special
718 // function shape. The predicates and handlers below were lifted verbatim out
719 // of the inline lambdas in get_dispatch_table() so that the table itself
720 // reads as a flat list of {predicate, handler, name} entries. Extraction is
721 // purely mechanical: each helper is invoked from the same one-line lambda it
722 // replaced, so the predicates are still evaluated lazily and in the same
723 // order, and each handler still runs only when its predicate matched.
724 // Predicates only read state, so they are const; handlers may append to the
725 // current GOTO block or call non-const helpers, so they are non-const.
726
727 // int.to_bytes() with an int/int-typed/int-literal receiver.
728 bool is_int_to_bytes_call() const;
729 // Zero-arg int methods (bit_length/bit_count/conjugate) on a constant literal
730 // receiver that fits a signed 64-bit value.
731 bool is_int_literal_method_call() const;
732 // float.is_integer() on a constant float-literal receiver.
733 bool is_float_is_integer_literal_call() const;
734 // float.hex() on a constant float-literal receiver.
735 bool is_float_hex_literal_call() const;
736 // __iter__ on a builtin iterable (range/list/tuple/str/set/...).
737 bool is_builtin_name_call(const std::string &name) const;
738 bool is_iter_on_builtin_call() const;
739 // x.__str__() with no args on a builtin scalar (int/float/bool/str).
740 bool is_str_on_builtin_scalar_call() const;
741 // x.__str__() on a builtin scalar -> route to str(x).
742 exprt handle_str_on_builtin_scalar();
743 // math.isnan / math.isinf (spelled __ESBMC_isnan / __ESBMC_isinf).
744 exprt handle_isnan_isinf();
745 // cmath.log / cmath.log10.
746 bool is_cmath_log_call() const;
747 exprt handle_cmath_log_dispatch();
748 // cmath inverse functions (asin/atan/asinh/atanh/acos/acosh) fast path.
749 bool is_cmath_inverse_call() const;
750 exprt handle_cmath_inverse_fast_path();
751 // Real-valued math module functions (sin/cos/sqrt/exp/log/etc.).
752 bool is_math_function_dispatch_call() const;
753 exprt dispatch_math_call();
754 exprt handle_math_function_dispatch();
755 std::optional<std::string> bounded_math_import() const;
756 bool takes_bounded_math(const std::string &function) const;
757 std::optional<exprt> try_bounded_math_call(const std::string &func_name);
758
759 // General function call handler
760 exprt handle_general_function_call();
761
763 symbolt *find_receiver_symbol(symbol_id &obj_symbol_id) const;
764
766 exprt build_value_receiver(const symbolt &method) const;
767
768 // --- handle_general_function_call() decomposition helpers ---
769 // Each "try_"/optional-returning helper extracts a self-contained block of
770 // handle_general_function_call(). A returned value is the result the caller
771 // must return immediately; std::nullopt means "no match — fall through",
772 // preserving the original sequential control flow exactly.
773
774 /*
775 * sorted() fast-path: a single-arg sorted() over a concrete int/tuple list is
776 * materialized in the frontend, avoiding the runtime list sort/equality
777 * model. Honours reverse=<constant bool>; returns nullopt for any other
778 * shape.
779 */
780 std::optional<exprt> try_fold_sorted();
781 std::optional<exprt> try_materialize_numpy_tolist();
782 std::optional<exprt> try_reduce_numpy_descriptor_method();
783
784 // a.sort(): in-place ascending sort over a concrete 1-D ndarray. Unlike
785 // sum/mean/argmin/..., this mutates its receiver (np.sort(a) instead
786 // returns a new array), so it cannot be normalized into the
787 // dispatch_rewrite_methods free-function shape; it is handled here as its
788 // own method. Returns nullopt for anything but a `<array>.sort()` call, so
789 // an unrelated (e.g. list) receiver falls through to its own handler
790 // unchanged.
791 std::optional<exprt> try_numpy_inplace_sort();
792
793 // a.sort()'s own axis= keyword scan: a literal integer or throws. Split
794 // out of try_numpy_inplace_sort to keep that function's own decision
795 // count down.
796 long long extract_numpy_inplace_sort_axis() const;
797
798 // reject_numpy_view_mutating_method_call (called from
799 // try_numpy_inplace_sort) only covers a *copied* view; a transpose/
800 // reshape view is not a copy (writes to it are meaningful) but sort() has
801 // no support for writing through one yet. Split out to keep
802 // try_numpy_inplace_sort's own decision count down.
803 void reject_numpy_sort_write_through_view(
804 const nlohmann::json &receiver_node) const;
805
806 // One-line dispatch guard combining the two numpy method fast paths above
807 // so handle_general_function_call()'s own decision count does not grow
808 // with each one -- same reasoning as numpy_call_expr.cpp's
809 // get_arange_expr()/try_get_pointer_view_call_result().
810 std::optional<exprt> try_numpy_tolist_or_inplace_sort();
811
812 // axis= handling for any()/all(), covering both call shapes
813 // try_reduce_numpy_descriptor_method() resolves a receiver for. Returns
814 // nullopt (no axis given) so the caller falls through to its own
815 // flattened reduction unchanged. Split out to keep that function's own
816 // decision count down.
817 std::optional<exprt> try_reduce_any_all_along_axis(
818 const std::string &func_name,
819 const std::string &qualifier,
820 const std::pair<std::vector<std::size_t>, std::vector<exprt>> &materialized,
821 std::size_t positional_offset);
822
823 // The k-th output slot's inner run (a column when axis==0, a row when
824 // axis==1), combined via the same truthiness reduction the flattened path
825 // uses. Split out of try_reduce_any_all_along_axis for the same reason.
826 exprt reduce_any_all_axis_slice(
827 const std::vector<exprt> &elems,
828 const std::vector<std::size_t> &shape,
829 long long normalized_axis,
830 std::size_t k,
831 ReduceOp op) const;
832
833 // np.any(a_1d, axis=0)/np.all(a_1d, axis=0): the only valid axis for a
834 // 1-D array reduces the whole array to a single scalar (matching real
835 // numpy's 0-d result), not a 1-element array. Split out of
836 // try_reduce_any_all_along_axis for the same reason as
837 // reduce_any_all_axis_slice.
838 exprt
839 reduce_any_all_rank1(const std::vector<exprt> &elems, ReduceOp op) const;
840
841 // Validates the materialized receiver's rank (1-D or 2-D only) and rejects
842 // a zero-size array (no reduction identity for any/all). Split out of
843 // try_reduce_any_all_along_axis to keep that function's own decision
844 // count down.
845 static void validate_any_all_axis_shape(
846 const std::vector<std::size_t> &shape,
847 const std::string &func_name,
848 const std::string &qualifier);
849
850 // Who any()/all()'s array operand is, and how the rest of
851 // try_reduce_numpy_descriptor_method must read the call: the method
852 // form's own error-message qualifier ("numpy.ndarray.") and a
853 // positional_offset of 0 (any real argument arrives via keywords only), or
854 // the free-function form's ("numpy.") and an offset of 1 (the array is
855 // call_["args"][0], so exactly one positional argument is expected).
856 struct any_all_receiver
857 {
858 std::pair<std::vector<std::size_t>, std::vector<exprt>> materialized;
859 std::string qualifier;
860 std::size_t positional_offset;
861 };
862
863 // Resolves the receiver above, trying the method form first and the
864 // free-function form second. nullopt means neither call_["func"]["value"]
865 // nor (if present) call_["args"][0] is a materializable numpy descriptor,
866 // so the caller should decline entirely. Split out of
867 // try_reduce_numpy_descriptor_method to keep that function's own decision
868 // count down.
869 std::optional<any_all_receiver>
870 resolve_any_all_receiver(const std::string &func_name);
871
873 std::string dict_keys_list_id_for_call(const nlohmann::json &arg) const;
874
875 std::optional<exprt> fold_sorted_int_list(
876 const std::string &list_id,
877 size_t map_size,
878 bool fast_path_reverse) const;
879 std::optional<exprt> fold_sorted_constant_tuples(
880 const std::string &list_id,
881 size_t map_size,
882 bool fast_path_reverse) const;
883 std::optional<exprt> fold_sorted_symbolic_tuples(
884 const std::string &list_id,
885 size_t map_size,
886 bool fast_path_reverse);
887
888 /*
889 * Folds the builtin round() on a Name receiver when it is not user-defined or
890 * user-imported. Returns nullopt to leave round() to the typed dispatch.
891 */
892 std::optional<exprt> try_handle_round(bool is_user_imported);
893
894 /*
895 * Inlines a call to a user function whose entire body is `return <param>`
896 * when that parameter resolves to an array/array-pointer type: arrays
897 * aren't a valid by-value return type yet, so the call is replaced with
898 * the caller's own argument expression for this exact identity pattern.
899 * Returns nullopt for any other function shape or non-array parameter.
900 */
901 std::optional<exprt> try_fold_identity_array_return();
902
918 std::string
919 random_sequence_suffix(const exprt &seq, const std::string &func_name);
920
934 std::optional<exprt> fold_random_choice_over_tuple(const exprt &seq);
935
936 exprt handle_bool_call(const nlohmann::json &arg, size_t arg_size) const;
937
949 std::optional<exprt>
950 fold_sum_over_tuple(bool is_user_imported, bool is_numpy_model_call);
951
952 /*
953 * Typed-builtin dispatch for min/max/sum/sorted/reversed: appends the
954 * _float/_str/_default suffix to actual_func_name based on element type, and
955 * may early-return the inline comparison for a mixed int/float min/max list.
956 */
957 std::optional<exprt> apply_builtin_dispatch(
958 std::string &actual_func_name,
959 bool is_user_imported,
960 bool is_numpy_model_call);
961
962 /*
963 * Indirect call through a variable holding a function pointer, e.g.
964 * times3 = make_multiplier(3); times3(4). Returns nullopt when the Name does
965 * not resolve to a non-code variable symbol.
966 */
967 std::optional<exprt> try_indirect_variable_call();
968
970 std::optional<exprt> try_indirect_member_call();
975 void refuse_unheld_attribute(
976 const std::string &class_name,
977 const std::string &attr) const;
981 void refuse_untyped_attribute(
982 const std::string &class_name,
983 const std::string &attr,
984 const typet &field_type) const;
987 bool
988 mro_may_bind(const std::string &class_name, const std::string &attr) const;
991 const python_converter::attribute_functions *held_functions(
992 const std::string &class_name,
993 const std::string &attr,
994 const typet &field_type) const;
996 code_typet
997 held_signature(const python_converter::attribute_functions &held) const;
1000 exprt call_field(
1001 const python_converter::attribute_functions *held,
1002 const exprt &recv,
1003 const exprt &member) const;
1004
1005 /*
1006 * Resolves the callee when the direct symbol lookup failed: dataclass
1007 * __post_init__ forward refs, base-class method resolution for
1008 * constructors/instance methods, AttributeError generation, global-scope
1009 * forward references, and the undefined-function fallback. On success either
1010 * returns the expression to emit, or returns nullopt after binding
1011 * func_symbol so the common call-building path continues.
1012 */
1013 std::optional<exprt> resolve_missing_function_symbol(
1014 const symbolt *&func_symbol,
1015 const std::string &func_symbol_id,
1016 symbolt *obj_symbol,
1017 const symbol_id &obj_symbol_id);
1018
1019 /*
1020 * Builds the resolved code_function_callt in three phases, mirroring the
1021 * original inline sequence:
1022 * - bind_call_receiver: prepends self/cls for ctor/instance/class methods
1023 * and returns the parameter offset for subsequent positional binding;
1024 * - build_positional_arguments: converts and appends positional args
1025 * (returns early for the __ESBMC_get_object_size/strlen list-size case,
1026 * or with the TypeError a surplus positional argument raises);
1027 * - finalize_call: forwards keyword args, fills defaults, and adds the
1028 * constructor temp-self, returning the final call (or an early result).
1029 */
1030 size_t bind_call_receiver(
1031 code_function_callt &call,
1032 symbolt *obj_symbol,
1033 const symbolt *func_symbol,
1034 const locationt &location);
1035
1036 /*
1037 * Reconciles a converted call argument with its parameter's type: passes
1038 * an already-tagged argument through, boxes a concrete numeric/string
1039 * scalar into a tagged-object temporary, or throws otherwise.
1040 */
1042 exprt retype_or_typecast(exprt expr, const typet &target) const;
1043
1044 exprt coerce_tagged_argument(
1045 exprt arg,
1046 const typet &param_type,
1047 const locationt &location) const;
1048
1049 void refuse_float_to_int_argument(
1050 const exprt &arg,
1051 const typet &param_type,
1052 const symbolt &callee) const;
1053
1054 typet dict_param_value_type(const symbolt *func, size_t idx) const;
1055 bool is_size_builtin() const;
1056 bool is_list_typed(const typet &type) const;
1057 exprt build_dict_size_call(const exprt &dict);
1058 std::optional<exprt> build_positional_arguments(
1059 code_function_callt &call,
1060 size_t param_offset,
1061 const locationt &location,
1062 const symbolt *func_symbol);
1063 std::optional<exprt>
1064 raise_positional_oversupply(const symbolt &func_symbol) const;
1065 void reject_numpy_layout_mismatch(
1066 const typet &arg_type,
1067 const symbolt &callee,
1068 size_t param_idx,
1069 bool is_bytes_literal) const;
1070 void reject_list_for_numpy_parameter(
1071 const typet &arg_type,
1072 const code_typet::argumentst &params,
1073 size_t param_idx) const;
1074 exprt finalize_call(
1075 code_function_callt &call,
1076 size_t param_offset,
1077 const locationt &location,
1078 const symbolt *func_symbol,
1079 symbolt *obj_symbol);
1080
1081 const symbolt *cached_find_symbol(const std::string &id) const;
1082
1083protected:
1084 // any/all's np.<f>(a, ...) free-function form is dispatched entirely
1085 // through numpy_call_expr::get() (is_numpy_call() routes it there before
1086 // this class's own table-driven dispatch ever runs), so that subclass
1087 // needs to reach these directly rather than through is_any_call()'s
1088 // table entry.
1089 exprt handle_any();
1090 exprt handle_all();
1091
1093 const nlohmann::json &call_;
1097
1098 mutable std::unordered_map<std::string, const symbolt *> sym_cache_;
1099};
1100
1103exprt to_value_expr(const exprt &arg, const namespacet &ns);
1104
1107bool is_cpp_throw_expr(const exprt &e);
static const twodig_t base
Definition bigint.cpp:28
typet float_type()
Definition c_types.cpp:230
Definition bigint.hh:58
A `switch' instruction */ class code_switcht : public codet { public: inline code_switcht() : codet("...
Definition std_code.h:574
Definition std_types.h:297
std::vector< argumentt > argumentst
Definition std_types.h:283
Definition expr.h:34
std::vector< exprt > operandst
Definition expr.h:39
symbol_id function_id_
Definition expr.h:1092
virtual ~function_call_expr()=default
friend class function_call_expr_test_access
Definition expr.h:73
std::unordered_map< std::string, const symbolt * > sym_cache_
Definition expr.h:1098
static exprt fold_from_bytes_byteorder(exprt arg, const nlohmann::json &node, const symbolt &func_symbol, const code_typet::argumentst &params, std::size_t param_idx, const nlohmann::json &module)
Folds a CPython byteorder string bound to params[param_idx] of the int.from_bytes model,...
Definition expr.cpp:7287
static exprt::operandst strip_ctor_self_parameters(const exprt::operandst &args)
Definition expr.cpp:8095
exprt handle_any()
Definition builtins.cpp:2794
python_converter & converter_
Definition expr.h:1094
const symbol_id & get_function_id() const
Definition expr.h:59
virtual exprt get()
Definition expr.cpp:4817
function_call_expr(const symbol_id &function_id, const nlohmann::json &call, python_converter &converter)
Definition expr.cpp:151
const type_handler & type_handler_
Definition expr.h:1095
exprt handle_all()
Definition builtins.cpp:2805
FunctionType function_type_
Definition expr.h:1096
const nlohmann::json & call_
Definition expr.h:1093
std::optional< exprt > check_explicit_super() const
super(C, self) in a class that is not a C, where self can never be a C, raises TypeError (#8631).
Definition expr.cpp:4784
Definition irep_idt.h:12
Definition location.h:7
Definition module.h:60
Definition namespace.h:10
Main converter for transforming Python AST into ESBMC's intermediate representation.
Definition python_converter.h:76
Definition symbol_id.h:6
Definition symbol.h:12
Definition type_handler.h:113
Definition type.h:9
Definition toml.hpp:1531
@ function
Definition converter_compare.cpp:885
FunctionType
Definition expr.h:14
@ InstanceMethod
Definition expr.h:17
@ ClassMethod
Definition expr.h:16
@ FreeFunction
Definition expr.h:18
@ Constructor
Definition expr.h:15
bool is_cpp_throw_expr(const exprt &e)
True when the expression is a synthesized C++ throw used by the Python frontend to propagate exceptio...
Definition expr.cpp:146
exprt to_value_expr(const exprt &arg, const namespacet &ns)
Convert a code_function_callt to a side_effect_expr_function_callt so it can be used as a value expre...
Definition expr.cpp:2147
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
Definition socket_lib.c:163
struct _ESBMC_Mapping * next
Definition solidity_mapping.c:3
#define bool
Definition stdbool.h:9