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

Reply via email to