[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