Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -344,6 +344,32 @@ // Kinds of Templates //===----------------------------------------------------------------------===// +/// \brief Stores the template parameter list and associated constraints for +/// \c TemplateDecl objects that track associated constraints. +class ConstrainedTemplateDeclInfo { + friend TemplateDecl; + +public: + ConstrainedTemplateDeclInfo() : TemplateParams(), AssociatedConstraints() {} + + TemplateParameterList *getTemplateParameters() const { + return TemplateParams; + } + + Expr *getAssociatedConstraints() const { return AssociatedConstraints; } + +protected: + void setTemplateParameters(TemplateParameterList *TParams) { + TemplateParams = TParams; + } + + void setAssociatedConstraints(Expr *AC) { AssociatedConstraints = AC; } + + TemplateParameterList *TemplateParams; + Expr *AssociatedConstraints; +}; + + /// \brief The base class of all kinds of template declarations (e.g., /// class, function, etc.). /// @@ -352,33 +378,53 @@ class TemplateDecl : public NamedDecl { void anchor() override; protected: - // This is probably never used. - TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name) + // Construct a template decl with the given name and parameters. + // Used when there is no templated element (e.g., for tt-params). + TemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, DeclContext *DC, + SourceLocation L, DeclarationName Name, + TemplateParameterList *Params) : NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr, false), - TemplateParams(nullptr) {} + TemplateParams(CTDI) { + this->setTemplateParameters(Params); + } - // 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) {} + : TemplateDecl(nullptr, DK, DC, L, Name, Params) {} // Construct a template decl with name, parameters, and templated element. - TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, + TemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, DeclContext *DC, + SourceLocation L, DeclarationName Name, TemplateParameterList *Params, NamedDecl *Decl) : NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl, false), - TemplateParams(Params) {} + TemplateParams(CTDI) { + this->setTemplateParameters(Params); + } + + TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, NamedDecl *Decl) + : TemplateDecl(nullptr, DK, DC, L, Name, Params, Decl) {} public: /// Get the list of template parameters TemplateParameterList *getTemplateParameters() const { - return TemplateParams; + const auto *const CTDI = + TemplateParams.dyn_cast(); + return CTDI ? CTDI->getTemplateParameters() + : TemplateParams.get(); } /// Get the constraint-expression from the associated requires-clause (if any) const Expr *getRequiresClause() const { - return TemplateParams ? TemplateParams->getRequiresClause() : nullptr; + const TemplateParameterList *const TP = getTemplateParameters(); + return TP ? TP->getRequiresClause() : nullptr; + } + + Expr *getAssociatedConstraints() const { + const TemplateDecl *const C = cast(getCanonicalDecl()); + const auto *const CTDI = + C->TemplateParams.dyn_cast(); + return CTDI ? CTDI->getAssociatedConstraints() : nullptr; } /// Get the underlying, templated declaration. @@ -391,7 +437,7 @@ } SourceRange getSourceRange() const override LLVM_READONLY { - return SourceRange(TemplateParams->getTemplateLoc(), + return SourceRange(getTemplateParameters()->getTemplateLoc(), TemplatedDecl.getPointer()->getSourceRange().getEnd()); } @@ -407,7 +453,29 @@ /// (function or variable) is a concept. llvm::PointerIntPair TemplatedDecl; - TemplateParameterList* TemplateParams; + /// \brief The template parameter list and optional requires-clause + /// associated with this declaration; alternatively, a + /// \c ConstrainedTemplateDeclInfo if the associated constraints of the + /// template are being tracked by this particular declaration. + llvm::PointerUnion + TemplateParams; + + void setTemplateParameters(TemplateParameterList *TParams) { + if (auto *const CTDI = + TemplateParams.dyn_cast()) { + CTDI->setTemplateParameters(TParams); + } else { + TemplateParams = TParams; + } + } + + void setAssociatedConstraints(Expr *AC) { + assert(isCanonicalDecl() && + "Attaching associated constraints to non-canonical Decl"); + TemplateParams.get() + ->setAssociatedConstraints(AC); + } public: /// \brief Initialize the underlying templated declaration and @@ -737,11 +805,17 @@ virtual CommonBase *newCommon(ASTContext &C) const = 0; // Construct a template decl with name, parameters, and templated element. + RedeclarableTemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, + ASTContext &C, DeclContext *DC, SourceLocation L, + DeclarationName Name, TemplateParameterList *Params, + NamedDecl *Decl) + : TemplateDecl(CTDI, DK, DC, L, Name, Params, Decl), redeclarable_base(C), + Common() {} + RedeclarableTemplateDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, TemplateParameterList *Params, NamedDecl *Decl) - : TemplateDecl(DK, DC, L, Name, Params, Decl), redeclarable_base(C), - Common() {} + : RedeclarableTemplateDecl(nullptr, DK, C, DC, L, Name, Params, Decl) {} public: template friend class RedeclarableTemplate; @@ -1997,10 +2071,16 @@ llvm::FoldingSetVector & getPartialSpecializations(); + ClassTemplateDecl(ConstrainedTemplateDeclInfo *CTDI, ASTContext &C, + DeclContext *DC, SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, NamedDecl *Decl) + : RedeclarableTemplateDecl(CTDI, ClassTemplate, C, DC, L, Name, Params, + Decl) {} + ClassTemplateDecl(ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, TemplateParameterList *Params, NamedDecl *Decl) - : RedeclarableTemplateDecl(ClassTemplate, C, DC, L, Name, Params, Decl) {} + : ClassTemplateDecl(nullptr, C, DC, L, Name, Params, Decl) {} CommonBase *newCommon(ASTContext &C) const override; @@ -2023,12 +2103,14 @@ return getTemplatedDecl()->isThisDeclarationADefinition(); } + // 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); + NamedDecl *Decl, + 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 @@ -2267,6 +2267,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 @@ -297,10 +297,18 @@ SourceLocation L, DeclarationName Name, TemplateParameterList *Params, - NamedDecl *Decl) { + NamedDecl *Decl, + Expr *AssociatedConstraints) { AdoptTemplateParameterList(Params, cast(Decl)); - ClassTemplateDecl *New = new (C, DC) ClassTemplateDecl(C, DC, L, Name, - Params, Decl); + + if (!AssociatedConstraints) { + return new (C, DC) ClassTemplateDecl(C, DC, L, Name, Params, Decl); + } + + ConstrainedTemplateDeclInfo *const CTDI = new (C) ConstrainedTemplateDeclInfo; + ClassTemplateDecl *const New = + new (C, DC) ClassTemplateDecl(CTDI, C, DC, L, Name, Params, Decl); + New->setAssociatedConstraints(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. @@ -1137,6 +1157,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 @@ -1148,6 +1171,29 @@ TPL_TemplateMatch)) return true; + // Check for matching associated constraints on redeclarations. + const Expr *const PrevAC = PrevClassTemplate->getAssociatedConstraints(); + const bool RedeclACMismatch = [&] { + if (!(CurAC || PrevAC)) + return false; // Nothing to check; no mismatch. + if (CurAC && PrevAC) { + llvm::FoldingSetNodeID CurACInfo, PrevACInfo; + CurAC->Profile(CurACInfo, Context, /*Canonical=*/true); + PrevAC->Profile(PrevACInfo, Context, /*Canonical=*/true); + if (CurACInfo == PrevACInfo) + return false; // All good; no mismatch. + } + return true; + }(); + + if (RedeclACMismatch) { + 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; + } + // C++ [temp.class]p4: // In a redeclaration, partial specialization, explicit // specialization or explicit instantiation of a class template, @@ -1250,10 +1296,15 @@ AddMsStructLayoutForRecord(NewClass); } + // Attach the associated constraints when the declaration will not be part of + // a decl chain + Expr *const ACtoAttach = + PrevClassTemplate && ShouldAddRedecl ? nullptr : CurAC; + ClassTemplateDecl *NewTemplate = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc, DeclarationName(Name), TemplateParams, - NewClass); + NewClass, ACtoAttach); if (ShouldAddRedecl) NewTemplate->setPreviousDecl(PrevClassTemplate); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1878,6 +1878,7 @@ DeclID PatternID = ReadDeclID(); NamedDecl *TemplatedDecl = cast_or_null(Reader.GetDecl(PatternID)); TemplateParameterList *TemplateParams = Record.readTemplateParameterList(); + // FIXME handle associated constraints D->init(TemplatedDecl, TemplateParams); return PatternID; 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,65 @@ +// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s + +namespace nodiag { + +template requires bool(T()) +struct A; +template requires bool(U()) +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 + +namespace nodiag { + +struct AA { + template requires someFunc(T()) + struct A; +}; + +template requires someFunc(T()) +struct AA::A { }; + +struct AAF { + template requires someFunc(T()) + friend struct AA::A; +}; + +} // end namespace nodiag + +namespace diag { + +template +struct TA { + template