saar.raz updated this revision to Diff 141248.
saar.raz added a comment.

Adjusted to piecewise substitution.

- Constraint satisfaction will no longer happen for depenent CSEs (was 
originally needed for normalization, but not worth the trouble with the new 
piecewise substitution and the fact that it prevents short-circuting)
- Constraint satisfaction checking now breaks down unsatisfied constraint exprs 
into atomic constraints with the ill-formed diagnostic or the substituted 
constraint expression for each, later consumed by the diagnostic functions.


Repository:
  rC Clang

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/Sema/SemaConcept.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/SemaTemplate.cpp
  lib/Sema/SemaTemplateDeduction.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

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,28 @@
+// 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]}}
+}
+
+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*>);
+}
\ 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,79 @@
+// 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}}
\ 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,39 @@
+// 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 {{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 {{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{{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}} expected-note {{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
@@ -26,7 +26,7 @@
 template<typename U>
 concept C4 = U::add(1, 2) == 3; // expected-error {{substitution into constraint expression resulted in a non-constant expression 'B::add(1, 2) == 3'}}
 static_assert(C4<A>);
-static_assert(!C4<B>); // expected-note {{in concept specialization 'C4<B>'}}
+static bool X = C4<B>; // expected-note {{in concept specialization 'C4<B>'}}
 
 template<typename T, typename U>
 constexpr bool is_same_v = false;
@@ -68,6 +68,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;
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -356,7 +356,25 @@
   for (unsigned i = 0; i < TALI->size(); ++i) {
     Record.AddTemplateArgumentLoc(TALI->getArgumentArray()[i]);
   }
-  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
@@ -623,7 +623,25 @@
   for (unsigned i = 0; i != NumTemplateArgs; ++i)
     ArgInfo.addArgument(Record.readTemplateArgumentLoc());
   E->setTemplateArguments(/*Sema=*/nullptr, &ArgInfo);
-  E->setSatisfied(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
@@ -2903,12 +2903,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
@@ -3025,11 +3034,18 @@
     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->getLocStart(),
           const_cast<TemplateArgumentListInfo &>(VarTemplateArgsInfo), false,
-          Converted))
+          Converted, /*UpdateArgsWithConversion=*/true,
+          &InstantiationDependent))
+    return nullptr;
+
+  if (!InstantiationDependent
+      && SemaRef.EnsureTemplateArgumentListConstraints(VarTemplate, Converted,
+                                                    VarTemplate->getLocStart()))
     return nullptr;
 
   // Find the variable template specialization declaration that
Index: lib/Sema/SemaTemplateDeduction.cpp
===================================================================
--- lib/Sema/SemaTemplateDeduction.cpp
+++ lib/Sema/SemaTemplateDeduction.cpp
@@ -2658,6 +2658,21 @@
   return Sema::TDK_Success;
 }
 
+template<typename TemplateDeclT>
+static Sema::TemplateDeductionResult
+CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
+                                ArrayRef<TemplateArgument> DeducedArgs,
+                                TemplateDeductionInfo& Info) {
+  if (S.CheckConstraintSatisfaction(Template,
+                                    Template->getAssociatedConstraints(),
+                                    DeducedArgs, Info.getLocation(),
+                                    Info.AssociatedConstraintsSatisfaction)
+      || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+    Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));
+    return Sema::TDK_ConstraintsNotSatisfied;
+  }
+  return Sema::TDK_Success;
+}
 
 /// \brief Perform template argument deduction to determine whether
 /// the given template arguments match the given class template
@@ -2698,6 +2713,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);
 }
@@ -2739,6 +2758,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);
 }
@@ -2848,6 +2871,12 @@
     = TemplateArgumentList::CreateCopy(Context, Builder);
   Info.reset(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
@@ -3155,6 +3184,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,
@@ -4911,6 +4945,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
@@ -2892,8 +2892,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())
@@ -2907,16 +2906,25 @@
 
   // 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,
+                                               TemplateLoc))
     return QualType();
 
   QualType CanonType;
 
   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())
@@ -3502,9 +3510,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,
+                                               TemplateNameLoc))
     return true;
 
   // Find the variable template (partial) specialization declaration that
