[clang] 51a3019 - [Clang][Sema] Implement proposed resolution for CWG2847 (#80899)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 6 13:06:42 PST 2024
Author: Krystian Stasiowski
Date: 2024-02-06T16:06:38-05:00
New Revision: 51a3019e4d096d93820f921af20d7a0bf3fffc48
URL: https://github.com/llvm/llvm-project/commit/51a3019e4d096d93820f921af20d7a0bf3fffc48
DIFF: https://github.com/llvm/llvm-project/commit/51a3019e4d096d93820f921af20d7a0bf3fffc48.diff
LOG: [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).
Added:
clang/test/CXX/drs/dr28xx.cpp
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaDecl.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f76e7a3392183e..b4dc4feee8e63a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -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<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 481d952d2389bb..18a5d93ab8e8c6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -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
diff erent constexpr-ness compared to their
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
@@ -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);
diff --git a/clang/test/CXX/drs/dr28xx.cpp b/clang/test/CXX/drs/dr28xx.cpp
new file mode 100644
index 00000000000000..7f72003d66f1e3
--- /dev/null
+++ b/clang/test/CXX/drs/dr28xx.cpp
@@ -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 at -1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+
+ friend void i<int>() requires true;
+ // since-cxx20-error at -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 at -1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}
+
+ friend void i<int>() requires true;
+ // since-cxx20-error at -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 at -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
More information about the cfe-commits
mailing list