diff --git a/regression/ansi-c/sizeof6/main.c b/regression/ansi-c/sizeof6/main.c new file mode 100644 index 000000000000..071b1008b0b7 --- /dev/null +++ b/regression/ansi-c/sizeof6/main.c @@ -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 +} diff --git a/regression/ansi-c/sizeof6/test.desc b/regression/ansi-c/sizeof6/test.desc new file mode 100644 index 000000000000..466da18b2b5f --- /dev/null +++ b/regression/ansi-c/sizeof6/test.desc @@ -0,0 +1,8 @@ +CORE +main.c + +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +^CONVERSION ERROR$ diff --git a/src/ansi-c/c_typecheck_expr.cpp b/src/ansi-c/c_typecheck_expr.cpp index e643719fde4d..c656aa89d95d 100644 --- a/src/ansi-c/c_typecheck_expr.cpp +++ b/src/ansi-c/c_typecheck_expr.cpp @@ -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()); }