@@ -3678,11 +3694,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,
+                                               TemplateNameLoc))
     return true;
 
   // Find the variable template specialization declaration that
@@ -3896,7 +3919,7 @@
                               TemplateKWLoc, TemplateArgs);
   }
 
-  if (R.getAsSingle<ConceptDecl>() && !DependentArguments) {
+  if (R.getAsSingle<ConceptDecl>()) {
     return CheckConceptTemplateId(SS, R.getLookupNameInfo(),
                                   R.getAsSingle<ConceptDecl>(),
                                   TemplateKWLoc, TemplateArgs);
@@ -4788,7 +4811,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.
@@ -4892,6 +4916,10 @@
           ++ArgIdx;
         }
 
+        // We cannot determine yet whether the arguments are well-formed.
+        if (InstantiationDependent)
+          *InstantiationDependent = true;
+
         return false;
       }
 
@@ -4904,6 +4932,8 @@
         Converted.push_back(
             TemplateArgument::CreatePackCopy(Context, ArgumentPack));
 
+      if (InstantiationDependent)
+        *InstantiationDependent = false;
       return false;
     }
 
@@ -5034,6 +5064,9 @@
   if (UpdateArgsWithConversions)
     TemplateArgs = std::move(NewArgs);
 
+  if (InstantiationDependent)
+    *InstantiationDependent = false;
+
   return false;
 }
 
@@ -7397,9 +7430,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,
+                                               TemplateNameLoc))
     return true;
 
   // Find the class template (partial) specialization declaration that
@@ -8573,10 +8614,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,
+                                               TemplateNameLoc)) {
     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
@@ -578,6 +578,10 @@
     TemplateArgumentList *TemplateArgs;
     unsigned CallArgIndex;
   };
+  struct CNSInfo {
+    TemplateArgumentList *TemplateArgs;
+    ConstraintSatisfaction Satisfaction;
+  };
 }
 
 /// \brief Convert from Sema's representation of template deduction information
@@ -646,6 +650,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");
@@ -685,6 +697,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;
@@ -710,6 +731,7 @@
   case Sema::TDK_NonDeducedMismatch:
   case Sema::TDK_CUDATargetMismatch:
   case Sema::TDK_NonDependentConversionFailure:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return TemplateParameter();
 
   case Sema::TDK_Incomplete:
@@ -751,6 +773,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;
@@ -771,6 +796,7 @@
   case Sema::TDK_SubstitutionFailure:
   case Sema::TDK_CUDATargetMismatch:
   case Sema::TDK_NonDependentConversionFailure:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return nullptr;
 
   case Sema::TDK_Inconsistent:
@@ -800,6 +826,7 @@
   case Sema::TDK_SubstitutionFailure:
   case Sema::TDK_CUDATargetMismatch:
   case Sema::TDK_NonDependentConversionFailure:
+  case Sema::TDK_ConstraintsNotSatisfied:
     return nullptr;
 
   case Sema::TDK_Inconsistent:
@@ -1178,6 +1205,8 @@
     return NewTarget != OldTarget;
   }
 
+  // TODO: Concepts: Check function trailing requires clauses here.
+
   // The signatures match; this is not an overload.
   return false;
 }
@@ -9865,6 +9894,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);
@@ -10271,15 +10315,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/SemaConcept.cpp
===================================================================
--- lib/Sema/SemaConcept.cpp
+++ lib/Sema/SemaConcept.cpp
@@ -11,7 +11,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/Template.h"
 #include "clang/Sema/SemaInternal.h"
+#include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerUnion.h"
 using namespace clang;
 using namespace sema;
 
@@ -55,4 +60,289 @@
       return true; // All good; no mismatch.
   }
   return false;
