Skip to content

Commit

Permalink
__builtin_constant_p: properly test, fix behaviour
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tautschnig committed Dec 14, 2023
1 parent 60d24b7 commit d39db8f
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 49 deletions.
28 changes: 0 additions & 28 deletions regression/ansi-c/gcc_builtin_constant_p1/main.c

This file was deleted.

47 changes: 47 additions & 0 deletions regression/cbmc/gcc_builtin_constant_p1/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <assert.h>

#ifdef __GNUC__
enum
{
E1 = 1
} var;

struct whatnot
{
} whatnot_var;
#endif

int main()
{
// this is gcc only

#ifdef __GNUC__
assert(__builtin_constant_p("some string"));
assert(__builtin_constant_p(1.0f));
assert(__builtin_constant_p(E1));
assert(!__builtin_constant_p(var));
assert(!__builtin_constant_p(main));
assert(!__builtin_constant_p(whatnot_var));
assert(!__builtin_constant_p(&var));
assert(__builtin_constant_p(__builtin_constant_p(var)));

// The following are not constant expressions in the sense of the C standard
// and GCC wouldn't deem them constant expressions either, but they are
// subject to GCC's constant folding. See also regression test ansi-c/sizeof6.
// Clang's behaviour, however, is somewhat different. See
// https://github.com/llvm/llvm-project/issues/55946 for further examples of
// where they differ.
int j;
# ifndef __clang__
assert(__builtin_constant_p(j * 0));
assert(__builtin_constant_p(j - j));
assert(__builtin_constant_p(j ? 0ll : 0ll));
# endif
assert(__builtin_constant_p(0 ? j : 0ll));

// side-effects are _not_ evaluated
int i = 0;
assert(!__builtin_constant_p(i++));
assert(i == 0);
#endif
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
CORE gcc-only broken-test-c++-front-end
CORE
main.c

^VERIFICATION SUCCESSFUL$
^EXIT=0$
^SIGNAL=0$
--
Expand Down
51 changes: 31 additions & 20 deletions src/ansi-c/c_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3710,8 +3710,11 @@ exprt c_typecheck_baset::do_special_functions(
}
else if(identifier=="__builtin_constant_p")
{
// this is a gcc extension to tell whether the argument
// is known to be a compile-time constant
// This is a gcc/clang extension to tell whether the argument
// is known to be a compile-time constant. The behavior of these two
// compiler families, however, is quite different, which we need to take
// care of in the below config-dependent branches.

if(expr.arguments().size()!=1)
{
error().source_location = f_op.source_location();
Expand All @@ -3722,27 +3725,35 @@ exprt c_typecheck_baset::do_special_functions(
// do not typecheck the argument - it is never evaluated, and thus side
// effects must not show up either

// try to produce constant
exprt tmp1=expr.arguments().front();
simplify(tmp1, *this);

bool is_constant=false;

// Need to do some special treatment for string literals,
// which are (void *)&("lit"[0])
if(
tmp1.id() == ID_typecast &&
to_typecast_expr(tmp1).op().id() == ID_address_of &&
to_address_of_expr(to_typecast_expr(tmp1).op()).object().id() ==
ID_index &&
to_index_expr(to_address_of_expr(to_typecast_expr(tmp1).op()).object())
.array()
.id() == ID_string_constant)
bool is_constant = false;
if(config.ansi_c.mode == configt::ansi_ct::flavourt::CLANG)
{
is_constant=true;
is_constant = is_compile_time_constantt(*this)(expr.arguments().front());
}
else
is_constant=tmp1.is_constant();
{
// try to produce constant
exprt tmp1 = expr.arguments().front();
simplify(tmp1, *this);

// Need to do some special treatment for string literals,
// which are (void *)&("lit"[0])
if(
tmp1.id() == ID_typecast &&
to_typecast_expr(tmp1).op().id() == ID_address_of &&
to_address_of_expr(to_typecast_expr(tmp1).op()).object().id() ==
ID_index &&
to_index_expr(to_address_of_expr(to_typecast_expr(tmp1).op()).object())
.array()
.id() == ID_string_constant)
{
is_constant = true;
}
else if(tmp1.id() == ID_string_constant)
is_constant = true;
else
is_constant = tmp1.is_constant();
}

exprt tmp2=from_integer(is_constant, expr.type());
tmp2.add_source_location()=source_location;
Expand Down

0 comments on commit d39db8f

Please sign in to comment.