Skip to content

Commit 2015626

Browse files
authored
[Clang] Implement CWG2918 'Consideration of constraints for address of overloaded function' (#127773)
Closes #122523
1 parent 29c5e42 commit 2015626

File tree

7 files changed

+190
-33
lines changed

7 files changed

+190
-33
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ Resolutions to C++ Defect Reports
9090
two releases. The improvements to template template parameter matching implemented
9191
in the previous release, as described in P3310 and P3579, made this flag unnecessary.
9292

93+
- Implemented `CWG2918 Consideration of constraints for address of overloaded `
94+
`function <https://cplusplus.github.io/CWG/issues/2918.html>`_
95+
9396
C Language Changes
9497
------------------
9598

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10519,7 +10519,8 @@ class Sema final : public SemaBase {
1051910519
/// returned.
1052010520
FunctionDecl *ResolveSingleFunctionTemplateSpecialization(
1052110521
OverloadExpr *ovl, bool Complain = false, DeclAccessPair *Found = nullptr,
10522-
TemplateSpecCandidateSet *FailedTSC = nullptr);
10522+
TemplateSpecCandidateSet *FailedTSC = nullptr,
10523+
bool ForTypeDeduction = false);
1052310524

1052410525
// Resolve and fix an overloaded expression that can be resolved
1052510526
// because it identifies a single function template specialization.

clang/lib/Sema/SemaConcept.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,20 +1796,19 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
17961796
NamedDecl *D2,
17971797
MutableArrayRef<const Expr *> AC2,
17981798
bool &Result) {
1799+
#ifndef NDEBUG
17991800
if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) {
18001801
auto IsExpectedEntity = [](const FunctionDecl *FD) {
18011802
FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind();
18021803
return Kind == FunctionDecl::TK_NonTemplate ||
18031804
Kind == FunctionDecl::TK_FunctionTemplate;
18041805
};
18051806
const auto *FD2 = dyn_cast<FunctionDecl>(D2);
1806-
(void)IsExpectedEntity;
1807-
(void)FD1;
1808-
(void)FD2;
18091807
assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
18101808
"use non-instantiated function declaration for constraints partial "
18111809
"ordering");
18121810
}
1811+
#endif
18131812