+}
+
+template<typename TemplateDeclT>
+static bool
+calculateConstraintSatisfaction(Sema& S, TemplateDeclT *Template,
+                                ArrayRef<TemplateArgument> TemplateArgs,
+                                SourceLocation TemplateNameLoc,
+                                MultiLevelTemplateArgumentList &MLTAL,
+                                const Expr *ConstraintExpr,
+                                ConstraintSatisfaction &Satisfaction) {
+  if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
+    if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
+      if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
+                                          TemplateNameLoc, MLTAL, BO->getLHS(),
+                                          Satisfaction))
+        return true;
+
+      bool IsLHSSatisfied = Satisfaction.IsSatisfied;
+
+      if (IsLHSSatisfied && BO->getOpcode() == BO_LOr)
+        // This disjunction is satisfied - diagnostic information will not be
+        // needed for RHS, no need to generate it.
+        return false;
+
+      if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
+                                          TemplateNameLoc, MLTAL, BO->getRHS(),
+                                          Satisfaction))
+        return true;
+
+      if (BO->getOpcode() == BO_LAnd)
+        Satisfaction.IsSatisfied &= IsLHSSatisfied;
+      else
+        Satisfaction.IsSatisfied |= IsLHSSatisfied;
+
+      return false;
+    }
+  } else if (auto *PO = dyn_cast<ParenExpr>(ConstraintExpr))
+    return calculateConstraintSatisfaction(S, Template, TemplateArgs,
+                                        TemplateNameLoc, MLTAL,
+                                        PO->getSubExpr(), Satisfaction);
+
+  EnterExpressionEvaluationContext ConstantEvaluated(
+          S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+  // Atomic constraint - substitute arguments and check satisfaction.
+  ExprResult SubstitutedExpression;
+  {
+    TemplateDeductionInfo Info(TemplateNameLoc);
+    Sema::InstantiatingTemplate Inst(S, TemplateNameLoc, Template, TemplateArgs,
+                                     Info);
+    // We do not want error diagnostics escaping here.
+    Sema::SFINAETrap Trap(S);
+    SubstitutedExpression = S.SubstExpr(const_cast<Expr*>(ConstraintExpr),
+                                        MLTAL);
+    if (!SubstitutedExpression.isUsable() ||
+        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()) {
+        PartialDiagnosticAt SubstDiag{ SourceLocation(),
+                                       PartialDiagnostic::NullDiagnostic() };
+        Info.takeSFINAEDiagnostic(SubstDiag);
+        SmallString<128> DiagString;
+        DiagString = ": ";
+        SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
+        Satisfaction.Details.emplace_back(
+            ConstraintExpr,
+            new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+                    SubstDiag.first,
+                    std::string(DiagString.begin(), DiagString.end())});
+      }
+      Satisfaction.IsSatisfied = false;
+      return false;
+    }
+  }
+
+  if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
+    return true;
+
+  assert(!SubstitutedExpression.get()->isInstantiationDependent() &&
+         "Instantiation dependent constraint expressions should not get here!");
+
+  if (!SubstitutedExpression.get()->EvaluateAsBooleanCondition(Satisfaction.IsSatisfied,
+                                                               S.Context)) {
+      // C++2a [temp.constr.atomic]p1
+      //   ...E shall be a constant expression of type bool.
+    S.Diag(SubstitutedExpression.get()->getLocStart(),
+           diag::err_non_constant_constraint_expression)
+        << SubstitutedExpression.get();
+    return true;
+  }
+
+  if (!Satisfaction.IsSatisfied)
+    Satisfaction.Details.emplace_back(ConstraintExpr,
+                                      SubstitutedExpression.get());
+
+  return false;
+}
+
+template<typename TemplateDeclT>
+static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
+                                        const Expr *ConstraintExpr,
+                                        ArrayRef<TemplateArgument> TemplateArgs,
+                                        SourceLocation TemplateNameLoc,
+                                        ConstraintSatisfaction &Satisfaction) {
+  if (!ConstraintExpr) {
+    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;
+    }
+
+  MultiLevelTemplateArgumentList MLTAL;
+  MLTAL.addOuterTemplateArguments(TemplateArgs);
+
+  return calculateConstraintSatisfaction(S, Template, TemplateArgs,
+                                         TemplateNameLoc, MLTAL, ConstraintExpr,
+                                         Satisfaction);
+}
+
+bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,
+                                       const Expr *ConstraintExpr,
+                                       ArrayRef<TemplateArgument> TemplateArgs,
+                                       SourceLocation TemplateNameLoc,
+                                       ConstraintSatisfaction &Satisfaction) {
+  return ::CheckConstraintSatisfaction(*this, Template, ConstraintExpr,
+                                       TemplateArgs, TemplateNameLoc,
+                                       Satisfaction);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,
+                                  const Expr *ConstraintExpr,
+                                  ArrayRef<TemplateArgument> TemplateArgs,
+                                  SourceLocation TemplateNameLoc,
+                                  ConstraintSatisfaction &Satisfaction) {
+  return ::CheckConstraintSatisfaction(*this, Part, ConstraintExpr,
+                                       TemplateArgs, TemplateNameLoc,
+                                       Satisfaction);
+}
+
+bool
+Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,
+                                  const Expr *ConstraintExpr,
+                                  ArrayRef<TemplateArgument> TemplateArgs,
+                                  SourceLocation TemplateNameLoc,
+                                  ConstraintSatisfaction &Satisfaction) {
+  return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExpr,
+                                       TemplateArgs, TemplateNameLoc,
+                                       Satisfaction);
+}
+
+bool Sema::EnsureTemplateArgumentListConstraints(
+    TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
+    SourceLocation TemplateNameLoc) {
+  ConstraintSatisfaction Satisfaction;
+  if (CheckConstraintSatisfaction(TD, TD->getAssociatedConstraints(),
+                                  TemplateArgs, TemplateNameLoc, Satisfaction))
+    return true;
+
+  if (!Satisfaction.IsSatisfied) {
+    SmallString<128> TemplateArgString;
+    TemplateArgString = " ";
+    TemplateArgString += getTemplateArgumentBindingsText(
+        TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
+
+    Diag(TemplateNameLoc, diag::err_template_arg_list_constraints_not_satisfied)
+        << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD
+        << TemplateArgString;
+    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()) {
+        llvm::APSInt SimplifiedLHS;
+        llvm::APSInt SimplifiedRHS;
+        BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);
+        BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);
+        S.Diag(SubstExpr->getSourceRange().getBegin(),
+               diag::note_atomic_constraint_evaluated_to_false_elaborated)
+            << (int)First << SubstExpr << SimplifiedLHS.toString(10)
+            << BinaryOperator::getOpcodeStr(BO->getOpcode())
+            << SimplifiedRHS.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->getTemplateArgumentListInfo()->size() == 1) {
+      S.Diag(
+          CSE->getSourceRange().getBegin(),
+          diag::
+          note_single_arg_concept_specialization_constraint_evaluated_to_false)
+          << (int)First
+          << CSE->getTemplateArgumentListInfo()->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/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -1453,91 +1453,6 @@
                                                      EmptyShell Empty)
   : Expr(ConceptSpecializationExprClass, Empty) { }
 
