Skip to content

Commit 22a2cae

Browse files
authored
[Clang] Fix cleanup attribute by delaying type checks after the type is deduced (#164440)
Previously, the handling of the `cleanup` attribute had some checks based on the type, but we were deducing the type after handling the attribute. This PR fixes the way the are dealing with type checks for the `cleanup` attribute by delaying these checks after we are deducing the type. It is also fixed in a way that the solution can be adapted for other attributes that does some type based checks. This is the list of C/C++ attributes that are doing type based checks and will need to be fixed in additional PRs: - CUDAShared - MutualExclusions - PassObjectSize - InitPriority - Sentinel - AcquireCapability - RequiresCapability - LocksExcluded - AcquireHandle NB: Some attributes could have been missed in my shallow search. Fixes #129631
1 parent 59ed6df commit 22a2cae

File tree

13 files changed

+140
-10
lines changed

13 files changed

+140
-10
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ Bug Fixes to Attribute Support
500500
- Fixes crashes or missing diagnostics with the `device_kernel` attribute. (#GH161905)
501501
- Fix handling of parameter indexes when an attribute is applied to a C++23 explicit object member function.
502502
- Fixed several false positives and false negatives in function effect (`nonblocking`) analysis. (#GH166078) (#GH166101) (#GH166110)
503+
- Fix ``cleanup`` attribute by delaying type checks until after the type is deduced. (#GH129631)
503504

504505
Bug Fixes to C++ Support
505506
^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/Attr.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,17 @@ class Attr {
741741
// our existing general parsing we need to have a separate flag that
742742
// opts an attribute into strict parsing of attribute parameters
743743
bit StrictEnumParameters = 0;
744+
// Set to true for attributes which have Sema checks which requires the type
745+
// to be deduced.
746+
// When `IsTypeDependent` is set to true, you should add an `ActOn*Attr`
747+
// function to `Sema.h`. The signature of the function must be:
748+
// `void ActOn*Attr(Decl *, const Attr *);` where the `Decl *` is the
749+
// declaration the attribute will be attached to; its type will have already
750+
// been deduced, and the `Attr *` is the attribute being applied to that
751+
// declaration. This function should handle all type-sensitive semantics for
752+
// the attribute. This function will be automatically called by
753+
// `Sema::CheckAttributesOnDeducedType()`.
754+
bit IsTypeDependent = 0;
744755
// Lists language options, one of which is required to be true for the
745756
// attribute to be applicable. If empty, no language options are required.
746757
list<LangOpt> LangOpts = [];
@@ -1400,6 +1411,7 @@ def Cleanup : InheritableAttr {
14001411
let Args = [DeclArgument<Function, "FunctionDecl">];
14011412
let Subjects = SubjectList<[LocalVar]>;
14021413
let Documentation = [CleanupDocs];
1414+
let IsTypeDependent = 1;
14031415
// FIXME: DeclArgument should be reworked to also store the
14041416
// Expr instead of adding attr specific hacks like the following.
14051417
// See the discussion in https://github.com/llvm/llvm-project/pull/14023.

clang/include/clang/Sema/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ clang_tablegen(AttrParsedAttrKinds.inc -gen-clang-attr-parsed-attr-kinds
88
SOURCE ../Basic/Attr.td
99
TARGET ClangAttrParsedAttrKinds)
1010

11+
clang_tablegen(AttrIsTypeDependent.inc -gen-clang-attr-is-type-dependent
12+
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
13+
SOURCE ../Basic/Attr.td
14+
TARGET ClangAttrIsTypeDependent)
15+
1116
clang_tablegen(AttrSpellingListIndex.inc -gen-clang-attr-spelling-index
1217
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
1318
SOURCE ../Basic/Attr.td

clang/include/clang/Sema/Sema.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4456,6 +4456,10 @@ class Sema final : public SemaBase {
44564456
NamedDecl *New, Decl *Old,
44574457
AvailabilityMergeKind AMK = AvailabilityMergeKind::Redeclaration);
44584458

4459+
/// CheckAttributesOnDeducedType - Calls Sema functions for attributes that
4460+
/// requires the type to be deduced.
4461+
void CheckAttributesOnDeducedType(Decl *D);
4462+
44594463
/// MergeTypedefNameDecl - We just parsed a typedef 'New' which has the
44604464
/// same name and scope as a previous declaration 'Old'. Figure out
44614465
/// how to resolve this situation, merging decls or emitting
@@ -4760,6 +4764,8 @@ class Sema final : public SemaBase {
47604764
// linkage or not.
47614765
static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD);
47624766

4767+
#include "clang/Sema/AttrIsTypeDependent.inc"
4768+
47634769
///@}
47644770

47654771
//
@@ -15469,6 +15475,8 @@ class Sema final : public SemaBase {
1546915475
std::optional<FunctionEffectMode>
1547015476
ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName);
1547115477

15478+
void ActOnCleanupAttr(Decl *D, const Attr *A);
15479+
1547215480
private:
1547315481
/// The implementation of RequireCompleteType
1547415482
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,

clang/lib/Sema/SemaDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,6 +3355,11 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
33553355
if (!foundAny) New->dropAttrs();
33563356
}
33573357

3358+
void Sema::CheckAttributesOnDeducedType(Decl *D) {
3359+
for (const Attr *A : D->attrs())
3360+
checkAttrIsTypeDependent(D, A);
3361+
}
3362+
33583363
// Returns the number of added attributes.
33593364
template <class T>
33603365
static unsigned propagateAttribute(ParmVarDecl *To, const ParmVarDecl *From,
@@ -13809,6 +13814,8 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
1380913814
return;
1381013815
}
1381113816

13817+
this->CheckAttributesOnDeducedType(RealDecl);
13818+
1381213819
// dllimport cannot be used on variable definitions.
1381313820
if (VDecl->hasAttr<DLLImportAttr>() && !VDecl->isStaticDataMember()) {
1381413821
Diag(VDecl->getLocation(), diag::err_attribute_dllimport_data_definition);
@@ -14300,6 +14307,8 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
1430014307
DeduceVariableDeclarationType(Var, false, nullptr))
1430114308
return;
1430214309

14310+
this->CheckAttributesOnDeducedType(RealDecl);
14311+
1430314312
// C++11 [class.static.data]p3: A static data member can be declared with
1430414313
// the constexpr specifier; if so, its declaration shall specify
1430514314
// a brace-or-equal-initializer.

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3511,16 +3511,6 @@ static void handleCleanupAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
35113511
return;
35123512
}
35133513

3514-
// We're currently more strict than GCC about what function types we accept.
3515-
// If this ever proves to be a problem it should be easy to fix.
3516-
QualType Ty = S.Context.getPointerType(cast<VarDecl>(D)->getType());
3517-
QualType ParamTy = FD->getParamDecl(0)->getType();
3518-
if (!S.IsAssignConvertCompatible(S.CheckAssignmentConstraints(
3519-
FD->getParamDecl(0)->getLocation(), ParamTy, Ty))) {
3520-
S.Diag(Loc, diag::err_attribute_cleanup_func_arg_incompatible_type)
3521-
<< NI.getName() << ParamTy << Ty;
3522-
return;
3523-
}
35243514
VarDecl *VD = cast<VarDecl>(D);
35253515
// Create a reference to the variable declaration. This is a fake/dummy
35263516
// reference.
@@ -8311,3 +8301,28 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) {
83118301
assert(curPool && "re-emitting in undelayed context not supported");
83128302
curPool->steal(pool);
83138303
}
8304+
8305+
void Sema::ActOnCleanupAttr(Decl *D, const Attr *A) {
8306+
VarDecl *VD = cast<VarDecl>(D);
8307+
if (VD->getType()->isDependentType())
8308+
return;
8309+
8310+
// Obtains the FunctionDecl that was found when handling the attribute
8311+
// earlier.
8312+
CleanupAttr *Attr = D->getAttr<CleanupAttr>();
8313+
FunctionDecl *FD = Attr->getFunctionDecl();
8314+
DeclarationNameInfo NI = FD->getNameInfo();
8315+
8316+
// We're currently more strict than GCC about what function types we accept.
8317+
// If this ever proves to be a problem it should be easy to fix.
8318+
QualType Ty = this->Context.getPointerType(VD->getType());
8319+
QualType ParamTy = FD->getParamDecl(0)->getType();
8320+
if (!this->IsAssignConvertCompatible(this->CheckAssignmentConstraints(
8321+
FD->getParamDecl(0)->getLocation(), ParamTy, Ty))) {
8322+
this->Diag(Attr->getArgLoc(),
8323+
diag::err_attribute_cleanup_func_arg_incompatible_type)
8324+
<< NI.getName() << ParamTy << Ty;
8325+
D->dropAttr<CleanupAttr>();
8326+
return;
8327+
}
8328+
}

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,15 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
10071007
continue;
10081008
}
10091009

1010+
if (auto *A = dyn_cast<CleanupAttr>(TmplAttr)) {
1011+
if (!New->hasAttr<CleanupAttr>()) {
1012+
auto *NewAttr = A->clone(Context);
1013+
NewAttr->setArgLoc(A->getArgLoc());
1014+
New->addAttr(NewAttr);
1015+
}
1016+
continue;
1017+
}
1018+
10101019
assert(!TmplAttr->isPackExpansion());
10111020
if (TmplAttr->isLateParsed() && LateAttrs) {
10121021
// Late parsed attributes must be instantiated and attached after the
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
2+
3+
int open() { return 0; }
4+
void close(typeof(open()) *) {}
5+
6+
void cleanup_attr() {
7+
int fd_int [[gnu::cleanup(close)]] = open();
8+
auto fd_auto [[gnu::cleanup(close)]] = open();
9+
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 *'}}
10+
}

clang/test/SemaCXX/attr-cleanup.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,28 @@ namespace E {
2727
int v1 __attribute__((cleanup(c3))); // expected-error {{'c3' is not a single function}}
2828
}
2929
}
30+
31+
namespace F {
32+
int open() { return 0; }
33+
void close(decltype(open()) *) {}
34+
35+
void test1() {
36+
auto fd [[gnu::cleanup(close)]] = open();
37+
}
38+
39+
template <typename Ty>
40+
void test2() {
41+
Ty fd [[gnu::cleanup(close)]] = open();
42+
}
43+
44+
template <typename Ty>
45+
void test3() {
46+
Ty fd [[gnu::cleanup(close)]] = open(); // #TEST3_CLEANUP
47+
}
48+
49+
int main() {
50+
test2<int>();
51+
test3<float>(); // expected-error@#TEST3_CLEANUP {{'cleanup' function 'close' parameter has type 'decltype(open()) *' (aka 'int *') which is incompatible with type 'float *'}} \
52+
expected-note {{in instantiation of function template specialization 'F::test3<float>' requested here}}
53+
}
54+
}

clang/utils/TableGen/ClangAttrEmitter.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5045,6 +5045,26 @@ void EmitClangAttrParsedAttrKinds(const RecordKeeper &Records,
50455045
<< "}\n";
50465046
}
50475047

5048+
// Emits Sema calls for type dependent attributes
5049+
void EmitClangAttrIsTypeDependent(const RecordKeeper &Records,
5050+
raw_ostream &OS) {
5051+
emitSourceFileHeader("Attribute is type dependent", OS, Records);
5052+
5053+
OS << "void checkAttrIsTypeDependent(Decl *D, const Attr *A) {\n";
5054+
OS << " switch (A->getKind()) {\n";
5055+
OS << " default:\n";
5056+
OS << " break;\n";
5057+
for (const auto *A : Records.getAllDerivedDefinitions("Attr")) {
5058+
if (A->getValueAsBit("IsTypeDependent")) {
5059+
OS << " case attr::" << A->getName() << ":\n";
5060+
OS << " ActOn" << A->getName() << "Attr(D, A);\n";
5061+
OS << " break;\n";
5062+
}
5063+
}
5064+
OS << " }\n";
5065+
OS << "}\n";
5066+
}
5067+
50485068
// Emits the code to dump an attribute.
50495069
void EmitClangAttrTextNodeDump(const RecordKeeper &Records, raw_ostream &OS) {
50505070
emitSourceFileHeader("Attribute text node dumper", OS, Records);

0 commit comments

Comments
 (0)