llvm-project/clang/test/Layout/ms-no-unique-address.cpp
Amy Huang 0faee97a92
Recommit "Implement [[msvc::no_unique_address]] (#65675)" (#67199)
This implements the [[msvc::no_unique_address]] attribute.

There is not ABI compatibility in this patch because the attribute is
relatively new and there's still some uncertainty in the MSVC version.

The recommit changes the attribute definitions so that instead of making
two separate attributes for no_unique_address
and msvc::no_unique_address, it modifies the attributes tablegen emitter
to allow spellings to be target-specific.

This reverts commit 71f9e7695b87298f9855d8890f0e6a3b89381eb5.
2023-09-28 14:29:32 -07:00

382 lines
12 KiB
C++

// 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]
}