-
-static bool
-calculateConstraintSatisfaction(Sema& S, MultiLevelTemplateArgumentList &MLTAL,
-                                Expr *ConstraintExpr, bool &IsSatisfied) {
-  if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
-    if (BO->getOpcode() == BO_LAnd) {
-      if (calculateConstraintSatisfaction(S, MLTAL, BO->getLHS(), IsSatisfied))
-        return true;
-      if (!IsSatisfied)
-        return false;
-      return calculateConstraintSatisfaction(S, MLTAL, BO->getRHS(),
-                                             IsSatisfied);
-    } else if (BO->getOpcode() == BO_LOr) {
-      if (calculateConstraintSatisfaction(S, MLTAL, BO->getLHS(), IsSatisfied))
-        return true;
-      if (IsSatisfied)
-        return false;
-      return calculateConstraintSatisfaction(S, MLTAL, BO->getRHS(),
-                                             IsSatisfied);
-    }
-  } else if (auto *PO = dyn_cast<ParenExpr>(ConstraintExpr))
-    return calculateConstraintSatisfaction(S, MLTAL, PO->getSubExpr(),
-                                           IsSatisfied);
-
-  EnterExpressionEvaluationContext ConstantEvaluated(
-          S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
-
-  // Atomic constraint - substitute arguments and check satisfaction.
-  ExprResult E;
-  {
-    // We do not want error diagnostics escaping here.
-    Sema::SFINAETrap Trap(S);
-    E = S.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;
-    }
-  }
-
-  if (!S.CheckConstraintExpression(E.get())) {
-    return true;
-  }
-
-  if (!E.get()->EvaluateAsBooleanCondition(IsSatisfied, S.Context)) {
-      // C++2a [temp.constr.atomic]p1
-      //   ...E shall be a constant expression of type bool.
-    S.Diag(E.get()->getLocStart(),
-           diag::err_non_constant_constraint_expression) << E.get();
-    return true;
-  }
-
-  return false;
-}
-
-bool ConceptSpecializationExpr::calculateSatisfaction(Sema &S)
-{
-  llvm::SmallVector<TemplateArgument, 4> Converted;
-  if (S.CheckTemplateArgumentList(NamedConcept, NamedConcept->getLocStart(),
-                                  TemplateArgInfo,
-                                  /*PartialTemplateArgs=*/false, Converted,
-                                  /*UpdateArgsWithConversion=*/false))
-    // We converted these arguments back in CheckConceptTemplateId, this should
-    // work.
-    return true;
-
-  MultiLevelTemplateArgumentList MLTAL;
-  MLTAL.addOuterTemplateArguments(Converted);
-
-
-  bool IsSatisfied;
-  if (calculateConstraintSatisfaction(S, MLTAL,
-                                      NamedConcept->getConstraintExpr(),
-                                      IsSatisfied)) {
-    S.Diag(getLocStart(),
-           diag::note_in_concept_specialization) << this;
-    return true;
-  }
-
-  this->IsSatisfied = IsSatisfied;
-  return false;
-}
-
 bool ConceptSpecializationExpr::setTemplateArguments(Sema *S,
                                           const TemplateArgumentListInfo *TALI){
   bool IsDependent = false;
@@ -1558,7 +1473,25 @@
   setValueDependent(IsDependent);
   setInstantiationDependent(IsDependent);
   setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
-  if (!IsDependent && S)
-    return calculateSatisfaction(*S);
+  if (!S || IsDependent)
+    // !S means the expression is probably being read from a module file.
+    return false;
+
+  llvm::SmallVector<TemplateArgument, 4> Converted;
+  if (S->CheckTemplateArgumentList(NamedConcept,
+                                   NamedConcept->getLocStart(),
+                                   TemplateArgInfo,
+                                   /*PartialTemplateArgs=*/false, Converted,
+                                   /*UpdateArgsWithConversion=*/false))
+    // We converted these arguments back in CheckConceptTemplateId, this should
+    // work.
+    return true;
+
+  if (S->CheckConstraintSatisfaction(NamedConcept,
+                                     NamedConcept->getConstraintExpr(),
+                                     Converted, ConceptNameLoc, Satisfaction)) {
+    S->Diag(getLocStart(), diag::note_in_concept_specialization) << this;
+    return true;
+  }
   return false;
 }
