diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index aabaab5ca5dc2..a41010d91e24c 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 `_) - +- 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 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index dd001dba2b912..7596b51bf0545 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(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(NewFD)) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 64db9d065f9c6..b6a4f2604255f 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 3c012c8d57bbe..83ec78dac9fe3 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@+1{{expected expression}} auto *p = new void(*)(char) requires true; + +namespace GH61748 { +template +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 +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 = [](T t) requires (sizeof(int)>1){}; + + X(1); + Y(1); +} + +template +union U { + void f() requires true; +}; +} diff --git a/clang/test/Parser/cxx2b-lambdas.cpp b/clang/test/Parser/cxx2b-lambdas.cpp index 9474dface2e3d..2de4b6593cd3c 100644 --- a/clang/test/Parser/cxx2b-lambdas.cpp +++ b/clang/test/Parser/cxx2b-lambdas.cpp @@ -18,7 +18,7 @@ auto L9 = [] { return true; }; auto L10 = [] noexcept { return true; }; auto L11 = [] -> 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 = [] requires true() requires true {}; auto L15 = [] 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 e22e3af02c5c0..8c9f6f69e91db 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) {}())); } +template void test_requires() { int x; @@ -77,6 +78,10 @@ void test_requires() { [x = 1]() mutable requires is_same {} (); } +void use() { + test_requires(); +} + void err() { int y, z; (void)[x = 1]