[clang] 6da3d66 - [concepts] Implement dcl.decl.general p4: No constraints on non-template funcs

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Tue May 17 06:21:56 PDT 2022


Author: Erich Keane
Date: 2022-05-17T06:21:51-07:00
New Revision: 6da3d66f03f9162ef341cc67218be40e22fe9808

URL: https://github.com/llvm/llvm-project/commit/6da3d66f03f9162ef341cc67218be40e22fe9808
DIFF: https://github.com/llvm/llvm-project/commit/6da3d66f03f9162ef341cc67218be40e22fe9808.diff

LOG: [concepts] Implement dcl.decl.general p4: No constraints on non-template funcs

The standard says:
The optional requires-clause ([temp.pre]) in an init-declarator or
member-declarator shall be present only if the declarator declares a
templated function ([dcl.fct]).

This implements that limitation, and updates the tests to the best of my
ability to capture the intent of the original checks.

Differential Revision: https://reviews.llvm.org/D125711

Added: 
    clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaDecl.cpp
    clang/test/CXX/dcl/dcl.decl/p3.cpp
    clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp
    clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
    clang/test/CXX/over/over.match/over.match.viable/p3.cpp
    clang/test/CXX/over/over.over/p4-2a.cpp
    clang/test/Parser/cxx-concepts-requires-clause.cpp
    clang/test/Parser/cxx2a-concepts-requires-expr.cpp
    clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8058df16bc4c3..79e5a4622e8a6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -365,6 +365,8 @@ C++ Language Changes in Clang
   of clang; use the ``-fclang-abi-compat=14`` option to get the old mangling.
 - Preprocessor character literals with a ``u8`` prefix are now correctly treated as
   unsigned character literals. This fixes `Issue 54886 <https://github.com/llvm/llvm-project/issues/54886>`_.
+- Stopped allowing constriants on non-template functions to be compliant with
+  dcl.decl.general p4.
 
 C++20 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e03c583c63fe4..da44a0405b05c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2853,6 +2853,8 @@ def err_constrained_virtual_method : Error<
   "virtual function cannot have a requires clause">;
 def err_trailing_requires_clause_on_deduction_guide : Error<
   "deduction guide cannot have a requires clause">;
+def err_constrained_non_templated_function
+    : Error<"non-templated function cannot have a requires clause">;
 def err_reference_to_function_with_unsatisfied_constraints : Error<
   "invalid reference to function %0: constraints not satisfied">;
 def err_requires_expr_local_parameter_default_argument : Error<

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 426985fea64b0..d8ed252954969 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11393,6 +11393,15 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
         checkThisInStaticMemberFunctionType(Method);
     }
 
+    // C++20: dcl.decl.general p4:
+    // The optional requires-clause ([temp.pre]) in an init-declarator or
+    // member-declarator shall be present only if the declarator declares a
+    // templated function ([dcl.fct]).
+    if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
+      if (!NewFD->isTemplated() && !NewFD->isTemplateInstantiation())
+        Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
+    }
+
     if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
       ActOnConversionDeclarator(Conversion);
 

diff  --git a/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
new file mode 100644
index 0000000000000..3c012c8d57bbe
--- /dev/null
+++ b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// expected-error at +2 {{non-templated function cannot have a requires clause}}
+void f1(int a)
+  requires true;
+template <typename T>
+auto f2(T a) -> bool
+  requires true; // OK
+
+// expected-error at +4 {{trailing return type must appear before trailing requires clause}}
+template <typename T>
+auto f3(T a)
+  requires true
+-> bool;
+
+// expected-error at +2{{trailing requires clause can only be used when declaring a function}}
+void (*pf)()
+  requires true;
+
+// expected-error at +1{{trailing requires clause can only be used when declaring a function}}
+void g(int (*)() requires true);
+
+// expected-error at +1{{expected expression}}
+auto *p = new void(*)(char)
+  requires true;

diff  --git a/clang/test/CXX/dcl/dcl.decl/p3.cpp b/clang/test/CXX/dcl/dcl.decl/p3.cpp
index 5bfec8a22da29..f141568ba6c22 100644
--- a/clang/test/CXX/dcl/dcl.decl/p3.cpp
+++ b/clang/test/CXX/dcl/dcl.decl/p3.cpp
@@ -6,13 +6,40 @@ constexpr bool is_same_v = false;
 template<typename T>
 constexpr bool is_same_v<T, T> = true;
 
