saar.raz updated this revision to Diff 204782.
saar.raz added a comment.
Add support for CSE mangling
Repository:
rC Clang
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D41569/new/
https://reviews.llvm.org/D41569
Files:
include/clang/AST/ExprCXX.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
include/clang/Sema/SemaConcept.h
include/clang/Sema/TemplateDeduction.h
lib/AST/ExprCXX.cpp
lib/AST/ItaniumMangle.cpp
lib/Sema/SemaConcept.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateDeduction.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriterStmt.cpp
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
test/CodeGenCXX/mangle-concept.cpp
Index: test/CodeGenCXX/mangle-concept.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/mangle-concept.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -verify -Wno-return-type -Wno-main -std=c++2a -fconcepts-ts -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s
+// expected-no-diagnostics
+
+namespace test1 {
+template <bool> struct S {};
+template <typename> concept C = true;
+template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
+template S<C<int>> f0<>();
+// CHECK: void @_ZN5test12f0IiEENS_1SIXL_ZNS_1CIT_EEEEEEv()
+}
+
+template <bool> struct S {};
+template <typename> concept C = true;
+template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
+template S<C<int>> f0<>();
+// CHECK: void @_Z2f0IiE1SIXL_Z1CIT_EEEEv()
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/partial-specializations.cpp
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+namespace class_templates
+{
+ template<typename T, typename U> requires sizeof(T) >= 4 // expected-note {{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
+ struct is_same { static constexpr bool value = false; };
+
+ template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4
+ struct is_same<T*, T*> { static constexpr bool value = true; };
+
+ static_assert(!is_same<char*, char*>::value);
+ static_assert(!is_same<short*, short*>::value);
+ static_assert(is_same<int*, int*>::value);
+ static_assert(is_same<char, char>::value); // expected-error {{constraints not satisfied for class template 'is_same' [with T = char, U = char]}}
+
+ template<typename T>
+ struct A { using type = typename T::type; }; // expected-error{{type 'int *' cannot be used prior to '::' because it has no members}}
+
+ template<typename T>
+ struct B {};
+
+ template<typename T> requires A<T>::type // expected-note{{in instantiation of template class 'class_templates::A<int *>' requested here}}
+ // expected-note@-1{{while substituting template arguments into constraint expression here}}
+ struct B<T*> {};
+
+ template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
+ struct B<T**> {};
+
+ static_assert((B<int**>{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B<int *>' required here}}
+ // expected-note@-1{{while checking constraint satisfaction for class template partial specialization 'B<int>' required here}}
+ // expected-note@-2{{during template argument deduction for class template partial specialization 'B<T *>' [with T = int *]}}
+ // expected-note@-3{{during template argument deduction for class template partial specialization 'B<T **>' [with T = int]}}
+ // expected-note@-4 2{{in instantiation of template class 'class_templates::B<int **>' requested here}}
+}
+
+namespace variable_templates
+{
+ template<typename T, typename U> requires sizeof(T) >= 4
+ constexpr bool is_same_v = false;
+
+ template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4
+ constexpr bool is_same_v<T*, T*> = true;
+
+ static_assert(!is_same_v<char*, char*>);
+ static_assert(!is_same_v<short*, short*>);
+ static_assert(is_same_v<int*, int*>);
+
+ template<typename T>
+ struct A { using type = typename T::type; }; // expected-error{{type 'int *' cannot be used prior to '::' because it has no members}}
+
+ template<typename T>
+ constexpr bool v1 = false;
+
+ template<typename T> requires A<T>::type // expected-note{{in instantiation of template class 'variable_templates::A<int *>' requested here}}
+ // expected-note@-1{{while substituting template arguments into constraint expression here}}
+ constexpr bool v1<T*> = true;
+
+ template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
+ constexpr bool v1<T**> = true;
+
+ static_assert(v1<int**>); // expected-note{{while checking constraint satisfaction for variable template partial specialization 'v1<int *>' required here}}
+ // expected-note@-1{{while checking constraint satisfaction for variable template partial specialization 'v1<int>' required here}}
+ // expected-note@-2{{during template argument deduction for variable template partial specialization 'v1<T *>' [with T = int *]}}
+ // expected-note@-3{{during template argument deduction for variable template partial specialization 'v1<T **>' [with T = int]}}
+ // expected-error@-4{{static_assert failed due to requirement 'v1<int **>'}}
+
+}
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T> requires sizeof(T) >= 2 // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}}
+struct A {
+ static constexpr int value = sizeof(T);
+};
+
+static_assert(A<int>::value == 4);
+static_assert(A<char>::value == 1); // expected-error{{constraints not satisfied for class template 'A' [with T = char]}}
+
+template<typename T, typename U>
+ requires sizeof(T) != sizeof(U) // expected-note{{because 'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}}
+ && sizeof(T) >= 4 // expected-note{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
+constexpr int SizeDiff = sizeof(T) > sizeof(U) ? sizeof(T) - sizeof(U) : sizeof(U) - sizeof(T);
+
+static_assert(SizeDiff<int, char> == 3);
+static_assert(SizeDiff<int, char[4]> == 0); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = int, U = char [4]]}}
+static_assert(SizeDiff<char, int> == 3); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = char, U = int]}}
+
+template<typename... Ts>
+ requires ((sizeof(Ts) == 4) || ...) // expected-note{{because 'sizeof(char) == 4' (1 == 4) evaluated to false}} expected-note{{'sizeof(long long) == 4' (8 == 4) evaluated to false}} expected-note{{'sizeof(int [20]) == 4' (80 == 4) evaluated to false}}
+constexpr auto SumSizes = (sizeof(Ts) + ...);
+
+static_assert(SumSizes<char, long long, int> == 13);
+static_assert(SumSizes<char, long long, int[20]> == 89); // expected-error{{constraints not satisfied for variable template 'SumSizes' [with Ts = <char, long long, int [20]>]}}
+
+template<typename T>
+concept IsBig = sizeof(T) > 100; // expected-note{{because 'sizeof(int) > 100' (4 > 100) evaluated to false}}
+
+template<typename T>
+ requires IsBig<T> // expected-note{{'int' does not satisfy 'IsBig'}}
+using BigPtr = T*;
+
+static_assert(sizeof(BigPtr<int>)); // expected-error{{constraints not satisfied for alias template 'BigPtr' [with T = int]}}}}
+
+template<typename T> requires T::value // expected-note{{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
+struct S { static constexpr bool value = true; };
+
+struct S2 { static constexpr bool value = true; };
+
+static_assert(S<int>::value); // expected-error{{constraints not satisfied for class template 'S' [with T = int]}}
+static_assert(S<S2>::value);
+
+template<typename T>
+struct AA
+{
+ template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
+ struct B
+ {
+ static constexpr int a = 0;
+ };
+
+ template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
+ static constexpr int b = 1;
+
+ template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
+ static constexpr int getB() { // expected-note{{candidate template ignored: constraints not satisfied [with U = int [2]]}}
+ return 2;
+ }
+
+ static auto foo()
+ {
+ return B<T[2]>::a; // expected-error{{constraints not satisfied for class template 'B' [with U = int [2]]}}
+ }
+
+ static auto foo1()
+ {
+ return b<T[2]>; // expected-error{{constraints not satisfied for variable template 'b' [with U = int [2]]}}
+ }
+
+ static auto foo2()
+ {
+ return AA<T>::getB<T[2]>(); // expected-error{{no matching function for call to 'getB'}}
+ }
+};
+
+constexpr auto x = AA<int>::foo(); // expected-note{{in instantiation of member function 'AA<int>::foo' requested here}}
+constexpr auto x1 = AA<int>::foo1(); // expected-note{{in instantiation of member function 'AA<int>::foo1' requested here}}
+constexpr auto x2 = AA<int>::foo2(); // expected-note{{in instantiation of member function 'AA<int>::foo2' requested here}}
+
+template<typename T>
+struct B { using type = typename T::type; }; // expected-error{{type 'int' cannot be used prior to '::' because it has no members}}
+
+template<typename T> requires B<T>::type // expected-note{{in instantiation of template class 'B<int>' requested here}}
+ // expected-note@-1{{while substituting template arguments into constraint expression here}}
+struct C { };
+
+template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
+struct D { };
+
+static_assert(C<int>{}); // expected-note{{while checking constraint satisfaction for template 'C<int>' required here}}
+static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' required here}}
\ No newline at end of file
Index: test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
===================================================================
--- /dev/null
+++ test/CXX/concepts-ts/temp/temp.constr/temp.constr.constr/function-templates.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T>
+constexpr bool is_ptr_v = false;
+
+template<typename T>
+constexpr bool is_ptr_v<T*> = true;
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T> requires is_ptr_v<T> // expected-note {{because 'is_ptr_v<int>' evaluated to false}}
+ // expected-note@-1{{because 'is_ptr_v<char>' evaluated to false}}
+auto dereference(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}}
+ // expected-note@-1{{candidate template ignored: constraints not satisfied [with T = char]}}
+ return *t;
+}
+
+static_assert(is_same_v<decltype(dereference<int*>(nullptr)), int>);
+static_assert(is_same_v<decltype(dereference(2)), int>); // expected-error {{no matching function for call to 'dereference'}}
+static_assert(is_same_v<decltype(dereference<char>('a')), char>); // expected-error {{no matching function for call to 'dereference'}}
+
+
+template<typename T> requires T{} + T{} // expected-note {{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}
+auto foo(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}
+ return t + t;
+}
+
+
+template<typename T> requires !((T{} - T{}) && (T{} + T{})) || false
+// expected-note@-1{{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}
+// expected-note@-2{{and 'false' evaluated to false}}
+auto bar(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}
+ return t + t;
+}
+
+struct A { };
+
+static_assert(foo(A{})); // expected-error {{no matching function for call to 'foo'}}
+static_assert(bar(A{})); // expected-error {{no matching function for call to 'bar'}}
\ No newline at end of file
Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
===================================================================
--- test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
+++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
@@ -72,6 +72,15 @@
static_assert(IsTypePredicate<T2>);
static_assert(!IsTypePredicate<T1>);
+template<typename T, typename U, typename... Ts>
+concept OneOf = (Same<T, Ts> || ...);
+
+template<typename... X>
+constexpr bool S = OneOf<X..., int, int>;
+
+static_assert(S<int, long, int>);
+static_assert(!S<long, int, char, char>);
+
namespace piecewise_substitution {
template <typename T>
concept True = true;
@@ -151,3 +160,25 @@
struct X { static constexpr bool a = SameSize<T>; };
static_assert(X<unsigned>::a);
+
+// static_assert concept diagnostics
+template<typename T>
+concept Large = sizeof(T) > 100;
+// expected-note@-1 2{{because 'sizeof(small) > 100' (1 > 100) evaluated to false}}
+
+struct small { };
+static_assert(Large<small>);
+// expected-error@-1 {{static_assert failed}}
+// expected-note@-2 {{because 'small' does not satisfy 'Large'}}
+static_assert(Large<small>, "small isn't large");
+// expected-error@-1 {{static_assert failed "small isn't large"}}
+// expected-note@-2 {{because 'small' does not satisfy 'Large'}}
+
+// Make sure access-checking can fail a concept specialization
+
+class T4 { static constexpr bool f = true; };
+template<typename T> concept AccessPrivate = T{}.f;
+// expected-note@-1{{because substituted constraint expression is ill-formed: 'f' is a private member of 'T4'}}
+static_assert(AccessPrivate<T4>);
+// expected-error@-1{{static_assert failed}}
+// expected-note@-2{{because 'T4' does not satisfy 'AccessPrivate'}}
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -397,7 +397,24 @@
Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
for (const TemplateArgument &Arg : TemplateArgs)
Record.AddTemplateArgument(Arg);
- Record.push_back(E->isSatisfied());
+ const ConstraintSatisfaction &Satisfaction = E->getSatisfaction();
+ Record.push_back(Satisfaction.IsSatisfied);
+ if (!Satisfaction.IsSatisfied) {
+ Record.push_back(Satisfaction.Details.size());
+ for (const auto &DetailRecord : Satisfaction.Details) {
+ Record.AddStmt(const_cast<Expr *>(DetailRecord.first));
+ auto *Diag =
+ DetailRecord.second
+ .dyn_cast<ConstraintSatisfaction::SubstitutionDiagnostic *>();
+ Record.push_back(Diag != nullptr);
+ if (Diag) {
+ Record.AddSourceLocation(Diag->first);
+ Record.AddString(Diag->second);
+ } else
+ Record.AddStmt(DetailRecord.second.get<Expr *>());
+ }
+ }
+
Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
}
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -721,14 +721,31 @@
E->TemplateKWLoc = Record.readSourceLocation();
E->ConceptNameLoc = Record.readSourceLocation();
E->FoundDecl = ReadDeclAs<NamedDecl>();
- E->NamedConcept.setPointer(ReadDeclAs<ConceptDecl>());
+ E->NamedConcept = ReadDeclAs<ConceptDecl>();
const ASTTemplateArgumentListInfo *ArgsAsWritten =
Record.readASTTemplateArgumentListInfo();
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < NumTemplateArgs; ++I)
Args.push_back(Record.readTemplateArgument());
E->setTemplateArguments(ArgsAsWritten, Args);
- E->NamedConcept.setInt(Record.readInt() == 1);
+ ConstraintSatisfaction Satisfaction;
+ Satisfaction.IsSatisfied = Record.readInt();
+ if (!Satisfaction.IsSatisfied) {
+ unsigned NumDetailRecords = Record.readInt();
+ for (unsigned i = 0; i != NumDetailRecords; ++i) {
+ Expr *ConstraintExpr = Record.readExpr();
+ bool IsDiagnostic = Record.readInt();
+ if (IsDiagnostic) {
+ SourceLocation DiagLocation = Record.readSourceLocation();
+ std::string DiagMessage = Record.readString();
+ Satisfaction.Details.emplace_back(
+ ConstraintExpr, new (Record.getContext())
+ ConstraintSatisfaction::SubstitutionDiagnostic{
+ DiagLocation, DiagMessage});
+ } else
+ Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr());
+ }
+ }
}
void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3173,12 +3173,21 @@
// Check that the template argument list is well-formed for this
// class template.
+ bool InstantiationDependent;
SmallVector<TemplateArgument, 4> Converted;
if (SemaRef.CheckTemplateArgumentList(InstClassTemplate,
D->getLocation(),
InstTemplateArgs,
false,
- Converted))
+ Converted,
+ /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependent))
+ return nullptr;
+
+ if (!InstantiationDependent
+ && SemaRef.EnsureTemplateArgumentListConstraints(InstClassTemplate,
+ Converted,
+ D->getLocation()))
return nullptr;
// Figure out where to insert this class template explicit specialization
@@ -3290,11 +3299,19 @@
return nullptr;
// Check that the template argument list is well-formed for this template.
+ bool InstantiationDependent;
SmallVector<TemplateArgument, 4> Converted;
if (SemaRef.CheckTemplateArgumentList(
VarTemplate, VarTemplate->getBeginLoc(),
const_cast<TemplateArgumentListInfo &>(VarTemplateArgsInfo), false,
- Converted))
+ Converted, /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependent))
+ return nullptr;
+
+ if (!InstantiationDependent
+ && SemaRef.EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+ SourceRange(VarTemplate->getLocation(),
+ VarTemplateArgsInfo.getRAngleLoc())))
return nullptr;
// Find the variable template specialization declaration that
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp
+++ lib/Sema/SemaTemplateInstantiate.cpp
@@ -358,7 +358,7 @@
Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation,
- ConstraintsCheck, TemplateDecl *Template,
+ ConstraintsCheck, NamedDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange)
: InstantiatingTemplate(
SemaRef, CodeSynthesisContext::ConstraintsCheck,
@@ -367,7 +367,7 @@
Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation,
- ConstraintSubstitution, TemplateDecl *Template,
+ ConstraintSubstitution, NamedDecl *Template,
sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange)
: InstantiatingTemplate(
SemaRef, CodeSynthesisContext::ConstraintSubstitution,
@@ -677,24 +677,27 @@
case CodeSynthesisContext::Memoization:
break;
- case CodeSynthesisContext::ConstraintsCheck:
- if (auto *CD = dyn_cast<ConceptDecl>(Active->Entity)) {
- SmallVector<char, 128> TemplateArgsStr;
- llvm::raw_svector_ostream OS(TemplateArgsStr);
- CD->printName(OS);
- printTemplateArgumentList(OS, Active->template_arguments(),
- getPrintingPolicy());
- Diags.Report(Active->PointOfInstantiation,
- diag::note_concept_specialization_here)
- << OS.str()
- << Active->InstantiationRange;
- break;
+ case CodeSynthesisContext::ConstraintsCheck: {
+ unsigned DiagID = 0;
+ if (isa<ConceptDecl>(Active->Entity))
+ DiagID = diag::note_concept_specialization_here;
+ else if (isa<TemplateDecl>(Active->Entity))
+ DiagID = diag::note_checking_constraints_for_template_id_here;
+ else if (isa<VarTemplatePartialSpecializationDecl>(Active->Entity))
+ DiagID = diag::note_checking_constraints_for_var_spec_id_here;
+ else {
+ assert(isa<ClassTemplatePartialSpecializationDecl>(Active->Entity));
+ DiagID = diag::note_checking_constraints_for_class_spec_id_here;
}
- // TODO: Concepts - implement this for constrained templates and partial
- // specializations.
- llvm_unreachable("only concept constraints are supported right now");
+ SmallVector<char, 128> TemplateArgsStr;
+ llvm::raw_svector_ostream OS(TemplateArgsStr);
+ cast<NamedDecl>(Active->Entity)->printName(OS);
+ printTemplateArgumentList(OS, Active->template_arguments(),
+ getPrintingPolicy());
+ Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str()
+ << Active->InstantiationRange;
break;
-
+ }
case CodeSynthesisContext::ConstraintSubstitution:
Diags.Report(Active->PointOfInstantiation,
diag::note_constraint_substitution_here)
Index: lib/Sema/SemaTemplateDeduction.cpp
===================================================================
--- lib/Sema/SemaTemplateDeduction.cpp
+++ lib/Sema/SemaTemplateDeduction.cpp
@@ -2831,6 +2831,22 @@
return Sema::TDK_Success;
}
+template<typename TemplateDeclT>
+static Sema::TemplateDeductionResult
+CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
+ ArrayRef<TemplateArgument> DeducedArgs,
+ TemplateDeductionInfo& Info) {
+ llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
+ Template->getAssociatedConstraints(AssociatedConstraints);
+ if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints,
+ DeducedArgs, Info.getLocation(),
+ Info.AssociatedConstraintsSatisfaction)
+ || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+ Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));
+ return Sema::TDK_ConstraintsNotSatisfied;
+ }
+ return Sema::TDK_Success;
+}
/// Perform template argument deduction to determine whether
/// the given template arguments match the given class template
@@ -2871,6 +2887,10 @@
if (Trap.hasErrorOccurred())
return Sema::TDK_SubstitutionFailure;
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, Partial, DeducedArgs, Info))
+ return Result;
+
return ::FinishTemplateArgumentDeduction(
*this, Partial, /*PartialOrdering=*/false, TemplateArgs, Deduced, Info);
}
@@ -2912,6 +2932,10 @@
if (Trap.hasErrorOccurred())
return Sema::TDK_SubstitutionFailure;
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, Partial, DeducedArgs, Info))
+ return Result;
+
return ::FinishTemplateArgumentDeduction(
*this, Partial, /*PartialOrdering=*/false, TemplateArgs, Deduced, Info);
}
@@ -3021,6 +3045,12 @@
= TemplateArgumentList::CreateCopy(Context, Builder);
Info.setExplicitArgs(ExplicitArgumentList);
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, FunctionTemplate,
+ ExplicitArgumentList->asArray(),
+ Info))
+ return Result;
+
// Template argument deduction and the final substitution should be
// done in the context of the templated declaration. Explicit
// argument substitution, on the other hand, needs to happen in the
@@ -3333,6 +3363,11 @@
PartialOverloading))
return Result;
+ if (TemplateDeductionResult Result
+ = CheckDeducedArgumentConstraints(*this, FunctionTemplate, DeducedArgs,
+ Info))
+ return Result;
+
// C++ [temp.deduct.call]p10: [DR1391]
// If deduction succeeds for all parameters that contain
// template-parameters that participate in template argument deduction,
@@ -5009,6 +5044,7 @@
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
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -3120,8 +3120,7 @@
TemplateDecl *Template = Name.getAsTemplateDecl();
if (!Template || isa<FunctionTemplateDecl>(Template) ||
- isa<VarTemplateDecl>(Template) ||
- isa<ConceptDecl>(Template)) {
+ isa<VarTemplateDecl>(Template) || isa<ConceptDecl>(Template)) {
// We might have a substituted template template parameter pack. If so,
// build a template specialization type for it.
if (Name.getAsSubstTemplateTemplateParmPack())
@@ -3135,9 +3134,17 @@
// Check that the template argument list is well-formed for this
// template.
+ bool InstantiationDependentMatch;
SmallVector<TemplateArgument, 4> Converted;
if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs,
- false, Converted))
+ false, Converted,
+ /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependentMatch))
+ return QualType();
+
+ if (!InstantiationDependentMatch
+ && EnsureTemplateArgumentListConstraints(Template, Converted,
+ SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc())))
return QualType();
QualType CanonType;
@@ -3145,6 +3152,7 @@
bool InstantiationDependent = false;
if (TypeAliasTemplateDecl *AliasTemplate =
dyn_cast<TypeAliasTemplateDecl>(Template)) {
+
// Find the canonical type for this type alias template specialization.
TypeAliasDecl *Pattern = AliasTemplate->getTemplatedDecl();
if (Pattern->isInvalidDecl())
@@ -3726,9 +3734,17 @@
// Check that the template argument list is well-formed for this
// template.
+ bool InstantiationDependent;
SmallVector<TemplateArgument, 4> Converted;
if (CheckTemplateArgumentList(VarTemplate, TemplateNameLoc, TemplateArgs,
- false, Converted))
+ false, Converted,
+ /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependent))
+ return true;
+
+ if (!InstantiationDependent
+ && EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+ SourceRange(TemplateNameLoc, RAngleLoc)))
return true;
// Find the variable template (partial) specialization declaration that
@@ -3902,11 +3918,18 @@
assert(Template && "A variable template id without template?");
// Check that the template argument list is well-formed for this template.
+ bool InstantiationDependentMatch;
SmallVector<TemplateArgument, 4> Converted;
if (CheckTemplateArgumentList(
Template, TemplateNameLoc,
const_cast<TemplateArgumentListInfo &>(TemplateArgs), false,
- Converted))
+ Converted, /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependentMatch))
+ return true;
+
+ if (!InstantiationDependentMatch
+ && EnsureTemplateArgumentListConstraints(Template, Converted,
+ SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc())))
return true;
// Find the variable template specialization declaration that
@@ -4097,7 +4120,7 @@
/*UpdateArgsWithConversion=*/false))
return ExprError();
- bool IsSatisfied = true;
+ ConstraintSatisfaction Satisfaction;
bool IsInstantiationDependent = false;
for (TemplateArgument &Arg : Converted) {
if (Arg.isInstantiationDependent()) {
@@ -4105,23 +4128,21 @@
break;
}
}
- if (!IsInstantiationDependent) {
- InstantiatingTemplate Inst(*this, ConceptNameLoc,
- InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted,
- SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc,
- TemplateArgs->getRAngleLoc()));
- MultiLevelTemplateArgumentList MLTAL;
- MLTAL.addOuterTemplateArguments(Converted);
- if (CalculateConstraintSatisfaction(NamedConcept, MLTAL,
- NamedConcept->getConstraintExpr(),
- IsSatisfied))
+ if (!IsInstantiationDependent)
+ if (CheckConstraintSatisfaction(NamedConcept,
+ {NamedConcept->getConstraintExpr()},
+ Converted,
+ SourceRange(SS.isSet() ? SS.getBeginLoc() :
+ ConceptNameLoc,
+ TemplateArgs->getRAngleLoc()),
+ Satisfaction))
return ExprError();
- }
+
return ConceptSpecializationExpr::Create(Context,
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept,
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted,
- IsSatisfied);
+ std::move(Satisfaction));
}
ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
@@ -4165,7 +4186,7 @@
TemplateKWLoc, TemplateArgs);
}
- if (R.getAsSingle<ConceptDecl>() && !AnyDependentArguments()) {
+ if (R.getAsSingle<ConceptDecl>()) {
return CheckConceptTemplateId(SS, TemplateKWLoc,
R.getLookupNameInfo().getBeginLoc(),
R.getFoundDecl(),
@@ -5057,7 +5078,8 @@
TemplateDecl *Template, SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs,
SmallVectorImpl<TemplateArgument> &Converted,
- bool UpdateArgsWithConversions) {
+ bool UpdateArgsWithConversions, bool *InstantiationDependent) {
+
// Make a copy of the template arguments for processing. Only make the
// changes at the end when successful in matching the arguments to the
// template.
@@ -5161,6 +5183,10 @@
++ArgIdx;
}
+ // We cannot determine yet whether the arguments are well-formed.
+ if (InstantiationDependent)
+ *InstantiationDependent = true;
+
return false;
}
@@ -5173,6 +5199,8 @@
Converted.push_back(
TemplateArgument::CreatePackCopy(Context, ArgumentPack));
+ if (InstantiationDependent)
+ *InstantiationDependent = false;
return false;
}
@@ -5311,6 +5339,9 @@
if (UpdateArgsWithConversions)
TemplateArgs = std::move(NewArgs);
+ if (InstantiationDependent)
+ *InstantiationDependent = false;
+
return false;
}
@@ -7620,9 +7651,17 @@
// Check that the template argument list is well-formed for this
// template.
+ bool InstantiationDependent;
SmallVector<TemplateArgument, 4> Converted;
if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc,
- TemplateArgs, false, Converted))
+ TemplateArgs, false, Converted,
+ /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependent))
+ return true;
+
+ if (!InstantiationDependent
+ && EnsureTemplateArgumentListConstraints(ClassTemplate, Converted,
+ SourceRange(TemplateNameLoc, RAngleLoc)))
return true;
// Find the class template (partial) specialization declaration that
@@ -8841,10 +8880,19 @@
// Check that the template argument list is well-formed for this
// template.
+ bool InstantiationDependent;
SmallVector<TemplateArgument, 4> Converted;
if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc,
- TemplateArgs, false, Converted))
+ TemplateArgs, false, Converted,
+ /*UpdateArgsWithConversion=*/true,
+ &InstantiationDependent))
+ return true;
+
+ if (!InstantiationDependent
+ && EnsureTemplateArgumentListConstraints(ClassTemplate, Converted,
+ SourceRange(TemplateNameLoc, RAngleLoc))) {
return true;
+ }
// Find the class template specialization declaration that
// corresponds to these arguments.
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -581,6 +581,10 @@
TemplateArgumentList *TemplateArgs;
unsigned CallArgIndex;
};
+ struct CNSInfo {
+ TemplateArgumentList *TemplateArgs;
+ ConstraintSatisfaction Satisfaction;
+ };
}
/// Convert from Sema's representation of template deduction information
@@ -651,6 +655,14 @@
}
break;
+ case Sema::TDK_ConstraintsNotSatisfied: {
+ CNSInfo *Saved = new (Context) CNSInfo;
+ Saved->TemplateArgs = Info.take();
+ Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction;
+ Result.Data = Saved;
+ break;
+ }
+
case Sema::TDK_Success:
case Sema::TDK_NonDependentConversionFailure:
llvm_unreachable("not a deduction failure");
@@ -691,6 +703,15 @@
}
break;
+ case Sema::TDK_ConstraintsNotSatisfied:
+ // FIXME: Destroy the template argument list?
+ Data = nullptr;
+ if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {
+ Diag->~PartialDiagnosticAt();
+ HasDiagnostic = false;
+ }
+ break;
+
// Unhandled
case Sema::TDK_MiscellaneousDeductionFailure:
break;
@@ -716,6 +737,7 @@
case Sema::TDK_NonDeducedMismatch:
case Sema::TDK_CUDATargetMismatch:
case Sema::TDK_NonDependentConversionFailure:
+ case Sema::TDK_ConstraintsNotSatisfied:
return TemplateParameter();
case Sema::TDK_Incomplete:
@@ -759,6 +781,9 @@
case Sema::TDK_SubstitutionFailure:
return static_cast<TemplateArgumentList*>(Data);
+ case Sema::TDK_ConstraintsNotSatisfied:
+ return static_cast<CNSInfo*>(Data)->TemplateArgs;
+
// Unhandled
case Sema::TDK_MiscellaneousDeductionFailure:
break;
@@ -779,6 +804,7 @@
case Sema::TDK_SubstitutionFailure:
case Sema::TDK_CUDATargetMismatch:
case Sema::TDK_NonDependentConversionFailure:
+ case Sema::TDK_ConstraintsNotSatisfied:
return nullptr;
case Sema::TDK_IncompletePack:
@@ -810,6 +836,7 @@
case Sema::TDK_SubstitutionFailure:
case Sema::TDK_CUDATargetMismatch:
case Sema::TDK_NonDependentConversionFailure:
+ case Sema::TDK_ConstraintsNotSatisfied:
return nullptr;
case Sema::TDK_Inconsistent:
@@ -1225,6 +1252,8 @@
return NewTarget != OldTarget;
}
+ // TODO: Concepts: Check function trailing requires clauses here.
+
// The signatures match; this is not an overload.
return false;
}
@@ -10109,6 +10138,21 @@
MaybeEmitInheritedConstructorNote(S, Found);
return;
+ case Sema::TDK_ConstraintsNotSatisfied: {
+ // Format the template argument list into the argument string.
+ SmallString<128> TemplateArgString;
+ TemplateArgumentList *Args = DeductionFailure.getTemplateArgumentList();
+ TemplateArgString = " ";
+ TemplateArgString += S.getTemplateArgumentBindingsText(
+ getDescribedTemplate(Templated)->getTemplateParameters(), *Args);
+ S.Diag(Templated->getLocation(),
+ diag::note_ovl_candidate_unsatisfied_constraints)
+ << TemplateArgString;
+
+ S.DiagnoseUnsatisfiedConstraint(
+ static_cast<CNSInfo*>(DeductionFailure.Data)->Satisfaction);
+ return;
+ }
case Sema::TDK_TooManyArguments:
case Sema::TDK_TooFewArguments:
DiagnoseArityMismatch(S, Found, Templated, NumArgs);
@@ -10522,15 +10566,18 @@
case Sema::TDK_CUDATargetMismatch:
return 3;
- case Sema::TDK_InstantiationDepth:
+ case Sema::TDK_ConstraintsNotSatisfied:
return 4;
- case Sema::TDK_InvalidExplicitArguments:
+ case Sema::TDK_InstantiationDepth:
return 5;
+ case Sema::TDK_InvalidExplicitArguments:
+ return 6;
+
case Sema::TDK_TooManyArguments:
case Sema::TDK_TooFewArguments:
- return 6;
+ return 7;
}
llvm_unreachable("Unhandled deduction result");
}
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -13977,8 +13977,16 @@
std::string InnerCondDescription;
std::tie(InnerCond, InnerCondDescription) =
findFailedBooleanCondition(Converted.get());
- if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond)
- && !isa<IntegerLiteral>(InnerCond)) {
+ if (InnerCond && isa<ConceptSpecializationExpr>(InnerCond)) {
+ // Drill down into concept specialization expressions to see why they
+ // weren't satisfied.
+ Diag(StaticAssertLoc, diag::err_static_assert_failed)
+ << !AssertMessage << Msg.str() << AssertExpr->getSourceRange();
+ ConstraintSatisfaction Satisfaction;
+ if (!CheckConstraintSatisfaction(InnerCond, Satisfaction))
+ DiagnoseUnsatisfiedConstraint(Satisfaction);
+ } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond)
+ && !isa<IntegerLiteral>(InnerCond)) {
Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed)
<< InnerCondDescription << !AssertMessage
<< Msg.str() << InnerCond->getSourceRange();
Index: lib/Sema/SemaConcept.cpp
===================================================================
--- lib/Sema/SemaConcept.cpp
+++ lib/Sema/SemaConcept.cpp
@@ -12,10 +12,13 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/Template.h"
#include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerUnion.h"
using namespace clang;
using namespace sema;
@@ -43,80 +46,350 @@
return true;
}
-bool
-Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
- MultiLevelTemplateArgumentList &MLTAL,
- Expr *ConstraintExpr,
- bool &IsSatisfied) {
+template <typename AtomicEvaluator>
+static bool
+calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
+ ConstraintSatisfaction &Satisfaction,
+ AtomicEvaluator &&Evaluator) {
if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
- if (BO->getOpcode() == BO_LAnd) {
- if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
- IsSatisfied))
+ if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
+ if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction,
+ Evaluator))
return true;
- if (!IsSatisfied)
+
+ bool IsLHSSatisfied = Satisfaction.IsSatisfied;
+
+ if (BO->getOpcode() == BO_LOr && IsLHSSatisfied)
+ // [temp.constr.op] p3
+ // A disjunction is a constraint taking two operands. To determine if
+ // a disjunction is satisfied, the satisfaction of the first operand
+ // is checked. If that is satisfied, the disjunction is satisfied.
+ // Otherwise, the disjunction is satisfied if and only if the second
+ // operand is satisfied.
return false;
- return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
- IsSatisfied);
- } else if (BO->getOpcode() == BO_LOr) {
- if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
- IsSatisfied))
- return true;
- if (IsSatisfied)
+
+ if (BO->getOpcode() == BO_LAnd && !IsLHSSatisfied)
+ // [temp.constr.op] p2
+ // A conjunction is a constraint taking two operands. To determine if
+ // a conjunction is satisfied, the satisfaction of the first operand
+ // is checked. If that is not satisfied, the conjunction is not
+ // satisfied. Otherwise, the conjunction is satisfied if and only if
+ // the second operand is satisfied.
return false;
- return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
- IsSatisfied);
+
+ return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction,
+ std::forward<AtomicEvaluator>(Evaluator));
}
} else if (auto *PO = dyn_cast<ParenExpr>(ConstraintExpr))
- return CalculateConstraintSatisfaction(NamedConcept, MLTAL,
- PO->getSubExpr(), IsSatisfied);
+ return calculateConstraintSatisfaction(S, PO->getSubExpr(), Satisfaction,
+ std::forward<AtomicEvaluator>(Evaluator));
else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
- return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),
- IsSatisfied);
-
- EnterExpressionEvaluationContext ConstantEvaluated(
- *this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
-
- // Atomic constraint - substitute arguments and check satisfaction.
- ExprResult E;
- {
- TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc());
- InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(),
- InstantiatingTemplate::ConstraintSubstitution{},
- NamedConcept, Info,
- ConstraintExpr->getSourceRange());
- if (Inst.isInvalid())
- return true;
- // We do not want error diagnostics escaping here.
- Sema::SFINAETrap Trap(*this);
+ return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
+ std::forward<AtomicEvaluator>(Evaluator));
- E = SubstExpr(ConstraintExpr, MLTAL);
- if (E.isInvalid() || Trap.hasErrorOccurred()) {
- // C++2a [temp.constr.atomic]p1
- // ...If substitution results in an invalid type or expression, the
- // constraint is not satisfied.
- IsSatisfied = false;
- return false;
- }
- }
+ // An atomic constraint expression
+ ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
- if (!CheckConstraintExpression(E.get()))
+ if (SubstitutedAtomicExpr.isInvalid())
return true;
+ if (!SubstitutedAtomicExpr.isUsable())
+ // Evaluator has decided satisfaction without yielding an expression.
+ return false;
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
Expr::EvalResult EvalResult;
EvalResult.Diag = &EvaluationDiags;
- if (!E.get()->EvaluateAsRValue(EvalResult, Context)) {
- // C++2a [temp.constr.atomic]p1
- // ...E shall be a constant expression of type bool.
- Diag(E.get()->getBeginLoc(),
- diag::err_non_constant_constraint_expression)
- << E.get()->getSourceRange();
+ if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) {
+ // C++2a [temp.constr.atomic]p1
+ // ...E shall be a constant expression of type bool.
+ S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(),
+ diag::err_non_constant_constraint_expression)
+ << SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
- Diag(PDiag.first, PDiag.second);
+ S.Diag(PDiag.first, PDiag.second);
return true;
}
- IsSatisfied = EvalResult.Val.getInt().getBoolValue();
+ Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue();
+ if (!Satisfaction.IsSatisfied)
+ Satisfaction.Details.emplace_back(ConstraintExpr,
+ SubstitutedAtomicExpr.get());
return false;
+}
+
+template <typename TemplateDeclT>
+static bool calculateConstraintSatisfaction(
+ Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs,
+ SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
+ const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
+ return calculateConstraintSatisfaction(
+ S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ // Atomic constraint - substitute arguments and check satisfaction.
+ ExprResult SubstitutedExpression;
+ {
+ TemplateDeductionInfo Info(TemplateNameLoc);
+ Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
+ Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template,
+ Info, AtomicExpr->getSourceRange());
+ if (Inst.isInvalid())
+ return ExprError();
+ // We do not want error diagnostics escaping here.
+ Sema::SFINAETrap Trap(S);
+ SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
+ MLTAL);
+ if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {
+ // C++2a [temp.constr.atomic]p1
+ // ...If substitution results in an invalid type or expression, the
+ // constraint is not satisfied.
+ if (!Trap.hasErrorOccurred())
+ // A non-SFINAE error has occured as a result of this
+ // substitution.
+ return ExprError();
+
+ PartialDiagnosticAt SubstDiag{SourceLocation(),
+ PartialDiagnostic::NullDiagnostic()};
+ Info.takeSFINAEDiagnostic(SubstDiag);
+ SmallString<128> DiagString;
+ DiagString = ": ";
+ SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
+ Satisfaction.Details.emplace_back(
+ AtomicExpr,
+ new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+ SubstDiag.first,
+ std::string(DiagString.begin(), DiagString.end())});
+ Satisfaction.IsSatisfied = false;
+ return ExprEmpty();
+ }
+ }
+
+ if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
+ return ExprError();
+
+ return SubstitutedExpression;
+ });
+}
+
+template<typename TemplateDeclT>
+static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction) {
+ if (ConstraintExprs.empty()) {
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+
+ for (auto& Arg : TemplateArgs)
+ if (Arg.isInstantiationDependent()) {
+ // No need to check satisfaction for dependent constraint expressions.
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+
+ Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,
+ TemplateIDRange);
+ if (Inst.isInvalid())
+ return true;
+
+ MultiLevelTemplateArgumentList MLTAL;
+ MLTAL.addOuterTemplateArguments(TemplateArgs);
+
+ for (const Expr *ConstraintExpr : ConstraintExprs) {
+ if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
+ TemplateIDRange.getBegin(), MLTAL,
+ ConstraintExpr, Satisfaction))
+ return true;
+ if (!Satisfaction.IsSatisfied)
+ // [temp.constr.op] p2
+ // [...] To determine if a conjunction is satisfied, the satisfaction
+ // of the first operand is checked. If that is not satisfied, the
+ // conjunction is not satisfied. [...]
+ return false;
+ }
+ return false;
+}
+
+bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction) {
+ return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
+ TemplateArgs, TemplateIDRange,
+ Satisfaction);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction) {
+ return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs,
+ TemplateArgs, TemplateIDRange,
+ Satisfaction);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction) {
+ return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs,
+ TemplateArgs, TemplateIDRange,
+ Satisfaction);
+}
+
+bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
+ ConstraintSatisfaction &Satisfaction) {
+ return calculateConstraintSatisfaction(
+ *this, ConstraintExpr, Satisfaction,
+ [](const Expr *AtomicExpr) -> ExprResult {
+ return ExprResult(const_cast<Expr *>(AtomicExpr));
+ });
+}
+
+bool Sema::EnsureTemplateArgumentListConstraints(
+ TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange) {
+ ConstraintSatisfaction Satisfaction;
+ llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
+ TD->getAssociatedConstraints(AssociatedConstraints);
+ if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs,
+ TemplateIDRange, Satisfaction))
+ return true;
+
+ if (!Satisfaction.IsSatisfied) {
+ SmallString<128> TemplateArgString;
+ TemplateArgString = " ";
+ TemplateArgString += getTemplateArgumentBindingsText(
+ TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
+
+ Diag(TemplateIDRange.getBegin(),
+ diag::err_template_arg_list_constraints_not_satisfied)
+ << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD
+ << TemplateArgString << TemplateIDRange;
+ DiagnoseUnsatisfiedConstraint(Satisfaction);
+ return true;
+ }
+ return false;
+}
+
+static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
+ Expr *SubstExpr,
+ bool First = true) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
+ switch (BO->getOpcode()) {
+ // These two cases will in practice only be reached when using fold
+ // expressions with || and &&, since otherwise the || and && will have been
+ // broken down into atomic constraints during satisfaction checking.
+ case BO_LOr:
+ // Or evaluated to false - meaning both RHS and LHS evaluated to false.
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
+ /*First=*/false);
+ return;
+ case BO_LAnd:
+ bool LHSSatisfied;
+ BO->getLHS()->EvaluateAsBooleanCondition(LHSSatisfied, S.Context);
+ if (LHSSatisfied) {
+ // LHS is true, so RHS must be false.
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), First);
+ return;
+ }
+ // LHS is false
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
+
+ // RHS might also be false
+ bool RHSSatisfied;
+ BO->getRHS()->EvaluateAsBooleanCondition(RHSSatisfied, S.Context);
+ if (!RHSSatisfied)
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
+ /*First=*/false);
+ return;
+ case BO_GE:
+ case BO_LE:
+ case BO_GT:
+ case BO_LT:
+ case BO_EQ:
+ case BO_NE:
+ if (BO->getLHS()->getType()->isIntegerType() &&
+ BO->getRHS()->getType()->isIntegerType()) {
+ Expr::EvalResult SimplifiedLHS;
+ Expr::EvalResult SimplifiedRHS;
+ BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);
+ BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);
+ if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {
+ S.Diag(SubstExpr->getBeginLoc(),
+ diag::note_atomic_constraint_evaluated_to_false_elaborated)
+ << (int)First << SubstExpr
+ << SimplifiedLHS.Val.getInt().toString(10)
+ << BinaryOperator::getOpcodeStr(BO->getOpcode())
+ << SimplifiedRHS.Val.getInt().toString(10);
+ return;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (ParenExpr *PE = dyn_cast<ParenExpr>(SubstExpr)) {
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, PE->getSubExpr(), First);
+ return;
+ } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
+ if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+ S.Diag(
+ CSE->getSourceRange().getBegin(),
+ diag::
+ note_single_arg_concept_specialization_constraint_evaluated_to_false)
+ << (int)First
+ << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+ << CSE->getNamedConcept();
+ } else {
+ S.Diag(SubstExpr->getSourceRange().getBegin(),
+ diag::note_concept_specialization_constraint_evaluated_to_false)
+ << (int)First << CSE;
+ }
+ S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
+ return;
+ }
+
+ S.Diag(SubstExpr->getSourceRange().getBegin(),
+ diag::note_atomic_constraint_evaluated_to_false)
+ << (int)First << SubstExpr;
+}
+
+static void diagnoseUnsatisfiedConstraintExpr(
+ Sema &S, const Expr *E, ConstraintSatisfaction::Detail Detail,
+ bool First = true) {
+ if (auto *Diag =
+ Detail.dyn_cast<ConstraintSatisfaction::SubstitutionDiagnostic *>()) {
+ S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
+ << Diag->second;
+ return;
+ }
+
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, Detail.get<Expr *>(), First);
+}
+
+void Sema::DiagnoseUnsatisfiedConstraint(
+ const ConstraintSatisfaction& Satisfaction) {
+ assert(!Satisfaction.IsSatisfied &&
+ "Attempted to diagnose a satisfied constraint");
+ bool First = true;
+ for (auto &Pair : Satisfaction.Details) {
+ diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
+ First = false;
+ }
}
\ No newline at end of file
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -964,7 +964,7 @@
assert(!AdditionalAbiTags &&
"template template param cannot have abi tags");
mangleTemplateParameter(TTP->getIndex());
- } else if (isa<BuiltinTemplateDecl>(ND)) {
+ } else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
mangleUnscopedName(ND, AdditionalAbiTags);
} else {
mangleUnscopedName(ND->getTemplatedDecl(), AdditionalAbiTags);
@@ -1832,7 +1832,7 @@
mangleTemplateParameter(TTP->getIndex());
} else {
manglePrefix(getEffectiveDeclContext(ND), NoFunction);
- if (isa<BuiltinTemplateDecl>(ND))
+ if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
mangleUnqualifiedName(ND, nullptr);
else
mangleUnqualifiedName(ND->getTemplatedDecl(), nullptr);
@@ -3557,7 +3557,6 @@
case Expr::ConvertVectorExprClass:
case Expr::StmtExprClass:
case Expr::TypeTraitExprClass:
- case Expr::ConceptSpecializationExprClass:
case Expr::ArrayTypeTraitExprClass:
case Expr::ExpressionTraitExprClass:
case Expr::VAArgExprClass:
@@ -4064,6 +4063,18 @@
mangleExpression(cast<ParenExpr>(E)->getSubExpr(), Arity);
break;
+
+ case Expr::ConceptSpecializationExprClass: {
+ // <expr-primary> ::= L <mangled-name> E # external name
+ Out << "L_Z";
+ auto *CSE = cast<ConceptSpecializationExpr>(E);
+ mangleTemplateName(CSE->getNamedConcept(),
+ CSE->getTemplateArguments().data(),
+ CSE->getTemplateArguments().size());
+ Out << 'E';
+ break;
+ }
+
case Expr::DeclRefExprClass: {
const NamedDecl *D = cast<DeclRefExpr>(E)->getDecl();
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -1665,7 +1665,8 @@
NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
- ArrayRef<TemplateArgument> ConvertedArgs, bool IsSatisfied)
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ ConstraintSatisfaction Satisfaction)
: Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
/*TypeDependent=*/false,
// All the flags below are set in setTemplateArguments.
@@ -1673,8 +1674,8 @@
/*ContainsUnexpandedParameterPacks=*/false),
NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),
- NamedConcept(NamedConcept, IsSatisfied),
- NumTemplateArgs(ConvertedArgs.size()) {
+ NamedConcept(NamedConcept), NumTemplateArgs(ConvertedArgs.size()),
+ Satisfaction(std::move(Satisfaction)) {
setTemplateArguments(ArgsAsWritten, ConvertedArgs);
}
@@ -1724,13 +1725,14 @@
ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
- bool IsSatisfied) {
+ ConstraintSatisfaction Satisfaction) {
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
ConvertedArgs.size()));
return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
ConceptNameLoc, FoundDecl,
NamedConcept, ArgsAsWritten,
- ConvertedArgs, IsSatisfied);
+ ConvertedArgs,
+ std::move(Satisfaction));
}
ConceptSpecializationExpr *
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h
+++ include/clang/Sema/TemplateDeduction.h
@@ -14,6 +14,8 @@
#ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
#define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
+#include "clang/Sema/SemaConcept.h"
+#include "clang/Sema/Ownership.h"
#include "clang/AST/DeclAccessPair.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/TemplateBase.h"
@@ -218,6 +220,10 @@
///
/// FIXME: This should be kept internal to SemaTemplateDeduction.
SmallVector<DeducedPack *, 8> PendingDeducedPacks;
+
+ /// \brief The constraint satisfaction details resulting from the associated
+ /// constraints satisfaction tests.
+ ConstraintSatisfaction AssociatedConstraintsSatisfaction;
};
} // namespace sema
Index: include/clang/Sema/SemaConcept.h
===================================================================
--- /dev/null
+++ include/clang/Sema/SemaConcept.h
@@ -0,0 +1,43 @@
+//===--- SemaConcept.h - Concept Utilities ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides some common utilities for processing constraints
+/// and concepts.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SEMA_SEMACONCEPTS_H
+#define LLVM_CLANG_SEMA_SEMACONCEPTS_H
+#include "clang/AST/Expr.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include <string>
+#include <utility>
+namespace clang {
+class Sema;
+/// \brief The result of a constraint satisfaction check, containing the
+/// necessary information to diagnose an unsatisfied constraint.
+struct ConstraintSatisfaction {
+ using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
+ using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;
+
+ bool IsSatisfied = false;
+
+ /// \brief Pairs of unsatisfied atomic constraint expressions along with the
+ /// substituted constraint expr, if the template arguments could be
+ /// substituted into them, or a diagnostic if substitution resulted in an
+ /// invalid expression.
+ llvm::SmallVector<std::pair<const Expr *, Detail>, 4> Details;
+};
+
+} // clang
+
+#endif
\ No newline at end of file
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -37,6 +37,7 @@
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TypeTraits.h"
+#include "clang/Sema/SemaConcept.h"
#include "clang/Sema/AnalysisBasedWarnings.h"
#include "clang/Sema/CleanupInfo.h"
#include "clang/Sema/DeclSpec.h"
@@ -5764,10 +5765,45 @@
/// A diagnostic is emitted if it is not, and false is returned.
bool CheckConstraintExpression(Expr *CE);
- bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
- MultiLevelTemplateArgumentList &MLTAL,
- Expr *ConstraintExpr,
- bool &IsSatisfied);
+ /// \brief Check whether the given list of constraint expressions are
+ /// satisfied (as if in a 'conjunction') given template arguments.
+ /// \param ConstraintExprs a list of constraint expressions, treated as if
+ /// they were 'AND'ed together.
+ /// \param TemplateArgs the list of template arguments to substitute into the
+ /// constraint expression.
+ /// \param TemplateIDRange The source range of the template id that
+ /// caused the constraints check.
+ /// \param Satisfaction if true is returned, will contain details of the
+ /// satisfaction, with enough information to diagnose an unsatisfied
+ /// expression.
+ /// \returns true if an error occurred and satisfaction could not be checked,
+ /// false otherwise.
+ bool CheckConstraintSatisfaction(TemplateDecl *Template,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction);
+
+ bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction);
+
+ bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,
+ ArrayRef<const Expr *> ConstraintExprs,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange,
+ ConstraintSatisfaction &Satisfaction);
+
+ /// \brief Check whether the given non-dependent constraint expression is
+ /// satisfied. Returns false and updates Satisfaction with the satisfaction
+ /// verdict if successful, emits a diagnostic and returns true if an error
+ /// occured and satisfaction could not be determined.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,
+ ConstraintSatisfaction &Satisfaction);
/// Check that the associated constraints of a template declaration match the
/// associated constraints of an older declaration of which it is a
@@ -5775,6 +5811,33 @@
bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old,
TemplateParameterList *New);
+ /// \brief Ensure that the given template arguments satisfy the constraints
+ /// associated with the given template, emitting a diagnostic if they do not.
+ ///
+ /// \param Template The template to which the template arguments are being
+ /// provided.
+ ///
+ /// \param TemplateArgs The converted, canonicalized template arguments.
+ ///
+ /// \param TemplateIDRange The source range of the template id that
+ /// caused the constraints check.
+ ///
+ /// \returns true if the constrains are not satisfied or could not be checked
+ /// for satisfaction, false if the constraints are satisfied.
+ bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange TemplateIDRange);
+
+ /// \brief Emit diagnostics explaining why a constraint expression was deemed
+ /// unsatisfied.
+ void
+ DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction);
+
+ /// \brief Emit diagnostics explaining why a constraint expression was deemed
+ /// unsatisfied because it was ill-formed.
+ void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation,
+ StringRef Diagnostic);
+
// ParseObjCStringLiteral - Parse Objective-C string literals.
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
ArrayRef<Expr *> Strings);
@@ -6553,13 +6616,19 @@
/// contain the converted forms of the template arguments as written.
/// Otherwise, \p TemplateArgs will not be modified.
///
+ /// \param InstantiationDependent If provided, and no error occured, will
+ /// receive true if the arguments' match to the given template is
+ /// instantiation dependant - e.g. the arguments contain a pack expansion
+ /// into a non pack parameter.
+ ///
/// \returns true if an error occurred, false otherwise.
bool CheckTemplateArgumentList(TemplateDecl *Template,
SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs,
bool PartialTemplateArgs,
SmallVectorImpl<TemplateArgument> &Converted,
- bool UpdateArgsWithConversions = true);
+ bool UpdateArgsWithConversions = true,
+ bool *InstantiationDependent = nullptr);
bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
TemplateArgumentLoc &Arg,
@@ -7101,6 +7170,9 @@
TDK_InvalidExplicitArguments,
/// Checking non-dependent argument conversions failed.
TDK_NonDependentConversionFailure,
+ /// The deduced arguments did not satisfy the constraints associated
+ /// with the template.
+ TDK_ConstraintsNotSatisfied,
/// Deduction failed; that's all we know.
TDK_MiscellaneousDeductionFailure,
/// CUDA Target attributes do not match.
@@ -7606,7 +7678,7 @@
/// constrained entity (a concept declaration or a template with associated
/// constraints).
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- ConstraintsCheck, TemplateDecl *Template,
+ ConstraintsCheck, NamedDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange);
@@ -7615,7 +7687,7 @@
/// with a template declaration or as part of the satisfaction check of a
/// concept.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- ConstraintSubstitution, TemplateDecl *Template,
+ ConstraintSubstitution, NamedDecl *Template,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange);
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2450,6 +2450,26 @@
"expression">;
def err_non_bool_atomic_constraint : Error<
"atomic constraint must be of type 'bool' (found %0)">;
+def err_template_arg_list_constraints_not_satisfied : Error<
+ "constraints not satisfied for %select{class template|function template|variable template|alias template|"
+ "template template parameter|template}0 %1%2">;
+def note_constraints_not_satisfied : Note<
+ "constraints not satisfied">;
+def note_substituted_constraint_expr_is_ill_formed : Note<
+ "because substituted constraint expression is ill-formed%0">;
+def note_atomic_constraint_evaluated_to_false : Note<
+ "%select{and |because }0'%1' evaluated to false">;
+def note_concept_specialization_constraint_evaluated_to_false : Note<
+ "%select{and |because }0'%1' evaluated to false">;
+def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
+ "%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 err_template_different_requires_clause : Error<
"requires clause differs in template redeclaration">;
@@ -3653,6 +3673,8 @@
def note_ovl_candidate_explicit_arg_mismatch_named : Note<
"candidate template ignored: invalid explicitly-specified argument "
"for template parameter %0">;
+def note_ovl_candidate_unsatisfied_constraints : Note<
+ "candidate template ignored: constraints not satisfied%0">;
def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note<
"candidate template ignored: invalid explicitly-specified argument "
"for %ordinal0 template parameter">;
@@ -4327,6 +4349,14 @@
"while checking a default template argument used here">;
def note_concept_specialization_here : Note<
"while checking the satisfaction of concept '%0' requested here">;
+def note_checking_constraints_for_template_id_here : Note<
+ "while checking constraint satisfaction for template '%0' required here">;
+def note_checking_constraints_for_var_spec_id_here : Note<
+ "while checking constraint satisfaction for variable template "
+ "partial specialization '%0' required here">;
+def note_checking_constraints_for_class_spec_id_here : Note<
+ "while checking constraint satisfaction for class template partial "
+ "specialization '%0' required here">;
def note_constraint_substitution_here : Note<
"while substituting template arguments into constraint expression here">;
def note_instantiation_contexts_suppressed : Note<
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H
+#include "clang/Sema/SemaConcept.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -4533,6 +4534,10 @@
TemplateArgument> {
friend class ASTStmtReader;
friend TrailingObjects;
+public:
+ using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
+
+protected:
// \brief The optional nested name specifier used when naming the concept.
NestedNameSpecifierLoc NestedNameSpec;
@@ -4550,11 +4555,8 @@
/// through a UsingShadowDecl.
NamedDecl *FoundDecl;
- /// \brief The concept named, and whether or not the concept with the given
- /// arguments was satisfied when the expression was created.
- /// If any of the template arguments are dependent (this expr would then be
- /// isValueDependent()), this bit is to be ignored.
- llvm::PointerIntPair<ConceptDecl *, 1, bool> NamedConcept;
+ /// \brief The concept named.
+ ConceptDecl *NamedConcept;
/// \brief The template argument list source info used to specialize the
/// concept.
@@ -4564,13 +4566,18 @@
/// converted template arguments.
unsigned NumTemplateArgs;
+ /// \brief Information about the satisfaction of the named concept with the
+ /// given arguments. If this expression is value dependent, this is to be
+ /// ignored.
+ ConstraintSatisfaction Satisfaction;
+
ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc,
SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
- bool IsSatisfied);
+ ConstraintSatisfaction Satisfaction);
ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
@@ -4581,7 +4588,8 @@
SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
- ArrayRef<TemplateArgument> ConvertedArgs, bool IsSatisfied);
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ ConstraintSatisfaction Satisfaction);
static ConceptSpecializationExpr *
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
@@ -4595,7 +4603,7 @@
}
ConceptDecl *getNamedConcept() const {
- return NamedConcept.getPointer();
+ return NamedConcept;
}
ArrayRef<TemplateArgument> getTemplateArguments() const {
@@ -4612,12 +4620,25 @@
ArrayRef<TemplateArgument> Converted);
/// \brief Whether or not the concept with the given arguments was satisfied
- /// when the expression was created. This method assumes that the expression
- /// is not dependent!
+ /// when the expression was created.
+ /// The expression must not be dependent.
bool isSatisfied() const {
assert(!isValueDependent()
&& "isSatisfied called on a dependent ConceptSpecializationExpr");
- return NamedConcept.getInt();
+ return Satisfaction.IsSatisfied;
+ }
+
+ /// \brief Get elaborated satisfaction info about the template arguments'
+ /// satisfaction of the named concept.
+ /// The expression must not be dependent.
+ const ConstraintSatisfaction &getSatisfaction() const {
+ assert(!isValueDependent()
+ && "getSatisfaction called on dependent ConceptSpecializationExpr");
+ return Satisfaction;
+ }
+
+ void setSatisfaction(ConstraintSatisfaction Satisfaction) {
+ this->Satisfaction = std::move(Satisfaction);
}
SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits