/* Test C2x enumerations with fixed underlying type.  Valid code.  */
/* { dg-do run } */
/* { dg-options "-std=c2x -pedantic-errors" } */

/* Check a type while defining an enum (via a diagnostic for incompatible
   pointer types if the wrong type was chosen).  */
#define TYPE_CHECK(cst, type)						\
  cst ## _type_check = sizeof (1 ? (type *) 0 : (typeof (cst) *) 0)

extern int i;

enum e1 : short { e1a = __SHRT_MAX__,
    TYPE_CHECK (e1a, short),
    e1z = (long long) 0,
    TYPE_CHECK (e1z, enum e1),
    e1b = -__SHRT_MAX__ - 1,
    e1c,
    TYPE_CHECK (e1c, enum e1) };
extern enum e1 e1v;
extern typeof (e1a) e1v;
extern typeof (e1b) e1v;
extern typeof (e1c) e1v;
extern typeof (e1z) e1v;
extern short e1v;
static_assert (e1a == __SHRT_MAX__);
static_assert (e1b == -__SHRT_MAX__ - 1);
static_assert (e1c == -__SHRT_MAX__);
static_assert (e1a > 0);
static_assert (e1b < 0);
static_assert (e1c < 0);
static_assert (e1z == 0);
extern typeof (+e1v) i;
extern typeof (+e1a) i;
extern typeof (e1a + e1b) i;
enum e1 : short;
enum e1 : volatile short;
enum e1 : _Atomic short;
enum e1 : typeof (short);

enum e2 : bool { b0, b1, b0a = 0, b1a = 1 };
extern enum e2 e2v;
extern typeof (b0) e2v;
extern typeof (b0a) e2v;
extern typeof (b1) e2v;
extern typeof (b1a) e2v;
extern bool e2v;
extern typeof (+e2v) i;
extern typeof (+b0) i;
static_assert (b0 == 0);
static_assert (b1 == 1);
static_assert (b0a == 0);
static_assert (b1a == 1);

enum e3 : volatile const _Atomic unsigned short;
enum e3 : unsigned short { e3a, e3b };
extern enum e3 e3v;
extern typeof (e3a) e3v;
extern typeof (e3b) e3v;
extern unsigned short e3v;

/* The enum type is complete from the end of the first enum type specifier
   (which is nested inside another enum type specifier in this example).  */
enum e4 : typeof ((enum e4 : long { e4a = sizeof (enum e4) })0, 0L);
extern enum e4 e4v;
extern typeof (e4a) e4v;
extern long e4v;

enum e5 : unsigned int;
extern enum e5 e5v;
extern typeof (e5v + e5v) e5v;
extern unsigned int e5v;

enum : unsigned short { e6a, e6b, TYPE_CHECK (e6a, unsigned short) } e6v;
extern typeof (e6a) e6v;
extern typeof (e6b) e6v;
extern unsigned short e6v;

struct s1;
struct s2 { int a; };
union u1;
union u2 { int a; };
enum xe1 { XE1 };
enum xe2 : long long { XE2 };
enum xe3 : unsigned long;

void
f ()
{
  /* Tags can be redeclared in an inner scope.  */
  enum s1 : char;
  enum s2 : int { S2 };
  enum u1 : long { U1 };
  enum u2 : unsigned char;
  enum xe1 : long long;
  enum xe2 : short;
  enum xe3 : char { XE3 };
  static_assert (sizeof (enum xe3) == 1);
  static_assert (sizeof (enum xe2) == sizeof (short));
  static_assert (sizeof (enum xe1) == sizeof (long long));
}

void *p;
typeof (nullptr) np;

extern void abort (void);
extern void exit (int);

int
main ()
{
  /* Conversions to enums with fixed underlying type have the same semantics as
     converting to the underlying type.  */
  volatile enum e1 e1vm;
  volatile enum e2 e2vm;
  e1vm = __LONG_LONG_MAX__; /* { dg-warning "overflow" } */
  if (e1vm != (short) __LONG_LONG_MAX__)
    abort ();
  e2vm = 10;
  if (e2vm != 1)
    abort ();
  e2vm = 0;
  if (e2vm != 0)
    abort ();
  /* Arithmetic on enums with fixed underlying type has the same semantics as
     arithmetic on the underlying type; in particular, the special semantics
     for bool apply to enums with bool as fixed underlying type.  */
  if (e2vm++ != 0)
    abort ();
  if (e2vm != 1)
    abort ();
  if (e2vm++ != 1)
    abort ();
  if (e2vm != 1)
    abort ();
  if (e2vm-- != 1)
    abort ();
  if (e2vm != 0)
    abort ();
  if (e2vm-- != 0)
    abort ();
  if (e2vm != 1)
    abort ();
  if (++e2vm != 1)
    abort ();
  if (e2vm != 1)
    abort ();
  e2vm = 0;
  if (++e2vm != 1)
    abort ();
  if (e2vm != 1)
    abort ();
  if (--e2vm != 0)
    abort ();
  if (e2vm != 0)
    abort ();
  if (--e2vm != 1)
    abort ();
  if (e2vm != 1)
    abort ();
  e2vm = p;
  e2vm = np;
  e2vm = (bool) p;
  e2vm = (bool) np;
  if (e2vm != 0)
    abort ();
  exit (0);
}