-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang] Emit error for invalid friend functions under [temp.friend]p9 #78083
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
@llvm/pr-subscribers-clang Author: None (antangelo) ChangesEmits an error for friend FunctionDecls that either:
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. Full diff: https://github.com/llvm/llvm-project/pull/78083.diff 6 Files Affected:
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@+1 {{non-templated function cannot have a requires clause}}
+ // expected-error@+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>
|
Emits an error for friend FunctionDecls that either:
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.