@@ -65,14 +65,14 @@ class is_compile_time_constantt
65
65
66
66
// / This function determines what expressions are to be propagated as
67
67
// / "constants"
68
- bool is_constant (const exprt &e) const
68
+ virtual bool is_constant (const exprt &e) const
69
69
{
70
70
// non-standard numeric constant
71
71
if (e.id () == ID_infinity)
72
72
return true ;
73
73
74
74
// numeric, character, or pointer-typed constant
75
- if (e.is_constant ())
75
+ if (e.is_constant () || e. id () == ID_string_constant )
76
76
return true ;
77
77
78
78
// possibly an address constant
@@ -137,6 +137,54 @@ class is_compile_time_constantt
137
137
}
138
138
};
139
139
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
+
140
188
void c_typecheck_baset::typecheck_expr (exprt &expr)
141
189
{
142
190
if (expr.id ()==ID_already_typechecked)
@@ -3789,39 +3837,47 @@ exprt c_typecheck_baset::do_special_functions(
3789
3837
}
3790
3838
else if (identifier==" __builtin_constant_p" )
3791
3839
{
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
+
3794
3845
if (expr.arguments ().size ()!=1 )
3795
3846
{
3796
3847
error ().source_location = f_op.source_location ();
3797
3848
error () << " __builtin_constant_p expects one argument" << eom;
3798
3849
throw 0 ;
3799
3850
}
3800
3851
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)
3820
3854
{
3821
- is_constant= true ;
3855
+ is_constant = clang_is_constant_foldedt (* this )(expr. arguments (). front ()) ;
3822
3856
}
3823
3857
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
+ }
3825
3881
3826
3882
exprt tmp2=from_integer (is_constant, expr.type ());
3827
3883
tmp2.add_source_location ()=source_location;
0 commit comments