lime updated this revision to Diff 465750.
lime added a comment.

I updated patch to fix the previous problem that failed to pass unit tests. 
And, isn't this patch accepted a little quickly? BTW, `NormalizationCache` 
becomes heavier than before.


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/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/SemaConcept.cpp
===================================================================
--- clang/lib/Sema/SemaConcept.cpp
+++ clang/lib/Sema/SemaConcept.cpp
@@ -587,6 +587,42 @@
   return MLTAL.getNumSubstitutedLevels();
 }
 
+// FIXME: may be incomplete
+static unsigned CalculateTemplateDepthForConstraints(const Expr *Constr) {
+  unsigned Depth = 0;
+  if (isa<ConceptSpecializationExpr>(Constr)) {
+    for (auto &Arg :
+         cast<ConceptSpecializationExpr>(Constr)->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> {
   unsigned TemplateDepth = 0;
@@ -610,32 +646,61 @@
     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,14 +1048,26 @@
 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))
@@ -1276,7 +1353,16 @@
     return false;
   }
 
-  if (subsumes(*this, D1, AC1, D2, AC2, Result,
+  std::size_t I = 0;
+  SmallVector<const Expr *, 3> MAC1{AC1.size()}, MAC2{AC2.size()};
+  for (; I != AC1.size() && I != AC2.size(); ++I) {
+    std::tie(MAC1[I], MAC2[I]) = AdjustConstraintDepth::UnifyConstraintDepth(
+        *this, D1, AC1[I], D2, AC2[I]);
+  }
+  std::copy(AC1.begin() + I, AC1.end(), MAC1.begin() + I);
+  std::copy(AC2.begin() + I, AC2.end(), MAC2.begin() + I);
+
+  if (subsumes(*this, D1, MAC1, D2, MAC2, Result,
         [this] (const AtomicConstraint &A, const AtomicConstraint &B) {
           return A.subsumes(Context, B);
         }))
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,10 @@
       FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
       LocalInstantiationScope &Scope);
 
+  const NormalizedConstraint *
+  getNormalizedAssociatedConstraintsNonPool(
+      NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints);
+
 public:
   const NormalizedConstraint *
   getNormalizedAssociatedConstraints(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to