Skip to content

Commit f9caa12

Browse files
committed
[Clang] Fix constraint checking of non-generic lambdas.
A lambda call operator can be a templated entity - and therefore have constraints while not being a function template template<class T> void f() { []() requires false { }(); } In that case, we would check the constraints of the call operator which is non-viable. However, we would find a viable candidate: the conversion operator to function pointer, and use it to perform a surrogate call. These constraints were not checked because: * We never check the constraints of surrogate functions * The lambda conversion operator has non constraints. From the wording, it is not clear what the intent is but it seems reasonable to expect the constraints of the lambda conversion operator to be checked and it is consistent with GCC and MSVC. This patch also improve the diagnostics for constraint failure on surrogate calls. Fixes #63181 Reviewed By: #clang-language-wg, aaron.ballman Differential Revision: https://reviews.llvm.org/D154368
1 parent f060f09 commit f9caa12

File tree

6 files changed

+153
-17
lines changed

6 files changed

+153
-17
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,8 @@ Bug Fixes to C++ Support
777777
- Fix location of default member initialization in parenthesized aggregate
778778
initialization.
779779
(`#63903 <https://github.com/llvm/llvm-project/issues/63903>`_)
780+
- Fix constraint checking of non-generic lambdas.
781+
(`#63181 <https://github.com/llvm/llvm-project/issues/63181>`_)
780782

781783
Bug Fixes to AST Handling
782784
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4732,6 +4732,8 @@ def note_ovl_candidate_bad_target : Note<
47324732
def note_ovl_candidate_constraints_not_satisfied : Note<
47334733
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: constraints "
47344734
"not satisfied">;
4735+
def note_ovl_surrogate_constraints_not_satisfied : Note<
4736+
"conversion candidate %0 not viable: constraints not satisfied">;
47354737
def note_implicit_member_target_infer_collision : Note<
47364738
"implicit %sub{select_special_member_kind}0 inferred target collision: call to both "
47374739
"%select{__device__|__global__|__host__|__host__ __device__}1 and "

clang/lib/Sema/SemaConcept.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,15 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
679679
return false;
680680
}
681681

682+
// A lambda conversion operator has the same constraints as the call operator
683+
// and constraints checking relies on whether we are in a lambda call operator
684+
// (and may refer to its parameters), so check the call operator instead.
685+
if (const auto *MD = dyn_cast<CXXConversionDecl>(FD);
686+
MD && isLambdaConversionOperator(const_cast<CXXConversionDecl *>(MD)))
687+
return CheckFunctionConstraints(MD->getParent()->getLambdaCallOperator(),
688+
Satisfaction, UsageLoc,
689+
ForOverloadResolution);
690+
682691
DeclContext *CtxToSave = const_cast<FunctionDecl *>(FD);
683692

684693
while (isLambdaCallOperator(CtxToSave) || FD->isTransparentContext()) {

clang/lib/Sema/SemaLambda.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,11 @@ static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange,
16331633
Conversion->setAccess(AS_public);
16341634
Conversion->setImplicit(true);
16351635

1636+
// A non-generic lambda may still be a templated entity. We need to preserve
1637+
// constraints when converting the lambda to a function pointer. See GH63181.
1638+
if (Expr *Requires = CallOperator->getTrailingRequiresClause())
1639+
Conversion->setTrailingRequiresClause(Requires);
1640+
16361641
if (Class->isGenericLambda()) {
16371642
// Create a template version of the conversion operator, using the template
16381643
// parameter list of the function call operator.

clang/lib/Sema/SemaOverload.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/AST/ASTContext.h"
14+
#include "clang/AST/ASTLambda.h"
1415
#include "clang/AST/CXXInheritance.h"
1516
#include "clang/AST/DeclCXX.h"
1617
#include "clang/AST/DeclObjC.h"
@@ -7885,6 +7886,17 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
78857886
}
78867887
}
78877888

