Skip to content

Commit 1fe3b2d

Browse files
authored
[Sema] Fix parameter index checks on explicit object member functions (llvm#165586)
With the C++23 explicit object parameter feature, it is no longer sufficient to only check if a function is an instance method to determine if it has an implicit this argument. That causes problems in attributes that have parameter indexes.
1 parent 63e45ef commit 1fe3b2d

File tree

12 files changed

+79
-20
lines changed

12 files changed

+79
-20
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ Bug Fixes to Attribute Support
451451
``[[gnu::error("some error")]]`` now correctly triggers an error. (#GH146520)
452452
- Fix a crash when the function name is empty in the `swift_name` attribute. (#GH157075)
453453
- Fixes crashes or missing diagnostics with the `device_kernel` attribute. (#GH161905)
454+
- Fix handling of parameter indexes when an attribute is applied to a C++23 explicit object member function.
454455

455456
Bug Fixes to C++ Support
456457
^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/Attr.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/AST/ASTFwd.h"
1717
#include "clang/AST/AttrIterator.h"
1818
#include "clang/AST/Decl.h"
19+
#include "clang/AST/DeclCXX.h"
1920
#include "clang/AST/Type.h"
2021
#include "clang/Basic/AttrKinds.h"
2122
#include "clang/Basic/AttributeCommonInfo.h"
@@ -327,8 +328,8 @@ class ParamIdx {
327328
ParamIdx(unsigned Idx, const Decl *D)
328329
: Idx(Idx), HasThis(false), IsValid(true) {
329330
assert(Idx >= 1 && "Idx must be one-origin");
330-
if (const auto *FD = dyn_cast<FunctionDecl>(D))
331-
HasThis = FD->isCXXInstanceMember();
331+
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D))
332+
HasThis = MethodDecl->isImplicitObjectMemberFunction();
332333
}
333334

334335
/// A type into which \c ParamIdx can be serialized.

clang/include/clang/Sema/Attr.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ inline bool isInstanceMethod(const Decl *D) {
123123
return false;
124124
}
125125

126+
inline bool hasImplicitObjectParameter(const Decl *D) {
127+
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D))
128+
return MethodDecl->isImplicitObjectMemberFunction();
129+
return false;
130+
}
131+
126132
/// Diagnose mutually exclusive attributes when present on a given
127133
/// declaration. Returns true if diagnosed.
128134
template <typename AttrTy>

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,13 +2608,13 @@ class Sema final : public SemaBase {
26082608
};
26092609

26102610
/// Given a function and its FormatAttr or FormatMatchesAttr info, attempts to
2611-
/// populate the FomatStringInfo parameter with the attribute's correct
2611+
/// populate the FormatStringInfo parameter with the attribute's correct
26122612
/// format_idx and firstDataArg. Returns true when the format fits the
26132613
/// function and the FormatStringInfo has been populated.
26142614
static bool getFormatStringInfo(const Decl *Function, unsigned FormatIdx,
26152615
unsigned FirstArg, FormatStringInfo *FSI);
26162616
static bool getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg,
2617-
bool IsCXXMember, bool IsVariadic,
2617+
bool HasImplicitThisParam, bool IsVariadic,
26182618
FormatStringInfo *FSI);
26192619

26202620
// Used by C++ template instantiation.
@@ -5119,7 +5119,7 @@ class Sema final : public SemaBase {
51195119
// In C++ the implicit 'this' function parameter also counts.
51205120
// Parameters are counted from one.
51215121
bool HP = hasFunctionProto(D);
5122-
bool HasImplicitThisParam = isInstanceMethod(D);
5122+
bool HasImplicitThisParam = hasImplicitObjectParameter(D);
51235123
bool IV = HP && isFunctionOrMethodVariadic(D);
51245124
unsigned NumParams =
51255125
(HP ? getFunctionOrMethodNumParams(D) : 0) + HasImplicitThisParam;

clang/lib/Sema/SemaChecking.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3542,9 +3542,7 @@ bool Sema::ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum) {
35423542

35433543
bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx,
35443544
unsigned FirstArg, FormatStringInfo *FSI) {
3545-
bool IsCXXMember = false;
3546-
if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
3547-
IsCXXMember = MD->isInstance();
3545+
bool HasImplicitThisParam = hasImplicitObjectParameter(D);
35483546
bool IsVariadic = false;
35493547
if (const FunctionType *FnTy = D->getFunctionType())
35503548
IsVariadic = cast<FunctionProtoType>(FnTy)->isVariadic();
@@ -3553,11 +3551,12 @@ bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx,
35533551
else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(D))
35543552
IsVariadic = OMD->isVariadic();
35553553

