[clang] Basic, Sema: introduce `__attribute__((__personality__(...)))` (PR #185225)

Saleem Abdulrasool via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 7 12:16:57 PST 2026


https://github.com/compnerd created https://github.com/llvm/llvm-project/pull/185225

This attribute allows specifying a custom personality routine for a function, overriding the default emitted by Clang. The motivating use case is the Swift concurrency runtime, where C/C++ runtime functions need to act as barriers for exception propagation — the custom personality ensures exceptions do not propagate through these frames unchecked. More generally, this is useful whenever a language runtime is implemented in a host language with different EH semantics. LLVM IR already supports arbitrary personality functions on definitions; this attribute simply exposes that capability to the C/C++ frontend.

>From cd202640655f4748365772380779b8a1eec6cad0 Mon Sep 17 00:00:00 2001
From: Saleem Abdulrasool <compnerd at compnerd.org>
Date: Fri, 6 Mar 2026 22:53:55 -0800
Subject: [PATCH] Basic,Sema: introduce `__attribute__((__personality__(...)))`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This attribute allows specifying a custom personality routine for a
function, overriding the default emitted by Clang. The motivating use
case is the Swift concurrency runtime, where C/C++ runtime functions
need to act as barriers for exception propagation — the custom
personality ensures exceptions do not propagate through these frames
unchecked. More generally, this is useful whenever a language runtime is
implemented in a host language with different EH semantics. LLVM IR
already supports arbitrary personality functions on definitions; this
attribute simply exposes that capability to the C/C++ frontend.
---
 clang/include/clang/Basic/Attr.td             |  7 +++++
 clang/include/clang/Basic/AttrDocs.td         | 11 +++++++
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 ++++
 clang/include/clang/Sema/Sema.h               |  3 ++
 clang/lib/CodeGen/CodeGenFunction.cpp         |  9 ++++++
 clang/lib/Sema/SemaDecl.cpp                   |  2 ++
 clang/lib/Sema/SemaDeclAttr.cpp               | 27 +++++++++++++++++
 .../test/CodeGen/attr-personality-failures.c  | 30 +++++++++++++++++++
 clang/test/CodeGen/attr-personality.c         | 30 +++++++++++++++++++
 ...a-attribute-supported-attributes-list.test |  1 +
 10 files changed, 125 insertions(+)
 create mode 100644 clang/test/CodeGen/attr-personality-failures.c
 create mode 100644 clang/test/CodeGen/attr-personality.c

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index befd671393c7c..8ab4aaa6f5781 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5402,3 +5402,10 @@ def OverflowBehavior : TypeAttr {
                              "variables, typedefs, and data members">;
   let Documentation = [Undocumented];
 }
+
+def Personality : InheritableAttr {
+  let Spellings = [Clang<"personality">];
+  let Args = [DeclArgument<Function, "Routine">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [PersonalityDocs];
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 60dfdfc2f23f3..b8fedee234695 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9876,3 +9876,14 @@ of the same size, while the GCC/Itanium variant packs all fields in a bitfield
 tightly.
   }];
 }
+
+def PersonalityDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+``__attribute__((personality(<routine>)))`` is used to specify a personality
+routine that is different from the language that is being used to implement the
+function. This is a targeted, low-level feature aimed at language runtime
+implementors who write runtime support code in C/C++ but need that code to
+participate in a foreign language’s exception-handling or unwinding model.
+  }];
+}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0e6b3f51a5231..e6cc09165273d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2035,6 +2035,11 @@ def warn_wasm_dynamic_exception_spec_ignored : ExtWarn<
   "dynamic exception specifications with types are currently ignored in wasm">,
   InGroup<WebAssemblyExceptionSpec>;
 
+def err_mismatched_personality
+  : Error<"personality routine in declaration does not match previous declaration">;
+def err_attribute_personality_arg_not_function
+  : Error<"%0 argument is not a function">;
+
 // C++ access checking
 def err_class_redeclared_with_different_access : Error<
   "%0 redeclared with '%1' access">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5917eb0ffbfe1..832e46286194a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5024,6 +5024,9 @@ class Sema final : public SemaBase {
                                             StringRef ImplName,
                                             MutableArrayRef<StringRef> Aspects);
 
+  PersonalityAttr *mergePersonalityAttr(Decl *D, FunctionDecl *Routine,
+                                        const AttributeCommonInfo &CI);
+
   /// AddAlignedAttr - Adds an aligned attribute to a particular declaration.
   void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
                       bool IsPackExpansion);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 870840783c0c8..1dee656b5f2d0 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1655,6 +1655,15 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
 
   PGO->verifyCounterMap();
 
+  if (CurCodeDecl->hasAttr<PersonalityAttr>()) {
+    StringRef Identifier =
+        CurCodeDecl->getAttr<PersonalityAttr>()->getRoutine()->getName();
+    llvm::FunctionCallee PersonalityRoutine =
+        CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.Int32Ty, true),
+                                  Identifier, {}, /*local=*/true);
+    Fn->setPersonalityFn(cast<llvm::Constant>(PersonalityRoutine.getCallee()));
+  }
+
   // If we haven't marked the function nothrow through other means, do
   // a quick pass now to see if we can.
   if (!CurFn->doesNotThrow())
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 405832a446e10..40c84d7a5e4d9 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2970,6 +2970,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
     NewAttr = S.OpenACC().mergeRoutineDeclAttr(*RD);
   else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
     NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));
+  else if (const auto *PA = dyn_cast<PersonalityAttr>(Attr))
+    NewAttr = S.mergePersonalityAttr(D, PA->getRoutine(), *PA);
 
   if (NewAttr) {
     NewAttr->setInherited(true);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 1b7f41061061d..91a9a2797841d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7256,6 +7256,29 @@ static bool MustDelayAttributeArguments(const ParsedAttr &AL) {
   return false;
 }
 
+PersonalityAttr *Sema::mergePersonalityAttr(Decl *D, FunctionDecl *Routine,
+                                            const AttributeCommonInfo &CI) {
+  if (PersonalityAttr *PA = D->getAttr<PersonalityAttr>()) {
+    const FunctionDecl *Personality = PA->getRoutine();
+    if (Personality == Routine)
+      return nullptr;
+    Diag(PA->getLocation(), diag::err_mismatched_personality);
+    Diag(CI.getLoc(), diag::note_previous_attribute);
+    D->dropAttr<PersonalityAttr>();
+  }
+  return ::new (Context) PersonalityAttr(Context, CI, Routine);
+}
+
+static void handlePersonalityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  Expr *E = AL.getArgAsExpr(0);
+  if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+    if (FunctionDecl *FD = dyn_cast<FunctionDecl>(DRE->getDecl()))
+      if (Attr *A = S.mergePersonalityAttr(D, FD, AL))
+        return D->addAttr(A);
+  S.Diag(E->getExprLoc(), diag::err_attribute_personality_arg_not_function)
+      << AL.getAttrName();
+}
+
 /// ProcessDeclAttribute - Apply the specific attribute to the specified decl if
 /// the attribute applies to decls.  If the attribute is a type attribute, just
 /// silently ignore it if a GNU attribute.
@@ -7898,6 +7921,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
     handleNoPFPAttrField(S, D, AL);
     break;
 
+  case ParsedAttr::AT_Personality:
+    handlePersonalityAttr(S, D, AL);
+    break;
+
   // Microsoft attributes:
   case ParsedAttr::AT_LayoutVersion:
     handleLayoutVersion(S, D, AL);
