564 changes: 564 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 %s -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck %s
#include <typeinfo>

struct A { int a; };

// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
// CHECK: @_ZTS1A = linkonce_odr hidden constant [3 x i8] c"1A\00"
// CHECK: @_ZTI1A = linkonce_odr hidden constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1A to i64), i64 -9223372036854775808) to ptr) }

auto ATI = typeid(A);
28 changes: 28 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-thunks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s

namespace Test1 {
struct B1 {
virtual void* foo1() {
return 0;
}
};
struct Pad1 {
virtual ~Pad1() {}
};
struct Proxy1 : Pad1, B1 {
virtual ~Proxy1() {}
};
struct D : virtual Proxy1 {
virtual ~D() {}
virtual void* foo1();
};
void* D::foo1() {
return (void*)this;
}
}

// CHECK-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test11DD0Ev(ptr noundef %this)
// CHECK: %[[This:.*]] = load ptr
// CHECK: %[[SignedVTable:.*]] = load ptr, ptr %[[This]], align 8
// CHECK: %[[SignedVTableAsInt:.*]] = ptrtoint ptr %[[SignedVTable]] to i64
// CHECK: %[[VTable:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[SignedVTableAsInt]], i32 2, i64 0)
581 changes: 581 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-virtual-function.cpp

Large diffs are not rendered by default.

309 changes: 309 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp

Large diffs are not rendered by default.

