diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -43,11 +43,23 @@ return false; for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) { + auto LHS = + C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()); + auto RHS = C.getCanonicalTemplateArgument( + (*Other.ParameterMapping)[I].getArgument()); + // QualType of constrained template template parameter is not the same as + // its tailing version, so give it a pass here. + if (LHS.getKind() == TemplateArgument::Type && + RHS.getKind() == TemplateArgument::Type && + isa(LHS.getAsType()) && + isa(RHS.getAsType())) { + if (cast(LHS.getAsType())->getIndex() == + cast(RHS.getAsType())->getIndex()) + continue; + } llvm::FoldingSetNodeID IDA, IDB; - C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) - .Profile(IDA, C); - C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument()) - .Profile(IDB, C); + LHS.Profile(IDA, C); + RHS.Profile(IDB, C); if (IDA != IDB) return false; } diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -874,27 +874,39 @@ /// template parameters. /// /// type-parameter: [C++ temp.param] -/// 'template' '<' template-parameter-list '>' type-parameter-key -/// ...[opt] identifier[opt] -/// 'template' '<' template-parameter-list '>' type-parameter-key -/// identifier[opt] = id-expression +/// template-head type-parameter-key ...[opt] identifier[opt] +/// template-head type-parameter-key identifier[opt] = id-expression /// type-parameter-key: /// 'class' /// 'typename' [C++1z] -NamedDecl * -Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { +/// template-head: [C++2a] +/// 'template' '<' template-parameter-list '>' +/// requires-clause[opt] +NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth, + unsigned Position) { assert(Tok.is(tok::kw_template) && "Expected 'template' keyword"); // Handle the template <...> part. SourceLocation TemplateLoc = ConsumeToken(); SmallVector TemplateParams; SourceLocation LAngleLoc, RAngleLoc; + ExprResult OptionalRequiresClauseConstraintER; { MultiParseScope TemplateParmScope(*this); if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams, LAngleLoc, RAngleLoc)) { return nullptr; } + if (TryConsumeToken(tok::kw_requires)) { + OptionalRequiresClauseConstraintER = + Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression( + /*IsTrailingRequiresClause=*/false)); + if (!OptionalRequiresClauseConstraintER.isUsable()) { + SkipUntil(tok::comma, tok::greater, tok::greatergreater, + StopAtSemi | StopBeforeMatch); + return nullptr; + } + } } // Provide an ExtWarn if the C++1z feature of using 'typename' here is used. @@ -956,11 +968,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 diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp @@ -1,22 +1,27 @@ // RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s -template concept C = T::f(); -// expected-note@-1{{similar constraint}} +template concept C = T::f(); // #C template concept D = C && T::g(); -template concept F = T::f(); -// expected-note@-1{{similar constraint expressions not considered equivalent}} -template class P> struct S1 { }; // expected-note 2{{'P' declared here}} +template concept F = T::f(); // #F +template class P> struct S1 { }; // #S1 template struct X { }; - -template struct Y { }; // expected-note{{'Y' declared here}} +template struct Y { }; // #Y template struct Z { }; -template struct W { }; // expected-note{{'W' declared here}} +template struct W { }; // #W S1 s11; -S1 s12; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}} +S1 s12; +// expected-error@-1 {{template template argument 'Y' is more constrained than template template parameter 'P'}} +// expected-note@#S1 {{'P' declared here}} +// expected-note@#Y {{'Y' declared here}} S1 s13; -S1 s14; // expected-error{{template template argument 'W' is more constrained than template template parameter 'P'}} +S1 s14; +// expected-error@-1 {{template template argument 'W' is more constrained than template template parameter 'P'}} +// expected-note@#S1 {{'P' declared here}} +// expected-note@#W {{'W' declared here}} +// expected-note@#F {{similar constraint expressions not considered equivalent}} +// expected-note@#C {{similar constraint}} template class P> struct S2 { }; @@ -32,3 +37,27 @@ using s31 = S3; using s32 = S3; + +template requires C class P> struct S4 { }; // #S4 + +S4 s41; +S4 s42; +// expected-error@-1 {{template template argument 'Y' is more constrained than template template parameter 'P'}} +// expected-note@#S4 {{'P' declared here}} +// expected-note@#Y {{'Y' declared here}} +S4 s43; +S4 s44; +// expected-error@-1 {{template template argument 'W' is more constrained than template template parameter 'P'}} +// expected-note@#S4 {{'P' declared here}} +// expected-note@#W {{'W' declared here}} +// expected-note@#F {{similar constraint expressions not considered equivalent}} +// expected-note@#C {{similar constraint}} + +template requires C typename U> struct S5 { + template static U V; +}; + +struct Nothing {}; + +// FIXME: Wait the standard to clarify the intent. +template<> template<> Z S5::V; diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -59,11 +59,10 @@ x.operator()(); // expected-error {{no matching member function}} } - // FIXME: This is valid under P0857R0. template concept C = true; - template requires C typename U> struct X {}; // expected-error {{requires 'class'}} expected-error 0+{{}} + template requires C typename U> struct X {}; template requires C struct Y {}; - X xy; // expected-error {{no template named 'X'}} + X xy; } namespace PR50306 { diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -912,11 +912,7 @@ P0857R0 - -
Partial - Constraining template template parameters is not yet supported. -
- + Clang 16 P1084R2