Skip to content

Commit

Permalink
Mimic GCC/Clang simplification behaviour when type checking ?:
Browse files Browse the repository at this point in the history
Neither GCC nor Clang simplify expressions involving symbols even when
the result would always be 0, which our simplifier figure out.

Fixes: diffblue#7927
  • Loading branch information
tautschnig committed Nov 16, 2023
1 parent b40e6c7 commit 6f82cc7
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
23 changes: 23 additions & 0 deletions regression/ansi-c/sizeof6/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
int main()
{
long long i;
#ifndef _MSC_VER
_Static_assert(sizeof(int) == sizeof(*(1 ? ((void *)(0ll)) : (int *)1)), "");
// We are able to simplify all of the expressions involving i below to 0, but
// GCC and Clang don't do so. Hence, the static asserts pass for those
// compilers.
_Static_assert(
sizeof(int) != sizeof(*(1 ? ((void *)(i * 0)) : (int *)1)), "");
_Static_assert(
sizeof(int) != sizeof(*(1 ? ((void *)(i - i)) : (int *)1)), "");
_Static_assert(
sizeof(int) != sizeof(*(1 ? ((void *)(i ? 0ll : 0ll)) : (int *)1)), "");
_Static_assert(
sizeof(int) != sizeof(*(1 ? ((void *)(0 ? i : 0ll)) : (int *)1)), "");
#else
static_assert(sizeof(int) == sizeof(*(1 ? ((void *)(0)) : (int *)1)), "");
// Visual Studio rejects this as "illegal indirection"
// static_assert(
// sizeof(int) == sizeof(*(1 ? ((void *)(i * 0)) : (int *)1)), "");
#endif
}
8 changes: 8 additions & 0 deletions regression/ansi-c/sizeof6/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE
main.c

^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
^CONVERSION ERROR$
21 changes: 14 additions & 7 deletions src/ansi-c/c_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1689,24 +1689,31 @@ void c_typecheck_baset::typecheck_expr_trinary(if_exprt &expr)
operands[1].type().id()!=ID_pointer)
implicit_typecast(operands[1], operands[2].type());

auto compile_time_null_pointer = [](const exprt &e, const namespacet &ns) {
if(!is_compile_time_constantt(ns)(e))
return false;
auto s = simplify_expr(e, ns);
if(!s.is_constant())
return false;
return is_null_pointer(to_constant_expr(s));
};

if(operands[1].type().id()==ID_pointer &&
operands[2].type().id()==ID_pointer &&
operands[1].type()!=operands[2].type())
{
exprt tmp1=simplify_expr(operands[1], *this);
exprt tmp2=simplify_expr(operands[2], *this);

// is one of them void * AND null? Convert that to the other.
// (at least that's how GCC behaves)
// Is one of them void * AND null? Convert that to the other.
// (At least that's how GCC, Clang, and Visual Studio behave. Presence of
// symbols blocks them from simplifying the expression to NULL.)
if(
to_pointer_type(operands[1].type()).base_type().id() == ID_empty &&
tmp1.is_constant() && is_null_pointer(to_constant_expr(tmp1)))
compile_time_null_pointer(operands[1], *this))
{
implicit_typecast(operands[1], operands[2].type());
}
else if(
to_pointer_type(operands[2].type()).base_type().id() == ID_empty &&
tmp2.is_constant() && is_null_pointer(to_constant_expr(tmp2)))
compile_time_null_pointer(operands[2], *this))
{
implicit_typecast(operands[2], operands[1].type());
}
Expand Down

0 comments on commit 6f82cc7

Please sign in to comment.