[clang] 4444eeb - Improve requirement clause limitation on non templated function
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 29 06:27:31 PDT 2023
Author: Erich Keane
Date: 2023-03-29T06:27:25-07:00
New Revision: 4444eeb753e5b7918141b830213cd3eae0c1fd75
URL: https://github.com/llvm/llvm-project/commit/4444eeb753e5b7918141b830213cd3eae0c1fd75
DIFF: https://github.com/llvm/llvm-project/commit/4444eeb753e5b7918141b830213cd3eae0c1fd75.diff
LOG: Improve requirement clause limitation on non templated function
The current implementation 6da3d66f03f9162ef341cc67218be40e22fe9808
got a few things wrong, particularly that a template, or definition
or member in a templated entity is required to be allowed to have a
trailing requires clause.
This patch corrects this, as reproted by #61748
Fixes: #61748
Differential Revision: https://reviews.llvm.org/D147070
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaLambda.cpp
clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
clang/test/Parser/cxx2b-lambdas.cpp
clang/test/SemaCXX/lambda-capture-type-deduction.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index aabaab5ca5dc..a41010d91e24 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -248,7 +248,11 @@ Bug Fixes in This Version
- Fix false-positive diagnostic issued for consteval initializers of temporary
objects.
(`#60286 <https://github.com/llvm/llvm-project/issues/60286>`_)
-
+- Correct restriction of trailing requirements clauses on a templated function.
+ Previously we only rejected non-'templated' things, but the restrictions ALSO need
+ to limit non-defined/non-member functions as well. Additionally, we now diagnose
+ requires on lambdas when not allowed, which we previously missed.
+ (`#61748 <https://github.com/llvm/llvm-project/issues/61748>`_)
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index dd001dba2b91..7596b51bf054 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11882,8 +11882,33 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
// member-declarator shall be present only if the declarator declares a
// templated function ([dcl.fct]).
if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
- if (!NewFD->isTemplated() && !NewFD->isTemplateInstantiation())
+ // [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.
+
+ if (!NewFD->getDescribedFunctionTemplate() && // -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()) {
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
+ }
}
if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 64db9d065f9c..b6a4f2604255 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1380,6 +1380,37 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
PushOnScopeChains(P, CurScope);
}
+ // 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 = Method->getTrailingRequiresClause()) {
+ // [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.
+
+ // Note: we only have to check if this is defined in a template entity, OR
+ // if we are a template, since the rest don't apply. The requires clause
+ // applies to the call operator, which we already know is a member function,
+ // AND defined.
+ if (!Method->getDescribedFunctionTemplate() && !Method->isTemplated()) {
+ Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
+ }
+ }
+
// Enter a new evaluation context to insulate the lambda from any
// cleanups from the enclosing full-expression.
PushExpressionEvaluationContext(
diff --git a/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
index 3c012c8d57bb..83ec78dac9fe 100644
--- a/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
@@ -23,3 +23,41 @@ void g(int (*)() requires true);
// expected-error at +1{{expected expression}}
auto *p = new void(*)(char)
requires true;
+
+namespace GH61748 {
+template<typename T>
+struct S {
+ // expected-error at +1 {{non-templated function cannot have a requires clause}}
+ friend void declared_friend() requires(sizeof(T) > 1);
+ // OK, is a definition.
+ friend void defined_friend() requires(sizeof(T) > 1){}
+ // OK, is a member.
+ void member() requires(sizeof(T) > 1);
+};
+
+template<typename T>
+void ContainingFunction() {
+ // expected-error at +1 {{non-templated function cannot have a requires clause}}
+ void bad() requires(sizeof(T) > 1);
+ // expected-error at +1 {{function definition is not allowed here}}
+ void still_bad() requires(sizeof(T) > 1) {}
+
+}
+
+void NonTemplContainingFunction() {
+ // expected-error at +1 {{non-templated function cannot have a requires clause}}
+ (void)[]() requires (sizeof(int)>1){};
+ // OK, a template.
+ auto X = [](auto) requires (sizeof(int)>1){};
+ // OK, a template.
+ auto Y = []<typename T>(T t) requires (sizeof(int)>1){};
+
+ X(1);
+ Y(1);
+}
+
+template<typename T>
+union U {
+ void f() requires true;
+};
+}
diff --git a/clang/test/Parser/cxx2b-lambdas.cpp b/clang/test/Parser/cxx2b-lambdas.cpp
index 9474dface2e3..2de4b6593cd3 100644
--- a/clang/test/Parser/cxx2b-lambdas.cpp
+++ b/clang/test/Parser/cxx2b-lambdas.cpp
@@ -18,7 +18,7 @@ auto L9 = []<typename T> { return true; };
auto L10 = []<typename T> noexcept { return true; };
auto L11 = []<typename T> -> bool { return true; };
auto L12 = [] consteval {};
-auto L13 = []() requires true {};
+auto L13 = []() requires true {}; // expected-error{{non-templated function cannot have a requires clause}}
auto L14 = []<auto> requires true() requires true {};
auto L15 = []<auto> requires true noexcept {};
auto L16 = [] [[maybe_unused]]{};
diff --git a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
index e22e3af02c5c..8c9f6f69e91d 100644
--- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
+++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
@@ -48,6 +48,7 @@ void test_noexcept() {
static_assert(noexcept([&] mutable noexcept(is_same<int &, decltype((y))>) {}()));
}
+template<typename T>
void test_requires() {
int x;
@@ -77,6 +78,10 @@ void test_requires() {
[x = 1]() mutable requires is_same<int &, decltype((x))> {} ();
}
+void use() {
+ test_requires<int>();
+}
+
void err() {
int y, z;
(void)[x = 1]<typename T>
More information about the cfe-commits
mailing list