// { dg-do compile }
// { dg-options "-Wpedantic -Wno-error=pedantic" }

#include "flexary.h"

struct Sx {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

// Verify that non-data members or static data members either before
// or after a zero-length array in an otherwise empty struct don't
// suppress the diagnostic.
struct Sx2 {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
  typedef int I;
};

struct Sx3 {
  typedef int I;
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx4 {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
  enum E { e };
};

struct Sx5 {
  enum E { e };
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx6 {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
  static int i;
};

struct Sx7 {
  static int i;
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx8 {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
  Sx8 () { }
};

struct Sx9 {
  Sx9 () { }
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx10 {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
  virtual ~Sx10 () { }
};

struct Sx11 {
  virtual ~Sx11 () { }
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx12 {
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
  virtual void foo () = 0;
};

struct Sx13 {
  virtual void foo () = 0;
  int a[0];                 // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx14 {
  int a[0][1];             // { dg-warning "zero-size|in an otherwise empty" }
};

struct Sx15 {
  typedef int A[0];         // { dg-warning "zero-size" }
  A a;                      // { dg-warning "in an otherwise empty" }
};

// Verify also that a zero-size array doesn't suppress the diagnostic.
struct Sx16 {
  int a_0 [0];              // { dg-warning "zero-size|in an otherwise empty" }
  int a_x [0];              // { dg-warning "zero-size array" }
};

struct Sx17 {
  int a_x [0];              // { dg-warning "zero-size|in an otherwise empty" }
  int a_0 [0];              // { dg-warning "zero-size array" }
};

// Empty structs are a GCC extension that (in C++ only) is treated
// as if it had a single member of type char.  Therefore, a struct
// containing a zero-length array followed by an empty struct
// is diagnosed to prevent the former subobject from sharing space
// with the latter.
struct Sx18 {
  int a_x [0];              // { dg-warning "zero-size array" }
  struct S { };
};

// Anonymous structs and unions are another GCC extension.  Since
// they cannot be named and thus used to store the size of a zero
// length array member, a struct containing both is diagnosed as
// if the zero-length array appeared alone.
struct Sx19 {
  struct S { };
  union U { };
  int a_x [0];              // { dg-warning "zero-size|in an otherwise empty" }
};

// Unlike in the case above, a named member of an anonymous struct
// prevents a subsequent zero-length array from being diagnosed.
struct Sx20 {
  struct S { } s;
  int a_x [0];              // { dg-warning "zero-size array" }
};

struct Sx21 {
  int a_x [0];              // { dg-warning "zero-size array|not at end" }
  struct S { } s;
};

struct Sx22 {
  int a_x [0];              // { dg-warning "zero-size array|not at end" }
  union { int i; };
};

struct Sx23 {
  union { int i; };
  int a_x [0];              // { dg-warning "zero-size array" }
};

// The following causes an incomplete type error error and a zero-size
// array warning.
struct Sx24 {
  struct S;
  S a_x [0];                // { dg-message "incomplete type|zero-size array" }
};

struct Sx25 {
  struct S { };
  S a_x [0];                // { dg-warning "zero-size array" }
};

struct Sx26 {
  struct { }
    a_x [0];                // { dg-warning "zero-size array" }
};

struct Sx27 {
  int i;
  struct { }
    a_x [0];                // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx27, a_x);

struct Sx28 {
  struct { }
    a_x [0];                // { dg-warning "zero-size array|not at end" }
  int i;
};

struct Sx29 {
  // Pointer to an array of zero size.
  int (*a_x)[0];            // { dg-warning "zero-size array" }
};

struct Sx30 {
  // Reference to an array of zero size.
  int (&a_x)[0];            // { dg-warning "zero-size array" }
};

struct Sx31 {
  int a [0];                // { dg-warning "zero-size array|not at end" }
  unsigned i: 1;
};

struct Sx32 {
  unsigned i: 1;
  int a [0];                // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx32, a);

struct Sx33 {
  int a [0];                // { dg-warning "zero-size array|otherwise empty" }
  friend int foo ();
};

struct Sx34 {
  friend int foo ();
  int a [0];                // { dg-warning "zero-size array|otherwise empty" }
};

// Verify that intervening non-field declarations of members other
// than non-static data members don't affect the diagnostics.
struct Sx35 {
  int a[0];                 // { dg-warning "zero-size array|not at end" }
  typedef int I;
  int n;
};

struct Sx36 {
  int n;
  typedef int I;
  int a[0];                 // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx36, a);

struct Sx37 {
  int a[0];                 // { dg-warning "zero-size array|not at end" }
  enum E { };
  int n;
};

struct Sx38 {
  int n;
  enum E { };
  int a[0];                 // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx38, a);

struct Sx39 {
  int a[0];                 // { dg-warning "zero-size array|not at end" }
  struct S;
  int n;
};

struct Sx40 {
  int n;
  struct S;
  int a[0];                 // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx40, a);

struct Sx41 {
  int a[0];                 // { dg-warning "zero-size array|not at end" }
  static int i;
  int n;
};

struct Sx42 {
  int n;
  static int i;
  int a[0];                 // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx42, a);

struct Sx43 {
  int a[0];                 // { dg-warning "zero-size array|not at end" }
  Sx43 ();
  int n;
};

struct Sx44 {
  int n;
  Sx44 ();
  int a[0];                 // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Sx44, a);

struct S_S_S_x {
  struct A {
    struct B {
      int a[0];             // { dg-warning "zero-size array" }
    } b;
  } a;
};

// Since members of an anonymous struct or union are considered to be
// members of the enclosing union the below defintions are valid and
// must be accepted.

struct Anon1 {
  int n;
  struct {                  // { dg-warning "invalid use \[^\n\r\]* with a zero-size array" }
    int good[0];            // { dg-warning "forbids zero-size array" }
  };                        // { dg-warning "anonymous struct" }
};

ASSERT_AT_END (Anon1, good);

struct Anon2 {
  struct {                  // { dg-warning "invalid use" }
    int n;
    struct {
      int good[0];          // { dg-warning "zero-size array" }
    };                      // { dg-warning "anonymous struct" }
  };                        // { dg-warning "anonymous struct" }
};

ASSERT_AT_END (Anon2, good);

struct Anon3 {
  struct {                  // { dg-warning "invalid use" }
    struct {
      int n;
      int good[0];          // { dg-warning "zero-size array" }
    };                      // { dg-warning "anonymous struct" }
  };                        // { dg-warning "anonymous struct" }
};

ASSERT_AT_END (Anon3, good);

struct Anon4 {
  struct {
    int in_empty_struct[0]; // { dg-warning "zero-size array|in an otherwise empty" }
  };                        // { dg-warning "anonymous struct" }
};

struct Anon5 {
  struct {
    int not_at_end[0];      // { dg-warning "zero-size array|not at end" }
  };                        // { dg-warning "anonymous struct" }
  int n;
};

struct Anon6 {
  struct {
    struct {
      int not_at_end[0];    // { dg-warning "zero-size array|not at end" }
    };                      // { dg-warning "anonymous struct" }
    int n;
  };                        // { dg-warning "anonymous struct" }
};


struct Anon7 {
  struct {
    struct {
      int not_at_end[0];    // { dg-warning "zero-size array|not at end" }
    };                      // { dg-warning "anonymous struct" }
  };                        // { dg-warning "anonymous struct" }
  int n;
};


struct Six {
  int i;
  int a[0];                 // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Six, a);

class Cx {
  int a[0];                 // { dg-warning "zero-size array" }
};

class Cix {
  int i;
  int a[0];                 // { dg-warning "zero-size array" }
};

struct Sxi {
  int a[0];                 // { dg-warning "zero-size array|not at end" }
  int i;
};

struct S0 {
  int a[0];                 // { dg-warning "zero-size array" }
};

struct S0i {
  int a[0];                 // { dg-warning "zero-size array" }
  int i;
};

struct S_a0_ax {
  int a1[0];                // { dg-warning "zero-size array" }
  int ax[0];                // { dg-warning "zero-size array" }
};

struct S_a0_i_ax {
  int a1[0];                // { dg-warning "zero-size array" }
  int i;
  int ax[0];                // { dg-warning "zero-size array" }
};

ASSERT_AT_END (S_a0_i_ax, ax);

struct Si_a0_ax {
  int i;
  int a1[0];                // { dg-warning "zero-size array" }
  int ax[0];                // { dg-warning "zero-size array" }
};

ASSERT_AT_END (Si_a0_ax, ax);

struct S_u0_ax {
  union { } u[0];           // { dg-warning "zero-size array" }
  int ax[0];                // { dg-warning "zero-size array" }
};

struct S_a1_s2 {
  int a[1];
  int b[2];
};