7889+
if (Conversion->getTrailingRequiresClause()) {
7890+
ConstraintSatisfaction Satisfaction;
7891+
if (CheckFunctionConstraints(Conversion, Satisfaction, /*Loc*/ {},
7892+
/*ForOverloadResolution*/ true) ||
7893+
!Satisfaction.IsSatisfied) {
7894+
Candidate.Viable = false;
7895+
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
7896+
return;
7897+
}
7898+
}
7899+
78887900
if (EnableIfAttr *FailedAttr =
78897901
CheckEnableIf(Conversion, CandidateSet.getLocation(), std::nullopt)) {
78907902
Candidate.Viable = false;
@@ -11646,8 +11658,17 @@ static void NoteSurrogateCandidate(Sema &S, OverloadCandidate *Cand) {
1164611658
if (isRValueReference) FnType = S.Context.getRValueReferenceType(FnType);
1164711659
if (isLValueReference) FnType = S.Context.getLValueReferenceType(FnType);
1164811660

11649-
S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand)
11650-
<< FnType;
11661+
if (Cand->FailureKind == ovl_fail_constraints_not_satisfied) {
11662+
S.Diag(Cand->Surrogate->getLocation(),
11663+
diag::note_ovl_surrogate_constraints_not_satisfied)
11664+
<< Cand->Surrogate;
11665+
ConstraintSatisfaction Satisfaction;
11666+
if (S.CheckFunctionConstraints(Cand->Surrogate, Satisfaction))
11667+
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
11668+
} else {
11669+
S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand)
11670+
<< FnType;
11671+
}
1165111672
}
1165211673

1165311674
static void NoteBuiltinOperatorCandidate(Sema &S, StringRef Opc,
@@ -14970,6 +14991,22 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
1497014991
/*SuppressUserConversion=*/false);
1497114992
}
1497214993

