Skip to content

Commit

Permalink
Improve requirement clause limitation on non templated function
Browse files Browse the repository at this point in the history
The current implementation 6da3d66
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
  • Loading branch information
Erich Keane committed Mar 29, 2023
1 parent c4861e3 commit 4444eeb
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 3 deletions.
6 changes: 5 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
27 changes: 26 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
38 changes: 38 additions & 0 deletions clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,41 @@ void g(int (*)() requires true);
// expected-error@+1{{expected expression}}
auto *p = new void(*)(char)
requires true;

namespace GH61748 {
template<typename T>
struct S {
// expected-error@+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@+1 {{non-templated function cannot have a requires clause}}
void bad() requires(sizeof(T) > 1);
// expected-error@+1 {{function definition is not allowed here}}
void still_bad() requires(sizeof(T) > 1) {}

}

void NonTemplContainingFunction() {
// expected-error@+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;
};
}
2 changes: 1 addition & 1 deletion clang/test/Parser/cxx2b-lambdas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]]{};
Expand Down
5 changes: 5 additions & 0 deletions clang/test/SemaCXX/lambda-capture-type-deduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>
Expand Down

0 comments on commit 4444eeb

Please sign in to comment.