Skip to content

Commit e40b6a0

Browse files
committed
Mimic GCC/Clang simplification behaviour when type checking ?:
Neither GCC nor Clang simplify expressions involving symbols even when the result would always be 0, which our simplifier figure out. Fixes: #7927
1 parent 57fd511 commit e40b6a0

File tree

3 files changed

+47
-7
lines changed

3 files changed

+47
-7
lines changed

regression/ansi-c/sizeof6/main.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
int main()
2+
{
3+
long long i;
4+
#ifndef _MSC_VER
5+
_Static_assert(sizeof(int) == sizeof(*(1 ? ((void *)(0ll)) : (int *)1)), "");
6+
// We are able to simplify all of the expressions involving i below to 0, but
7+
// GCC and Clang don't do so. Hence, the static asserts pass for those
8+
// compilers.
9+
_Static_assert(
10+
sizeof(int) != sizeof(*(1 ? ((void *)(i * 0)) : (int *)1)), "");
11+
_Static_assert(
12+
sizeof(int) != sizeof(*(1 ? ((void *)(i - i)) : (int *)1)), "");
13+
_Static_assert(
14+
sizeof(int) != sizeof(*(1 ? ((void *)(i ? 0ll : 0ll)) : (int *)1)), "");
15+
_Static_assert(
16+
sizeof(int) != sizeof(*(1 ? ((void *)(0 ? i : 0ll)) : (int *)1)), "");
17+
#else
18+
static_assert(sizeof(int) == sizeof(*(1 ? ((void *)(0)) : (int *)1)), "");
19+
// Visual Studio rejects this as "illegal indirection"
20+
// static_assert(
21+
// sizeof(int) == sizeof(*(1 ? ((void *)(i * 0)) : (int *)1)), "");
22+
#endif
23+
}

regression/ansi-c/sizeof6/test.desc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
^CONVERSION ERROR$

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,24 +1771,33 @@ void c_typecheck_baset::typecheck_expr_trinary(if_exprt &expr)
17711771
operands[1].type().id()!=ID_pointer)
17721772
implicit_typecast(operands[1], operands[2].type());
17731773

1774+
auto compile_time_null_pointer = [](const exprt &e, const namespacet &ns)
1775+
{
1776+
if(!is_compile_time_constantt(ns)(e))
1777+
return false;
1778+
auto s = simplify_expr(e, ns);
1779+
CHECK_RETURN(is_compile_time_constantt(ns)(s));
1780+
if(!s.is_constant())
1781+
return false;
1782+
return to_constant_expr(s).is_null_pointer();
1783+
};
1784+
17741785
if(operands[1].type().id()==ID_pointer &&
17751786
operands[2].type().id()==ID_pointer &&
17761787
operands[1].type()!=operands[2].type())
17771788
{
1778-
exprt tmp1=simplify_expr(operands[1], *this);
1779-
exprt tmp2=simplify_expr(operands[2], *this);
1780-
1781-
// is one of them void * AND null? Convert that to the other.
1782-
// (at least that's how GCC behaves)
1789+
// Is one of them void * AND null? Convert that to the other.
1790+
// (At least that's how GCC, Clang, and Visual Studio behave. Presence of
1791+
// symbols blocks them from simplifying the expression to NULL.)
17831792
if(
17841793
to_pointer_type(operands[1].type()).base_type().id() == ID_empty &&
1785-
tmp1.is_constant() && to_constant_expr(tmp1).is_null_pointer())
1794+
compile_time_null_pointer(operands[1], *this))
17861795
{
17871796
implicit_typecast(operands[1], operands[2].type());
17881797
}
17891798
else if(
17901799
to_pointer_type(operands[2].type()).base_type().id() == ID_empty &&
1791-
tmp2.is_constant() && to_constant_expr(tmp2).is_null_pointer())
1800+
compile_time_null_pointer(operands[2], *this))
17921801
{
17931802
implicit_typecast(operands[2], operands[1].type());
17941803
}

0 commit comments

Comments
 (0)