[clang] 93314bd - [clang][PAC] Add __builtin_get_vtable_pointer (#139790)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 4 00:21:24 PDT 2025
Author: Oliver Hunt
Date: 2025-06-04T00:21:20-07:00
New Revision: 93314bd9462d5c41a23fb402be7ae0c7d099e274
URL: https://github.com/llvm/llvm-project/commit/93314bd9462d5c41a23fb402be7ae0c7d099e274
DIFF: https://github.com/llvm/llvm-project/commit/93314bd9462d5c41a23fb402be7ae0c7d099e274.diff
LOG: [clang][PAC] Add __builtin_get_vtable_pointer (#139790)
With pointer authentication it becomes non-trivial to correctly load the
vtable pointer of a polymorphic object.
__builtin_get_vtable_pointer is a function that performs the load and
performs the appropriate authentication operations if necessary.
Added:
clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
clang/test/SemaCXX/builtin-get-vtable-pointer.cpp
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Builtins.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/Sema/SemaChecking.cpp
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e1929740356e7..73544826809c3 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3074,6 +3074,41 @@ following way:
Query for this feature with ``__has_builtin(__builtin_offsetof)``.
+``__builtin_get_vtable_pointer``
+--------------------------------
+
+``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
+pointer from an instance of a polymorphic C++ class. This builtin is needed
+for directly loading the vtable pointer when on platforms using
+:doc:`PointerAuthentication`.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_get_vtable_pointer(PolymorphicClass*)
+
+**Example of Use**:
+
+.. code-block:: c++
+
+ struct PolymorphicClass {
+ virtual ~PolymorphicClass();
+ };
+
+ PolymorphicClass anInstance;
+ const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
+
+**Description**:
+
+The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
+pointer from a polymorphic C++ type. If the target platform authenticates
+vtable pointers, this builtin will perform the authentication and produce
+the underlying raw pointer. The object being queried must be polymorphic,
+and so must also be a complete type.
+
+Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
+
``__builtin_call_with_static_chain``
------------------------------------
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d6b994d36df8a..512071427b65c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -317,6 +317,8 @@ Non-comprehensive list of changes in this release
``sizeof`` or ``typeof`` expression. (#GH138444)
- Deprecation warning is emitted for the deprecated ``__reference_binds_to_temporary`` intrinsic.
``__reference_constructs_from_temporary`` should be used instead. (#GH44056)
+- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a
+ polymorphic object.
New Compiler Flags
------------------
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index e43b87fb3c131..b15cde05410ab 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -970,6 +970,12 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
let Prototype = "bool(void*)";
}
+def GetVtablePointer : LangBuiltin<"CXX_LANG"> {
+ let Spellings = ["__builtin_get_vtable_pointer"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*)";
+}
+
// GCC exception builtins
def EHReturn : Builtin {
let Spellings = ["__builtin_eh_return"];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6f1e8d9fc74e6..c8c45438f969e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12803,6 +12803,14 @@ def err_bit_cast_non_trivially_copyable : Error<
def err_bit_cast_type_size_mismatch : Error<
"size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
+def err_get_vtable_pointer_incorrect_type
+ : Error<"__builtin_get_vtable_pointer requires an argument of%select{| "
+ "polymorphic}0 class pointer type"
+ ", but %1 %select{was provided|has no virtual methods}0">;
+def err_get_vtable_pointer_requires_complete_type
+ : Error<"__builtin_get_vtable_pointer requires an argument with a complete "
+ "type, but %0 is incomplete">;
+
// SYCL-specific diagnostics
def warn_sycl_kernel_num_of_template_params : Warning<
"'sycl_kernel' attribute only applies to a function template with at least"
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 369cff35b1bbf..dff23348729a2 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -17,6 +17,7 @@
#include "CGDebugInfo.h"
#include "CGObjCRuntime.h"
#include "CGOpenCLRuntime.h"
+#include "CGPointerAuthInfo.h"
#include "CGRecordLayout.h"
#include "CGValue.h"
#include "CodeGenFunction.h"
@@ -5621,6 +5622,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Result);
}
+ case Builtin::BI__builtin_get_vtable_pointer: {
+ const Expr *Target = E->getArg(0);
+ QualType TargetType = Target->getType();
+ const CXXRecordDecl *Decl = TargetType->getPointeeCXXRecordDecl();
+ assert(Decl);
+ auto ThisAddress = EmitPointerWithAlignment(Target);
+ assert(ThisAddress.isValid());
+ llvm::Value *VTablePointer =
+ GetVTablePtr(ThisAddress, Int8PtrTy, Decl, VTableAuthMode::MustTrap);
+ return RValue::get(VTablePointer);
+ }
+
case Builtin::BI__exception_code:
case Builtin::BI_exception_code:
return RValue::get(EmitSEHExceptionCode());
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 49593e55a6933..9dd2c6cd3bf54 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1829,6 +1829,40 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
return Call;
}
+static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 1))
+ return ExprError();
+ Expr *FirstArg = Call->getArg(0);
+ ExprResult FirstValue = S.DefaultFunctionArrayLvalueConversion(FirstArg);
+ if (FirstValue.isInvalid())
+ return ExprError();
+ Call->setArg(0, FirstValue.get());
+ QualType FirstArgType = FirstArg->getType();
+ if (FirstArgType->canDecayToPointerType() && FirstArgType->isArrayType())
+ FirstArgType = S.Context.getDecayedType(FirstArgType);
+
+ const CXXRecordDecl *FirstArgRecord = FirstArgType->getPointeeCXXRecordDecl();
+ if (!FirstArgRecord) {
+ S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
+ << /*isPolymorphic=*/0 << FirstArgType;
+ return ExprError();
+ }
+ if (S.RequireCompleteType(
+ FirstArg->getBeginLoc(), FirstArgType->getPointeeType(),
+ diag::err_get_vtable_pointer_requires_complete_type)) {
+ return ExprError();
+ }
+
+ if (!FirstArgRecord->isPolymorphic()) {
+ S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
+ << /*isPolymorphic=*/1 << FirstArgRecord;
+ return ExprError();
+ }
+ QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
+ Call->setType(ReturnType);
+ return Call;
+}
+
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
if (S.checkArgCount(TheCall, 1))
return ExprError();
@@ -2727,6 +2761,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return PointerAuthAuthAndResign(*this, TheCall);
case Builtin::BI__builtin_ptrauth_string_discriminator:
return PointerAuthStringDiscriminator(*this, TheCall);
+
+ case Builtin::BI__builtin_get_vtable_pointer:
+ return GetVTablePointer(*this, TheCall);
+
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
diff --git a/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
new file mode 100644
index 0000000000000..604fb6c5585ac
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
@@ -0,0 +1,370 @@
+// RUN: %clang_cc1 %s -x c++ -std=c++23 -triple x86_64-apple-darwin10 -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-NOAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++23 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-TYPEAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++23 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-ADDRESSAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++23 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-BOTHAUTH %s
+// FIXME: Assume load should not require -fstrict-vtable-pointers
+
+namespace test1 {
+struct A {
+ A();
+ virtual void bar();
+};
+
+struct B : A {
+ B();
+ virtual void foo();
+};
+
+struct Z : A {};
+struct C : Z, B {
+ C();
+ virtual void wibble();
+};
+
+struct D : virtual A {
+};
+
+struct E : D, B {
+};
+
+template <class A, class B> struct same_type {
+ static const bool value = false;
+};
+
+template <class A> struct same_type<A, A> {
+ static const bool value = true;
+};
+
+const void *a(A *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 {
+ // CHECK-TYPEAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %0 = load ptr, ptr %o.addr, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *b(B *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-TYPEAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 {
+ // CHECK-NOAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *b_as_A(B *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test16b_as_AEPNS_1BE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((A *)o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c(C *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test11cEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c_as_Z(C *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test16c_as_ZEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((Z *)o);
+ // CHECK-NOAUTH: %0 = load ptr, ptr %o.addr, align 8
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c_as_B(C *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test16c_as_BEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((B *)o);
+ // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 8
+ // CHECK-NOAUTH: br label %cast.end
+ // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-TYPEAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %cast.result to i64
+ // CHECK-ADDRESSAUTH: %3 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 2, i64 %2)
+ // CHECK-ADDRESSAUTH: %5 = inttoptr i64 %4 to ptr
+ // CHECK-ADDRESSAUTH: %6 = load volatile i8, ptr %5, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %2, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *d(D *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test11dEPNS_1DE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %1 = ptrtoint ptr %0 to i64
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *d_as_A(D *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test16d_as_AEPNS_1DE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((A *)o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-NOAUTH: %vbase.offset.ptr = getelementptr i8, ptr %vtable, i64 -32
+ // CHECK-NOAUTH: %vbase.offset = load i64, ptr %vbase.offset.ptr, align 8
+ // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 %vbase.offset
+ // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-NOAUTH: %vtable1 = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %vtable1 = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %5 = ptrtoint ptr %vtable1 to i64
+ // CHECK-TYPEAUTH: %6 = call i64 @llvm.ptrauth.auth(i64 %5, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %7 = inttoptr i64 %6 to ptr
+ // CHECK-TYPEAUTH: %8 = load volatile i8, ptr %7, align 8
+ // CHECK-ADDRESSAUTH: %6 = ptrtoint ptr %cast.result to i64
+ // CHECK-ADDRESSAUTH: %7 = ptrtoint ptr %vtable1 to i64
+ // CHECK-ADDRESSAUTH: %8 = call i64 @llvm.ptrauth.auth(i64 %7, i32 2, i64 %6)
+ // CHECK-ADDRESSAUTH: %9 = inttoptr i64 %8 to ptr
+ // CHECK-ADDRESSAUTH: %10 = load volatile i8, ptr %9, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %cast.result to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable1 to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *e(E *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test11eEPNS_1EE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]])
+ // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr
+ // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *e_as_B(E *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test16e_as_BEPNS_1EE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((B *)o);
+ // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 8
+ // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-TYPEAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %cast.result to i64
+ // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]])
+ // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr
+ // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %cast.result to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *e_as_D(E *o) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(o)), const void*>::value);
+ // CHECK-NOAUTH: define ptr @_ZN5test16e_as_DEPNS_1EE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((D *)o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]])
+ // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr
+ // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+extern "C" const void *aArrayParameter(A aArray[]) {
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(aArray)), const void*>::value);
+ // CHECK-NOAUTH: [[THIS_OBJ:%.*]] = load ptr, ptr %aArray.addr
+ // CHECK-NOAUTH: %vtable = load ptr, ptr [[THIS_OBJ]]
+ // CHECK-TYPEAUTH: [[THIS_OBJ:%.*]] = load ptr, ptr %aArray.addr
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr [[THIS_OBJ]]
+ // CHECK-TYPEAUTH: [[VTABLEI:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI]], i32 2, i64 48388)
+ // CHECK-ADDRESSAUTH: [[VTABLE_ADDR:%.*]] = load ptr, ptr %aArray.addr, align 8, !tbaa !2
+ // CHECK-ADDRESSAUTH: %vtable = load ptr, ptr %0, align 8, !tbaa !7
+ // CHECK-ADDRESSAUTH: [[VTABLE_ADDRI:%.*]] = ptrtoint ptr [[VTABLE_ADDR]] to i64
+ // CHECK-ADDRESSAUTH: [[VTABLEI:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI]], i32 2, i64 [[VTABLE_ADDRI]])
+ // CHECK-BOTHAUTH: [[VTABLE_ADDR:%.*]] = load ptr, ptr %aArray.addr, align 8, !tbaa !2
+ // CHECK-BOTHAUTH: %vtable = load ptr, ptr [[VTABLE_ADDR]], align 8, !tbaa !7
+ // CHECK-BOTHAUTH: [[VTABLE_ADDRI:%.*]] = ptrtoint ptr [[VTABLE_ADDR]] to i64
+ // CHECK-BOTHAUTH: [[VTABLE_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTABLE_ADDRI]], i64 48388)
+ // CHECK-BOTHAUTH: [[VTABLE_PTR:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLE_PTR]], i32 2, i64 [[VTABLE_DISC]])
+ return __builtin_get_vtable_pointer(aArray);
+}
+
+extern "C" const void *aArrayLocal() {
+ A array[] = { A() };
+ static_assert(same_type<decltype(__builtin_get_vtable_pointer(array)), const void*>::value);
+ // CHECK-NOAUTH: [[THIS_OBJ:%.*]] = getelementptr inbounds [1 x %"struct.test1::A"], ptr %array
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %arraydecay
+ // CHECK-TYPEAUTH: %arraydecay = getelementptr inbounds [1 x %"struct.test1::A"]
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %arraydecay
+ // CHECK-TYPEAUTH: [[VTABLEI:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI]], i32 2, i64 48388)
+ // CHECK-ADDRESSAUTH: %arraydecay = getelementptr inbounds [1 x %"struct.test1::A"], ptr %array, i64 0, i64 0
+ // CHECK-ADDRESSAUTH: %vtable = load ptr, ptr %arraydecay, align 8, !tbaa !7
+ // CHECK-ADDRESSAUTH: [[VTABLE_ADDRI:%.*]] = ptrtoint ptr %arraydecay to i64
+ // CHECK-ADDRESSAUTH: [[VTABLEI:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI]], i32 2, i64 [[VTABLE_ADDRI]])
+ // CHECK-BOTHAUTH: %arraydecay = getelementptr inbounds [1 x %"struct.test1::A"], ptr %array, i64 0, i64 0
+ // CHECK-BOTHAUTH: %vtable = load ptr, ptr %arraydecay, align 8, !tbaa !7
+ // CHECK-BOTHAUTH: [[VTABLE_ADDRI:%.*]] = ptrtoint ptr %arraydecay to i64
+ // CHECK-BOTHAUTH: [[VTABLE_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 %0, i64 48388)
+ // CHECK-BOTHAUTH: [[VTABLEI:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI]], i32 2, i64 [[VTABLE_DISC]])
+ return __builtin_get_vtable_pointer(array);
+}
+
+void test() {
+ A aInstance;
+ B bInstance;
+ C cInstance;
+ D dInstance;
+ E eInstance;
+ E eArray[] = { E() };
+ a(&aInstance);
+ a(&bInstance);
+ a((B *)&cInstance);
+ a(&dInstance);
+ a((D *)&eInstance);
+ a((B *)&eInstance);
+ b(&bInstance);
+ b(&cInstance);
+ b(&eInstance);
+ b_as_A(&bInstance);
+ c(&cInstance);
+ c_as_Z(&cInstance);
+ c_as_B(&cInstance);
+ d(&dInstance);
+ d(&eInstance);
+ d_as_A(&dInstance);
+ d_as_A(&eInstance);
+ e(&eInstance);
+ e_as_B(&eInstance);
+ e_as_D(&eInstance);
+ (void)__builtin_get_vtable_pointer(eArray);
+}
+} // namespace test1
diff --git a/clang/test/SemaCXX/builtin-get-vtable-pointer.cpp b/clang/test/SemaCXX/builtin-get-vtable-pointer.cpp
new file mode 100644
index 0000000000000..273f9c3b4c667
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-get-vtable-pointer.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s
+
+namespace basic {
+struct ForwardDeclaration; // expected-note{{forward declaration of 'basic::ForwardDeclaration'}}
+ // expected-note at -1{{forward declaration of 'basic::ForwardDeclaration'}}
+struct NonPolymorphic {};
+struct Polymorphic {
+ virtual ~Polymorphic();
+};
+
+template <typename T>
+struct Foo {
+ virtual ~Foo();
+};
+
+template <>
+struct Foo<int> {
+};
+
+template <typename T>
+struct Bar {
+ using SubType = typename T::SubType;
+ SubType *ty() const;
+};
+
+struct Thing1 {
+ using SubType = Thing1;
+};
+
+struct Thing2 {
+ using SubType = Thing2;
+ virtual ~Thing2();
+};
+
+struct Thing3 {
+ using SubType = int;
+};
+
+struct Thing4 {
+ using SubType = Polymorphic;
+};
+
+struct Thing5 {
+ using SubType = NonPolymorphic;
+};
+
+struct Thing6 {
+ using SubType = ForwardDeclaration;
+};
+
+template <typename T>
+const void *getThing(const Bar<T> *b = nullptr) {
+ return __builtin_get_vtable_pointer(b->ty()); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'SubType *' (aka 'int *') was provided}}
+ // expected-error at -1{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'Thing1' has no virtual methods}}
+ // expected-error at -2{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'NonPolymorphic' has no virtual methods}}
+ // expected-error at -3{{__builtin_get_vtable_pointer requires an argument with a complete type, but 'SubType' (aka 'basic::ForwardDeclaration') is incomplete}}
+}
+template <typename>
+struct IncompleteTemplate; // expected-note{{template is declared here}}
+template <typename>
+struct MonomorphicTemplate {
+};
+template <typename>
+struct PolymorphicTemplate {
+ virtual ~PolymorphicTemplate();
+};
+
+void test_function(int); // expected-note{{possible target for call}}
+ // expected-note at -1{{possible target for call}}
+void test_function(double); // expected-note{{possible target for call}}
+ // expected-note at -1{{possible target for call}}
+
+void getVTablePointer() {
+ ForwardDeclaration *fd = nullptr;
+ NonPolymorphic np;
+ Polymorphic p;
+ NonPolymorphic np_array[1];
+ Polymorphic p_array[1];
+ __builtin_get_vtable_pointer(0); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'int' was provided}}
+ __builtin_get_vtable_pointer(nullptr); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'std::nullptr_t' was provided}}
+ __builtin_get_vtable_pointer(0.5); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'double' was provided}}
+ __builtin_get_vtable_pointer(fd); // expected-error{{__builtin_get_vtable_pointer requires an argument with a complete type, but 'ForwardDeclaration' is incomplete}}
+ __builtin_get_vtable_pointer(np); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'NonPolymorphic' was provided}}
+ __builtin_get_vtable_pointer(&np); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'NonPolymorphic' has no virtual methods}}
+ __builtin_get_vtable_pointer(p); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'Polymorphic' was provided}}
+ __builtin_get_vtable_pointer(&p); // expected-warning{{ignoring return value of function declared with const attribute}}
+ __builtin_get_vtable_pointer(p_array); // expected-warning{{ignoring return value of function declared with const attribute}}
+ __builtin_get_vtable_pointer(&p_array); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'Polymorphic (*)[1]' was provided}}
+ __builtin_get_vtable_pointer(np_array); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'NonPolymorphic' has no virtual methods}}
+ __builtin_get_vtable_pointer(&np_array); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'NonPolymorphic (*)[1]' was provided}}
+ __builtin_get_vtable_pointer(test_function); // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}}
+ // expected-error at -1{{reference to overloaded function could not be resolved; did you mean to call it?}}
+ Foo<double> Food;
+ Foo<int> Fooi;
+ __builtin_get_vtable_pointer(Food); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'Foo<double>' was provided}}
+ (void)__builtin_get_vtable_pointer(&Food);
+ __builtin_get_vtable_pointer(Fooi); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'Foo<int>' was provided}}
+ __builtin_get_vtable_pointer(&Fooi); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'Foo<int>' has no virtual methods}}
+
+ IncompleteTemplate<bool> *incomplete = nullptr;
+ (void)__builtin_get_vtable_pointer(incomplete); // expected-error{{implicit instantiation of undefined template 'basic::IncompleteTemplate<bool>'}}
+ PolymorphicTemplate<bool> *ptb = nullptr;
+ MonomorphicTemplate<bool> *mtb = nullptr;
+ PolymorphicTemplate<int> pti;
+ MonomorphicTemplate<int> mti;
+ PolymorphicTemplate<float> ptf;
+ MonomorphicTemplate<float> mtf;
+ (void)__builtin_get_vtable_pointer(ptb);
+ __builtin_get_vtable_pointer(mtb); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'MonomorphicTemplate<bool>' has no virtual methods}}
+ __builtin_get_vtable_pointer(pti); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'PolymorphicTemplate<int>' was provided}}
+ __builtin_get_vtable_pointer(mti); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'MonomorphicTemplate<int>' was provided}}
+ (void)__builtin_get_vtable_pointer(&ptf);
+ __builtin_get_vtable_pointer(&mtf); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'MonomorphicTemplate<float>' has no virtual methods}}
+
+ getThing<Thing1>(); // expected-note{{in instantiation of function template specialization 'basic::getThing<basic::Thing1>' requested here}}
+ getThing<Thing2>();
+ getThing<Thing3>(); // expected-note{{in instantiation of function template specialization 'basic::getThing<basic::Thing3>' requested here}}
+ getThing<Thing4>();
+ getThing<Thing5>(); // expected-note{{in instantiation of function template specialization 'basic::getThing<basic::Thing5>' requested here}}
+ getThing<Thing6>(); // expected-note{{in instantiation of function template specialization 'basic::getThing<basic::Thing6>' requested here}}
+}
+
+} // namespace basic
More information about the cfe-commits
mailing list