-void f1(int a) requires true; // OK
-auto f2(int a) -> bool requires true; // OK
+using T = void ();
+
+void f1non_templ(int a) requires true; // expected-error{{non-templated function cannot have a requires clause}}
+auto f2non_templ(int a) -> bool requires true; // expected-error{{non-templated function cannot have a requires clause}}
+auto f3non_templ(int a) -> bool (*)(int b) requires true; // expected-error{{non-templated function cannot have a requires clause}}
+// expected-error at +1{{non-templated function cannot have a requires clause}}
+auto f4_non_templ(int a) requires true -> bool; // expected-error{{trailing return type must appear before trailing requires clause}}
+void (f7non_templ()) requires true; // expected-error{{non-templated function cannot have a requires clause}}
+// expected-error at +1{{non-templated function cannot have a requires clause}}
+void (f8non_templ() requires true); // expected-error{{trailing requires clause should be placed outside parentheses}}
+// expected-error at +1{{non-templated function cannot have a requires clause}}
+T xnon_templ requires true;
+// expected-error at +2 2{{non-templated function cannot have a requires clause}}
+struct Snon_templ {
+  T m1 requires true, m2 requires true;
+};
+
+template <typename>
+void f1(int a)
+  requires true;                               // OK
+
+template <typename>
+auto f2(int a) -> bool
+  requires true;                                 // OK
+
+template <typename>
 auto f3(int a) -> bool (*)(int b) requires true; // OK
+template <typename>
 auto f4(int a) requires true -> bool; // expected-error{{trailing return type must appear before trailing requires clause}}
 int f5(int a) requires; // expected-error{{expected expression}}
 int f6(int a) requires {} // expected-error{{expected expression}}
+template<typename>
 void (f7()) requires true;
+template<typename>
 void (f8() requires true); // expected-error{{trailing requires clause should be placed outside parentheses}}
 void (*(f9 requires (true)))(); // expected-error{{trailing requires clause should be placed outside parentheses}}
 static_assert(is_same_v<decltype(f9), void (*)()>);
@@ -20,8 +47,14 @@ void (*pf)() requires true; // expected-error{{trailing requires clause can only
 void g1(int (*dsdads)() requires false); // expected-error{{trailing requires clause can only be used when declaring a function}}
 void g2(int (*(*dsdads)())() requires true); // expected-error{{trailing requires clause can only be used when declaring a function}}
 void g3(int (*(*dsdads)(int) requires true)() ); // expected-error{{trailing requires clause should be placed outside parentheses}}
-using T = void ();
+
+template<typename U>
+struct Foo{
 T x requires true;
+};
+Foo<int> f;
+
+template<typename>
 struct S {
   T m1 requires true, m2 requires true;
 };

