https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/143969
>From f47680310ed68a9f8e8cb15bc1cd474740072463 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <oli...@apple.com> Date: Thu, 12 Jun 2025 13:17:11 -0700 Subject: [PATCH 1/2] [clang] Fix PointerAuth semantics of cpp_trivially_relocatable This adds a function to ASTContext to query whether a type contains values with address discriminated pointer auth, and performs the required semantic checks to ensure correct reporting of relocatablity in those cases. For the standardized version, __builtin_is_cpp_trivially_relocatable this means rejecting unions of types containing address discriminated values. For the old deprecated __builtin_is_trivially_relocatable this means rejecting any type containing an address discriminated value. This PR does not update the codegen for __builtin_trivially_relocate, that will be in a follow on PR that is much more complex. --- clang/include/clang/AST/ASTContext.h | 4 + clang/lib/AST/ASTContext.cpp | 49 +++++++++ clang/lib/Sema/SemaTypeTraits.cpp | 14 +-- .../SemaCXX/cxx2c-trivially-relocatable.cpp | 1 + clang/test/SemaCXX/ptrauth-triviality.cpp | 10 +- .../SemaCXX/trivially-relocatable-ptrauth.cpp | 102 ++++++++++++++++++ 6 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 8d24d393eab09..826f5257b0463 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -628,10 +628,13 @@ class ASTContext : public RefCountedBase<ASTContext> { getRelocationInfoForCXXRecord(const CXXRecordDecl *) const; void setRelocationInfoForCXXRecord(const CXXRecordDecl *, CXXRecordDeclRelocationInfo); + bool containsAddressDiscriminatedPointerAuth(QualType T); private: llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo> RelocatableClasses; + llvm::DenseMap<const RecordDecl *, bool> + RecordContainsAddressDiscriminatedPointerAuth; ImportDecl *FirstLocalImport = nullptr; ImportDecl *LastLocalImport = nullptr; @@ -3668,6 +3671,7 @@ OPT_LIST(V) /// authentication policy for the specified record. const CXXRecordDecl * baseForVTableAuthentication(const CXXRecordDecl *ThisClass); + bool hasAddressDiscriminatedVTableAuthentication(const CXXRecordDecl *Class); bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, StringRef MangledName); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b51f7622288df..34b540fd36efc 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1705,6 +1705,40 @@ void ASTContext::setRelocationInfoForCXXRecord( RelocatableClasses.insert({D, Info}); } +bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) { + if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthIntrinsics) + return false; + + T = T.getCanonicalType(); + if (T.hasAddressDiscriminatedPointerAuth()) + return true; + const RecordDecl *RD = T->getAsRecordDecl(); + if (!RD) + return false; + + auto SaveReturn = [this, RD](bool Result) { + RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result}); + return Result; + }; + if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD); + Existing != RecordContainsAddressDiscriminatedPointerAuth.end()) + return Existing->second; + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + if (CXXRD->isPolymorphic() && + hasAddressDiscriminatedVTableAuthentication(CXXRD)) + return SaveReturn(true); + for (auto Base : CXXRD->bases()) { + if (containsAddressDiscriminatedPointerAuth(Base.getType())) + return SaveReturn(true); + } + } + for (auto *FieldDecl : RD->fields()) { + if (containsAddressDiscriminatedPointerAuth(FieldDecl->getType())) + return SaveReturn(true); + } + return SaveReturn(false); +} + void ASTContext::addedLocalImportDecl(ImportDecl *Import) { assert(!Import->getNextLocalImport() && "Import declaration already in the chain"); @@ -15121,6 +15155,21 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) { return PrimaryBase; } +bool ASTContext::hasAddressDiscriminatedVTableAuthentication( + const CXXRecordDecl *Class) { + assert(Class->isPolymorphic()); + const CXXRecordDecl *BaseType = baseForVTableAuthentication(Class); + using AuthAttr = VTablePointerAuthenticationAttr; + const auto *ExplicitAuth = BaseType->getAttr<AuthAttr>(); + if (!ExplicitAuth) + return LangOpts.PointerAuthVTPtrAddressDiscrimination; + AuthAttr::AddressDiscriminationMode AddressDiscrimination = + ExplicitAuth->getAddressDiscrimination(); + if (AddressDiscrimination == AuthAttr::DefaultAddressDiscrimination) + return LangOpts.PointerAuthVTPtrAddressDiscrimination; + return AddressDiscrimination == AuthAttr::AddressDiscrimination; +} + bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, StringRef MangledName) { auto *Method = cast<CXXMethodDecl>(VirtualMethodDecl.getDecl()); diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 1738ab4466001..43af236068655 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -188,15 +188,20 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef, return false; } + bool IsUnion = D->isUnion(); for (const FieldDecl *Field : D->fields()) { - if (Field->getType()->isDependentType()) + QualType FieldType = Field->getType(); + if (FieldType->isDependentType()) continue; - if (Field->getType()->isReferenceType()) + if (FieldType->isReferenceType()) continue; // ... has a non-static data member of an object type that is not // of a trivially relocatable type if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType())) return false; + if (IsUnion && + SemaRef.Context.containsAddressDiscriminatedPointerAuth(FieldType)) + return false; } return !D->hasDeletedDestructor(); } @@ -322,9 +327,6 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) { if (BaseElementType.hasNonTrivialObjCLifetime()) return false; - if (BaseElementType.hasAddressDiscriminatedPointerAuth()) - return false; - if (BaseElementType->isIncompleteType()) return false; @@ -670,7 +672,7 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) { if (!BaseElementType->isObjectType()) return false; - if (T.hasAddressDiscriminatedPointerAuth()) + if (SemaRef.getASTContext().containsAddressDiscriminatedPointerAuth(T)) return false; if (const auto *RD = BaseElementType->getAsCXXRecordDecl(); diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp index 9d43994ee7661..7152a5937d9b7 100644 --- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp +++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++2c -verify %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-intrinsics -fptrauth-calls -std=c++2c -verify %s class Trivial {}; static_assert(__builtin_is_cpp_trivially_relocatable(Trivial)); diff --git a/clang/test/SemaCXX/ptrauth-triviality.cpp b/clang/test/SemaCXX/ptrauth-triviality.cpp index 60d1b57230f18..6f3650f7ac2e3 100644 --- a/clang/test/SemaCXX/ptrauth-triviality.cpp +++ b/clang/test/SemaCXX/ptrauth-triviality.cpp @@ -26,7 +26,7 @@ static_assert(!__is_trivially_assignable(S1, const S1&)); static_assert(__is_trivially_destructible(S1)); static_assert(!__is_trivially_copyable(S1)); static_assert(!__is_trivially_relocatable(S1)); // expected-warning{{deprecated}} -static_assert(!__builtin_is_cpp_trivially_relocatable(S1)); +static_assert(__builtin_is_cpp_trivially_relocatable(S1)); static_assert(!__is_trivially_equality_comparable(S1)); static_assert(__is_trivially_constructible(Holder<S1>)); @@ -35,7 +35,7 @@ static_assert(!__is_trivially_assignable(Holder<S1>, const Holder<S1>&)); static_assert(__is_trivially_destructible(Holder<S1>)); static_assert(!__is_trivially_copyable(Holder<S1>)); static_assert(!__is_trivially_relocatable(Holder<S1>)); // expected-warning{{deprecated}} -static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S1>)); +static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S1>)); static_assert(!__is_trivially_equality_comparable(Holder<S1>)); struct S2 { @@ -83,7 +83,7 @@ static_assert(!__is_trivially_constructible(Holder<S3>, const Holder<S3>&)); static_assert(!__is_trivially_assignable(Holder<S3>, const Holder<S3>&)); static_assert(__is_trivially_destructible(Holder<S3>)); static_assert(!__is_trivially_copyable(Holder<S3>)); -static_assert(__is_trivially_relocatable(Holder<S3>)); // expected-warning{{deprecated}} +static_assert(!__is_trivially_relocatable(Holder<S3>)); // expected-warning{{deprecated}} static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S3>)); static_assert(!__is_trivially_equality_comparable(Holder<S3>)); @@ -148,7 +148,7 @@ static_assert(!__is_trivially_assignable(S6, const S6&)); static_assert(__is_trivially_destructible(S6)); static_assert(!__is_trivially_copyable(S6)); static_assert(!__is_trivially_relocatable(S6)); // expected-warning{{deprecated}} -static_assert(!__builtin_is_cpp_trivially_relocatable(S6)); +static_assert(__builtin_is_cpp_trivially_relocatable(S6)); static_assert(!__is_trivially_equality_comparable(S6)); static_assert(__is_trivially_constructible(Holder<S6>)); @@ -157,7 +157,7 @@ static_assert(!__is_trivially_assignable(Holder<S6>, const Holder<S6>&)); static_assert(__is_trivially_destructible(Holder<S6>)); static_assert(!__is_trivially_copyable(Holder<S6>)); static_assert(!__is_trivially_relocatable(Holder<S6>)); // expected-warning{{deprecated}} -static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S6>)); +static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S6>)); static_assert(!__is_trivially_equality_comparable(Holder<S6>)); struct S7 { diff --git a/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp new file mode 100644 index 0000000000000..29722fadd4d17 --- /dev/null +++ b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -triple arm64 -fptrauth-calls -fptrauth-intrinsics -std=c++26 -verify %s + +// This test intentionally does not enable the global address discrimination +// of vtable pointers. This lets us configure them with different schemas +// and verify that we're correctly tracking the existence of address discrimination + +// expected-no-diagnostics + +struct NonAddressDiscPtrauth { + void * __ptrauth(1, 0, 1234) p; +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscPtrauth)); + +struct AddressDiscPtrauth { + void * __ptrauth(1, 1, 1234) p; +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(AddressDiscPtrauth)); + +struct MultipleBaseClasses : NonAddressDiscPtrauth, AddressDiscPtrauth { + +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(MultipleBaseClasses)); + +struct MultipleMembers { + NonAddressDiscPtrauth field0; + AddressDiscPtrauth field1; +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(MultipleMembers)); + +struct UnionOfPtrauth { + union { + NonAddressDiscPtrauth field0; + AddressDiscPtrauth field1; + } u; +}; + +static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPtrauth)); + +struct [[clang::ptrauth_vtable_pointer(process_independent,address_discrimination,no_extra_discrimination)]] Polymorphic trivially_relocatable_if_eligible { + virtual ~Polymorphic(); +}; + +struct Foo : Polymorphic { + Foo(const Foo&); + ~Foo(); +}; + + +static_assert(__builtin_is_cpp_trivially_relocatable(Polymorphic)); + +struct [[clang::ptrauth_vtable_pointer(process_independent,no_address_discrimination,no_extra_discrimination)]] NonAddressDiscriminatedPolymorphic trivially_relocatable_if_eligible { + virtual ~NonAddressDiscriminatedPolymorphic(); +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscriminatedPolymorphic)); + + +struct PolymorphicMembers { + Polymorphic field; +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(PolymorphicMembers)); + +struct UnionOfPolymorphic { + union trivially_relocatable_if_eligible { + Polymorphic p; + int i; + } u; +}; + +static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPolymorphic)); + + +struct UnionOfNonAddressDiscriminatedPolymorphic { + union trivially_relocatable_if_eligible { + NonAddressDiscriminatedPolymorphic p; + int i; + } u; +}; +static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPolymorphic)); + +struct UnionOfNonAddressDiscriminatedPtrauth { + union { + NonAddressDiscPtrauth p; + int i; + } u; +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPtrauth)); + +struct UnionOfAddressDisriminatedPtrauth { + union { + AddressDiscPtrauth p; + int i; + } u; +}; + +static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfAddressDisriminatedPtrauth)); >From 3190a524391c5f31f957ef369359b1f631c32e07 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <oli...@apple.com> Date: Thu, 12 Jun 2025 17:23:41 -0700 Subject: [PATCH 2/2] Add comments and more tests --- clang/include/clang/AST/ASTContext.h | 20 +++++++++++ clang/lib/AST/ASTContext.cpp | 14 ++++---- clang/lib/Sema/SemaTypeTraits.cpp | 16 +++++---- clang/test/SemaCXX/ptrauth-triviality.cpp | 42 ++++++++++++++++++++--- 4 files changed, 76 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 826f5257b0463..68b8d67ab4828 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -628,11 +628,25 @@ class ASTContext : public RefCountedBase<ASTContext> { getRelocationInfoForCXXRecord(const CXXRecordDecl *) const; void setRelocationInfoForCXXRecord(const CXXRecordDecl *, CXXRecordDeclRelocationInfo); + + /// Examines a given type, and returns whether the T itself + /// is address discriminated, or any transitively embedded types + /// contain data that is address discriminated. This includes + /// implicitly authenticated values like vtable pointers, as well as + /// explicitly qualified fields. bool containsAddressDiscriminatedPointerAuth(QualType T); private: + // A simple helper function to short circuit pointer auth checks. + bool isPointerAuthenticationAvailable() const { + return LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics || + LangOpts.PointerAuthVTPtrAddressDiscrimination; + } + llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo> RelocatableClasses; + + // FIXME: store in RecordDeclBitfields in future? llvm::DenseMap<const RecordDecl *, bool> RecordContainsAddressDiscriminatedPointerAuth; @@ -3671,7 +3685,13 @@ OPT_LIST(V) /// authentication policy for the specified record. const CXXRecordDecl * baseForVTableAuthentication(const CXXRecordDecl *ThisClass); + + /// If this class is polymorphic, returns true if any of this class's + /// vtable pointers have an address discriminated pointer authentication + /// schema. + /// This does not check fields of the class or base classes. bool hasAddressDiscriminatedVTableAuthentication(const CXXRecordDecl *Class); + bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, StringRef MangledName); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 34b540fd36efc..6db95ac106703 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1706,7 +1706,7 @@ void ASTContext::setRelocationInfoForCXXRecord( } bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) { - if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthIntrinsics) + if (!isPointerAuthenticationAvailable()) return false; T = T.getCanonicalType(); @@ -1716,13 +1716,14 @@ bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) { if (!RD) return false; + if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD); + Existing != RecordContainsAddressDiscriminatedPointerAuth.end()) + return Existing->second; + auto SaveReturn = [this, RD](bool Result) { RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result}); return Result; }; - if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD); - Existing != RecordContainsAddressDiscriminatedPointerAuth.end()) - return Existing->second; if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { if (CXXRD->isPolymorphic() && hasAddressDiscriminatedVTableAuthentication(CXXRD)) @@ -15157,10 +15158,11 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) { bool ASTContext::hasAddressDiscriminatedVTableAuthentication( const CXXRecordDecl *Class) { - assert(Class->isPolymorphic()); + if (!isPointerAuthenticationAvailable() || !Class->isPolymorphic()) + return false; const CXXRecordDecl *BaseType = baseForVTableAuthentication(Class); using AuthAttr = VTablePointerAuthenticationAttr; - const auto *ExplicitAuth = BaseType->getAttr<AuthAttr>(); + const AuthAttr *ExplicitAuth = BaseType->getAttr<AuthAttr>(); if (!ExplicitAuth) return LangOpts.PointerAuthVTPtrAddressDiscrimination; AuthAttr::AddressDiscriminationMode AddressDiscrimination = diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 43af236068655..a0b989fc33b2a 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -190,17 +190,19 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef, bool IsUnion = D->isUnion(); for (const FieldDecl *Field : D->fields()) { - QualType FieldType = Field->getType(); - if (FieldType->isDependentType()) + if (Field->getType()->isDependentType()) continue; - if (FieldType->isReferenceType()) + if (Field->getType()->isReferenceType()) continue; // ... has a non-static data member of an object type that is not // of a trivially relocatable type if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType())) return false; - if (IsUnion && - SemaRef.Context.containsAddressDiscriminatedPointerAuth(FieldType)) + + // A union contains values with address discriminated pointer auth + // cannot be relocated. + if (IsUnion && SemaRef.Context.containsAddressDiscriminatedPointerAuth( + Field->getType())) return false; } return !D->hasDeletedDestructor(); @@ -318,7 +320,6 @@ bool Sema::IsCXXTriviallyRelocatableType(const CXXRecordDecl &RD) { } bool Sema::IsCXXTriviallyRelocatableType(QualType Type) { - QualType BaseElementType = getASTContext().getBaseElementType(Type); if (Type->isVariableArrayType()) @@ -672,6 +673,9 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) { if (!BaseElementType->isObjectType()) return false; + // The deprecated __builtin_is_trivially_relocatable does not have + // an equivalent to __builtin_trivially_relocate, so there is no + // safe way to use it if there are any address discriminated values. if (SemaRef.getASTContext().containsAddressDiscriminatedPointerAuth(T)) return false; diff --git a/clang/test/SemaCXX/ptrauth-triviality.cpp b/clang/test/SemaCXX/ptrauth-triviality.cpp index 6f3650f7ac2e3..2c95e93239790 100644 --- a/clang/test/SemaCXX/ptrauth-triviality.cpp +++ b/clang/test/SemaCXX/ptrauth-triviality.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++20 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++26 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++26 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s #define AQ __ptrauth(1,1,50) #define IQ __ptrauth(1,0,50) @@ -99,7 +99,6 @@ static_assert(!__is_trivially_assignable(S4, const S4&)); static_assert(__is_trivially_destructible(S4)); static_assert(!__is_trivially_copyable(S4)); static_assert(!__is_trivially_relocatable(S4)); // expected-warning{{deprecated}} -//FIXME static_assert(__builtin_is_cpp_trivially_relocatable(S4)); static_assert(!__is_trivially_equality_comparable(S4)); @@ -124,7 +123,6 @@ static_assert(!__is_trivially_assignable(S5, const S5&)); static_assert(__is_trivially_destructible(S5)); static_assert(!__is_trivially_copyable(S5)); static_assert(!__is_trivially_relocatable(S5)); // expected-warning{{deprecated}} -//FIXME static_assert(__builtin_is_cpp_trivially_relocatable(S5)); static_assert(!__is_trivially_equality_comparable(S5)); @@ -182,3 +180,39 @@ static_assert(__is_trivially_copyable(Holder<S7>)); static_assert(__is_trivially_relocatable(Holder<S7>)); // expected-warning{{deprecated}} static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S7>)); static_assert(__is_trivially_equality_comparable(Holder<S7>)); + +template <class... Bases> struct MultipleInheriter : Bases... { +}; + +template <class T> static const bool test_is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T); +template <class... Types> static const bool multiple_inheritance_is_relocatable = test_is_trivially_relocatable_v<MultipleInheriter<Types...>>; +template <class... Types> static const bool inheritance_relocatability_matches_bases_v = + (test_is_trivially_relocatable_v<Types> && ...) == multiple_inheritance_is_relocatable<Types...>; + +static_assert(multiple_inheritance_is_relocatable<S4, S5> == multiple_inheritance_is_relocatable<S5, S4>); +static_assert(inheritance_relocatability_matches_bases_v<S4, S5>); +static_assert(inheritance_relocatability_matches_bases_v<S5, S4>); + +struct AA AddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible { + virtual void foo(); +}; + +struct IA NoAddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible { + virtual void bar(); +}; + +template <class T> struct UnionWrapper trivially_relocatable_if_eligible { + union U { + T field1; + } u; +}; + +static_assert(test_is_trivially_relocatable_v<AddressDiscriminatedPolymorphicBase>); +static_assert(test_is_trivially_relocatable_v<NoAddressDiscriminatedPolymorphicBase>); +static_assert(inheritance_relocatability_matches_bases_v<AddressDiscriminatedPolymorphicBase, NoAddressDiscriminatedPolymorphicBase>); +static_assert(inheritance_relocatability_matches_bases_v<NoAddressDiscriminatedPolymorphicBase, AddressDiscriminatedPolymorphicBase>); + +static_assert(!test_is_trivially_relocatable_v<UnionWrapper<AddressDiscriminatedPolymorphicBase>>); +static_assert(test_is_trivially_relocatable_v<UnionWrapper<NoAddressDiscriminatedPolymorphicBase>>); +static_assert(!test_is_trivially_relocatable_v<UnionWrapper<MultipleInheriter<NoAddressDiscriminatedPolymorphicBase, AddressDiscriminatedPolymorphicBase>>>); +static_assert(!test_is_trivially_relocatable_v<UnionWrapper<MultipleInheriter<AddressDiscriminatedPolymorphicBase, NoAddressDiscriminatedPolymorphicBase>>>); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits