// RUN: %clang_cc1 -std=c++2a -fsyntax-only -triple x86_64-linux-gnu -fdump-record-layouts %s | FileCheck %s

namespace Empty {
  struct A {};
  struct B { [[no_unique_address]] A a; char b; };
  static_assert(sizeof(B) == 1);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::B
  // CHECK-NEXT:     0 |   struct Empty::A a (empty)
  // CHECK-NEXT:     0 |   char b
  // CHECK-NEXT:       | [sizeof=1, dsize=1, align=1,
  // CHECK-NEXT:       |  nvsize=1, nvalign=1]

  struct C {};
  struct D {
    [[no_unique_address]] A a;
    [[no_unique_address]] C c;
    char d;
  };
  static_assert(sizeof(D) == 1);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::D
  // CHECK-NEXT:     0 |   struct Empty::A a (empty)
  // CHECK-NEXT:     0 |   struct Empty::C c (empty)
  // CHECK-NEXT:     0 |   char d
  // CHECK-NEXT:       | [sizeof=1, dsize=1, align=1,
  // CHECK-NEXT:       |  nvsize=1, nvalign=1]

  struct E {
    [[no_unique_address]] A a1;
    [[no_unique_address]] A a2;
    char e;
  };
  static_assert(sizeof(E) == 2);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::E
  // CHECK-NEXT:     0 |   struct Empty::A a1 (empty)
  // CHECK-NEXT:     1 |   struct Empty::A a2 (empty)
  // CHECK-NEXT:     0 |   char e
  // CHECK-NEXT:       | [sizeof=2, dsize=2, align=1,
  // CHECK-NEXT:       |  nvsize=2, nvalign=1]

  struct F {
    ~F();
    [[no_unique_address]] A a1;
    [[no_unique_address]] A a2;
    char f;
  };
  static_assert(sizeof(F) == 2);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::F
  // CHECK-NEXT:     0 |   struct Empty::A a1 (empty)
  // CHECK-NEXT:     1 |   struct Empty::A a2 (empty)
  // CHECK-NEXT:     0 |   char f
  // CHECK-NEXT:       | [sizeof=2, dsize=1, align=1,
  // CHECK-NEXT:       |  nvsize=2, nvalign=1]

  struct G { [[no_unique_address]] A a; ~G(); };
  static_assert(sizeof(G) == 1);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::G
  // CHECK-NEXT:     0 |   struct Empty::A a (empty)
  // CHECK-NEXT:       | [sizeof=1, dsize=0, align=1,
  // CHECK-NEXT:       |  nvsize=1, nvalign=1]

  struct H { [[no_unique_address]] A a, b; ~H(); };
  static_assert(sizeof(H) == 2);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::H
  // CHECK-NEXT:     0 |   struct Empty::A a (empty)
  // CHECK-NEXT:     1 |   struct Empty::A b (empty)
  // CHECK-NEXT:       | [sizeof=2, dsize=0, align=1,
  // CHECK-NEXT:       |  nvsize=2, nvalign=1]

  struct OversizedEmpty : A {
    ~OversizedEmpty();
    [[no_unique_address]] A a;
  };
  static_assert(sizeof(OversizedEmpty) == 2);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::OversizedEmpty
  // CHECK-NEXT:     0 |   struct Empty::A (base) (empty)
  // CHECK-NEXT:     1 |   struct Empty::A a (empty)
  // CHECK-NEXT:       | [sizeof=2, dsize=0, align=1,
  // CHECK-NEXT:       |  nvsize=2, nvalign=1]

  struct HasOversizedEmpty {
    [[no_unique_address]] OversizedEmpty m;
  };
  static_assert(sizeof(HasOversizedEmpty) == 2);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::HasOversizedEmpty
  // CHECK-NEXT:     0 |   struct Empty::OversizedEmpty m (empty)
  // CHECK-NEXT:     0 |     struct Empty::A (base) (empty)
  // CHECK-NEXT:     1 |     struct Empty::A a (empty)
  // CHECK-NEXT:       | [sizeof=2, dsize=0, align=1,
  // CHECK-NEXT:       |  nvsize=2, nvalign=1]

  struct EmptyWithNonzeroDSize {
    [[no_unique_address]] A a;
    int x;
    [[no_unique_address]] A b;
    int y;
    [[no_unique_address]] A c;
  };
  static_assert(sizeof(EmptyWithNonzeroDSize) == 12);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::EmptyWithNonzeroDSize
  // CHECK-NEXT:     0 |   struct Empty::A a (empty)
  // CHECK-NEXT:     0 |   int x
  // CHECK-NEXT:     4 |   struct Empty::A b (empty)
  // CHECK-NEXT:     4 |   int y
  // CHECK-NEXT:     8 |   struct Empty::A c (empty)
  // CHECK-NEXT:       | [sizeof=12, dsize=12, align=4,
  // CHECK-NEXT:       |  nvsize=12, nvalign=4]

  struct EmptyWithNonzeroDSizeNonPOD {
    ~EmptyWithNonzeroDSizeNonPOD();
    [[no_unique_address]] A a;
    int x;
    [[no_unique_address]] A b;
    int y;
    [[no_unique_address]] A c;
  };
  static_assert(sizeof(EmptyWithNonzeroDSizeNonPOD) == 12);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct Empty::EmptyWithNonzeroDSizeNonPOD
  // CHECK-NEXT:     0 |   struct Empty::A a (empty)
  // CHECK-NEXT:     0 |   int x
  // CHECK-NEXT:     4 |   struct Empty::A b (empty)
  // CHECK-NEXT:     4 |   int y
  // CHECK-NEXT:     8 |   struct Empty::A c (empty)
  // CHECK-NEXT:       | [sizeof=12, dsize=8, align=4,
  // CHECK-NEXT:       |  nvsize=9, nvalign=4]
}

namespace POD {
  // Cannot reuse tail padding of a PDO type.
  struct A { int n; char c[3]; };
  struct B { [[no_unique_address]] A a; char d; };
  static_assert(sizeof(B) == 12);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct POD::B
  // CHECK-NEXT:     0 |   struct POD::A a
  // CHECK-NEXT:     0 |     int n
  // CHECK-NEXT:     4 |     char[3] c
  // CHECK-NEXT:     8 |   char d
  // CHECK-NEXT:       | [sizeof=12, dsize=12, align=4,
  // CHECK-NEXT:       |  nvsize=12, nvalign=4]
}

namespace NonPOD {
  struct A { int n; char c[3]; ~A(); };
  struct B { [[no_unique_address]] A a; char d; };
  static_assert(sizeof(B) == 8);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct NonPOD::B
  // CHECK-NEXT:     0 |   struct NonPOD::A a
  // CHECK-NEXT:     0 |     int n
  // CHECK-NEXT:     4 |     char[3] c
  // CHECK-NEXT:     7 |   char d
  // CHECK-NEXT:       | [sizeof=8, dsize=8, align=4,
  // CHECK-NEXT:       |  nvsize=8, nvalign=4]
}

namespace NVSizeGreaterThanDSize {
  // The nvsize of an object includes the complete size of its empty subobjects
  // (although it's unclear why). Ensure this corner case is handled properly.
  struct alignas(8) A { ~A(); }; // dsize 0, nvsize 0, size 8
  struct B : A { char c; }; // dsize 1, nvsize 8, size 8
  static_assert(sizeof(B) == 8);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct NVSizeGreaterThanDSize::B
  // CHECK-NEXT:     0 |   struct NVSizeGreaterThanDSize::A (base) (empty)
  // CHECK-NEXT:     0 |   char c
  // CHECK-NEXT:       | [sizeof=8, dsize=1, align=8,
  // CHECK-NEXT:       |  nvsize=8, nvalign=8]

  struct V { int n; };

