Skip to content

Commit 51a3019

Browse files
authored
[Clang][Sema] Implement proposed resolution for CWG2847 (#80899)
Per the approved resolution for CWG2847, [temp.expl.spec] p8 will state: > An explicit specialization shall not have a trailing _requires-clause_ unless it declares a function template. We already implement this _partially_ insofar that a diagnostic is issued upon instantiation of `A<int>` in the following example: ``` template<typename> struct A { template<typename> void f(); template<> void f<int>() requires true; // error: non-templated function cannot have a requires clause }; template struct A<int>; // note: in instantiation of template class 'A<int>' requested here ``` This patch adds a bespoke diagnostic for such declarations, and moves the point of diagnosis for non-templated functions with trailing requires-clauses from `CheckFunctionDeclaration` to `ActOnFunctionDeclarator` (there is no point in diagnosing this during instantiation since we already have all the necessary information when parsing the declaration).
1 parent 3a05e01 commit 51a3019

File tree

3 files changed

+117
-49
lines changed

3 files changed

+117
-49
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2982,6 +2982,9 @@ def err_trailing_requires_clause_on_deduction_guide : Error<
29822982
"deduction guide cannot have a requires clause">;
29832983
def err_constrained_non_templated_function
29842984
: Error<"non-templated function cannot have a requires clause">;
2985+
def err_non_temp_spec_requires_clause : Error<
2986+
"%select{explicit|friend}0 specialization cannot have a trailing requires clause "
2987+
"unless it declares a function template">;
29852988
def err_reference_to_function_with_unsatisfied_constraints : Error<
29862989
"invalid reference to function %0: constraints not satisfied">;
29872990
def err_requires_expr_local_parameter_default_argument : Error<

clang/lib/Sema/SemaDecl.cpp

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10440,6 +10440,60 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1044010440
diag::ext_operator_new_delete_declared_inline)
1044110441
<< NewFD->getDeclName();
1044210442

10443+
if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
10444+
// C++20 [dcl.decl.general]p4:
10445+
// The optional requires-clause in an init-declarator or
10446+
// member-declarator shall be present only if the declarator declares a
10447+
// templated function.
10448+
//
10449+
// C++20 [temp.pre]p8:
10450+
// An entity is templated if it is
10451+
// - a template,
10452+
// - an entity defined or created in a templated entity,
10453+
// - a member of a templated entity,
10454+
// - an enumerator for an enumeration that is a templated entity, or
10455+
// - the closure type of a lambda-expression appearing in the
10456+
// declaration of a templated entity.
10457+
//
10458+
// [Note 6: A local class, a local or block variable, or a friend
10459+
// function defined in a templated entity is a templated entity.
10460+
// — end note]
10461+
//
10462+
// A templated function is a function template or a function that is
10463+
// templated. A templated class is a class template or a class that is
10464+
// templated. A templated variable is a variable template or a variable
10465+
// that is templated.
10466+
if (!FunctionTemplate) {
10467+
if (isFunctionTemplateSpecialization || isMemberSpecialization) {
10468+
// C++ [temp.expl.spec]p8 (proposed resolution for CWG2847):
10469+
// An explicit specialization shall not have a trailing
10470+
// requires-clause unless it declares a function template.
10471+
//
10472+
// Since a friend function template specialization cannot be
10473+
// definition, and since a non-template friend declaration with a
10474+
// trailing requires-clause must be a definition, we diagnose
10475+
// friend function template specializations with trailing
10476+
// requires-clauses on the same path as explicit specializations
10477+
// even though they aren't necessarily prohibited by the same
10478+
// language rule.
10479+
Diag(TRC->getBeginLoc(), diag::err_non_temp_spec_requires_clause)
10480+
<< isFriend;
10481+
} else if (isFriend && NewFD->isTemplated() &&
10482+
!D.isFunctionDefinition()) {
10483+
// C++ [temp.friend]p9:
10484+
// A non-template friend declaration with a requires-clause shall be
10485+
// a definition.
10486+
Diag(NewFD->getBeginLoc(),
10487+
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
10488+
NewFD->setInvalidDecl();
10489+
} else if (!NewFD->isTemplated() ||
10490+
!(isa<CXXMethodDecl>(NewFD) || D.isFunctionDefinition())) {
10491+
Diag(TRC->getBeginLoc(),
10492+
diag::err_constrained_non_templated_function);
10493+
}
10494+
}
10495+
}
10496+
1044310497
// We do not add HD attributes to specializations here because
1044410498
// they may have different constexpr-ness compared to their
1044510499
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
@@ -12063,55 +12117,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1206312117
checkThisInStaticMemberFunctionType(Method);
1206412118
}
1206512119

12066-
if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
12067-
// C++20: dcl.decl.general p4:
12068-
// The optional requires-clause ([temp.pre]) in an init-declarator or
12069-
// member-declarator shall be present only if the declarator declares a
12070-
// templated function ([dcl.fct]).
12071-
//
12072-
// [temp.pre]/8:
12073-
// An entity is templated if it is
12074-
// - a template,
12075-
// - an entity defined ([basic.def]) or created ([class.temporary]) in a
12076-
// templated entity,
12077-
// - a member of a templated entity,
12078-
// - an enumerator for an enumeration that is a templated entity, or
12079-
// - the closure type of a lambda-expression ([expr.prim.lambda.closure])
12080-
// appearing in the declaration of a templated entity. [Note 6: A local
12081-
// class, a local or block variable, or a friend function defined in a
12082-
// templated entity is a templated entity. — end note]
12083-
//
12084-
// A templated function is a function template or a function that is
12085-
// templated. A templated class is a class template or a class that is
12086-
// templated. A templated variable is a variable template or a variable
12087-
// that is templated.
12088-
12089-
bool IsTemplate = NewFD->getDescribedFunctionTemplate();
12090-
bool IsFriend = NewFD->getFriendObjectKind();
12091-
if (!IsTemplate && // -a template
12092-
// defined... in a templated entity
12093-
!(DeclIsDefn && NewFD->isTemplated()) &&
12094-
// a member of a templated entity
12095-
!(isa<CXXMethodDecl>(NewFD) && NewFD->isTemplated()) &&
12096-
// Don't complain about instantiations, they've already had these
12097-
// rules + others enforced.
12098-
!NewFD->isTemplateInstantiation() &&
12099-
// If the function violates [temp.friend]p9 because it is missing
12100-
// a definition, and adding a definition would make it templated,
12101-
// then let that error take precedence.
12102-
!(!DeclIsDefn && IsFriend && NewFD->isTemplated())) {
12103-
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
12104-
} else if (!DeclIsDefn && !IsTemplate && IsFriend &&
12105-
!NewFD->isTemplateInstantiation()) {
12106-
// C++ [temp.friend]p9:
12107-
// A non-template friend declaration with a requires-clause shall be a
12108-
// definition.
12109-
Diag(NewFD->getBeginLoc(),
12110-
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
12111-
NewFD->setInvalidDecl();
12112-
}
12113-
}
12114-
1211512120
if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
1211612121
ActOnConversionDeclarator(Conversion);
1211712122

clang/test/CXX/drs/dr28xx.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %clang_cc1 -std=c++98 -verify=expected %s
2+
// RUN: %clang_cc1 -std=c++11 -verify=expected %s
3+
// RUN: %clang_cc1 -std=c++14 -verify=expected %s
4+
// RUN: %clang_cc1 -std=c++17 -verify=expected %s
5+
// RUN: %clang_cc1 -std=c++20 -verify=expected,since-cxx20 %s
6+
// RUN: %clang_cc1 -std=c++23 -verify=expected,since-cxx20,since-cxx23 %s
7+
// RUN: %clang_cc1 -std=c++2c -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s
8+
9+
#if __cplusplus < 202002L
10+
// expected-no-diagnostics
11+
#endif
12+
13+
namespace dr2847 { // dr2847: 19
14+
15+
#if __cplusplus >= 202002L
16+
17+
template<typename>
18+
void i();
19+
20+
struct A {
21+
template<typename>
22+
void f() requires true;
23+
24+
template<>
25+
void f<int>() requires true;
26+
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
27+
28+
friend void i<int>() requires true;
29+
// since-cxx20-error@-1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
30+
};
31+
32+
template<typename>
33+
struct B {
34+
void f() requires true;
35+
36+
template<typename>
37+
void g() requires true;
38+
39+
template<typename>
40+
void h() requires true;
41+
42+
template<>
43+
void h<int>() requires true;
44+
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
45+
46+
friend void i<int>() requires true;
47+
// since-cxx20-error@-1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
48+
};
49+
50+
template<>
51+
void B<int>::f() requires true;
52+
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
53+
54+
template<>
55+
template<typename T>
56+
void B<int>::g() requires true;
57+
58+
#endif
59+
60+
} // namespace dr2847

0 commit comments

Comments
 (0)