@@ -66,14 +66,14 @@ class is_compile_time_constantt
6666
6767 // / This function determines what expressions are to be propagated as
6868 // / "constants"
69- bool is_constant (const exprt &e) const
69+ virtual bool is_constant (const exprt &e) const
7070 {
7171 // non-standard numeric constant
7272 if (e.id () == ID_infinity)
7373 return true ;
7474
7575 // numeric, character, or pointer-typed constant
76- if (e.is_constant ())
76+ if (e.is_constant () || e. id () == ID_string_constant )
7777 return true ;
7878
7979 // possibly an address constant
@@ -138,6 +138,54 @@ class is_compile_time_constantt
138138 }
139139};
140140
141+ // / Clang appears to have a somewhat different idea of what is/isn't to be
142+ // / considered a constant at compile time.
143+ class clang_is_constant_foldedt : public is_compile_time_constantt
144+ {
145+ public:
146+ explicit clang_is_constant_foldedt (const namespacet &ns)
147+ : is_compile_time_constantt(ns)
148+ {
149+ }
150+
151+ protected:
152+ // / This function determines what expressions are constant folded by clang
153+ bool is_constant (const exprt &e) const override
154+ {
155+ // we need to adhere to short-circuit semantics for the following
156+ if (e.id () == ID_if)
157+ {
158+ const if_exprt &if_expr = to_if_expr (e);
159+ if (!is_constant (if_expr.cond ()))
160+ return false ;
161+ exprt const_cond = simplify_expr (if_expr.cond (), ns);
162+ CHECK_RETURN (const_cond.is_constant ());
163+ if (const_cond.is_true ())
164+ return is_constant (if_expr.true_case ());
165+ else
166+ return is_constant (if_expr.false_case ());
167+ }
168+ else if (e.id () == ID_and || e.id () == ID_or)
169+ {
170+ for (const auto &op : e.operands ())
171+ {
172+ if (!is_constant (op))
173+ return false ;
174+ exprt const_cond = simplify_expr (op, ns);
175+ CHECK_RETURN (const_cond.is_constant ());
176+ // stop when we hit false (for an and) or true (for an or)
177+ if (const_cond == make_boolean_expr (e.id () == ID_or))
178+ break ;
179+ }
180+ return true ;
181+ }
182+ else if (e.id () == ID_address_of)
183+ return false ;
184+ else
185+ return is_compile_time_constantt::is_constant (e);
186+ }
187+ };
188+
141189void c_typecheck_baset::typecheck_expr (exprt &expr)
142190{
143191 if (expr.id ()==ID_already_typechecked)
@@ -3684,39 +3732,47 @@ exprt c_typecheck_baset::do_special_functions(
36843732 }
36853733 else if (identifier==" __builtin_constant_p" )
36863734 {
3687- // this is a gcc extension to tell whether the argument
3688- // is known to be a compile-time constant
3735+ // This is a gcc/clang extension to tell whether the argument
3736+ // is known to be a compile-time constant. The behavior of these two
3737+ // compiler families, however, is quite different, which we need to take
3738+ // care of in the below config-dependent branches.
3739+
36893740 if (expr.arguments ().size ()!=1 )
36903741 {
36913742 error ().source_location = f_op.source_location ();
36923743 error () << " __builtin_constant_p expects one argument" << eom;
36933744 throw 0 ;
36943745 }
36953746
3696- // do not typecheck the argument - it is never evaluated, and thus side
3697- // effects must not show up either
3698-
3699- // try to produce constant
3700- exprt tmp1=expr.arguments ().front ();
3701- simplify (tmp1, *this );
3702-
3703- bool is_constant=false ;
3704-
3705- // Need to do some special treatment for string literals,
3706- // which are (void *)&("lit"[0])
3707- if (
3708- tmp1.id () == ID_typecast &&
3709- to_typecast_expr (tmp1).op ().id () == ID_address_of &&
3710- to_address_of_expr (to_typecast_expr (tmp1).op ()).object ().id () ==
3711- ID_index &&
3712- to_index_expr (to_address_of_expr (to_typecast_expr (tmp1).op ()).object ())
3713- .array ()
3714- .id () == ID_string_constant)
3747+ bool is_constant = false ;
3748+ if (config.ansi_c .mode == configt::ansi_ct::flavourt::CLANG)
37153749 {
3716- is_constant= true ;
3750+ is_constant = clang_is_constant_foldedt (* this )(expr. arguments (). front ()) ;
37173751 }
37183752 else
3719- is_constant=tmp1.is_constant ();
3753+ {
3754+ // try to produce constant
3755+ exprt tmp1 = expr.arguments ().front ();
3756+ simplify (tmp1, *this );
3757+
3758+ // Need to do some special treatment for string literals,
3759+ // which are (void *)&("lit"[0])
3760+ if (
3761+ tmp1.id () == ID_typecast &&
3762+ to_typecast_expr (tmp1).op ().id () == ID_address_of &&
3763+ to_address_of_expr (to_typecast_expr (tmp1).op ()).object ().id () ==
3764+ ID_index &&
3765+ to_index_expr (to_address_of_expr (to_typecast_expr (tmp1).op ()).object ())
3766+ .array ()
3767+ .id () == ID_string_constant)
3768+ {
3769+ is_constant = true ;
3770+ }
3771+ else if (tmp1.id () == ID_string_constant)
3772+ is_constant = true ;
3773+ else
3774+ is_constant = tmp1.is_constant ();
3775+ }
37203776
37213777 exprt tmp2=from_integer (is_constant, expr.type ());
37223778 tmp2.add_source_location ()=source_location;
0 commit comments