Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -762,9 +762,12 @@ return SpecIterator(isEnd ? Specs.end() : Specs.begin()); } - template typename SpecEntryTraits::DeclType* + void loadLazySpecializationsImpl() const; + + template + typename SpecEntryTraits::DeclType* findSpecializationImpl(llvm::FoldingSetVector &Specs, - ArrayRef Args, void *&InsertPos); + void *&InsertPos, ProfileArguments... ProfileArgs); template void addSpecializationImpl(llvm::FoldingSetVector &Specs, @@ -1934,7 +1937,7 @@ /// including constraint-expressions derived from the requires-clause, /// trailing requires-clause (for functions and methods) and constrained /// template parameters. - Expr *getAssociatedConstraints(); + Expr *getAssociatedConstraints() const; /// Get the template arguments as written. const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { @@ -2018,7 +2021,22 @@ ->getInjectedSpecializationType(); } - // FIXME: Add Profile support! + void Profile(llvm::FoldingSetNodeID &ID) const { + Profile(ID, getTemplateArgs().asArray(), getAssociatedConstraints(), + getASTContext()); + } + + static void + Profile(llvm::FoldingSetNodeID &ID, ArrayRef TemplateArgs, + Expr *AssociatedConstraints, ASTContext &Context) { + ID.AddInteger(TemplateArgs.size()); + for (const TemplateArgument &TemplateArg : TemplateArgs) + TemplateArg.Profile(ID, Context); + ID.AddBoolean(AssociatedConstraints != nullptr); + if (AssociatedConstraints) { + AssociatedConstraints->Profile(ID, Context, /*Canonical=*/true); + } + } static bool classof(const Decl *D) { return classofKind(D->getKind()); } @@ -2150,7 +2168,8 @@ /// \brief Return the partial specialization with the provided arguments if it /// exists, otherwise return the insertion point. ClassTemplatePartialSpecializationDecl * - findPartialSpecialization(ArrayRef Args, void *&InsertPos); + findPartialSpecialization(ArrayRef Args, + Expr *AssociatedConstraints, void *&InsertPos); /// \brief Insert the specified partial specialization knowing that it is not /// already in. InsertPos must be obtained from findPartialSpecialization. @@ -2785,7 +2804,7 @@ /// including constraint-expressions derived from the requires-clause, /// trailing requires-clause (for functions and methods) and constrained /// template parameters. - Expr *getAssociatedConstraints(); + Expr *getAssociatedConstraints() const; /// \brief Retrieve the member variable template partial specialization from /// which this particular variable template partial specialization was @@ -2851,6 +2870,23 @@ return First->InstantiatedFromMember.setInt(true); } + void Profile(llvm::FoldingSetNodeID &ID) const { + Profile(ID, getTemplateArgs().asArray(), getAssociatedConstraints(), + getASTContext()); + } + + static void + Profile(llvm::FoldingSetNodeID &ID, ArrayRef TemplateArgs, + Expr *AssociatedConstraints, ASTContext &Context) { + ID.AddInteger(TemplateArgs.size()); + for (const TemplateArgument &TemplateArg : TemplateArgs) + TemplateArg.Profile(ID, Context); + ID.AddBoolean(AssociatedConstraints != nullptr); + if (AssociatedConstraints) { + AssociatedConstraints->Profile(ID, Context, /*Canonical=*/true); + } + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { @@ -2976,7 +3012,8 @@ /// \brief Return the partial specialization with the provided arguments if it /// exists, otherwise return the insertion point. VarTemplatePartialSpecializationDecl * - findPartialSpecialization(ArrayRef Args, void *&InsertPos); + findPartialSpecialization(ArrayRef Args, + Expr *AssociatedConstraints, void *&InsertPos); /// \brief Insert the specified partial specialization knowing that it is not /// already in. InsertPos must be obtained from findPartialSpecialization. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5580,6 +5580,11 @@ /// expression. A diagnostic is emmited if it is not, and false is returned. bool CheckConstraintExpression(Expr *CE); + /// \brief Caches pairs of template-like decls whose associated constraints + /// were checked for subsumption and whether or not the first's constraints + /// did in fact subsume the second's. + llvm::DenseMap, bool> SubsumptionCache; + /// \brief Check whether the given constraint expression is satisfied given /// template arguments. Returns false and updates IsSatisfied with the /// satisfaction verdict if successful, emits a diagnostic and returns true if @@ -5648,6 +5653,9 @@ StringRef Diagnostic); void DiagnoseUnsatisfiedIllFormedConstraint(PartialDiagnosticAt *Diagnostic); + void DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, + const TemplateParameterList *New); + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ArrayRef Strings); Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -241,15 +241,28 @@ return Common; } -template +void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const { + // Grab the most recent declaration to ensure we've loaded any lazy + // redeclarations of this template. + CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); + if (CommonBasePtr->LazySpecializations) { + ASTContext &Context = getASTContext(); + uint32_t *Specs = CommonBasePtr->LazySpecializations; + CommonBasePtr->LazySpecializations = nullptr; + for (uint32_t I = 0, N = *Specs++; I != N; ++I) + (void)Context.getExternalSource()->GetExternalDecl(Specs[I]); + } +} + +template typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * RedeclarableTemplateDecl::findSpecializationImpl( - llvm::FoldingSetVector &Specs, ArrayRef Args, - void *&InsertPos) { + llvm::FoldingSetVector &Specs, void *&InsertPos, + ProfileArguments... ProfileArgs) { using SETraits = SpecEntryTraits; llvm::FoldingSetNodeID ID; - EntryType::Profile(ID,Args, getASTContext()); + EntryType::Profile(ID, ProfileArgs..., getASTContext()); EntryType *Entry = Specs.FindNodeOrInsertPos(ID, InsertPos); return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr; } @@ -264,8 +277,8 @@ #ifndef NDEBUG void *CorrectInsertPos; assert(!findSpecializationImpl(Specializations, - SETraits::getTemplateArgs(Entry), - CorrectInsertPos) && + CorrectInsertPos, + SETraits::getTemplateArgs(Entry)) && InsertPos == CorrectInsertPos && "given incorrect InsertPos for specialization"); #endif @@ -333,7 +346,7 @@ FunctionDecl * FunctionTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void FunctionTemplateDecl::addSpecialization( @@ -416,7 +429,7 @@ ClassTemplateSpecializationDecl * ClassTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D, @@ -426,8 +439,10 @@ ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecialization(ArrayRef Args, + Expr *AssociatedConstraints, void *&InsertPos) { - return findSpecializationImpl(getPartialSpecializations(), Args, InsertPos); + return findSpecializationImpl(getPartialSpecializations(), InsertPos, + Args, AssociatedConstraints); } void ClassTemplateDecl::AddPartialSpecialization( @@ -920,9 +935,10 @@ return Result; } -Expr* ClassTemplatePartialSpecializationDecl::getAssociatedConstraints() { +Expr* ClassTemplatePartialSpecializationDecl::getAssociatedConstraints() const { return getOrCollectAssociatedConstraints(getASTContext(), - cast(getCanonicalDecl()) + const_cast( + cast(getCanonicalDecl())) ->TemplateParams); } @@ -1051,7 +1067,7 @@ VarTemplateSpecializationDecl * VarTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D, @@ -1061,8 +1077,10 @@ VarTemplatePartialSpecializationDecl * VarTemplateDecl::findPartialSpecialization(ArrayRef Args, + Expr *AssociatedConstraints, void *&InsertPos) { - return findSpecializationImpl(getPartialSpecializations(), Args, InsertPos); + return findSpecializationImpl(getPartialSpecializations(), InsertPos, Args, + AssociatedConstraints); } void VarTemplateDecl::AddPartialSpecialization( @@ -1211,9 +1229,10 @@ return new (C, ID) VarTemplatePartialSpecializationDecl(C); } -Expr* VarTemplatePartialSpecializationDecl::getAssociatedConstraints() { +Expr* VarTemplatePartialSpecializationDecl::getAssociatedConstraints() const { return getOrCollectAssociatedConstraints(getASTContext(), - cast(getCanonicalDecl()) + const_cast( + cast(getCanonicalDecl())) ->TemplateParams); } Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -1969,11 +1969,7 @@ if (OldParams && !CheckRedeclarationConstraintMatch(OldParams->getRequiresClause(), NewParams->getRequiresClause())) { - Diag(NewParams->getTemplateLoc(), - diag::err_template_different_associated_constraints); - Diag(OldParams->getTemplateLoc(), diag::note_template_prev_declaration) - << /*declaration*/0; - + DiagnoseRedeclarationConstraintMismatch(OldParams, NewParams); Invalid = true; } @@ -3548,7 +3544,11 @@ } if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(), - Converted)) { + Converted) + && (!Context.getLangOpts().ConceptsTS + // TODO: Concepts: change this to getAssociatedConstraints when we + // have them. + || TemplateParams->getRequiresClause() == nullptr)) { // C++ [temp.class.spec]p9b3: // // -- The argument list of the specialization shall not be identical @@ -3568,7 +3568,12 @@ if (IsPartialSpecialization) // FIXME: Template parameter list matters too - PrevDecl = VarTemplate->findPartialSpecialization(Converted, InsertPos); + PrevDecl = VarTemplate->findPartialSpecialization(Converted, + // TODO: Concepts - replace with + // AssociatedConstraints once we + // have them. + TemplateParams->getRequiresClause(), + InsertPos); else PrevDecl = VarTemplate->findSpecialization(Converted, InsertPos); @@ -6945,6 +6950,7 @@ bool Complain, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { + // TODO: Concepts: Check constrained-parameter constraints here. // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { @@ -7154,6 +7160,13 @@ return false; } + if (!CheckRedeclarationConstraintMatch(Old->getRequiresClause(), + New->getRequiresClause())) { + if (Complain) + DiagnoseRedeclarationConstraintMismatch(Old, New); + return false; + } + return true; } @@ -7699,7 +7712,12 @@ if (isPartialSpecialization) // FIXME: Template parameter list matters, too - PrevDecl = ClassTemplate->findPartialSpecialization(Converted, InsertPos); + PrevDecl = ClassTemplate->findPartialSpecialization(Converted, + // TODO: Concepts: Replace with + // AssociatedConstraints once we + // have them. + TemplateParams->getRequiresClause(), + InsertPos); else PrevDecl = ClassTemplate->findSpecialization(Converted, InsertPos); @@ -7723,7 +7741,11 @@ Converted); if (Context.hasSameType(CanonType, - ClassTemplate->getInjectedClassNameSpecialization())) { + ClassTemplate->getInjectedClassNameSpecialization()) + && (!Context.getLangOpts().ConceptsTS + // TODO: Concepts: change this to getAssociatedConstraints when we + // have them. + || TemplateParams->getRequiresClause() == nullptr)) { // C++ [temp.class.spec]p9b3: // // -- The argument list of the specialization shall not be identical Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -4610,6 +4610,192 @@ ArgTypes.push_back(ArgTy); } +static Expr *normalizeConstraintExpr(Sema &S, Expr *E) { + assert(E != nullptr); + + if (ParenExpr *P = dyn_cast(E)) + return normalizeConstraintExpr(S, P->getSubExpr()); + if (BinaryOperator *BO = dyn_cast(E)) { + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) + return new (S.Context) BinaryOperator( + normalizeConstraintExpr(S, BO->getLHS()), + normalizeConstraintExpr(S, BO->getRHS()), BO->getOpcode(), + BO->getType(), BO->getValueKind(), BO->getObjectKind(), + BO->getOperatorLoc(), BO->getFPFeatures()); + } else if (auto* CSE = dyn_cast(E)) { + if (CSE->getSubstitutedConstraintExpr() == nullptr) { + S.Diag(E->getLocStart(), + diag::err_could_not_normalize_ill_formed_constraint) << CSE; + if (auto *SubstDiag = CSE->getSubstitutionDiagnostic()) { + S.Diag(SubstDiag->first, + diag::note_could_not_normalize_ill_formed_constraint_reason) + << SubstDiag->second; + } + return nullptr; + } + return normalizeConstraintExpr(S, CSE->getSubstitutedConstraintExpr()); + } + + return E; +} + +using NormalForm = llvm::SmallVector, 4>; + +static NormalForm makeCNF(Expr *Normalized) { + if (BinaryOperator *BO = dyn_cast(Normalized)) { + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { + NormalForm LCNF = makeCNF(BO->getLHS()); + NormalForm RCNF = makeCNF(BO->getRHS()); + if (BO->getOpcode() == BO_LAnd) { + LCNF.reserve(LCNF.size() + RCNF.size()); + while (!RCNF.empty()) + LCNF.push_back(std::move(RCNF.pop_back_val())); + return LCNF; + } else { + NormalForm Res; + Res.reserve(LCNF.size() * RCNF.size()); + for (auto& LDisjunction : LCNF) { + for (auto& RDisjunction : RCNF) { + NormalForm::value_type Combined; + Combined.reserve(LDisjunction.size() + RDisjunction.size()); + std::copy(LDisjunction.begin(), LDisjunction.end(), + std::back_inserter(Combined)); + std::copy(RDisjunction.begin(), RDisjunction.end(), + std::back_inserter(Combined)); + Res.emplace_back(Combined); + } + } + return Res; + } + } + } + return{ { Normalized } }; +} + +static NormalForm makeDNF(Expr *Normalized) { + if (BinaryOperator *BO = dyn_cast(Normalized)) { + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { + NormalForm LDNF = makeDNF(BO->getLHS()); + NormalForm RDNF = makeDNF(BO->getRHS()); + if (BO->getOpcode() == BO_LOr) { + LDNF.reserve(LDNF.size() + RDNF.size()); + while (!RDNF.empty()) + LDNF.push_back(std::move(RDNF.pop_back_val())); + return LDNF; + } else { + NormalForm Res; + Res.reserve(LDNF.size() * RDNF.size()); + for (auto& LConjunction : LDNF) { + for (auto& RConjunction : RDNF) { + NormalForm::value_type Combined; + Combined.reserve(LConjunction.size() + RConjunction.size()); + std::copy(LConjunction.begin(), LConjunction.end(), + std::back_inserter(Combined)); + std::copy(RConjunction.begin(), RConjunction.end(), + std::back_inserter(Combined)); + Res.emplace_back(Combined); + } + } + return Res; + } + } + } + return{ { Normalized } }; +} + +static bool atomicConstraintSubsumes(ASTContext &C, const Expr *A, + const Expr *B) { + // C++ [temp.constr.order] p2 + // - an atomic constraint A subsumes another atomic constraint B + // if and only if the A and B are identical [...] + // + // C++ [temp.constr.atomic] p2 + // Two atomic constraints are identical if they are formed from the + // same expression and the targets of the parameter mappings are + // equivalent according to the rules for expressions [...] + + if (A == B) { + return true; + } + + // We use the location as a means to identify that the two expressions + // originate (i.e. were instantiated from or are) the same source level + // construct. + if (A->getLocStart() != B->getLocStart()) { + return false; + } + + // We already have substituted parameters in the two expressions - simply see + // if they are identical. + + llvm::FoldingSetNodeID AID; + A->Profile(AID, C, /*Canonical=*/true); + + llvm::FoldingSetNodeID BID; + B->Profile(BID, C, /*Canonical=*/true); + + return AID == BID; +} + +static bool subsumes(Sema &S, Expr *P, Expr *Q) { + // C++ [temp.constr.order] p2 + // In order to determine if a constraint P subsumes a constraint Q, P is + // transformed into disjunctive normal form, and Q is transformed into + // conjunctive normal form. [...] + const NormalForm PDNF = makeDNF(normalizeConstraintExpr(S, P)); + const NormalForm QCNF = makeCNF(normalizeConstraintExpr(S, Q)); + + // C++ [temp.constr.order] p2 + // Then, P subsumes Q if and only if, for every disjunctive clause Pi in the + // disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in + // the conjuctive normal form of Q, where [...] + for (const auto& Pi : PDNF) { + for (const auto& Qj : QCNF) { + // C++ [temp.constr.order] p2 + // - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if + // and only if there exists an atomic constraint Pia in Pi for which + // there exists an atomic constraint, Qjb, in Qj such that Pia + // subsumes Qjb. + bool Found = false; + for (const Expr *Pia : Pi) { + for (const Expr *Qjb : Qj) { + if (atomicConstraintSubsumes(S.Context, Pia, Qjb)) { + Found = true; + break; + } + } + if (Found) + break; + } + if (!Found) { + return false; + } + } + } + return true; +} + +template +static bool isMoreConstrained(Sema& S, TemplateLikeDecl1 *TD1, + TemplateLikeDecl2 *TD2) { + Expr *AC1 = TD1->getAssociatedConstraints(); + Expr *AC2 = TD2->getAssociatedConstraints(); + if (!AC1) + return AC2 == nullptr; + if (!AC2) + // TD1 has associated constraints and TD2 does not. + return true; + + std::pair Key{TD1, TD2}; + auto CacheEntry = S.SubsumptionCache.find(Key); + if (CacheEntry != S.SubsumptionCache.end()) { + return CacheEntry->second; + } + bool Subsumes = subsumes(S, AC1, AC2); + S.SubsumptionCache.try_emplace(Key, Subsumes); + return Subsumes; +} + /// \brief Determine whether the function template \p FT1 is at least as /// specialized as \p FT2. static bool isAtLeastAsSpecializedAs(Sema &S, @@ -4761,6 +4947,65 @@ return true; } +/// Determine whether one partial specialization, P1, is at least as +/// specialized than another, P2, based on deduction checking. +/// +/// \tparam TemplateLikeDecl The kind of P2, which must be a TemplateDecl or +/// {Class,Var}TemplatePartialSpecializationDecl. +/// \param T1 The injected-class-name of P1 (faked for a variable template). +/// \param T2 The injected-class-name of P2 (faked for a variable template). +template +static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, + TemplateLikeDecl *P2, + TemplateDeductionInfo &Info) { + // C++ [temp.class.order]p1: + // For two class template partial specializations, the first is at least as + // specialized as the second if, given the following rewrite to two + // function templates, the first function template is at least as + // specialized as the second according to the ordering rules for function + // templates (14.6.6.2): + // - the first function template has the same template parameters as the + // first partial specialization and has a single function parameter + // whose type is a class template specialization with the template + // arguments of the first partial specialization, and + // - the second function template has the same template parameters as the + // second partial specialization and has a single function parameter + // whose type is a class template specialization with the template + // arguments of the second partial specialization. + // + // Rather than synthesize function templates, we merely perform the + // equivalent partial ordering by performing deduction directly on + // the template arguments of the class template partial + // specializations. This computation is slightly simpler than the + // general problem of function template partial ordering, because + // class template partial specializations are more constrained. We + // know that every template parameter is deducible from the class + // template partial specialization's template arguments, for + // example. + SmallVector Deduced; + + // Determine whether P1 is at least as specialized as P2. + Deduced.resize(P2->getTemplateParameters()->size()); + if (DeduceTemplateArgumentsByTypeMatch(S, P2->getTemplateParameters(), + T2, T1, Info, Deduced, TDF_None, + /*PartialOrdering=*/true)) + return false; + + SmallVector DeducedArgs(Deduced.begin(), + Deduced.end()); + Sema::InstantiatingTemplate Inst(S, Info.getLocation(), P2, DeducedArgs, + Info); + auto *TST1 = T1->castAs(); + if (FinishTemplateArgumentDeduction( + S, P2, /*PartialOrdering=*/true, + TemplateArgumentList(TemplateArgumentList::OnStack, + TST1->template_arguments()), + Deduced, Info)) + return false; + + return true; +} + /// \brief Determine whether this a function template whose parameter-type-list /// ends with a function parameter pack. static bool isVariadicFunctionTemplate(FunctionTemplateDecl *FunTmpl) { @@ -4807,6 +5052,15 @@ TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, unsigned NumCallArguments2) { + + auto JudgeByConstraints = [&] () -> FunctionTemplateDecl * { + bool MoreConstrained1 = isMoreConstrained(*this, FT1, FT2); + bool MoreConstrained2 = isMoreConstrained(*this, FT2, FT1); + if (MoreConstrained1 == MoreConstrained2) + return nullptr; + return MoreConstrained1 ? FT1 : FT2; + }; + bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, NumCallArguments1); bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, @@ -4816,7 +5070,7 @@ return Better1 ? FT1 : FT2; if (!Better1 && !Better2) // Neither is better than the other - return nullptr; + return JudgeByConstraints(); // FIXME: This mimics what GCC implements, but doesn't match up with the // proposed resolution for core issue 692. This area needs to be sorted out, @@ -4826,7 +5080,7 @@ if (Variadic1 != Variadic2) return Variadic1? FT2 : FT1; - return nullptr; + return JudgeByConstraints(); } /// \brief Determine if the two templates are equivalent. @@ -4940,66 +5194,6 @@ return SpecEnd; } -/// Determine whether one partial specialization, P1, is at least as -/// specialized than another, P2. -/// -/// \tparam TemplateLikeDecl The kind of P2, which must be a -/// TemplateDecl or {Class,Var}TemplatePartialSpecializationDecl. -/// \param T1 The injected-class-name of P1 (faked for a variable template). -/// \param T2 The injected-class-name of P2 (faked for a variable template). -template -static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, - TemplateLikeDecl *P2, - TemplateDeductionInfo &Info) { - // TODO: Concepts: Regard constraints - // C++ [temp.class.order]p1: - // For two class template partial specializations, the first is at least as - // specialized as the second if, given the following rewrite to two - // function templates, the first function template is at least as - // specialized as the second according to the ordering rules for function - // templates (14.6.6.2): - // - the first function template has the same template parameters as the - // first partial specialization and has a single function parameter - // whose type is a class template specialization with the template - // arguments of the first partial specialization, and - // - the second function template has the same template parameters as the - // second partial specialization and has a single function parameter - // whose type is a class template specialization with the template - // arguments of the second partial specialization. - // - // Rather than synthesize function templates, we merely perform the - // equivalent partial ordering by performing deduction directly on - // the template arguments of the class template partial - // specializations. This computation is slightly simpler than the - // general problem of function template partial ordering, because - // class template partial specializations are more constrained. We - // know that every template parameter is deducible from the class - // template partial specialization's template arguments, for - // example. - SmallVector Deduced; - - // Determine whether P1 is at least as specialized as P2. - Deduced.resize(P2->getTemplateParameters()->size()); - if (DeduceTemplateArgumentsByTypeMatch(S, P2->getTemplateParameters(), - T2, T1, Info, Deduced, TDF_None, - /*PartialOrdering=*/true)) - return false; - - SmallVector DeducedArgs(Deduced.begin(), - Deduced.end()); - Sema::InstantiatingTemplate Inst(S, Info.getLocation(), P2, DeducedArgs, - Info); - auto *TST1 = T1->castAs(); - if (FinishTemplateArgumentDeduction( - S, P2, /*PartialOrdering=*/true, - TemplateArgumentList(TemplateArgumentList::OnStack, - TST1->template_arguments()), - Deduced, Info)) - return false; - - return true; -} - /// \brief Returns the more specialized class template partial specialization /// according to the rules of partial ordering of class template partial /// specializations (C++ [temp.class.order]). @@ -5022,8 +5216,13 @@ bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info); bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info); - if (Better1 == Better2) - return nullptr; + if (Better1 == Better2) { + bool MoreConstrained1 = isMoreConstrained(*this, PS1, PS2); + bool MoreConstrained2 = isMoreConstrained(*this, PS2, PS1); + if (MoreConstrained1 == MoreConstrained2) + return nullptr; + return MoreConstrained1 ? PS1 : PS2; + } return Better1 ? PS1 : PS2; } @@ -5033,11 +5232,18 @@ ClassTemplateDecl *Primary = Spec->getSpecializedTemplate(); QualType PrimaryT = Primary->getInjectedClassNameSpecialization(); QualType PartialT = Spec->getInjectedSpecializationType(); + auto JudgeByConstraints = [&] { + bool MoreConstrainedPrimary = isMoreConstrained(*this, Primary, Spec); + bool MoreConstrainedSpec = isMoreConstrained(*this, Spec, Primary); + if (MoreConstrainedPrimary == MoreConstrainedSpec) + return false; + return MoreConstrainedSpec; + }; if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info)) - return false; - if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)) { + return JudgeByConstraints(); + if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)){ Info.clearSFINAEDiagnostic(); - return false; + return JudgeByConstraints(); } return true; } @@ -5062,8 +5268,14 @@ bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info); bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info); - if (Better1 == Better2) - return nullptr; + if (Better1 == Better2) { + bool MoreConstrained1 = isMoreConstrained(*this, PS1, PS2); + bool MoreConstrained2 = isMoreConstrained(*this, PS2, PS1); + if (MoreConstrained1 == MoreConstrained2) { + return nullptr; + } + return MoreConstrained1 ? PS1 : PS2; + } return Better1 ? PS1 : PS2; } @@ -5083,11 +5295,20 @@ CanonTemplate, PrimaryArgs); QualType PartialT = Context.getTemplateSpecializationType( CanonTemplate, Spec->getTemplateArgs().asArray()); + + auto JudgeByConstraints = [&] { + bool MoreConstrainedPrimary = isMoreConstrained(*this, Primary, Spec); + bool MoreConstrainedSpec = isMoreConstrained(*this, Spec, Primary); + if (MoreConstrainedPrimary == MoreConstrainedSpec) + return false; + return MoreConstrainedSpec; + }; + if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info)) - return false; - if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)) { + return JudgeByConstraints(); + if (isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info)){ Info.clearSFINAEDiagnostic(); - return false; + return JudgeByConstraints(); } return true; } Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3223,7 +3223,12 @@ // in the member template's set of class template partial specializations. void *InsertPos = nullptr; ClassTemplateSpecializationDecl *PrevDecl - = ClassTemplate->findPartialSpecialization(Converted, InsertPos); + = ClassTemplate->findPartialSpecialization(Converted, + // TODO: Concepts - change this + // to associated constraints once + // we have them. + InstParams->getRequiresClause(), + InsertPos); // Build the canonical type that describes the converted template // arguments of the class template partial specialization. @@ -3354,7 +3359,12 @@ // in the member template's set of variable template partial specializations. void *InsertPos = nullptr; VarTemplateSpecializationDecl *PrevDecl = - VarTemplate->findPartialSpecialization(Converted, InsertPos); + VarTemplate->findPartialSpecialization(Converted, + // TODO: Concepts - change this + // to associated constraints once + // we have them. + InstParams->getRequiresClause(), + InsertPos); // Build the canonical type that describes the converted template // arguments of the variable template partial specialization. Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -2142,12 +2142,14 @@ void ASTDeclReader::VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D) { - RedeclarableResult Redecl = VisitClassTemplateSpecializationDeclImpl(D); - + // We need to read the template params first because redeclarable is going to + // need them for profiling TemplateParameterList *Params = Record.readTemplateParameterList(); D->TemplateParams.setPointer(Params); D->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + RedeclarableResult Redecl = VisitClassTemplateSpecializationDeclImpl(D); + // These are read/set from/to the first declaration. if (ThisDeclID == Redecl.getFirstID()) { D->InstantiatedFromMember.setPointer( @@ -2253,12 +2255,12 @@ /// using Template(Partial)SpecializationDecl as input type. void ASTDeclReader::VisitVarTemplatePartialSpecializationDecl( VarTemplatePartialSpecializationDecl *D) { - RedeclarableResult Redecl = VisitVarTemplateSpecializationDeclImpl(D); - TemplateParameterList *Params = Record.readTemplateParameterList(); D->TemplateParams.setPointer(Params); D->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + RedeclarableResult Redecl = VisitVarTemplateSpecializationDeclImpl(D); + // These are read/set from/to the first declaration. if (ThisDeclID == Redecl.getFirstID()) { D->InstantiatedFromMember.setPointer( Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1460,11 +1460,11 @@ void ASTDeclWriter::VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D) { - VisitClassTemplateSpecializationDecl(D); - Record.AddTemplateParameterList(D->getTemplateParameters()); Record.AddASTTemplateArgumentListInfo(D->getTemplateArgsAsWritten()); + VisitClassTemplateSpecializationDecl(D); + // These are read/set from/to the first declaration. if (D->getPreviousDecl() == nullptr) { Record.AddDeclRef(D->getInstantiatedFromMember()); @@ -1525,11 +1525,11 @@ void ASTDeclWriter::VisitVarTemplatePartialSpecializationDecl( VarTemplatePartialSpecializationDecl *D) { - VisitVarTemplateSpecializationDecl(D); - Record.AddTemplateParameterList(D->getTemplateParameters()); Record.AddASTTemplateArgumentListInfo(D->getTemplateArgsAsWritten()); + VisitVarTemplateSpecializationDecl(D); + // These are read/set from/to the first declaration. if (D->getPreviousDecl() == nullptr) { Record.AddDeclRef(D->getInstantiatedFromMember()); Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp @@ -9,23 +9,6 @@ } // end namespace nodiag -namespace diag { - -template requires true // expected-note{{previous template declaration is here}} -int A(); -template int A(); // expected-error{{associated constraints differ in template redeclaration}} - -template int B(); // expected-note{{previous template declaration is here}} -template requires true // expected-error{{associated constraints differ in template redeclaration}} -int B(); - -template requires true // expected-note{{previous template declaration is here}} -int C(); -template requires !0 // expected-error{{associated constraints differ in template redeclaration}} -int C(); - -} // end namespace diag - namespace nodiag { struct AA { @@ -42,11 +25,11 @@ template struct TA { - template