blob: 9f2cb6c8256e1f52055140aef257434f4411aab5 [file] [log] [blame]
/* Test valid usage of C23 nullptr. */
/* { dg-do run } */
/* { dg-options "-std=c2x -pedantic-errors -Wall -Wextra -Wno-unused-variable" } */
#include <stdarg.h>
typedef __typeof__(nullptr) nullptr_t;
void f1 (nullptr_t) { }
void f2 (int *) { }
void f3 (_Bool) { }
nullptr_t cmp (void) { return nullptr; }
/* The type nullptr_t shall not be converted to any type other than void, bool or
a pointer type. No type other than nullptr_t shall be converted to nullptr_t. */
void
test1 (void)
{
const nullptr_t nptr = nullptr;
static nullptr_t static_nptr;
int *p1 = nullptr;
void *p2 = nullptr;
float *p3 = nullptr;
void (*p4)(int) = nullptr;
int (*p5)[10] = nullptr;
int *p6 = nptr;
void *p7 = nptr;
float *p8 = nptr;
void (*p9)(int) = nptr;
int (*p10)[10] = nptr;
int *p11 = (int *) nullptr;
int *p12 = (int *) nptr;
int *p13 = (nullptr);
int *p14 = _Generic(0, int : nullptr);
if (nullptr || p1 || p2 || p3 || p4 || p5 || p6 || p7 || p8 || p9 || p10
|| p11 || p12 || p13 || p14)
__builtin_abort ();
_Bool b1 = nullptr;
_Bool b2 = (_Bool) nullptr;
_Bool b3 = nptr;
_Bool b4 = (_Bool) nptr;
_Bool b5 = _Generic(0, int : nullptr);
if (b1 || b2 || b3 || b4 || b5 || (_Bool) nullptr || (_Bool) nptr)
__builtin_abort ();
__auto_type a1 = nullptr;
__auto_type a2 = nptr;
/* We can convert nullptr_t to nullptr_t. */
__typeof__(nullptr) x = nullptr;
f1 (x);
f1 (nullptr);
f1 (_Generic(0, int : nullptr));
f2 (x);
f2 (nullptr);
f3 (nullptr);
const nullptr_t np1 = nullptr;
const nullptr_t np2 = np1;
(void) nullptr;
(void) np1;
(void) np2;
(void) cmp ();
(void)(nullptr_t) nullptr;
}
/* Test valid comparison. */
void
test2 (int *p)
{
/* If both operands have type nullptr_t or one operand has type nullptr_t
and the other is a null pointer constant, they compare equal. */
const nullptr_t nptr = nullptr;
int r = 0;
/* Both operands have type nullptr_t. */
r |= nullptr != nullptr;
r |= cmp () != nullptr;
r |= nullptr != cmp ();
r |= !(nullptr == nullptr);
r |= !(cmp () == nullptr);
r |= !(nullptr == cmp ());
r |= nptr != nptr;
r |= cmp () != nptr;
r |= nptr != cmp ();
r |= !(nptr == nptr);
r |= !(cmp () == nptr);
r |= !(nptr == cmp ());
/* One operand has type nullptr_t and the other is a null pointer constant. */
r |= nullptr != (void *) 0;
r |= _Generic(0, int : nullptr) != (void *) 0;
r |= (nullptr) != (void *) 0;
r |= !(nullptr == (void *) 0);
r |= (void *) 0 != nullptr;
r |= (void *) 0 != (nullptr);
r |= !((void *) 0 == nullptr);
r |= nullptr != 0;
r |= _Generic(0, int : nullptr) != 0;
r |= (nullptr) != 0;
r |= 0 != nullptr;
r |= 0 != (nullptr);
r |= !(nullptr == 0);
r |= !(0 == nullptr);
r |= nullptr != 0u;
r |= 0u != nullptr;
r |= !(nullptr == 0u);
r |= !(0u == nullptr);
r |= nptr != (void *) 0;
r |= !(nptr == (void *) 0);
r |= (void *) 0 != nptr;
r |= !((void *) 0 == nptr);
r |= nptr != 0;
r |= 0 != nptr;
r |= !(nptr == 0);
r |= !(0 == nptr);
r |= nptr != 0u;
r |= 0u != nptr;
r |= !(nptr == 0u);
r |= !(0u == nptr);
r |= nptr != _Generic(0, int : nullptr);
r |= _Generic(0, int : nullptr) != nptr;
if (r)
__builtin_abort ();
/* One operand is a pointer and the other is a null pointer constant. */
(void) (p == nullptr);
(void) (p != nullptr);
(void) (nullptr == p);
(void) (nullptr != p);
(void) (p == (nullptr));
(void) (p != (nullptr));
(void) ((nullptr) == p);
(void) ((nullptr) != p);
(void) ((void *)nullptr == nullptr);
(void) ((void *)nullptr != nullptr);
(void) (nullptr == (void *)nullptr);
(void) (nullptr != (void *)nullptr);
(void) (p == _Generic(0, int : nullptr));
(void) (p != _Generic(0, int : nullptr));
(void) (_Generic(0, int : nullptr) == p);
(void) (_Generic(0, int : nullptr) != p);
}
/* Test ?:. */
void
test3 (int *p, _Bool b)
{
int x = nullptr ? 1 : 2;
(void) x;
const nullptr_t nptr = nullptr;
/* One of the following shall hold for the second and third operands:
-- both operands have nullptr_t type. */
__auto_type r1 = b ? nullptr : nullptr;
__auto_type r2 = b ? nptr : nptr;
/* -- one operand is a pointer and the other is a null pointer constant
or has type nullptr_t; */
__auto_type r3 = b ? p : nullptr;
__auto_type r4 = b ? nullptr : p;
__auto_type r5 = b ? nptr : p;
__auto_type r6 = b ? p : nptr;
__auto_type r7 = b ? 0 : p;
__auto_type r8 = b ? p : 0;
__auto_type r9 = b ? p : cmp ();
__auto_type r10 = b ? cmp () : p;
__auto_type r11 = b ? p : _Generic(0, int : nullptr);
__auto_type r12 = b ? _Generic(0, int : nullptr) : p;
}
void test_arg1 (const nullptr_t, _Atomic nullptr_t, volatile nullptr_t) { }
void test_arg2 (_Atomic int *, const int *, volatile int *) { }
void test_arg3 (_Atomic _Bool, const _Bool, volatile _Bool) { }
nullptr_t retn (void) { return nullptr; }
_Atomic int *ai (void) { return nullptr; }
const int *ci (void) { return nullptr; }
volatile int *vi (void) { return nullptr; }
_Bool retb (void) { return nullptr; }
/* Simple assignment. */
void
test4 (void)
{
/* -- the left operand has an atomic, qualified, or unqualified version of
the nullptr_t type and the type of the right is nullptr_t; */
nullptr_t n1;
const nullptr_t n2 = nullptr;
_Atomic nullptr_t n3 = nullptr;
volatile nullptr_t n4 = nullptr;
_Atomic volatile nullptr_t n5 = nullptr;
n1 = nullptr;
n3 = nullptr;
n4 = nullptr;
n5 = nullptr;
n5 = _Generic(0, int : nullptr);
/* -- the left operand is an atomic, qualified, or unqualified pointer,
and the type of the right is nullptr_t; */
int *p1 = cmp ();
_Atomic int *p2 = cmp ();
const int *volatile p3 = cmp ();
const int *const *const p4 = cmp ();
double (*const p5)(void) = n1;
p2 = _Generic(0, int : nullptr);
p3 = nullptr;
/* -- the left operand is an atomic, qualified, or unqualified bool, and
the type of the right is nullptr_t; */
_Bool b1;
b1 = cmp ();
const _Bool b2 = nullptr;
_Atomic _Bool b3;
b3 = n1;
(void) b1;
(void) b3;
(void) n3;
(void) n4;
(void) n5;
(void) p2;
(void) p3;
test_arg1 (nullptr, nullptr, nullptr);
test_arg2 (nullptr, nullptr, nullptr);
test_arg3 (nullptr, nullptr, nullptr);
}
/* var_arg etc. */
static void
test5 (int i, ...)
{
(void) i;
va_list ap;
va_start (ap, i);
if (va_arg (ap, void *))
__builtin_abort ();
}
/* Operand of alignas, sizeof or typeof operators. */
void
test6 (void)
{
_Static_assert (sizeof (nullptr) == sizeof (void *), "sizeof (nullptr)");
_Static_assert (sizeof (nullptr_t) == sizeof (void *), "sizeof (nullptr_t)");
_Static_assert (sizeof (nullptr) == sizeof (char *), "sizeof (nullptr)");
_Static_assert (sizeof (nullptr_t) == sizeof (char *), "sizeof (nullptr_t)");
_Static_assert (_Alignof (nullptr_t) == _Alignof (char *), "_Alignof (nullptr_t)");
__typeof__(nullptr) t = nullptr;
f1 (t);
_Alignas (nullptr_t) char i1 = 'q';
_Static_assert (_Generic (nullptr, nullptr_t: 1, default: 0) == 1, "_Generic");
_Static_assert (_Generic (t, nullptr_t: 1, default: 0) == 1, "_Generic");
_Static_assert (_Generic (cmp (), nullptr_t: 1, default: 0) == 1, "_Generic");
_Static_assert (_Generic (0, nullptr_t: 1, int: 2, default: 0) == 2, "_Generic");
_Static_assert (_Generic ((void *)0, nullptr_t: 1, void *: 2, default: 0) == 2, "_Generic");
_Static_assert (_Generic (nullptr, nullptr_t: 1, void *: 2, default: 0) == 1, "_Generic");
}
/* Play with !, ||, &&. */
void
test7 (void)
{
if (nullptr)
__builtin_abort ();
if (1 && nullptr)
__builtin_abort ();
if (0 || nullptr)
__builtin_abort ();
if (nullptr && 1)
__builtin_abort ();
if (nullptr || 0)
__builtin_abort ();
if (!nullptr)
{
}
else
__builtin_abort ();
while (nullptr)
__builtin_abort ();
int i = 0;
do
++i;
while (nullptr);
if (i != 1)
__builtin_abort ();
for (;nullptr;)
__builtin_abort ();
}
int
main (void)
{
int i = 42;
test1 ();
test2 (&i);
test3 (&i, 0);
test4 ();
test5 (42, nullptr);
test6 ();
test7 ();
}