Skip to content

Commit d0223b9

Browse files
authored
[Clang] Static and explicit object member functions with the same parameter-type-lists (#93430)
Implement 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 id-expression of the form `(&Foo::bar)(args...)`, which we previously failed to do consistently.
1 parent c537f35 commit d0223b9

File tree

12 files changed

+229
-35
lines changed

12 files changed

+229
-35
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ C++23 Feature Support
214214
- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
215215
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.
216216

217+
- Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`_.
218+
This completes the support for "deducing this".
219+
217220
C++2c Feature Support
218221
^^^^^^^^^^^^^^^^^^^^^
219222

clang/include/clang/AST/ExprCXX.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3025,9 +3025,10 @@ class OverloadExpr : public Expr {
30253025

30263026
public:
30273027
struct FindResult {
3028-
OverloadExpr *Expression;
3029-
bool IsAddressOfOperand;
3030-
bool HasFormOfMemberPointer;
3028+
OverloadExpr *Expression = nullptr;
3029+
bool IsAddressOfOperand = false;
3030+
bool IsAddressOfOperandWithParen = false;
3031+
bool HasFormOfMemberPointer = false;
30313032
};
30323033

30333034
/// Finds the overloaded expression in the given expression \p E of
@@ -3039,6 +3040,7 @@ class OverloadExpr : public Expr {
30393040
assert(E->getType()->isSpecificBuiltinType(BuiltinType::Overload));
30403041

30413042
FindResult Result;
3043+
bool HasParen = isa<ParenExpr>(E);
30423044

30433045
E = E->IgnoreParens();
30443046
if (isa<UnaryOperator>(E)) {
@@ -3048,10 +3050,9 @@ class OverloadExpr : public Expr {
30483050

30493051
Result.HasFormOfMemberPointer = (E == Ovl && Ovl->getQualifier());
30503052
Result.IsAddressOfOperand = true;
3053+
Result.IsAddressOfOperandWithParen = HasParen;
30513054
Result.Expression = Ovl;
30523055
} else {
3053-
Result.HasFormOfMemberPointer = false;
3054-
Result.IsAddressOfOperand = false;
30553056
Result.Expression = cast<OverloadExpr>(E);
30563057
}
30573058

clang/include/clang/Sema/Overload.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,8 @@ class Sema;
899899
/// object argument.
900900
bool IgnoreObjectArgument : 1;
901901

902+
bool TookAddressOfOverload : 1;
903+
902904
/// True if the candidate was found using ADL.
903905
CallExpr::ADLCallKind IsADLCandidate : 1;
904906

@@ -999,6 +1001,10 @@ class Sema;
9991001
/// Initialization of an object of class type by constructor,
10001002
/// using either a parenthesized or braced list of arguments.
10011003
CSK_InitByConstructor,
1004+
1005+
/// C++ [over.match.call.general]
1006+
/// Resolve a call through the address of an overload set.
1007+
CSK_AddressOfOverloadSet,
10021008
};
10031009

10041010
/// Information about operator rewrites to consider when adding operator

clang/lib/Sema/SemaExpr.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5813,6 +5813,27 @@ static TypoCorrection TryTypoCorrectionForCall(Sema &S, Expr *Fn,
58135813
return TypoCorrection();
58145814
}
58155815

5816+
// [C++26][[expr.unary.op]/p4
5817+
// A pointer to member is only formed when an explicit &
5818+
// is used and its operand is a qualified-id not enclosed in parentheses.
5819+
static bool isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) {
5820+
if (!isa<ParenExpr>(Fn))
5821+
return false;
5822+
5823+
Fn = Fn->IgnoreParens();
5824+
5825+
auto *UO = dyn_cast<UnaryOperator>(Fn);
5826+
if (!UO || UO->getOpcode() != clang::UO_AddrOf)
5827+
return false;
5828+
if (auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) {
5829+
assert(isa<FunctionDecl>(DRE->getDecl()) && "expected a function");
5830+
return DRE->hasQualifier();
5831+
}
5832+
if (auto *OVL = dyn_cast<OverloadExpr>(UO->getSubExpr()->IgnoreParens()))
5833+
return OVL->getQualifier();
5834+
return false;
5835+
}
5836+
58165837
/// ConvertArgumentsForCall - Converts the arguments specified in
58175838
/// Args/NumArgs to the parameter types of the function FDecl with
58185839
/// function prototype Proto. Call is the call expression itself, and
@@ -5834,8 +5855,10 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
58345855

58355856
// C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
58365857
// assignment, to the types of the corresponding parameter, ...
5858+
5859+
bool AddressOf = isParenthetizedAndQualifiedAddressOfExpr(Fn);
58375860
bool HasExplicitObjectParameter =
5838-
FDecl && FDecl->hasCXXExplicitFunctionObjectParameter();
5861+
!AddressOf && FDecl && FDecl->hasCXXExplicitFunctionObjectParameter();
58395862
unsigned ExplicitObjectParameterOffset = HasExplicitObjectParameter ? 1 : 0;
58405863
unsigned NumParams = Proto->getNumParams();
58415864
bool Invalid = false;
@@ -6546,7 +6569,7 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
65466569
OverloadExpr::FindResult find = OverloadExpr::find(Fn);
65476570

65486571
// We aren't supposed to apply this logic if there's an '&' involved.
6549-
if (!find.HasFormOfMemberPointer) {
6572+
if (!find.HasFormOfMemberPointer || find.IsAddressOfOperandWithParen) {
65506573
if (Expr::hasAnyTypeDependentArguments(ArgExprs))
65516574
return CallExpr::Create(Context, Fn, ArgExprs, Context.DependentTy,
65526575
VK_PRValue, RParenLoc, CurFPFeatureOverrides());

clang/lib/Sema/SemaOverload.cpp

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6972,6 +6972,7 @@ void Sema::AddOverloadCandidate(
69726972
Candidate.IsSurrogate = false;
69736973
Candidate.IsADLCandidate = IsADLCandidate;
69746974
Candidate.IgnoreObjectArgument = false;
6975+
Candidate.TookAddressOfOverload = false;
69756976
Candidate.ExplicitCallArguments = Args.size();
69766977

69776978
// Explicit functions are not actually candidates at all if we're not
@@ -7546,10 +7547,24 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
75467547
CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
75477548
Candidate.IsSurrogate = false;
75487549
Candidate.IgnoreObjectArgument = false;
7550+
Candidate.TookAddressOfOverload =
7551+
CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet;
75497552
Candidate.ExplicitCallArguments = Args.size();
75507553

7551-
unsigned NumParams = Method->getNumExplicitParams();
7552-
unsigned ExplicitOffset = Method->isExplicitObjectMemberFunction() ? 1 : 0;
7554+
bool IgnoreExplicitObject =
7555+
(Method->isExplicitObjectMemberFunction() &&
7556+
CandidateSet.getKind() ==
7557+
OverloadCandidateSet::CSK_AddressOfOverloadSet);
7558+
bool ImplicitObjectMethodTreatedAsStatic =
7559+
CandidateSet.getKind() ==
7560+
OverloadCandidateSet::CSK_AddressOfOverloadSet &&
7561+
Method->isImplicitObjectMemberFunction();
7562+
7563+
unsigned ExplicitOffset =
7564+
!IgnoreExplicitObject && Method->isExplicitObjectMemberFunction() ? 1 : 0;
7565+
7566+
unsigned NumParams = Method->getNumParams() - ExplicitOffset +
7567+
int(ImplicitObjectMethodTreatedAsStatic);
75537568

75547569
// (C++ 13.3.2p2): A candidate function having fewer than m
75557570
// parameters is viable only if it has an ellipsis in its parameter
@@ -7567,7 +7582,10 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
75677582
// (8.3.6). For the purposes of overload resolution, the
75687583
// parameter list is truncated on the right, so that there are
75697584
// exactly m parameters.
7570-
unsigned MinRequiredArgs = Method->getMinRequiredExplicitArguments();
7585+
unsigned MinRequiredArgs = Method->getMinRequiredArguments() -
7586+
ExplicitOffset +
7587+
int(ImplicitObjectMethodTreatedAsStatic);
7588+
75717589
if (Args.size() < MinRequiredArgs && !PartialOverloading) {
75727590
// Not enough arguments.
75737591
Candidate.Viable = false;
@@ -7637,7 +7655,14 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
76377655
// exist for each argument an implicit conversion sequence
76387656
// (13.3.3.1) that converts that argument to the corresponding
76397657
// parameter of F.
7640-
QualType ParamType = Proto->getParamType(ArgIdx + ExplicitOffset);
7658+
QualType ParamType;
7659+
if (ImplicitObjectMethodTreatedAsStatic) {
7660+
ParamType = ArgIdx == 0
7661+
? Method->getFunctionObjectParameterReferenceType()
7662+
: Proto->getParamType(ArgIdx - 1);
7663+
} else {
7664+
ParamType = Proto->getParamType(ArgIdx + ExplicitOffset);
7665+
}
76417666
Candidate.Conversions[ConvIdx]
76427667
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
76437668
SuppressUserConversions,
@@ -7718,6 +7743,7 @@ void Sema::AddMethodTemplateCandidate(
77187743
Candidate.IgnoreObjectArgument =
77197744
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
77207745
ObjectType.isNull();
7746+
Candidate.TookAddressOfOverload = false;
77217747
Candidate.ExplicitCallArguments = Args.size();
77227748
if (Result == TemplateDeductionResult::NonDependentConversionFailure)
77237749
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -7808,6 +7834,7 @@ void Sema::AddTemplateOverloadCandidate(
78087834
Candidate.IgnoreObjectArgument =
78097835
isa<CXXMethodDecl>(Candidate.Function) &&
78107836
!isa<CXXConstructorDecl>(Candidate.Function);
7837+
Candidate.TookAddressOfOverload = false;
78117838
Candidate.ExplicitCallArguments = Args.size();
78127839
if (Result == TemplateDeductionResult::NonDependentConversionFailure)
78137840
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -7999,6 +8026,7 @@ void Sema::AddConversionCandidate(
79998026
Candidate.Function = Conversion;
80008027
Candidate.IsSurrogate = false;
80018028
Candidate.IgnoreObjectArgument = false;
8029+
Candidate.TookAddressOfOverload = false;
80028030
Candidate.FinalConversion.setAsIdentityConversion();
80038031
Candidate.FinalConversion.setFromType(ConvType);
80048032
Candidate.FinalConversion.setAllToTypes(ToType);
@@ -8201,6 +8229,7 @@ void Sema::AddTemplateConversionCandidate(
82018229
Candidate.FailureKind = ovl_fail_bad_deduction;
82028230
Candidate.IsSurrogate = false;
82038231
Candidate.IgnoreObjectArgument = false;
8232+
Candidate.TookAddressOfOverload = false;
82048233
Candidate.ExplicitCallArguments = 1;
82058234
Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
82068235
Info);
@@ -8241,6 +8270,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
82418270
Candidate.Viable = true;
82428271
Candidate.IsSurrogate = true;
82438272
Candidate.IgnoreObjectArgument = false;
8273+
Candidate.TookAddressOfOverload = false;
82448274
Candidate.ExplicitCallArguments = Args.size();
82458275

82468276
// Determine the implicit conversion sequence for the implicit
@@ -8466,6 +8496,7 @@ void Sema::AddBuiltinCandidate(QualType *ParamTys, ArrayRef<Expr *> Args,
84668496
Candidate.Function = nullptr;
84678497
Candidate.IsSurrogate = false;
84688498
Candidate.IgnoreObjectArgument = false;
8499+
Candidate.TookAddressOfOverload = false;
84698500
std::copy(ParamTys, ParamTys + Args.size(), Candidate.BuiltinParamTypes);
84708501

84718502
// Determine the implicit conversion sequences for each of the
@@ -10930,6 +10961,12 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
1093010961
if (Best->Function && Best->Function->isDeleted())
1093110962
return OR_Deleted;
1093210963

10964+
if (auto *M = dyn_cast_or_null<CXXMethodDecl>(Best->Function);
10965+
Kind == CSK_AddressOfOverloadSet && M &&
10966+
M->isImplicitObjectMemberFunction()) {
10967+
return OR_No_Viable_Function;
10968+
}
10969+
1093310970
if (!EquivalentCands.empty())
1093410971
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
1093510972
EquivalentCands);
@@ -11517,9 +11554,10 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand,
1151711554
/// candidates. This is not covered by the more general DiagnoseArityMismatch()
1151811555
/// over a candidate in any candidate set.
1151911556
static bool CheckArityMismatch(Sema &S, OverloadCandidate *Cand,
11520-
unsigned NumArgs) {
11557+
unsigned NumArgs, bool IsAddressOf = false) {
1152111558
FunctionDecl *Fn = Cand->Function;
11522-
unsigned MinParams = Fn->getMinRequiredArguments();
11559+
unsigned MinParams = Fn->getMinRequiredExplicitArguments() +
11560+
((IsAddressOf && !Fn->isStatic()) ? 1 : 0);
1152311561

1152411562
// With invalid overloaded operators, it's possible that we think we
1152511563
// have an arity mismatch when in fact it looks like we have the
@@ -11547,7 +11585,8 @@ static bool CheckArityMismatch(Sema &S, OverloadCandidate *Cand,
1154711585

1154811586
/// General arity mismatch diagnosis over a candidate in a candidate set.
1154911587
static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
11550-
unsigned NumFormalArgs) {
11588+
unsigned NumFormalArgs,
11589+
bool IsAddressOf = false) {
1155111590
assert(isa<FunctionDecl>(D) &&
1155211591
"The templated declaration should at least be a function"
1155311592
" when diagnosing bad template argument deduction due to too many"
@@ -11557,12 +11596,17 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
1155711596

1155811597
// TODO: treat calls to a missing default constructor as a special case
1155911598
const auto *FnTy = Fn->getType()->castAs<FunctionProtoType>();
11560-
unsigned MinParams = Fn->getMinRequiredExplicitArguments();
11599+
unsigned MinParams = Fn->getMinRequiredExplicitArguments() +
11600+
((IsAddressOf && !Fn->isStatic()) ? 1 : 0);
1156111601

1156211602
// at least / at most / exactly
11563-
bool HasExplicitObjectParam = Fn->hasCXXExplicitFunctionObjectParameter();
11564-
unsigned ParamCount = FnTy->getNumParams() - (HasExplicitObjectParam ? 1 : 0);
11603+
bool HasExplicitObjectParam =
11604+
!IsAddressOf && Fn->hasCXXExplicitFunctionObjectParameter();
11605+
11606+
unsigned ParamCount =
11607+
Fn->getNumNonObjectParams() + ((IsAddressOf && !Fn->isStatic()) ? 1 : 0);
1156511608
unsigned mode, modeCount;
11609+
1156611610
if (NumFormalArgs < MinParams) {
1156711611
if (MinParams != ParamCount || FnTy->isVariadic() ||
1156811612
FnTy->isTemplateVariadic())
@@ -11582,7 +11626,7 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
1158211626
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
1158311627
ClassifyOverloadCandidate(S, Found, Fn, CRK_None, Description);
1158411628

11585-
if (modeCount == 1 &&
11629+
if (modeCount == 1 && !IsAddressOf &&
1158611630
Fn->getParamDecl(HasExplicitObjectParam ? 1 : 0)->getDeclName())
1158711631
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
1158811632
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
@@ -11601,8 +11645,9 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
1160111645
/// Arity mismatch diagnosis specific to a function overload candidate.
1160211646
static void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand,
1160311647
unsigned NumFormalArgs) {
11604-
if (!CheckArityMismatch(S, Cand, NumFormalArgs))
11605-
DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs);
11648+
if (!CheckArityMismatch(S, Cand, NumFormalArgs, Cand->TookAddressOfOverload))
11649+
DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs,
11650+
Cand->TookAddressOfOverload);
1160611651
}
1160711652

1160811653
static TemplateDecl *getDescribedTemplate(Decl *Templated) {
@@ -12042,6 +12087,13 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
1204212087
Cand->FailureKind != ovl_fail_bad_conversion)
1204312088
return;
1204412089

12090+
// Skip implicit member functions when trying to resolve
12091+
// the address of a an overload set for a function pointer.
12092+
if (Cand->TookAddressOfOverload &&
12093+
!Cand->Function->hasCXXExplicitFunctionObjectParameter() &&
12094+
!Cand->Function->isStatic())
12095+
return;
12096+
1204512097
// Note deleted candidates, but only if they're viable.
1204612098
if (Cand->Viable) {
1204712099
if (Fn->isDeleted()) {
@@ -14085,6 +14137,21 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
1408514137
}
1408614138

1408714139
case OR_No_Viable_Function: {
14140+
if (*Best != CandidateSet->end() &&
14141+
CandidateSet->getKind() ==
14142+
clang::OverloadCandidateSet::CSK_AddressOfOverloadSet) {
14143+
if (CXXMethodDecl *M =
14144+
dyn_cast_if_present<CXXMethodDecl>((*Best)->Function);
14145+
M && M->isImplicitObjectMemberFunction()) {
14146+
CandidateSet->NoteCandidates(
14147+
PartialDiagnosticAt(
14148+
Fn->getBeginLoc(),
14149+
SemaRef.PDiag(diag::err_member_call_without_object) << 0 << M),
14150+
SemaRef, OCD_AmbiguousCandidates, Args);
14151+
return ExprError();
14152+
}
14153+
}
14154+
1408814155
// Try to recover by looking for viable functions which the user might
1408914156
// have meant to call.
1409014157
ExprResult Recovery = BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc,
@@ -14176,8 +14243,10 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
1417614243
Expr *ExecConfig,
1417714244
bool AllowTypoCorrection,
1417814245
bool CalleesAddressIsTaken) {
14179-
OverloadCandidateSet CandidateSet(Fn->getExprLoc(),
14180-
OverloadCandidateSet::CSK_Normal);
14246+
OverloadCandidateSet CandidateSet(
14247+
Fn->getExprLoc(), CalleesAddressIsTaken
14248+
? OverloadCandidateSet::CSK_AddressOfOverloadSet
14249+
: OverloadCandidateSet::CSK_Normal);
1418114250
ExprResult result;
1418214251

1418314252
if (buildOverloadedCallSet(S, Fn, ULE, Args, LParenLoc, &CandidateSet,
@@ -16342,9 +16411,9 @@ ExprResult Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
1634216411
assert(UnOp->getOpcode() == UO_AddrOf &&
1634316412
"Can only take the address of an overloaded function");
1634416413
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn)) {
16345-
if (Method->isStatic()) {
16346-
// Do nothing: static member functions aren't any different
16347-
// from non-member functions.
16414+
if (!Method->isImplicitObjectMemberFunction()) {
16415+
// Do nothing: the address of static and
16416+
// explicit object member functions is a (non-member) function pointer.
1634816417
} else {
1634916418
// Fix the subexpression, which really has to be an
1635016419
// UnresolvedLookupExpr holding an overloaded member function
@@ -16402,7 +16471,10 @@ ExprResult Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
1640216471
}
1640316472

1640416473
QualType Type = Fn->getType();
16405-
ExprValueKind ValueKind = getLangOpts().CPlusPlus ? VK_LValue : VK_PRValue;
16474+
ExprValueKind ValueKind =
16475+
getLangOpts().CPlusPlus && !Fn->hasCXXExplicitFunctionObjectParameter()
16476+
? VK_LValue
16477+
: VK_PRValue;
1640616478

1640716479
// FIXME: Duplicated from BuildDeclarationNameExpr.
1640816480
if (unsigned BID = Fn->getBuiltinID()) {

0 commit comments

Comments
 (0)