  // V is at offset 16, not offset 12, because B's tail padding is strangely not
  // usable for virtual bases.
  struct C : B, virtual V {};
  static_assert(sizeof(C) == 24);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct NVSizeGreaterThanDSize::C
  // CHECK-NEXT:     0 |   (C vtable pointer)
  // CHECK-NEXT:     8 |   struct NVSizeGreaterThanDSize::B (base)
  // CHECK-NEXT:     8 |     struct NVSizeGreaterThanDSize::A (base) (empty)
  // CHECK-NEXT:     8 |     char c
  // CHECK-NEXT:    16 |   struct NVSizeGreaterThanDSize::V (virtual base)
  // CHECK-NEXT:    16 |     int n
  // CHECK-NEXT:       | [sizeof=24, dsize=20, align=8,
  // CHECK-NEXT:       |  nvsize=16, nvalign=8]

  struct D : virtual V {
    [[no_unique_address]] B b;
  };
  static_assert(sizeof(D) == 24);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct NVSizeGreaterThanDSize::D
  // CHECK-NEXT:     0 |   (D vtable pointer)
  // CHECK-NEXT:     8 |   struct NVSizeGreaterThanDSize::B b
  // CHECK-NEXT:     8 |     struct NVSizeGreaterThanDSize::A (base) (empty)
  // CHECK-NEXT:     8 |     char c
  // CHECK-NEXT:    16 |   struct NVSizeGreaterThanDSize::V (virtual base)
  // CHECK-NEXT:    16 |     int n
  // CHECK-NEXT:       | [sizeof=24, dsize=20, align=8,
  // CHECK-NEXT:       |  nvsize=16, nvalign=8]

  struct X : virtual A { [[no_unique_address]] A a; };
  struct E : virtual A {
    [[no_unique_address]] A a;
    // Here, we arrange for X to hang over the end of the nvsize of E. This
    // should force the A vbase to be laid out at offset 24, not 16.
    [[no_unique_address]] X x;
  };
  static_assert(sizeof(E) == 32);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct NVSizeGreaterThanDSize::E
  // CHECK-NEXT:     0 |   (E vtable pointer)
  // CHECK-NEXT:     0 |   struct NVSizeGreaterThanDSize::A a (empty)
  // CHECK-NEXT:     8 |   struct NVSizeGreaterThanDSize::X x
  // CHECK-NEXT:     8 |     (X vtable pointer)
  // CHECK-NEXT:     8 |     struct NVSizeGreaterThanDSize::A a (empty)
  // CHECK-NEXT:    16 |     struct NVSizeGreaterThanDSize::A (virtual base) (empty)
  // CHECK-NEXT:    24 |   struct NVSizeGreaterThanDSize::A (virtual base) (empty)
  // CHECK-NEXT:       | [sizeof=32, dsize=16, align=8,
  // CHECK-NEXT:       |  nvsize=16, nvalign=8]
}

namespace RepeatedVBase {
  struct alignas(16) A { ~A(); };
  struct B : A {};
  struct X : virtual A, virtual B {};
  struct Y { [[no_unique_address]] X x; char c; };
  static_assert(sizeof(Y) == 32);

  // CHECK:*** Dumping AST Record Layout
  // CHECK:          0 | struct RepeatedVBase::Y
  // CHECK-NEXT:     0 |   struct RepeatedVBase::X x
  // CHECK-NEXT:     0 |     (X vtable pointer)
  // CHECK-NEXT:     0 |     struct RepeatedVBase::A (virtual base) (empty)
  // CHECK-NEXT:    16 |     struct RepeatedVBase::B (virtual base) (empty)
  // CHECK-NEXT:    16 |       struct RepeatedVBase::A (base) (empty)
  // CHECK-NEXT:     8 |     char c
  // CHECK-NEXT:       | [sizeof=32, dsize=9, align=16,
  // CHECK-NEXT:       |  nvsize=9, nvalign=16]
}
