hubert.reinterpretcast created this revision.
hubert.reinterpretcast added reviewers: rsmith, faisalv, aaron.ballman.
hubert.reinterpretcast added subscribers: nwilson, cfe-commits.

This adds associated constraints as a property of class templates.
An error is produced if redeclarations are not similarly constrained.


https://reviews.llvm.org/D25674

Files:
  include/clang/AST/DeclTemplate.h
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/DeclTemplate.cpp
  lib/Sema/SemaTemplate.cpp
  test/CXX/concepts-ts/temp/
  test/CXX/concepts-ts/temp/temp.constr/
  test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/
  test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp

Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
+
+namespace nodiag {
+
+using MyBool = bool;
+
+template <typename T> requires !!sizeof(bool)
+struct A;
+template <typename T> requires !!sizeof(MyBool)
+struct A;
+
+} // end namespace nodiag
+
+namespace diag {
+
+template <typename T> requires true // expected-note{{previous template declaration is here}}
+struct A;
+template <typename T> struct A; // expected-error{{associated constraints differ in template redeclaration}}
+
+template <typename T> struct B; // expected-note{{previous template declaration is here}}
+template <typename T> requires true // expected-error{{associated constraints differ in template redeclaration}}
+struct B;
+
+template <typename T> requires true // expected-note{{previous template declaration is here}}
+struct C;
+template <typename T> requires !0 // expected-error{{associated constraints differ in template redeclaration}}
+struct C;
+
+} // end namespace diag
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -45,6 +45,26 @@
   return SourceRange(Ps[0]->getTemplateLoc(), Ps[N-1]->getRAngleLoc());
 }
 
+namespace clang {
+/// \brief [temp.constr.decl]p2: A template's associated constraints are
+/// defined as a single constraint-expression derived from the introduced
+/// constraint-expressions [ ... ].
+///
+/// \param Params The template parameter list and optional requires-clause.
+///
+/// \param FD The underlying templated function declaration for a function
+/// template.
+static Expr *formAssociatedConstraints(TemplateParameterList *Params,
+                                       FunctionDecl *FD);
+}
+
+static Expr *clang::formAssociatedConstraints(TemplateParameterList *Params,
+                                              FunctionDecl *FD) {
+  // FIXME: Concepts: collect additional introduced constraint-expressions
+  assert(!FD && "Cannot collect constraints from function declaration yet.");
+  return Params->getRequiresClause();
+}
+
 /// \brief Determine whether the declaration found is acceptable as the name
 /// of a template and, if so, return that template declaration. Otherwise,
 /// returns NULL.
@@ -1117,6 +1137,9 @@
     }
   }
 
+  // TODO Memory management; associated constraints are not always stored.
+  Expr *const CurAC = formAssociatedConstraints(TemplateParams, nullptr);
+
   if (PrevClassTemplate) {
     // Ensure that the template parameter lists are compatible. Skip this check
     // for a friend in a dependent context: the template parameter list itself
@@ -1128,6 +1151,26 @@
                                         TPL_TemplateMatch))
       return true;
 
+    // Check for matching associated constraints on redeclarations.
+    do {
+      const Expr *const PrevAC = PrevClassTemplate->getAssociatedConstraints();
+      if (!(CurAC || PrevAC))
+        continue; // nothing to check
+      if (CurAC && PrevAC) {
+        llvm::FoldingSetNodeID CurACInfo, PrevACInfo;
+        CurAC->Profile(CurACInfo, Context, /*Canonical=*/true);
+        PrevAC->Profile(PrevACInfo, Context, /*Canonical=*/true);
+        if (CurACInfo == PrevACInfo)
+          continue; // all good
+      }
+
+      Diag(CurAC ? CurAC->getLocStart() : NameLoc,
+           diag::err_template_different_associated_constraints);
+      Diag(PrevAC ? PrevAC->getLocStart() : PrevClassTemplate->getLocation(),
+           diag::note_template_prev_declaration) << /*declaration*/0;
+      return true;
+    } while (0);
+
     // C++ [temp.class]p4:
     //   In a redeclaration, partial specialization, explicit
     //   specialization or explicit instantiation of a class template,
@@ -1225,7 +1268,8 @@
   ClassTemplateDecl *NewTemplate
     = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
                                 DeclarationName(Name), TemplateParams,
-                                NewClass, PrevClassTemplate);
+                                NewClass, PrevClassTemplate,
+                                PrevClassTemplate ? nullptr : CurAC);
   NewClass->setDescribedClassTemplate(NewTemplate);
   
   if (ModulePrivateLoc.isValid())
Index: lib/AST/DeclTemplate.cpp
===================================================================
--- lib/AST/DeclTemplate.cpp
+++ lib/AST/DeclTemplate.cpp
@@ -334,11 +334,20 @@
                                              DeclarationName Name,
                                              TemplateParameterList *Params,
                                              NamedDecl *Decl,
-                                             ClassTemplateDecl *PrevDecl) {
+                                             ClassTemplateDecl *PrevDecl,
+                                             Expr *AssociatedConstraints) {
   AdoptTemplateParameterList(Params, cast<DeclContext>(Decl));
-  ClassTemplateDecl *New = new (C, DC) ClassTemplateDecl(C, DC, L, Name,
-                                                         Params, Decl);
+  assert(!(AssociatedConstraints && PrevDecl) &&
+         "Attaching associated constraints to later (not first) declaration");
+  ClassTemplateDecl *New =
+      new (C, DC, ClassTemplateDecl::additionalSizeToAlloc<Expr *>(
+                      AssociatedConstraints ? 1u : 0u))
+          ClassTemplateDecl(C, DC, L, Name, Params, Decl);
   New->setPreviousDecl(PrevDecl);
+  if (AssociatedConstraints) {
+    New->setStoresAssociatedConstraints();
+    New->storedAssociatedConstraintsField() = AssociatedConstraints;
+  }
   return New;
 }
 
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2178,6 +2178,9 @@
   "%select{function|variable}0 concept cannot be "
   "%select{explicitly instantiated|explicitly specialized|partially specialized}1">;
 
+def err_template_different_associated_constraints : Error<
+  "associated constraints differ in template redeclaration">;
+
 // C++11 char16_t/char32_t
 def warn_cxx98_compat_unicode_type : Warning<
   "'%0' type specifier is incompatible with C++98">,
Index: include/clang/AST/DeclTemplate.h
===================================================================
--- include/clang/AST/DeclTemplate.h
+++ include/clang/AST/DeclTemplate.h
@@ -355,30 +355,32 @@
   // This is probably never used.
   TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name)
       : NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr, false),
-        TemplateParams(nullptr) {}
+        TemplateParams(nullptr, false) {}
 
   // Construct a template decl with the given name and parameters.
   // Used when there is not templated element (tt-params).
   TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name,
                TemplateParameterList *Params)
       : NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr, false),
-        TemplateParams(Params) {}
+        TemplateParams(Params, false) {}
 
   // Construct a template decl with name, parameters, and templated element.
   TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name,
                TemplateParameterList *Params, NamedDecl *Decl)
       : NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl, false),
-        TemplateParams(Params) {}
+        TemplateParams(Params, false) {}
 
 public:
   /// Get the list of template parameters
   TemplateParameterList *getTemplateParameters() const {
-    return TemplateParams;
+    return TemplateParams.getPointer();
   }
 
   /// Get the constraint-expression from the associated requires-clause (if any)
   const Expr *getRequiresClause() const {
-    return TemplateParams ? TemplateParams->getRequiresClause() : nullptr;
+    return TemplateParams.getPointer()
+               ? TemplateParams.getPointer()->getRequiresClause()
+               : nullptr;
   }
 
   /// Get the underlying, templated declaration.
@@ -391,7 +393,7 @@
   }
 
   SourceRange getSourceRange() const override LLVM_READONLY {
-    return SourceRange(TemplateParams->getTemplateLoc(),
+    return SourceRange(TemplateParams.getPointer()->getTemplateLoc(),
                        TemplatedDecl.getPointer()->getSourceRange().getEnd());
   }
 
@@ -407,16 +409,24 @@
   /// (function or variable) is a concept.
   llvm::PointerIntPair<NamedDecl *, 1, bool> TemplatedDecl;
 
-  TemplateParameterList* TemplateParams;
+  /// \brief The template parameter list and optional requires-clause
+  /// associated with this declaration.
+  ///
+  /// The boolean value indicates whether this particular declaration has an
+  /// attached \c Expr representing the associated constraints of the template.
+  llvm::PointerIntPair<TemplateParameterList *, 1, bool> TemplateParams;
+
+  bool storesAssociatedConstraints() const { return TemplateParams.getInt(); }
+  void setStoresAssociatedConstraints() { TemplateParams.setInt(true); }
 
 public:
   /// \brief Initialize the underlying templated declaration and
   /// template parameters.
   void init(NamedDecl *templatedDecl, TemplateParameterList* templateParams) {
     assert(!TemplatedDecl.getPointer() && "TemplatedDecl already set!");
-    assert(!TemplateParams && "TemplateParams already set!");
+    assert(!TemplateParams.getPointer() && "TemplateParams already set!");
     TemplatedDecl.setPointer(templatedDecl);
-    TemplateParams = templateParams;
+    TemplateParams.setPointer(templateParams);
   }
 };
 
@@ -1959,7 +1969,10 @@
 };
 
 /// Declaration of a class template.
-class ClassTemplateDecl : public RedeclarableTemplateDecl {
+class ClassTemplateDecl final
+    : public RedeclarableTemplateDecl,
+      private llvm::TrailingObjects<ClassTemplateDecl, Expr *> {
+  friend TrailingObjects;
   static void DeallocateCommon(void *Ptr);
 
 protected:
@@ -2008,6 +2021,16 @@
     return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
   }
 
+  Expr *&storedAssociatedConstraintsField() {
+    assert(storesAssociatedConstraints() && "Accessing non-existent field?");
+    return *getTrailingObjects<Expr *>();
+  }
+
+  Expr *storedAssociatedConstraintsField() const {
+    assert(storesAssociatedConstraints() && "Accessing non-existent field?");
+    return *getTrailingObjects<Expr *>();
+  }
+
 public:
   /// \brief Load any lazily-loaded specializations from the external source.
   void LoadLazySpecializations() const;
@@ -2023,13 +2046,23 @@
     return getTemplatedDecl()->isThisDeclarationADefinition();
   }
 
+  /// \brief Returns the associated constraints for this template (if any).
+  Expr *getAssociatedConstraints() const {
+    const ClassTemplateDecl *const C = ClassTemplateDecl::getCanonicalDecl();
+    return C->storesAssociatedConstraints()
+               ? C->storedAssociatedConstraintsField()
+               : nullptr;
+  }
+
+  // FIXME: remove default argument for AssociatedConstraints
   /// \brief Create a class template node.
   static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC,
                                    SourceLocation L,
                                    DeclarationName Name,
                                    TemplateParameterList *Params,
                                    NamedDecl *Decl,
-                                   ClassTemplateDecl *PrevDecl);
+                                   ClassTemplateDecl *PrevDecl,
+                                   Expr *AssociatedConstraints = nullptr);
 
   /// \brief Create an empty class template node.
   static ClassTemplateDecl *CreateDeserialized(ASTContext &C, unsigned ID);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to