diff  --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp
index 3c6b1b1bac590..def39e67f7a4f 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp
@@ -2,21 +2,23 @@
 
 namespace functions
 {
-  void foo(int) requires false {}
+  template<typename T>
+  struct S {
+  static void foo(int) requires false {}
   // expected-note at -1 3{{because 'false' evaluated to false}}
-  // expected-note at -2 {{candidate function not viable: constraints not satisfied}}
-  void bar(int) requires true {}
+  static void bar(int) requires true {}
+  };
 
   void a(int);
   void a(double);
 
   void baz() {
-    foo(1); // expected-error{{no matching function for call to 'foo'}}
-    bar(1);
-    void (*p1)(int) = foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
-    void (*p3)(int) = bar;
-    decltype(foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
-    decltype(bar)* a2 = nullptr;
+    S<int>::foo(1); // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
+    S<int>::bar(1);
+    void (*p1)(int) = S<int>::foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
+    void (*p3)(int) = S<int>::bar;
+    decltype(S<int>::foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
+    decltype(S<int>::bar)* a2 = nullptr;
   }
 }
 

diff  --git a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
index dff06308a98ed..047d3dc13cc31 100644
--- a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
+++ b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
@@ -63,51 +63,61 @@ namespace non_template
   template<typename T>
   concept AtMost8 = sizeof(T) <= 8;
 
+  template<typename T>
   int foo() requires AtLeast2<long> && AtMost8<long> {
     return 0;
   }
 
+  template<typename T>
   double foo() requires AtLeast2<long> {
     return 0.0;
   }
 
+  template<typename T>
   double baz() requires AtLeast2<long> && AtMost8<long> { // expected-note {{candidate function}}
     return 0.0;
   }
 
+  template<typename T>
   int baz() requires AtMost8<long> && AtLeast2<long> { // expected-note {{candidate function}}
     return 0.0;
   }
 
+  template<typename T>
   void bar() requires (sizeof(char[8]) >= 8) { }
   // expected-note at -1 {{candidate function}}
   // expected-note at -2 {{similar constraint expressions not considered equivalent}}
 
+  template<typename T>
   void bar() requires (sizeof(char[8]) >= 8 && sizeof(int) <= 30) { }
   // expected-note at -1 {{candidate function}}
   // expected-note at -2 {{similar constraint expression here}}
 
-  static_assert(is_same_v<decltype(foo()), int>);
-  static_assert(is_same_v<decltype(baz()), int>); // expected-error {{call to 'baz' is ambiguous}}
-  static_assert(is_same_v<decltype(bar()), void>); // expected-error {{call to 'bar' is ambiguous}}
-  
+  static_assert(is_same_v<decltype(foo<int>()), int>);
+  static_assert(is_same_v<decltype(baz<int>()), int>); // expected-error {{call to 'baz' is ambiguous}}
+  static_assert(is_same_v<decltype(bar<int>()), void>); // expected-error {{call to 'bar' is ambiguous}}
+
+  template<typename T>
   constexpr int goo(int a) requires AtLeast2<int> && true {
     return 1;
   }
 
+  template<typename T>
   constexpr int goo(const int b) requires AtLeast2<int> {
     return 2;
   }
 
   // Only trailing requires clauses of redeclarations are compared for overload resolution.
+  template<typename T>
   constexpr int doo(int a, ...) requires AtLeast2<int> && true { // expected-note {{candidate function}}
     return 1;
   }
 
+  template<typename T>
   constexpr int doo(int b) requires AtLeast2<int> { // expected-note {{candidate function}}
     return 2;
   }
 
-  static_assert(goo(1) == 1);
-  static_assert(doo(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
+  static_assert(goo<int>(1) == 1);
+  static_assert(doo<int>(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
 }

diff  --git a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp
index c6f9922226771..66a3f3452a580 100644
--- a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp
+++ b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp
@@ -1,10 +1,11 @@
 // RUN:  %clang_cc1 -std=c++2a -verify %s
 
 struct S2 {};
-// expected-note at -1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S1' to 'const S2' for 1st argument}}
-// expected-note at -2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'S1' to 'S2' for 1st argument}}
+// expected-note at -1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S1<int>' to 'const S2' for 1st argument}}
+// expected-note at -2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'S1<int>' to 'S2' for 1st argument}}
 // expected-note at -3 {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}}
 
+template<typename T>
 struct S1 {
   void foo() const requires true {}
   void foo() const requires false {}
@@ -18,12 +19,12 @@ struct S1 {
 };
 
 void foo() {
-  S1().foo();
-  S1().bar();
+  S1<int>().foo();
+  S1<int>().bar();
   // expected-error at -1 {{invalid reference to function 'bar': constraints not satisfied}}
-  (void) static_cast<bool>(S1());
-  (void) static_cast<S2>(S1());
-  // expected-error at -1 {{no matching conversion for static_cast from 'S1' to 'S2'}}
+  (void) static_cast<bool>(S1<int>());
+  (void) static_cast<S2>(S1<int>());
+  // expected-error at -1 {{no matching conversion for static_cast from 'S1<int>' to 'S2'}}
 }
 
 // Test that constraints are checked before implicit conversions are formed.
@@ -35,9 +36,13 @@ struct A {
   operator T() {}
 };
 
-void foo(int) requires false;
-void foo(A) requires true;
+template<typename>
+struct WrapsStatics {
+  static void foo(int) requires false;
+  static void foo(A) requires true;
+};
 
+template<typename T>
 struct S {
   void foo(int) requires false;
   void foo(A) requires true;
@@ -51,13 +56,13 @@ struct S {
 };
 
 void bar() {
-  foo(A{});
-  S{1.}.foo(A{});
+  WrapsStatics<int>::foo(A{});
+  S<int>{1.}.foo(A{});
   // expected-error at -1{{invalid reference to function '~S': constraints not satisfied}}
   // Note - this behavior w.r.t. constrained dtors is a consequence of current
   // wording, which does not invoke overload resolution when a dtor is called.
   // P0848 is set to address this issue.
-  S s = 1;
+  S<int> s = 1;
   // expected-error at -1{{invalid reference to function '~S': constraints not satisfied}}
   int a = s;
-}
\ No newline at end of file
+}

diff  --git a/clang/test/CXX/over/over.over/p4-2a.cpp b/clang/test/CXX/over/over.over/p4-2a.cpp
index 669620360f882..9d0e7c463d43e 100644
--- a/clang/test/CXX/over/over.over/p4-2a.cpp
+++ b/clang/test/CXX/over/over.over/p4-2a.cpp
@@ -12,50 +12,48 @@ concept AtLeast2 = sizeof(T) >= 2;
 template<typename T>
 concept AtMost8 = sizeof(T) <= 8;
 
-int foo() requires AtLeast2<long> && AtMost8<long> {
+template<typename T>
+struct S {
+static int foo() requires AtLeast2<long> && AtMost8<long> {
   return 0;
 }
 
-double foo() requires AtLeast2<char> {
+static double foo() requires AtLeast2<char> {
   return 0.0;
 }
 
-char bar() requires AtLeast2<char> { // expected-note {{possible target for call}}
+static char bar() requires AtLeast2<char> {
   return 1.0;
 }
 
-short bar() requires AtLeast2<long> && AtMost8<long> {
-// expected-note at -1{{possible target for call}}
-// expected-note at -2{{candidate function}}
+static short bar() requires AtLeast2<long> && AtMost8<long> {
   return 0.0;
 }
 
-int bar() requires AtMost8<long> && AtLeast2<long> {
-// expected-note at -1{{possible target for call}}
-// expected-note at -2{{candidate function}}
+static int bar() requires AtMost8<long> && AtLeast2<long> {
   return 0.0;
 }
 
-char baz() requires AtLeast2<char> {
+static char baz() requires AtLeast2<char> {
   return 1.0;
 }
 
-short baz() requires AtLeast2<long> && AtMost8<long> {
+static short baz() requires AtLeast2<long> && AtMost8<long> {
   return 0.0;
 }
 
-int baz() requires AtMost8<long> && AtLeast2<long> {
+static int baz() requires AtMost8<long> && AtLeast2<long> {
   return 0.0;
 }
 
-long baz() requires AtMost8<long> && AtLeast2<long> && AtLeast2<short> {
+static long baz() requires AtMost8<long> && AtLeast2<long> && AtLeast2<short> {
   return 3.0;
 }
+};
 
 void a() {
-  static_assert(is_same_v<decltype(&foo), int(*)()>);
-  static_assert(is_same_v<decltype(&bar), long(*)()>);
-  // expected-error at -1{{reference to overloaded function could not be resolved; did you mean to call it with no arguments?}}
-  // expected-error at -2{{call to 'bar' is ambiguous}}
-  static_assert(is_same_v<decltype(&baz), long(*)()>);
-}
\ No newline at end of file
+  static_assert(is_same_v<decltype(&S<int>::foo), int(*)()>);
+  static_assert(is_same_v<decltype(&S<int>::bar), long(*)()>);
+  // expected-error at -1{{reference to overloaded function could not be resolved; did you mean to call it?}}
+  static_assert(is_same_v<decltype(&S<int>::baz), long(*)()>);
+}

diff  --git a/clang/test/Parser/cxx-concepts-requires-clause.cpp b/clang/test/Parser/cxx-concepts-requires-clause.cpp
index 68f1af8b12cc0..db8f167ba9755 100644
--- a/clang/test/Parser/cxx-concepts-requires-clause.cpp
+++ b/clang/test/Parser/cxx-concepts-requires-clause.cpp
@@ -139,8 +139,10 @@ template<typename T>
 void bar() requires (sizeof(T)) == 0;
 // expected-error at -1{{parentheses are required around this expression in a requires clause}}
 
+template<typename T>
 void bar(int x, int y) requires (x, y, true);
 
+template<typename T>
 struct B {
   int x;
   void foo(int y) requires (x, this, this->x, y, true);

diff  --git a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp
index d3f46c163ffd8..a18a54c7fad06 100644
--- a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp
+++ b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp
@@ -130,6 +130,7 @@ bool r35 = requires { requires true };
 
 bool r36 = requires (bool b) { requires sizeof(b) == 1; };
 
+template<typename T>
 void r37(bool b) requires requires { 1 } {}
 // expected-error at -1 {{expected ';' at end of requirement}}
 

diff  --git a/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp b/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp
index 3b93e6fc87858..8d7248ebec080 100644
--- a/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp
+++ b/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp
@@ -1,12 +1,15 @@
 // RUN: %clang_cc1 -std=c++20 -verify %s
 
 namespace P1972 {
-void f(int) requires false; // expected-note 4{{because 'false' evaluated to false}} \
-                            // expected-note{{constraints not satisfied}}
+template <typename T>
+struct S {
+  static void f(int)
+    requires false; // expected-note 4{{because 'false' evaluated to false}}
+};
 void g() {
-  f(0);                      // expected-error{{no matching function for call to 'f'}}
-  void (*p1)(int) = f;       // expected-error{{invalid reference to function 'f': constraints not satisfied}}
-  void (*p21)(int) = &f;     // expected-error{{invalid reference to function 'f': constraints not satisfied}}
-  decltype(f) *p2 = nullptr; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
+  S<int>::f(0);                      // expected-error{{invalid reference to function 'f': constraints not satisfied}}
+  void (*p1)(int) = S<int>::f;       // expected-error{{invalid reference to function 'f': constraints not satisfied}}
+  void (*p21)(int) = &S<int>::f;     // expected-error{{invalid reference to function 'f': constraints not satisfied}}
+  decltype(S<int>::f) *p2 = nullptr; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
 }
 }


        


More information about the cfe-commits mailing list