18141813
if (AC1.empty()) {
18151814
Result = AC2.empty();

clang/lib/Sema/SemaOverload.cpp

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10369,20 +10369,16 @@ static bool allowAmbiguity(ASTContext &Context, const FunctionDecl *F1,
1036910369
/// [over.match.best.general]p2.6
1037010370
/// F1 and F2 are non-template functions with the same
1037110371
/// non-object-parameter-type-lists, and F1 is more constrained than F2 [...]
10372-
static bool sameFunctionParameterTypeLists(Sema &S,
10373-
const OverloadCandidate &Cand1,
10374-
const OverloadCandidate &Cand2) {
10375-
if (!Cand1.Function || !Cand2.Function)
10376-
return false;
10377-
10378-
FunctionDecl *Fn1 = Cand1.Function;
10379-
FunctionDecl *Fn2 = Cand2.Function;
10380-
10372+
static bool sameFunctionParameterTypeLists(Sema &S, FunctionDecl *Fn1,
10373+
FunctionDecl *Fn2,
10374+
bool IsFn1Reversed,
10375+
bool IsFn2Reversed) {
10376+
assert(Fn1 && Fn2);
1038110377
if (Fn1->isVariadic() != Fn2->isVariadic())
1038210378
return false;
1038310379

10384-
if (!S.FunctionNonObjectParamTypesAreEqual(
10385-
Fn1, Fn2, nullptr, Cand1.isReversed() ^ Cand2.isReversed()))
10380+
if (!S.FunctionNonObjectParamTypesAreEqual(Fn1, Fn2, nullptr,
10381+
IsFn1Reversed ^ IsFn2Reversed))
1038610382
return false;
1038710383

1038810384
auto *Mem1 = dyn_cast<CXXMethodDecl>(Fn1);
@@ -10403,6 +10399,30 @@ static bool sameFunctionParameterTypeLists(Sema &S,
1040310399
return true;
1040410400
}
1040510401

10402+
static FunctionDecl *
10403+
getMorePartialOrderingConstrained(Sema &S, FunctionDecl *Fn1, FunctionDecl *Fn2,
10404+
bool IsFn1Reversed, bool IsFn2Reversed) {
10405+
if (!Fn1 || !Fn2)
10406+
return nullptr;
10407+
10408+
// C++ [temp.constr.order]:
10409+
// A non-template function F1 is more partial-ordering-constrained than a
10410+
// non-template function F2 if:
10411+
bool Cand1IsSpecialization = Fn1->getPrimaryTemplate();
10412+
bool Cand2IsSpecialization = Fn2->getPrimaryTemplate();
10413+
10414+
if (Cand1IsSpecialization || Cand2IsSpecialization)
10415+
return nullptr;
10416+
10417+
// - they have the same non-object-parameter-type-lists, and [...]
10418+
if (!sameFunctionParameterTypeLists(S, Fn1, Fn2, IsFn1Reversed,
10419+
IsFn2Reversed))
10420+
return nullptr;
10421+
10422+
// - the declaration of F1 is more constrained than the declaration of F2.
10423+
return S.getMoreConstrainedFunction(Fn1, Fn2);
10424+
}
10425+
1040610426
/// isBetterOverloadCandidate - Determines whether the first overload
1040710427
/// candidate is a better candidate than the second (C++ 13.3.3p1).
1040810428
bool clang::isBetterOverloadCandidate(
@@ -10649,12 +10669,12 @@ bool clang::isBetterOverloadCandidate(
1064910669
}
1065010670
}
1065110671

10652-
// -— F1 and F2 are non-template functions with the same
10653-
// parameter-type-lists, and F1 is more constrained than F2 [...],
10654-
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
10655-
sameFunctionParameterTypeLists(S, Cand1, Cand2) &&
10656-
S.getMoreConstrainedFunction(Cand1.Function, Cand2.Function) ==
10657-
Cand1.Function)
10672+
// -— F1 and F2 are non-template functions and F1 is more
10673+
// partial-ordering-constrained than F2 [...],
10674+
if (FunctionDecl *F = getMorePartialOrderingConstrained(
10675+
S, Cand1.Function, Cand2.Function, Cand1.isReversed(),
10676+
Cand2.isReversed());
10677+
F && F == Cand1.Function)
1065810678
return true;
1065910679

1066010680
// -- F1 is a constructor for a class D, F2 is a constructor for a base
@@ -12999,9 +13019,10 @@ class AddressOfFunctionResolver {
1299913019
// C++ [over.over]p4:
1300013020
// If more than one function is selected, [...]
1300113021
if (Matches.size() > 1 && !eliminiateSuboptimalOverloadCandidates()) {
13002-
if (FoundNonTemplateFunction)
13022+
if (FoundNonTemplateFunction) {
1300313023
EliminateAllTemplateMatches();
13004-
else
13024+
EliminateLessPartialOrderingConstrainedMatches();
13025+
} else
1300513026
EliminateAllExceptMostSpecializedTemplate();
1300613027
}
1300713028
}
@@ -13252,6 +13273,33 @@ class AddressOfFunctionResolver {
1325213273
}
1325313274
}
1325413275

13276+
void EliminateLessPartialOrderingConstrainedMatches() {
13277+
// C++ [over.over]p5:
13278+
// [...] Any given non-template function F0 is eliminated if the set
13279+
// contains a second non-template function that is more
13280+
// partial-ordering-constrained than F0. [...]
13281+
assert(Matches[0].second->getPrimaryTemplate() == nullptr &&
13282+
"Call EliminateAllTemplateMatches() first");
13283+
SmallVector<std::pair<DeclAccessPair, FunctionDecl *>, 4> Results;
13284+
Results.push_back(Matches[0]);
13285+
for (unsigned I = 1, N = Matches.size(); I < N; ++I) {
13286+
assert(Matches[I].second->getPrimaryTemplate() == nullptr);
13287+
FunctionDecl *F = getMorePartialOrderingConstrained(
13288+
S, Matches[I].second, Results[0].second,
13289+
/*IsFn1Reversed=*/false,
13290+
/*IsFn2Reversed=*/false);
13291+
if (!F) {
13292+
Results.push_back(Matches[I]);
13293+
continue;
13294+
}
13295+
if (F == Matches[I].second) {
13296+
Results.clear();
13297+
Results.push_back(Matches[I]);
13298+
}
13299+
}
13300+
std::swap(Matches, Results);
13301+
}
13302+
1325513303
void EliminateSuboptimalCudaMatches() {
1325613304
S.CUDA().EraseUnwantedMatches(S.getCurFunctionDecl(/*AllowLambda=*/true),
1325713305
Matches);
@@ -13408,8 +13456,8 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
1340813456
Result = FD;
1340913457
};
1341013458

13411-
// We have more than one result - see if it is more constrained than the
13412-
// previous one.
13459+
// We have more than one result - see if it is more
13460+
// partial-ordering-constrained than the previous one.
1341313461
if (Result) {
1341413462
// Check CUDA preference first. If the candidates have differennt CUDA
1341513463
// preference, choose the one with higher CUDA preference. Otherwise,
@@ -13424,9 +13472,17 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
1342413472
continue;
1342513473
}
1342613474
}
13427-
// FD has the same CUDA prefernece than Result. Continue check
13475+
// FD has the same CUDA preference than Result. Continue to check
1342813476
// constraints.
13429-
FunctionDecl *MoreConstrained = getMoreConstrainedFunction(FD, Result);
13477+
13478+
// C++ [over.over]p5:
13479+
// [...] Any given non-template function F0 is eliminated if the set
13480+
// contains a second non-template function that is more
13481+
// partial-ordering-constrained than F0 [...]
13482+
FunctionDecl *MoreConstrained =
13483+
getMorePartialOrderingConstrained(*this, FD, Result,
13484+
/*IsFn1Reversed=*/false,
13485+
/*IsFn2Reversed=*/false);
1343013486
if (MoreConstrained != FD) {
1343113487
if (!MoreConstrained) {
1343213488
IsResultAmbiguous = true;
@@ -13443,7 +13499,6 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
1344313499
return nullptr;
1344413500

1344513501
if (Result) {
13446-
SmallVector<const Expr *, 1> ResultAC;
1344713502
// We skipped over some ambiguous declarations which might be ambiguous with
1344813503
// the selected result.
1344913504
for (FunctionDecl *Skipped : AmbiguousDecls) {
@@ -13489,7 +13544,7 @@ bool Sema::resolveAndFixAddressOfSingleOverloadCandidate(
1348913544

1349013545
FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization(
1349113546
OverloadExpr *ovl, bool Complain, DeclAccessPair *FoundResult,
13492-
TemplateSpecCandidateSet *FailedTSC) {
13547+
TemplateSpecCandidateSet *FailedTSC, bool ForTypeDeduction) {
1349313548
// C++ [over.over]p1:
1349413549
// [...] [Note: any redundant set of parentheses surrounding the
1349513550
// overloaded function name is ignored (5.1). ]
@@ -13540,8 +13595,16 @@ FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization(
1354013595

1354113596
assert(Specialization && "no specialization and no error?");
1354213597

13543-
// Multiple matches; we can't resolve to a single declaration.
13598+
// C++ [temp.deduct.call]p6:
13599+
// [...] If all successful deductions yield the same deduced A, that
13600+
// deduced A is the result of deduction; otherwise, the parameter is
13601+
// treated as a non-deduced context.
1354413602
if (Matched) {
13603+
if (ForTypeDeduction &&
13604+
isSameOrCompatibleFunctionType(Matched->getType(),
13605+
Specialization->getType()))
13606+
continue;
13607+
// Multiple matches; we can't resolve to a single declaration.
1354513608
if (Complain) {
1354613609
Diag(ovl->getExprLoc(), diag::err_addr_ovl_ambiguous)
1354713610
<< ovl->getName();

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4252,7 +4252,8 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
42524252
if (FunctionDecl *ExplicitSpec =
42534253
S.ResolveSingleFunctionTemplateSpecialization(
42544254
Ovl, /*Complain=*/false,
4255-
/*FoundDeclAccessPair=*/nullptr, FailedTSC))
4255+
/*Found=*/nullptr, FailedTSC,
4256+
/*ForTypeDeduction=*/true))
42564257
return GetTypeOfFunction(S, R, ExplicitSpec);
42574258
}
42584259

@@ -4321,7 +4322,11 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
43214322
/*HasDeducedAnyParam=*/nullptr);
43224323
if (Result != TemplateDeductionResult::Success)
43234324
continue;
4324-
if (!Match.isNull())
4325+
// C++ [temp.deduct.call]p6:
4326+
// [...] If all successful deductions yield the same deduced A, that
4327+
// deduced A is the result of deduction; otherwise, the parameter is
4328+
// treated as a non-deduced context. [...]
4329+
if (!Match.isNull() && !S.isSameOrCompatibleFunctionType(Match, ArgType))
43254330
return {};
43264331
Match = ArgType;
43274332
}

clang/test/CXX/drs/cwg29xx.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,92 @@ struct S {
6060
#endif
6161
} // namespace cwg2917
6262

63+
namespace cwg2918 { // cwg2918: 21
64+
65+
#if __cplusplus >= 202002L
66+
67+
namespace Example1 {
68+
69+
template<bool B> struct X {
70+
void f(short) requires B;
71+
void f(long);
72+
template<typename> void g(short) requires B;
73+
template<typename> void g(long);
74+
};
75+
76+
void test() {
77+
&X<true>::f; // since-cxx20-error {{reference to overloaded function could not be resolved}}
78+
&X<true>::g<int>; // since-cxx20-error {{reference to overloaded function could not be resolved}}
79+
}
80+
81+
} // namespace Example1
82+
83+
namespace Example2 {
84+
85+
template <bool B> struct X {
86+
static constexpr int f(short) requires B {
87+
return 42;
88+
}
89+
static constexpr int f(short) {
90+
return 24;
91+
}
92+
};
93+
94+
template <typename T>
95+
constexpr int f(T) { return 1; }
96+
97+
template <typename T>
98+
requires __is_same(T, int)
99+
constexpr int f(T) { return 2; }
100+
101+
void test() {
102+
constexpr auto x = &X<true>::f;
103+
static_assert(__is_same(decltype(x), int(*const)(short)), "");
104+
static_assert(x(0) == 42, "");
105+
106+
constexpr auto y = &X<false>::f;
107+
static_assert(__is_same(decltype(y), int(*const)(short)));
108+
static_assert(y(0) == 24, "");
109+
110+
constexpr auto z = &f<int>;
111+
static_assert(__is_same(decltype(z), int(*const)(int)));
112+
static_assert(z(0) == 2, "");
113+
114+
// C++ [temp.deduct.call]p6:
115+
// If the argument is an overload set containing one or more function templates,
116+
// the parameter is treated as a non-deduced context.
117+
auto w = f; // since-cxx20-error {{variable 'w' with type 'auto' has incompatible initializer of type '<overloaded function type>'}}
118+
}
119+
120+
} // namespace Example2
121+
#endif
122+
123+
#if __cplusplus >= 201103L
124+
namespace Example3 {
125+
126+
template <typename T> void f(T &&, void (*)(T &&)); // #cwg2918_f
127+
128+
void g(int &);
129+
inline namespace A {
130+
void g(short &&);
131+
}
132+
inline namespace B {
133+
void g(short &&);
134+
}
135+
136+
void q() {
137+
int x;
138+
f(x, g);
139+
// since-cxx11-error@-1 {{no matching function for call to 'f'}}
140+
// since-cxx11-note@#cwg2918_f {{candidate template ignored: deduced conflicting types for parameter 'T' ('int &' vs. 'short')}}
141+
}
142+
143+
} // namespace Example3
144+
145+
#endif
146+
147+
} // namespace cwg2918
148+
63149
#if __cplusplus > 202302L
64150
namespace std {
65151
using size_t = decltype(sizeof(0));

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17364,7 +17364,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1736417364
<td><a href="https://cplusplus.github.io/CWG/issues/2918.html">2918</a></td>
1736517365
<td>DR</td>
1736617366
<td>Consideration of constraints for address of overloaded function</td>
17367-
<td class="unknown" align="center">Unknown</td>
17367+
<td class="unreleased" align="center">Clang 21</td>
1736817368
</tr>
1736917369
<tr id="2919">
1737017370
<td><a href="https://cplusplus.github.io/CWG/issues/2919.html">2919</a></td>

0 commit comments

Comments
 (0)