cor3ntin updated this revision to Diff 536976.
cor3ntin added a comment.

- Move the check for lambda conversion operator in CheckFunmctionConstraints so 
that it is always performed.

- Add tests of constraints referring to parameters of the lambda


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D154368/new/

https://reviews.llvm.org/D154368

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaConcept.cpp
  clang/lib/Sema/SemaLambda.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/SemaTemplate/concepts.cpp

Index: clang/test/SemaTemplate/concepts.cpp
===================================================================
--- clang/test/SemaTemplate/concepts.cpp
+++ clang/test/SemaTemplate/concepts.cpp
@@ -410,8 +410,8 @@
 
 template <typename U>
 void SingleDepthReferencesTopLambda(U &&u) {
-  []()
-    requires IsInt<decltype(u)>
+  []() // #SDRTL_OP
+    requires IsInt<decltype(u)> // #SDRTL_REQ
   {}();
 }
 
@@ -434,8 +434,8 @@
 
 template <typename U>
 void DoubleDepthReferencesTopLambda(U &&u) {
-  []() { []()
-           requires IsInt<decltype(u)>
+  []() { []() // #DDRTL_OP
+           requires IsInt<decltype(u)> // #DDRTL_REQ
          {}(); }();
 }
 
@@ -459,10 +459,11 @@
 
 template <typename U>
 void DoubleDepthReferencesAllLambda(U &&u) {
-  [](U &&u2) {
-    [](U && u3)
-      requires IsInt<decltype(u)> &&
-               IsInt<decltype(u2)> && IsInt<decltype(u3)>
+  [](U &&u2) { // #DDRAL_OP1
+    [](U && u3) // #DDRAL_OP2
+      requires IsInt<decltype(u)> // #DDRAL_REQ
+            && IsInt<decltype(u2)>
+            && IsInt<decltype(u3)>
     {}(u2);
   }(u);
 }
@@ -484,8 +485,8 @@
 template <typename T>
 void ChecksLocalVar(T x) {
   T Local;
-  []()
-    requires(IsInt<decltype(Local)>)
+  []() // #CLV_OP
+    requires(IsInt<decltype(Local)>) // #CLV_REQ
   {}();
 }
 
@@ -529,6 +530,11 @@
   SingleDepthReferencesTopLambda(v);
   // FIXME: This should error on constraint failure! (Lambda!)
   SingleDepthReferencesTopLambda(will_fail);
+  // expected-note@-1{{in instantiation of function template specialization}}
+  // expected-error@#SDRTL_OP{{no matching function for call to object of type}}
+  // expected-note@#SDRTL_OP{{candidate function not viable: constraints not satisfied}}
+  // expected-note@#SDRTL_REQ{{because 'IsInt<decltype(u)>' evaluated to false}}
+
   DoubleDepthReferencesTop(v);
   DoubleDepthReferencesTop(will_fail);
   // expected-error@#DDRT_CALL{{no matching function for call to object of type 'lc2'}}
@@ -538,8 +544,12 @@
   // expected-note@#DDRT_REQ{{'IsInt<decltype(u)>' evaluated to false}}
 
   DoubleDepthReferencesTopLambda(v);
-  // FIXME: This should error on constraint failure! (Lambda!)
   DoubleDepthReferencesTopLambda(will_fail);
+  // expected-note@-1{{in instantiation of function template specialization}}
+  // expected-error@#DDRTL_OP{{no matching function for call to object of type}}
+  // expected-note@#DDRTL_OP{{candidate function not viable: constraints not satisfied}}
+  // expected-note@#DDRTL_OP{{while substituting into a lambda expression here}}
+  // expected-note@#DDRTL_REQ{{because 'IsInt<decltype(u)>' evaluated to false}}
   DoubleDepthReferencesAll(v);
   DoubleDepthReferencesAll(will_fail);
   // expected-error@#DDRA_CALL{{no matching function for call to object of type 'lc2'}}
@@ -549,8 +559,12 @@
   // expected-note@#DDRA_REQ{{'IsInt<decltype(u)>' evaluated to false}}
 
   DoubleDepthReferencesAllLambda(v);
-  // FIXME: This should error on constraint failure! (Lambda!)
   DoubleDepthReferencesAllLambda(will_fail);
+  // expected-note@-1{{in instantiation of function template specialization}}
+  // expected-note@#DDRAL_OP1{{while substituting into a lambda expression here}}
+  // expected-error@#DDRAL_OP2{{no matching function for call to object of type}}
+  // expected-note@#DDRAL_OP2{{candidate function not viable: constraints not satisfied}}
+  // expected-note@#DDRAL_REQ{{because 'IsInt<decltype(u)>' evaluated to false}}
 
   CausesFriendConstraint<int> CFC;
   FriendFunc(CFC, 1);
@@ -565,6 +579,12 @@
   ChecksLocalVar(v);
   // FIXME: This should error on constraint failure! (Lambda!)
   ChecksLocalVar(will_fail);
+  // expected-note@-1{{in instantiation of function template specialization}}
+  // expected-error@#CLV_OP{{no matching function for call to object of type}}
+  // expected-note@#CLV_OP{{candidate function not viable: constraints not satisfied}}
+  // expected-note@#CLV_REQ{{because 'IsInt<decltype(Local)>' evaluated to false}}
+
+
 
   LocalStructMemberVar(v);
   LocalStructMemberVar(will_fail);
@@ -701,6 +721,18 @@
 } // namespace SelfFriend
 
 
+namespace Surrogates {
+int f1(int);
+template <auto N>
+struct A {
+    using F = int(int);
+    operator F*() requires N { return f1; } // expected-note{{candidate surrogate function 'operator int (*)(int)' not viable: constraints not satisfied}}
+};
+int i = A<true>{}(0);
+int j = A<false>{}(0); // expected-error{{no matching function for call to object of type 'A<false>'}}
+}
+
+
 namespace ConstrainedMemberVarTemplate {
 template <long Size> struct Container {
   static constexpr long arity = Size;
@@ -914,3 +946,53 @@
 
 static_assert(W0<0>::W1<1>::F<int>::value == 1);
 } // TemplateInsideTemplateInsideTemplate
+
+
+namespace GH63181 {
+
+template<auto N, class T> void f() {
+auto l = []() requires N { }; // expected-note 2{{candidate function not viable: constraints not satisfied}} \
+                              // expected-note 2{{because 'false' evaluated to false}}
+
+l();
+// expected-error@-1 {{no matching function for call to object of type}}
+void(*ptr)() = l;
+// expected-error-re@-1 {{no viable conversion from '(lambda {{.*}})' to 'void (*)()'}}
+}
+
+template void f<false, int>(); // expected-note {{in instantiation of function template specialization 'GH63181::f<false, int>' requested here}}
+template void f<true, int>();
+
+template<class T> concept C = __is_same(T, int); // expected-note{{because '__is_same(char, int)' evaluated to false}}
+
+template<class... Ts> void f() {
+  ([]() requires C<Ts> { return Ts(); }(), ...);
+  // expected-error@-1 {{no matching function for call to object of type}} \
+  // expected-note@-1 {{candidate function not viable: constraints not satisfied}} \
+  // expected-note@-1 {{because 'char' does not satisfy 'C'}}
+}
+
+template void f<int, int, int>();
+template void f<int, int, char>();
+//expected-note@-1{{in instantiation of function template specialization 'GH63181::f<int, int, char>' requested here}}
+
+
+template <typename T, bool IsTrue>
+concept Test = IsTrue; // expected-note 2{{because 'false' evaluated to false}}
+
+template <typename T, bool IsTrue>
+void params() {
+    auto l = [](T t)  // expected-note 2{{candidate function not viable: constraints not satisfied}}
+    requires Test<decltype(t), IsTrue> // expected-note 2{{because 'Test<decltype(t), false>' evaluated to false}}
+    {};
+    using F = void(T);
+    F* f = l; // expected-error {{no viable conversion from}}
+    l(0); // expected-error {{no matching function for call to object}}
+}
+
+void test_params() {
+    params<int, true>();
+    params<int, false>(); // expected-note {{in instantiation of function template specialization 'GH63181::params<int, false>' requested here}}
+}
+
+}
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
@@ -7851,6 +7852,17 @@
     }
   }
 
