lime updated this revision to Diff 467039. lime added a comment. I updated the patch as I said, while kept the key of `NormalizationCache` relatively heavy. That means I:
- changed the parameter of `IsAtLeastAsConstrained` to `MutableArrayRef`, - and let the new `CalculateTemplateDepthForConstraints` handle `BinaryOperator` and `ParenExpr`. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D134128/new/ https://reviews.llvm.org/D134128 Files: clang/include/clang/Sema/Sema.h clang/lib/Parse/ParseTemplate.cpp clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaOverload.cpp clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp clang/test/SemaTemplate/concepts.cpp clang/www/cxx_status.html
Index: clang/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -912,11 +912,7 @@ </tr> <tr> <!-- from Albuquerque --> <td><a href="https://wg21.link/p0857r0">P0857R0</a></td> - <td class="partial" align="center"> - <details><summary>Partial</summary> - Constraining template template parameters is not yet supported. - </details> - </td> + <td class="unreleased" align="center">Clang 16</td> </tr> <tr> <!-- from San Diego --> <td><a href="https://wg21.link/p1084r2">P1084R2</a></td> Index: clang/test/SemaTemplate/concepts.cpp =================================================================== --- clang/test/SemaTemplate/concepts.cpp +++ clang/test/SemaTemplate/concepts.cpp @@ -59,11 +59,10 @@ x.operator()<false>(); // expected-error {{no matching member function}} } - // FIXME: This is valid under P0857R0. template<typename T> concept C = true; - template<template<typename T> requires C<T> typename U> struct X {}; // expected-error {{requires 'class'}} expected-error 0+{{}} + template<template<typename T> requires C<T> typename U> struct X {}; template<typename T> requires C<T> struct Y {}; - X<Y> xy; // expected-error {{no template named 'X'}} + X<Y> xy; } namespace PR50306 { 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 @@ -1,22 +1,27 @@ // RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s -template<typename T> concept C = T::f(); -// expected-note@-1{{similar constraint}} +template<typename T> concept C = T::f(); // #C template<typename T> concept D = C<T> && T::g(); -template<typename T> concept F = T::f(); -// expected-note@-1{{similar constraint expressions not considered equivalent}} -template<template<C> class P> struct S1 { }; // expected-note 2{{'P' declared here}} +template<typename T> concept F = T::f(); // #F +template<template<C> class P> struct S1 { }; // #S1 template<C> struct X { }; - -template<D> struct Y { }; // expected-note{{'Y' declared here}} +template<D> struct Y { }; // #Y template<typename T> struct Z { }; -template<F> struct W { }; // expected-note{{'W' declared here}} +template<F> struct W { }; // #W S1<X> s11; -S1<Y> s12; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}} +S1<Y> 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<Z> s13; -S1<W> s14; // expected-error{{template template argument 'W' is more constrained than template template parameter 'P'}} +S1<W> 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 1-2{{similar constraint expressions not considered equivalent}} +// expected-note@#C 1-2{{similar constraint}} template<template<typename> class P> struct S2 { }; @@ -32,3 +37,25 @@ using s31 = S3<N>; using s32 = S3<Z>; + +template<template<typename T> requires C<T> class P> struct S4 { }; // #S4 + +S4<X> s41; +S4<Y> 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<Z> s43; +S4<W> 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}} + +template<template<typename T> requires C<T> typename U> struct S5 { + template<typename T> static U<T> V; +}; + +struct Nothing {}; + +// FIXME: Wait the standard to clarify the intent. +template<> template<> Z<Nothing> S5<Z>::V<Nothing>; Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -6429,7 +6429,7 @@ NamedDecl *ND = Function; if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) ND = SpecInfo->getTemplate(); - + if (ND->getFormalLinkage() == Linkage::InternalLinkage) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_module_mismatched; @@ -9700,7 +9700,7 @@ const OverloadCandidate &Cand1, const OverloadCandidate &Cand2) { // FIXME: Per P2113R0 we also need to compare the template parameter lists - // when comparing template functions. + // when comparing template functions. if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() && Cand2.Function->hasPrototype()) { auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType()); @@ -9954,13 +9954,13 @@ // parameter-type-lists, and F1 is more constrained than F2 [...], if (!Cand1IsSpecialization && !Cand2IsSpecialization && canCompareFunctionConstraints(S, Cand1, Cand2)) { - Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); - Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); + const Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); + const Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); if (RC1 && RC2) { bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function, {RC2}, + if (S.IsAtLeastAsConstrained(Cand1.Function, RC1, Cand2.Function, RC2, AtLeastAsConstrained1) || - S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function, {RC1}, + S.IsAtLeastAsConstrained(Cand2.Function, RC2, Cand1.Function, RC1, AtLeastAsConstrained2)) return false; if (AtLeastAsConstrained1 != AtLeastAsConstrained2) Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -18138,8 +18138,8 @@ AnotherMethodIsMoreConstrained = true; break; } - if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, Method, - {Constraints}, + if (S.IsAtLeastAsConstrained(OtherMethod, OtherConstraints, Method, + Constraints, AnotherMethodIsMoreConstrained)) { // There was an error with the constraints comparison. Exit the loop // and don't consider this function eligible. Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -587,10 +587,53 @@ return MLTAL.getNumSubstitutedLevels(); } +// FIXME: may be incomplete +static unsigned CalculateTemplateDepthForConstraints(const Expr *Constr) { + unsigned Depth = 0; + while (auto BOP = dyn_cast_or_null<BinaryOperator>(Constr->IgnoreParens())) { + auto LD = CalculateTemplateDepthForConstraints(BOP->getLHS()); + if (Depth < LD) + Depth = LD; + Constr = BOP->getRHS(); + } + if (auto CSE = + dyn_cast_or_null<ConceptSpecializationExpr>(Constr->IgnoreParens())) { + for (auto &Arg : CSE->getTemplateArguments()) { + unsigned ArgDepth = 0; + switch (Arg.getKind()) { + case TemplateArgument::Type: + if (isa<TemplateTypeParmType>(Arg.getAsType())) { + ArgDepth = cast<TemplateTypeParmType>(Arg.getAsType())->getDepth(); + } + break; + case TemplateArgument::Declaration: + if (isa<NonTypeTemplateParmDecl>(Arg.getAsDecl())) { + ArgDepth = cast<NonTypeTemplateParmDecl>(Arg.getAsDecl())->getDepth(); + } + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: { + auto TD = Arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl(); + if (isa<TemplateTemplateParmDecl>(TD)) { + ArgDepth = cast<TemplateTemplateParmDecl>(TD)->getDepth(); + } + break; + } + default: + continue; + } + if (Depth < ArgDepth) + Depth = ArgDepth; + } + } + return Depth; +} + namespace { - class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> { +class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> { unsigned TemplateDepth = 0; - public: + +public: using inherited = TreeTransform<AdjustConstraintDepth>; AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth) : inherited(SemaRef), TemplateDepth(TemplateDepth) {} @@ -610,32 +653,58 @@ NewTL.setNameLoc(TL.getNameLoc()); return Result; } - }; -} // namespace - -bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, - const Expr *OldConstr, - const NamedDecl *New, - const Expr *NewConstr) { - if (Old && New && Old != New) { - unsigned Depth1 = CalculateTemplateDepthForConstraints( - *this, Old); - unsigned Depth2 = CalculateTemplateDepthForConstraints( - *this, New); +private: + static auto UnifyConstraintDepth(Sema &S, unsigned Depth1, + const Expr *OldConstr, unsigned Depth2, + const Expr *NewConstr) { // Adjust the 'shallowest' verison of this to increase the depth to match // the 'other'. if (Depth2 > Depth1) { - OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1) + OldConstr = AdjustConstraintDepth(S, Depth2 - Depth1) .TransformExpr(const_cast<Expr *>(OldConstr)) .get(); } else if (Depth1 > Depth2) { - NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2) + NewConstr = AdjustConstraintDepth(S, Depth1 - Depth2) .TransformExpr(const_cast<Expr *>(NewConstr)) .get(); } + return std::make_pair(OldConstr, NewConstr); + } + +public: + static auto UnifyConstraintDepthFromDecl(Sema &S, const NamedDecl *Old, + const Expr *OldConstr, + const NamedDecl *New, + const Expr *NewConstr) { + if (Old && New && Old != New) { + unsigned Depth1 = CalculateTemplateDepthForConstraints(S, Old); + unsigned Depth2 = CalculateTemplateDepthForConstraints(S, New); + return UnifyConstraintDepth(S, Depth1, OldConstr, Depth2, NewConstr); + } + return std::make_pair(OldConstr, NewConstr); } + static auto UnifyConstraintDepth(Sema &S, const NamedDecl *Old, + const Expr *OldConstr, const NamedDecl *New, + const Expr *NewConstr) { + if (Old && New && Old != New) { + unsigned Depth1 = CalculateTemplateDepthForConstraints(OldConstr); + unsigned Depth2 = CalculateTemplateDepthForConstraints(NewConstr); + return UnifyConstraintDepth(S, Depth1, OldConstr, Depth2, NewConstr); + } + return std::make_pair(OldConstr, NewConstr); + } +}; +} // namespace + +bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, + const Expr *OldConstr, + const NamedDecl *New, + const Expr *NewConstr) { + std::tie(OldConstr, NewConstr) = + AdjustConstraintDepth::UnifyConstraintDepthFromDecl(*this, Old, OldConstr, + New, NewConstr); llvm::FoldingSetNodeID ID1, ID2; OldConstr->Profile(ID1, Context, /*Canonical=*/true); NewConstr->Profile(ID2, Context, /*Canonical=*/true); @@ -983,17 +1052,28 @@ const NormalizedConstraint * Sema::getNormalizedAssociatedConstraints( NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints) { - auto CacheEntry = NormalizationCache.find(ConstrainedDecl); + std::size_t Beg = NormalizationExprPool.size(); + NormalizationExprPool.append(AssociatedConstraints.begin(), + AssociatedConstraints.end()); + return getNormalizedAssociatedConstraintsNonPool( + ConstrainedDecl, llvm::makeArrayRef(NormalizationExprPool.begin() + Beg, + NormalizationExprPool.end())); +} + +const NormalizedConstraint *Sema::getNormalizedAssociatedConstraintsNonPool( + NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints) { + auto Key = std::make_pair(ConstrainedDecl, AssociatedConstraints); + auto CacheEntry = NormalizationCache.find(Key); if (CacheEntry == NormalizationCache.end()) { auto Normalized = NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl, AssociatedConstraints); CacheEntry = NormalizationCache - .try_emplace(ConstrainedDecl, + .try_emplace(std::move(Key), Normalized - ? new (Context) NormalizedConstraint( - std::move(*Normalized)) + ? new (Context) + NormalizedConstraint(std::move(*Normalized)) : nullptr) .first; } @@ -1256,8 +1336,10 @@ return false; } -bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1, - NamedDecl *D2, ArrayRef<const Expr *> AC2, +bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, + MutableArrayRef<const Expr *> AC1, + NamedDecl *D2, + MutableArrayRef<const Expr *> AC2, bool &Result) { if (AC1.empty()) { Result = AC2.empty(); @@ -1276,10 +1358,16 @@ return false; } + std::size_t I = 0; + for (; I != AC1.size() && I != AC2.size(); ++I) { + std::tie(AC1[I], AC2[I]) = AdjustConstraintDepth::UnifyConstraintDepth( + *this, D1, AC1[I], D2, AC2[I]); + } + if (subsumes(*this, D1, AC1, D2, AC2, Result, - [this] (const AtomicConstraint &A, const AtomicConstraint &B) { - return A.subsumes(Context, B); - })) + [this](const AtomicConstraint &A, const AtomicConstraint &B) { + return A.subsumes(Context, B); + })) return true; SubsumptionCache.try_emplace(Key, Result); return false; Index: clang/lib/Parse/ParseTemplate.cpp =================================================================== --- clang/lib/Parse/ParseTemplate.cpp +++ 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<NamedDecl*,8> 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 Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -7170,8 +7170,11 @@ /// constrained declarations). If an error occurred while normalizing the /// associated constraints of the template or concept, nullptr will be cached /// here. - llvm::DenseMap<NamedDecl *, NormalizedConstraint *> + llvm::DenseMap<std::pair<NamedDecl *, ArrayRef<const Expr *>>, + NormalizedConstraint *> NormalizationCache; + /// Provides space for ArrayRefs in NormalizationCache. + llvm::SmallVector<const Expr *, 16> NormalizationExprPool; llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &> SatisfactionCache; @@ -7199,6 +7202,9 @@ FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs, LocalInstantiationScope &Scope); + const NormalizedConstraint *getNormalizedAssociatedConstraintsNonPool( + NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints); + public: const NormalizedConstraint * getNormalizedAssociatedConstraints( @@ -7212,8 +7218,8 @@ /// at least constrained than D2, and false otherwise. /// /// \returns true if an error occurred, false otherwise. - bool IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1, - NamedDecl *D2, ArrayRef<const Expr *> AC2, + bool IsAtLeastAsConstrained(NamedDecl *D1, MutableArrayRef<const Expr *> AC1, + NamedDecl *D2, MutableArrayRef<const Expr *> AC2, bool &Result); /// If D1 was not at least as constrained as D2, but would've been if a pair
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits