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 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 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 { + friend TrailingObjects; static void DeallocateCommon(void *Ptr); protected: @@ -2008,6 +2021,16 @@ return static_cast(RedeclarableTemplateDecl::getCommonPtr()); } + Expr *&storedAssociatedConstraintsField() { + assert(storesAssociatedConstraints() && "Accessing non-existent field?"); + return *getTrailingObjects(); + } + + Expr *storedAssociatedConstraintsField() const { + assert(storesAssociatedConstraints() && "Accessing non-existent field?"); + return *getTrailingObjects(); + } + 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); 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: 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(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( + AssociatedConstraints ? 1u : 0u)) + ClassTemplateDecl(C, DC, L, Name, Params, Decl); New->setPreviousDecl(PrevDecl); + if (AssociatedConstraints) { + New->setStoresAssociatedConstraints(); + New->storedAssociatedConstraintsField() = AssociatedConstraints; + } return New; } 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: 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 requires !!sizeof(bool) +struct A; +template requires !!sizeof(MyBool) +struct A; + +} // end namespace nodiag + +namespace diag { + +template requires true // expected-note{{previous template declaration is here}} +struct A; +template struct A; // expected-error{{associated constraints differ in template redeclaration}} + +template struct B; // expected-note{{previous template declaration is here}} +template requires true // expected-error{{associated constraints differ in template redeclaration}} +struct B; + +template requires true // expected-note{{previous template declaration is here}} +struct C; +template requires !0 // expected-error{{associated constraints differ in template redeclaration}} +struct C; + +} // end namespace diag