48 changes: 46 additions & 2 deletions clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK-NULL --check-prefix=MSABI
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=ITANIUM
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=MSABI --check-prefix=CHECK-VPTR-MS
// RUN: %clang_cc1 -std=c++11 -triple arm64e-ios-13 -emit-llvm -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=ITANIUM --check-prefix=CHECK-PTRAUTH
struct T {
virtual ~T() {}
virtual int v() { return 1; }
Expand All @@ -26,18 +27,49 @@ int get_v(T* t) {
// CHECK-NULL: call void @__ubsan_handle_type_mismatch_v1_abort
// Second, we check that vtable is actually loaded once the type check is done.
// CHECK-NULL: load ptr, ptr {{.*}}

// CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable to i64
// CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0), !nosanitize !2
// CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr
// CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64
// Make sure authed vtable pointer feeds into hashing
// CHECK-PTRAUTH: {{%.*}} = mul i64 [[STRIPPED_INT]], {{.*}}

// Verify that we authenticate for the actual vcall
// CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 17113)
// CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable2 to i64
// CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]])
// CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr
// CHECK-PTRAUTH: {{%.*}} = getelementptr inbounds ptr, ptr [[AUTHED_PTR]], i64 2
return t->v();
}

// ITANIUM: define{{.*}} void @_Z9delete_itP1T
// MSABI: define dso_local void @"?delete_it
void delete_it(T *t) {
// First, we check that vtable is not loaded before a type check.
// CHECK-VPTR-NOT: load {{.*}} (ptr{{.*}})**, {{.*}} (ptr{{.*}})***
// CHECK-VPTR: br i1 {{.*}} label %{{.*}}
// CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
// CHECK-VPTR: call void @__ubsan_handle_type_mismatch_v1_abort
// Second, we check that vtable is actually loaded once the type check is done.
// CHECK-VPTR: load ptr, ptr {{.*}}

// First, we check that vtable is not loaded before a type check.
// CHECK-PTRAUTH: ptrtoint ptr {{%.*}} to i64
// CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr [[VTABLE:%.*]] to i64
// CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0)
// CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr
// CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64
// CHECK-PTRAUTH: {{%.*}} = mul i64 [[STRIPPED_INT]], {{.*}}
// CHECK-PTRAUTH: call void @__ubsan_handle_dynamic_type_cache_miss_abort(
// Second, we check that vtable is actually loaded once the type check is done.
// ptrauth for the virtual function load
// CHECK-PTRAUTH: [[VTABLE2:%.*]] = load ptr, ptr {{.*}}
// CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 %{{.*}}, i64 17113)
// CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr [[VTABLE2]] to i64
// CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]])
// CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr
// CHECK-PTRAUTH: getelementptr inbounds ptr, ptr
// CHECK-PTRAUTH {{%.*}} = getelementptr inbounds ptr, ptr [[AUTHED_PTR]], i64 1
delete t;
}

Expand All @@ -47,7 +79,19 @@ U* dyncast(T *t) {
// First, we check that dynamic_cast is not called before a type check.
// CHECK-VPTR-NOT: call ptr @__{{dynamic_cast|RTDynamicCast}}
// CHECK-VPTR: br i1 {{.*}} label %{{.*}}
// CHECK-PTRAUTH: [[V0:%.*]] = ptrtoint ptr {{%.*}} to i64
// CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[V0]], i64 17113)
// CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr {{%.*}} to i64
// CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0)
// CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr
// CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64
// CHECK-PTRAUTH: {{%.*}} = mul i64 [[STRIPPED_INT]], {{.*}}
// CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
// CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 17113)
// CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable1 to i64
// CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]])
// CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr
// CHECK-PTRAUTH: {{%.*}} = load volatile i8, ptr [[AUTHED_PTR]], align 8
// Second, we check that dynamic_cast is actually called once the type check is done.
// CHECK-VPTR: call ptr @__{{dynamic_cast|RTDynamicCast}}
return dynamic_cast<U*>(t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
// CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local)
// CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function)
// CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: VTablePointerAuthentication (SubjectMatchRule_record)
// CHECK-NEXT: VecReturn (SubjectMatchRule_record)
// CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
// CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -Wno-vla -fsyntax-only -verify -fptrauth-intrinsics -fptrauth-calls %s

struct Incomplete0; // expected-note 3 {{forward declaration of 'Incomplete0'}}

template <class T>
struct Incomplete1; // expected-note {{template is declared here}}

struct Complete0 {
};

template <class T>
struct Complete1 {
};

struct S {
virtual int foo();
virtual Incomplete0 virtual0(); // expected-note 2 {{'Incomplete0' is incomplete}}
virtual void virtual1(Incomplete1<int>); // expected-note {{'Incomplete1<int>' is incomplete}}
virtual Complete0 virtual2();
virtual Complete1<int> virtual3();
Incomplete0 nonvirtual0();
template <class T>
void m0() {
(void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}}
}
};

template <bool T>
struct S2 {
virtual Incomplete0 virtual0() noexcept(T); // expected-note {{'Incomplete0' is incomplete}}

void m0() {
(void)&S2<T>::virtual0;
}

void m1() {
(void)&S2<T>::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}}
}
};

void test_incomplete_virtual_member_function_return_arg_type() {
(void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0}} expected-note {{cannot take an address of a virtual member function}}
(void)&S::virtual1; // expected-error {{implicit instantiation of undefined template 'Incomplete1<int>'}} expected-note {{cannot take an address of a virtual member function}}
(void)&S::virtual2;
(void)&S::virtual3;
(void)&S::nonvirtual0;
int s = sizeof(&S::virtual0);
S2<true>().m1(); // expected-note {{in instantiation of}}
}

225 changes: 225 additions & 0 deletions clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// RUN: %clang_cc1 -fsyntax-only -triple arm64-apple-ios -verify -fptrauth-calls -std=c++2a %s

