[clang] [Clang] Static and explicit object member functions with the same parameter-type-lists (PR #93430)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 5 00:53:58 PDT 2024
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/93430
>From 131f515c1341122896ea3c9624751a634db06cb7 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 27 May 2024 01:16:06 +0200
Subject: [PATCH 1/6] [Clang] Static and explicit object member functions with
the same parameter-type-lists
Implement wg21.link/P2797.
Because taking the address of an explicit object member function
results in a function pointer, a call expression where
the id-expression is an explicit object member is made to behave
consistently with that model.
This change forces clang to perform overload resolution
in the presence of an ixpression of the form `(&Foo::bar)(args...)`,
which we previously failed to do consistently.
---
clang/docs/ReleaseNotes.rst | 3 +
clang/include/clang/AST/ExprCXX.h | 4 +
clang/include/clang/Sema/Overload.h | 4 +
clang/lib/Sema/SemaExpr.cpp | 27 ++++++-
clang/lib/Sema/SemaOverload.cpp | 74 ++++++++++++++++---
clang/test/CXX/drs/cwg1xx.cpp | 8 +-
clang/test/CXX/drs/cwg26xx.cpp | 26 +++++++
clang/test/CXX/drs/cwg2771.cpp | 18 +++++
clang/test/CodeGenCXX/cxx2b-deducing-this.cpp | 20 +++++
clang/test/SemaCXX/cxx2b-deducing-this.cpp | 27 +++++++
clang/www/cxx_dr_status.html | 6 +-
clang/www/cxx_status.html | 2 +-
12 files changed, 197 insertions(+), 22 deletions(-)
create mode 100644 clang/test/CXX/drs/cwg2771.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 825e91876ffce..0a945a9989b0d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -209,6 +209,9 @@ C++23 Feature Support
- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.
+- Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`.
+ This completes the support for "deducing this".
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index dbf693611a7fa..557e9fd99c293 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3027,6 +3027,7 @@ class OverloadExpr : public Expr {
struct FindResult {
OverloadExpr *Expression;
bool IsAddressOfOperand;
+ bool IsAddressOfOperandWithParen;
bool HasFormOfMemberPointer;
};
@@ -3039,6 +3040,7 @@ class OverloadExpr : public Expr {
assert(E->getType()->isSpecificBuiltinType(BuiltinType::Overload));
FindResult Result;
+ bool HasParen = isa<ParenExpr>(E);
E = E->IgnoreParens();
if (isa<UnaryOperator>(E)) {
@@ -3048,10 +3050,12 @@ class OverloadExpr : public Expr {
Result.HasFormOfMemberPointer = (E == Ovl && Ovl->getQualifier());
Result.IsAddressOfOperand = true;
+ Result.IsAddressOfOperandWithParen = HasParen;
Result.Expression = Ovl;
} else {
Result.HasFormOfMemberPointer = false;
Result.IsAddressOfOperand = false;
+ Result.IsAddressOfOperandWithParen = false;
Result.Expression = cast<OverloadExpr>(E);
}
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 76311b00d2fc5..64cdd6cdf043d 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -899,6 +899,8 @@ class Sema;
/// object argument.
bool IgnoreObjectArgument : 1;
+ bool TookAddressOfOverload : 1;
+
/// True if the candidate was found using ADL.
CallExpr::ADLCallKind IsADLCandidate : 1;
@@ -999,6 +1001,8 @@ class Sema;
/// Initialization of an object of class type by constructor,
/// using either a parenthesized or braced list of arguments.
CSK_InitByConstructor,
+
+ CSK_AddressOfOverloadSet,
};
/// Information about operator rewrites to consider when adding operator
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 410f80ae864a1..15496f3323b02 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5813,6 +5813,24 @@ static TypoCorrection TryTypoCorrectionForCall(Sema &S, Expr *Fn,
return TypoCorrection();
}
+static bool isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) {
+ if (!isa<ParenExpr>(Fn))
+ return false;
+
+ Fn = Fn->IgnoreParens();
+ auto *UO = dyn_cast<UnaryOperator>(Fn);
+ if (!UO)
+ return false;
+ assert(cast<UnaryOperator>(Fn)->getOpcode() == UO_AddrOf);
+ if (auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) {
+ return DRE->hasQualifier();
+ }
+ if (auto *OVL = dyn_cast<OverloadExpr>(UO->getSubExpr()->IgnoreParens())) {
+ return OVL->getQualifier();
+ }
+ return false;
+}
+
/// ConvertArgumentsForCall - Converts the arguments specified in
/// Args/NumArgs to the parameter types of the function FDecl with
/// function prototype Proto. Call is the call expression itself, and
@@ -5834,9 +5852,12 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
// C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
// assignment, to the types of the corresponding parameter, ...
+
+ bool AddressOf = isParenthetizedAndQualifiedAddressOfExpr(Fn);
bool HasExplicitObjectParameter =
- FDecl && FDecl->hasCXXExplicitFunctionObjectParameter();
- unsigned ExplicitObjectParameterOffset = HasExplicitObjectParameter ? 1 : 0;
+ !AddressOf && FDecl && FDecl->hasCXXExplicitFunctionObjectParameter();
+ unsigned ExplicitObjectParameterOffset =
+ HasExplicitObjectParameter && !AddressOf ? 1 : 0;
unsigned NumParams = Proto->getNumParams();
bool Invalid = false;
unsigned MinArgs = FDecl ? FDecl->getMinRequiredArguments() : NumParams;
@@ -6546,7 +6567,7 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
OverloadExpr::FindResult find = OverloadExpr::find(Fn);
// We aren't supposed to apply this logic if there's an '&' involved.
- if (!find.HasFormOfMemberPointer) {
+ if (!find.HasFormOfMemberPointer || find.IsAddressOfOperandWithParen) {
if (Expr::hasAnyTypeDependentArguments(ArgExprs))
return CallExpr::Create(Context, Fn, ArgExprs, Context.DependentTy,
VK_PRValue, RParenLoc, CurFPFeatureOverrides());
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 0c89fca8d38eb..17cccefa8e929 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6971,6 +6971,7 @@ void Sema::AddOverloadCandidate(
Candidate.IsSurrogate = false;
Candidate.IsADLCandidate = IsADLCandidate;
Candidate.IgnoreObjectArgument = false;
+ Candidate.TookAddressOfOverload = false;
Candidate.ExplicitCallArguments = Args.size();
// Explicit functions are not actually candidates at all if we're not
@@ -7545,10 +7546,19 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument = false;
+ Candidate.TookAddressOfOverload =
+ CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet;
Candidate.ExplicitCallArguments = Args.size();
- unsigned NumParams = Method->getNumExplicitParams();
- unsigned ExplicitOffset = Method->isExplicitObjectMemberFunction() ? 1 : 0;
+ bool IgnoreExplicitObject =
+ (Method->isExplicitObjectMemberFunction() &&
+ CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_AddressOfOverloadSet);
+ unsigned ExplicitOffset =
+ !IgnoreExplicitObject && Method->isExplicitObjectMemberFunction() ? 1 : 0;
+ unsigned NumParams = Method->getNumParams() - ExplicitOffset;
+ if (CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet)
+ NumParams += int(Method->isImplicitObjectMemberFunction());
// (C++ 13.3.2p2): A candidate function having fewer than m
// parameters is viable only if it has an ellipsis in its parameter
@@ -7566,7 +7576,12 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// (8.3.6). For the purposes of overload resolution, the
// parameter list is truncated on the right, so that there are
// exactly m parameters.
- unsigned MinRequiredArgs = Method->getMinRequiredExplicitArguments();
+ unsigned MinRequiredArgs = Method->getMinRequiredArguments() - ExplicitOffset;
+ if (CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_AddressOfOverloadSet &&
+ Method->isImplicitObjectMemberFunction())
+ MinRequiredArgs++;
+
if (Args.size() < MinRequiredArgs && !PartialOverloading) {
// Not enough arguments.
Candidate.Viable = false;
@@ -7636,7 +7651,16 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// exist for each argument an implicit conversion sequence
// (13.3.3.1) that converts that argument to the corresponding
// parameter of F.
- QualType ParamType = Proto->getParamType(ArgIdx + ExplicitOffset);
+ QualType ParamType;
+ if (CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_AddressOfOverloadSet &&
+ Method->isImplicitObjectMemberFunction()) {
+ ParamType = ArgIdx == 0
+ ? Method->getFunctionObjectParameterReferenceType()
+ : Proto->getParamType(ArgIdx - 1);
+ } else {
+ ParamType = Proto->getParamType(ArgIdx + ExplicitOffset);
+ }
Candidate.Conversions[ConvIdx]
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
SuppressUserConversions,
@@ -7998,6 +8022,7 @@ void Sema::AddConversionCandidate(
Candidate.Function = Conversion;
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument = false;
+ Candidate.TookAddressOfOverload = false;
Candidate.FinalConversion.setAsIdentityConversion();
Candidate.FinalConversion.setFromType(ConvType);
Candidate.FinalConversion.setAllToTypes(ToType);
@@ -8200,6 +8225,7 @@ void Sema::AddTemplateConversionCandidate(
Candidate.FailureKind = ovl_fail_bad_deduction;
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument = false;
+ Candidate.TookAddressOfOverload = false;
Candidate.ExplicitCallArguments = 1;
Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
Info);
@@ -8240,6 +8266,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
Candidate.Viable = true;
Candidate.IsSurrogate = true;
Candidate.IgnoreObjectArgument = false;
+ Candidate.TookAddressOfOverload = false;
Candidate.ExplicitCallArguments = Args.size();
// Determine the implicit conversion sequence for the implicit
@@ -8465,6 +8492,7 @@ void Sema::AddBuiltinCandidate(QualType *ParamTys, ArrayRef<Expr *> Args,
Candidate.Function = nullptr;
Candidate.IsSurrogate = false;
Candidate.IgnoreObjectArgument = false;
+ Candidate.TookAddressOfOverload = false;
std::copy(ParamTys, ParamTys + Args.size(), Candidate.BuiltinParamTypes);
// Determine the implicit conversion sequences for each of the
@@ -10929,6 +10957,12 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
if (Best->Function && Best->Function->isDeleted())
return OR_Deleted;
+ if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
+ Kind == CSK_AddressOfOverloadSet && M &&
+ M->isImplicitObjectMemberFunction()) {
+ return OR_No_Viable_Function;
+ }
+
if (!EquivalentCands.empty())
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
EquivalentCands);
@@ -11538,7 +11572,8 @@ static bool CheckArityMismatch(Sema &S, OverloadCandidate *Cand,
/// General arity mismatch diagnosis over a candidate in a candidate set.
static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
- unsigned NumFormalArgs) {
+ unsigned NumFormalArgs,
+ bool IsAddressOf = false) {
assert(isa<FunctionDecl>(D) &&
"The templated declaration should at least be a function"
" when diagnosing bad template argument deduction due to too many"
@@ -11551,7 +11586,8 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
unsigned MinParams = Fn->getMinRequiredExplicitArguments();
// at least / at most / exactly
- bool HasExplicitObjectParam = Fn->hasCXXExplicitFunctionObjectParameter();
+ bool HasExplicitObjectParam =
+ !IsAddressOf && Fn->hasCXXExplicitFunctionObjectParameter();
unsigned ParamCount = FnTy->getNumParams() - (HasExplicitObjectParam ? 1 : 0);
unsigned mode, modeCount;
if (NumFormalArgs < MinParams) {
@@ -11593,7 +11629,8 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
static void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand,
unsigned NumFormalArgs) {
if (!CheckArityMismatch(S, Cand, NumFormalArgs))
- DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs);
+ DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs,
+ Cand->TookAddressOfOverload);
}
static TemplateDecl *getDescribedTemplate(Decl *Templated) {
@@ -14076,6 +14113,21 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
}
case OR_No_Viable_Function: {
+ if (*Best != CandidateSet->end() &&
+ CandidateSet->getKind() ==
+ clang::OverloadCandidateSet::CSK_AddressOfOverloadSet) {
+ if (CXXMethodDecl *M =
+ dyn_cast_if_present<CXXMethodDecl>((*Best)->Function);
+ M && M->isImplicitObjectMemberFunction()) {
+ CandidateSet->NoteCandidates(
+ PartialDiagnosticAt(
+ Fn->getBeginLoc(),
+ SemaRef.PDiag(diag::err_member_call_without_object) << 0 << M),
+ SemaRef, OCD_AmbiguousCandidates, Args);
+ return ExprError();
+ }
+ }
+
// Try to recover by looking for viable functions which the user might
// have meant to call.
ExprResult Recovery = BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc,
@@ -14167,8 +14219,10 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
Expr *ExecConfig,
bool AllowTypoCorrection,
bool CalleesAddressIsTaken) {
- OverloadCandidateSet CandidateSet(Fn->getExprLoc(),
- OverloadCandidateSet::CSK_Normal);
+ OverloadCandidateSet CandidateSet(
+ Fn->getExprLoc(), CalleesAddressIsTaken
+ ? OverloadCandidateSet::CSK_AddressOfOverloadSet
+ : OverloadCandidateSet::CSK_Normal);
ExprResult result;
if (buildOverloadedCallSet(S, Fn, ULE, Args, LParenLoc, &CandidateSet,
@@ -16333,7 +16387,7 @@ ExprResult Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
assert(UnOp->getOpcode() == UO_AddrOf &&
"Can only take the address of an overloaded function");
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn)) {
- if (Method->isStatic()) {
+ if (!Method->isImplicitObjectMemberFunction()) {
// Do nothing: static member functions aren't any different
// from non-member functions.
} else {
diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp
index a8f9b705a9866..21859fc6b1cbf 100644
--- a/clang/test/CXX/drs/cwg1xx.cpp
+++ b/clang/test/CXX/drs/cwg1xx.cpp
@@ -843,23 +843,21 @@ namespace cwg161 { // cwg161: 3.1
};
}
-namespace cwg162 { // cwg162: no
+namespace cwg162 { // cwg162: 19
struct A {
char &f(char);
static int &f(int);
void g() {
int &a = (&A::f)(0);
- // FIXME: expected-error at -1 {{reference to overloaded function could not be resolved; did you mean to call it?}}
char &b = (&A::f)('0');
- // expected-error at -1 {{reference to overloaded function could not be resolved; did you mean to call it?}}
+ // expected-error at -1 {{non-const lvalue reference to type 'char' cannot bind to a value of unrelated type 'int'}}
}
};
int &c = (&A::f)(0);
- // FIXME: expected-error at -1 {{reference to overloaded function could not be resolved; did you mean to call it?}}
char &d = (&A::f)('0');
- // expected-error at -1 {{reference to overloaded function could not be resolved; did you mean to call it?}}
+ // expected-error at -1 {{non-const lvalue reference to type 'char' cannot bind to a value of unrelated type 'int'}}
}
// cwg163: na
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index d3c5b5bb7b6b9..6c91377643fb9 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -240,3 +240,29 @@ void test() {
}
}
#endif
+
+
+#if __cplusplus >= 202302L
+namespace cwg2692 { // cwg2692: 19
+
+ struct A {
+ static void f(A); // #cwg2692-1
+ void f(this A); // #cwg2692-2
+
+ void g();
+ };
+
+ void A::g() {
+ (&A::f)(A()); // expected-error {{call to 'f' is ambiguous}}
+ // expected-note@#cwg2692-1 {{candidate}}
+ // expected-note@#cwg2692-2 {{candidate}}
+
+
+
+ (&A::f)(); // expected-error {{no matching function for call to 'f'}}
+ // expected-note@#cwg2692-1 {{candidate function not viable: requires 1 argument, but 0 were provided}}
+ // expected-note@#cwg2692-2 {{candidate function not viable: requires at most 1 argument, but 0 were provided}}
+ }
+
+}
+#endif
diff --git a/clang/test/CXX/drs/cwg2771.cpp b/clang/test/CXX/drs/cwg2771.cpp
new file mode 100644
index 0000000000000..e877e6fe4a5fe
--- /dev/null
+++ b/clang/test/CXX/drs/cwg2771.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++23 %s -ast-dump | FileCheck --check-prefixes=CXX23 %s
+
+namespace cwg2771 { // cwg2771: 18
+
+struct A{
+ int a;
+ void cwg2771(){
+ int* r = &a;
+ }
+};
+// CXX23: CXXMethodDecl{{.+}}cwg2771
+// CXX23-NEXT: CompoundStmt
+// CXX23-NEXT: DeclStmt
+// CXX23-NEXT: VarDecl
+// CXX23-NEXT: UnaryOperator
+// CXX23-NEXT: MemberExpr
+// CXX23-NEXT: CXXThisExpr{{.+}}'cwg2771::A *'
+}
diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
index 649fe2afbf4e9..f9f9fbd7397f8 100644
--- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
@@ -245,3 +245,23 @@ void f() {
d();
}
}
+
+
+namespace P2797 {
+struct C {
+ void c(this const C&); // #first
+ void c() &; // #second
+ static void c(int = 0); // #third
+
+ void d() {
+ (&C::c)(C{});
+ (&C::c)();
+ }
+};
+void test() {
+ (void)C{}.d();
+}
+// CHECK-LABEL: {{.*}} @_ZN5P27971C1dEv
+// CHECK: call void @_ZNH5P27971C1cERKS0_
+// CHECK: call void @_ZN5P27971C1cEi
+}
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index cdb9d1324b974..86046497c2ef2 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -893,3 +893,30 @@ void g() {
a * lval;
}
}
+
+namespace P2797 {
+struct C {
+ void c(this const C&); // #first
+ void c() &; // #second
+ static void c(int = 0); // #third
+
+ void d() {
+ c(); // expected-error {{call to member function 'c' is ambiguous}}
+ // expected-note@#first {{candidate function}}
+ // expected-note@#second {{candidate function}}
+ // expected-note@#third {{candidate function}}
+
+ (C::c)(); // expected-error {{call to member function 'c' is ambiguous}}
+ // expected-note@#first {{candidate function}}
+ // expected-note@#second {{candidate function}}
+ // expected-note@#third {{candidate function}}
+
+ (&(C::c))(); // expected-error {{cannot create a non-constant pointer to member function}}
+ (&C::c)(C{});
+ (&C::c)(*this); // expected-error {{call to non-static member function without an object argument}}
+ // expected-note@#second {{candidate function}}
+
+ (&C::c)();
+ }
+};
+}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 4cce88fe0490f..7df6c0a05a487 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -1010,7 +1010,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/162.html">162</a></td>
<td>CD1</td>
<td>(<TT>&C::f)()</TT> with nonstatic members</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 19</td>
</tr>
<tr id="163">
<td><a href="https://cplusplus.github.io/CWG/issues/163.html">163</a></td>
@@ -15960,7 +15960,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2692.html">2692</a></td>
<td>C++23</td>
<td>Static and explicit object member functions with the same parameter-type-lists</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 19</td>
</tr>
<tr class="open" id="2693">
<td><a href="https://cplusplus.github.io/CWG/issues/2693.html">2693</a></td>
@@ -16435,7 +16435,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2771.html">2771</a></td>
<td>DR</td>
<td>Transformation for <I>unqualified-id</I>s in address operator</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="full" align="center">Clang 18</td>
</tr>
<tr id="2772">
<td><a href="https://cplusplus.github.io/CWG/issues/2772.html">2772</a></td>
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 45416170b16e5..65dd31a0fb802 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -283,7 +283,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
</tr>
<tr>
<td><a href="https://wg21.link/P2797R0">P2797R0</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 19</td>
</tr>
<tr>
<td rowspan=2>Change scope of lambda trailing-return-type</td>
>From 5bdf858ae5677e601132009892f957932209e454 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 28 May 2024 15:04:19 +0200
Subject: [PATCH 2/6] fix tests
---
clang/lib/Sema/SemaExpr.cpp | 3 +--
clang/lib/Sema/SemaOverload.cpp | 31 +++++++++++++++++-----
clang/test/CXX/drs/cwg26xx.cpp | 2 +-
clang/test/SemaCXX/cxx2b-deducing-this.cpp | 2 --
4 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 15496f3323b02..eb5ae072a465b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5819,9 +5819,8 @@ static bool isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) {
Fn = Fn->IgnoreParens();
auto *UO = dyn_cast<UnaryOperator>(Fn);
- if (!UO)
+ if (!UO || UO->getOpcode() != clang::UO_AddrOf)
return false;
- assert(cast<UnaryOperator>(Fn)->getOpcode() == UO_AddrOf);
if (auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) {
return DRE->hasQualifier();
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 17cccefa8e929..21a9fe09d1bc2 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7741,6 +7741,7 @@ void Sema::AddMethodTemplateCandidate(
Candidate.IgnoreObjectArgument =
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
ObjectType.isNull();
+ Candidate.TookAddressOfOverload = false;
Candidate.ExplicitCallArguments = Args.size();
if (Result == TemplateDeductionResult::NonDependentConversionFailure)
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -7831,6 +7832,7 @@ void Sema::AddTemplateOverloadCandidate(
Candidate.IgnoreObjectArgument =
isa<CXXMethodDecl>(Candidate.Function) &&
!isa<CXXConstructorDecl>(Candidate.Function);
+ Candidate.TookAddressOfOverload = false;
Candidate.ExplicitCallArguments = Args.size();
if (Result == TemplateDeductionResult::NonDependentConversionFailure)
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -11542,9 +11544,10 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand,
/// candidates. This is not covered by the more general DiagnoseArityMismatch()
/// over a candidate in any candidate set.
static bool CheckArityMismatch(Sema &S, OverloadCandidate *Cand,
- unsigned NumArgs) {
+ unsigned NumArgs, bool IsAddressOf = false) {
FunctionDecl *Fn = Cand->Function;
- unsigned MinParams = Fn->getMinRequiredArguments();
+ unsigned MinParams = Fn->getMinRequiredExplicitArguments() +
+ ((IsAddressOf && !Fn->isStatic()) ? 1 : 0);
// With invalid overloaded operators, it's possible that we think we
// have an arity mismatch when in fact it looks like we have the
@@ -11583,13 +11586,17 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
// TODO: treat calls to a missing default constructor as a special case
const auto *FnTy = Fn->getType()->castAs<FunctionProtoType>();
- unsigned MinParams = Fn->getMinRequiredExplicitArguments();
+ unsigned MinParams = Fn->getMinRequiredExplicitArguments() +
+ ((IsAddressOf && !Fn->isStatic()) ? 1 : 0);
// at least / at most / exactly
bool HasExplicitObjectParam =
!IsAddressOf && Fn->hasCXXExplicitFunctionObjectParameter();
- unsigned ParamCount = FnTy->getNumParams() - (HasExplicitObjectParam ? 1 : 0);
+
+ unsigned ParamCount =
+ Fn->getNumNonObjectParams() + ((IsAddressOf && !Fn->isStatic()) ? 1 : 0);
unsigned mode, modeCount;
+
if (NumFormalArgs < MinParams) {
if (MinParams != ParamCount || FnTy->isVariadic() ||
FnTy->isTemplateVariadic())
@@ -11609,7 +11616,7 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Found, Fn, CRK_None, Description);
- if (modeCount == 1 &&
+ if (modeCount == 1 && !IsAddressOf &&
Fn->getParamDecl(HasExplicitObjectParam ? 1 : 0)->getDeclName())
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
@@ -11628,7 +11635,7 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
/// Arity mismatch diagnosis specific to a function overload candidate.
static void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand,
unsigned NumFormalArgs) {
- if (!CheckArityMismatch(S, Cand, NumFormalArgs))
+ if (!CheckArityMismatch(S, Cand, NumFormalArgs, Cand->TookAddressOfOverload))
DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs,
Cand->TookAddressOfOverload);
}
@@ -12070,6 +12077,13 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
Cand->FailureKind != ovl_fail_bad_conversion)
return;
+ // Skip implicit member functions when trying to resolve
+ // the address of a an overload set for a function pointer.
+ if (Cand->TookAddressOfOverload &&
+ !Cand->Function->hasCXXExplicitFunctionObjectParameter() &&
+ !Cand->Function->isStatic())
+ return;
+
// Note deleted candidates, but only if they're viable.
if (Cand->Viable) {
if (Fn->isDeleted()) {
@@ -16447,7 +16461,10 @@ ExprResult Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
}
QualType Type = Fn->getType();
- ExprValueKind ValueKind = getLangOpts().CPlusPlus ? VK_LValue : VK_PRValue;
+ ExprValueKind ValueKind =
+ getLangOpts().CPlusPlus && !Fn->hasCXXExplicitFunctionObjectParameter()
+ ? VK_LValue
+ : VK_PRValue;
// FIXME: Duplicated from BuildDeclarationNameExpr.
if (unsigned BID = Fn->getBuiltinID()) {
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index 6c91377643fb9..76c02ec2f4ea9 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -261,7 +261,7 @@ namespace cwg2692 { // cwg2692: 19
(&A::f)(); // expected-error {{no matching function for call to 'f'}}
// expected-note@#cwg2692-1 {{candidate function not viable: requires 1 argument, but 0 were provided}}
- // expected-note@#cwg2692-2 {{candidate function not viable: requires at most 1 argument, but 0 were provided}}
+ // expected-note@#cwg2692-2 {{candidate function not viable: requires 1 argument, but 0 were provided}}
}
}
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index 86046497c2ef2..2c19b091fabad 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -914,8 +914,6 @@ struct C {
(&(C::c))(); // expected-error {{cannot create a non-constant pointer to member function}}
(&C::c)(C{});
(&C::c)(*this); // expected-error {{call to non-static member function without an object argument}}
- // expected-note@#second {{candidate function}}
-
(&C::c)();
}
};
>From b2434776139910ef0216c51774e3bf3bc65c95df Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 29 May 2024 10:20:04 +0200
Subject: [PATCH 3/6] address review comments
---
clang/docs/ReleaseNotes.rst | 2 +-
clang/lib/Sema/SemaOverload.cpp | 27 ++++++++++++++-------------
clang/test/CXX/drs/cwg26xx.cpp | 19 ++++++++++---------
clang/test/CXX/drs/cwg2771.cpp | 3 ++-
4 files changed, 27 insertions(+), 24 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0a945a9989b0d..4ed610b1b6a50 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -209,7 +209,7 @@ C++23 Feature Support
- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.
-- Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`.
+- Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`_.
This completes the support for "deducing this".
C++2c Feature Support
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 21a9fe09d1bc2..d97cd2a8559aa 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7554,11 +7554,16 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
(Method->isExplicitObjectMemberFunction() &&
CandidateSet.getKind() ==
OverloadCandidateSet::CSK_AddressOfOverloadSet);
+ bool ImplicitObjectMethodTreatedAsStatic =
+ CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_AddressOfOverloadSet &&
+ Method->isImplicitObjectMemberFunction();
+
unsigned ExplicitOffset =
!IgnoreExplicitObject && Method->isExplicitObjectMemberFunction() ? 1 : 0;
- unsigned NumParams = Method->getNumParams() - ExplicitOffset;
- if (CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet)
- NumParams += int(Method->isImplicitObjectMemberFunction());
+
+ unsigned NumParams = Method->getNumParams() - ExplicitOffset +
+ int(ImplicitObjectMethodTreatedAsStatic);
// (C++ 13.3.2p2): A candidate function having fewer than m
// parameters is viable only if it has an ellipsis in its parameter
@@ -7576,11 +7581,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// (8.3.6). For the purposes of overload resolution, the
// parameter list is truncated on the right, so that there are
// exactly m parameters.
- unsigned MinRequiredArgs = Method->getMinRequiredArguments() - ExplicitOffset;
- if (CandidateSet.getKind() ==
- OverloadCandidateSet::CSK_AddressOfOverloadSet &&
- Method->isImplicitObjectMemberFunction())
- MinRequiredArgs++;
+ unsigned MinRequiredArgs = Method->getMinRequiredArguments() -
+ ExplicitOffset +
+ int(ImplicitObjectMethodTreatedAsStatic);
if (Args.size() < MinRequiredArgs && !PartialOverloading) {
// Not enough arguments.
@@ -7652,9 +7655,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// (13.3.3.1) that converts that argument to the corresponding
// parameter of F.
QualType ParamType;
- if (CandidateSet.getKind() ==
- OverloadCandidateSet::CSK_AddressOfOverloadSet &&
- Method->isImplicitObjectMemberFunction()) {
+ if (ImplicitObjectMethodTreatedAsStatic) {
ParamType = ArgIdx == 0
? Method->getFunctionObjectParameterReferenceType()
: Proto->getParamType(ArgIdx - 1);
@@ -16402,8 +16403,8 @@ ExprResult Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
"Can only take the address of an overloaded function");
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn)) {
if (!Method->isImplicitObjectMemberFunction()) {
- // Do nothing: static member functions aren't any different
- // from non-member functions.
+ // Do nothing: the address of static and
+ // explicit object member functions is a (non-member) function pointer.
} else {
// Fix the subexpression, which really has to be an
// UnresolvedLookupExpr holding an overloaded member function
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index 76c02ec2f4ea9..7e79b150f36d7 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -242,8 +242,8 @@ void test() {
#endif
-#if __cplusplus >= 202302L
namespace cwg2692 { // cwg2692: 19
+#if __cplusplus >= 202302L
struct A {
static void f(A); // #cwg2692-1
@@ -253,16 +253,17 @@ namespace cwg2692 { // cwg2692: 19
};
void A::g() {
- (&A::f)(A()); // expected-error {{call to 'f' is ambiguous}}
- // expected-note@#cwg2692-1 {{candidate}}
- // expected-note@#cwg2692-2 {{candidate}}
+ (&A::f)(A());
+ // expected-error at -1 {{call to 'f' is ambiguous}}
+ // expected-note@#cwg2692-1 {{candidate}}
+ // expected-note@#cwg2692-2 {{candidate}}
- (&A::f)(); // expected-error {{no matching function for call to 'f'}}
- // expected-note@#cwg2692-1 {{candidate function not viable: requires 1 argument, but 0 were provided}}
- // expected-note@#cwg2692-2 {{candidate function not viable: requires 1 argument, but 0 were provided}}
+ (&A::f)();
+ // expected-error at -1 {{no matching function for call to 'f'}}
+ // expected-note@#cwg2692-1 {{candidate function not viable: requires 1 argument, but 0 were provided}}
+ // expected-note@#cwg2692-2 {{candidate function not viable: requires 1 argument, but 0 were provided}}
}
-
-}
#endif
+}
diff --git a/clang/test/CXX/drs/cwg2771.cpp b/clang/test/CXX/drs/cwg2771.cpp
index e877e6fe4a5fe..474660aa28440 100644
--- a/clang/test/CXX/drs/cwg2771.cpp
+++ b/clang/test/CXX/drs/cwg2771.cpp
@@ -15,4 +15,5 @@ struct A{
// CXX23-NEXT: UnaryOperator
// CXX23-NEXT: MemberExpr
// CXX23-NEXT: CXXThisExpr{{.+}}'cwg2771::A *'
-}
+
+} // namespace cwg2771
>From 16eed40d6b36f261525d35f1dc6db8e9f90ca04e Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 29 May 2024 10:23:16 +0200
Subject: [PATCH 4/6] address review comments
---
clang/lib/Sema/SemaExpr.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index eb5ae072a465b..772bf48f36c91 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5855,8 +5855,7 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
bool AddressOf = isParenthetizedAndQualifiedAddressOfExpr(Fn);
bool HasExplicitObjectParameter =
!AddressOf && FDecl && FDecl->hasCXXExplicitFunctionObjectParameter();
- unsigned ExplicitObjectParameterOffset =
- HasExplicitObjectParameter && !AddressOf ? 1 : 0;
+ unsigned ExplicitObjectParameterOffset = HasExplicitObjectParameter ? 1 : 0;
unsigned NumParams = Proto->getNumParams();
bool Invalid = false;
unsigned MinArgs = FDecl ? FDecl->getMinRequiredArguments() : NumParams;
>From ad4df6d1e0b26cdd815badbd4a159141ec76a829 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 4 Jun 2024 17:19:58 +0200
Subject: [PATCH 5/6] Address Erich's feedback
---
clang/include/clang/AST/ExprCXX.h | 11 ++++-------
clang/lib/Sema/SemaExpr.cpp | 4 ++++
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 557e9fd99c293..d2e8d93656359 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3025,10 +3025,10 @@ class OverloadExpr : public Expr {
public:
struct FindResult {
- OverloadExpr *Expression;
- bool IsAddressOfOperand;
- bool IsAddressOfOperandWithParen;
- bool HasFormOfMemberPointer;
+ OverloadExpr *Expression = nullptr;
+ bool IsAddressOfOperand = false;
+ bool IsAddressOfOperandWithParen = false;
+ bool HasFormOfMemberPointer = false;
};
/// Finds the overloaded expression in the given expression \p E of
@@ -3053,9 +3053,6 @@ class OverloadExpr : public Expr {
Result.IsAddressOfOperandWithParen = HasParen;
Result.Expression = Ovl;
} else {
- Result.HasFormOfMemberPointer = false;
- Result.IsAddressOfOperand = false;
- Result.IsAddressOfOperandWithParen = false;
Result.Expression = cast<OverloadExpr>(E);
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 772bf48f36c91..c8078f335c513 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5813,11 +5813,15 @@ static TypoCorrection TryTypoCorrectionForCall(Sema &S, Expr *Fn,
return TypoCorrection();
}
+// [C++26][[expr.unary.op]/p4
+// A pointer to member is only formed when an explicit &
+// is used and its operand is a qualified-id not enclosed in parentheses.
static bool isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) {
if (!isa<ParenExpr>(Fn))
return false;
Fn = Fn->IgnoreParens();
+
auto *UO = dyn_cast<UnaryOperator>(Fn);
if (!UO || UO->getOpcode() != clang::UO_AddrOf)
return false;
>From f772345153d94855d5e6e02985e29e1f10f83060 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 5 Jun 2024 09:53:40 +0200
Subject: [PATCH 6/6] Address Aarons's feedback
---
clang/include/clang/Sema/Overload.h | 2 ++
clang/lib/Sema/SemaExpr.cpp | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 64cdd6cdf043d..3d974eea63afb 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1002,6 +1002,8 @@ class Sema;
/// using either a parenthesized or braced list of arguments.
CSK_InitByConstructor,
+ /// C++ [over.match.call.general]
+ /// Resolve a call through the address of an overload set
CSK_AddressOfOverloadSet,
};
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c8078f335c513..079cbc00e788d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5826,11 +5826,11 @@ static bool isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) {
if (!UO || UO->getOpcode() != clang::UO_AddrOf)
return false;
if (auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) {
+ assert(isa<FunctionDecl>(DRE->getDecl()) && "expected a function");
return DRE->hasQualifier();
}
- if (auto *OVL = dyn_cast<OverloadExpr>(UO->getSubExpr()->IgnoreParens())) {
+ if (auto *OVL = dyn_cast<OverloadExpr>(UO->getSubExpr()->IgnoreParens()))
return OVL->getQualifier();
- }
return false;
}
More information about the cfe-commits
mailing list