\ No newline at end of file
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h
+++ include/clang/Sema/TemplateDeduction.h
@@ -13,6 +13,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/DeclTemplate.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "llvm/ADT/SmallVector.h"
@@ -192,6 +194,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;
 };
 
 } // end 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"
@@ -5577,15 +5578,73 @@
                                   ConceptDecl *CD,
                                   const TemplateArgumentListInfo *TALI);
 
-  /// Check whether the given expression is a valid constraint expression.
-  /// A diagnostic is emitted if it is not, and false is returned.
+  /// \brief Check whether the given expression is a valid constraint
+  /// expression. A diagnostic is emitted if it is not, and false is returned.
   bool CheckConstraintExpression(Expr *CE);
 
+
+  /// \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
+  /// an error occured and satisfaction could not be determined.
+  ///
+  /// \param SubstitutedExpr if not null, will be used to return the substituted
+  /// constraint expression (to be used for diagnostics, for example).
+  ///
+  /// \param SubstDiag if the constraint was not satisfied because of a
+  /// substitution failure, this will contain the emitted diagnostics, if any.
+  ///
+  /// \returns true if an error occurred, false otherwise.
+  bool CheckConstraintSatisfaction(TemplateDecl *Template,
+                                   const Expr *ConstraintExpr,
+                                   ArrayRef<TemplateArgument> TemplateArgs,
+                                   SourceLocation TemplateNameLoc,
+                                   ConstraintSatisfaction &Satisfaction);
+
+  bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,
+                                   const Expr *ConstraintExpr,
+                                   ArrayRef<TemplateArgument> TemplateArgs,
+                                   SourceLocation TemplateNameLoc,
+                                   ConstraintSatisfaction &Satisfaction);
+
+  bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,
+                                   const Expr *ConstraintExpr,
+                                   ArrayRef<TemplateArgument> TemplateArgs,
+                                   SourceLocation TemplateNameLoc,
+                                   ConstraintSatisfaction &Satisfaction);
+
   /// \brief Check that the associated constraints of a template declaration
   /// match the associated constraints of an older declaration of which it is a
   /// redeclaration
   bool CheckRedeclarationConstraintMatch(const Expr *OldAC, const Expr *NewAC);
 