3556-
return getFormatStringInfo(FormatIdx, FirstArg, IsCXXMember, IsVariadic, FSI);
3554+
return getFormatStringInfo(FormatIdx, FirstArg, HasImplicitThisParam,
3555+
IsVariadic, FSI);
35573556
}
35583557

35593558
bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg,
3560-
bool IsCXXMember, bool IsVariadic,
3559+
bool HasImplicitThisParam, bool IsVariadic,
35613560
FormatStringInfo *FSI) {
35623561
if (FirstArg == 0)
35633562
FSI->ArgPassingKind = FAPK_VAList;
@@ -3571,7 +3570,7 @@ bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg,
35713570
// The way the format attribute works in GCC, the implicit this argument
35723571
// of member functions is counted. However, it doesn't appear in our own
35733572
// lists, so decrement format_idx in that case.
3574-
if (IsCXXMember) {
3573+
if (HasImplicitThisParam) {
35753574
if(FSI->FormatIdx == 0)
35763575
return false;
35773576
--FSI->FormatIdx;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3785,7 +3785,7 @@ static bool handleFormatAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
37853785

37863786
// In C++ the implicit 'this' function parameter also counts, and they are
37873787
// counted from one.
3788-
bool HasImplicitThisParam = isInstanceMethod(D);
3788+
bool HasImplicitThisParam = hasImplicitObjectParameter(D);
37893789
Info->NumArgs = getFunctionOrMethodNumParams(D) + HasImplicitThisParam;
37903790

37913791
Info->Identifier = AL.getArgAsIdent(0)->getIdentifierInfo();
@@ -3926,7 +3926,7 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
39263926
return;
39273927
}
39283928

3929-
bool HasImplicitThisParam = isInstanceMethod(D);
3929+
bool HasImplicitThisParam = hasImplicitObjectParameter(D);
39303930
int32_t NumArgs = getFunctionOrMethodNumParams(D);
39313931

39323932
FunctionDecl *FD = D->getAsFunction();
@@ -4110,7 +4110,7 @@ static void handleLifetimeCaptureByAttr(Sema &S, Decl *D,
41104110
}
41114111

41124112
void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) {
4113-
bool HasImplicitThisParam = isInstanceMethod(FD);
4113+
bool HasImplicitThisParam = hasImplicitObjectParameter(FD);
41144114
SmallVector<LifetimeCaptureByAttr *, 1> Attrs;
41154115
for (ParmVarDecl *PVD : FD->parameters())
41164116
if (auto *A = PVD->getAttr<LifetimeCaptureByAttr>())

clang/test/CodeGenCXX/attr-callback.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s
1+
// RUN: %clang_cc1 -triple i386-unknown-unknown -std=c++23 %s -emit-llvm -o - | FileCheck %s
22

33
struct Base {
44

@@ -47,9 +47,30 @@ struct Derived_2 : public Base {
4747
// CHECK-NOT: !callback
4848
void Derived_2::virtual_1(void (*callback)(void)) {}
4949

50+
class ExplicitParameterObject {
51+
__attribute__((callback(1, 0))) void implicit_this_idx(void (*callback)(ExplicitParameterObject*));
52+
__attribute__((callback(1, this))) void implicit_this_identifier(void (*callback)(ExplicitParameterObject*));
53+
__attribute__((callback(2, 1))) void explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*));
54+
__attribute__((callback(2, self))) void explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*));
55+
};
56+
57+
// CHECK-DAG: define{{.*}} void @_ZN23ExplicitParameterObject17implicit_this_idxEPFvPS_E({{[^!]*!callback}} ![[cid3:[0-9]+]]
58+
void ExplicitParameterObject::implicit_this_idx(void (*callback)(ExplicitParameterObject*)) {}
59+
60+
// CHECK-DAG: define{{.*}} void @_ZN23ExplicitParameterObject24implicit_this_identifierEPFvPS_E({{[^!]*!callback}} ![[cid3]]
61+
void ExplicitParameterObject::implicit_this_identifier(void (*callback)(ExplicitParameterObject*)) {}
62+
63+
// CHECK-DAG: define{{.*}} void @_ZNH23ExplicitParameterObject17explicit_this_idxEPS_PFvS0_E({{[^!]*!callback}} ![[cid3]]
64+
void ExplicitParameterObject::explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)) {}
65+
66+
// CHECK-DAG: define{{.*}} void @_ZNH23ExplicitParameterObject24explicit_this_identifierEPS_PFvS0_E({{[^!]*!callback}} ![[cid3]]
67+
void ExplicitParameterObject::explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*)) {}
68+
5069
// CHECK-DAG: ![[cid0]] = !{![[cid0b:[0-9]+]]}
5170
// CHECK-DAG: ![[cid0b]] = !{i64 1, i1 false}
5271
// CHECK-DAG: ![[cid1]] = !{![[cid1b:[0-9]+]]}
5372
// CHECK-DAG: ![[cid1b]] = !{i64 2, i1 false}
5473
// CHECK-DAG: ![[cid2]] = !{![[cid2b:[0-9]+]]}
5574
// CHECK-DAG: ![[cid2b]] = !{i64 1, i64 0, i64 -1, i64 0, i1 false}
75+
// CHECK-DAG: ![[cid3]] = !{![[cid3b:[0-9]+]]}
76+
// CHECK-DAG: ![[cid3b]] = !{i64 1, i64 0, i1 false}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
// RUN: %clang_cc1 %s -verify -fsyntax-only
1+
// RUN: %clang_cc1 %s -std=c++23 -verify -fsyntax-only
22