14994+
// When calling a lambda, both the call operator, and
14995+
// the conversion operator to function pointer
14996+
// are considered. But when constraint checking
14997+
// on the call operator fails, it will also fail on the
14998+
// conversion operator as the constraints are always the same.
14999+
// As the user probably does not intend to perform a surrogate call,
15000+
// we filter them out to produce better error diagnostics, ie to avoid
15001+
// showing 2 failed overloads instead of one.
15002+
bool IgnoreSurrogateFunctions = false;
15003+
if (CandidateSet.size() == 1 && Record->getAsCXXRecordDecl()->isLambda()) {
15004+
const OverloadCandidate &Candidate = *CandidateSet.begin();
15005+
if (!Candidate.Viable &&
15006+
Candidate.FailureKind == ovl_fail_constraints_not_satisfied)
15007+
IgnoreSurrogateFunctions = true;
15008+
}
15009+
1497315010
// C++ [over.call.object]p2:
1497415011
// In addition, for each (non-explicit in C++0x) conversion function
1497515012
// declared in T of the form
@@ -14989,7 +15026,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
1498915026
// within T by another intervening declaration.
1499015027
const auto &Conversions =
1499115028
cast<CXXRecordDecl>(Record->getDecl())->getVisibleConversionFunctions();
14992-
for (auto I = Conversions.begin(), E = Conversions.end(); I != E; ++I) {
15029+
for (auto I = Conversions.begin(), E = Conversions.end();
15030+
!IgnoreSurrogateFunctions && I != E; ++I) {
1499315031
NamedDecl *D = *I;
1499415032
CXXRecordDecl *ActingContext = cast<CXXRecordDecl>(D->getDeclContext());
1499515033
if (isa<UsingShadowDecl>(D))

clang/test/SemaTemplate/concepts.cpp

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,8 @@ void SingleDepthReferencesTopCalled(U &&u) {
410410

411411
template <typename U>
412412
void SingleDepthReferencesTopLambda(U &&u) {
413-
[]()
414-
requires IsInt<decltype(u)>
413+
[]() // #SDRTL_OP
414+
requires IsInt<decltype(u)> // #SDRTL_REQ
415415
{}();
416416
}
417417

@@ -434,8 +434,8 @@ void DoubleDepthReferencesTop(U &&u) {
434434

435435
template <typename U>
436436
void DoubleDepthReferencesTopLambda(U &&u) {
437-
[]() { []()
438-
requires IsInt<decltype(u)>
437+
[]() { []() // #DDRTL_OP
438+
requires IsInt<decltype(u)> // #DDRTL_REQ
439439
{}(); }();
440440
}
441441

@@ -459,10 +459,11 @@ void DoubleDepthReferencesAll(U &&u) {
459459

460460
template <typename U>
461461
void DoubleDepthReferencesAllLambda(U &&u) {
462-
[](U &&u2) {
463-
[](U && u3)
464-
requires IsInt<decltype(u)> &&
465-
IsInt<decltype(u2)> && IsInt<decltype(u3)>
462+
[](U &&u2) { // #DDRAL_OP1
463+
[](U && u3) // #DDRAL_OP2
464+
requires IsInt<decltype(u)> // #DDRAL_REQ
465+
&& IsInt<decltype(u2)>
466+
&& IsInt<decltype(u3)>
466467
{}(u2);
467468
}(u);
468469
}
@@ -484,8 +485,8 @@ struct CausesFriendConstraint {
484485
template <typename T>
485486
void ChecksLocalVar(T x) {
486487
T Local;
487-
[]()
488-
requires(IsInt<decltype(Local)>)
488+
[]() // #CLV_OP
489+
requires(IsInt<decltype(Local)>) // #CLV_REQ
489490
{}();
490491
}
491492

@@ -527,8 +528,12 @@ void test_dependent() {
527528
SingleDepthReferencesTopNotCalled(will_fail);
528529
SingleDepthReferencesTopCalled(v); // #SDRTC
529530
SingleDepthReferencesTopLambda(v);
530-
// FIXME: This should error on constraint failure! (Lambda!)
531531
SingleDepthReferencesTopLambda(will_fail);
532+
// expected-note@-1{{in instantiation of function template specialization}}
533+
// expected-error@#SDRTL_OP{{no matching function for call to object of type}}
534+
// expected-note@#SDRTL_OP{{candidate function not viable: constraints not satisfied}}
535+
// expected-note@#SDRTL_REQ{{because 'IsInt<decltype(u)>' evaluated to false}}
536+
532537
DoubleDepthReferencesTop(v);
533538
DoubleDepthReferencesTop(will_fail);
534539
// expected-error@#DDRT_CALL{{no matching function for call to object of type 'lc2'}}
@@ -538,8 +543,12 @@ void test_dependent() {
538543
// expected-note@#DDRT_REQ{{'IsInt<decltype(u)>' evaluated to false}}
539544

540545
DoubleDepthReferencesTopLambda(v);
541-
// FIXME: This should error on constraint failure! (Lambda!)
542546
DoubleDepthReferencesTopLambda(will_fail);
547+
// expected-note@-1{{in instantiation of function template specialization}}
548+
// expected-error@#DDRTL_OP{{no matching function for call to object of type}}
549+
// expected-note@#DDRTL_OP{{candidate function not viable: constraints not satisfied}}
550+
// expected-note@#DDRTL_OP{{while substituting into a lambda expression here}}
551+
// expected-note@#DDRTL_REQ{{because 'IsInt<decltype(u)>' evaluated to false}}
543552
DoubleDepthReferencesAll(v);
544553
DoubleDepthReferencesAll(will_fail);
545554
// expected-error@#DDRA_CALL{{no matching function for call to object of type 'lc2'}}
@@ -549,8 +558,12 @@ void test_dependent() {
549558
// expected-note@#DDRA_REQ{{'IsInt<decltype(u)>' evaluated to false}}
550559

551560
DoubleDepthReferencesAllLambda(v);
552-
// FIXME: This should error on constraint failure! (Lambda!)
553561
DoubleDepthReferencesAllLambda(will_fail);
562+
// expected-note@-1{{in instantiation of function template specialization}}
563+
// expected-note@#DDRAL_OP1{{while substituting into a lambda expression here}}
564+
// expected-error@#DDRAL_OP2{{no matching function for call to object of type}}
565+
// expected-note@#DDRAL_OP2{{candidate function not viable: constraints not satisfied}}
566+
// expected-note@#DDRAL_REQ{{because 'IsInt<decltype(u)>' evaluated to false}}
554567

555568
CausesFriendConstraint<int> CFC;
556569
FriendFunc(CFC, 1);
@@ -563,8 +576,13 @@ void test_dependent() {
563576
// ChecksCapture(v);
564577

565578
ChecksLocalVar(v);
566-
// FIXME: This should error on constraint failure! (Lambda!)
567579
ChecksLocalVar(will_fail);
580+
// expected-note@-1{{in instantiation of function template specialization}}
581+
// expected-error@#CLV_OP{{no matching function for call to object of type}}
582+
// expected-note@#CLV_OP{{candidate function not viable: constraints not satisfied}}
583+
// expected-note@#CLV_REQ{{because 'IsInt<decltype(Local)>' evaluated to false}}
584+
585+
568586

569587
LocalStructMemberVar(v);
570588
LocalStructMemberVar(will_fail);
@@ -701,6 +719,18 @@ namespace SelfFriend {
701719
} // namespace SelfFriend
702720

703721

722+
namespace Surrogates {
723+
int f1(int);
724+
template <auto N>
725+
struct A {
726+
using F = int(int);
727+
operator F*() requires N { return f1; } // expected-note{{conversion candidate 'operator int (*)(int)' not viable: constraints not satisfied}}
728+
};
729+
int i = A<true>{}(0);
730+
int j = A<false>{}(0); // expected-error{{no matching function for call to object of type 'A<false>'}}
731+
}
732+
733+
704734
namespace ConstrainedMemberVarTemplate {
705735
template <long Size> struct Container {
706736
static constexpr long arity = Size;
@@ -914,3 +944,53 @@ struct W0 {
914944

915945
static_assert(W0<0>::W1<1>::F<int>::value == 1);
916946
} // TemplateInsideTemplateInsideTemplate
947+
948+
949+
namespace GH63181 {
950+
951+
template<auto N, class T> void f() {
952+
auto l = []() requires N { }; // expected-note 2{{candidate function not viable: constraints not satisfied}} \
953+
// expected-note 2{{because 'false' evaluated to false}}
954+
955+
l();
956+
// expected-error@-1 {{no matching function for call to object of type}}
957+
void(*ptr)() = l;
958+
// expected-error-re@-1 {{no viable conversion from '(lambda {{.*}})' to 'void (*)()'}}
959+
}
960+
961+
template void f<false, int>(); // expected-note {{in instantiation of function template specialization 'GH63181::f<false, int>' requested here}}
962+
template void f<true, int>();
963+
964+
template<class T> concept C = __is_same(T, int); // expected-note{{because '__is_same(char, int)' evaluated to false}}
965+
966+
template<class... Ts> void f() {
967+
([]() requires C<Ts> { return Ts(); }(), ...);
968+
// expected-error@-1 {{no matching function for call to object of type}} \
969+
// expected-note@-1 {{candidate function not viable: constraints not satisfied}} \
970+
// expected-note@-1 {{because 'char' does not satisfy 'C'}}
971+
}
972+
973+
template void f<int, int, int>();
974+
template void f<int, int, char>();
975+
//expected-note@-1{{in instantiation of function template specialization 'GH63181::f<int, int, char>' requested here}}
976+
977+
978+
template <typename T, bool IsTrue>
979+
concept Test = IsTrue; // expected-note 2{{because 'false' evaluated to false}}
980+
981+
template <typename T, bool IsTrue>
982+
void params() {
983+
auto l = [](T t) // expected-note 2{{candidate function not viable: constraints not satisfied}}
984+
requires Test<decltype(t), IsTrue> // expected-note 2{{because 'Test<decltype(t), false>' evaluated to false}}
985+
{};
986+
using F = void(T);
987+
F* f = l; // expected-error {{no viable conversion from}}
988+
l(0); // expected-error {{no matching function for call to object}}
989+
}
990+
991+
void test_params() {
992+
params<int, true>();
993+
params<int, false>(); // expected-note {{in instantiation of function template specialization 'GH63181::params<int, false>' requested here}}
994+
}
995+
996+
}

0 commit comments

Comments
 (0)