@@ -66,7 +66,7 @@ class is_compile_time_constantt
66
66
67
67
// / This function determines what expressions are to be propagated as
68
68
// / "constants"
69
- bool is_constant (const exprt &e) const
69
+ virtual bool is_constant (const exprt &e) const
70
70
{
71
71
// non-standard numeric constant
72
72
if (e.id () == ID_infinity)
@@ -138,6 +138,54 @@ class is_compile_time_constantt
138
138
}
139
139
};
140
140
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
+
141
189
void c_typecheck_baset::typecheck_expr (exprt &expr)
142
190
{
143
191
if (expr.id ()==ID_already_typechecked)
@@ -3684,39 +3732,47 @@ exprt c_typecheck_baset::do_special_functions(
3684
3732
}
3685
3733
else if (identifier==" __builtin_constant_p" )
3686
3734
{
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
+
3689
3740
if (expr.arguments ().size ()!=1 )
3690
3741
{
3691
3742
error ().source_location = f_op.source_location ();
3692
3743
error () << " __builtin_constant_p expects one argument" << eom;
3693
3744
throw 0 ;
3694
3745
}
3695
3746
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)
3715
3749
{
3716
- is_constant= true ;
3750
+ is_constant = clang_is_constant_foldedt (* this )(expr. arguments (). front ()) ;
3717
3751
}
3718
3752
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
+ }
3720
3776
3721
3777
exprt tmp2=from_integer (is_constant, expr.type ());
3722
3778
tmp2.add_source_location ()=source_location;
0 commit comments