Skip to content

Commit 46a395d

Browse files
authored
[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.
1 parent 705c5b8 commit 46a395d

File tree

6 files changed

+59
-13
lines changed

6 files changed

+59
-13
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,9 @@ Improvements to Clang's diagnostics
560560
- Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
561561
- Clang now diagnoses attempts to bind a bitfield to an NTTP of a reference type as erroneous
562562
converted constant expression and not as a reference to subobject.
563+
- Clang now diagnoses the requirement that non-template friend declarations with requires clauses
564+
and template friend declarations with a constraint that depends on a template parameter from an
565+
enclosing template must be a definition.
563566

564567

565568
Improvements to Clang's time-trace

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7006,6 +7006,11 @@ def err_member_decl_does_not_match : Error<
70067006
"does not match any declaration in %1">;
70077007
def err_friend_decl_with_def_arg_must_be_def : Error<
70087008
"friend declaration specifying a default argument must be a definition">;
7009+
def err_friend_decl_with_enclosing_temp_constraint_must_be_def : Error<
7010+
"friend declaration with a constraint that depends on an enclosing "
7011+
"template parameter must be a definition">;
7012+
def err_non_temp_friend_decl_with_requires_clause_must_be_def : Error<
7013+
"non-template friend declaration with a requires clause must be a definition">;
70097014
def err_friend_decl_with_def_arg_redeclared : Error<
70107015
"friend declaration specifying a default argument must be the only declaration">;
70117016
def err_friend_decl_does_not_match : Error<

clang/lib/Sema/SemaDecl.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10846,9 +10846,19 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1084610846
// Precalculate whether this is a friend function template with a constraint
1084710847
// that depends on an enclosing template, per [temp.friend]p9.
1084810848
if (isFriend && FunctionTemplate &&
10849-
FriendConstraintsDependOnEnclosingTemplate(NewFD))
10849+
FriendConstraintsDependOnEnclosingTemplate(NewFD)) {
1085010850
NewFD->setFriendConstraintRefersToEnclosingTemplate(true);
1085110851

10852+
// C++ [temp.friend]p9:
10853+
// A friend function template with a constraint that depends on a
10854+
// template parameter from an enclosing template shall be a definition.
10855+
if (!D.isFunctionDefinition()) {
10856+
Diag(NewFD->getBeginLoc(),
10857+
diag::err_friend_decl_with_enclosing_temp_constraint_must_be_def);
10858+
NewFD->setInvalidDecl();
10859+
}
10860+
}
10861+
1085210862
if (FunctionTemplate) {
1085310863
if (NewFD->isInvalidDecl())
1085410864
FunctionTemplate->setInvalidDecl();
@@ -12065,11 +12075,12 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1206512075
checkThisInStaticMemberFunctionType(Method);
1206612076
}
1206712077

12068-
// C++20: dcl.decl.general p4:
12069-
// The optional requires-clause ([temp.pre]) in an init-declarator or
12070-
// member-declarator shall be present only if the declarator declares a
12071-
// templated function ([dcl.fct]).
1207212078
if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
12079+
// C++20: dcl.decl.general p4:
12080+
// The optional requires-clause ([temp.pre]) in an init-declarator or
12081+
// member-declarator shall be present only if the declarator declares a
12082+
// templated function ([dcl.fct]).
12083+
//
1207312084
// [temp.pre]/8:
1207412085
// An entity is templated if it is
1207512086
// - a template,
@@ -12087,15 +12098,29 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1208712098
// templated. A templated variable is a variable template or a variable
1208812099
// that is templated.
1208912100

12090-
if (!NewFD->getDescribedFunctionTemplate() && // -a template
12091-
// defined... in a templated entity
12101+
bool IsTemplate = NewFD->getDescribedFunctionTemplate();
12102+
bool IsFriend = NewFD->getFriendObjectKind();
12103+
if (!IsTemplate && // -a template
12104+
// defined... in a templated entity
1209212105
!(DeclIsDefn && NewFD->isTemplated()) &&
1209312106
// a member of a templated entity
1209412107
!(isa<CXXMethodDecl>(NewFD) && NewFD->isTemplated()) &&
1209512108
// Don't complain about instantiations, they've already had these
1209612109
// rules + others enforced.
12097-
!NewFD->isTemplateInstantiation()) {
12110+
!NewFD->isTemplateInstantiation() &&
12111+
// If the function violates [temp.friend]p9 because it is missing
12112+
// a definition, and adding a definition would make it templated,
12113+
// then let that error take precedence.
12114+
!(!DeclIsDefn && IsFriend && NewFD->isTemplated())) {
1209812115
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
12116+
} else if (!DeclIsDefn && !IsTemplate && IsFriend &&
12117+
!NewFD->isTemplateInstantiation()) {
12118+
// C++ [temp.friend]p9:
12119+
// A non-template friend declaration with a requires-clause shall be a
12120+
// definition.
12121+
Diag(NewFD->getBeginLoc(),
12122+
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
12123+
NewFD->setInvalidDecl();
1209912124
}
1210012125
}
1210112126

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ auto *p = new void(*)(char)
2727
namespace GH61748 {
2828
template<typename T>
2929
struct S {
30-
// expected-error@+1 {{non-templated function cannot have a requires clause}}
30+
// expected-error@+1 {{non-template friend declaration with a requires clause must be a definition}}
3131
friend void declared_friend() requires(sizeof(T) > 1);
3232
// OK, is a definition.
3333
friend void defined_friend() requires(sizeof(T) > 1){}

clang/test/SemaTemplate/GH71595.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ void f() {
1818
template<class A>
1919
class temp {
2020
template<C<temp> T>
21-
friend void g();
21+
friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
2222

23-
temp(); // expected-note {{implicitly declared private here}}
23+
temp();
2424
};
2525

2626
template<C<temp<int>> T>
2727
void g() {
28-
auto v = temp<T>(); // expected-error {{calling a private constructor of class 'temp<int>'}}
28+
auto v = temp<T>();
2929
}
3030

3131
void h() {
3232
f<int>();
33-
g<int>(); // expected-note {{in instantiation of function template specialization 'g<int>' requested here}}
33+
g<int>();
3434
}

clang/test/SemaTemplate/concepts-friends.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
template <typename T>
44
concept constraint = false;
5+
56
namespace temp_friend_9 {
67
// A non-template friend declaration with a requires-clause shall be a
78
// definition. ...Such a constrained friend function ... does not declare the
@@ -11,6 +12,14 @@ struct NonTemplateFriend {
1112
friend void foo()
1213
requires true
1314
{}
15+
16+
friend void baz() // expected-error {{non-template friend declaration with a requires clause must be a definition}}
17+
requires true;
18+
};
19+
20+
struct TempP9NotShownIfFunctionWouldBeInvalidAnyway {
21+
friend void foo()
22+
requires true; // expected-error {{non-templated function cannot have a requires clause}}
1423
};
1524

1625
// A friend function template with a constraint that depends on a template
@@ -19,6 +28,10 @@ struct NonTemplateFriend {
1928
// function template as a declaration in any other scope.
2029
template <typename T>
2130
struct TemplateFromEnclosing {
31+
template <typename U>
32+
friend void bar2() // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
33+
requires constraint<T>;
34+
2235
template <typename U>
2336
friend void foo()
2437
requires constraint<T>

0 commit comments

Comments
 (0)