-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[Clang] Static and explicit object member functions with the same parameter-type-lists #93430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
131f515
5bdf858
b243477
16eed40
ad4df6d
ad5dc97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -899,6 +899,8 @@ class Sema; | |
/// object argument. | ||
bool IgnoreObjectArgument : 1; | ||
|
||
bool TookAddressOfOverload : 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CC @Endilll -- it might be nice to refactor this code to use |
||
|
||
/// 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, | ||
cor3ntin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
/// Information about operator rewrites to consider when adding operator | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5813,6 +5813,23 @@ static TypoCorrection TryTypoCorrectionForCall(Sema &S, Expr *Fn, | |
return TypoCorrection(); | ||
} | ||
|
||
static bool isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) { | ||
if (!isa<ParenExpr>(Fn)) | ||
return false; | ||
|
||
Fn = Fn->IgnoreParens(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also ignore implicit casts along the way here for all of these? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, Any cast would make it a different shape than |
||
auto *UO = dyn_cast<UnaryOperator>(Fn); | ||
if (!UO || UO->getOpcode() != clang::UO_AddrOf) | ||
return false; | ||
if (auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) { | ||
return DRE->hasQualifier(); | ||
} | ||
if (auto *OVL = dyn_cast<OverloadExpr>(UO->getSubExpr()->IgnoreParens())) { | ||
return OVL->getQualifier(); | ||
} | ||
AaronBallman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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,8 +5851,10 @@ 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(); | ||
!AddressOf && FDecl && FDecl->hasCXXExplicitFunctionObjectParameter(); | ||
unsigned ExplicitObjectParameterOffset = HasExplicitObjectParameter ? 1 : 0; | ||
unsigned NumParams = Proto->getNumParams(); | ||
bool Invalid = false; | ||
|
@@ -6546,7 +6565,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()); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,24 @@ 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); | ||
bool ImplicitObjectMethodTreatedAsStatic = | ||
CandidateSet.getKind() == | ||
OverloadCandidateSet::CSK_AddressOfOverloadSet && | ||
Method->isImplicitObjectMemberFunction(); | ||
|
||
unsigned ExplicitOffset = | ||
!IgnoreExplicitObject && Method->isExplicitObjectMemberFunction() ? 1 : 0; | ||
|
||
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 | ||
|
@@ -7566,7 +7581,10 @@ 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 + | ||
int(ImplicitObjectMethodTreatedAsStatic); | ||
|
||
if (Args.size() < MinRequiredArgs && !PartialOverloading) { | ||
// Not enough arguments. | ||
Candidate.Viable = false; | ||
|
@@ -7636,7 +7654,14 @@ 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 (ImplicitObjectMethodTreatedAsStatic) { | ||
ParamType = ArgIdx == 0 | ||
? Method->getFunctionObjectParameterReferenceType() | ||
: Proto->getParamType(ArgIdx - 1); | ||
} else { | ||
ParamType = Proto->getParamType(ArgIdx + ExplicitOffset); | ||
} | ||
Candidate.Conversions[ConvIdx] | ||
= TryCopyInitialization(*this, Args[ArgIdx], ParamType, | ||
SuppressUserConversions, | ||
|
@@ -7717,6 +7742,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; | ||
|
@@ -7807,6 +7833,7 @@ void Sema::AddTemplateOverloadCandidate( | |
Candidate.IgnoreObjectArgument = | ||
isa<CXXMethodDecl>(Candidate.Function) && | ||
!isa<CXXConstructorDecl>(Candidate.Function); | ||
Candidate.TookAddressOfOverload = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feel like Candidate should have a deleted default ctor/constructor pair too :D For exactly this reason. |
||
Candidate.ExplicitCallArguments = Args.size(); | ||
if (Result == TemplateDeductionResult::NonDependentConversionFailure) | ||
Candidate.FailureKind = ovl_fail_bad_conversion; | ||
|
@@ -7998,6 +8025,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 +8228,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 +8269,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 +8495,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 +10960,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); | ||
|
@@ -11508,9 +11545,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 | ||
|
@@ -11538,7 +11576,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" | ||
|
@@ -11548,12 +11587,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 = Fn->hasCXXExplicitFunctionObjectParameter(); | ||
unsigned ParamCount = FnTy->getNumParams() - (HasExplicitObjectParam ? 1 : 0); | ||
bool HasExplicitObjectParam = | ||
!IsAddressOf && Fn->hasCXXExplicitFunctionObjectParameter(); | ||
|
||
unsigned ParamCount = | ||
Fn->getNumNonObjectParams() + ((IsAddressOf && !Fn->isStatic()) ? 1 : 0); | ||
unsigned mode, modeCount; | ||
|
||
if (NumFormalArgs < MinParams) { | ||
if (MinParams != ParamCount || FnTy->isVariadic() || | ||
FnTy->isTemplateVariadic()) | ||
|
@@ -11573,7 +11617,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 | ||
|
@@ -11592,8 +11636,9 @@ 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)) | ||
DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs); | ||
if (!CheckArityMismatch(S, Cand, NumFormalArgs, Cand->TookAddressOfOverload)) | ||
DiagnoseArityMismatch(S, Cand->FoundDecl, Cand->Function, NumFormalArgs, | ||
Cand->TookAddressOfOverload); | ||
} | ||
|
||
static TemplateDecl *getDescribedTemplate(Decl *Templated) { | ||
|
@@ -12033,6 +12078,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()) { | ||
|
@@ -14076,6 +14128,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 +14234,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,9 +16402,9 @@ 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()) { | ||
// Do nothing: static member functions aren't any different | ||
// from non-member functions. | ||
if (!Method->isImplicitObjectMemberFunction()) { | ||
// 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 | ||
|
@@ -16393,7 +16462,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()) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleanup nit: I would vastly prefer FindResult have a constructor & deleted default constructor. This seems like it would be really easy to forget to initialize on some branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a default member initializer for everything