[clang] [Clang][Sema] Implement proposed resolution for CWG2847 (PR #80899)

Krystian Stasiowski via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 6 12:16:28 PST 2024


https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/80899

>From f9079ac4098b2debf3df4ebee3e5f4f0c2b1e6a2 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 6 Feb 2024 14:22:37 -0500
Subject: [PATCH 1/2] [Clang][Sema] Implement proposed resolution for CWG2847

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/lib/Sema/SemaDecl.cpp                   | 103 +++++++++---------
 clang/test/CXX/drs/dr28xx.cpp                 |  41 +++++++
 3 files changed, 98 insertions(+), 49 deletions(-)
 create mode 100644 clang/test/CXX/drs/dr28xx.cpp

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f76e7a3392183..b4dc4feee8e63 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2982,6 +2982,9 @@ 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_non_temp_spec_requires_clause : Error<
+  "%select{explicit|friend}0 specialization cannot have a trailing requires clause "
+  "unless it declares a function template">;
 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 481d952d2389b..18a5d93ab8e8c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10440,6 +10440,60 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
            diag::ext_operator_new_delete_declared_inline)
         << NewFD->getDeclName();
 
+    if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
+      // C++20 [dcl.decl.general]p4:
+      //   The optional requires-clause in an init-declarator or
+      //   member-declarator shall be present only if the declarator declares a
+      //   templated function.
+      //
+      // C++20 [temp.pre]p8:
+      //   An entity is templated if it is
+      //     - a template,
+      //     - an entity defined or created in a templated entity,
+      //     - a member of a templated entity,
+      //     - an enumerator for an enumeration that is a templated entity, or
+      //     - the closure type of a lambda-expression appearing in the
+      //       declaration of a templated entity.
+      //
+      //   [Note 6: A local class, a local or block variable, or a friend
+      //   function defined in a templated entity is a templated entity.
+      //   — end note]
+      //
+      //   A templated function is a function template or a function that is
+      //   templated. A templated class is a class template or a class that is
+      //   templated. A templated variable is a variable template or a variable
+      //   that is templated.
+      if (!FunctionTemplate) {
+        if (isFunctionTemplateSpecialization || isMemberSpecialization) {
+          // C++ [temp.expl.spec]p8 (proposed resolution for CWG2847):
+          //   An explicit specialization shall not have a trailing
+          //   requires-clause unless it declares a function template.
+          //
+          // Since a friend function template specialization cannot be
+          // definition, and since a non-template friend declaration with a
+          // trailing requires-clause must be a definition, we diagnose
+          // friend function template specializations with trailing
+          // requires-clauses on the same path as explicit specializations
+          // even though they aren't necessarily prohibited by the same
+          // language rule.
+          Diag(TRC->getBeginLoc(), diag::err_non_temp_spec_requires_clause)
+              << isFriend;
+        } else if (isFriend && NewFD->isTemplated() &&
+                   !D.isFunctionDefinition()) {
+          // C++ [temp.friend]p9:
+          //   A non-template friend declaration with a requires-clause shall be
+          //   a definition.
+          Diag(NewFD->getBeginLoc(),
+               diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
+          NewFD->setInvalidDecl();
+        } else if (!NewFD->isTemplated() ||
+                   !(isa<CXXMethodDecl>(NewFD) || D.isFunctionDefinition())) {
+          Diag(TRC->getBeginLoc(),
+               diag::err_constrained_non_templated_function);
+        }
+      }
+    }
+
     // We do not add HD attributes to specializations here because
     // they may have different constexpr-ness compared to their
     // templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
@@ -12063,55 +12117,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
         checkThisInStaticMemberFunctionType(Method);
     }
 
