// RUN: %clang_cc1 -std=c++2a -fsyntax-only -triple x86_64-windows-msvc -fms-compatibility -fdump-record-layouts %s | FileCheck %s namespace Empty { struct A {}; struct A2 {}; struct A3 { [[msvc::no_unique_address]] A a; }; struct alignas(8) A4 {}; struct B { [[msvc::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, align=1, // CHECK-NEXT: | nvsize=1, nvalign=1] struct C { [[msvc::no_unique_address]] A a; [[msvc::no_unique_address]] A2 a2; char c; }; static_assert(sizeof(C) == 1); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct Empty::C // CHECK-NEXT: 0 | struct Empty::A a (empty) // CHECK-NEXT: 0 | struct Empty::A2 a2 (empty) // CHECK-NEXT: 0 | char c // CHECK-NEXT: | [sizeof=1, align=1, // CHECK-NEXT: | nvsize=1, nvalign=1] struct D { [[msvc::no_unique_address]] A3 a; int i; }; static_assert(sizeof(D) == 8); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct Empty::D // CHECK-NEXT: 0 | struct Empty::A3 a (empty) // CHECK-NEXT: 0 | struct Empty::A a (empty) // CHECK-NEXT: 4 | int i // CHECK-NEXT: | [sizeof=8, align=4, // CHECK-NEXT: | nvsize=8, nvalign=4] struct E { [[msvc::no_unique_address]] A a1; [[msvc::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, align=1, // CHECK-NEXT: | nvsize=2, nvalign=1] struct F { ~F(); [[msvc::no_unique_address]] A a1; [[msvc::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, align=1, // CHECK-NEXT: | nvsize=2, nvalign=1] struct G { [[msvc::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, align=1, // CHECK-NEXT: | nvsize=1, nvalign=1] struct H { [[msvc::no_unique_address]] A a; [[msvc::no_unique_address]] 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, align=1, // CHECK-NEXT: | nvsize=2, nvalign=1] struct I { [[msvc::no_unique_address]] A4 a; [[msvc::no_unique_address]] A4 b; }; static_assert(sizeof(I) == 16); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct Empty::I // CHECK-NEXT: 0 | struct Empty::A4 a (empty) // CHECK-NEXT: 8 | struct Empty::A4 b (empty) // CHECK-NEXT: | [sizeof=16, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] struct J { [[msvc::no_unique_address]] A4 a; A4 b; }; static_assert(sizeof(J) == 16); // MSVC puts a and b at the same offset. // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct Empty::J // CHECK-NEXT: 0 | struct Empty::A4 a (empty) // CHECK-NEXT: 8 | struct Empty::A4 b (empty) // CHECK-NEXT: | [sizeof=16, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] struct K { [[msvc::no_unique_address]] A4 a; [[msvc::no_unique_address]] char c; [[msvc::no_unique_address]] A4 b; }; static_assert(sizeof(K) == 16); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct Empty::K // CHECK-NEXT: 0 | struct Empty::A4 a (empty) // CHECK-NEXT: 0 | char c // CHECK-NEXT: 8 | struct Empty::A4 b (empty) // CHECK-NEXT: | [sizeof=16, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] struct OversizedEmpty : A { ~OversizedEmpty(); [[msvc::no_unique_address]] A a; }; static_assert(sizeof(OversizedEmpty) == 1); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct Empty::OversizedEmpty // CHECK-NEXT: 0 | struct Empty::A (base) (empty) // CHECK-NEXT: 0 | struct Empty::A a (empty) // CHECK-NEXT: | [sizeof=1, align=1, // CHECK-NEXT: | nvsize=1, nvalign=1] struct HasOversizedEmpty { [[msvc::no_unique_address]] OversizedEmpty m; }; static_assert(sizeof(HasOversizedEmpty) == 1); // 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: 0 | struct Empty::A a (empty) // CHECK-NEXT: | [sizeof=1, align=1, // CHECK-NEXT: | nvsize=1, nvalign=1] struct EmptyWithNonzeroDSize { [[msvc::no_unique_address]] A a; int x; [[msvc::no_unique_address]] A b; int y; [[msvc::no_unique_address]] A c; }; static_assert(sizeof(EmptyWithNonzeroDSize) == 8); // 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: 1 | struct Empty::A b (empty) // CHECK-NEXT: 4 | int y // CHECK-NEXT: 2 | struct Empty::A c (empty) // CHECK-NEXT: | [sizeof=8, align=4, // CHECK-NEXT: | nvsize=8, nvalign=4] struct EmptyWithNonzeroDSizeNonPOD { ~EmptyWithNonzeroDSizeNonPOD(); [[msvc::no_unique_address]] A a; int x; [[msvc::no_unique_address]] A b; int y; [[msvc::no_unique_address]] A c; }; static_assert(sizeof(EmptyWithNonzeroDSizeNonPOD) == 8); // 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: 1 | struct Empty::A b (empty) // CHECK-NEXT: 4 | int y // CHECK-NEXT: 2 | struct Empty::A c (empty) // CHECK-NEXT: | [sizeof=8, align=4, // CHECK-NEXT: | nvsize=8, nvalign=4] } namespace POD { struct A { int n; char c[3]; }; struct B { [[msvc::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, align=4, // CHECK-NEXT: | nvsize=12, nvalign=4] } namespace NonPOD { struct A { int n; char c[3]; ~A(); }; struct B { [[msvc::no_unique_address]] A a; char d; }; static_assert(sizeof(B) == 12); // 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: 8 | char d // CHECK-NEXT: | [sizeof=12, align=4, // CHECK-NEXT: | nvsize=12, nvalign=4] } namespace VBases { // 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 Empty {}; struct alignas(8) 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 VBases::B // CHECK-NEXT: 0 | struct VBases::A (base) (empty) // CHECK-NEXT: 0 | char c // CHECK-NEXT: | [sizeof=8, align=8, // CHECK-NEXT: | nvsize=8, nvalign=8] struct V { int n; }; struct C : B, virtual V {}; static_assert(sizeof(C) == 24); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct VBases::C // CHECK-NEXT: 0 | struct VBases::B (base) // CHECK-NEXT: 0 | struct VBases::A (base) (empty) // CHECK-NEXT: 0 | char c // CHECK-NEXT: 8 | (C vbtable pointer) // CHECK-NEXT: 16 | struct VBases::V (virtual base) // CHECK-NEXT: 16 | int n // CHECK-NEXT: | [sizeof=24, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] struct D : virtual Empty { [[msvc::no_unique_address]] Empty a; }; static_assert(sizeof(D) == 16); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct VBases::D // CHECK-NEXT: 0 | (D vbtable pointer) // CHECK-NEXT: 8 | struct VBases::Empty a // CHECK-NEXT: 16 | struct VBases::Empty (virtual base) (empty) // CHECK-NEXT: | [sizeof=16, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] struct E : virtual V { [[msvc::no_unique_address]] B b; }; static_assert(sizeof(E) == 24); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct VBases::E // CHECK-NEXT: 0 | (E vbtable pointer) // CHECK-NEXT: 8 | struct VBases::B b // CHECK-NEXT: 8 | struct VBases::A (base) (empty) // CHECK-NEXT: 8 | char c // CHECK-NEXT: 16 | struct VBases::V (virtual base) // CHECK-NEXT: 16 | int n // CHECK-NEXT: | [sizeof=24, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] struct X : virtual A { [[msvc::no_unique_address]] A a; }; struct F : virtual A { [[msvc::no_unique_address]] A a; [[msvc::no_unique_address]] X x; }; static_assert(sizeof(F) == 24); // MSVC places x after a and the total size is 48. // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct VBases::F // CHECK-NEXT: 0 | (F vbtable pointer) // CHECK-NEXT: 8 | struct VBases::A a (empty) // CHECK-NEXT: 8 | struct VBases::X x // CHECK-NEXT: 8 | (X vbtable pointer) // CHECK-NEXT: 16 | struct VBases::A a (empty) // CHECK-NEXT: 24 | struct VBases::A (virtual base) (empty) // CHECK-NEXT: 24 | struct VBases::A (virtual base) (empty) // CHECK-NEXT: | [sizeof=24, align=8, // CHECK-NEXT: | nvsize=24, nvalign=8] struct G : virtual Empty { int i; [[msvc::no_unique_address]] A a; }; static_assert(sizeof(G) == 16); // MSVC places a at offset 12. // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct VBases::G // CHECK-NEXT: 0 | (G vbtable pointer) // CHECK-NEXT: 8 | int i // CHECK-NEXT: 8 | struct VBases::A a (empty) // CHECK-NEXT: 16 | struct VBases::Empty (virtual base) (empty) // CHECK-NEXT: | [sizeof=16, align=8, // CHECK-NEXT: | nvsize=16, nvalign=8] } namespace ZeroSize { struct empty {}; union empty_union {}; struct empty_union_container { [[msvc::no_unique_address]] empty_union x; }; union union_of_empty { [[msvc::no_unique_address]] empty x; }; struct struct_of_empty { [[msvc::no_unique_address]] empty x; }; struct union_of_empty_container { [[msvc::no_unique_address]] union_of_empty x; }; static_assert(sizeof(union_of_empty_container) == 1); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct ZeroSize::union_of_empty_container // CHECK-NOT: (empty) // CHECK: 0 | union ZeroSize::union_of_empty x (empty) // CHECK: 0 | struct ZeroSize::empty x (empty) // CHECK: | [sizeof=1, align=1, // CHECK: | nvsize=1, nvalign=1] struct struct_of_empty_container { [[msvc::no_unique_address]] struct_of_empty x; }; static_assert(sizeof(struct_of_empty_container) == 1); // CHECK:*** Dumping AST Record Layout // CHECK: 0 | struct ZeroSize::struct_of_empty_container // CHECK-NOT: (empty) // CHECK: 0 | struct ZeroSize::struct_of_empty x (empty) // CHECK: 0 | struct ZeroSize::empty x (empty) // CHECK: | [sizeof=1, align=1, // CHECK: | nvsize=1, nvalign=1] }