[clang] [clang] Emit error for invalid friend functions under [temp.friend]p9 (PR #78083)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 13 22:32:05 PST 2024


https://github.com/antangelo created https://github.com/llvm/llvm-project/pull/78083

Emits an error for friend FunctionDecls that either:

* are not templates and have a requires clause
* are templates, and have a constrained parameter that depends on a template parameter from an enclosing template

and are not a definition.

For a non-template friend function with a requires clause, if the function is not templated then the original error message indicating that such a function is disallowed is shown instead, as the function will still be rejected if a definition is added.

>From c1721e27831d124ad4d18552f98d6949ef75c5af Mon Sep 17 00:00:00 2001
From: Antonio Abbatangelo <contact at antangelo.com>
Date: Sat, 13 Jan 2024 19:29:27 -0500
Subject: [PATCH] [clang] Emit error for invalid friend functions under
 [temp.friend]p9

Emits an error for friend FunctionDecls that either:

* are not templates and have a requires clause
* are templates, and have a constrained parameter that depends on a
  template parameter from an enclosing template

and are not a definition.

For a non-template friend function with a requires clause, if the
function is not templated then the original error message indicating
that such a function is disallowed is shown instead, as the function
will still be rejected if a definition is added.
---
 clang/docs/ReleaseNotes.rst                   |  3 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 +++
 clang/lib/Sema/SemaDecl.cpp                   | 41 +++++++++++++++----
 .../CXX/dcl.decl/dcl.decl.general/p4-20.cpp   |  2 +-
 clang/test/SemaTemplate/GH71595.cpp           |  8 ++--
 clang/test/SemaTemplate/concepts-friends.cpp  | 13 ++++++
 6 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3cbce1be1594376..a9b9ff9dcc672c9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -560,6 +560,9 @@ Improvements to Clang's diagnostics
 - Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
 - Clang now diagnoses attempts to bind a bitfield to an NTTP of a reference type as erroneous
   converted constant expression and not as a reference to subobject.
+- Clang now diagnoses the requirement that non-template friend declarations with requires clauses
+  and template friend declarations with a constraint that depends on a template parameter from an
+  enclosing template must be a definition.
 
 
 Improvements to Clang's time-trace
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a79892e40030ae..d08ef92bb614f81 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7000,6 +7000,11 @@ def err_member_decl_does_not_match : Error<
   "does not match any declaration in %1">;
 def err_friend_decl_with_def_arg_must_be_def : Error<
   "friend declaration specifying a default argument must be a definition">;
+def err_friend_decl_with_enclosing_temp_constraint_must_be_def : Error<
+  "friend declaration with a constraint that depends on an enclosing "
+  "template parameter must be a definition">;
+def err_non_temp_friend_decl_with_requires_clause_must_be_def : Error<
+  "non-template friend declaration with a requires clause must be a definition">;
 def err_friend_decl_with_def_arg_redeclared : Error<
   "friend declaration specifying a default argument must be the only declaration">;
 def err_friend_decl_does_not_match : Error<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e92fd104d78eb57..3b913cfe6cbb6cf 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10847,9 +10847,19 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
     // Precalculate whether this is a friend function template with a constraint
     // that depends on an enclosing template, per [temp.friend]p9.
     if (isFriend && FunctionTemplate &&
-        FriendConstraintsDependOnEnclosingTemplate(NewFD))
+        FriendConstraintsDependOnEnclosingTemplate(NewFD)) {
       NewFD->setFriendConstraintRefersToEnclosingTemplate(true);
 
+      // C++ [temp.friend]p9:
+      //    A friend function template with a constraint that depends on a
+      //    template parameter from an enclosing template shall be a definition.
+      if (!D.isFunctionDefinition()) {
+        Diag(NewFD->getBeginLoc(),
+             diag::err_friend_decl_with_enclosing_temp_constraint_must_be_def);
+        NewFD->setInvalidDecl();
+      }
+    }
+
     if (FunctionTemplate) {
       if (NewFD->isInvalidDecl())
         FunctionTemplate->setInvalidDecl();
@@ -12066,11 +12076,12 @@ 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()) {
+      // 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,
@@ -12088,15 +12099,29 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
       // templated. A templated variable is a variable template or a variable
       // that is templated.
 
-      if (!NewFD->getDescribedFunctionTemplate() && // -a template
-          // defined... in a templated entity
+      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()) {
+          !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();
       }
     }
 
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
index 83ec78dac9fe3f8..c7596218db5379f 100644
--- a/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
@@ -27,7 +27,7 @@ auto *p = new void(*)(char)
 namespace GH61748 {
 template<typename T>
 struct S {
-  // expected-error at +1 {{non-templated function cannot have a requires clause}}
+  // expected-error at +1 {{non-template friend declaration with a requires clause must be a definition}}
   friend void declared_friend() requires(sizeof(T) > 1);
   // OK, is a definition.
   friend void defined_friend() requires(sizeof(T) > 1){}
diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
index 7d34d1bf054e4bc..daec9410e547a6b 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -18,17 +18,17 @@ void f() {
 template<class A>
 class temp {
     template<C<temp> T>
-    friend void g();
+    friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
 
-    temp(); // expected-note {{implicitly declared private here}}
+    temp();
 };
 
 template<C<temp<int>> T>
 void g() {
-    auto v = temp<T>(); // expected-error {{calling a private constructor of class 'temp<int>'}}
+    auto v = temp<T>();
 }
 
 void h() {
     f<int>();
-    g<int>(); // expected-note {{in instantiation of function template specialization 'g<int>' requested here}}
+    g<int>();
 }
diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
index 5c4609520a3c7ea..255b0858917fb6f 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -2,6 +2,7 @@
 
 template <typename T>
 concept constraint = false;
+
 namespace temp_friend_9 {
 // A non-template friend declaration with a requires-clause shall be a
 // definition. ...Such a constrained friend function ... does not declare the
@@ -11,6 +12,14 @@ struct NonTemplateFriend {
   friend void foo()
     requires true
   {}
+
+  friend void baz() // expected-error {{non-template friend declaration with a requires clause must be a definition}}
+    requires true;
+};
+
+struct TempP9NotShownIfFunctionWouldBeInvalidAnyway {
+  friend void foo()
+    requires true; // expected-error {{non-templated function cannot have a requires clause}}
 };
 
 // A friend function template with a constraint that depends on a template
@@ -19,6 +28,10 @@ struct NonTemplateFriend {
 // function template as a declaration in any other scope.
 template <typename T>
 struct TemplateFromEnclosing {
+  template <typename U>
+  friend void bar2() // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
+    requires constraint<T>;
+
   template <typename U>
   friend void foo()
     requires constraint<T>



More information about the cfe-commits mailing list