@@ -65,14 +65,14 @@ class is_compile_time_constantt
6565
6666 // / This function determines what expressions are to be propagated as
6767 // / "constants"
68- bool is_constant (const exprt &e) const
68+ virtual bool is_constant (const exprt &e) const
6969 {
7070 // non-standard numeric constant
7171 if (e.id () == ID_infinity)
7272 return true ;
7373
7474 // numeric, character, or pointer-typed constant
75- if (e.is_constant ())
75+ if (e.is_constant () || e. id () == ID_string_constant )
7676 return true ;
7777
7878 // possibly an address constant
@@ -137,6 +137,54 @@ class is_compile_time_constantt
137137 }
138138};
139139
140+ // / Clang appears to have a somewhat different idea of what is/isn't to be
141+ // / considered a constant at compile time.
142+ class clang_is_constant_foldedt : public is_compile_time_constantt
143+ {
144+ public:
145+ explicit clang_is_constant_foldedt (const namespacet &ns)
146+ : is_compile_time_constantt(ns)
147+ {
148+ }
149+
150+ protected:
151+ // / This function determines what expressions are constant folded by clang
152+ bool is_constant (const exprt &e) const override
153+ {
154+ // we need to adhere to short-circuit semantics for the following
155+ if (e.id () == ID_if)
156+ {
157+ const if_exprt &if_expr = to_if_expr (e);
158+ if (!is_constant (if_expr.cond ()))
159+ return false ;
160+ exprt const_cond = simplify_expr (if_expr.cond (), ns);
161+ CHECK_RETURN (const_cond.is_constant ());
162+ if (const_cond.is_true ())
163+ return is_constant (if_expr.true_case ());
164+ else
165+ return is_constant (if_expr.false_case ());
166+ }
167+ else if (e.id () == ID_and || e.id () == ID_or)
168+ {
169+ for (const auto &op : e.operands ())
170+ {
171+ if (!is_constant (op))
172+ return false ;
173+ exprt const_cond = simplify_expr (op, ns);
174+ CHECK_RETURN (const_cond.is_constant ());
175+ // stop when we hit false (for an and) or true (for an or)
176+ if (const_cond == make_boolean_expr (e.id () == ID_or))
177+ break ;
178+ }
179+ return true ;
180+ }
181+ else if (e.id () == ID_address_of)
182+ return false ;
183+ else
184+ return is_compile_time_constantt::is_constant (e);
185+ }
186+ };
187+
140188void c_typecheck_baset::typecheck_expr (exprt &expr)
141189{
142190 if (expr.id ()==ID_already_typechecked)
@@ -3789,39 +3837,47 @@ exprt c_typecheck_baset::do_special_functions(
37893837 }
37903838 else if (identifier==" __builtin_constant_p" )
37913839 {
3792- // this is a gcc extension to tell whether the argument
3793- // is known to be a compile-time constant
3840+ // This is a gcc/clang extension to tell whether the argument
3841+ // is known to be a compile-time constant. The behavior of these two
3842+ // compiler families, however, is quite different, which we need to take
3843+ // care of in the below config-dependent branches.
3844+
37943845 if (expr.arguments ().size ()!=1 )
37953846 {
37963847 error ().source_location = f_op.source_location ();
37973848 error () << " __builtin_constant_p expects one argument" << eom;
37983849 throw 0 ;
37993850 }
38003851
3801- // do not typecheck the argument - it is never evaluated, and thus side
3802- // effects must not show up either
3803-
3804- // try to produce constant
3805- exprt tmp1=expr.arguments ().front ();
3806- simplify (tmp1, *this );
3807-
3808- bool is_constant=false ;
3809-
3810- // Need to do some special treatment for string literals,
3811- // which are (void *)&("lit"[0])
3812- if (
3813- tmp1.id () == ID_typecast &&
3814- to_typecast_expr (tmp1).op ().id () == ID_address_of &&
3815- to_address_of_expr (to_typecast_expr (tmp1).op ()).object ().id () ==
3816- ID_index &&
3817- to_index_expr (to_address_of_expr (to_typecast_expr (tmp1).op ()).object ())
3818- .array ()
3819- .id () == ID_string_constant)
3852+ bool is_constant = false ;
3853+ if (config.ansi_c .mode == configt::ansi_ct::flavourt::CLANG)
38203854 {
3821- is_constant= true ;
3855+ is_constant = clang_is_constant_foldedt (* this )(expr. arguments (). front ()) ;
38223856 }
38233857 else
3824- is_constant=tmp1.is_constant ();
3858+ {
3859+ // try to produce constant
3860+ exprt tmp1 = expr.arguments ().front ();
3861+ simplify (tmp1, *this );
3862+
3863+ // Need to do some special treatment for string literals,
3864+ // which are (void *)&("lit"[0])
3865+ if (
3866+ tmp1.id () == ID_typecast &&
3867+ to_typecast_expr (tmp1).op ().id () == ID_address_of &&
3868+ to_address_of_expr (to_typecast_expr (tmp1).op ()).object ().id () ==
3869+ ID_index &&
3870+ to_index_expr (to_address_of_expr (to_typecast_expr (tmp1).op ()).object ())
3871+ .array ()
3872+ .id () == ID_string_constant)
3873+ {
3874+ is_constant = true ;
3875+ }
3876+ else if (tmp1.id () == ID_string_constant)
3877+ is_constant = true ;
3878+ else
3879+ is_constant = tmp1.is_constant ();
3880+ }
38253881
38263882 exprt tmp2=from_integer (is_constant, expr.type ());
38273883 tmp2.add_source_location ()=source_location;
0 commit comments