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 @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 3 additions & 2 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<FunctionDecl>(D))
HasThis = FD->isCXXInstanceMember();
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D))
HasThis = MethodDecl->isImplicitObjectMemberFunction();
}

/// A type into which \c ParamIdx can be serialized.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Sema/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ inline bool isInstanceMethod(const Decl *D) {
return false;
}

inline bool hasImplicitObjectParameter(const Decl *D) {
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D))
return MethodDecl->isImplicitObjectMemberFunction();
return false;
}

/// Diagnose mutually exclusive attributes when present on a given
/// declaration. Returns true if diagnosed.
template <typename AttrTy>
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 5 additions & 6 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CXXMethodDecl>(D))
IsCXXMember = MD->isInstance();
bool HasImplicitThisParam = hasImplicitObjectParameter(D);
bool IsVariadic = false;
if (const FunctionType *FnTy = D->getFunctionType())
IsVariadic = cast<FunctionProtoType>(FnTy)->isVariadic();
Expand All @@ -3553,11 +3551,12 @@ bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx,
else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(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;
Expand All @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<LifetimeCaptureByAttr *, 1> Attrs;
for (ParmVarDecl *PVD : FD->parameters())
if (auto *A = PVD->getAttr<LifetimeCaptureByAttr>())
Expand Down
23 changes: 22 additions & 1 deletion clang/test/CodeGenCXX/attr-callback.cpp
Original file line number Diff line number Diff line change
@@ -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 {

Expand Down Expand Up @@ -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}
7 changes: 6 additions & 1 deletion clang/test/SemaCXX/attr-callback-broken.cpp
Original file line number Diff line number Diff line change
@@ -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'}}
};
7 changes: 6 additions & 1 deletion clang/test/SemaCXX/attr-callback.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
// RUN: %clang_cc1 %s -std=c++23 -verify -fsyntax-only

// expected-no-diagnostics

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));
Expand Down
13 changes: 12 additions & 1 deletion clang/test/SemaCXX/attr-format.cpp
Original file line number Diff line number Diff line change
@@ -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 <stdarg.h>

int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
Expand All @@ -11,17 +11,28 @@ 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}}
void h2(const char*, ...) __attribute__((format(printf, 2, 1))); // \
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) {
Expand Down
3 changes: 3 additions & 0 deletions clang/test/SemaCXX/attr-lifetime-capture-by.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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'}}
};
11 changes: 9 additions & 2 deletions clang/test/SemaCXX/attr-nonnull.cpp
Original file line number Diff line number Diff line change
@@ -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)));

Expand All @@ -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() {
Expand Down