Skip to content

[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

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
41 changes: 33 additions & 8 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -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();
}
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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){}
Expand Down
8 changes: 4 additions & 4 deletions clang/test/SemaTemplate/GH71595.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
}
13 changes: 13 additions & 0 deletions clang/test/SemaTemplate/concepts-friends.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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>
Expand Down