-    if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
-      // 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]).
-      //
-      // [temp.pre]/8:
-      // An entity is templated if it is
-      // - a template,
-      // - an entity defined ([basic.def]) or created ([class.temporary]) in a
-      // templated entity,
-      // - a member of a templated entity,
-      // - an enumerator for an enumeration that is a templated entity, or
-      // - the closure type of a lambda-expression ([expr.prim.lambda.closure])
-      // appearing in the declaration of a templated entity. [Note 6: A local
-      // class, a local or block variable, or a friend function defined in a
-      // templated entity is a templated entity.  — end note]
-      //
-      // A templated function is a function template or a function that is
-      // templated. A templated class is a class template or a class that is
-      // templated. A templated variable is a variable template or a variable
-      // that is templated.
-
-      bool IsTemplate = NewFD->getDescribedFunctionTemplate();
-      bool IsFriend = NewFD->getFriendObjectKind();
-      if (!IsTemplate && // -a template
-                         // defined... in a templated entity
-          !(DeclIsDefn && NewFD->isTemplated()) &&
-          // a member of a templated entity
-          !(isa<CXXMethodDecl>(NewFD) && NewFD->isTemplated()) &&
-          // Don't complain about instantiations, they've already had these
-          // rules + others enforced.
-          !NewFD->isTemplateInstantiation() &&
-          // If the function violates [temp.friend]p9 because it is missing
-          // a definition, and adding a definition would make it templated,
-          // then let that error take precedence.
-          !(!DeclIsDefn && IsFriend && NewFD->isTemplated())) {
-        Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
-      } else if (!DeclIsDefn && !IsTemplate && IsFriend &&
-                 !NewFD->isTemplateInstantiation()) {
-        // C++ [temp.friend]p9:
-        //   A non-template friend declaration with a requires-clause shall be a
-        //   definition.
-        Diag(NewFD->getBeginLoc(),
-             diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
-        NewFD->setInvalidDecl();
-      }
-    }
-
     if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
       ActOnConversionDeclarator(Conversion);
 
diff --git a/clang/test/CXX/drs/dr28xx.cpp b/clang/test/CXX/drs/dr28xx.cpp
new file mode 100644
index 0000000000000..f8e804baa7385
--- /dev/null
+++ b/clang/test/CXX/drs/dr28xx.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -std=c++20 -verify=expected %s
+
+namespace dr2847 { // dr2847: 19
+
+template<typename>
+void i();
+
+struct A {
+  template<typename>
+  void f() requires true;
+
+  template<>
+  void f<int>() requires true; // expected-error {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+
+  friend void i<int>() requires true; // expected-error {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
+};
+
+template<typename>
+struct B {
+  void f() requires true;
+
+  template<typename>
+  void g() requires true;
+
+  template<typename>
+  void h() requires true;
+
+  template<>
+  void h<int>() requires true; // expected-error {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+
+  friend void i<int>() requires true; // expected-error {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
+};
+
+template<>
+void B<int>::f() requires true; // expected-error {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+
+template<>
+template<typename T>
+void B<int>::g() requires true;
+
+} // namespace dr2847

>From 0744fe431f965e53bce7a66a95c7da72b4312f95 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 6 Feb 2024 15:16:17 -0500
Subject: [PATCH 2/2] [FOLD]

---
 clang/test/CXX/drs/dr28xx.cpp | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/clang/test/CXX/drs/dr28xx.cpp b/clang/test/CXX/drs/dr28xx.cpp
index f8e804baa7385..cd07fb8d0e013 100644
--- a/clang/test/CXX/drs/dr28xx.cpp
+++ b/clang/test/CXX/drs/dr28xx.cpp
@@ -1,4 +1,12 @@
-// RUN: %clang_cc1 -std=c++20 -verify=expected %s
+// RUN: %clang_cc1 -std=c++98 -verify=expected %s
+// RUN: %clang_cc1 -std=c++11 -verify=expected %s
+// RUN: %clang_cc1 -std=c++14 -verify=expected %s
+// RUN: %clang_cc1 -std=c++17 -verify=expected %s
+// RUN: %clang_cc1 -std=c++20 -verify=expected,since-cxx20 %s
+// RUN: %clang_cc1 -std=c++23 -verify=expected,since-cxx20,since-cxx23 %s
+// RUN: %clang_cc1 -std=c++2c -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s
+
+#if __cplusplus >= 202302L
 
 namespace dr2847 { // dr2847: 19
 
@@ -10,9 +18,11 @@ struct A {
   void f() requires true;
 
   template<>
-  void f<int>() requires true; // expected-error {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+  void f<int>() requires true;
+  // since-cxx20-error at -1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
 
-  friend void i<int>() requires true; // expected-error {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
+  friend void i<int>() requires true;
+  // since-cxx20-error at -1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
 };
 
 template<typename>
@@ -26,16 +36,21 @@ struct B {
   void h() requires true;
 
   template<>
-  void h<int>() requires true; // expected-error {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+  void h<int>() requires true;
+  // since-cxx20-error at -1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
 
-  friend void i<int>() requires true; // expected-error {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
+  friend void i<int>() requires true;
+  // since-cxx20-error at -1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
 };
 
 template<>
-void B<int>::f() requires true; // expected-error {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+void B<int>::f() requires true;
+// since-cxx20-error at -1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
 
 template<>
 template<typename T>
 void B<int>::g() requires true;
 
 } // namespace dr2847
+
+#endif



More information about the cfe-commits mailing list