Index: clang/lib/Parse/ParseTemplate.cpp =================================================================== --- clang/lib/Parse/ParseTemplate.cpp +++ clang/lib/Parse/ParseTemplate.cpp @@ -871,6 +871,31 @@ /// type-parameter-key: /// 'class' /// 'typename' [C++1z] +/// +/// In C++20: +/// template-head: [C++ temp.pre] +/// template '<' template-parameter-list '>' requires-clause[opt] +/// +/// template-parameter: [C++ temp.param] +/// type-parameter +/// parameter-declaration +/// +/// type-parameter: +/// type-parameter-key ...[opt] identifier[opt] +/// type-parameter-key identifier[opt] = type-id +/// type-constraint ...[opt] identifier[opt] +/// type-constraint identifier[opt] = type-id +/// template-head type-parameter-key ...[opt] identifier[opt] +/// template-head type-parameter-key identifier[opt] = id-expression +/// +/// type-parameter-key: +/// 'class' +/// 'typename' +/// +/// type-constraint: +/// nested-name-specifier[opt] concept-name +/// nested-name-specifier[opt] concept-name '<' +/// template-argument-list[opt] '>' NamedDecl * Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { assert(Tok.is(tok::kw_template) && "Expected 'template' keyword"); @@ -887,6 +912,20 @@ } } + // Parse optional requires-clause. + ExprResult OptionalRequiresClauseConstraintER; + if (getLangOpts().CPlusPlus20 && TryConsumeToken(tok::kw_requires)) { + OptionalRequiresClauseConstraintER = + Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression( + /*IsTrailingRequiresClause=*/false)); + if (!OptionalRequiresClauseConstraintER.isUsable()) { + // Skip until the semi-colon or a '}'. + SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); + TryConsumeToken(tok::semi); + return nullptr; + } + } + // Provide an ExtWarn if the C++1z feature of using 'typename' here is used. // Generate a meaningful error if the user forgot to put class before the // identifier, comma, or greater. Provide a fixit if the identifier, comma, @@ -946,11 +985,9 @@ if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, true); - TemplateParameterList *ParamList = - Actions.ActOnTemplateParameterList(Depth, SourceLocation(), - TemplateLoc, LAngleLoc, - TemplateParams, - RAngleLoc, nullptr); + TemplateParameterList *ParamList = Actions.ActOnTemplateParameterList( + Depth, SourceLocation(), TemplateLoc, LAngleLoc, TemplateParams, + RAngleLoc, OptionalRequiresClauseConstraintER.get()); // Grab a default argument (if available). // Per C++0x [basic.scope.pdecl]p9, we parse the default argument before Index: clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp =================================================================== --- clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp +++ clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp @@ -32,3 +32,35 @@ using s31 = S3; using s32 = S3; + +template +struct Evals { + bool f() { return true; } +}; + +template<> +struct Evals { + bool f() { return false; } +}; + +template requires C typename P> +struct S4 {}; + +template +requires requires { requires T::f(); } +typename P > + void func(const P &p){}; + +template requires false typename P> +void func_always_requires_false(const P &p) {}; + +void use() { + S4 s4; + func(Evals{}); + // A naive individual might expect the following to all fail concept checking, + // but there does not seem to be any requirement to check these in the + // standard, and none of the other implementations do so either. + S4 s4b; + func(Evals{}); + func_always_requires_false(Evals{}); +} Index: clang/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -912,7 +912,7 @@ P0857R0 - Partial + Clang 14 P1084R2