CBMC
Loading...
Searching...
No Matches
cpp_typecheck_expr.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: C++ Language Type Checking
4
5Author: Daniel Kroening, kroening@cs.cmu.edu
6
7\*******************************************************************/
8
11
12#include "cpp_typecheck.h"
13
14#ifdef DEBUG
15#include <iostream>
16#endif
17
18#include <util/arith_tools.h>
19#include <util/c_types.h>
20#include <util/config.h>
23#include <util/pointer_expr.h>
26
27#include <ansi-c/c_qualifiers.h>
28
29#include "cpp_exception_id.h"
30#include "cpp_type2name.h"
31#include "cpp_typecheck_fargs.h"
32#include "cpp_util.h"
33#include "expr2cpp.h"
34
36 const symbolt &symb,
37 const irep_idt &base_name,
38 irep_idt &identifier)
39{
40 for(const auto &b : to_struct_type(symb.type).bases())
41 {
42 const irep_idt &id = b.type().get_identifier();
43 if(lookup(id).base_name == base_name)
44 {
45 identifier = id;
46 return true;
47 }
48 }
49
50 return false;
51}
52
55{
56 if(expr.id()==ID_cpp_name)
58 else if(expr.id()=="cpp-this")
60 else if(expr.id() == ID_pointer_to_member)
61 convert_pmop(expr);
62 else if(expr.id() == ID_new_object)
63 {
64 }
65 else if(operator_is_overloaded(expr))
66 {
67 }
68 else if(expr.id()=="explicit-typecast")
70 else if(expr.id()=="explicit-constructor-call")
72 else if(expr.id()==ID_code)
73 {
74#ifdef DEBUG
75 std::cerr << "E: " << expr.pretty() << '\n';
76 std::cerr << "cpp_typecheckt::typecheck_expr_main got code\n";
77#endif
79 }
80 else if(expr.id()==ID_symbol)
81 {
82 // ignore here
83#ifdef DEBUG
84 std::cerr << "E: " << expr.pretty() << '\n';
85 std::cerr << "cpp_typecheckt::typecheck_expr_main got symbol\n";
86#endif
87 }
88 else if(expr.id()=="__is_base_of")
89 {
90 // an MS extension
91 // http://msdn.microsoft.com/en-us/library/ms177194(v=vs.80).aspx
92
93 typet base=static_cast<const typet &>(expr.find("type_arg1"));
94 typet deriv=static_cast<const typet &>(expr.find("type_arg2"));
95
96 typecheck_type(base);
98
99 if(base.id() != ID_struct_tag || deriv.id() != ID_struct_tag)
100 expr=false_exprt();
101 else
102 {
103 irep_idt base_name = follow_tag(to_struct_tag_type(base)).get(ID_name);
104 const class_typet &class_type =
106
107 if(class_type.has_base(base_name))
108 expr=true_exprt();
109 else
110 expr=false_exprt();
111 }
112 }
113 else if(expr.id()==ID_msc_uuidof)
114 {
115 // these appear to have type "struct _GUID"
116 // and they are lvalues!
117 expr.type() = struct_tag_typet("tag-_GUID");
118 expr.set(ID_C_lvalue, true);
119 }
120 else if(expr.id()==ID_noexcept)
121 {
122 // TODO
123 expr=false_exprt();
124 }
125 else if(expr.id()==ID_initializer_list)
126 {
128 }
129 else
131}
132
134{
135 PRECONDITION(expr.operands().size() == 3);
136
138
139 if(expr.op1().type().id()==ID_empty ||
140 expr.op1().type().id()==ID_empty)
141 {
142 if(expr.op1().get_bool(ID_C_lvalue))
143 {
144 exprt e1(expr.op1());
146 {
147 error().source_location=e1.find_source_location();
148 error() << "lvalue to rvalue conversion" << eom;
149 throw 0;
150 }
151 }
152
153 if(expr.op1().type().id()==ID_array)
154 {
155 exprt e1(expr.op1());
157 {
158 error().source_location=e1.find_source_location();
159 error() << "array to pointer conversion" << eom;
160 throw 0;
161 }
162 }
163
164 if(expr.op1().type().id()==ID_code)
165 {
166 exprt e1(expr.op1());
168 {
169 error().source_location=e1.find_source_location();
170 error() << "function to pointer conversion" << eom;
171 throw 0;
172 }
173 }
174
175 if(expr.op2().get_bool(ID_C_lvalue))
176 {
177 exprt e2(expr.op2());
179 {
180 error().source_location=e2.find_source_location();
181 error() << "lvalue to rvalue conversion" << eom;
182 throw 0;
183 }
184 }
185
186 if(expr.op2().type().id()==ID_array)
187 {
188 exprt e2(expr.op2());
190 {
191 error().source_location=e2.find_source_location();
192 error() << "array to pointer conversion" << eom;
193 throw 0;
194 }
195 }
196
197 if(expr.op2().type().id()==ID_code)
198 {
199 exprt e2(expr.op2());
201 {
203 error() << "function to pointer conversion" << eom;
204 throw 0;
205 }
206 }
207
208 if(expr.op1().get(ID_statement)==ID_throw &&
209 expr.op2().get(ID_statement)!=ID_throw)
210 expr.type()=expr.op2().type();
211 else if(expr.op2().get(ID_statement)==ID_throw &&
212 expr.op1().get(ID_statement)!=ID_throw)
213 expr.type()=expr.op1().type();
214 else if(expr.op1().type().id()==ID_empty &&
215 expr.op2().type().id()==ID_empty)
216 expr.type() = void_type();
217 else
218 {
220 error() << "bad types for operands" << eom;
221 throw 0;
222 }
223 return;
224 }
225
226 if(expr.op1().type() == expr.op2().type())
227 {
229 qual1.read(expr.op1().type());
230 qual2.read(expr.op2().type());
231
232 if(qual1.is_subset_of(qual2))
233 expr.type()=expr.op1().type();
234 else
235 expr.type()=expr.op2().type();
236 }
237 else
238 {
239 exprt e1=expr.op1();
240 exprt e2=expr.op2();
241
242 if(implicit_conversion_sequence(expr.op1(), expr.op2().type(), e1))
243 {
244 expr.type()=e1.type();
245 expr.op1().swap(e1);
246 }
247 else if(implicit_conversion_sequence(expr.op2(), expr.op1().type(), e2))
248 {
249 expr.type()=e2.type();
250 expr.op2().swap(e2);
251 }
252 else if(
253 expr.op1().type().id() == ID_array &&
254 expr.op2().type().id() == ID_array &&
255 to_array_type(expr.op1().type()).element_type() ==
256 to_array_type(expr.op2().type()).element_type())
257 {
258 // array-to-pointer conversion
259
261
263
266
267 expr.op1()=addr1;
268 expr.op2()=addr2;
269 expr.type()=addr1.type();
270 return;
271 }
272 else
273 {
275 error() << "types are incompatible.\n"
276 << "I got '" << type2cpp(expr.op1().type(), *this) << "' and '"
277 << type2cpp(expr.op2().type(), *this) << "'." << eom;
278 throw 0;
279 }
280 }
281
282 if(expr.op1().get_bool(ID_C_lvalue) &&
283 expr.op2().get_bool(ID_C_lvalue))
284 expr.set(ID_C_lvalue, true);
285
286 return;
287}
288
295
297{
298 // We need to overload, "sizeof-expression" can be mis-parsed
299 // as a type.
300
301 if(expr.operands().empty())
302 {
303 const typet &type=
304 static_cast<const typet &>(expr.find(ID_type_arg));
305
306 if(type.id()==ID_cpp_name)
307 {
308 // sizeof(X) may be ambiguous -- X can be either a type or
309 // an expression.
310
312
313 exprt symbol_expr=resolve(
314 to_cpp_name(static_cast<const irept &>(type)),
316 fargs);
317
318 if(symbol_expr.id()!=ID_type)
319 {
320 expr.copy_to_operands(symbol_expr);
321 expr.remove(ID_type_arg);
322 }
323 }
324 else if(type.id()==ID_array)
325 {
326 // sizeof(expr[index]) can be parsed as an array type!
327
328 if(to_array_type(type).element_type().id() == ID_cpp_name)
329 {
331
332 exprt symbol_expr = resolve(
334 static_cast<const irept &>(to_array_type(type).element_type())),
336 fargs);
337
338 if(symbol_expr.id()!=ID_type)
339 {
340 // _NOT_ a type
341 index_exprt index_expr(symbol_expr, to_array_type(type).size());
343 expr.remove(ID_type_arg);
344 }
345 }
346 }
347 }
348
350}
351
356
358 exprt &expr,
360{
361 if(expr.id()==ID_cpp_name)
363 else if(expr.id()==ID_member)
364 {
367 }
368 else if(expr.id()==ID_ptrmember)
369 {
372
373 // is operator-> overloaded?
374 if(to_unary_expr(expr).op().type().id() != ID_pointer)
375 {
376 std::string op_name="operator->";
377
378 // turn this into a function call
379 // first do function/operator
380 const cpp_namet cpp_name(op_name, expr.source_location());
381
383 cpp_name.as_expr(),
384 {to_unary_expr(expr).op()},
386 expr.source_location());
387 function_call.arguments().reserve(expr.operands().size());
388
390
392
393 to_unary_expr(expr).op().swap(function_call);
395 return;
396 }
397
399 }
400 else
401 typecheck_expr(expr);
402}
403
405{
406 // at least one argument must have class or enumerated type
407
408 for(const auto &op : expr.operands())
409 {
410 typet t = op.type();
411
412 if(is_reference(t))
413 t = to_reference_type(t).base_type();
414
415 if(
416 t.id() == ID_struct || t.id() == ID_union || t.id() == ID_c_enum ||
417 t.id() == ID_c_enum_tag || t.id() == ID_struct_tag ||
418 t.id() == ID_union_tag)
419 {
420 return true;
421 }
422 }
423
424 return false;
425}
426
428{
430 const char *op_name;
431} const operators[] =
432{
433 { ID_plus, "+" },
434 { ID_minus, "-" },
435 { ID_mult, "*" },
436 { ID_div, "/" },
437 { ID_bitnot, "~" },
438 { ID_bitand, "&" },
439 { ID_bitor, "|" },
440 { ID_bitxor, "^" },
441 { ID_not, "!" },
442 { ID_unary_minus, "-" },
443 { ID_and, "&&" },
444 { ID_or, "||" },
445 { ID_not, "!" },
446 { ID_index, "[]" },
447 { ID_equal, "==" },
448 { ID_lt, "<"},
449 { ID_le, "<="},
450 { ID_gt, ">"},
451 { ID_ge, ">="},
452 { ID_shl, "<<"},
453 { ID_shr, ">>"},
454 { ID_notequal, "!=" },
455 { ID_dereference, "*" },
456 { ID_ptrmember, "->" },
457 { irep_idt(), nullptr }
459
461{
462 // Check argument types first.
463 // At least one struct/enum operand is required.
464
465 if(!overloadable(expr))
466 return false;
467 else if(expr.id()==ID_dereference &&
469 return false;
470
471 PRECONDITION(expr.operands().size() >= 1);
472
473 if(expr.id()=="explicit-typecast")
474 {
475 // the cast operator can be overloaded
476
477 typet t=expr.type();
479 std::string op_name=std::string("operator")+"("+cpp_type2name(t)+")";
480
481 // turn this into a function call
482 const cpp_namet cpp_name(op_name, expr.source_location());
483
484 // See if the struct declares the cast operator as a member
485 bool found_in_struct=false;
486 PRECONDITION(!expr.operands().empty());
487 const typet &t0 = to_unary_expr(expr).op().type();
488
489 if(t0.id() == ID_struct_tag)
490 {
491 for(const auto &c : follow_tag(to_struct_tag_type(t0)).components())
492 {
493 if(!c.get_bool(ID_from_base) && c.get_base_name() == op_name)
494 {
495 found_in_struct=true;
496 break;
497 }
498 }
499 }
500
501 if(!found_in_struct)
502 return false;
503
504 exprt member(ID_member);
506
507 member.copy_to_operands(
509
511 std::move(member), {}, uninitialized_typet{}, expr.source_location());
512 function_call.arguments().reserve(expr.operands().size());
513
514 if(expr.operands().size()>1)
515 {
516 for(exprt::operandst::const_iterator
517 it=(expr.operands().begin()+1);
518 it!=(expr).operands().end();
519 it++)
520 function_call.arguments().push_back(*it);
521 }
522
524
525 if(expr.id()==ID_ptrmember)
526 {
527 add_implicit_dereference(function_call);
529 to_unary_expr(expr).op().swap(function_call);
530 typecheck_expr(expr);
531 return true;
532 }
533
534 expr.swap(function_call);
535 return true;
536 }
537
538 for(const operator_entryt *e=operators;
539 !e->id.empty();
540 e++)
541 {
542 if(expr.id()==e->id)
543 {
545 expr.id() != ID_dereference || !expr.get_bool(ID_C_implicit),
546 "no implicit dereference");
547
548 std::string op_name=std::string("operator")+e->op_name;
549
550 // first do function/operator
551 const cpp_namet cpp_name(op_name, expr.source_location());
552
553 // turn this into a function call
554 // There are two options to overload an operator:
555 //
556 // 1. In the scope of a as a.operator(b, ...)
557 // 2. Anywhere in scope as operator(a, b, ...)
558 //
559 // Using both is not allowed.
560 //
561 // We try and fail silently, maybe conversions will work
562 // instead.
563
564 // TODO: need to resolve an incomplete struct (template) here
565 // go into scope of first operand
566 if(to_multi_ary_expr(expr).op0().type().id() == ID_struct_tag)
567 {
569 to_multi_ary_expr(expr).op0().type().get(ID_identifier);
570
571 // get that scope
574
575 // build fargs for resolver
577 fargs.operands=expr.operands();
578 fargs.has_object=true;
579 fargs.in_use=true;
580
581 // should really be a qualified search
584
585 if(resolve_result.is_not_nil())
586 {
587 // Found! We turn op(a, b, ...) into a.op(b, ...)
588 exprt member(ID_member);
590
591 member.copy_to_operands(
593
595 std::move(member),
596 {},
598 expr.source_location());
599 function_call.arguments().reserve(expr.operands().size());
600
601 if(expr.operands().size()>1)
602 {
603 // skip first
604 for(exprt::operandst::const_iterator
605 it=expr.operands().begin()+1;
606 it!=expr.operands().end();
607 it++)
608 function_call.arguments().push_back(*it);
609 }
610
612
613 expr=function_call;
614
615 return true;
616 }
617 }
618
619 // 2nd option!
620 {
622 fargs.operands=expr.operands();
623 fargs.has_object=false;
624 fargs.in_use=true;
625
628
629 if(resolve_result.is_not_nil())
630 {
631 // found!
633 cpp_name.as_expr(),
634 {},
636 expr.source_location());
637 function_call.arguments().reserve(expr.operands().size());
638
639 // now do arguments
640 for(const auto &op : as_const(expr).operands())
641 function_call.arguments().push_back(op);
642
644
645 if(expr.id()==ID_ptrmember)
646 {
647 add_implicit_dereference(function_call);
649 to_multi_ary_expr(expr).op0() = function_call;
650 typecheck_expr(expr);
651 return true;
652 }
653
654 expr=function_call;
655
656 return true;
657 }
658 }
659 }
660 }
661
662 return false;
663}
664
666{
667 if(expr.operands().size()!=1)
668 {
670 error() << "address_of expects one operand" << eom;
671 throw 0;
672 }
673
674 exprt &op = to_address_of_expr(expr).op();
675
676 if(!op.get_bool(ID_C_lvalue) && expr.type().id()==ID_code)
677 {
679 error() << "expr not an lvalue" << eom;
680 throw 0;
681 }
682
683 if(op.type().id() == ID_code)
684 {
685 // we take the address of the method.
686 DATA_INVARIANT(op.id() == ID_member, "address-of code must be a member");
688 address_of_exprt address(symb, pointer_type(symb.type()));
689 address.set(ID_C_implicit, true);
690 op.swap(address);
691 }
692
693 if(op.id() == ID_address_of && op.get_bool(ID_C_implicit))
694 {
695 // must be the address of a function
697 to_code_type(to_pointer_type(op.type()).base_type());
698
699 code_typet::parameterst &args=code_type.parameters();
700 if(!args.empty() && args.front().get_this())
701 {
702 // it's a pointer to member function
704 op.type().add(ID_to_member) = symbol;
705
706 if(code_type.get_bool(ID_C_is_virtual))
707 {
709 error() << "pointers to virtual methods"
710 << " are currently not implemented" << eom;
711 throw 0;
712 }
713 }
714 }
715 else if(op.id() == ID_ptrmember && to_unary_expr(op).op().id() == "cpp-this")
716 {
717 expr.type() = pointer_type(op.type());
719 to_pointer_type(to_unary_expr(op).op().type()).base_type());
720 return;
721 }
722
723 // the C front end does not know about references
724 const bool is_ref=is_reference(expr.type());
726 if(is_ref)
727 expr.type() = reference_type(to_pointer_type(expr.type()).base_type());
728}
729
731{
732 expr.type() = void_type();
733
734 PRECONDITION(expr.operands().size() == 1 || expr.operands().empty());
735
736 if(expr.operands().size()==1)
737 {
738 // nothing really to do; one can throw _almost_ anything
739 const typet &exception_type = to_unary_expr(expr).op().type();
740
741 if(exception_type.id() == ID_empty)
742 {
743 error().source_location = to_unary_expr(expr).op().find_source_location();
744 error() << "cannot throw void" << eom;
745 throw 0;
746 }
747
748 // annotate the relevant exception IDs
751 }
752}
753
755{
756 // next, find out if we do an array
757
758 if(expr.type().id()==ID_array)
759 {
760 // first typecheck the element type
761 typecheck_type(to_array_type(expr.type()).element_type());
762
763 // typecheck the size
764 exprt &size=to_array_type(expr.type()).size();
765 typecheck_expr(size);
766
767 bool size_is_unsigned=(size.type().id()==ID_unsignedbv);
771
773
774 // save the size expression
775 expr.set(ID_size, to_array_type(expr.type()).size());
776
777 // new actually returns a pointer, not an array
779 pointer_type(to_array_type(expr.type()).element_type());
780 expr.type().swap(ptr_type);
781 }
782 else
783 {
784 // first typecheck type
785 typecheck_type(expr.type());
786
788
790 expr.type().swap(ptr_type);
791 }
792
793 exprt object_expr(ID_new_object, to_pointer_type(expr.type()).base_type());
794 object_expr.set(ID_C_lvalue, true);
795
797
798 // not yet typechecked-stuff
799 exprt &initializer=static_cast<exprt &>(expr.add(ID_initializer));
800
801 // arrays must not have an initializer
802 if(!initializer.operands().empty() &&
804 {
806 to_multi_ary_expr(expr).op0().find_source_location();
807 error() << "new with array type must not use initializer" << eom;
808 throw 0;
809 }
810
811 auto code = cpp_constructor(
812 expr.find_source_location(), object_expr, initializer.operands());
813
814 if(code.has_value())
815 expr.add(ID_initializer).swap(code.value());
816 else
817 expr.add(ID_initializer) = nil_exprt();
818
819 // we add the size of the object for convenience of the
820 // runtime library
821 auto size_of_opt =
822 size_of_expr(to_pointer_type(expr.type()).base_type(), *this);
823
824 if(size_of_opt.has_value())
825 {
826 auto &sizeof_expr = static_cast<exprt &>(expr.add(ID_sizeof));
827 sizeof_expr = size_of_opt.value();
829 to_pointer_type(expr.type()).base_type();
830 }
831}
832
834{
835 exprt result;
836
837 if(src.id()==ID_comma)
838 {
839 PRECONDITION(src.operands().size() == 2);
840 result = collect_comma_expression(to_binary_expr(src).op0());
841 result.copy_to_operands(to_binary_expr(src).op1());
842 }
843 else
844 result.copy_to_operands(src);
845
846 return result;
847}
848
850{
851 // these can have 0 or 1 arguments
852
853 if(expr.operands().empty())
854 {
855 // Default value, e.g., int()
856 typecheck_type(expr.type());
857 auto new_expr =
858 ::zero_initializer(expr.type(), expr.find_source_location(), *this);
859 if(!new_expr.has_value())
860 {
862 error() << "cannot zero-initialize '" << to_string(expr.type()) << "'"
863 << eom;
864 throw 0;
865 }
866
867 new_expr->add_source_location() = expr.source_location();
868 expr = *new_expr;
869 }
870 else if(expr.operands().size()==1)
871 {
872 auto &op = to_unary_expr(expr).op();
873
874 // Explicitly given value, e.g., int(1).
875 // There is an expr-vs-type ambiguity, as it is possible to write
876 // (f)(1), where 'f' is a function symbol and not a type.
877 // This also exists with a "comma expression", e.g.,
878 // (f)(1, 2, 3)
879
880 if(expr.type().id()==ID_cpp_name)
881 {
882 // try to resolve as type
884
885 exprt symbol_expr=resolve(
886 to_cpp_name(static_cast<const irept &>(expr.type())),
888 fargs,
889 false); // fail silently
890
891 if(symbol_expr.id()==ID_type)
892 expr.type()=symbol_expr.type();
893 else
894 {
895 // It's really a function call. Note that multiple arguments
896 // become a comma expression, and that these are already typechecked.
898 static_cast<const exprt &>(static_cast<const irept &>(expr.type())),
899 collect_comma_expression(op).operands(),
901 expr.source_location());
902
904
905 expr.swap(f_call);
906 return;
907 }
908 }
909 else
910 typecheck_type(expr.type());
911
912 // We allow (TYPE){ initializer_list }
913 // This is called "compound literal", and is syntactic
914 // sugar for a (possibly local) declaration.
915 if(op.id() == ID_initializer_list)
916 {
917 // just do a normal initialization
918 do_initializer(op, expr.type(), false);
919
920 // This produces a struct-expression,
921 // union-expression, array-expression,
922 // or an expression for a pointer or scalar.
923 // We produce a compound_literal expression.
925 tmp.add_to_operands(std::move(op));
926 expr=tmp;
927 expr.set(ID_C_lvalue, true); // these are l-values
928 return;
929 }
930
932
933 if(
934 const_typecast(op, expr.type(), new_expr) ||
935 static_typecast(op, expr.type(), new_expr, false) ||
936 reinterpret_typecast(op, expr.type(), new_expr, false))
937 {
938 expr=new_expr;
940 }
941 else
942 {
944 error() << "invalid explicit cast:\n"
945 << "operand type: '" << to_string(op.type()) << "'\n"
946 << "casting to: '" << to_string(expr.type()) << "'" << eom;
947 throw 0;
948 }
949 }
950 else
951 {
953 error() << "explicit typecast expects 0 or 1 operands" << eom;
954 throw 0;
955 }
956}
957
959{
960 typecheck_type(expr.type());
961
962 if(cpp_is_pod(expr.type()))
963 {
964 expr.id("explicit-typecast");
966 }
967 else
968 {
969 CHECK_RETURN(expr.type().id() == ID_struct);
970
971 struct_tag_typet tag(expr.type().get(ID_name));
973
974 exprt e=expr;
975 new_temporary(e.source_location(), tag, e.operands(), expr);
976 }
977}
978
980{
982 {
984 error() << quote_begin << "this" << quote_end << " is not allowed here"
985 << eom;
986 throw 0;
987 }
988
989 const exprt &this_expr=cpp_scopes.current_scope().this_expr;
990 const source_locationt source_location=expr.find_source_location();
991
992 PRECONDITION(this_expr.is_not_nil());
993 PRECONDITION(this_expr.type().id() == ID_pointer);
994
995 expr=this_expr;
996 expr.add_source_location()=source_location;
997}
998
1000{
1001 if(expr.operands().size()!=1)
1002 {
1004 error() << "delete expects one operand" << eom;
1005 throw 0;
1006 }
1007
1008 const irep_idt statement=expr.get(ID_statement);
1009
1010 if(statement==ID_cpp_delete)
1011 {
1012 }
1013 else if(statement==ID_cpp_delete_array)
1014 {
1015 }
1016 else
1018
1019 typet pointer_type = to_unary_expr(expr).op().type();
1020
1022 {
1024 error() << "delete takes a pointer type operand, but got '"
1025 << to_string(pointer_type) << "'" << eom;
1026 throw 0;
1027 }
1028
1029 // remove any const-ness of the argument
1030 // (which would impair the call to the destructor)
1031 to_pointer_type(pointer_type).base_type().remove(ID_C_constant);
1032
1033 // delete expressions are always void
1034 expr.type()=typet(ID_empty);
1035
1036 // we provide the right destructor, for the convenience
1037 // of later stages
1039 new_object.add_source_location()=expr.source_location();
1040 new_object.set(ID_C_lvalue, true);
1041
1043
1045
1046 if(destructor_code.has_value())
1047 {
1048 // this isn't typechecked yet
1050 expr.set(ID_destructor, destructor_code.value());
1051 }
1052 else
1053 expr.set(ID_destructor, nil_exprt());
1054}
1055
1057{
1058 // should not be called
1059 #if 0
1060 std::cout << "E: " << expr.pretty() << '\n';
1062 #endif
1063}
1064
1066 exprt &expr,
1068{
1069 if(expr.operands().size()!=1)
1070 {
1072 error() << "member operator expects one operand" << eom;
1073 throw 0;
1074 }
1075
1076 exprt &op0 = to_unary_expr(expr).op();
1078
1079 // The notation for explicit calls to destructors can be used regardless
1080 // of whether the type defines a destructor. This allows you to make such
1081 // explicit calls without knowing if a destructor is defined for the type.
1082 // An explicit call to a destructor where none is defined has no effect.
1083
1084 if(
1085 expr.find(ID_component_cpp_name).is_not_nil() &&
1087 op0.type().id() != ID_struct && op0.type().id() != ID_struct_tag)
1088 {
1090 tmp.add_source_location()=expr.source_location();
1091 expr.swap(tmp);
1092 return;
1093 }
1094
1095 // The member operator will trigger template elaboration
1097
1098 if(op0.type().id() != ID_struct_tag && op0.type().id() != ID_union_tag)
1099 {
1101 error() << "member operator requires struct/union type "
1102 << "on left hand side but got '" << to_string(op0.type()) << "'"
1103 << eom;
1104 throw 0;
1105 }
1106
1107 const struct_union_typet &type =
1108 op0.type().id() == ID_struct_tag
1109 ? static_cast<const struct_union_typet &>(
1111 : static_cast<const struct_union_typet &>(
1113
1114 if(type.is_incomplete())
1115 {
1117 error() << "member operator got incomplete type "
1118 << "on left hand side" << eom;
1119 throw 0;
1120 }
1121
1123
1124 if(expr.find(ID_component_cpp_name).is_not_nil())
1125 {
1128
1129 // go to the scope of the struct/union
1132
1133 // resolve the member name in this scope
1135 new_fargs.add_object(op0);
1136
1137 exprt symbol_expr=resolve(
1140 new_fargs);
1141
1142 if(symbol_expr.id()==ID_dereference)
1143 {
1144 CHECK_RETURN(symbol_expr.get_bool(ID_C_implicit));
1145 exprt tmp = to_dereference_expr(symbol_expr).pointer();
1146 symbol_expr.swap(tmp);
1147 }
1148
1150 symbol_expr.id() == ID_symbol || symbol_expr.id() == ID_member ||
1151 symbol_expr.is_constant(),
1152 "expression kind unexpected");
1153
1154 // If it is a symbol or a constant, just return it!
1155 // Note: the resolver returns a symbol if the member
1156 // is static or if it is a constructor.
1157
1158 if(symbol_expr.id()==ID_symbol)
1159 {
1160 if(
1161 symbol_expr.type().id() == ID_code &&
1162 to_code_type(symbol_expr.type()).return_type().id() == ID_constructor)
1163 {
1165 error() << "member '"
1166 << lookup(symbol_expr.get(ID_identifier)).base_name
1167 << "' is a constructor" << eom;
1168 throw 0;
1169 }
1170 else
1171 {
1172 // it must be a static component
1174 type.get_component(to_symbol_expr(symbol_expr).identifier());
1175
1176 if(pcomp.is_nil())
1177 {
1179 error() << "'" << symbol_expr.get(ID_identifier)
1180 << "' is not static member "
1181 << "of class '" << to_string(op0.type()) << "'" << eom;
1182 throw 0;
1183 }
1184 }
1185
1186 expr=symbol_expr;
1187 return;
1188 }
1189 else if(symbol_expr.is_constant())
1190 {
1191 expr=symbol_expr;
1192 return;
1193 }
1194
1195 const irep_idt component_name=symbol_expr.get(ID_component_name);
1196
1198 expr.set(ID_component_name, component_name);
1199 }
1200
1201 const irep_idt &component_name=expr.get(ID_component_name);
1202 INVARIANT(!component_name.empty(), "component name should not be empty");
1203
1205 component.make_nil();
1206
1208 op0.type().id() == ID_struct || op0.type().id() == ID_union ||
1209 op0.type().id() == ID_struct_tag || op0.type().id() == ID_union_tag);
1210
1211 exprt member;
1212
1213 if(get_component(expr.source_location(), op0, component_name, member))
1214 {
1215 // because of possible anonymous members
1216 expr.swap(member);
1217 }
1218 else
1219 {
1221 error() << "member '" << component_name << "' of '" << to_string(type)
1222 << "' not found" << eom;
1223 throw 0;
1224 }
1225
1227
1228 if(expr.type().id()==ID_code)
1229 {
1230 // Check if the function body has to be typechecked
1232
1234 component_symbol.value.set(ID_is_used, true);
1235 }
1236}
1237
1239 exprt &expr,
1241{
1242 PRECONDITION(expr.id() == ID_ptrmember);
1243
1244 if(expr.operands().size()!=1)
1245 {
1247 error() << "ptrmember operator expects one operand" << eom;
1248 throw 0;
1249 }
1250
1251 auto &op = to_unary_expr(expr).op();
1252
1254
1255 if(op.type().id() != ID_pointer)
1256 {
1258 error() << "ptrmember operator requires pointer type "
1259 << "on left hand side, but got '" << to_string(op.type()) << "'"
1260 << eom;
1261 throw 0;
1262 }
1263
1264 exprt tmp;
1265 op.swap(tmp);
1266
1267 op.id(ID_dereference);
1268 op.add_to_operands(std::move(tmp));
1269 op.add_source_location()=expr.source_location();
1271
1272 expr.id(ID_member);
1274}
1275
1277{
1280
1281 if(e.arguments().size() != 1)
1282 {
1284 error() << "cast expressions expect one operand" << eom;
1285 throw 0;
1286 }
1287
1288 exprt &f_op=e.function();
1289 exprt &cast_op=e.arguments().front();
1290
1292
1293 const irep_idt &id=
1294 f_op.get_sub().front().get(ID_identifier);
1295
1296 if(f_op.get_sub().size()!=2 ||
1297 f_op.get_sub()[1].id()!=ID_template_args)
1298 {
1300 error() << id << " expects template argument" << eom;
1301 throw 0;
1302 }
1303
1304 irept &template_arguments=f_op.get_sub()[1].add(ID_arguments);
1305
1306 if(template_arguments.get_sub().size()!=1)
1307 {
1309 error() << id << " expects one template argument" << eom;
1310 throw 0;
1311 }
1312
1313 irept &template_arg=template_arguments.get_sub().front();
1314
1315 if(template_arg.id() != ID_type && template_arg.id() != ID_ambiguous)
1316 {
1318 error() << id << " expects a type as template argument" << eom;
1319 throw 0;
1320 }
1321
1322 typet &type=static_cast<typet &>(
1323 template_arguments.get_sub().front().add(ID_type));
1324
1325 typecheck_type(type);
1326
1327 source_locationt source_location=expr.source_location();
1328
1330 if(id==ID_const_cast)
1331 {
1332 if(!const_typecast(cast_op, type, new_expr))
1333 {
1334 error().source_location=cast_op.find_source_location();
1335 error() << "type mismatch on const_cast:\n"
1336 << "operand type: '" << to_string(cast_op.type()) << "'\n"
1337 << "cast type: '" << to_string(type) << "'" << eom;
1338 throw 0;
1339 }
1340 }
1341 else if(id==ID_dynamic_cast)
1342 {
1343 if(!dynamic_typecast(cast_op, type, new_expr))
1344 {
1345 error().source_location=cast_op.find_source_location();
1346 error() << "type mismatch on dynamic_cast:\n"
1347 << "operand type: '" << to_string(cast_op.type()) << "'\n"
1348 << "cast type: '" << to_string(type) << "'" << eom;
1349 throw 0;
1350 }
1351 }
1352 else if(id==ID_reinterpret_cast)
1353 {
1355 {
1356 error().source_location=cast_op.find_source_location();
1357 error() << "type mismatch on reinterpret_cast:\n"
1358 << "operand type: '" << to_string(cast_op.type()) << "'\n"
1359 << "cast type: '" << to_string(type) << "'" << eom;
1360 throw 0;
1361 }
1362 }
1363 else if(id==ID_static_cast)
1364 {
1365 if(!static_typecast(cast_op, type, new_expr))
1366 {
1367 error().source_location=cast_op.find_source_location();
1368 error() << "type mismatch on static_cast:\n"
1369 << "operand type: '" << to_string(cast_op.type()) << "'\n"
1370 << "cast type: '" << to_string(type) << "'" << eom;
1371 throw 0;
1372 }
1373 }
1374 else
1376
1377 expr.swap(new_expr);
1378}
1379
1381 exprt &expr,
1383{
1384 source_locationt source_location=
1386
1387 if(expr.get_sub().size()==1 &&
1388 expr.get_sub()[0].id()==ID_name)
1389 {
1390 const irep_idt identifier=expr.get_sub()[0].get(ID_identifier);
1391
1392 if(
1394 identifier, fargs.operands, source_location))
1395 {
1396 expr = std::move(*gcc_polymorphic);
1397 return;
1398 }
1399 }
1400
1401 for(std::size_t i=0; i<expr.get_sub().size(); i++)
1402 {
1403 if(expr.get_sub()[i].id()==ID_cpp_name)
1404 {
1405 typet &type=static_cast<typet &>(expr.get_sub()[i]);
1406 typecheck_type(type);
1407
1408 std::string tmp="("+cpp_type2name(type)+")";
1409
1410 typet name(ID_name);
1411 name.set(ID_identifier, tmp);
1412 name.add_source_location()=source_location;
1413
1414 type=name;
1415 }
1416 }
1417
1418 if(expr.get_sub().size()>=1 &&
1419 expr.get_sub().front().id()==ID_name)
1420 {
1421 const irep_idt &id=expr.get_sub().front().get(ID_identifier);
1422
1423 if(id==ID_const_cast ||
1424 id==ID_dynamic_cast ||
1425 id==ID_reinterpret_cast ||
1426 id==ID_static_cast)
1427 {
1428 expr.id(ID_cast_expression);
1429 return;
1430 }
1431 }
1432
1433 exprt symbol_expr=
1434 resolve(
1435 to_cpp_name(expr),
1437 fargs);
1438
1439 // we want VAR
1440 CHECK_RETURN(symbol_expr.id() != ID_type);
1441
1442 if(symbol_expr.id()==ID_member)
1443 {
1444 if(
1445 symbol_expr.operands().empty() ||
1446 to_multi_ary_expr(symbol_expr).op0().is_nil())
1447 {
1448 if(to_code_type(symbol_expr.type()).return_type().id() != ID_constructor)
1449 {
1451 {
1452 if(symbol_expr.type().id()!=ID_code)
1453 {
1454 error().source_location=source_location;
1455 error() << "object missing" << eom;
1456 throw 0;
1457 }
1458
1459 // may still be good for address of
1460 }
1461 else
1462 {
1463 // Try again
1465 ptrmem.operands().push_back(
1467
1468 ptrmem.add(ID_component_cpp_name)=expr;
1469
1470 ptrmem.add_source_location()=source_location;
1472 symbol_expr.swap(ptrmem);
1473 }
1474 }
1475 }
1476 }
1477
1478 symbol_expr.add_source_location()=source_location;
1479 expr=symbol_expr;
1480
1481 if(expr.id()==ID_symbol)
1483
1485}
1486
1488{
1489 if(is_reference(expr.type()))
1490 {
1491 // add implicit dereference
1492 dereference_exprt tmp(expr);
1493 tmp.set(ID_C_implicit, true);
1494 tmp.add_source_location()=expr.source_location();
1495 tmp.set(ID_C_lvalue, true);
1496 expr.swap(tmp);
1497 }
1498}
1499
1502{
1503 // For virtual functions, it is important to check whether
1504 // the function name is qualified. If it is qualified, then
1505 // the call is not virtual.
1506 bool is_qualified=false;
1507
1508 if(expr.function().id()==ID_member ||
1509 expr.function().id()==ID_ptrmember)
1510 {
1512 {
1513 const cpp_namet &cpp_name=
1515 is_qualified=cpp_name.is_qualified();
1516 }
1517 }
1518 else if(expr.function().id()==ID_cpp_name)
1519 {
1520 const cpp_namet &cpp_name=to_cpp_name(expr.function());
1521 is_qualified=cpp_name.is_qualified();
1522 }
1523
1524 // Backup of the original operand
1525 exprt op0=expr.function();
1526
1527 // now do the function -- this has been postponed
1529
1530 if(expr.function().id() == ID_pod_constructor)
1531 {
1532 PRECONDITION(expr.function().type().id() == ID_code);
1533
1534 // This must be a POD.
1535 const typet &pod=to_code_type(expr.function().type()).return_type();
1537
1538 // These aren't really function calls, but either conversions or
1539 // initializations.
1540 if(expr.arguments().size() <= 1)
1541 {
1542 exprt typecast("explicit-typecast");
1543 typecast.type()=pod;
1544 typecast.add_source_location()=expr.source_location();
1545 if(!expr.arguments().empty())
1546 typecast.copy_to_operands(expr.arguments().front());
1548 expr.swap(typecast);
1549 }
1550 else
1551 {
1553 error() << "zero or one argument expected" << eom;
1554 throw 0;
1555 }
1556
1557 return;
1558 }
1559 else if(expr.function().id() == ID_cast_expression)
1560 {
1561 // These are not really function calls,
1562 // but usually just type adjustments.
1563 typecheck_cast_expr(expr);
1565 return;
1566 }
1567 else if(expr.function().id() == ID_cpp_dummy_destructor)
1568 {
1569 // these don't do anything, e.g., (char*)->~char()
1571 expr.swap(no_op);
1572 return;
1573 }
1574
1575 // look at type of function
1576
1577 if(expr.function().type().id()==ID_pointer)
1578 {
1579 if(expr.function().type().find(ID_to_member).is_not_nil())
1580 {
1581 const exprt &bound =
1582 static_cast<const exprt &>(expr.function().type().find(ID_C_bound));
1583
1584 if(bound.is_nil())
1585 {
1587 error() << "pointer-to-member not bound" << eom;
1588 throw 0;
1589 }
1590
1591 // add `this'
1592 DATA_INVARIANT(bound.type().id() == ID_pointer, "should be pointer");
1593 expr.arguments().insert(expr.arguments().begin(), bound);
1594
1595 // we don't need the object any more
1596 expr.function().type().remove(ID_C_bound);
1597 }
1598
1599 // do implicit dereference
1600 if(expr.function().id() == ID_address_of)
1601 {
1602 exprt tmp;
1603 tmp.swap(to_address_of_expr(expr.function()).object());
1604 expr.function().swap(tmp);
1605 }
1606 else
1607 {
1608 PRECONDITION(expr.function().type().id() == ID_pointer);
1610 tmp.add_source_location() = expr.function().source_location();
1611 expr.function().swap(tmp);
1612 }
1613
1614 if(expr.function().type().id()!=ID_code)
1615 {
1617 error() << "expecting code as argument" << eom;
1618 throw 0;
1619 }
1620 }
1621 else if(expr.function().type().id()==ID_code)
1622 {
1623 if(expr.function().type().get_bool(ID_C_is_virtual) && !is_qualified)
1624 {
1626 if(op0.id()==ID_member || op0.id()==ID_ptrmember)
1627 {
1628 vtptr_member.id(op0.id());
1629 vtptr_member.add_to_operands(std::move(to_unary_expr(op0).op()));
1630 }
1631 else
1632 {
1634 exprt this_expr("cpp-this");
1635 vtptr_member.add_to_operands(std::move(this_expr));
1636 }
1637
1638 // get the virtual table
1640 to_code_type(expr.function().type()).parameters().front().type());
1642 this_type.base_type().get_string(ID_identifier) + "::@vtable_pointer";
1643
1644 const struct_typet &vt_struct =
1646
1648 vt_struct.get_component(vtable_name);
1649
1650 CHECK_RETURN(vt_compo.is_not_nil());
1651
1653
1654 // look for the right entry
1656 to_pointer_type(vt_compo.type()).base_type().get_string(ID_identifier) +
1657 "::" + expr.function().type().get_string(ID_C_virtual_name);
1658
1660 vtentry_member.copy_to_operands(vtptr_member);
1663
1664 CHECK_RETURN(vtentry_member.type().id() == ID_pointer);
1665
1666 {
1668 tmp.add_source_location() = expr.function().source_location();
1669 vtentry_member.swap(tmp);
1670 }
1671
1672 // Typecheck the expression as if it was not virtual
1673 // (add the this pointer)
1674
1675 expr.type()=
1676 to_code_type(expr.function().type()).return_type();
1677
1679
1680 // Let's make the call virtual
1682
1685 return;
1686 }
1687 }
1688 else if(expr.function().type().id() == ID_struct_tag)
1689 {
1690 const cpp_namet cppname("operator()", expr.source_location());
1691
1692 exprt member(ID_member);
1694
1695 member.add_to_operands(std::move(op0));
1696
1697 expr.function().swap(member);
1699
1700 return;
1701 }
1702 else
1703 {
1705 error() << "function call expects function or function "
1706 << "pointer as argument, but got '"
1707 << to_string(expr.function().type()) << "'" << eom;
1708 throw 0;
1709 }
1710
1711 expr.type()=
1712 to_code_type(expr.function().type()).return_type();
1713
1714 if(expr.type().id()==ID_constructor)
1715 {
1716 PRECONDITION(expr.function().id() == ID_symbol);
1717
1718 const code_typet::parameterst &parameters=
1719 to_code_type(expr.function().type()).parameters();
1720
1721 DATA_INVARIANT(!parameters.empty(), "parameters expected");
1722
1723 const auto &this_type = to_pointer_type(parameters[0].type());
1724
1725 // change type from 'constructor' to object type
1726 expr.type() = this_type.base_type();
1727
1728 // create temporary object
1730 ID_temporary_object, this_type.base_type(), expr.source_location());
1731 tmp_object_expr.set(ID_C_lvalue, true);
1733
1734 exprt member;
1735
1737 new_object.set(ID_C_lvalue, true);
1738
1740
1742 new_object,
1743 expr.function().get(ID_identifier),
1744 member);
1745
1746 // special case for the initialization of parents
1747 if(member.get_bool(ID_C_not_accessible))
1748 {
1749 PRECONDITION(!member.get(ID_C_access).empty());
1752 }
1753
1754 // the constructor is being used, so make sure the destructor
1755 // will be available
1756 {
1757 // find name of destructor
1758 const struct_typet::componentst &components =
1759 follow_tag(to_struct_tag_type(tmp_object_expr.type())).components();
1760
1761 for(const auto &c : components)
1762 {
1763 const typet &type = c.type();
1764
1765 if(
1766 !c.get_bool(ID_from_base) && type.id() == ID_code &&
1767 to_code_type(type).return_type().id() == ID_destructor)
1768 {
1770 break;
1771 }
1772 }
1773 }
1774
1775 expr.function().swap(member);
1776
1779
1780 const code_expressiont new_code(expr);
1781 tmp_object_expr.add(ID_initializer)=new_code;
1782 expr.swap(tmp_object_expr);
1783 return;
1784 }
1785
1786 PRECONDITION(expr.operands().size() == 2);
1787
1788 if(expr.function().id()==ID_member)
1790 else
1791 {
1792 // for the object of a method call,
1793 // we are willing to add an "address_of"
1794 // for the sake of operator overloading
1795
1796 const code_typet::parameterst &parameters =
1797 to_code_type(expr.function().type()).parameters();
1798
1799 if(
1800 !parameters.empty() && parameters.front().get_this() &&
1801 !expr.arguments().empty())
1802 {
1803 const code_typet::parametert &parameter = parameters.front();
1804
1805 exprt &operand = expr.arguments().front();
1806 INVARIANT(
1807 parameter.type().id() == ID_pointer,
1808 "`this' parameter should be a pointer");
1809
1810 if(
1811 operand.type().id() != ID_pointer &&
1812 operand.type() == to_pointer_type(parameter.type()).base_type())
1813 {
1815 tmp.add_source_location()=operand.source_location();
1816 operand=tmp;
1817 }
1818 }
1819 }
1820
1821 CHECK_RETURN(expr.operands().size() == 2);
1822
1824
1825 CHECK_RETURN(expr.operands().size() == 2);
1826
1828
1829 // we will deal with some 'special' functions here
1831 if(tmp.is_not_nil())
1832 expr.swap(tmp);
1833}
1834
1838{
1839 exprt &f_op=expr.function();
1840 const code_typet &code_type=to_code_type(f_op.type());
1841 const code_typet::parameterst &parameters=code_type.parameters();
1842
1843 // do default arguments
1844
1845 if(parameters.size()>expr.arguments().size())
1846 {
1847 std::size_t i=expr.arguments().size();
1848
1849 for(; i<parameters.size(); i++)
1850 {
1851 if(!parameters[i].has_default_value())
1852 break;
1853
1854 const exprt &value=parameters[i].default_value();
1855 expr.arguments().push_back(value);
1856 }
1857 }
1858
1859 exprt::operandst::iterator arg_it=expr.arguments().begin();
1860 for(const auto &parameter : parameters)
1861 {
1862 if(parameter.get_bool(ID_C_call_by_value))
1863 {
1864 DATA_INVARIANT(is_reference(parameter.type()), "reference expected");
1865
1866 if(arg_it->id()!=ID_temporary_object)
1867 {
1868 // create a temporary for the parameter
1869
1872 arg_it->source_location(),
1873 to_reference_type(parameter.type()).base_type(),
1875 temporary);
1876 arg_it->swap(temporary);
1877 }
1878 }
1879
1880 ++arg_it;
1881 }
1882
1884}
1885
1887 side_effect_exprt &expr)
1888{
1889 const irep_idt &statement=expr.get(ID_statement);
1890
1891 if(statement==ID_cpp_new ||
1892 statement==ID_cpp_new_array)
1893 {
1894 typecheck_expr_new(expr);
1895 }
1896 else if(statement==ID_cpp_delete ||
1897 statement==ID_cpp_delete_array)
1898 {
1900 }
1901 else if(statement==ID_preincrement ||
1902 statement==ID_predecrement ||
1903 statement==ID_postincrement ||
1904 statement==ID_postdecrement)
1905 {
1907 }
1908 else if(statement==ID_throw)
1909 {
1911 }
1912 else if(statement==ID_temporary_object)
1913 {
1914 // TODO
1915 }
1916 else
1918}
1919
1922{
1923 PRECONDITION(expr.operands().size() == 2);
1924
1925 PRECONDITION(expr.function().id() == ID_member);
1926 PRECONDITION(expr.function().operands().size() == 1);
1927
1928 // turn e.f(...) into xx::f(e, ...)
1929
1931 member_expr.swap(expr.function());
1932
1933 symbolt &method_symbol =
1935 const symbolt &tag_symbol = lookup(method_symbol.type.get(ID_C_member_name));
1936
1937 // build the right template map
1938 // if this is an instantiated template class method
1939 if(tag_symbol.type.find(ID_C_template)!=irept())
1940 {
1942 const irept &template_type = tag_symbol.type.find(ID_C_template);
1945 static_cast<const template_typet &>(template_type),
1946 static_cast<const cpp_template_args_tct &>(template_args));
1947 add_method_body(&method_symbol);
1948#ifdef DEBUG
1949 std::cout << "MAP for " << method_symbol << ":\n";
1950 template_map.print(std::cout);
1951#endif
1952 }
1953 else
1954 add_method_body(&method_symbol);
1955
1956 // build new function expression
1957 exprt new_function(cpp_symbol_expr(method_symbol));
1958 new_function.add_source_location()=member_expr.source_location();
1959 expr.function().swap(new_function);
1960
1961 if(!expr.function().type().get_bool(ID_C_is_static))
1962 {
1963 const code_typet &func_type = to_code_type(method_symbol.type);
1964 typet this_type=func_type.parameters().front().type();
1965
1966 // Special case. Make it a reference.
1967 DATA_INVARIANT(this_type.id() == ID_pointer, "this should be pointer");
1968 this_type.set(ID_C_reference, true);
1969 this_type.set(ID_C_this, true);
1970
1971 if(expr.arguments().size()==func_type.parameters().size())
1972 {
1973 // this might be set up for base-class initialisation
1974 if(
1975 expr.arguments().front().type() !=
1976 func_type.parameters().front().type())
1977 {
1978 implicit_typecast(expr.arguments().front(), this_type);
1980 is_reference(expr.arguments().front().type()),
1981 "argument should be reference");
1982 expr.arguments().front().type().remove(ID_C_reference);
1983 }
1984 }
1985 else
1986 {
1990 is_reference(this_arg.type()), "argument should be reference");
1991 this_arg.type().remove(ID_C_reference);
1992 expr.arguments().insert(expr.arguments().begin(), this_arg);
1993 }
1994 }
1995
1996 if(
1997 method_symbol.value.id() == ID_cpp_not_typechecked &&
1998 !method_symbol.value.get_bool(ID_is_used))
1999 {
2000 method_symbol.value.set(ID_is_used, true);
2001 }
2002}
2003
2005{
2006 if(expr.operands().size()!=2)
2007 {
2009 error() << "assignment side effect expected to have two operands"
2010 << eom;
2011 throw 0;
2012 }
2013
2014 typet type0 = to_binary_expr(expr).op0().type();
2015
2016 if(is_reference(type0))
2017 type0 = to_reference_type(type0).base_type();
2018
2019 if(cpp_is_pod(type0))
2020 {
2021 // for structs we use the 'implicit assignment operator',
2022 // and therefore, it is allowed to assign to a rvalue struct.
2023 if(type0.id() == ID_struct_tag)
2024 to_binary_expr(expr).op0().set(ID_C_lvalue, true);
2025
2027
2028 // Note that in C++ (as opposed to C), the assignment yields
2029 // an lvalue!
2030 expr.set(ID_C_lvalue, true);
2031 return;
2032 }
2033
2034 // It's a non-POD.
2035 // Turn into an operator call
2036
2037 std::string strop="operator";
2038
2039 const irep_idt statement=expr.get(ID_statement);
2040
2041 if(statement==ID_assign)
2042 strop += "=";
2043 else if(statement==ID_assign_shl)
2044 strop += "<<=";
2045 else if(statement==ID_assign_shr)
2046 strop += ">>=";
2047 else if(statement==ID_assign_plus)
2048 strop += "+=";
2049 else if(statement==ID_assign_minus)
2050 strop += "-=";
2051 else if(statement==ID_assign_mult)
2052 strop += "*=";
2053 else if(statement==ID_assign_div)
2054 strop += "/=";
2055 else if(statement==ID_assign_bitand)
2056 strop += "&=";
2057 else if(statement==ID_assign_bitor)
2058 strop += "|=";
2059 else if(statement==ID_assign_bitxor)
2060 strop += "^=";
2061 else
2062 {
2064 error() << "bad assignment operator '" << statement << "'" << eom;
2065 throw 0;
2066 }
2067
2068 const cpp_namet cpp_name(strop, expr.source_location());
2069
2070 // expr.op0() is already typechecked
2071 exprt member(ID_member);
2074
2076 std::move(member),
2077 {to_binary_expr(expr).op1()},
2079 expr.source_location());
2080
2082
2083 expr=new_expr;
2084}
2085
2087 side_effect_exprt &expr)
2088{
2089 if(expr.operands().size()!=1)
2090 {
2092 error() << "statement " << expr.get_statement()
2093 << " expected to have one operand" << eom;
2094 throw 0;
2095 }
2096
2097 auto &op = to_unary_expr(expr).op();
2098
2100
2101 const typet &tmp_type = op.type();
2102
2103 if(is_number(tmp_type) ||
2104 tmp_type.id()==ID_pointer)
2105 {
2106 // standard stuff
2108 return;
2109 }
2110
2111 // Turn into an operator call
2112
2113 std::string str_op="operator";
2114 bool post=false;
2115
2117 str_op += "++";
2118 else if(expr.get(ID_statement)==ID_predecrement)
2119 str_op += "--";
2120 else if(expr.get(ID_statement)==ID_postincrement)
2121 {
2122 str_op += "++";
2123 post=true;
2124 }
2125 else if(expr.get(ID_statement)==ID_postdecrement)
2126 {
2127 str_op += "--";
2128 post=true;
2129 }
2130 else
2131 {
2133 error() << "bad assignment operator '" << expr.get_statement() << "'"
2134 << eom;
2135 throw 0;
2136 }
2137
2139
2140 exprt member(ID_member);
2143
2145 std::move(member), {}, uninitialized_typet{}, expr.source_location());
2146
2147 // the odd C++ way to denote the post-inc/dec operator
2148 if(post)
2149 new_expr.arguments().push_back(
2151
2153 expr.swap(new_expr);
2154}
2155
2157{
2158 if(expr.operands().size()!=1)
2159 {
2161 error() << "unary operator * expects one operand" << eom;
2162 throw 0;
2163 }
2164
2165 exprt &op = to_dereference_expr(expr).pointer();
2166 const typet &op_type = op.type();
2167
2168 if(op_type.id() == ID_pointer && op_type.find(ID_to_member).is_not_nil())
2169 {
2171 error() << "pointer-to-member must use "
2172 << "the .* or ->* operators" << eom;
2173 throw 0;
2174 }
2175
2177}
2178
2180{
2182 PRECONDITION(expr.operands().size() == 2);
2183
2184 auto &op0 = to_binary_expr(expr).op0();
2185 auto &op1 = to_binary_expr(expr).op1();
2186
2187 if(op1.type().id() != ID_pointer || op1.type().find(ID_to_member).is_nil())
2188 {
2190 error() << "pointer-to-member expected" << eom;
2191 throw 0;
2192 }
2193
2194 typet t0 = op0.type().id() == ID_pointer
2195 ? to_pointer_type(op0.type()).base_type()
2196 : op0.type();
2197
2198 typet t1((const typet &)op1.type().find(ID_to_member));
2199
2200 if(t0.id() != ID_struct_tag)
2201 {
2203 error() << "pointer-to-member type error" << eom;
2204 throw 0;
2205 }
2206
2209
2211 {
2213 error() << "pointer-to-member type error" << eom;
2214 throw 0;
2215 }
2216
2218
2219 if(op0.type().id() != ID_pointer)
2220 {
2221 if(op0.id() == ID_dereference)
2222 {
2223 op0 = to_dereference_expr(op0).pointer();
2224 }
2225 else
2226 {
2228 op0.get_bool(ID_C_lvalue),
2229 "pointer-to-member must have lvalue operand");
2230 op0 = address_of_exprt(op0);
2231 }
2232 }
2233
2234 exprt tmp(op1);
2235 tmp.type().set(ID_C_bound, op0);
2236 expr.swap(tmp);
2237 return;
2238}
2239
2241{
2242 if(expr.id()==ID_symbol)
2243 {
2244 // Check if the function body has to be typechecked
2247
2248 if(function_symbol.value.id() == ID_cpp_not_typechecked)
2249 function_symbol.value.set(ID_is_used, true);
2250 }
2251
2253}
2254
2256{
2258
2259 // We take care of an ambiguity in the C++ grammar.
2260 // Needs to be done before the operands!
2262
2263 // cpp_name uses get_sub, which can get confused with expressions.
2264 if(expr.id()==ID_cpp_name)
2266 else
2267 {
2268 // This does the operands, and then calls typecheck_expr_main.
2270 }
2271
2273 expr.type().set(ID_C_constant, false);
2274}
2275
2277{
2278 // There is an ambiguity in the C++ grammar as follows:
2279 // (TYPENAME) + expr (typecast of unary plus) vs.
2280 // (expr) + expr (sum of two expressions)
2281 // Same issue with the operators & and - and *
2282
2283 // We figure this out by resolving the type argument
2284 // and re-writing if needed
2285
2286 if(expr.id()!="explicit-typecast")
2287 return;
2288
2289 PRECONDITION(expr.operands().size() == 1);
2290
2291 irep_idt op0_id = to_unary_expr(expr).op().id();
2292
2293 if(
2294 expr.type().id() == ID_cpp_name &&
2295 to_unary_expr(expr).op().operands().size() == 1 &&
2298 {
2300 resolve(
2301 to_cpp_name(expr.type()),
2304
2305 if(resolve_result.id()!=ID_type)
2306 {
2307 // need to re-write the expression
2308 // e.g., (ID) +expr -> ID+expr
2310
2311 new_binary_expr.operands().resize(2);
2312 to_binary_expr(new_binary_expr).op0().swap(expr.type());
2314 .op1()
2315 .swap(to_unary_expr(to_unary_expr(expr).op()).op());
2316
2319 else if(op0_id==ID_unary_minus)
2321 else if(op0_id==ID_address_of)
2323 else if(op0_id==ID_dereference)
2325
2326 new_binary_expr.add_source_location() =
2327 to_unary_expr(expr).op().source_location();
2328 expr.swap(new_binary_expr);
2329 }
2330 }
2331}
2332
2334{
2335 if(expr.operands().size()!=2)
2336 {
2338 error() << "operator '" << expr.id() << "' expects two operands" << eom;
2339 throw 0;
2340 }
2341
2344
2346}
2347
2352
2354{
2355 if(expr.operands().size()!=2)
2356 {
2358 error() << "comma operator expects two operands" << eom;
2359 throw 0;
2360 }
2361
2362 const auto &op0_type = to_binary_expr(expr).op0().type();
2363
2364 if(op0_type.id() == ID_struct || op0_type.id() == ID_struct_tag)
2365 {
2366 // TODO: check if the comma operator has been overloaded!
2367 }
2368
2370}
2371
configt config
Definition config.cpp:25
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
const T & as_const(T &value)
Return a reference to the same object but ensures the type is const.
Definition as_const.h:14
reference_typet reference_type(const typet &subtype)
Definition c_types.cpp:240
empty_typet void_type()
Definition c_types.cpp:245
signedbv_typet signed_int_type()
Definition c_types.cpp:22
pointer_typet pointer_type(const typet &subtype)
Definition c_types.cpp:235
bitvector_typet c_index_type()
Definition c_types.cpp:16
const union_tag_typet & to_union_tag_type(const typet &type)
Cast a typet to a union_tag_typet.
Definition c_types.h:224
Operator to return the address of an object.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:566
static void make_already_typechecked(exprt &expr)
A base class for relations, i.e., binary predicates whose two operands have the same type.
Definition std_expr.h:784
Base class of fixed-width bit-vector types.
The Boolean type.
Definition std_types.h:35
virtual void typecheck_expr_main(exprt &expr)
symbol_table_baset & symbol_table
virtual void typecheck_expr_address_of(exprt &expr)
virtual void typecheck_expr(exprt &expr)
virtual void do_initializer(exprt &initializer, const typet &type, bool force_constant)
virtual void typecheck_expr_binary_arithmetic(exprt &expr)
virtual void typecheck_expr_sizeof(exprt &expr)
virtual void typecheck_expr_side_effect(side_effect_exprt &expr)
virtual void typecheck_expr_index(exprt &expr)
virtual void typecheck_expr_function_identifier(exprt &expr)
virtual void typecheck_expr_comma(exprt &expr)
virtual exprt do_special_functions(side_effect_expr_function_callt &expr)
virtual void typecheck_side_effect_assignment(side_effect_exprt &expr)
virtual void typecheck_expr_operands(exprt &expr)
virtual std::optional< symbol_exprt > typecheck_gcc_polymorphic_builtin(const irep_idt &identifier, const exprt::operandst &arguments, const source_locationt &source_location)
virtual void typecheck_function_call_arguments(side_effect_expr_function_callt &expr)
Typecheck the parameters in a function call expression, and where necessary, make implicit casts arou...
virtual void typecheck_expr_dereference(exprt &expr)
virtual void typecheck_expr_rel(binary_relation_exprt &expr)
Class type.
Definition std_types.h:324
codet representation of an expression statement.
Definition std_code.h:1394
Base type of functions.
Definition std_types.h:582
std::vector< parametert > parameterst
Definition std_types.h:585
struct configt::ansi_ct ansi_c
exprt this_expr
Definition cpp_id.h:76
irep_idt class_identifier
Definition cpp_id.h:75
bool is_destructor() const
Definition cpp_name.h:119
const source_locationt & source_location() const
Definition cpp_name.h:73
cpp_scopet & set_scope(const irep_idt &identifier)
Definition cpp_scopes.h:87
cpp_scopet & current_scope()
Definition cpp_scopes.h:32
bool find_parent(const symbolt &symb, const irep_idt &base_name, irep_idt &identifier)
void typecheck_expr_typecast(exprt &) override
void typecheck_side_effect_assignment(side_effect_exprt &) override
bool implicit_conversion_sequence(const exprt &expr, const typet &type, exprt &new_expr, unsigned &rank)
implicit conversion sequence
void typecheck_type(typet &) override
void explicit_typecast_ambiguity(exprt &)
template_mapt template_map
bool reinterpret_typecast(const exprt &expr, const typet &type, exprt &new_expr, bool check_constantness=true)
void convert_pmop(exprt &expr)
void typecheck_expr_sizeof(exprt &) override
void typecheck_code(codet &) override
void typecheck_expr_explicit_typecast(exprt &)
void implicit_typecast(exprt &expr, const typet &type) override
void typecheck_cast_expr(exprt &)
void typecheck_expr_dereference(exprt &) override
void typecheck_side_effect_function_call(side_effect_expr_function_callt &) override
bool cpp_is_pod(const typet &type) const
bool get_component(const source_locationt &source_location, const exprt &object, const irep_idt &component_name, exprt &member)
bool const_typecast(const exprt &expr, const typet &type, exprt &new_expr)
void new_temporary(const source_locationt &source_location, const typet &, const exprt::operandst &ops, exprt &temporary)
std::optional< codet > cpp_constructor(const source_locationt &source_location, const exprt &object, const exprt::operandst &operands)
void typecheck_expr_trinary(if_exprt &) override
void typecheck_expr_rel(binary_relation_exprt &) override
bool standard_conversion_function_to_pointer(const exprt &expr, exprt &new_expr) const
Function-to-pointer conversion.
bool standard_conversion_array_to_pointer(const exprt &expr, exprt &new_expr) const
Array-to-pointer conversion.
bool dynamic_typecast(const exprt &expr, const typet &type, exprt &new_expr)
void add_method_body(symbolt *_method_symbol)
void typecheck_expr_binary_arithmetic(exprt &) override
void typecheck_expr_delete(exprt &)
void typecheck_expr_main(exprt &) override
Called after the operands are done.
void elaborate_class_template(const typet &type)
elaborate class template instances
bool operator_is_overloaded(exprt &)
bool standard_conversion_lvalue_to_rvalue(const exprt &expr, exprt &new_expr) const
Lvalue-to-rvalue conversion.
void typecheck_expr_new(exprt &)
void add_implicit_dereference(exprt &)
void typecheck_expr_cpp_name(exprt &, const cpp_typecheck_fargst &)
void typecheck_expr(exprt &) override
void typecheck_expr_side_effect(side_effect_exprt &) override
void typecheck_expr_comma(exprt &) override
bool static_typecast(const exprt &expr, const typet &type, exprt &new_expr, bool check_constantness=true)
bool subtype_typecast(const struct_typet &from, const struct_typet &to) const
std::optional< codet > cpp_destructor(const source_locationt &source_location, const exprt &object)
void typecheck_expr_explicit_constructor_call(exprt &)
void typecheck_function_expr(exprt &, const cpp_typecheck_fargst &)
void typecheck_method_application(side_effect_expr_function_callt &)
std::string to_string(const typet &) override
void typecheck_expr_this(exprt &)
void typecheck_expr_throw(exprt &)
bool overloadable(const exprt &)
void typecheck_expr_index(exprt &) override
void typecheck_side_effect_inc_dec(side_effect_exprt &)
void zero_initializer(const exprt &object, const typet &type, const source_locationt &source_location, exprt::operandst &ops)
void typecheck_function_call_arguments(side_effect_expr_function_callt &) override
void typecheck_expr_ptrmember(exprt &) override
exprt resolve(const cpp_namet &cpp_name, const cpp_typecheck_resolvet::wantt want, const cpp_typecheck_fargst &fargs, bool fail_with_exception=true)
cpp_scopest cpp_scopes
void typecheck_expr_address_of(exprt &) override
void typecheck_expr_function_identifier(exprt &) override
void typecheck_expr_member(exprt &) override
Operator to dereference a pointer.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
bool empty() const
Definition dstring.h:101
Base class for all expressions.
Definition expr.h:57
const source_locationt & find_source_location() const
Get a source_locationt from the expression or from its operands (non-recursively).
Definition expr.cpp:68
void copy_to_operands(const exprt &expr)
Copy the given argument to the end of exprt's operands.
Definition expr.h:164
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:213
typet & type()
Return the type of the expression.
Definition expr.h:85
operandst & operands()
Definition expr.h:95
const source_locationt & source_location() const
Definition expr.h:236
source_locationt & add_source_location()
Definition expr.h:241
void add_to_operands(const exprt &expr)
Add the given argument to the end of exprt's operands.
Definition expr.h:171
The Boolean constant false.
Definition std_expr.h:3135
The trinary if-then-else operator.
Definition std_expr.h:2426
Array index operator.
Definition std_expr.h:1431
There are a large number of kinds of tree structured or tree-like data in CPROVER.
Definition irep.h:364
bool get_bool(const irep_idt &name) const
Definition irep.cpp:57
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition irep.cpp:482
const irept & find(const irep_idt &name) const
Definition irep.cpp:93
const irep_idt & get(const irep_idt &name) const
Definition irep.cpp:44
void remove(const irep_idt &name)
Definition irep.cpp:87
void set(const irep_idt &name, const irep_idt &value)
Definition irep.h:412
bool is_not_nil() const
Definition irep.h:372
subt & get_sub()
Definition irep.h:448
void swap(irept &irep)
Definition irep.h:434
const irep_idt & id() const
Definition irep.h:388
irept & add(const irep_idt &name)
Definition irep.cpp:103
bool is_nil() const
Definition irep.h:368
const std::string & get_string(const irep_idt &name) const
Definition irep.h:401
source_locationt source_location
Definition message.h:239
static const commandt quote_begin
Start quoted text.
Definition message.h:389
mstreamt & error() const
Definition message.h:401
static const commandt quote_end
End quoted text.
Definition message.h:393
static eomt eom
Definition message.h:289
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition namespace.cpp:49
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
The NIL expression.
Definition std_expr.h:3144
The pointer type These are both 'bitvector_typet' (they have a width) and 'type_with_subtypet' (they ...
A side_effect_exprt representation of a function call side effect.
Definition std_code.h:1692
exprt::operandst & arguments()
Definition std_code.h:1718
An expression containing a side effect.
Definition std_code.h:1450
const irep_idt & get_statement() const
Definition std_code.h:1472
A struct tag type, i.e., struct_typet with an identifier.
Definition std_types.h:492
Structure type, corresponds to C style structs.
Definition std_types.h:230
Base type for structs and unions.
Definition std_types.h:61
const componentt & get_component(const irep_idt &component_name) const
Get the reference to a component with given name.
Definition std_types.cpp:64
bool is_incomplete() const
A struct/union may be incomplete.
Definition std_types.h:184
std::vector< componentt > componentst
Definition std_types.h:139
symbolt & get_writeable_ref(const irep_idt &name)
Find a symbol in the symbol table for read-write access.
Symbol table entry.
Definition symbol.h:28
typet type
Type of symbol.
Definition symbol.h:31
exprt value
Initial value of symbol.
Definition symbol.h:34
void build(const template_typet &template_type, const cpp_template_args_tct &template_args)
void print(std::ostream &out) const
exprt & op0()
Definition expr.h:134
exprt & op1()
Definition expr.h:137
exprt & op2()
Definition expr.h:140
The Boolean constant true.
Definition std_expr.h:3126
Semantic type conversion.
Definition std_expr.h:1995
The type of an expression, extends irept.
Definition type.h:29
source_locationt & add_source_location()
Definition type.h:77
irept cpp_exception_list(const typet &src, const namespacet &ns)
turns a type into a list of relevant exception IDs
C++ Language Type Checking.
cpp_namet & to_cpp_name(irept &cpp_name)
Definition cpp_name.h:148
std::string cpp_type2name(const typet &type)
C++ Language Module.
C++ Language Type Checking.
static exprt collect_comma_expression(const exprt &src)
struct operator_entryt operators[]
C++ Language Type Checking.
symbol_exprt cpp_symbol_expr(const symbolt &symbol)
Definition cpp_util.cpp:14
std::string type2cpp(const typet &type, const namespacet &ns)
Definition expr2cpp.cpp:493
Expression Initialization.
bool is_number(const typet &type)
Returns true if the type is a rational, real, integer, natural, complex, unsignedbv,...
Mathematical types.
API to expression classes for Pointers.
bool is_reference(const typet &type)
Returns true if the type is a reference.
const reference_typet & to_reference_type(const typet &type)
Cast a typet to a reference_typet.
const address_of_exprt & to_address_of_expr(const exprt &expr)
Cast an exprt to an address_of_exprt.
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
const dereference_exprt & to_dereference_expr(const exprt &expr)
Cast an exprt to a dereference_exprt.
std::optional< exprt > size_of_expr(const typet &type, const namespacet &ns)
Pointer Logic.
BigInt mp_integer
Definition smt_terms.h:17
#define CHECK_RETURN(CONDITION)
Definition invariant.h:495
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
side_effect_expr_function_callt & to_side_effect_expr_function_call(exprt &expr)
Definition std_code.h:1739
auto component(T &struct_expr, const irep_idt &name, const namespacet &ns) -> decltype(struct_expr.op0())
Definition std_expr.cpp:291
const binary_exprt & to_binary_expr(const exprt &expr)
Cast an exprt to a binary_exprt.
Definition std_expr.h:721
const unary_exprt & to_unary_expr(const exprt &expr)
Cast an exprt to a unary_exprt.
Definition std_expr.h:424
const multi_ary_exprt & to_multi_ary_expr(const exprt &expr)
Cast an exprt to a multi_ary_exprt.
Definition std_expr.h:991
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition std_expr.h:2953
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:221
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition std_types.h:787
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition std_types.h:307
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition std_types.h:517
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition std_types.h:887
const class_typet & to_class_type(const typet &type)
Cast a typet to a class_typet.
Definition std_types.h:380
Author: Diffblue Ltd.
dstringt irep_idt