diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index add1582344a0e..83318c76db820 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -451,6 +451,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 handling of parameter indexes when an attribute is applied to a C++23 explicit object member function. Bug Fixes to C++ Support ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index ce273c167aa22..14d7caa0e16d7 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -16,6 +16,7 @@ #include "clang/AST/ASTFwd.h" #include "clang/AST/AttrIterator.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" #include "clang/Basic/AttrKinds.h" #include "clang/Basic/AttributeCommonInfo.h" @@ -327,8 +328,8 @@ class ParamIdx { ParamIdx(unsigned Idx, const Decl *D) : Idx(Idx), HasThis(false), IsValid(true) { assert(Idx >= 1 && "Idx must be one-origin"); - if (const auto *FD = dyn_cast(D)) - HasThis = FD->isCXXInstanceMember(); + if (const auto *MethodDecl = dyn_cast(D)) + HasThis = MethodDecl->isImplicitObjectMemberFunction(); } /// A type into which \c ParamIdx can be serialized. diff --git a/clang/include/clang/Sema/Attr.h b/clang/include/clang/Sema/Attr.h index 3f0b10212789a..5836231818eec 100644 --- a/clang/include/clang/Sema/Attr.h +++ b/clang/include/clang/Sema/Attr.h @@ -123,6 +123,12 @@ inline bool isInstanceMethod(const Decl *D) { return false; } +inline bool hasImplicitObjectParameter(const Decl *D) { + if (const auto *MethodDecl = dyn_cast(D)) + return MethodDecl->isImplicitObjectMemberFunction(); + return false; +} + /// Diagnose mutually exclusive attributes when present on a given /// declaration. Returns true if diagnosed. template diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 52904c72d1cfc..c67ed99b1f49e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2608,13 +2608,13 @@ class Sema final : public SemaBase { }; /// Given a function and its FormatAttr or FormatMatchesAttr info, attempts to - /// populate the FomatStringInfo parameter with the attribute's correct + /// populate the FormatStringInfo parameter with the attribute's correct /// format_idx and firstDataArg. Returns true when the format fits the /// function and the FormatStringInfo has been populated. static bool getFormatStringInfo(const Decl *Function, unsigned FormatIdx, unsigned FirstArg, FormatStringInfo *FSI); static bool getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg, - bool IsCXXMember, bool IsVariadic, + bool HasImplicitThisParam, bool IsVariadic, FormatStringInfo *FSI); // Used by C++ template instantiation. @@ -5119,7 +5119,7 @@ class Sema final : public SemaBase { // In C++ the implicit 'this' function parameter also counts. // Parameters are counted from one. bool HP = hasFunctionProto(D); - bool HasImplicitThisParam = isInstanceMethod(D); + bool HasImplicitThisParam = hasImplicitObjectParameter(D); bool IV = HP && isFunctionOrMethodVariadic(D); unsigned NumParams = (HP ? getFunctionOrMethodNumParams(D) : 0) + HasImplicitThisParam; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f4517877b04c8..ad2c2e4a97bb9 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3542,9 +3542,7 @@ bool Sema::ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum) { bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx, unsigned FirstArg, FormatStringInfo *FSI) { - bool IsCXXMember = false; - if (const auto *MD = dyn_cast(D)) - IsCXXMember = MD->isInstance(); + bool HasImplicitThisParam = hasImplicitObjectParameter(D); bool IsVariadic = false; if (const FunctionType *FnTy = D->getFunctionType()) IsVariadic = cast(FnTy)->isVariadic(); @@ -3553,11 +3551,12 @@ bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx, else if (const auto *OMD = dyn_cast(D)) IsVariadic = OMD->isVariadic(); - return getFormatStringInfo(FormatIdx, FirstArg, IsCXXMember, IsVariadic, FSI); + return getFormatStringInfo(FormatIdx, FirstArg, HasImplicitThisParam, + IsVariadic, FSI); } bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg, - bool IsCXXMember, bool IsVariadic, + bool HasImplicitThisParam, bool IsVariadic, FormatStringInfo *FSI) { if (FirstArg == 0) FSI->ArgPassingKind = FAPK_VAList; @@ -3571,7 +3570,7 @@ bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg, // The way the format attribute works in GCC, the implicit this argument // of member functions is counted. However, it doesn't appear in our own // lists, so decrement format_idx in that case. - if (IsCXXMember) { + if (HasImplicitThisParam) { if(FSI->FormatIdx == 0) return false; --FSI->FormatIdx; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 964a2a791e18f..a9e7b44ac9d73 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3785,7 +3785,7 @@ static bool handleFormatAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, // In C++ the implicit 'this' function parameter also counts, and they are // counted from one. - bool HasImplicitThisParam = isInstanceMethod(D); + bool HasImplicitThisParam = hasImplicitObjectParameter(D); Info->NumArgs = getFunctionOrMethodNumParams(D) + HasImplicitThisParam; Info->Identifier = AL.getArgAsIdent(0)->getIdentifierInfo(); @@ -3926,7 +3926,7 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) { return; } - bool HasImplicitThisParam = isInstanceMethod(D); + bool HasImplicitThisParam = hasImplicitObjectParameter(D); int32_t NumArgs = getFunctionOrMethodNumParams(D); FunctionDecl *FD = D->getAsFunction(); @@ -4110,7 +4110,7 @@ static void handleLifetimeCaptureByAttr(Sema &S, Decl *D, } void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { - bool HasImplicitThisParam = isInstanceMethod(FD); + bool HasImplicitThisParam = hasImplicitObjectParameter(FD); SmallVector Attrs; for (ParmVarDecl *PVD : FD->parameters()) if (auto *A = PVD->getAttr()) diff --git a/clang/test/CodeGenCXX/attr-callback.cpp b/clang/test/CodeGenCXX/attr-callback.cpp index c3456d6c430ff..efa705b9d06dc 100644 --- a/clang/test/CodeGenCXX/attr-callback.cpp +++ b/clang/test/CodeGenCXX/attr-callback.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -std=c++23 %s -emit-llvm -o - | FileCheck %s struct Base { @@ -47,9 +47,30 @@ struct Derived_2 : public Base { // CHECK-NOT: !callback void Derived_2::virtual_1(void (*callback)(void)) {} +class ExplicitParameterObject { + __attribute__((callback(1, 0))) void implicit_this_idx(void (*callback)(ExplicitParameterObject*)); + __attribute__((callback(1, this))) void implicit_this_identifier(void (*callback)(ExplicitParameterObject*)); + __attribute__((callback(2, 1))) void explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)); + __attribute__((callback(2, self))) void explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)); +}; + +// CHECK-DAG: define{{.*}} void @_ZN23ExplicitParameterObject17implicit_this_idxEPFvPS_E({{[^!]*!callback}} ![[cid3:[0-9]+]] +void ExplicitParameterObject::implicit_this_idx(void (*callback)(ExplicitParameterObject*)) {} + +// CHECK-DAG: define{{.*}} void @_ZN23ExplicitParameterObject24implicit_this_identifierEPFvPS_E({{[^!]*!callback}} ![[cid3]] +void ExplicitParameterObject::implicit_this_identifier(void (*callback)(ExplicitParameterObject*)) {} + +// CHECK-DAG: define{{.*}} void @_ZNH23ExplicitParameterObject17explicit_this_idxEPS_PFvS0_E({{[^!]*!callback}} ![[cid3]] +void ExplicitParameterObject::explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)) {} + +// CHECK-DAG: define{{.*}} void @_ZNH23ExplicitParameterObject24explicit_this_identifierEPS_PFvS0_E({{[^!]*!callback}} ![[cid3]] +void ExplicitParameterObject::explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)) {} + // CHECK-DAG: ![[cid0]] = !{![[cid0b:[0-9]+]]} // CHECK-DAG: ![[cid0b]] = !{i64 1, i1 false} // CHECK-DAG: ![[cid1]] = !{![[cid1b:[0-9]+]]} // CHECK-DAG: ![[cid1b]] = !{i64 2, i1 false} // CHECK-DAG: ![[cid2]] = !{![[cid2b:[0-9]+]]} // CHECK-DAG: ![[cid2b]] = !{i64 1, i64 0, i64 -1, i64 0, i1 false} +// CHECK-DAG: ![[cid3]] = !{![[cid3b:[0-9]+]]} +// CHECK-DAG: ![[cid3b]] = !{i64 1, i64 0, i1 false} diff --git a/clang/test/SemaCXX/attr-callback-broken.cpp b/clang/test/SemaCXX/attr-callback-broken.cpp index a5469b22ba350..53b331a49251b 100644 --- a/clang/test/SemaCXX/attr-callback-broken.cpp +++ b/clang/test/SemaCXX/attr-callback-broken.cpp @@ -1,7 +1,12 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only +// RUN: %clang_cc1 %s -std=c++23 -verify -fsyntax-only class C_in_class { #define HAS_THIS #include "../Sema/attr-callback-broken.c" #undef HAS_THIS }; + +class ExplicitParameterObject { + __attribute__((callback(2, 0))) void explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)); // expected-error {{'callback' argument at position 2 references unavailable implicit 'this'}} + __attribute__((callback(2, this))) void explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)); // expected-error {{'callback' argument at position 2 references unavailable implicit 'this'}} +}; diff --git a/clang/test/SemaCXX/attr-callback.cpp b/clang/test/SemaCXX/attr-callback.cpp index ee02f7d3d24f7..ff5a241e92f74 100644 --- a/clang/test/SemaCXX/attr-callback.cpp +++ b/clang/test/SemaCXX/attr-callback.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only +// RUN: %clang_cc1 %s -std=c++23 -verify -fsyntax-only // expected-no-diagnostics @@ -6,6 +6,11 @@ class C_in_class { #include "../Sema/attr-callback.c" }; +class ExplicitParameterObject { + __attribute__((callback(2, 1))) void explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)); + __attribute__((callback(2, self))) void explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)); +}; + struct Base { void no_args_1(void (*callback)(void)); diff --git a/clang/test/SemaCXX/attr-format.cpp b/clang/test/SemaCXX/attr-format.cpp index adc05fc46776c..c0aeb5d07dfe9 100644 --- a/clang/test/SemaCXX/attr-format.cpp +++ b/clang/test/SemaCXX/attr-format.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -Wformat-nonliteral -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -Wformat-nonliteral -verify %s #include int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); @@ -11,6 +11,10 @@ struct S { // the format argument is argument 2 here. void g(const char*, ...) __attribute__((format(printf, 2, 3))); const char* g2(const char*) __attribute__((format_arg(2))); + // From C++23 'this' can also be specified explicitly. + void g3(this S&, const char *, ...) __attribute__((format(printf, 2, 3))); + void g4(this const char* s, ...) __attribute__((format(printf, 1, 2))); + consteval operator const char*() const { return "%f"; } // #g4_fmt_string void h(const char*, ...) __attribute__((format(printf, 1, 4))); // \ expected-error{{implicit this argument as the format string}} @@ -18,10 +22,17 @@ struct S { expected-error{{out of bounds}} const char* h3(const char*) __attribute__((format_arg(1))); // \ expected-error{{invalid for the implicit this argument}} + void h4(this S&, const char *, ...) __attribute__((format(printf, 1, 3))); // \ + expected-error {{format argument not a string type}} void operator() (const char*, ...) __attribute__((format(printf, 2, 3))); }; +void s() { + S().g4(4); // expected-warning {{format specifies type 'double' but the argument has type 'int'}} + // expected-note@#g4_fmt_string {{format string is defined here}} +} + // PR5521 struct A { void a(const char*,...) __attribute((format(printf,2,3))); }; void b(A x) { diff --git a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp index 70a5fe5a45376..8606592c6b771 100644 --- a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp +++ b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp @@ -44,4 +44,7 @@ struct T { { s.captureInt(x); } + + void explicit_this1(this T& self, const int &x [[clang::lifetime_capture_by(self)]]); + void explicit_this2(this T& self, const int &x [[clang::lifetime_capture_by(this)]]); // expected-error {{argument references unavailable implicit 'this'}} }; diff --git a/clang/test/SemaCXX/attr-nonnull.cpp b/clang/test/SemaCXX/attr-nonnull.cpp index 6f9119b519d09..0fba6b50cb354 100644 --- a/clang/test/SemaCXX/attr-nonnull.cpp +++ b/clang/test/SemaCXX/attr-nonnull.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -// RUN: %clang_cc1 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter struct S { S(const char *) __attribute__((nonnull(2))); @@ -11,6 +11,13 @@ struct S { void h(const char*) __attribute__((nonnull(1))); // \ expected-error{{invalid for the implicit this argument}} + + void i(this S* self, const char*) __attribute__((nonnull(1))); + + void j(this S* self, const char*) __attribute__((nonnull(2))); + + void k(this S* self, const char*) __attribute__((nonnull(3))); // \ + expected-error{{'nonnull' attribute parameter 1 is out of bounds}} }; void test() {