diff --git a/clang/test/CodeGen/attr-personality-failures.c b/clang/test/CodeGen/attr-personality-failures.c
new file mode 100644
index 0000000000000..a882a4135100b
--- /dev/null
+++ b/clang/test/CodeGen/attr-personality-failures.c
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -Oz -triple x86_64-unknown-linux-gnu -fexceptions -fsyntax-only %s -verify
+
+typedef __UINT64_TYPE__ uint64_t;
+
+typedef enum _Unwind_Reason_Code _Unwind_Reason_Code;
+typedef enum _Unwind_Action _Unwind_Action;
+
+struct _Unwind_Exception;
+struct _Unwind_Context;
+
+int __swift_personality_int;
+_Unwind_Reason_Code __swift_personality_v0(int version, _Unwind_Action action, uint64_t exception_class, struct _Unwind_Exception *exception, struct _Unwind_Context *context);
+_Unwind_Reason_Code __swift_personality_v1(int version, _Unwind_Action action, uint64_t exception_class, struct _Unwind_Exception *exception, struct _Unwind_Context *context);
+_Unwind_Reason_Code __swift_personality_v2(int version, _Unwind_Action action, uint64_t exception_class, struct _Unwind_Exception *exception, struct _Unwind_Context *context);
+
+// expected-note at +2{{previous attribute is here}}
+// expected-note at +1{{previous attribute is here}}
+void __attribute__((__personality__(__swift_personality_v0))) function(void);
+// expected-error at +1{{personality routine in declaration does not match previous declaration}}
+void __attribute__((__personality__(__swift_personality_v1))) function(void);
+// expected-note at +3{{previous attribute is here}}
+// expected-error at +2{{personality routine in declaration does not match previous declaration}}
+// expected-error at +1{{personality routine in declaration does not match previous declaration}}
+void __attribute__((__personality__(__swift_personality_v1))) function(void) __attribute__((__personality__(__swift_personality_v2)));
+// expected-error at +1{{'__personality__' argument is not a function}}
+void __attribute__((__personality__(__swift_personality_int))) function(void) {
+}
+
+// expected-warning at +1{{'__personality__' attribute only applies to functions}}
+int variable __attribute__((__personality__(__swift_personality_v0)));
diff --git a/clang/test/CodeGen/attr-personality.c b/clang/test/CodeGen/attr-personality.c
new file mode 100644
index 0000000000000..91c587d0598d2
--- /dev/null
+++ b/clang/test/CodeGen/attr-personality.c
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -Oz -triple x86_64-unknown-linux-gnu -fexceptions -o - -emit-llvm %s | FileCheck %s
+
+typedef __UINT64_TYPE__ uint64_t;
+
+void abort(void) __attribute__((__noreturn__));
+
+typedef enum _Unwind_Reason_Code _Unwind_Reason_Code;
+typedef enum _Unwind_Action _Unwind_Action;
+
+struct _Unwind_Exception;
+struct _Unwind_Context;
+
+_Unwind_Reason_Code __swift_personality_v0(int version, _Unwind_Action action, uint64_t exception_class, struct _Unwind_Exception *exception, struct _Unwind_Context *context);
+
+void function(void);
+
+void __attribute__((__personality__(__swift_personality_v0))) function_with_custom_personality(void) {
+}
+
+static void cleanup_int(int *p) {
+  *p = 0;
+}
+
+void function_without_custom_personality(void) {
+  int variable __attribute__((__cleanup__(cleanup_int))) = 1;
+  function();
+}
+
+// CHECK-DAG: define dso_local void @function_with_custom_personality(){{.*}}personality ptr @__swift_personality_v0
+// CHECK-DAG: define dso_local void @function_without_custom_personality(){{.*}}personality ptr @__gcc_personality_v0
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index fde8f14f5cd83..03b9a77ec1814 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -183,6 +183,7 @@
 // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method)
+// CHECK-NEXT: Personality (SubjectMatchRule_function)
 // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union)
 // CHECK-NEXT: PointerFieldProtection (SubjectMatchRule_record)
 // CHECK-NEXT: PreserveNone (SubjectMatchRule_hasType_functionType)



More information about the cfe-commits mailing list