+  /// \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 TemplateNameLoc Where was the template name that evoked this
+  /// 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,
+                                             SourceLocation TemplateNameLoc);
+
+  /// \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);
@@ -6352,13 +6411,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,
@@ -6900,6 +6965,9 @@
     TDK_InvalidExplicitArguments,
     /// \brief Checking non-dependent argument conversions failed.
     TDK_NonDependentConversionFailure,
+    /// \brief The deduced arguments did not satisfy the constraints associated
+    /// with the template.
+    TDK_ConstraintsNotSatisfied,
     /// \brief Deduction failed; that's all we know.
     TDK_MiscellaneousDeductionFailure,
     /// \brief CUDA Target attributes do not match.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2402,12 +2402,31 @@
 def err_concept_no_associated_constraints : Error<
   "concept may not have associated constraints">;
 def err_non_constant_constraint_expression : Error<
-  "substitution into constraint expression resulted in a non-constant "
-  "expression '%0'">;
+  "substitution into constraint expression resulted in a non-constant expression '%0'">;
 def err_non_bool_atomic_constraint : Error<
   "atomic constraint '%0' must be of type 'bool' (found %1)">;
 def note_in_concept_specialization : Note<
   "in concept specialization '%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_associated_constraints : Error<
   "associated constraints differ in template redeclaration">;
@@ -3541,6 +3560,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">;
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -15,6 +15,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"
@@ -4415,6 +4416,9 @@
 /// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
 /// specialization of a concept results in a prvalue of type bool.
 class ConceptSpecializationExpr final : public Expr {
+public:
+  using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
+
 protected:
   /// \brief The concept named.
   ConceptDecl *NamedConcept;
@@ -4426,16 +4430,10 @@
   /// \brief The location of the concept name in the expression.
   SourceLocation ConceptNameLoc;
 
-  /// \brief 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 is to be
+  /// \brief Information about the satisfaction of the named concept with the
+  /// given arguments. If this expression is value dependent, this is to be
   /// ignored.
-  bool IsSatisfied : 1;
-
-  /// \brief Evaluates this concept specialization to determine whether or not
-  /// the concept is satisfied, and updates the IsSatisfied field. Returns true
-  /// if an error occured and the concept could not be checked for satisfaction.
-  bool calculateSatisfaction(Sema &S);
+  ConstraintSatisfaction Satisfaction;
 
 public:
   ConceptSpecializationExpr(ASTContext &C, Sema &S,
@@ -4467,20 +4465,29 @@
   bool setTemplateArguments(Sema *S, const TemplateArgumentListInfo *TALI);
 
   /// \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 IsSatisfied;
+    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 setSatisfied(bool Satisfied) {
-    IsSatisfied = Satisfied;
+  void setSatisfaction(ConstraintSatisfaction Satisfaction) {
+    this->Satisfaction = std::move(Satisfaction);
   }
 
   SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
-  void setConceptNameLoc(SourceLocation Loc) {
+    void setConceptNameLoc(SourceLocation Loc) {
     ConceptNameLoc = Loc;
   }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to