Skip to content

Commit

Permalink
[clang] Emit error for invalid friend functions under [temp.friend]p9 (
Browse files Browse the repository at this point in the history
…#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.
  • Loading branch information
antangelo committed Jan 17, 2024
1 parent 705c5b8 commit 46a395d
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 13 deletions.
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 @@ -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<
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 @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -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();
}
}

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

0 comments on commit 46a395d

Please sign in to comment.