[clang] 46a395d - [clang] Emit error for invalid friend functions under [temp.friend]p9 (#78083)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 16 18:06:07 PST 2024
Author: antangelo
Date: 2024-01-16T21:06:02-05:00
New Revision: 46a395d8c41f6009a7fbae51f408c3c6ea2399d3
URL: https://github.com/llvm/llvm-project/commit/46a395d8c41f6009a7fbae51f408c3c6ea2399d3
DIFF: https://github.com/llvm/llvm-project/commit/46a395d8c41f6009a7fbae51f408c3c6ea2399d3.diff
LOG: [clang] Emit error for invalid friend functions under [temp.friend]p9 (#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.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaDecl.cpp
clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
clang/test/SemaTemplate/GH71595.cpp
clang/test/SemaTemplate/concepts-friends.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 36d9da9b89bab01..1eba8ab5590c521 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 414779a7970ab8e..991c72cad33cad6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7006,6 +7006,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 c6d00ddf5c10885..bd38298a1481faa 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10846,9 +10846,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();
@@ -12065,11 +12075,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,
@@ -12087,15 +12098,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