Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ Bug Fixes to Attribute Support
- Fixes crashes or missing diagnostics with the `device_kernel` attribute. (#GH161905)
- Fix handling of parameter indexes when an attribute is applied to a C++23 explicit object member function.
- Fixed several false positives and false negatives in function effect (`nonblocking`) analysis. (#GH166078) (#GH166101) (#GH166110)
- Fix ``cleanup`` attribute by delaying type checks until after the type is deduced. (#GH129631)

Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,17 @@ 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.
// When `IsTypeDependent` is set to true, you should add an `ActOn*Attr`
// function to `Sema.h`. The signature of the function must be:
// `void ActOn*Attr(Decl *, const Attr *);` where the `Decl *` is the
// declaration the attribute will be attached to; its type will have already
// been deduced, and the `Attr *` is the attribute being applied to that
// declaration. This function should handle all type-sensitive semantics for
// the attribute. This function will be automatically called by
// `Sema::CheckAttributesOnDeducedType()`.
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 = [];
Expand Down Expand Up @@ -1400,6 +1411,7 @@ def Cleanup : InheritableAttr {
let Args = [DeclArgument<Function, "FunctionDecl">];
let Subjects = SubjectList<[LocalVar]>;
let Documentation = [CleanupDocs];
let 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.
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4456,6 +4456,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(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
Expand Down Expand Up @@ -4760,6 +4764,8 @@ class Sema final : public SemaBase {
// linkage or not.
static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD);

#include "clang/Sema/AttrIsTypeDependent.inc"

///@}

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

void ActOnCleanupAttr(Decl *D, const Attr *A);

private:
/// The implementation of RequireCompleteType
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3355,6 +3355,11 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
if (!foundAny) New->dropAttrs();
}

void Sema::CheckAttributesOnDeducedType(Decl *D) {
for (const Attr *A : D->attrs())
checkAttrIsTypeDependent(D, A);
}

// Returns the number of added attributes.
template <class T>
static unsigned propagateAttribute(ParmVarDecl *To, const ParmVarDecl *From,
Expand Down Expand Up @@ -13809,6 +13814,8 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
return;
}

this->CheckAttributesOnDeducedType(RealDecl);

// dllimport cannot be used on variable definitions.
if (VDecl->hasAttr<DLLImportAttr>() && !VDecl->isStaticDataMember()) {
Diag(VDecl->getLocation(), diag::err_attribute_dllimport_data_definition);
Expand Down Expand Up @@ -14300,6 +14307,8 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
DeduceVariableDeclarationType(Var, false, nullptr))
return;

this->CheckAttributesOnDeducedType(RealDecl);

// C++11 [class.static.data]p3: A static data member can be declared with
// the constexpr specifier; if so, its declaration shall specify
// a brace-or-equal-initializer.
Expand Down
35 changes: 25 additions & 10 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -8311,3 +8301,28 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) {
assert(curPool && "re-emitting in undelayed context not supported");
curPool->steal(pool);
}

void Sema::ActOnCleanupAttr(Decl *D, const Attr *A) {
VarDecl *VD = cast<VarDecl>(D);
if (VD->getType()->isDependentType())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my reading of the documentation in Attr.td this seems like it should be an assert on !VD->getType()->isDependentType() right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well no, if isDependentType() is true we would delay (by exiting the function early) till the template has been instantiated before trying to do type checks. After template instantiation, this function is called again and would perform type checks as usual.

return;

// Obtains the FunctionDecl that was found when handling the attribute
// earlier.
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.
QualType Ty = this->Context.getPointerType(VD->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;
}
}
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,15 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
continue;
}

if (auto *A = dyn_cast<CleanupAttr>(TmplAttr)) {
if (!New->hasAttr<CleanupAttr>()) {
auto *NewAttr = A->clone(Context);
NewAttr->setArgLoc(A->getArgLoc());
New->addAttr(NewAttr);
}
continue;
}

assert(!TmplAttr->isPackExpansion());
if (TmplAttr->isLateParsed() && LateAttrs) {
// Late parsed attributes must be instantiated and attached after the
Expand Down
10 changes: 10 additions & 0 deletions clang/test/Sema/type-dependent-attrs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s

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 *'}}
}
25 changes: 25 additions & 0 deletions clang/test/SemaCXX/attr-cleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,28 @@ namespace E {
int v1 __attribute__((cleanup(c3))); // expected-error {{'c3' is not a single function}}
}
}

namespace F {
int open() { return 0; }
void close(decltype(open()) *) {}

void test1() {
auto fd [[gnu::cleanup(close)]] = open();
}

template <typename Ty>
void test2() {
Ty fd [[gnu::cleanup(close)]] = open();
}

template <typename Ty>
void test3() {
Ty fd [[gnu::cleanup(close)]] = open(); // #TEST3_CLEANUP
}

int main() {
test2<int>();
test3<float>(); // expected-error@#TEST3_CLEANUP {{'cleanup' function 'close' parameter has type 'decltype(open()) *' (aka 'int *') which is incompatible with type 'float *'}} \
expected-note {{in instantiation of function template specialization 'F::test3<float>' requested here}}
}
}
20 changes: 20 additions & 0 deletions clang/utils/TableGen/ClangAttrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5045,6 +5045,26 @@ 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);

OS << "void checkAttrIsTypeDependent(Decl *D, const Attr *A) {\n";
OS << " switch (A->getKind()) {\n";
OS << " default:\n";
OS << " break;\n";
for (const auto *A : Records.getAllDerivedDefinitions("Attr")) {
if (A->getValueAsBit("IsTypeDependent")) {
OS << " case attr::" << A->getName() << ":\n";
OS << " ActOn" << A->getName() << "Attr(D, A);\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);
Expand Down
7 changes: 7 additions & 0 deletions clang/utils/TableGen/TableGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum ActionType {
GenClangAttrParsedAttrList,
GenClangAttrParsedAttrImpl,
GenClangAttrParsedAttrKinds,
GenClangAttrIsTypeDependent,
GenClangAttrTextNodeDump,
GenClangAttrNodeTraverse,
GenClangBasicReader,
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions clang/utils/TableGen/TableGenBackends.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions llvm/docs/TableGen/BackEnds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------

Expand Down
Loading