Skip to content

Commit d39db8f

Browse files
committed
__builtin_constant_p: properly test, fix behaviour
We previously only compile-time tested what required a run-time check of assertions, so move this to the "cbmc" regression test suite. Also, extend it by behaviour that distinguishes it from actual C-standard specified constant expressions. Finally, fix the behaviour for string literals.
1 parent 60d24b7 commit d39db8f

File tree

4 files changed

+80
-49
lines changed

4 files changed

+80
-49
lines changed

regression/ansi-c/gcc_builtin_constant_p1/main.c

-28
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include <assert.h>
2+
3+
#ifdef __GNUC__
4+
enum
5+
{
6+
E1 = 1
7+
} var;
8+
9+
struct whatnot
10+
{
11+
} whatnot_var;
12+
#endif
13+
14+
int main()
15+
{
16+
// this is gcc only
17+
18+
#ifdef __GNUC__
19+
assert(__builtin_constant_p("some string"));
20+
assert(__builtin_constant_p(1.0f));
21+
assert(__builtin_constant_p(E1));
22+
assert(!__builtin_constant_p(var));
23+
assert(!__builtin_constant_p(main));
24+
assert(!__builtin_constant_p(whatnot_var));
25+
assert(!__builtin_constant_p(&var));
26+
assert(__builtin_constant_p(__builtin_constant_p(var)));
27+
28+
// The following are not constant expressions in the sense of the C standard
29+
// and GCC wouldn't deem them constant expressions either, but they are
30+
// subject to GCC's constant folding. See also regression test ansi-c/sizeof6.
31+
// Clang's behaviour, however, is somewhat different. See
32+
// https://github.com/llvm/llvm-project/issues/55946 for further examples of
33+
// where they differ.
34+
int j;
35+
# ifndef __clang__
36+
assert(__builtin_constant_p(j * 0));
37+
assert(__builtin_constant_p(j - j));
38+
assert(__builtin_constant_p(j ? 0ll : 0ll));
39+
# endif
40+
assert(__builtin_constant_p(0 ? j : 0ll));
41+
42+
// side-effects are _not_ evaluated
43+
int i = 0;
44+
assert(!__builtin_constant_p(i++));
45+
assert(i == 0);
46+
#endif
47+
}

regression/ansi-c/gcc_builtin_constant_p1/test.desc regression/cbmc/gcc_builtin_constant_p1/test.desc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
CORE gcc-only broken-test-c++-front-end
1+
CORE
22
main.c
33

4+
^VERIFICATION SUCCESSFUL$
45
^EXIT=0$
56
^SIGNAL=0$
67
--

src/ansi-c/c_typecheck_expr.cpp

+31-20
Original file line numberDiff line numberDiff line change
@@ -3710,8 +3710,11 @@ exprt c_typecheck_baset::do_special_functions(
37103710
}
37113711
else if(identifier=="__builtin_constant_p")
37123712
{
3713-
// this is a gcc extension to tell whether the argument
3714-
// is known to be a compile-time constant
3713+
// This is a gcc/clang extension to tell whether the argument
3714+
// is known to be a compile-time constant. The behavior of these two
3715+
// compiler families, however, is quite different, which we need to take
3716+
// care of in the below config-dependent branches.
3717+
37153718
if(expr.arguments().size()!=1)
37163719
{
37173720
error().source_location = f_op.source_location();
@@ -3722,27 +3725,35 @@ exprt c_typecheck_baset::do_special_functions(
37223725
// do not typecheck the argument - it is never evaluated, and thus side
37233726
// effects must not show up either
37243727

3725-
// try to produce constant
3726-
exprt tmp1=expr.arguments().front();
3727-
simplify(tmp1, *this);
3728-
3729-
bool is_constant=false;
3730-
3731-
// Need to do some special treatment for string literals,
3732-
// which are (void *)&("lit"[0])
3733-
if(
3734-
tmp1.id() == ID_typecast &&
3735-
to_typecast_expr(tmp1).op().id() == ID_address_of &&
3736-
to_address_of_expr(to_typecast_expr(tmp1).op()).object().id() ==
3737-
ID_index &&
3738-
to_index_expr(to_address_of_expr(to_typecast_expr(tmp1).op()).object())
3739-
.array()
3740-
.id() == ID_string_constant)
3728+
bool is_constant = false;
3729+
if(config.ansi_c.mode == configt::ansi_ct::flavourt::CLANG)
37413730
{
3742-
is_constant=true;
3731+
is_constant = is_compile_time_constantt(*this)(expr.arguments().front());
37433732
}
37443733
else
3745-
is_constant=tmp1.is_constant();
3734+
{
3735+
// try to produce constant
3736+
exprt tmp1 = expr.arguments().front();
3737+
simplify(tmp1, *this);
3738+
3739+
// Need to do some special treatment for string literals,
3740+
// which are (void *)&("lit"[0])
3741+
if(
3742+
tmp1.id() == ID_typecast &&
3743+
to_typecast_expr(tmp1).op().id() == ID_address_of &&
3744+
to_address_of_expr(to_typecast_expr(tmp1).op()).object().id() ==
3745+
ID_index &&
3746+
to_index_expr(to_address_of_expr(to_typecast_expr(tmp1).op()).object())
3747+
.array()
3748+
.id() == ID_string_constant)
3749+
{
3750+
is_constant = true;
3751+
}
3752+
else if(tmp1.id() == ID_string_constant)
3753+
is_constant = true;
3754+
else
3755+
is_constant = tmp1.is_constant();
3756+
}
37463757

37473758
exprt tmp2=from_integer(is_constant, expr.type());
37483759
tmp2.add_source_location()=source_location;

0 commit comments

Comments
 (0)