Skip to content

[Clang][Sema] Implement proposed resolution for CWG2847 #80899

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
Feb 6, 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/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2982,6 +2982,9 @@ def err_trailing_requires_clause_on_deduction_guide : Error<
"deduction guide cannot have a requires clause">;
def err_constrained_non_templated_function
: Error<"non-templated function cannot have a requires clause">;
def err_non_temp_spec_requires_clause : Error<
"%select{explicit|friend}0 specialization cannot have a trailing requires clause "
"unless it declares a function template">;
def err_reference_to_function_with_unsatisfied_constraints : Error<
"invalid reference to function %0: constraints not satisfied">;
def err_requires_expr_local_parameter_default_argument : Error<
Expand Down
103 changes: 54 additions & 49 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10440,6 +10440,60 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
diag::ext_operator_new_delete_declared_inline)
<< NewFD->getDeclName();

if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
// C++20 [dcl.decl.general]p4:
// The optional requires-clause in an init-declarator or
// member-declarator shall be present only if the declarator declares a
// templated function.
//
// C++20 [temp.pre]p8:
// An entity is templated if it is
// - a template,
// - an entity defined or created in a templated entity,
// - a member of a templated entity,
// - an enumerator for an enumeration that is a templated entity, or
// - the closure type of a lambda-expression appearing in the
// declaration of a templated entity.
//
// [Note 6: A local class, a local or block variable, or a friend
// function defined in a templated entity is a templated entity.
// — end note]
//
// A templated function is a function template or a function that is
// templated. A templated class is a class template or a class that is
// templated. A templated variable is a variable template or a variable
// that is templated.
if (!FunctionTemplate) {
if (isFunctionTemplateSpecialization || isMemberSpecialization) {
// C++ [temp.expl.spec]p8 (proposed resolution for CWG2847):
// An explicit specialization shall not have a trailing
// requires-clause unless it declares a function template.
//
// Since a friend function template specialization cannot be
// definition, and since a non-template friend declaration with a
// trailing requires-clause must be a definition, we diagnose
// friend function template specializations with trailing
// requires-clauses on the same path as explicit specializations
// even though they aren't necessarily prohibited by the same
// language rule.
Diag(TRC->getBeginLoc(), diag::err_non_temp_spec_requires_clause)
<< isFriend;
} else if (isFriend && NewFD->isTemplated() &&
!D.isFunctionDefinition()) {
// 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();
} else if (!NewFD->isTemplated() ||
!(isa<CXXMethodDecl>(NewFD) || D.isFunctionDefinition())) {
Diag(TRC->getBeginLoc(),
diag::err_constrained_non_templated_function);
}
}
}

// We do not add HD attributes to specializations here because
// they may have different constexpr-ness compared to their
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
Expand Down Expand Up @@ -12063,55 +12117,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
checkThisInStaticMemberFunctionType(Method);
}

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,
// - an entity defined ([basic.def]) or created ([class.temporary]) in a
// templated entity,
// - a member of a templated entity,
// - an enumerator for an enumeration that is a templated entity, or
// - the closure type of a lambda-expression ([expr.prim.lambda.closure])
// appearing in the declaration of a templated entity. [Note 6: A local
// class, a local or block variable, or a friend function defined in a
// templated entity is a templated entity. — end note]
//
// A templated function is a function template or a function that is
// templated. A templated class is a class template or a class that is
// templated. A templated variable is a variable template or a variable
// that is templated.

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() &&
// 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();
}
}

if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
ActOnConversionDeclarator(Conversion);

Expand Down
60 changes: 60 additions & 0 deletions clang/test/CXX/drs/dr28xx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: %clang_cc1 -std=c++98 -verify=expected %s
// RUN: %clang_cc1 -std=c++11 -verify=expected %s
// RUN: %clang_cc1 -std=c++14 -verify=expected %s
// RUN: %clang_cc1 -std=c++17 -verify=expected %s
// RUN: %clang_cc1 -std=c++20 -verify=expected,since-cxx20 %s
// RUN: %clang_cc1 -std=c++23 -verify=expected,since-cxx20,since-cxx23 %s
// RUN: %clang_cc1 -std=c++2c -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s

#if __cplusplus < 202002L
// expected-no-diagnostics
#endif

namespace dr2847 { // dr2847: 19

#if __cplusplus >= 202002L

template<typename>
void i();

struct A {
template<typename>
void f() requires true;

template<>
void f<int>() requires true;
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}

friend void i<int>() requires true;
// since-cxx20-error@-1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
};

template<typename>
struct B {
void f() requires true;

template<typename>
void g() requires true;

template<typename>
void h() requires true;

template<>
void h<int>() requires true;
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}

friend void i<int>() requires true;
// since-cxx20-error@-1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
};

template<>
void B<int>::f() requires true;
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}

template<>
template<typename T>
void B<int>::g() requires true;

#endif

} // namespace dr2847