+  if (Conversion->getTrailingRequiresClause()) {
+    ConstraintSatisfaction Satisfaction;
+    if (CheckFunctionConstraints(Conversion, Satisfaction, /*Loc*/ {},
+                                 /*ForOverloadResolution*/ true) ||
+        !Satisfaction.IsSatisfied) {
+      Candidate.Viable = false;
+      Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
+      return;
+    }
+  }
+
   if (EnableIfAttr *FailedAttr =
           CheckEnableIf(Conversion, CandidateSet.getLocation(), std::nullopt)) {
     Candidate.Viable = false;
@@ -11620,8 +11632,17 @@
   if (isRValueReference) FnType = S.Context.getRValueReferenceType(FnType);
   if (isLValueReference) FnType = S.Context.getLValueReferenceType(FnType);
 
-  S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand)
-    << FnType;
+  if (Cand->FailureKind == ovl_fail_constraints_not_satisfied) {
+    S.Diag(Cand->Surrogate->getLocation(),
+           diag::note_ovl_surrogate_constraints_not_satisfied)
+        << Cand->Surrogate;
+    ConstraintSatisfaction Satisfaction;
+    if (S.CheckFunctionConstraints(Cand->Surrogate, Satisfaction))
+      S.DiagnoseUnsatisfiedConstraint(Satisfaction);
+  } else {
+    S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand)
+        << FnType;
+  }
 }
 
 static void NoteBuiltinOperatorCandidate(Sema &S, StringRef Opc,
@@ -14944,6 +14965,22 @@
                        /*SuppressUserConversion=*/false);
   }
 
+  // When calling a lambda, both the call operator, and
+  // the conversion operator to function pointer
+  // are considered. But when constraint checking
+  // on the call operator fails, it will also fail on the
+  // conversion operator as the constraints are always the same.
+  // As the user probably do not intend to perform a surrogate call,
+  // we filter them out to produce better error diagnostics, ie to avoid
+  // showing 2 failed overloads instead of one.
+  bool IgnoreSurrogateFunctions = false;
+  if (CandidateSet.size() == 1 && Record->getAsCXXRecordDecl()->isLambda()) {
+    const OverloadCandidate &Candidate = *CandidateSet.begin();
+    if (!Candidate.Viable &&
+        Candidate.FailureKind == ovl_fail_constraints_not_satisfied)
+      IgnoreSurrogateFunctions = true;
+  }
+
   // C++ [over.call.object]p2:
   //   In addition, for each (non-explicit in C++0x) conversion function
   //   declared in T of the form
@@ -14963,7 +15000,8 @@
   //   within T by another intervening declaration.
   const auto &Conversions =
       cast<CXXRecordDecl>(Record->getDecl())->getVisibleConversionFunctions();
-  for (auto I = Conversions.begin(), E = Conversions.end(); I != E; ++I) {
+  for (auto I = Conversions.begin(), E = Conversions.end();
+       !IgnoreSurrogateFunctions && I != E; ++I) {
     NamedDecl *D = *I;
     CXXRecordDecl *ActingContext = cast<CXXRecordDecl>(D->getDeclContext());
     if (isa<UsingShadowDecl>(D))
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1633,6 +1633,12 @@
   Conversion->setAccess(AS_public);
   Conversion->setImplicit(true);
 
+  // A non-generic lambda may still be a templated entity
+  // We need to preserve constraints when converting the
+  // lambda to a function pointer. See GH63181.
+  if (Expr *Requires = CallOperator->getTrailingRequiresClause())
+    Conversion->setTrailingRequiresClause(Requires);
+
   if (Class->isGenericLambda()) {
     // Create a template version of the conversion operator, using the template
     // parameter list of the function call operator.
Index: clang/lib/Sema/SemaConcept.cpp
===================================================================
--- clang/lib/Sema/SemaConcept.cpp
+++ clang/lib/Sema/SemaConcept.cpp
@@ -679,6 +679,15 @@
     return false;
   }
 
+  // A lambda conversion operator has the same constraints as the call operator
+  // and constraints checking relies on whether we are in a lambda call operator
+  // (and may refer to its parameters), so check the call operator instead.
+  if (const CXXConversionDecl *MD = dyn_cast<CXXConversionDecl>(FD);
+      MD && isLambdaConversionOperator(const_cast<CXXConversionDecl *>(MD)))
+    return CheckFunctionConstraints(MD->getParent()->getLambdaCallOperator(),
+                                    Satisfaction, UsageLoc,
+                                    ForOverloadResolution);
+
   DeclContext *CtxToSave = const_cast<FunctionDecl *>(FD);
 
   while (isLambdaCallOperator(CtxToSave) || FD->isTransparentContext()) {
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4714,6 +4714,9 @@
 def note_ovl_candidate_constraints_not_satisfied : Note<
     "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: constraints "
     "not satisfied">;
+def note_ovl_surrogate_constraints_not_satisfied : Note<
+    "candidate surrogate function %0 not viable: constraints "
+    "not satisfied">;
 def note_implicit_member_target_infer_collision : Note<
     "implicit %sub{select_special_member_kind}0 inferred target collision: call to both "
     "%select{__device__|__global__|__host__|__host__ __device__}1 and "
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -630,6 +630,8 @@
 - Allow abstract parameter and return types in functions that are
   either deleted or not defined.
   (`#63012 <https://github.com/llvm/llvm-project/issues/63012>`_)
+- Fix constraint checking of non-generic lambdas.
+  (`#63181 <https://github.com/llvm/llvm-project/issues/63181>`_)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to