saar.raz updated this revision to Diff 137908.
saar.raz added a comment.
- Correct handling of non-substitutable concept specialization expressions.
Repository:
rC Clang
https://reviews.llvm.org/D41910
Files:
include/clang/AST/DeclTemplate.h
include/clang/Sema/Sema.h
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.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,75 @@
+// 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}}
\ 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
@@ -4761,6 +4761,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) {
@@ -4807,6 +4866,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,
@@ -4816,7 +4890,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 +4900,7 @@
if (Variadic1 != Variadic2)
return Variadic1? FT2 : FT1;
- return nullptr;
+ return JudgeByConstraints();
}
/// \brief Determine if the two templates are equivalent.
@@ -4940,66 +5014,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]).
@@ -5022,8 +5036,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;
}
@@ -5033,11 +5058,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;
}
@@ -5062,8 +5100,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;
}
@@ -5083,11 +5133,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
@@ -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);
@@ -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
@@ -60,6 +60,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>
bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
const Expr *ConstraintExpr,
@@ -294,9 +304,208 @@
DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation(), "");
}
}
+
void Sema::DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLoc,
StringRef Diagnostic) {
Diag(DiagnosticLoc,
diag::note_substituted_constraint_expr_is_ill_formed)
<< Diagnostic;
+}
+
+
+static Expr *normalizeConstraintExpr(Sema &S, Expr *E) {
+ assert(E != nullptr);
+
+ if (ParenExpr *P = dyn_cast<ParenExpr>(E))
+ return normalizeConstraintExpr(S, P->getSubExpr());
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(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<ConceptSpecializationExpr>(E)) {
+ if (Expr *CE = CSE->getSubstitutedConstraintExpr())
+ return normalizeConstraintExpr(S, CE);
+
+ if (auto *SubstDiag = CSE->getSubstitutionDiagnostic()) {
+ S.Diag(E->getLocStart(),
+ diag::err_could_not_normalize_ill_formed_constraint) << CSE;
+
+ S.Diag(SubstDiag->first,
+ diag::note_could_not_normalize_ill_formed_constraint_reason)
+ << SubstDiag->second;
+ return nullptr;
+ }
+
+ // We could not instantiate the concept's constraint expression with its
+ // arguments, for example:
+ //
+ // template<typename A, typename B>
+ // concept C = true;
+ //
+ // template<typename... T> requires sizeof...(T) == 2
+ // struct B {
+ // void foo() requires C<T...>;
+ // };
+ //
+ // Treat it as an atomic expression.
+ }
+
+ return E;
+}
+
+using NormalForm = llvm::SmallVector<llvm::SmallVector<Expr *, 2>, 4>;
+
+static NormalForm makeCNF(Expr *Normalized) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(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<BinaryOperator>(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;
+}
+
+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: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5580,6 +5580,18 @@
/// expression. A diagnostic is emmited 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
/// satisfaction verdict if successful, emits a diagnostic and returns true if
@@ -5648,6 +5660,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<Expr *> Strings);
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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits