[clang] [llvm] [Clang] Fix cleanup attribute by delaying type checks after the type is deduced (PR #164440)
Guillot Tony via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 24 03:03:21 PDT 2025
https://github.com/to268 updated https://github.com/llvm/llvm-project/pull/164440
>From eef160b0e9d26b21f76557e9d077e67f9db2e38c Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Tue, 21 Oct 2025 15:21:33 +0200
Subject: [PATCH 1/3] Fix for cleaup attribute and sets base for fixing other
type dependent attributes
Fixes #129631
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Sema/Sema.h | 6 +++++
clang/lib/Sema/SemaDecl.cpp | 18 ++++++++++++++
clang/lib/Sema/SemaDeclAttr.cpp | 34 ++++++++++++++++++--------
clang/test/Sema/type-dependent-attrs.c | 11 +++++++++
5 files changed, 60 insertions(+), 10 deletions(-)
create mode 100644 clang/test/Sema/type-dependent-attrs.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e6e33e7a9a280..367c1c455aca4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -448,6 +448,7 @@ Bug Fixes to Attribute Support
``[[gnu::error("some error")]]`` now correctly triggers an error. (#GH146520)
- Fix a crash when the function name is empty in the `swift_name` attribute. (#GH157075)
- Fixes crashes or missing diagnostics with the `device_kernel` attribute. (#GH161905)
+- Fix ``cleanup`` attribute by delaying type checks after the type is deduced. (#GH129631)
Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 189798f71dbad..fb9f193861ac7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4461,6 +4461,10 @@ class Sema final : public SemaBase {
NamedDecl *New, Decl *Old,
AvailabilityMergeKind AMK = AvailabilityMergeKind::Redeclaration);
+ /// CheckAttributesOnDeducedType - Calls Sema functions for attributes that
+ /// requires the type to be deduced.
+ void CheckAttributesOnDeducedType(Expr *E, Decl *D);
+
/// MergeTypedefNameDecl - We just parsed a typedef 'New' which has the
/// same name and scope as a previous declaration 'Old'. Figure out
/// how to resolve this situation, merging decls or emitting
@@ -15479,6 +15483,8 @@ class Sema final : public SemaBase {
std::optional<FunctionEffectMode>
ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName);
+ void ActOnCleanupAttr(Expr *E, Decl *D, const Attr *A);
+
private:
/// The implementation of RequireCompleteType
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fc3aabf5741ca..13600218b9d83 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3354,6 +3354,21 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
if (!foundAny) New->dropAttrs();
}
+void Sema::CheckAttributesOnDeducedType(Expr *E, Decl *D) {
+ if (!D->hasAttrs())
+ return;
+
+ for (const Attr *A : D->getAttrs()) {
+ switch (A->getKind()) {
+ case attr::Cleanup:
+ ActOnCleanupAttr(E, D, A);
+ break;
+ default:
+ continue;
+ }
+ }
+}
+
// Returns the number of added attributes.
template <class T>
static unsigned propagateAttribute(ParmVarDecl *To, const ParmVarDecl *From,
@@ -13797,6 +13812,8 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
return;
}
+ this->CheckAttributesOnDeducedType(Init, RealDecl);
+
// dllimport cannot be used on variable definitions.
if (VDecl->hasAttr<DLLImportAttr>() && !VDecl->isStaticDataMember()) {
Diag(VDecl->getLocation(), diag::err_attribute_dllimport_data_definition);
@@ -14587,6 +14604,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
Var->setInit(RecoveryExpr.get());
}
+ this->CheckAttributesOnDeducedType(Init.get(), RealDecl);
CheckCompleteVariableDeclaration(Var);
}
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 964a2a791e18f..cf01341310934 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3511,16 +3511,6 @@ static void handleCleanupAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
return;
}
- // We're currently more strict than GCC about what function types we accept.
- // If this ever proves to be a problem it should be easy to fix.
- QualType Ty = S.Context.getPointerType(cast<VarDecl>(D)->getType());
- QualType ParamTy = FD->getParamDecl(0)->getType();
- if (!S.IsAssignConvertCompatible(S.CheckAssignmentConstraints(
- FD->getParamDecl(0)->getLocation(), ParamTy, Ty))) {
- S.Diag(Loc, diag::err_attribute_cleanup_func_arg_incompatible_type)
- << NI.getName() << ParamTy << Ty;
- return;
- }
VarDecl *VD = cast<VarDecl>(D);
// Create a reference to the variable declaration. This is a fake/dummy
// reference.
@@ -8311,3 +8301,27 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) {
assert(curPool && "re-emitting in undelayed context not supported");
curPool->steal(pool);
}
+
+void Sema::ActOnCleanupAttr(Expr *E, Decl *D, const Attr *A) {
+ FunctionDecl *FD = nullptr;
+ DeclarationNameInfo NI;
+ CleanupAttr *Attr = D->getAttr<CleanupAttr>();
+
+ // Obtains the FunctionDecl that was found when handling the attribute
+ // earlier.
+ FD = Attr->getFunctionDecl();
+ NI = FD->getNameInfo();
+
+ // We're currently more strict than GCC about what function types we accept.
+ // If this ever proves to be a problem it should be easy to fix.
+ QualType Ty = this->Context.getPointerType(cast<VarDecl>(D)->getType());
+ QualType ParamTy = FD->getParamDecl(0)->getType();
+ if (!this->IsAssignConvertCompatible(this->CheckAssignmentConstraints(
+ FD->getParamDecl(0)->getLocation(), ParamTy, Ty))) {
+ this->Diag(Attr->getArgLoc(),
+ diag::err_attribute_cleanup_func_arg_incompatible_type)
+ << NI.getName() << ParamTy << Ty;
+ D->dropAttr<CleanupAttr>();
+ return;
+ }
+}
diff --git a/clang/test/Sema/type-dependent-attrs.c b/clang/test/Sema/type-dependent-attrs.c
new file mode 100644
index 0000000000000..f99293d1cd639
--- /dev/null
+++ b/clang/test/Sema/type-dependent-attrs.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
+
+// #GH129631-CleanupAttr
+int open() { return 0; }
+void close(typeof(open()) *) {}
+
+void cleanup_attr() {
+ int fd_int [[gnu::cleanup(close)]] = open();
+ auto fd_auto [[gnu::cleanup(close)]] = open();
+ float fd_invalid [[gnu::cleanup(close)]] = open(); // expected-error {{'cleanup' function 'close' parameter has type 'typeof (open()) *' (aka 'int *') which is incompatible with type 'float *'}}
+}
>From 3f3b45cf3730218152e10449454bf74227d4c90d Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Tue, 21 Oct 2025 17:40:44 +0200
Subject: [PATCH 2/3] Fixed small issues that where appointed
---
clang/lib/Sema/SemaDeclAttr.cpp | 9 +++------
clang/test/Sema/type-dependent-attrs.c | 1 -
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index cf01341310934..391ddf72abe1e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8303,14 +8303,11 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) {
}
void Sema::ActOnCleanupAttr(Expr *E, Decl *D, const Attr *A) {
- FunctionDecl *FD = nullptr;
- DeclarationNameInfo NI;
- CleanupAttr *Attr = D->getAttr<CleanupAttr>();
-
// Obtains the FunctionDecl that was found when handling the attribute
// earlier.
- FD = Attr->getFunctionDecl();
- NI = FD->getNameInfo();
+ CleanupAttr *Attr = D->getAttr<CleanupAttr>();
+ FunctionDecl *FD = Attr->getFunctionDecl();
+ DeclarationNameInfo NI = FD->getNameInfo();
// We're currently more strict than GCC about what function types we accept.
// If this ever proves to be a problem it should be easy to fix.
diff --git a/clang/test/Sema/type-dependent-attrs.c b/clang/test/Sema/type-dependent-attrs.c
index f99293d1cd639..13068b3f94ad4 100644
--- a/clang/test/Sema/type-dependent-attrs.c
+++ b/clang/test/Sema/type-dependent-attrs.c
@@ -1,6 +1,5 @@
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
-// #GH129631-CleanupAttr
int open() { return 0; }
void close(typeof(open()) *) {}
>From 20e94aec358d5237540ca1f768d4e7e32b65a117 Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Fri, 24 Oct 2025 11:56:31 +0200
Subject: [PATCH 3/3] Reworked switch to be generated using TableGen
---
clang/include/clang/Basic/Attr.td | 4 ++++
clang/include/clang/Sema/CMakeLists.txt | 5 +++++
clang/include/clang/Sema/Sema.h | 2 ++
clang/lib/Sema/SemaDecl.cpp | 8 +------
clang/utils/TableGen/ClangAttrEmitter.cpp | 26 +++++++++++++++++++++++
clang/utils/TableGen/TableGen.cpp | 7 ++++++
clang/utils/TableGen/TableGenBackends.h | 2 ++
llvm/docs/TableGen/BackEnds.rst | 7 ++++++
8 files changed, 54 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 749f531ec9ab1..c34bc94aae5b0 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -737,6 +737,9 @@ class Attr {
// our existing general parsing we need to have a separate flag that
// opts an attribute into strict parsing of attribute parameters
bit StrictEnumParameters = 0;
+ // Set to true for attributes which have Sema checks which requires the type
+ // to be deduced.
+ bit IsTypeDependent = 0;
// Lists language options, one of which is required to be true for the
// attribute to be applicable. If empty, no language options are required.
list<LangOpt> LangOpts = [];
@@ -1408,6 +1411,7 @@ def Cleanup : InheritableAttr {
let Args = [DeclArgument<Function, "FunctionDecl">];
let Subjects = SubjectList<[LocalVar]>;
let Documentation = [CleanupDocs];
+ bit IsTypeDependent = 1;
// FIXME: DeclArgument should be reworked to also store the
// Expr instead of adding attr specific hacks like the following.
// See the discussion in https://github.com/llvm/llvm-project/pull/14023.
diff --git a/clang/include/clang/Sema/CMakeLists.txt b/clang/include/clang/Sema/CMakeLists.txt
index 9077e22c2307c..3f540ea596871 100644
--- a/clang/include/clang/Sema/CMakeLists.txt
+++ b/clang/include/clang/Sema/CMakeLists.txt
@@ -8,6 +8,11 @@ clang_tablegen(AttrParsedAttrKinds.inc -gen-clang-attr-parsed-attr-kinds
SOURCE ../Basic/Attr.td
TARGET ClangAttrParsedAttrKinds)
+clang_tablegen(AttrIsTypeDependent.inc -gen-clang-attr-is-type-dependent
+ -I ${CMAKE_CURRENT_SOURCE_DIR}/../../
+ SOURCE ../Basic/Attr.td
+ TARGET ClangAttrIsTypeDependent)
+
clang_tablegen(AttrSpellingListIndex.inc -gen-clang-attr-spelling-index
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
SOURCE ../Basic/Attr.td
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fb9f193861ac7..66ae7362662fa 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4769,6 +4769,8 @@ class Sema final : public SemaBase {
// linkage or not.
static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD);
+#include "clang/Sema/AttrIsTypeDependent.inc"
+
///@}
//
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 13600218b9d83..10afe40d05673 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3359,13 +3359,7 @@ void Sema::CheckAttributesOnDeducedType(Expr *E, Decl *D) {
return;
for (const Attr *A : D->getAttrs()) {
- switch (A->getKind()) {
- case attr::Cleanup:
- ActOnCleanupAttr(E, D, A);
- break;
- default:
- continue;
- }
+ checkAttrIsTypeDependent(E, D, A);
}
}
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 183952af590e1..5e2ceef0245e6 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5061,6 +5061,32 @@ void EmitClangAttrParsedAttrKinds(const RecordKeeper &Records,
<< "}\n";
}
+// Emits Sema calls for type dependent attributes
+void EmitClangAttrIsTypeDependent(const RecordKeeper &Records,
+ raw_ostream &OS) {
+ emitSourceFileHeader("Attribute is type dependent", OS, Records);
+
+ std::set<StringRef> Seen;
+ for (const auto *A : Records.getAllDerivedDefinitions("Attr")) {
+ const Record &Attr = *A;
+ if (Attr.getValueAsBit("IsTypeDependent")) {
+ Seen.insert(Attr.getName());
+ }
+ }
+
+ OS << "void checkAttrIsTypeDependent(Expr *E, Decl *D, const Attr *A) {\n";
+ OS << " switch (A->getKind()) {\n";
+ for (const StringRef &SeenAttr : Seen) {
+ OS << " case attr::" << SeenAttr << ":\n";
+ OS << " ActOn" << SeenAttr << "Attr(E, D, A);\n";
+ OS << " break;\n";
+ }
+ OS << " default:\n";
+ OS << " break;\n";
+ OS << " }\n";
+ OS << "}\n";
+}
+
// Emits the code to dump an attribute.
void EmitClangAttrTextNodeDump(const RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute text node dumper", OS, Records);
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index 866040d503646..707ce617cb2d0 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -43,6 +43,7 @@ enum ActionType {
GenClangAttrParsedAttrList,
GenClangAttrParsedAttrImpl,
GenClangAttrParsedAttrKinds,
+ GenClangAttrIsTypeDependent,
GenClangAttrTextNodeDump,
GenClangAttrNodeTraverse,
GenClangBasicReader,
@@ -179,6 +180,9 @@ cl::opt<ActionType> Action(
clEnumValN(GenClangAttrParsedAttrKinds,
"gen-clang-attr-parsed-attr-kinds",
"Generate a clang parsed attribute kinds"),
+ clEnumValN(GenClangAttrIsTypeDependent,
+ "gen-clang-attr-is-type-dependent",
+ "Generate clang is type dependent attribute code"),
clEnumValN(GenClangAttrTextNodeDump, "gen-clang-attr-text-node-dump",
"Generate clang attribute text node dumper"),
clEnumValN(GenClangAttrNodeTraverse, "gen-clang-attr-node-traverse",
@@ -423,6 +427,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
case GenClangAttrParsedAttrKinds:
EmitClangAttrParsedAttrKinds(Records, OS);
break;
+ case GenClangAttrIsTypeDependent:
+ EmitClangAttrIsTypeDependent(Records, OS);
+ break;
case GenClangAttrTextNodeDump:
EmitClangAttrTextNodeDump(Records, OS);
break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index fa49dcd289bc2..058bda3ebd246 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -82,6 +82,8 @@ void EmitClangAttrParsedAttrImpl(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitClangAttrParsedAttrKinds(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
+void EmitClangAttrIsTypeDependent(const llvm::RecordKeeper &Records,
+ llvm::raw_ostream &OS);
void EmitClangAttrTextNodeDump(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitClangAttrNodeTraverse(const llvm::RecordKeeper &Records,
diff --git a/llvm/docs/TableGen/BackEnds.rst b/llvm/docs/TableGen/BackEnds.rst
index 7f571378860b2..1e3cb8783df16 100644
--- a/llvm/docs/TableGen/BackEnds.rst
+++ b/llvm/docs/TableGen/BackEnds.rst
@@ -355,6 +355,13 @@ ClangAttrParsedAttrKinds
``AttributeList::getKind`` function, mapping a string (and syntax) to a parsed
attribute ``AttributeList::Kind`` enumeration.
+ClangAttrIsTypeDependent
+------------------------
+
+**Purpose**: Creates ``AttrIsTypeDependent.inc``, which is used to implement the
+``Sema::CheckAttributesOnDeducedType`` function, mapping an attribute kind to a
+Sema function if it exists.
+
ClangAttrDump
-------------
More information about the cfe-commits
mailing list