33
class C_in_class {
44
#define HAS_THIS
55
#include "../Sema/attr-callback-broken.c"
66
#undef HAS_THIS
77
};
8+
9+
class ExplicitParameterObject {
10+
__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'}}
11+
__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'}}
12+
};

clang/test/SemaCXX/attr-callback.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
// RUN: %clang_cc1 %s -verify -fsyntax-only
1+
// RUN: %clang_cc1 %s -std=c++23 -verify -fsyntax-only
22

33
// expected-no-diagnostics
44

55
class C_in_class {
66
#include "../Sema/attr-callback.c"
77
};
88

9+
class ExplicitParameterObject {
10+
__attribute__((callback(2, 1))) void explicit_this_idx(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*));
11+
__attribute__((callback(2, self))) void explicit_this_identifier(this ExplicitParameterObject* self, void (*callback)(ExplicitParameterObject*));
12+
};
13+
914
struct Base {
1015

1116
void no_args_1(void (*callback)(void));

clang/test/SemaCXX/attr-format.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fsyntax-only -Wformat-nonliteral -verify %s
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++23 -Wformat-nonliteral -verify %s
22
#include <stdarg.h>
33

44
int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
@@ -11,17 +11,28 @@ struct S {
1111
// the format argument is argument 2 here.
1212
void g(const char*, ...) __attribute__((format(printf, 2, 3)));
1313
const char* g2(const char*) __attribute__((format_arg(2)));
14+
// From C++23 'this' can also be specified explicitly.
15+
void g3(this S&, const char *, ...) __attribute__((format(printf, 2, 3)));
16+
void g4(this const char* s, ...) __attribute__((format(printf, 1, 2)));
17+
consteval operator const char*() const { return "%f"; } // #g4_fmt_string
1418

1519
void h(const char*, ...) __attribute__((format(printf, 1, 4))); // \
1620
expected-error{{implicit this argument as the format string}}
1721
void h2(const char*, ...) __attribute__((format(printf, 2, 1))); // \
1822
expected-error{{out of bounds}}
1923
const char* h3(const char*) __attribute__((format_arg(1))); // \
2024
expected-error{{invalid for the implicit this argument}}
25+
void h4(this S&, const char *, ...) __attribute__((format(printf, 1, 3))); // \
26+
expected-error {{format argument not a string type}}
2127

2228
void operator() (const char*, ...) __attribute__((format(printf, 2, 3)));
2329
};
2430

31+
void s() {
32+
S().g4(4); // expected-warning {{format specifies type 'double' but the argument has type 'int'}}
33+
// expected-note@#g4_fmt_string {{format string is defined here}}
34+
}
35+
2536
// PR5521
2637
struct A { void a(const char*,...) __attribute((format(printf,2,3))); };
2738
void b(A x) {

0 commit comments

Comments
 (0)