namespace basic {

#define authenticated(a, b, c...) [[clang::ptrauth_vtable_pointer(a, b, c)]]

// Basic sanity tests
#define TEST_AUTH(name, auth...) \
struct [[clang::ptrauth_vtable_pointer(auth)]] name { \
virtual ~name() {} \
}

TEST_AUTH(NoParams);
// expected-error@-1{{'ptrauth_vtable_pointer' attribute takes at least 3 arguments}}
TEST_AUTH(NoAuth, no_authentication, default_address_discrimination, default_extra_discrimination);
TEST_AUTH(InvalidKey, wat, default_address_discrimination, default_extra_discrimination);
// expected-error@-1{{invalid authentication key 'wat'}}
TEST_AUTH(InvalidAddressDiscrimination, no_authentication, wat, default_extra_discrimination);
// expected-error@-1{{invalid address discrimination mode 'wat'}}
TEST_AUTH(InvalidExtraDiscrimination, no_authentication, default_address_discrimination, wat);
// expected-error@-1{{invalid extra discrimination selection 'wat'}}
TEST_AUTH(InvalidNoCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination);
// expected-error@-1{{missing custom discrimination}}
TEST_AUTH(InvalidCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination, wat);
// expected-error@-1{{invalid custom discrimination}}
TEST_AUTH(Default, default_key, default_address_discrimination, default_extra_discrimination);
TEST_AUTH(InvalidDefaultExtra, default_key, default_address_discrimination, default_extra_discrimination, 1);
// expected-error@-1{{'ptrauth_vtable_pointer' attribute takes no more than 3 arguments}}
TEST_AUTH(ProcessDependentKey, process_dependent, default_address_discrimination, default_extra_discrimination);
TEST_AUTH(ProcessIndependentKey, process_independent, default_address_discrimination, default_extra_discrimination);
TEST_AUTH(DefaultAddressDiscrimination, process_independent, default_address_discrimination, default_extra_discrimination);
TEST_AUTH(NoAddressDiscrimination, process_independent, no_address_discrimination, default_extra_discrimination);
TEST_AUTH(AddressDiscrimination, process_independent, address_discrimination, default_extra_discrimination);
TEST_AUTH(DefaultExtraDiscrimination, process_independent, default_address_discrimination, default_extra_discrimination);
TEST_AUTH(NoExtraDiscrimination, process_independent, default_address_discrimination, no_extra_discrimination);
TEST_AUTH(TypeExtraDiscrimination, process_independent, default_address_discrimination, type_discrimination);
TEST_AUTH(InvalidCustomExtraDiscrimination, process_independent, default_address_discrimination, custom_discrimination);
// expected-error@-1{{missing custom discrimination}}
TEST_AUTH(ValidCustomExtraDiscrimination, process_independent, default_address_discrimination, custom_discrimination, 1);

// Basic valid authentication configuration
#define generic_authenticated \
authenticated(process_independent, address_discrimination, type_discrimination)

struct generic_authenticated ForwardDecl;

struct generic_authenticated generic_authenticated InvalidDuplicateAttribute {
// expected-error@-1{{multiple vtable pointer authentication policies on 'InvalidDuplicateAttribute'}}
virtual ~InvalidDuplicateAttribute(){};
};
struct generic_authenticated ValidPolymorphic {
virtual ~ValidPolymorphic(){};
};
struct generic_authenticated InvalidMonomorphic { // expected-error{{cannot set vtable pointer authentication on monomorphic type 'InvalidMonomorphic'}}
};
struct ValidMonomorphic {
};

struct ValidSubclass : ValidPolymorphic {};
struct generic_authenticated InvalidSubclass : ValidPolymorphic {}; // expected-error{{cannot set vtable pointer authentication on 'InvalidSubclass' which is a subclass of polymorphic type 'ValidPolymorphic'}}

// Awful template time
template <typename T>
struct generic_authenticated ExplicitlyAuthedMonomorphicTemplateClass : T {};
// expected-error@-1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidPolymorphic>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
// expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidMonomorphic>'}}
template <typename T>
struct generic_authenticated ExplicitlyAuthedPolymorphicTemplateClass : T { // expected-error{{cannot set vtable pointer authentication on 'ExplicitlyAuthedPolymorphicTemplateClass<basic::ValidPolymorphic>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
virtual ~ExplicitlyAuthedPolymorphicTemplateClass(){};
};
template <typename T>
struct UnauthedMonomorphicTemplateClass : T {};
template <typename T>
struct UnauthedPolymorphicTemplateClass : T {
virtual ~UnauthedPolymorphicTemplateClass(){};
};

ExplicitlyAuthedMonomorphicTemplateClass<ValidPolymorphic> test1;
// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidPolymorphic>' requested here}}
ExplicitlyAuthedMonomorphicTemplateClass<ValidMonomorphic> test2;
// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidMonomorphic>' requested here}}
ExplicitlyAuthedPolymorphicTemplateClass<ValidPolymorphic> test3;
// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedPolymorphicTemplateClass<basic::ValidPolymorphic>' requested here}}
ExplicitlyAuthedPolymorphicTemplateClass<ValidMonomorphic> test4;

UnauthedMonomorphicTemplateClass<ValidPolymorphic> test5;
UnauthedMonomorphicTemplateClass<ValidMonomorphic> test6;
UnauthedPolymorphicTemplateClass<ValidPolymorphic> test7;
UnauthedPolymorphicTemplateClass<ValidMonomorphic> test8;

// Just use a different policy from the generic macro to verify we won't complain
// about the insanity
struct authenticated(process_independent, no_address_discrimination, type_discrimination) SecondAuthenticatedPolymorphic {
virtual ~SecondAuthenticatedPolymorphic(){};
};
struct UnauthenticatedPolymorphic {
virtual ~UnauthenticatedPolymorphic(){};
};

struct MultipleParents1 : ValidPolymorphic, SecondAuthenticatedPolymorphic, UnauthenticatedPolymorphic {};
struct MultipleParents2 : UnauthenticatedPolymorphic, ValidPolymorphic, SecondAuthenticatedPolymorphic {};
struct generic_authenticated InvalidMultipleParents : UnauthenticatedPolymorphic, ValidPolymorphic, SecondAuthenticatedPolymorphic {};
// expected-error@-1{{cannot set vtable pointer authentication on 'InvalidMultipleParents' which is a subclass of polymorphic type 'UnauthenticatedPolymorphic'}}

template <typename T>
struct generic_authenticated ExplicitlyAuthedPolymorphicTemplateClassNoBase {
virtual ~ExplicitlyAuthedPolymorphicTemplateClassNoBase();
};

ExplicitlyAuthedPolymorphicTemplateClassNoBase<int> v;

struct ValidSubclassOfTemplate : ExplicitlyAuthedPolymorphicTemplateClassNoBase<int> {
};

struct generic_authenticated InvalidSubclassOfTemplate : ExplicitlyAuthedPolymorphicTemplateClassNoBase<int> {
// expected-error@-1{{cannot set vtable pointer authentication on 'InvalidSubclassOfTemplate' which is a subclass of polymorphic type 'ExplicitlyAuthedPolymorphicTemplateClassNoBase<int>'}}
};

template <typename T>
struct generic_authenticated ExplicitlyAuthedMonomorphicTemplateClassNoBase {
// expected-error@-1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase'}}
// expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase<int>'}}
};

ExplicitlyAuthedMonomorphicTemplateClassNoBase<int> X;
// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClassNoBase<int>' requested here}}

template <typename T>
struct generic_authenticated ExplicitlyAuthedTemplateClassValidBase : ValidMonomorphic {
// expected-error@-1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase'}}
// expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase<int>'}}
};

ExplicitlyAuthedTemplateClassValidBase<int> Y;
// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassValidBase<int>' requested here}}

template <typename T>
struct generic_authenticated ExplicitlyAuthedTemplateClassInvalidBase : ValidPolymorphic {
// expected-error@-1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase' which is a subclass of polymorphic type 'ValidPolymorphic'}}
// expected-error@-2{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase<int>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
};

ExplicitlyAuthedTemplateClassInvalidBase<int> Z;
// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassInvalidBase<int>' requested here}}

template <class test1, class test2>
class generic_authenticated TestPolymorphicTemplateSpecialization;

template <>
class TestPolymorphicTemplateSpecialization<double, float> {
MissingDecl *zl;
// expected-error@-1 {{unknown type name 'MissingDecl'}}
public:
virtual ~TestPolymorphicTemplateSpecialization();
};
template <class test1>
class generic_authenticated TestPolymorphicTemplateSpecialization<test1, double>
// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization<test1, double>'}}
// expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization<double, double>'}}
{
};

TestPolymorphicTemplateSpecialization<double, float> b;
TestPolymorphicTemplateSpecialization<double, double> b2;
// expected-note@-1 {{in instantiation of template class 'basic::TestPolymorphicTemplateSpecialization<double, double>' requested here}}

template <typename A> class generic_authenticated TestMonomorphic {};
// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic'}}
// expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic<double>'}}

template <> class generic_authenticated TestMonomorphic<int> {
public:
virtual ~TestMonomorphic();
};

struct TestMonomorphicSubclass : TestMonomorphic<int> {
};
template <typename T> struct generic_authenticated TestMonomorphicSubclass2 : TestMonomorphic<T> {
// expected-error@-1 {{cannot set vtable pointer authentication on 'TestMonomorphicSubclass2<int>' which is a subclass of polymorphic type 'TestMonomorphic<int>'}}
// expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphicSubclass2<double>'}}
// expected-note@-3 {{in instantiation of template class 'basic::TestMonomorphic<double>' requested here}}
};

TestMonomorphicSubclass tms_1;
TestMonomorphicSubclass2<int> tms2_1;
// expected-note@-1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<int>' requested here}}
TestMonomorphicSubclass2<double> tms2_2;
// expected-note@-1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<double>' requested here}}
// expected-note@-2 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<double>' requested here}}

template <typename T>
class generic_authenticated dependent_type {
// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'dependent_type'}}
static constexpr unsigned small_object_size = 1;
char _model[small_object_size];
};

template <typename... T>
class generic_authenticated dependent_type2 : public T... {
// expected-error@-1 {{cannot set vtable pointer authentication on 'dependent_type2<basic::Foo>' which is a subclass of polymorphic type 'Foo'}}
static constexpr unsigned small_object_size = 1;
char _model[small_object_size];
};

struct Foo {
virtual ~Foo();
};

dependent_type2<Foo> thing;
// expected-note@-1 {{in instantiation of template class 'basic::dependent_type2<basic::Foo>' requested here}}

template <class>
class task;
template <unsigned align> struct alignedthing {
char buffer[align];
};

template <class R, class... Args>
class generic_authenticated task<R(Args...)> {
// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'task<R (Args...)>'}}
static constexpr __SIZE_TYPE__ small_object_size = 256;
alignedthing<small_object_size> _model;
};

} // namespace basic
27 changes: 27 additions & 0 deletions clang/utils/TableGen/ClangAttrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2578,6 +2578,32 @@ static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &O
OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n";
}

// Emits the indexed-argument-is-identifier property for attributes.
static void emitClangAttrStrictIdentifierArgAtIndexList(RecordKeeper &Records,
raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST)\n";
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");

for (const auto *Attr : Attrs) {
if (!Attr->getValueAsBit("StrictEnumParameters"))
continue;
// Determine whether the first argument is an identifier.
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
uint64_t enumAtIndex = 0;
for (size_t i = 0; i < Args.size(); i++) {
enumAtIndex |= ((uint64_t)isIdentifierArgument(Args[0])) << i;
}
if (!enumAtIndex)
continue;

// All these spellings take an identifier argument.
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", " << enumAtIndex << "ull)\n";
});
}
OS << "#endif // CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST\n\n";
}

static bool keywordThisIsaIdentifierInArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(
Expand Down Expand Up @@ -4959,6 +4985,7 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) {
emitClangAttrTypeArgList(Records, OS);
emitClangAttrLateParsedList(Records, OS);
emitClangAttrLateParsedExperimentalList(Records, OS);
emitClangAttrStrictIdentifierArgAtIndexList(Records, OS);
}

void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
Expand Down