saar.raz updated this revision to Diff 147187. saar.raz added a comment. - Adjusted constraint normalization to piecewise constraint substitution - Normalized constraints are now represented as described in the standard (a non-substituted expression along with a list of "template arguments").
Repository: rC Clang https://reviews.llvm.org/D41910 Files: include/clang/AST/DeclTemplate.h include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/AST/ASTContext.cpp lib/AST/DeclTemplate.cpp lib/Sema/SemaConcept.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateDeduction.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriterDecl.cpp test/CXX/concepts-ts/temp/temp.constr/temp.constr.decl/func-template-decl.cpp test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template<typename T> requires sizeof(T) >= 4 +bool a = false; // expected-note{{template is declared here}} + +template<typename T> requires sizeof(T) >= 4 && sizeof(T) <= 10 +bool a<T> = true; // expected-error{{variable template partial specialization is not more specialized than the primary template}} + +template<typename T> +concept C1 = sizeof(T) >= 4; + +template<typename T> requires C1<T> +bool b = false; + +template<typename T> requires C1<T> && sizeof(T) <= 10 +bool b<T> = true; + +template<typename T> +concept C2 = sizeof(T) > 1 && sizeof(T) <= 8; + +template<typename T> +bool c = false; + +template<typename T> requires C1<T> +bool c<T> = true; + +template<typename T> +bool d = false; + +template<typename T> +bool d<T> = true; // expected-error{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template<typename T> requires C1<T> +bool e = false; + +template<typename T> +bool e<T> = true; // expected-error{{variable template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template<typename T> +constexpr int f = 1; + +template<typename T> requires C1<T> && C2<T> +constexpr int f<T> = 2; + +template<typename T> requires C1<T> || C2<T> +constexpr int f<T> = 3; + +static_assert(f<unsigned> == 2); +static_assert(f<char[10]> == 3); +static_assert(f<char> == 1); + + + Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/function-templates.cpp @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template<typename T> requires sizeof(T) >= 4 +bool a() { return false; } // expected-note {{candidate function [with T = unsigned int]}} + +template<typename T> requires sizeof(T) >= 4 && sizeof(T) <= 10 +bool a() { return true; } // expected-note {{candidate function [with T = unsigned int]}} + +bool av = a<unsigned>(); // expected-error {{call to 'a' is ambiguous}} + +template<typename T> +concept C1 = sizeof(T) >= 4; + +template<typename T> requires C1<T> +constexpr bool b() { return false; } + +template<typename T> requires C1<T> && sizeof(T) <= 10 +constexpr bool b() { return true; } + +static_assert(b<int>()); +static_assert(!b<int[10]>()); + +template<typename T> +concept C2 = sizeof(T) > 1 && sizeof(T) <= 8; + +template<typename T> +bool c() { return false; } + +template<typename T> requires C1<T> +bool c() { return true; } + +template<typename T> requires C1<T> +constexpr bool d() { return false; } + +template<typename T> +constexpr bool d() { return true; } + +static_assert(!d<int>()); + +template<typename T> +constexpr int e() { return 1; } + +template<typename T> requires C1<T> && C2<T> +constexpr int e() { return 2; } + +template<typename T> requires C1<T> || C2<T> +constexpr int e() { return 3; } + +static_assert(e<unsigned>() == 2); +static_assert(e<char[10]>() == 3); +static_assert(e<char>() == 1); + + + Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template<typename T> requires sizeof(T) >= 4 +class A{}; // expected-note{{template is declared here}} + +template<typename T> requires sizeof(T) >= 4 && sizeof(T) <= 10 +class A<T>{}; // expected-error{{class template partial specialization is not more specialized than the primary template}} + +template<typename T> +concept C1 = sizeof(T) >= 4; + +template<typename T> requires C1<T> +class B{}; + +template<typename T> requires C1<T> && sizeof(T) <= 10 +class B<T>{}; + +template<typename T> +concept C2 = sizeof(T) > 1 && sizeof(T) <= 8; + +template<typename T> +class C{}; + +template<typename T> requires C1<T> +class C<T>{}; + +template<typename T> +class D{}; // expected-note{{previous definition is here}} + +template<typename T> +class D<T>{}; // expected-error{{class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} expected-error{{redefinition of 'D'}} + +template<typename T> requires C1<T> // expected-note{{previous template declaration is here}} +class E{}; + +template<typename T> // expected-error{{associated constraints differ in template redeclaration}} +class E<T>{}; // expected-error{{class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list}} + +template<typename T> +struct F{ enum{ value = 1 }; }; + +template<typename T> requires C1<T> && C2<T> +struct F<T>{ enum{ value = 2 }; }; + +template<typename T> requires C1<T> || C2<T> +struct F<T>{ enum{ value = 3 }; }; + +static_assert(F<unsigned>::value == 2); +static_assert(F<char[10]>::value == 3); +static_assert(F<char>::value == 1); + +template<typename T1, typename T2> +concept C3 = true; + +template<typename... Ts> +concept C4 = C3<Ts...>; + +// C4 is normalized to C3<Ts...>@<C4's definition> because there is a pack +// expansion into a non-pack parameter. therefore the two C4<T...> subsume each +// other and the following is non-ambiguous. + +template<typename... T> requires C4<T...> +struct G { }; + +template<typename... T> requires C4<T...> && C4<int, short> +struct G<T...> { }; + +// Here the two C3s cannot be normalized further, and do not subsume each other +// because they originate in two different locations in code. + +template<typename... T> requires C3<T...> +struct H { }; // expected-note {{template is declared here}} + +template<typename... T> requires C3<T...> && C4<int, short> +struct H<T...> { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +// Make sure atomic constraints subsume each other only if their parameter +// mappings are identical. + +template<typename T, typename U> requires C2<T> +struct I { }; // expected-note {{template is declared here}} + +template<typename T, typename U> requires C2<U> +struct I<T, U> { }; // expected-error {{class template partial specialization is not more specialized than the primary template}} \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.normal/p1.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +namespace maybe_incorrect_args { + template<typename U, typename T> + concept C = true; + + // diagnostic expected here - the C<Ts...>s are treated as atomic, and since + // they originate from different source-level constructs, they do not subsume + // each other. + template<typename... Ts> requires C<Ts...> + struct A {}; // expected-note{{template is declared here}} + + template<typename... Ts> requires C<Ts...> && true + struct A<Ts...> {}; // expected-error{{class template partial specialization is not more specialized than the primary template}} +} + +namespace ill_formed_subst { + + template<typename T> + struct B; // expected-note 2{{template is declared here}} + + template<typename T> + concept C1 = true; + + template<typename T, typename U> + concept C2 = C1<typename B<U>::foo>; // expected-error 2{{implicit instantiation of undefined template 'ill_formed_subst::B<int>'}} expected-note 2{{could not substitute template arguments into C1<typename B<U>::foo> [with T = type-parameter-0-0, U = int]. Template arguments to a concept specialization expression must be valid expressions}} + + template<typename T> requires C2<T, int> + struct A {}; // expected-note {{template is declared here}} + + template<typename T> requires C2<T, int> && true + struct A<T> {}; // expected-error {{class template partial specialization is not more specialized than the primary template}} +} + +namespace incorrect_args_after_subst { + template<typename T> + concept C1 = true; // expected-note 2{{template is declared here}} + + template<typename... Ts> + concept C2 = C1<Ts...>; // expected-error 2{{too many template arguments for template 'C1'}} expected-note 2{{could not substitute template arguments into C1<Ts...> [with Ts = <type-parameter-0-0, type-parameter-0-0>] - the resulting template argument list does not match the concept's parameter list}} + + template<typename T> requires C2<T, T> + struct A {}; // expected-note{{template is declared here}} + + template<typename T> requires C2<T, T> && true + struct A<T> {}; // expected-error{{class template partial specialization is not more specialized than the primary template}} +} + +namespace maybe_incorrect_args_after_subst { + template<typename T, typename U> + concept C1 = true; + + template<typename... Us> + concept C2 = C1<Us...>; + + // no diagnostic expected here - C1<Us...> is treated as atomic, and since it + // originates at the same source level construct, the specialized subsumes the + // primary. + template<typename... Ts> requires C2<Ts...> + struct A {}; + + template<typename... Ts> requires C2<Ts...> && true + struct A<Ts...> {}; +} \ No newline at end of file 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 <typename T> requires true // expected-note{{previous template declaration is here}} -int A(); -template <typename T> int A(); // expected-error{{associated constraints differ in template redeclaration}} - -template <typename T> int B(); // expected-note{{previous template declaration is here}} -template <typename T> requires true // expected-error{{associated constraints differ in template redeclaration}} -int B(); - -template <typename T> requires true // expected-note{{previous template declaration is here}} -int C(); -template <typename T> requires !0 // expected-error{{associated constraints differ in template redeclaration}} -int C(); - -} // end namespace diag - namespace nodiag { struct AA { @@ -42,11 +25,11 @@ template <unsigned N> struct TA { - template <template <unsigned> class TT> requires TT<N>::happy // expected-note{{previous template declaration is here}} + template <template <unsigned> class TT> requires TT<N>::happy int A(); }; template <unsigned N> -template <template <unsigned> class TT> int TA<N>::A() { return sizeof(TT<N>); } // expected-error{{associated constraints differ in template redeclaration}} +template <template <unsigned> class TT> int TA<N>::A() { return sizeof(TT<N>); } // expected-error{{out-of-line definition of 'A' does not match any declaration in 'TA<N>'}} } // end namespace diag Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1458,11 +1458,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()); @@ -1523,11 +1523,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: 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/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3232,7 +3232,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. @@ -3363,7 +3368,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/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -4755,6 +4755,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<typename TemplateLikeDecl> +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<DeducedTemplateArgument, 4> 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<TemplateArgument, 4> DeducedArgs(Deduced.begin(), + Deduced.end()); + Sema::InstantiatingTemplate Inst(S, Info.getLocation(), P2, DeducedArgs, + Info); + auto *TST1 = T1->castAs<TemplateSpecializationType>(); + 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) { @@ -4801,6 +4860,21 @@ TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, unsigned NumCallArguments2) { + + auto JudgeByConstraints = [&] () -> FunctionTemplateDecl * { + bool MoreConstrained1 = IsMoreConstrained(FT1, + FT1->getAssociatedConstraints(), + FT2, + FT2->getAssociatedConstraints()); + bool MoreConstrained2 = IsMoreConstrained(FT2, + FT2->getAssociatedConstraints(), + FT1, + FT1->getAssociatedConstraints()); + 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, @@ -4810,7 +4884,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, @@ -4820,7 +4894,7 @@ if (Variadic1 != Variadic2) return Variadic1? FT2 : FT1; - return nullptr; + return JudgeByConstraints(); } /// \brief Determine if the two templates are equivalent. @@ -4934,66 +5008,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<typename TemplateLikeDecl> -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<DeducedTemplateArgument, 4> 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<TemplateArgument, 4> DeducedArgs(Deduced.begin(), - Deduced.end()); - Sema::InstantiatingTemplate Inst(S, Info.getLocation(), P2, DeducedArgs, - Info); - auto *TST1 = T1->castAs<TemplateSpecializationType>(); - 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]). @@ -5016,8 +5030,19 @@ 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(PS1, + PS1->getAssociatedConstraints(), + PS2, + PS2->getAssociatedConstraints()); + bool MoreConstrained2 = IsMoreConstrained(PS2, + PS2->getAssociatedConstraints(), + PS1, + PS1->getAssociatedConstraints()); + if (MoreConstrained1 == MoreConstrained2) + return nullptr; + return MoreConstrained1 ? PS1 : PS2; + } return Better1 ? PS1 : PS2; } @@ -5027,11 +5052,24 @@ ClassTemplateDecl *Primary = Spec->getSpecializedTemplate(); QualType PrimaryT = Primary->getInjectedClassNameSpecialization(); QualType PartialT = Spec->getInjectedSpecializationType(); + auto JudgeByConstraints = [&] { + bool MoreConstrainedPrimary = IsMoreConstrained(Primary, + Primary->getAssociatedConstraints(), + Spec, + Spec->getAssociatedConstraints()); + bool MoreConstrainedSpec = IsMoreConstrained(Spec, + Spec->getAssociatedConstraints(), + Primary, + Primary->getAssociatedConstraints()); + 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; } @@ -5056,8 +5094,20 @@ 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(PS1, + PS1->getAssociatedConstraints(), + PS2, + PS2->getAssociatedConstraints()); + bool MoreConstrained2 = IsMoreConstrained(PS2, + PS2->getAssociatedConstraints(), + PS1, + PS1->getAssociatedConstraints()); + if (MoreConstrained1 == MoreConstrained2) { + return nullptr; + } + return MoreConstrained1 ? PS1 : PS2; + } return Better1 ? PS1 : PS2; } @@ -5077,11 +5127,26 @@ CanonTemplate, PrimaryArgs); QualType PartialT = Context.getTemplateSpecializationType( CanonTemplate, Spec->getTemplateArgs().asArray()); + + auto JudgeByConstraints = [&] { + bool MoreConstrainedPrimary = IsMoreConstrained(Primary, + Primary->getAssociatedConstraints(), + Spec, + Spec->getAssociatedConstraints()); + bool MoreConstrainedSpec = IsMoreConstrained(Spec, + Spec->getAssociatedConstraints(), + Primary, + Primary->getAssociatedConstraints()); + 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/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -915,9 +915,9 @@ auto CheckValidDeclSpecifiers = [this, &D] { // C++ [temp.param] // p1 - // template-parameter: - // ... - // parameter-declaration + // template-parameter: + // ... + // parameter-declaration // p2 // ... A storage class shall not be specified in a template-parameter // declaration. @@ -2009,11 +2009,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; } @@ -3597,7 +3593,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 @@ -3617,7 +3617,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); @@ -3934,7 +3939,7 @@ // Check that the template argument list is well-formed for this template. SmallVector<TemplateArgument, 4> Converted; - if (CheckTemplateArgumentList(Template, TemplateLoc, + if (CheckTemplateArgumentList(Template, NameInfo.getLoc(), const_cast<TemplateArgumentListInfo &>(*TemplateArgs), false, Converted, /*UpdateArgsWithConversions=*/false)) return ExprError(); @@ -6763,6 +6768,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) { @@ -6972,6 +6978,13 @@ return false; } + if (!CheckRedeclarationConstraintMatch(Old->getRequiresClause(), + New->getRequiresClause())) { + if (Complain) + DiagnoseRedeclarationConstraintMismatch(Old, New); + return false; + } + return true; } @@ -7521,7 +7534,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); @@ -7545,7 +7563,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/SemaConcept.cpp =================================================================== --- lib/Sema/SemaConcept.cpp +++ lib/Sema/SemaConcept.cpp @@ -62,6 +62,16 @@ return false; } +void +Sema::DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, + const TemplateParameterList *New){ + Diag(New->getTemplateLoc(), + diag::err_template_different_associated_constraints); + + Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) + << /*declaration*/0; +} + template<typename TemplateDeclT> static bool calculateConstraintSatisfaction(Sema& S, TemplateDeclT *Template, @@ -345,4 +355,373 @@ diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; } +} +namespace { +struct AtomicConstraint { + AtomicConstraint(Expr *ConstraintExpr, + const TemplateArgumentListInfo *ParameterMapping = nullptr) + : ConstraintExpr{ConstraintExpr}, ParameterMapping{ParameterMapping} {} + + bool subsumes(ASTContext &C, const AtomicConstraint &Other) const { + // 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 [...] + + // We do not actually substitute the parameter mappings, therefore the + // constraint expressions are the originals, and comparing them will + // suffice. + if (ConstraintExpr != Other.ConstraintExpr) + return false; + + // Check that the parameter lists are identical + if ((!ParameterMapping) != (!Other.ParameterMapping)) + return false; + if (!ParameterMapping) + return true; + if (ParameterMapping->size() != Other.ParameterMapping->size()) + return false; + + for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) + if (!C.getCanonicalTemplateArgument( + ParameterMapping->arguments()[I].getArgument()) + .structurallyEquals(C.getCanonicalTemplateArgument( + Other.ParameterMapping->arguments()[I].getArgument()))) + return false; + + + return true; + } + + Expr *ConstraintExpr; + const TemplateArgumentListInfo *ParameterMapping; +}; + +class NormalizedConstraint { +public: + enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; + +private: + using CompoundConstraint = llvm::PointerIntPair< + std::pair<NormalizedConstraint, NormalizedConstraint> *, 1, + CompoundConstraintKind>; + + llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint; + + NormalizedConstraint(AtomicConstraint *C) : Constraint{C} {}; + NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, + NormalizedConstraint RHS, CompoundConstraintKind Kind) + : Constraint{CompoundConstraint{ + new std::pair<NormalizedConstraint, NormalizedConstraint>{LHS, RHS}, + Kind}} {}; + +public: + CompoundConstraintKind getCompoundKind() const { + assert(!isAtomic() && "getCompoundKind called on atomic constraint."); + return Constraint.get<CompoundConstraint>().getInt(); + } + + bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); } + + NormalizedConstraint &getLHS() const { + assert(!isAtomic() && "getLHS called on atomic constraint."); + return Constraint.get<CompoundConstraint>().getPointer()->first; + } + + NormalizedConstraint &getRHS() const { + assert(!isAtomic() && "getRHS called on atomic constraint."); + return Constraint.get<CompoundConstraint>().getPointer()->second; + } + + AtomicConstraint *getAtomicConstraint() const { + assert(isAtomic() && + "getAtomicConstraint called on non-atomic constraint."); + return Constraint.get<AtomicConstraint *>(); + } + static llvm::Optional<NormalizedConstraint> fromConstraintExpr( + Sema &S, Expr *E, TemplateDecl *TD = nullptr, + const TemplateArgumentListInfo *ParameterMapping = nullptr) { + assert(E != nullptr); + + // C++ [temp.constr.normal]p1.1 + // - The normal form of an expression (E) is the normal form of E. + if (ParenExpr *P = dyn_cast<ParenExpr>(E)) + return fromConstraintExpr(S, P->getSubExpr(), TD, ParameterMapping); + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) { + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { + auto LHS = fromConstraintExpr(S, BO->getLHS(), TD, ParameterMapping); + if (!LHS) + return llvm::Optional<NormalizedConstraint>{}; + auto RHS = fromConstraintExpr(S, BO->getRHS(), TD, ParameterMapping); + if (!RHS) + return llvm::Optional<NormalizedConstraint>{}; + + return NormalizedConstraint( + S.Context, *LHS, *RHS, + BO->getOpcode() == BO_LAnd ? CCK_Conjunction : CCK_Disjunction); + } + } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(E)) { + const TemplateArgumentListInfo *Mapping = + CSE->getTemplateArgumentListInfo(); + if (!ParameterMapping) { + llvm::SmallVector<TemplateArgument, 4> TempList; + bool InstantiationDependent = false; + bool Failed = S.CheckTemplateArgumentList( + CSE->getNamedConcept(), E->getLocStart(), + const_cast<TemplateArgumentListInfo &>(*Mapping), + /*PartialTemplateArgs=*/false, TempList, + /*UpdateArgsWithConversions=*/false, &InstantiationDependent); + // The potential failure case here is this: + // + // template<typename U> + // concept C = true; + // + // template<typename T> + // void foo() requires C<T, T> // The immediate constraint expr + // // contains a CSE with incorrect no. + // // of arguments. + // {} + // This will be handled when C<T, T> is parsed. + assert( + !Failed && + "Unmatched arguments in top level concept specialization " + "expression should've been caught while it was being constructed"); + + if (InstantiationDependent) + // The case is this: + // + // template<typename U, typename T> + // concept C = true; + // + // template<typename... Ts> + // void foo() requires C<Ts...> // The immediate constraint expr + // // contains a CSE whose parameters + // // are not mappable to arguments + // // without concrete values. + // {} + // + // Just treat C<Ts...> as an atomic constraint. + return NormalizedConstraint{new (S.Context) + AtomicConstraint(E, Mapping)}; + + return fromConstraintExpr(S, + CSE->getNamedConcept()->getConstraintExpr(), + CSE->getNamedConcept(), Mapping); + } + + assert(TD && "ParameterMapping provided without TemplateDecl"); + + llvm::SmallVector<TemplateArgument, 4> TempList; + bool InstantiationDependent = false; + bool Success = + !S.CheckTemplateArgumentList( + TD, ParameterMapping->getLAngleLoc(), + const_cast<TemplateArgumentListInfo &>(*ParameterMapping), + /*PartialTemplateArgs=*/false, TempList, + /*UpdateArgsWithConversions=*/true, &InstantiationDependent) && + !InstantiationDependent; + assert(Success && "ParameterMapping should have already been cheked " + "against template argument list earlier."); + + auto DiagnoseSubstitutionError = [&](unsigned int Diag) { + std::string TemplateArgString = S.getTemplateArgumentBindingsText( + TD->getTemplateParameters(), TempList.data(), TempList.size()); + S.Diag(CSE->getLocStart(), Diag) << CSE << TemplateArgString; + }; + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(TempList); + + ExprResult Result = S.SubstExpr(CSE, MLTAL); + if (!Result.isUsable() || Result.isInvalid()) { + // C++ [temp.constr.normal] + // If any such substitution results in an invalid type or + // expression, the program is ill-formed; no diagnostic is required. + + // A diagnostic was already emitted from the substitution , but + // we'll let the user know why it's not SFINAEd from them. + DiagnoseSubstitutionError( + diag::note_could_not_normalize_argument_substitution_failed); + return llvm::Optional<NormalizedConstraint>{}; + } + Mapping = cast<ConceptSpecializationExpr>(Result.get()) + ->getTemplateArgumentListInfo(); + + llvm::SmallVector<TemplateArgument, 4> Converted; + if (S.CheckTemplateArgumentList( + CSE->getNamedConcept(), CSE->getLocStart(), + const_cast<TemplateArgumentListInfo &>(*Mapping), + /*PartialTemplateArgs=*/false, Converted, + /*UpdateArgsWithConversions=*/true, &InstantiationDependent)) { + // The case is this: + // + // template<typename T, typename U> + // concept C1 = true; + // + // template<typename... Ts> + // concept C2 = C1<Ts...>; // After substituting Ts = {T}, the + // // resulting argument list does not match + // // the parameter list. + // + // template<typename T> + // void foo() requires C2<T> {} + DiagnoseSubstitutionError( + diag::note_could_not_normalize_unmatched_argument_list_after_subst); + return llvm::Optional<NormalizedConstraint>{}; + } + if (InstantiationDependent) + // The case is this: + // + // template<typename T, typename U> + // concept C1 = true; + // + // template<typename... Us> + // concept C2 = C1<Us...>; // After substituting Us = {Ts}, we cannot + // // match arguments to parameters. + // + // template<typename... Ts> + // void foo() requires C2<T...> {} + // + // Treat the CSE as an atomic expression. + return NormalizedConstraint{new (S.Context) + AtomicConstraint(E, ParameterMapping)}; + + return fromConstraintExpr(S, CSE->getNamedConcept()->getConstraintExpr(), + CSE->getNamedConcept(), Mapping); + } + return NormalizedConstraint{new (S.Context) + AtomicConstraint(E, ParameterMapping)}; + } +}; +} // namespace + +using NormalForm = + llvm::SmallVector<llvm::SmallVector<AtomicConstraint *, 2>, 4>; + +static NormalForm makeCNF(const NormalizedConstraint &Normalized) { + if (Normalized.isAtomic()) + return {{Normalized.getAtomicConstraint()}}; + + NormalForm LCNF = makeCNF(Normalized.getLHS()); + NormalForm RCNF = makeCNF(Normalized.getRHS()); + if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) { + LCNF.reserve(LCNF.size() + RCNF.size()); + while (!RCNF.empty()) + LCNF.push_back(std::move(RCNF.pop_back_val())); + return LCNF; + } + + // Disjunction + 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; +} + +static NormalForm makeDNF(const NormalizedConstraint &Normalized) { + if (Normalized.isAtomic()) + return {{Normalized.getAtomicConstraint()}}; + + NormalForm LDNF = makeDNF(Normalized.getLHS()); + NormalForm RDNF = makeDNF(Normalized.getRHS()); + if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) { + LDNF.reserve(LDNF.size() + RDNF.size()); + while (!RDNF.empty()) + LDNF.push_back(std::move(RDNF.pop_back_val())); + return LDNF; + } + + // Conjunction + 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; +} + +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. [...] + auto PNormalized = NormalizedConstraint::fromConstraintExpr(S, P); + if (!PNormalized) + // Program is ill formed at this point. + return false; + const NormalForm PDNF = makeDNF(*PNormalized); + + auto QNormalized = NormalizedConstraint::fromConstraintExpr(S, Q); + if (!QNormalized) + // Program is ill formed at this point. + return false; + const NormalForm QCNF = makeCNF(*QNormalized); + + // 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 AtomicConstraint *Pia : Pi) { + for (const AtomicConstraint *Qjb : Qj) { + if (Pia->subsumes(S.Context, *Qjb)) { + Found = true; + break; + } + } + if (Found) + break; + } + if (!Found) { + return false; + } + } + } + return true; +} + +bool Sema::IsMoreConstrained(NamedDecl *D1, Expr *AC1, NamedDecl *D2, + Expr *AC2) { + if (!AC1) + return AC2 == nullptr; + if (!AC2) + // TD1 has associated constraints and TD2 does not. + return true; + + std::pair<NamedDecl *, NamedDecl *> Key{D1, D2}; + auto CacheEntry = SubsumptionCache.find(Key); + if (CacheEntry != SubsumptionCache.end()) { + return CacheEntry->second; + } + bool Subsumes = subsumes(*this, AC1, AC2); + SubsumptionCache.try_emplace(Key, Subsumes); + return Subsumes; } \ No newline at end of file Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -254,15 +254,15 @@ } } -template<class EntryType> +template<class EntryType, typename... ProfileArguments> typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType * RedeclarableTemplateDecl::findSpecializationImpl( - llvm::FoldingSetVector<EntryType> &Specs, ArrayRef<TemplateArgument> Args, - void *&InsertPos) { + llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos, + ProfileArguments... ProfileArgs) { using SETraits = SpecEntryTraits<EntryType>; 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; } @@ -277,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 @@ -335,7 +335,7 @@ FunctionDecl * FunctionTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void FunctionTemplateDecl::addSpecialization( @@ -407,7 +407,7 @@ ClassTemplateSpecializationDecl * ClassTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D, @@ -417,8 +417,10 @@ ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecialization(ArrayRef<TemplateArgument> Args, + Expr *AssociatedConstraints, void *&InsertPos) { - return findSpecializationImpl(getPartialSpecializations(), Args, InsertPos); + return findSpecializationImpl(getPartialSpecializations(), InsertPos, + Args, AssociatedConstraints); } void ClassTemplateDecl::AddPartialSpecialization( @@ -911,9 +913,10 @@ return Result; } -Expr* ClassTemplatePartialSpecializationDecl::getAssociatedConstraints() { +Expr* ClassTemplatePartialSpecializationDecl::getAssociatedConstraints() const { return getOrCollectAssociatedConstraints(getASTContext(), - cast<ClassTemplatePartialSpecializationDecl>(getCanonicalDecl()) + const_cast<ClassTemplatePartialSpecializationDecl*>( + cast<ClassTemplatePartialSpecializationDecl>(getCanonicalDecl())) ->TemplateParams); } @@ -1029,7 +1032,7 @@ VarTemplateSpecializationDecl * VarTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D, @@ -1039,8 +1042,10 @@ VarTemplatePartialSpecializationDecl * VarTemplateDecl::findPartialSpecialization(ArrayRef<TemplateArgument> Args, + Expr *AssociatedConstraints, void *&InsertPos) { - return findSpecializationImpl(getPartialSpecializations(), Args, InsertPos); + return findSpecializationImpl(getPartialSpecializations(), InsertPos, Args, + AssociatedConstraints); } void VarTemplateDecl::AddPartialSpecialization( @@ -1189,9 +1194,10 @@ return new (C, ID) VarTemplatePartialSpecializationDecl(C); } -Expr* VarTemplatePartialSpecializationDecl::getAssociatedConstraints() { +Expr* VarTemplatePartialSpecializationDecl::getAssociatedConstraints() const { return getOrCollectAssociatedConstraints(getASTContext(), - cast<VarTemplatePartialSpecializationDecl>(getCanonicalDecl()) + const_cast<VarTemplatePartialSpecializationDecl*>( + cast<VarTemplatePartialSpecializationDecl>(getCanonicalDecl())) ->TemplateParams); } Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -699,7 +699,7 @@ cast<TemplateTemplateParmDecl>(*P))); } - assert(!TTP->getRequiresClause() && + assert(!TTP->getTemplateParameters()->getRequiresClause() && "Unexpected requires-clause on template template-parameter"); Expr *const CanonRequiresClause = nullptr; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5582,6 +5582,17 @@ /// expression. A diagnostic is emitted if it is not, and false is returned. bool CheckConstraintExpression(Expr *CE); +private: + /// \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<std::pair<NamedDecl *, NamedDecl *>, bool> SubsumptionCache; + +public: + /// \brief Returns whether the given declaration's associated constraints are + /// more constrained than another declaration's according to the partial + /// ordering of constraints. + bool IsMoreConstrained(NamedDecl *D1, Expr *AC1, NamedDecl *D2, Expr *AC2); /// \brief Check whether the given constraint expression is satisfied given /// template arguments. Returns false and updates IsSatisfied with the @@ -5645,6 +5656,9 @@ void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation, StringRef Diagnostic); + void DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, + const TemplateParameterList *New); + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ArrayRef<Expr *> Strings); Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2422,11 +2422,13 @@ "%select{and |because }0%1 does not satisfy %2">; def note_atomic_constraint_evaluated_to_false_elaborated : Note< "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">; -def err_could_not_normalize_ill_formed_constraint : Error< - "required expansion of concept specialization %0 failed, substituted " - "expression would be illegal">; -def note_could_not_normalize_ill_formed_constraint_reason : Note< - "because: %0">; +def note_could_not_normalize_argument_substitution_failed : Note< + "could not substitute template arguments into %0 %1. " + "Template arguments to a concept specialization expression must be valid " + "expressions">; +def note_could_not_normalize_unmatched_argument_list_after_subst : Note< + "could not substitute template arguments into %0 %1 - the resulting template " + "argument list does not match the concept's parameter list">; def err_template_different_associated_constraints : Error< "associated constraints differ in template redeclaration">; Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -764,9 +764,10 @@ void loadLazySpecializationsImpl() const; - template <class EntryType> typename SpecEntryTraits<EntryType>::DeclType* + template <class EntryType, typename... ProfileArguments> + typename SpecEntryTraits<EntryType>::DeclType* findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs, - ArrayRef<TemplateArgument> Args, void *&InsertPos); + void *&InsertPos, ProfileArguments... ProfileArgs); template <class Derived, class EntryType> void addSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs, @@ -1936,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 { @@ -2020,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<TemplateArgument> 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()); } @@ -2145,7 +2161,8 @@ /// \brief Return the partial specialization with the provided arguments if it /// exists, otherwise return the insertion point. ClassTemplatePartialSpecializationDecl * - findPartialSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos); + findPartialSpecialization(ArrayRef<TemplateArgument> Args, + Expr *AssociatedConstraints, void *&InsertPos); /// \brief Insert the specified partial specialization knowing that it is not /// already in. InsertPos must be obtained from findPartialSpecialization. @@ -2780,7 +2797,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 @@ -2846,6 +2863,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<TemplateArgument> 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) { @@ -2964,7 +2998,8 @@ /// \brief Return the partial specialization with the provided arguments if it /// exists, otherwise return the insertion point. VarTemplatePartialSpecializationDecl * - findPartialSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos); + findPartialSpecialization(ArrayRef<TemplateArgument> Args, + Expr *AssociatedConstraints, void *&InsertPos); /// \brief Insert the specified partial specialization knowing that it is not /// already in. InsertPos must be obtained from findPartialSpecialization.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits