https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/218546
>From 4682552960d1df367763fdf4b1be6a0575d2c82f Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Thu, 20 Aug 2026 14:05:14 -0700 Subject: [PATCH 1/4] [LLVMABI] Align RecordType::isEmpty() with Clang's isEmptyRecord() While working on empty record handling for AArch64, I stumbled across a couple of differences between the LLVM ABI library's `RecordType::isEmpty()` and Clang's `CodeGen::isEmptyRecord()`. Testing verified this led to observable differences in classification when using the ABI library. This change updates the ABI library's empty record handling to match Clang. Assisted-by: Cursor / various models --- clang/lib/CodeGen/QualTypeMapper.cpp | 12 +- .../X86/x86_64-empty-cxx-member-abi.cpp | 35 +++++ llvm/include/llvm/ABI/Types.h | 16 +- llvm/lib/ABI/Types.cpp | 28 ++-- llvm/unittests/ABI/CMakeLists.txt | 1 + llvm/unittests/ABI/TypesTest.cpp | 138 ++++++++++++++++++ .../gn/secondary/llvm/unittests/ABI/BUILD.gn | 5 +- 7 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp create mode 100644 llvm/unittests/ABI/TypesTest.cpp diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp index c8be9de4769c8..7c08fd9968ace 100644 --- a/clang/lib/CodeGen/QualTypeMapper.cpp +++ b/clang/lib/CodeGen/QualTypeMapper.cpp @@ -393,7 +393,10 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) { if (RD->isPolymorphic()) { const llvm::abi::Type *VtablePointer = createPointerTypeForPointee(ASTCtx.VoidPtrTy); - Fields.emplace_back(VtablePointer, 0); + Fields.emplace_back(VtablePointer, 0, /*IsBitField=*/false, + /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/false, + /*IsVTablePointer=*/true); } for (const auto &Base : RD->bases()) { @@ -409,6 +412,8 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) { 8; BaseClasses.emplace_back(BaseType, BaseOffset, /*IsBitField=*/false, /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/false, + /*IsVTablePointer=*/false, /*IsVirtualBase=*/Base.isVirtual()); } @@ -424,6 +429,8 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) { /*IsBitField=*/false, /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/false, + /*IsVTablePointer=*/false, /*IsVirtualBase=*/true); } @@ -575,8 +582,9 @@ void QualTypeMapper::computeFieldInfo( IsUnnamedBitField = FD->isUnnamedBitField(); } + bool HasNoUniqueAddress = FD->hasAttr<NoUniqueAddressAttr>(); Fields.emplace_back(FieldType, OffsetInBits, IsBitField, BitFieldWidth, - IsUnnamedBitField); + IsUnnamedBitField, HasNoUniqueAddress); ++FieldIndex; } } diff --git a/clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp b/clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp new file mode 100644 index 0000000000000..7ce36fc620707 --- /dev/null +++ b/clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -fexperimental-abi-lowering %s -o - | FileCheck %s + +typedef float V4F __attribute__((vector_size(16))); + +struct Empty {}; + +extern "C" { + +union VecAndEmpty { + V4F v; + Empty e; +}; + +void take_vec_and_empty(union VecAndEmpty u); +void call_vec_and_empty(union VecAndEmpty u) { take_vec_and_empty(u); } +// CHECK-DAG: declare void @take_vec_and_empty(<2 x double>) + +union VecAndEmpty ret_vec_and_empty(void); +void call_ret_vec_and_empty(void) { ret_vec_and_empty(); } +// CHECK-DAG: declare <2 x double> @ret_vec_and_empty() + +struct VecNoUniqueAddress { + V4F v; + [[no_unique_address]] Empty e; +}; + +void take_vec_nua(VecNoUniqueAddress s); +void call_vec_nua(VecNoUniqueAddress s) { take_vec_nua(s); } +// CHECK-DAG: declare void @take_vec_nua(<4 x float>) + +VecNoUniqueAddress ret_vec_nua(void); +void call_ret_vec_nua(void) { ret_vec_nua(); } +// CHECK-DAG: declare <4 x float> @ret_vec_nua() +} diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h index 07c0480835794..2247cc59b3978 100644 --- a/llvm/include/llvm/ABI/Types.h +++ b/llvm/include/llvm/ABI/Types.h @@ -237,14 +237,19 @@ struct FieldInfo { uint64_t BitFieldWidth; bool IsBitField; bool IsUnnamedBitfield; + bool HasNoUniqueAddress; + bool IsVTablePointer; bool IsVirtualBase; FieldInfo(const Type *FieldType, uint64_t OffsetInBits = 0, bool IsBitField = false, uint64_t BitFieldWidth = 0, - bool IsUnnamedBitField = false, bool IsVirtualBase = false) + bool IsUnnamedBitField = false, bool HasNoUniqueAddress = false, + bool IsVTablePointer = false, bool IsVirtualBase = false) : FieldType(FieldType), OffsetInBits(OffsetInBits), BitFieldWidth(BitFieldWidth), IsBitField(IsBitField), - IsUnnamedBitfield(IsUnnamedBitField), IsVirtualBase(IsVirtualBase) {} + IsUnnamedBitfield(IsUnnamedBitField), + HasNoUniqueAddress(HasNoUniqueAddress), + IsVTablePointer(IsVTablePointer), IsVirtualBase(IsVirtualBase) {} LLVM_ABI bool isEmpty() const; }; @@ -419,10 +424,9 @@ class TypeBuilder { FieldInfo *FieldArray = Allocator.Allocate<FieldInfo>(Fields.size()); for (size_t I = 0, E = Fields.size(); I != E; ++I) { - const FieldInfo &Field = Fields[I]; - new (&FieldArray[I]) - FieldInfo(Field.FieldType, 0, Field.IsBitField, Field.BitFieldWidth, - Field.IsUnnamedBitfield); + FieldInfo Field = Fields[I]; + Field.OffsetInBits = 0; + new (&FieldArray[I]) FieldInfo(Field); } ArrayRef<FieldInfo> FieldsRef(FieldArray, Fields.size()); diff --git a/llvm/lib/ABI/Types.cpp b/llvm/lib/ABI/Types.cpp index 6c44e1f4d1e48..2e3ec907cf251 100644 --- a/llvm/lib/ABI/Types.cpp +++ b/llvm/lib/ABI/Types.cpp @@ -13,10 +13,10 @@ using namespace llvm; using namespace llvm::abi; bool RecordType::isEmpty() const { - if (hasFlexibleArrayMember() || isPolymorphic() || - getNumVirtualBaseClasses() != 0) + if (hasFlexibleArrayMember()) return false; + // Direct virtual bases are not included in the base class list. for (const FieldInfo &Base : getBaseClasses()) { const auto *BaseRT = dyn_cast<RecordType>(Base.FieldType); if (!BaseRT || !BaseRT->isEmpty()) @@ -24,6 +24,9 @@ bool RecordType::isEmpty() const { } for (const FieldInfo &FI : getFields()) { + // Don't treat vtable pointers as a source field for emptiness. + if (FI.IsVTablePointer) + continue; if (!FI.isEmpty()) return false; } @@ -67,18 +70,25 @@ RecordType::getElementContainingOffset(unsigned OffsetInBits) const { bool FieldInfo::isEmpty() const { if (IsUnnamedBitfield) return true; - if (IsBitField && BitFieldWidth == 0) - return true; const Type *Ty = FieldType; + bool WasArray = false; while (const auto *AT = dyn_cast<ArrayType>(Ty)) { - if (AT->getNumElements() != 1) - break; + // Constant arrays of zero length always count as empty. + if (AT->getNumElements() == 0) + return true; Ty = AT->getElementType(); + WasArray = true; } - if (const auto *RT = dyn_cast<RecordType>(Ty)) - return RT->isEmpty(); + const auto *RT = dyn_cast<RecordType>(Ty); + if (!RT) + return false; + + // C++ record fields are never empty unless [[no_unique_address]] applies. + // That exception does not apply to arrays of C++ empty records. + if (RT->isCXXRecord() && (WasArray || !HasNoUniqueAddress)) + return false; - return Ty->isZeroSize(); + return RT->isEmpty(); } diff --git a/llvm/unittests/ABI/CMakeLists.txt b/llvm/unittests/ABI/CMakeLists.txt index a26da474bd83a..fdc665e5a2c98 100644 --- a/llvm/unittests/ABI/CMakeLists.txt +++ b/llvm/unittests/ABI/CMakeLists.txt @@ -7,4 +7,5 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(ABITests AArch64TargetInfoTest.cpp X86TargetInfoTest.cpp + TypesTest.cpp ) diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp new file mode 100644 index 0000000000000..ad448882b82b1 --- /dev/null +++ b/llvm/unittests/ABI/TypesTest.cpp @@ -0,0 +1,138 @@ +//===- TypesTest.cpp - ABI type emptiness unit tests ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ABI/Types.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/TypeSize.h" +#include "gtest/gtest.h" + +using llvm::Align; +using llvm::TypeSize; +using llvm::abi::FieldInfo; +using llvm::abi::RecordFlags; +using llvm::abi::RecordType; +using llvm::abi::StructPacking; +using llvm::abi::TypeBuilder; + +namespace { + +class ABITypesTest : public ::testing::Test { +protected: + llvm::BumpPtrAllocator Alloc; + TypeBuilder TB; + + ABITypesTest() : TB(Alloc) {} + + const RecordType *makeRecord(llvm::ArrayRef<FieldInfo> Fields, + uint64_t SizeBits, RecordFlags Flags, + llvm::ArrayRef<FieldInfo> Bases = {}, + llvm::ArrayRef<FieldInfo> VBases = {}) { + return TB.getRecordType(Fields, TypeSize::getFixed(SizeBits), Align(1), + StructPacking::Default, Bases, VBases, Flags); + } +}; + +TEST_F(ABITypesTest, EmptyCRecord) { + const RecordType *Empty = makeRecord({}, 0, RecordFlags::CanPassInRegisters); + EXPECT_TRUE(Empty->isEmpty()); +} + +TEST_F(ABITypesTest, NestedEmptyCRecordField) { + const RecordType *Empty = makeRecord({}, 8, RecordFlags::CanPassInRegisters); + const RecordType *Nested = + makeRecord({FieldInfo(Empty, 0)}, 8, RecordFlags::CanPassInRegisters); + EXPECT_TRUE(Nested->isEmpty()); +} + +TEST_F(ABITypesTest, CXXNestedEmptyFieldRequiresNoUniqueAddress) { + RecordFlags CXXFlags = static_cast<RecordFlags>( + RecordFlags::CanPassInRegisters | RecordFlags::IsCXXRecord); + const RecordType *Empty = makeRecord({}, 8, CXXFlags); + + const RecordType *WithoutNUA = makeRecord({FieldInfo(Empty, 0)}, 8, CXXFlags); + EXPECT_FALSE(WithoutNUA->isEmpty()); + + FieldInfo NUAField(Empty, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0, + /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/true); + const RecordType *WithNUA = makeRecord({NUAField}, 8, CXXFlags); + EXPECT_TRUE(WithNUA->isEmpty()); +} + +TEST_F(ABITypesTest, ArrayOfEmptyRecords) { + RecordFlags CFlags = RecordFlags::CanPassInRegisters; + RecordFlags CXXFlags = static_cast<RecordFlags>( + RecordFlags::CanPassInRegisters | RecordFlags::IsCXXRecord); + const RecordType *EmptyC = makeRecord({}, 8, CFlags); + const RecordType *EmptyCXX = makeRecord({}, 8, CXXFlags); + const llvm::abi::Type *ArrC = TB.getArrayType(EmptyC, 2, 16); + const llvm::abi::Type *ArrCXX = TB.getArrayType(EmptyCXX, 2, 16); + const llvm::abi::Type *ZeroArrCXX = TB.getArrayType(EmptyCXX, 0, 0); + + EXPECT_TRUE(makeRecord({FieldInfo(ArrC, 0)}, 16, CFlags)->isEmpty()); + EXPECT_FALSE(makeRecord({FieldInfo(ArrCXX, 0)}, 16, CXXFlags)->isEmpty()); + EXPECT_TRUE(makeRecord({FieldInfo(ZeroArrCXX, 0)}, 0, CXXFlags)->isEmpty()); +} + +TEST_F(ABITypesTest, BitfieldsAndFlexibleArrays) { + const llvm::abi::Type *I32 = TB.getIntegerType(32, Align(4), /*Signed=*/true); + FieldInfo Unnamed(I32, 0, /*IsBitField=*/true, /*BitFieldWidth=*/3, + /*IsUnnamedBitField=*/true); + FieldInfo NamedZero(I32, 0, /*IsBitField=*/true, /*BitFieldWidth=*/0); + + EXPECT_TRUE( + makeRecord({Unnamed}, 8, RecordFlags::CanPassInRegisters)->isEmpty()); + EXPECT_FALSE( + makeRecord({NamedZero}, 8, RecordFlags::CanPassInRegisters)->isEmpty()); + EXPECT_FALSE( + makeRecord({}, 0, RecordFlags::HasFlexibleArrayMember)->isEmpty()); +} + +TEST_F(ABITypesTest, DirectVirtualBasesAndVTablePointer) { + RecordFlags CXXFlags = static_cast<RecordFlags>( + RecordFlags::CanPassInRegisters | RecordFlags::IsCXXRecord); + const RecordType *Empty = makeRecord({}, 8, CXXFlags); + const RecordType *IntField = makeRecord( + {FieldInfo(TB.getIntegerType(32, Align(4), /*Signed=*/true), 0)}, 32, + CXXFlags); + const llvm::abi::Type *VPtr = TB.getPointerType(64, Align(8)); + FieldInfo VTable(VPtr, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0, + /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/false, + /*IsVTablePointer=*/true); + // Direct virtual bases appear in both the base-class list (with + // IsVirtualBase) and the virtual-base list, matching CXXRecordDecl::bases() + // and vbases(). + FieldInfo EmptyVBase(Empty, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0, + /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/false, + /*IsVTablePointer=*/false, + /*IsVirtualBase=*/true); + FieldInfo NonEmptyVBase(IntField, 0, /*IsBitField=*/false, + /*BitFieldWidth=*/0, + /*IsUnnamedBitField=*/false, + /*HasNoUniqueAddress=*/false, + /*IsVTablePointer=*/false, + /*IsVirtualBase=*/true); + + EXPECT_TRUE(makeRecord({}, 8, CXXFlags, /*Bases=*/{EmptyVBase}, + /*VBases=*/{EmptyVBase}) + ->isEmpty()); + EXPECT_FALSE(makeRecord({}, 32, CXXFlags, /*Bases=*/{NonEmptyVBase}, + /*VBases=*/{NonEmptyVBase}) + ->isEmpty()); + EXPECT_TRUE(makeRecord({VTable}, 64, + static_cast<RecordFlags>(CXXFlags | + RecordFlags::IsPolymorphic), + {FieldInfo(Empty, 0)}) + ->isEmpty()); +} + +} // namespace diff --git a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn index a1c51980f0cbe..b355517a9251d 100644 --- a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn @@ -6,5 +6,8 @@ unittest("ABITests") { "//llvm/lib/IR", "//llvm/lib/Support", ] - sources = [ "AArch64TargetInfoTest.cpp" ] + sources = [ + "AArch64TargetInfoTest.cpp", + "TypesTest.cpp", + ] } >From d0afb5ec0d9ab6b7b983c71fe5f10025d2595e8f Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 24 Aug 2026 17:09:10 -0700 Subject: [PATCH 2/4] Remove stale comment --- llvm/lib/ABI/Types.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/llvm/lib/ABI/Types.cpp b/llvm/lib/ABI/Types.cpp index 2e3ec907cf251..6c5684cf26eeb 100644 --- a/llvm/lib/ABI/Types.cpp +++ b/llvm/lib/ABI/Types.cpp @@ -16,7 +16,6 @@ bool RecordType::isEmpty() const { if (hasFlexibleArrayMember()) return false; - // Direct virtual bases are not included in the base class list. for (const FieldInfo &Base : getBaseClasses()) { const auto *BaseRT = dyn_cast<RecordType>(Base.FieldType); if (!BaseRT || !BaseRT->isEmpty()) >From 87a3747b75cff31d063520d4529ec96f2366332a Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 25 Aug 2026 09:57:30 -0700 Subject: [PATCH 3/4] Remove special tracking of vtable pointer --- clang/lib/CodeGen/QualTypeMapper.cpp | 7 +------ llvm/include/llvm/ABI/Types.h | 5 ++--- llvm/lib/ABI/Types.cpp | 7 ++----- llvm/unittests/ABI/TypesTest.cpp | 25 ++++++++++--------------- 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp index 7c08fd9968ace..d26c6be88d1d4 100644 --- a/clang/lib/CodeGen/QualTypeMapper.cpp +++ b/clang/lib/CodeGen/QualTypeMapper.cpp @@ -393,10 +393,7 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) { if (RD->isPolymorphic()) { const llvm::abi::Type *VtablePointer = createPointerTypeForPointee(ASTCtx.VoidPtrTy); - Fields.emplace_back(VtablePointer, 0, /*IsBitField=*/false, - /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, - /*HasNoUniqueAddress=*/false, - /*IsVTablePointer=*/true); + Fields.emplace_back(VtablePointer, 0); } for (const auto &Base : RD->bases()) { @@ -413,7 +410,6 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) { BaseClasses.emplace_back(BaseType, BaseOffset, /*IsBitField=*/false, /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, /*HasNoUniqueAddress=*/false, - /*IsVTablePointer=*/false, /*IsVirtualBase=*/Base.isVirtual()); } @@ -430,7 +426,6 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) { /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, /*HasNoUniqueAddress=*/false, - /*IsVTablePointer=*/false, /*IsVirtualBase=*/true); } diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h index 2247cc59b3978..21a614e97f8bd 100644 --- a/llvm/include/llvm/ABI/Types.h +++ b/llvm/include/llvm/ABI/Types.h @@ -238,18 +238,17 @@ struct FieldInfo { bool IsBitField; bool IsUnnamedBitfield; bool HasNoUniqueAddress; - bool IsVTablePointer; bool IsVirtualBase; FieldInfo(const Type *FieldType, uint64_t OffsetInBits = 0, bool IsBitField = false, uint64_t BitFieldWidth = 0, bool IsUnnamedBitField = false, bool HasNoUniqueAddress = false, - bool IsVTablePointer = false, bool IsVirtualBase = false) + bool IsVirtualBase = false) : FieldType(FieldType), OffsetInBits(OffsetInBits), BitFieldWidth(BitFieldWidth), IsBitField(IsBitField), IsUnnamedBitfield(IsUnnamedBitField), HasNoUniqueAddress(HasNoUniqueAddress), - IsVTablePointer(IsVTablePointer), IsVirtualBase(IsVirtualBase) {} + IsVirtualBase(IsVirtualBase) {} LLVM_ABI bool isEmpty() const; }; diff --git a/llvm/lib/ABI/Types.cpp b/llvm/lib/ABI/Types.cpp index 6c5684cf26eeb..09636189cdb50 100644 --- a/llvm/lib/ABI/Types.cpp +++ b/llvm/lib/ABI/Types.cpp @@ -22,13 +22,10 @@ bool RecordType::isEmpty() const { return false; } - for (const FieldInfo &FI : getFields()) { - // Don't treat vtable pointers as a source field for emptiness. - if (FI.IsVTablePointer) - continue; + for (const FieldInfo &FI : getFields()) if (!FI.isEmpty()) return false; - } + return true; } diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp index ad448882b82b1..31ba4aa8ef423 100644 --- a/llvm/unittests/ABI/TypesTest.cpp +++ b/llvm/unittests/ABI/TypesTest.cpp @@ -103,36 +103,31 @@ TEST_F(ABITypesTest, DirectVirtualBasesAndVTablePointer) { {FieldInfo(TB.getIntegerType(32, Align(4), /*Signed=*/true), 0)}, 32, CXXFlags); const llvm::abi::Type *VPtr = TB.getPointerType(64, Align(8)); - FieldInfo VTable(VPtr, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0, - /*IsUnnamedBitField=*/false, - /*HasNoUniqueAddress=*/false, - /*IsVTablePointer=*/true); + FieldInfo VTable(VPtr, 0); // Direct virtual bases appear in both the base-class list (with // IsVirtualBase) and the virtual-base list, matching CXXRecordDecl::bases() // and vbases(). FieldInfo EmptyVBase(Empty, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, /*HasNoUniqueAddress=*/false, - /*IsVTablePointer=*/false, /*IsVirtualBase=*/true); FieldInfo NonEmptyVBase(IntField, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false, /*HasNoUniqueAddress=*/false, - /*IsVTablePointer=*/false, /*IsVirtualBase=*/true); - EXPECT_TRUE(makeRecord({}, 8, CXXFlags, /*Bases=*/{EmptyVBase}, - /*VBases=*/{EmptyVBase}) - ->isEmpty()); - EXPECT_FALSE(makeRecord({}, 32, CXXFlags, /*Bases=*/{NonEmptyVBase}, + EXPECT_FALSE(makeRecord({VTable}, 8, CXXFlags, /*Bases=*/{EmptyVBase}, + /*VBases=*/{EmptyVBase}) + ->isEmpty()); + EXPECT_FALSE(makeRecord({VTable}, 32, CXXFlags, /*Bases=*/{NonEmptyVBase}, /*VBases=*/{NonEmptyVBase}) ->isEmpty()); - EXPECT_TRUE(makeRecord({VTable}, 64, - static_cast<RecordFlags>(CXXFlags | - RecordFlags::IsPolymorphic), - {FieldInfo(Empty, 0)}) - ->isEmpty()); + EXPECT_FALSE(makeRecord({VTable}, 64, + static_cast<RecordFlags>(CXXFlags | + RecordFlags::IsPolymorphic), + {FieldInfo(Empty, 0)}) + ->isEmpty()); } } // namespace >From 5eafef12f03d1d6eb44a12ee61642f1d0c63d1f9 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 25 Aug 2026 12:50:43 -0700 Subject: [PATCH 4/4] Fix formatting --- llvm/include/llvm/ABI/Types.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h index 21a614e97f8bd..6cf70daa5bcbe 100644 --- a/llvm/include/llvm/ABI/Types.h +++ b/llvm/include/llvm/ABI/Types.h @@ -247,8 +247,7 @@ struct FieldInfo { : FieldType(FieldType), OffsetInBits(OffsetInBits), BitFieldWidth(BitFieldWidth), IsBitField(IsBitField), IsUnnamedBitfield(IsUnnamedBitField), - HasNoUniqueAddress(HasNoUniqueAddress), - IsVirtualBase(IsVirtualBase) {} + HasNoUniqueAddress(HasNoUniqueAddress), IsVirtualBase(IsVirtualBase) {} LLVM_ABI bool isEmpty() const; }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
