<div dir="ltr">





<p class="gmail-p1" style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:12px;line-height:normal;font-family:"Helvetica Neue"">I’ve reverted this change as it causes the following tests to fail under check-clang when built with ASAN:</p>
<p class="gmail-p1" style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:12px;line-height:normal;font-family:"Helvetica Neue""><span class="gmail-Apple-converted-space">    </span>Clang :: CXX/expr/expr.prim/<a href="http://expr.prim.id/p3.cpp">expr.prim.id/p3.cpp</a></p>
<p class="gmail-p1" style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:12px;line-height:normal;font-family:"Helvetica Neue""><span class="gmail-Apple-converted-space">    </span>Clang :: CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp</p>
<p class="gmail-p1" style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:12px;line-height:normal;font-family:"Helvetica Neue""><span class="gmail-Apple-converted-space">    </span>Clang :: CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp</p>
<p class="gmail-p2" style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:12px;line-height:normal;font-family:"Helvetica Neue";min-height:14px"><br></p>
<p class="gmail-p3" style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:12px;line-height:normal;font-family:"Helvetica Neue";color:rgb(220,161,13)"><span class="gmail-s1" style="color:rgb(0,0,0)">You can see these failures here: <a href="http://lab.llvm.org:8014/builders/sanitizer-x86_64-linux-bootstrap/builds/124/steps/check-clang%20asan/logs/stdio"><span class="gmail-s2">http://lab.llvm.org:8014/builders/sanitizer-x86_64-linux-bootstrap/builds/124/steps/check-clang%20asan/logs/stdio</span></a></span></p></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Oct 24, 2019 at 2:33 PM Saar Raz via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Saar Raz<br>
Date: 2019-10-25T00:19:51+03:00<br>
New Revision: ffa214ef22892d75340dc6720271863901dc2c90<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/ffa214ef22892d75340dc6720271863901dc2c90" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/ffa214ef22892d75340dc6720271863901dc2c90</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/ffa214ef22892d75340dc6720271863901dc2c90.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/ffa214ef22892d75340dc6720271863901dc2c90.diff</a><br>
<br>
LOG: [Concepts] Constraint Enforcement & Diagnostics<br>
<br>
Part of the C++20 concepts implementation effort.<br>
- Associated constraints (requires clauses, currently) are now enforced when instantiating/specializing templates and when considering partial specializations and function overloads.<br>
- Elaborated diagnostics give helpful insight as to why the constraints were not satisfied.<br>
Phabricator: D41569<br>
<br>
Added: <br>
    clang/include/clang/AST/ASTConcept.h<br>
    clang/lib/AST/ASTConcept.cpp<br>
    clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp<br>
    clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp<br>
    clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp<br>
<br>
Modified: <br>
    clang/include/clang/AST/ExprCXX.h<br>
    clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
    clang/include/clang/Sema/Sema.h<br>
    clang/include/clang/Sema/TemplateDeduction.h<br>
    clang/lib/AST/ASTContext.cpp<br>
    clang/lib/AST/CMakeLists.txt<br>
    clang/lib/AST/Decl.cpp<br>
    clang/lib/AST/ExprCXX.cpp<br>
    clang/lib/Sema/SemaConcept.cpp<br>
    clang/lib/Sema/SemaDeclCXX.cpp<br>
    clang/lib/Sema/SemaOverload.cpp<br>
    clang/lib/Sema/SemaTemplate.cpp<br>
    clang/lib/Sema/SemaTemplateDeduction.cpp<br>
    clang/lib/Sema/SemaTemplateInstantiate.cpp<br>
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp<br>
    clang/lib/Serialization/ASTReaderStmt.cpp<br>
    clang/lib/Serialization/ASTWriterStmt.cpp<br>
    clang/test/CXX/expr/expr.prim/<a href="http://expr.prim.id/p3.cpp" rel="noreferrer" target="_blank">expr.prim.id/p3.cpp</a><br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h<br>
new file mode 100644<br>
index 000000000000..f5e99a8bfa1e<br>
--- /dev/null<br>
+++ b/clang/include/clang/AST/ASTConcept.h<br>
@@ -0,0 +1,80 @@<br>
+//===--- ASTConcept.h - Concepts Related AST Data Structures ----*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// \file<br>
+/// \brief This file provides AST data structures related to concepts.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLVM_CLANG_AST_ASTCONCEPT_H<br>
+#define LLVM_CLANG_AST_ASTCONCEPT_H<br>
+#include "clang/AST/Expr.h"<br>
+#include "clang/Basic/SourceLocation.h"<br>
+#include "llvm/ADT/PointerUnion.h"<br>
+#include "llvm/ADT/SmallVector.h"<br>
+#include <string><br>
+#include <utility><br>
+namespace clang {<br>
+<br>
+/// \brief The result of a constraint satisfaction check, containing the<br>
+/// necessary information to diagnose an unsatisfied constraint.<br>
+struct ConstraintSatisfaction {<br>
+  using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;<br>
+  using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;<br>
+<br>
+  bool IsSatisfied = false;<br>
+<br>
+  /// \brief Pairs of unsatisfied atomic constraint expressions along with the<br>
+  /// substituted constraint expr, if the template arguments could be<br>
+  /// substituted into them, or a diagnostic if substitution resulted in an<br>
+  /// invalid expression.<br>
+  llvm::SmallVector<std::pair<const Expr *, Detail>, 4> Details;<br>
+<br>
+  // This can leak if used in an AST node, use ASTConstraintSatisfaction<br>
+  // instead.<br>
+  void *operator new(size_t bytes, ASTContext &C) = delete;<br>
+};<br>
+<br>
+/// Pairs of unsatisfied atomic constraint expressions along with the<br>
+/// substituted constraint expr, if the template arguments could be<br>
+/// substituted into them, or a diagnostic if substitution resulted in<br>
+/// an invalid expression.<br>
+using UnsatisfiedConstraintRecord =<br>
+    std::pair<const Expr *,<br>
+              llvm::PointerUnion<Expr *,<br>
+                                 std::pair<SourceLocation, StringRef> *>>;<br>
+<br>
+/// \brief The result of a constraint satisfaction check, containing the<br>
+/// necessary information to diagnose an unsatisfied constraint.<br>
+///<br>
+/// This is safe to store in an AST node, as opposed to ConstraintSatisfaction.<br>
+struct ASTConstraintSatisfaction final :<br>
+    llvm::TrailingObjects<ASTConstraintSatisfaction,<br>
+                          UnsatisfiedConstraintRecord> {<br>
+  std::size_t NumRecords;<br>
+  bool IsSatisfied : 1;<br>
+<br>
+  const UnsatisfiedConstraintRecord *begin() const {<br>
+    return getTrailingObjects<UnsatisfiedConstraintRecord>();<br>
+  }<br>
+<br>
+  const UnsatisfiedConstraintRecord *end() const {<br>
+    return getTrailingObjects<UnsatisfiedConstraintRecord>() + NumRecords;<br>
+  }<br>
+<br>
+  ASTConstraintSatisfaction(const ASTContext &C,<br>
+                            const ConstraintSatisfaction &Satisfaction);<br>
+<br>
+  static ASTConstraintSatisfaction *<br>
+  Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction);<br>
+};<br>
+<br>
+} // clang<br>
+<br>
+#endif // LLVM_CLANG_AST_ASTCONCEPT_H<br>
\ No newline at end of file<br>
<br>
diff  --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h<br>
index 2152e108c7cb..3719f8229a36 100644<br>
--- a/clang/include/clang/AST/ExprCXX.h<br>
+++ b/clang/include/clang/AST/ExprCXX.h<br>
@@ -14,6 +14,7 @@<br>
 #ifndef LLVM_CLANG_AST_EXPRCXX_H<br>
 #define LLVM_CLANG_AST_EXPRCXX_H<br>
<br>
+#include "clang/AST/ASTConcept.h"<br>
 #include "clang/AST/Decl.h"<br>
 #include "clang/AST/DeclBase.h"<br>
 #include "clang/AST/DeclCXX.h"<br>
@@ -4851,6 +4852,10 @@ class ConceptSpecializationExpr final : public Expr,<br>
                                     TemplateArgument> {<br>
   friend class ASTStmtReader;<br>
   friend TrailingObjects;<br>
+public:<br>
+  using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;<br>
+<br>
+protected:<br>
<br>
   // \brief The optional nested name specifier used when naming the concept.<br>
   NestedNameSpecifierLoc NestedNameSpec;<br>
@@ -4868,11 +4873,8 @@ class ConceptSpecializationExpr final : public Expr,<br>
   /// through a UsingShadowDecl.<br>
   NamedDecl *FoundDecl;<br>
<br>
-  /// \brief The concept named, and whether or not the concept with the given<br>
-  /// arguments was satisfied when the expression was created.<br>
-  /// If any of the template arguments are dependent (this expr would then be<br>
-  /// isValueDependent()), this bit is to be ignored.<br>
-  llvm::PointerIntPair<ConceptDecl *, 1, bool> NamedConcept;<br>
+  /// \brief The concept named.<br>
+  ConceptDecl *NamedConcept;<br>
<br>
   /// \brief The template argument list source info used to specialize the<br>
   /// concept.<br>
@@ -4882,13 +4884,18 @@ class ConceptSpecializationExpr final : public Expr,<br>
   /// converted template arguments.<br>
   unsigned NumTemplateArgs;<br>
<br>
+  /// \brief Information about the satisfaction of the named concept with the<br>
+  /// given arguments. If this expression is value dependent, this is to be<br>
+  /// ignored.<br>
+  ASTConstraintSatisfaction *Satisfaction;<br>
+<br>
   ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,<br>
                             SourceLocation TemplateKWLoc,<br>
                             SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,<br>
                             ConceptDecl *NamedConcept,<br>
                             const ASTTemplateArgumentListInfo *ArgsAsWritten,<br>
                             ArrayRef<TemplateArgument> ConvertedArgs,<br>
-                            Optional<bool> IsSatisfied);<br>
+                            const ConstraintSatisfaction *Satisfaction);<br>
<br>
   ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);<br>
<br>
@@ -4899,7 +4906,8 @@ class ConceptSpecializationExpr final : public Expr,<br>
          SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,<br>
          NamedDecl *FoundDecl, ConceptDecl *NamedConcept,<br>
          const ASTTemplateArgumentListInfo *ArgsAsWritten,<br>
-         ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied);<br>
+         ArrayRef<TemplateArgument> ConvertedArgs,<br>
+         const ConstraintSatisfaction *Satisfaction);<br>
<br>
   static ConceptSpecializationExpr *<br>
   Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);<br>
@@ -4913,7 +4921,7 @@ class ConceptSpecializationExpr final : public Expr,<br>
   }<br>
<br>
   ConceptDecl *getNamedConcept() const {<br>
-    return NamedConcept.getPointer();<br>
+    return NamedConcept;<br>
   }<br>
<br>
   ArrayRef<TemplateArgument> getTemplateArguments() const {<br>
@@ -4930,12 +4938,21 @@ class ConceptSpecializationExpr final : public Expr,<br>
                             ArrayRef<TemplateArgument> Converted);<br>
<br>
   /// \brief Whether or not the concept with the given arguments was satisfied<br>
-  /// when the expression was created. This method assumes that the expression<br>
-  /// is not dependent!<br>
+  /// when the expression was created.<br>
+  /// The expression must not be dependent.<br>
   bool isSatisfied() const {<br>
     assert(!isValueDependent()<br>
            && "isSatisfied called on a dependent ConceptSpecializationExpr");<br>
-    return NamedConcept.getInt();<br>
+    return Satisfaction->IsSatisfied;<br>
+  }<br>
+<br>
+  /// \brief Get elaborated satisfaction info about the template arguments'<br>
+  /// satisfaction of the named concept.<br>
+  /// The expression must not be dependent.<br>
+  const ASTConstraintSatisfaction &getSatisfaction() const {<br>
+    assert(!isValueDependent()<br>
+           && "getSatisfaction called on dependent ConceptSpecializationExpr");<br>
+    return *Satisfaction;<br>
   }<br>
<br>
   SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }<br>
<br>
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
index c1722bb6424d..385be4b44923 100644<br>
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
@@ -2542,6 +2542,26 @@ def err_non_constant_constraint_expression : Error<<br>
   "expression">;<br>
 def err_non_bool_atomic_constraint : Error<<br>
   "atomic constraint must be of type 'bool' (found %0)">;<br>
+def err_template_arg_list_constraints_not_satisfied : Error<<br>
+  "constraints not satisfied for %select{class template|function template|variable template|alias template|"<br>
+  "template template parameter|template}0 %1%2">;<br>
+def note_constraints_not_satisfied : Note<<br>
+  "constraints not satisfied">;<br>
+def note_substituted_constraint_expr_is_ill_formed : Note<<br>
+  "because substituted constraint expression is ill-formed%0">;<br>
+def note_atomic_constraint_evaluated_to_false : Note<<br>
+  "%select{and |because }0'%1' evaluated to false">;<br>
+def note_concept_specialization_constraint_evaluated_to_false : Note<<br>
+  "%select{and |because }0'%1' evaluated to false">;<br>
+def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<<br>
+  "%select{and |because }0%1 does not satisfy %2">;<br>
+def note_atomic_constraint_evaluated_to_false_elaborated : Note<<br>
+  "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">;<br>
+def err_could_not_normalize_ill_formed_constraint : Error<<br>
+  "required expansion of concept specialization %0 failed, substituted "<br>
+  "expression would be illegal">;<br>
+def note_could_not_normalize_ill_formed_constraint_reason : Note<<br>
+  "because: %0">;<br>
<br>
 def err_template_<br>
diff erent_requires_clause : Error<<br>
   "requires clause <br>
diff ers in template redeclaration">;<br>
@@ -3820,6 +3840,8 @@ def note_ovl_candidate_inconsistent_deduction_types : Note<<br>
 def note_ovl_candidate_explicit_arg_mismatch_named : Note<<br>
     "candidate template ignored: invalid explicitly-specified argument "<br>
     "for template parameter %0">;<br>
+def note_ovl_candidate_unsatisfied_constraints : Note<<br>
+    "candidate template ignored: constraints not satisfied%0">;<br>
 def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note<<br>
     "candidate template ignored: invalid explicitly-specified argument "<br>
     "for %ordinal0 template parameter">;<br>
@@ -4512,6 +4534,14 @@ def note_template_default_arg_checking : Note<<br>
   "while checking a default template argument used here">;<br>
 def note_concept_specialization_here : Note<<br>
   "while checking the satisfaction of concept '%0' requested here">;<br>
+def note_checking_constraints_for_template_id_here : Note<<br>
+  "while checking constraint satisfaction for template '%0' required here">;<br>
+def note_checking_constraints_for_var_spec_id_here : Note<<br>
+  "while checking constraint satisfaction for variable template "<br>
+  "partial specialization '%0' required here">;<br>
+def note_checking_constraints_for_class_spec_id_here : Note<<br>
+  "while checking constraint satisfaction for class template partial "<br>
+  "specialization '%0' required here">;<br>
 def note_constraint_substitution_here : Note<<br>
   "while substituting template arguments into constraint expression here">;<br>
 def note_instantiation_contexts_suppressed : Note<<br>
<br>
diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h<br>
index 7453ab21fa0e..d5b3582655f2 100644<br>
--- a/clang/include/clang/Sema/Sema.h<br>
+++ b/clang/include/clang/Sema/Sema.h<br>
@@ -14,6 +14,7 @@<br>
 #ifndef LLVM_CLANG_SEMA_SEMA_H<br>
 #define LLVM_CLANG_SEMA_SEMA_H<br>
<br>
+#include "clang/AST/ASTConcept.h"<br>
 #include "clang/AST/Attr.h"<br>
 #include "clang/AST/Availability.h"<br>
 #include "clang/AST/ComparisonCategories.h"<br>
@@ -6131,10 +6132,45 @@ class Sema {<br>
   /// A diagnostic is emitted if it is not, and false is returned.<br>
   bool CheckConstraintExpression(Expr *CE);<br>
<br>
-  bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,<br>
-                                       MultiLevelTemplateArgumentList &MLTAL,<br>
-                                       Expr *ConstraintExpr,<br>
-                                       bool &IsSatisfied);<br>
+  /// \brief Check whether the given list of constraint expressions are<br>
+  /// satisfied (as if in a 'conjunction') given template arguments.<br>
+  /// \param ConstraintExprs a list of constraint expressions, treated as if<br>
+  /// they were 'AND'ed together.<br>
+  /// \param TemplateArgs the list of template arguments to substitute into the<br>
+  /// constraint expression.<br>
+  /// \param TemplateIDRange The source range of the template id that<br>
+  /// caused the constraints check.<br>
+  /// \param Satisfaction if true is returned, will contain details of the<br>
+  /// satisfaction, with enough information to diagnose an unsatisfied<br>
+  /// expression.<br>
+  /// \returns true if an error occurred and satisfaction could not be checked,<br>
+  /// false otherwise.<br>
+  bool CheckConstraintSatisfaction(TemplateDecl *Template,<br>
+                                   ArrayRef<const Expr *> ConstraintExprs,<br>
+                                   ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                   SourceRange TemplateIDRange,<br>
+                                   ConstraintSatisfaction &Satisfaction);<br>
+<br>
+  bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,<br>
+                                   ArrayRef<const Expr *> ConstraintExprs,<br>
+                                   ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                   SourceRange TemplateIDRange,<br>
+                                   ConstraintSatisfaction &Satisfaction);<br>
+<br>
+  bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,<br>
+                                   ArrayRef<const Expr *> ConstraintExprs,<br>
+                                   ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                   SourceRange TemplateIDRange,<br>
+                                   ConstraintSatisfaction &Satisfaction);<br>
+<br>
+  /// \brief Check whether the given non-dependent constraint expression is<br>
+  /// satisfied. Returns false and updates Satisfaction with the satisfaction<br>
+  /// verdict if successful, emits a diagnostic and returns true if an error<br>
+  /// occured and satisfaction could not be determined.<br>
+  ///<br>
+  /// \returns true if an error occurred, false otherwise.<br>
+  bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,<br>
+                                   ConstraintSatisfaction &Satisfaction);<br>
<br>
   /// Check that the associated constraints of a template declaration match the<br>
   /// associated constraints of an older declaration of which it is a<br>
@@ -6142,6 +6178,38 @@ class Sema {<br>
   bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old,<br>
                                          TemplateParameterList *New);<br>
<br>
+  /// \brief Ensure that the given template arguments satisfy the constraints<br>
+  /// associated with the given template, emitting a diagnostic if they do not.<br>
+  ///<br>
+  /// \param Template The template to which the template arguments are being<br>
+  /// provided.<br>
+  ///<br>
+  /// \param TemplateArgs The converted, canonicalized template arguments.<br>
+  ///<br>
+  /// \param TemplateIDRange The source range of the template id that<br>
+  /// caused the constraints check.<br>
+  ///<br>
+  /// \returns true if the constrains are not satisfied or could not be checked<br>
+  /// for satisfaction, false if the constraints are satisfied.<br>
+  bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,<br>
+                                       ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                             SourceRange TemplateIDRange);<br>
+<br>
+  /// \brief Emit diagnostics explaining why a constraint expression was deemed<br>
+  /// unsatisfied.<br>
+  void<br>
+  DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction);<br>
+<br>
+  /// \brief Emit diagnostics explaining why a constraint expression was deemed<br>
+  /// unsatisfied.<br>
+  void<br>
+  DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction& Satisfaction);<br>
+<br>
+  /// \brief Emit diagnostics explaining why a constraint expression was deemed<br>
+  /// unsatisfied because it was ill-formed.<br>
+  void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation,<br>
+                                              StringRef Diagnostic);<br>
+<br>
   // ParseObjCStringLiteral - Parse Objective-C string literals.<br>
   ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,<br>
                                     ArrayRef<Expr *> Strings);<br>
@@ -6957,13 +7025,18 @@ class Sema {<br>
   /// contain the converted forms of the template arguments as written.<br>
   /// Otherwise, \p TemplateArgs will not be modified.<br>
   ///<br>
+  /// \param ConstraintsNotSatisfied If provided, and an error occured, will<br>
+  /// receive true if the cause for the error is the associated constraints of<br>
+  /// the template not being satisfied by the template arguments.<br>
+  ///<br>
   /// \returns true if an error occurred, false otherwise.<br>
   bool CheckTemplateArgumentList(TemplateDecl *Template,<br>
                                  SourceLocation TemplateLoc,<br>
                                  TemplateArgumentListInfo &TemplateArgs,<br>
                                  bool PartialTemplateArgs,<br>
                                  SmallVectorImpl<TemplateArgument> &Converted,<br>
-                                 bool UpdateArgsWithConversions = true);<br>
+                                 bool UpdateArgsWithConversions = true,<br>
+                                 bool *ConstraintsNotSatisfied = nullptr);<br>
<br>
   bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,<br>
                                  TemplateArgumentLoc &Arg,<br>
@@ -7505,6 +7578,9 @@ class Sema {<br>
     TDK_InvalidExplicitArguments,<br>
     /// Checking non-dependent argument conversions failed.<br>
     TDK_NonDependentConversionFailure,<br>
+    /// The deduced arguments did not satisfy the constraints associated<br>
+    /// with the template.<br>
+    TDK_ConstraintsNotSatisfied,<br>
     /// Deduction failed; that's all we know.<br>
     TDK_MiscellaneousDeductionFailure,<br>
     /// CUDA Target attributes do not match.<br>
@@ -8017,7 +8093,7 @@ class Sema {<br>
     /// constrained entity (a concept declaration or a template with associated<br>
     /// constraints).<br>
     InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,<br>
-                          ConstraintsCheck, TemplateDecl *Template,<br>
+                          ConstraintsCheck, NamedDecl *Template,<br>
                           ArrayRef<TemplateArgument> TemplateArgs,<br>
                           SourceRange InstantiationRange);<br>
<br>
@@ -8026,7 +8102,7 @@ class Sema {<br>
     /// with a template declaration or as part of the satisfaction check of a<br>
     /// concept.<br>
     InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,<br>
-                          ConstraintSubstitution, TemplateDecl *Template,<br>
+                          ConstraintSubstitution, NamedDecl *Template,<br>
                           sema::TemplateDeductionInfo &DeductionInfo,<br>
                           SourceRange InstantiationRange);<br>
<br>
<br>
diff  --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h<br>
index 662c4072c978..b60939c97872 100644<br>
--- a/clang/include/clang/Sema/TemplateDeduction.h<br>
+++ b/clang/include/clang/Sema/TemplateDeduction.h<br>
@@ -14,6 +14,8 @@<br>
 #ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H<br>
 #define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H<br>
<br>
+#include "clang/Sema/Ownership.h"<br>
+#include "clang/AST/ASTConcept.h"<br>
 #include "clang/AST/DeclAccessPair.h"<br>
 #include "clang/AST/DeclTemplate.h"<br>
 #include "clang/AST/TemplateBase.h"<br>
@@ -218,6 +220,10 @@ class TemplateDeductionInfo {<br>
   ///<br>
   /// FIXME: This should be kept internal to SemaTemplateDeduction.<br>
   SmallVector<DeducedPack *, 8> PendingDeducedPacks;<br>
+<br>
+  /// \brief The constraint satisfaction details resulting from the associated<br>
+  /// constraints satisfaction tests.<br>
+  ConstraintSatisfaction AssociatedConstraintsSatisfaction;<br>
 };<br>
<br>
 } // namespace sema<br>
<br>
diff  --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp<br>
new file mode 100644<br>
index 000000000000..3446a6a6da14<br>
--- /dev/null<br>
+++ b/clang/lib/AST/ASTConcept.cpp<br>
@@ -0,0 +1,56 @@<br>
+//===--- ASTConcept.cpp - Concepts Related AST Data Structures --*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// \file<br>
+/// \brief This file defines AST data structures related to concepts.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "clang/AST/ASTConcept.h"<br>
+#include "clang/AST/ASTContext.h"<br>
+using namespace clang;<br>
+<br>
+ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C,<br>
+    const ConstraintSatisfaction &Satisfaction):<br>
+    NumRecords{Satisfaction.Details.size()},<br>
+    IsSatisfied{Satisfaction.IsSatisfied} {<br>
+  for (unsigned I = 0; I < NumRecords; ++I) {<br>
+    auto &Detail = Satisfaction.Details[I];<br>
+    if (<a href="http://Detail.second.is" rel="noreferrer" target="_blank">Detail.second.is</a><Expr *>())<br>
+      new (getTrailingObjects<UnsatisfiedConstraintRecord>() + I)<br>
+         UnsatisfiedConstraintRecord{Detail.first,<br>
+                                     UnsatisfiedConstraintRecord::second_type(<br>
+                                         Detail.second.get<Expr *>())};<br>
+    else {<br>
+      auto &SubstitutionDiagnostic =<br>
+          *Detail.second.get<std::pair<SourceLocation, std::string> *>();<br>
+      unsigned MessageSize = SubstitutionDiagnostic.second.size();<br>
+      char *Mem = new (C) char[MessageSize + 1];<br>
+      memcpy(Mem, SubstitutionDiagnostic.second.c_str(), MessageSize);<br>
+      Mem[MessageSize + 1] = '\0';<br>
+      auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>(<br>
+          SubstitutionDiagnostic.first, StringRef(Mem));<br>
+      new (getTrailingObjects<UnsatisfiedConstraintRecord>() + I)<br>
+         UnsatisfiedConstraintRecord{Detail.first,<br>
+                                     UnsatisfiedConstraintRecord::second_type(<br>
+                                         NewSubstDiag)};<br>
+    }<br>
+  }<br>
+}<br>
+<br>
+<br>
+ASTConstraintSatisfaction *<br>
+ASTConstraintSatisfaction::Create(const ASTContext &C,<br>
+                                  const ConstraintSatisfaction &Satisfaction) {<br>
+  std::size_t size =<br>
+      totalSizeToAlloc<UnsatisfiedConstraintRecord>(<br>
+          Satisfaction.Details.size());<br>
+  void *Mem = C.Allocate(size, alignof(ASTConstraintSatisfaction));<br>
+  return new (Mem) ASTConstraintSatisfaction(C, Satisfaction);<br>
+}<br>
<br>
diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp<br>
index cda51ec755a8..cdd05c02ef36 100644<br>
--- a/clang/lib/AST/ASTContext.cpp<br>
+++ b/clang/lib/AST/ASTContext.cpp<br>
@@ -14,6 +14,7 @@<br>
 #include "CXXABI.h"<br>
 #include "Interp/Context.h"<br>
 #include "clang/AST/APValue.h"<br>
+#include "clang/AST/ASTConcept.h"<br>
 #include "clang/AST/ASTMutationListener.h"<br>
 #include "clang/AST/ASTTypeTraits.h"<br>
 #include "clang/AST/Attr.h"<br>
<br>
diff  --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt<br>
index 5bae40c86539..1ede58cc9ed6 100644<br>
--- a/clang/lib/AST/CMakeLists.txt<br>
+++ b/clang/lib/AST/CMakeLists.txt<br>
@@ -14,6 +14,7 @@ clang_tablegen(Opcodes.inc<br>
<br>
 add_clang_library(clangAST<br>
   APValue.cpp<br>
+  ASTConcept.cpp<br>
   ASTConsumer.cpp<br>
   ASTContext.cpp<br>
   ASTDiagnostic.cpp<br>
<br>
diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp<br>
index 66446626c2eb..03fb55db14ef 100644<br>
--- a/clang/lib/AST/Decl.cpp<br>
+++ b/clang/lib/AST/Decl.cpp<br>
@@ -4585,7 +4585,7 @@ LabelDecl *LabelDecl::CreateDeserialized(ASTContext &C, unsigned ID) {<br>
 }<br>
<br>
 void LabelDecl::setMSAsmLabel(StringRef Name) {<br>
-  char *Buffer = new (getASTContext(), 1) char[Name.size() + 1];<br>
+char *Buffer = new (getASTContext(), 1) char[Name.size() + 1];<br>
   memcpy(Buffer, Name.data(), Name.size());<br>
   Buffer[Name.size()] = '\0';<br>
   MSAsmName = Buffer;<br>
<br>
diff  --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp<br>
index 904928bdf286..a6252d45b267 100644<br>
--- a/clang/lib/AST/ExprCXX.cpp<br>
+++ b/clang/lib/AST/ExprCXX.cpp<br>
@@ -1755,18 +1755,19 @@ ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,<br>
     NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,<br>
     SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,<br>
     ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,<br>
-    ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied)<br>
+    ArrayRef<TemplateArgument> ConvertedArgs,<br>
+    const ConstraintSatisfaction *Satisfaction)<br>
     : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,<br>
            /*TypeDependent=*/false,<br>
            // All the flags below are set in setTemplateArguments.<br>
-           /*ValueDependent=*/!IsSatisfied.hasValue(),<br>
-           /*InstantiationDependent=*/false,<br>
+           /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false,<br>
            /*ContainsUnexpandedParameterPacks=*/false),<br>
       NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),<br>
       ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),<br>
-      NamedConcept(NamedConcept, IsSatisfied ? *IsSatisfied : true),<br>
-      NumTemplateArgs(ConvertedArgs.size()) {<br>
-<br>
+      NamedConcept(NamedConcept), NumTemplateArgs(ConvertedArgs.size()),<br>
+      Satisfaction(Satisfaction ?<br>
+                   ASTConstraintSatisfaction::Create(C, *Satisfaction) :<br>
+                   nullptr) {<br>
   setTemplateArguments(ArgsAsWritten, ConvertedArgs);<br>
 }<br>
<br>
@@ -1813,13 +1814,13 @@ ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,<br>
                                   ConceptDecl *NamedConcept,<br>
                                const ASTTemplateArgumentListInfo *ArgsAsWritten,<br>
                                   ArrayRef<TemplateArgument> ConvertedArgs,<br>
-                                  Optional<bool> IsSatisfied) {<br>
+                                  const ConstraintSatisfaction *Satisfaction) {<br>
   void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(<br>
                                 ConvertedArgs.size()));<br>
   return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,<br>
                                                 ConceptNameLoc, FoundDecl,<br>
                                                 NamedConcept, ArgsAsWritten,<br>
-                                                ConvertedArgs, IsSatisfied);<br>
+                                                ConvertedArgs, Satisfaction);<br>
 }<br>
<br>
 ConceptSpecializationExpr *<br>
<br>
diff  --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp<br>
index 848ccf543445..cbe265971df6 100644<br>
--- a/clang/lib/Sema/SemaConcept.cpp<br>
+++ b/clang/lib/Sema/SemaConcept.cpp<br>
@@ -12,10 +12,13 @@<br>
 //===----------------------------------------------------------------------===//<br>
<br>
 #include "clang/Sema/Sema.h"<br>
+#include "clang/Sema/SemaInternal.h"<br>
 #include "clang/Sema/SemaDiagnostic.h"<br>
 #include "clang/Sema/TemplateDeduction.h"<br>
 #include "clang/Sema/Template.h"<br>
 #include "clang/AST/ExprCXX.h"<br>
+#include "llvm/ADT/DenseMap.h"<br>
+#include "llvm/ADT/PointerUnion.h"<br>
 using namespace clang;<br>
 using namespace sema;<br>
<br>
@@ -46,80 +49,367 @@ bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {<br>
   return true;<br>
 }<br>
<br>
-bool<br>
-Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,<br>
-                                      MultiLevelTemplateArgumentList &MLTAL,<br>
-                                      Expr *ConstraintExpr,<br>
-                                      bool &IsSatisfied) {<br>
+template <typename AtomicEvaluator><br>
+static bool<br>
+calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,<br>
+                                ConstraintSatisfaction &Satisfaction,<br>
+                                AtomicEvaluator &&Evaluator) {<br>
   ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();<br>
<br>
   if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {<br>
-    if (BO->getOpcode() == BO_LAnd) {<br>
-      if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),<br>
-                                          IsSatisfied))<br>
+    if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {<br>
+      if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction,<br>
+                                          Evaluator))<br>
         return true;<br>
-      if (!IsSatisfied)<br>
+<br>
+      bool IsLHSSatisfied = Satisfaction.IsSatisfied;<br>
+<br>
+      if (BO->getOpcode() == BO_LOr && IsLHSSatisfied)<br>
+        // [temp.constr.op] p3<br>
+        //    A disjunction is a constraint taking two operands. To determine if<br>
+        //    a disjunction is satisfied, the satisfaction of the first operand<br>
+        //    is checked. If that is satisfied, the disjunction is satisfied.<br>
+        //    Otherwise, the disjunction is satisfied if and only if the second<br>
+        //    operand is satisfied.<br>
         return false;<br>
-      return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),<br>
-                                             IsSatisfied);<br>
-    } else if (BO->getOpcode() == BO_LOr) {<br>
-      if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),<br>
-                                          IsSatisfied))<br>
-        return true;<br>
-      if (IsSatisfied)<br>
+<br>
+      if (BO->getOpcode() == BO_LAnd && !IsLHSSatisfied)<br>
+        // [temp.constr.op] p2<br>
+        //    A conjunction is a constraint taking two operands. To determine if<br>
+        //    a conjunction is satisfied, the satisfaction of the first operand<br>
+        //    is checked. If that is not satisfied, the conjunction is not<br>
+        //    satisfied. Otherwise, the conjunction is satisfied if and only if<br>
+        //    the second operand is satisfied.<br>
         return false;<br>
-      return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),<br>
-                                             IsSatisfied);<br>
+<br>
+      return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction,<br>
+          std::forward<AtomicEvaluator>(Evaluator));<br>
     }<br>
   }<br>
   else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))<br>
-    return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),<br>
-                                           IsSatisfied);<br>
+    return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,<br>
+        std::forward<AtomicEvaluator>(Evaluator));<br>
<br>
-  EnterExpressionEvaluationContext ConstantEvaluated(<br>
-      *this, Sema::ExpressionEvaluationContext::ConstantEvaluated);<br>
-<br>
-  // Atomic constraint - substitute arguments and check satisfaction.<br>
-  ExprResult E;<br>
-  {<br>
-    TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc());<br>
-    InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(),<br>
-                               InstantiatingTemplate::ConstraintSubstitution{},<br>
-                               NamedConcept, Info,<br>
-                               ConstraintExpr->getSourceRange());<br>
-    if (Inst.isInvalid())<br>
-      return true;<br>
-    // We do not want error diagnostics escaping here.<br>
-    Sema::SFINAETrap Trap(*this);<br>
+  // An atomic constraint expression<br>
+  ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);<br>
<br>
-    E = SubstExpr(ConstraintExpr, MLTAL);<br>
-    if (E.isInvalid() || Trap.hasErrorOccurred()) {<br>
-      // C++2a [temp.constr.atomic]p1<br>
-      //   ...If substitution results in an invalid type or expression, the<br>
-      //   constraint is not satisfied.<br>
-      IsSatisfied = false;<br>
-      return false;<br>
-    }<br>
-  }<br>
-<br>
-  if (!CheckConstraintExpression(E.get()))<br>
+  if (SubstitutedAtomicExpr.isInvalid())<br>
     return true;<br>
<br>
+  if (!SubstitutedAtomicExpr.isUsable())<br>
+    // Evaluator has decided satisfaction without yielding an expression.<br>
+    return false;<br>
+<br>
+  EnterExpressionEvaluationContext ConstantEvaluated(<br>
+      S, Sema::ExpressionEvaluationContext::ConstantEvaluated);<br>
   SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;<br>
   Expr::EvalResult EvalResult;<br>
   EvalResult.Diag = &EvaluationDiags;<br>
-  if (!E.get()->EvaluateAsRValue(EvalResult, Context)) {<br>
-    // C++2a [temp.constr.atomic]p1<br>
-    //   ...E shall be a constant expression of type bool.<br>
-    Diag(E.get()->getBeginLoc(),<br>
-         diag::err_non_constant_constraint_expression)<br>
-        << E.get()->getSourceRange();<br>
+  if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) {<br>
+      // C++2a [temp.constr.atomic]p1<br>
+      //   ...E shall be a constant expression of type bool.<br>
+    S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(),<br>
+           diag::err_non_constant_constraint_expression)<br>
+        << SubstitutedAtomicExpr.get()->getSourceRange();<br>
     for (const PartialDiagnosticAt &PDiag : EvaluationDiags)<br>
-      Diag(PDiag.first, PDiag.second);<br>
+      S.Diag(PDiag.first, PDiag.second);<br>
     return true;<br>
   }<br>
<br>
-  IsSatisfied = EvalResult.Val.getInt().getBoolValue();<br>
+  Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue();<br>
+  if (!Satisfaction.IsSatisfied)<br>
+    Satisfaction.Details.emplace_back(ConstraintExpr,<br>
+                                      SubstitutedAtomicExpr.get());<br>
+<br>
+  return false;<br>
+}<br>
+<br>
+template <typename TemplateDeclT><br>
+static bool calculateConstraintSatisfaction(<br>
+    Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs,<br>
+    SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,<br>
+    const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {<br>
+  return calculateConstraintSatisfaction(<br>
+      S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {<br>
+        EnterExpressionEvaluationContext ConstantEvaluated(<br>
+            S, Sema::ExpressionEvaluationContext::ConstantEvaluated);<br>
+<br>
+        // Atomic constraint - substitute arguments and check satisfaction.<br>
+        ExprResult SubstitutedExpression;<br>
+        {<br>
+          TemplateDeductionInfo Info(TemplateNameLoc);<br>
+          Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),<br>
+              Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template,<br>
+              Info, AtomicExpr->getSourceRange());<br>
+          if (Inst.isInvalid())<br>
+            return ExprError();<br>
+          // We do not want error diagnostics escaping here.<br>
+          Sema::SFINAETrap Trap(S);<br>
+          SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),<br>
+                                              MLTAL);<br>
+          if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {<br>
+            // C++2a [temp.constr.atomic]p1<br>
+            //   ...If substitution results in an invalid type or expression, the<br>
+            //   constraint is not satisfied.<br>
+            if (!Trap.hasErrorOccurred())<br>
+              // A non-SFINAE error has occured as a result of this<br>
+              // substitution.<br>
+              return ExprError();<br>
+<br>
+            PartialDiagnosticAt SubstDiag{SourceLocation(),<br>
+                                          PartialDiagnostic::NullDiagnostic()};<br>
+            Info.takeSFINAEDiagnostic(SubstDiag);<br>
+            // FIXME: Concepts: This is an unfortunate consequence of there<br>
+            //  being no serialization code for PartialDiagnostics and the fact<br>
+            //  that serializing them would likely take a lot more storage than<br>
+            //  just storing them as strings. We would still like, in the<br>
+            //  future, to serialize the proper PartialDiagnostic as serializing<br>
+            //  it as a string defeats the purpose of the diagnostic mechanism.<br>
+            SmallString<128> DiagString;<br>
+            DiagString = ": ";<br>
+            SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);<br>
+            Satisfaction.Details.emplace_back(<br>
+                AtomicExpr,<br>
+                new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{<br>
+                        SubstDiag.first,<br>
+                        std::string(DiagString.begin(), DiagString.end())});<br>
+            Satisfaction.IsSatisfied = false;<br>
+            return ExprEmpty();<br>
+          }<br>
+        }<br>
+<br>
+        if (!S.CheckConstraintExpression(SubstitutedExpression.get()))<br>
+          return ExprError();<br>
+<br>
+        return SubstitutedExpression;<br>
+      });<br>
+}<br>
+<br>
+template<typename TemplateDeclT><br>
+static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,<br>
+                                        ArrayRef<const Expr *> ConstraintExprs,<br>
+                                        ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                        SourceRange TemplateIDRange,<br>
+                                        ConstraintSatisfaction &Satisfaction) {<br>
+  if (ConstraintExprs.empty()) {<br>
+    Satisfaction.IsSatisfied = true;<br>
+    return false;<br>
+  }<br>
+<br>
+  for (auto& Arg : TemplateArgs)<br>
+    if (Arg.isInstantiationDependent()) {<br>
+      // No need to check satisfaction for dependent constraint expressions.<br>
+      Satisfaction.IsSatisfied = true;<br>
+      return false;<br>
+    }<br>
+<br>
+  Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),<br>
+      Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,<br>
+      TemplateIDRange);<br>
+  if (Inst.isInvalid())<br>
+    return true;<br>
+<br>
+  MultiLevelTemplateArgumentList MLTAL;<br>
+  MLTAL.addOuterTemplateArguments(TemplateArgs);<br>
+<br>
+  for (const Expr *ConstraintExpr : ConstraintExprs) {<br>
+    if (calculateConstraintSatisfaction(S, Template, TemplateArgs,<br>
+                                        TemplateIDRange.getBegin(), MLTAL,<br>
+                                        ConstraintExpr, Satisfaction))<br>
+      return true;<br>
+    if (!Satisfaction.IsSatisfied)<br>
+      // [temp.constr.op] p2<br>
+      //   [...] To determine if a conjunction is satisfied, the satisfaction<br>
+      //   of the first operand is checked. If that is not satisfied, the<br>
+      //   conjunction is not satisfied. [...]<br>
+      return false;<br>
+  }<br>
+  return false;<br>
+}<br>
+<br>
+bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,<br>
+                                       ArrayRef<const Expr *> ConstraintExprs,<br>
+                                       ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                       SourceRange TemplateIDRange,<br>
+                                       ConstraintSatisfaction &Satisfaction) {<br>
+  return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,<br>
+                                       TemplateArgs, TemplateIDRange,<br>
+                                       Satisfaction);<br>
+}<br>
<br>
+bool<br>
+Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,<br>
+                                  ArrayRef<const Expr *> ConstraintExprs,<br>
+                                  ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                  SourceRange TemplateIDRange,<br>
+                                  ConstraintSatisfaction &Satisfaction) {<br>
+  return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs,<br>
+                                       TemplateArgs, TemplateIDRange,<br>
+                                       Satisfaction);<br>
+}<br>
+<br>
+bool<br>
+Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,<br>
+                                  ArrayRef<const Expr *> ConstraintExprs,<br>
+                                  ArrayRef<TemplateArgument> TemplateArgs,<br>
+                                  SourceRange TemplateIDRange,<br>
+                                  ConstraintSatisfaction &Satisfaction) {<br>
+  return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs,<br>
+                                       TemplateArgs, TemplateIDRange,<br>
+                                       Satisfaction);<br>
+}<br>
+<br>
+bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,<br>
+                                       ConstraintSatisfaction &Satisfaction) {<br>
+  return calculateConstraintSatisfaction(<br>
+      *this, ConstraintExpr, Satisfaction,<br>
+      [](const Expr *AtomicExpr) -> ExprResult {<br>
+        return ExprResult(const_cast<Expr *>(AtomicExpr));<br>
+      });<br>
+}<br>
+<br>
+bool Sema::EnsureTemplateArgumentListConstraints(<br>
+    TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,<br>
+    SourceRange TemplateIDRange) {<br>
+  ConstraintSatisfaction Satisfaction;<br>
+  llvm::SmallVector<const Expr *, 3> AssociatedConstraints;<br>
+  TD->getAssociatedConstraints(AssociatedConstraints);<br>
+  if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs,<br>
+                                  TemplateIDRange, Satisfaction))<br>
+    return true;<br>
+<br>
+  if (!Satisfaction.IsSatisfied) {<br>
+    SmallString<128> TemplateArgString;<br>
+    TemplateArgString = " ";<br>
+    TemplateArgString += getTemplateArgumentBindingsText(<br>
+        TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());<br>
+<br>
+    Diag(TemplateIDRange.getBegin(),<br>
+         diag::err_template_arg_list_constraints_not_satisfied)<br>
+        << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD<br>
+        << TemplateArgString << TemplateIDRange;<br>
+    DiagnoseUnsatisfiedConstraint(Satisfaction);<br>
+    return true;<br>
+  }<br>
   return false;<br>
+}<br>
+<br>
+static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,<br>
+                                                        Expr *SubstExpr,<br>
+                                                        bool First = true) {<br>
+  SubstExpr = SubstExpr->IgnoreParenImpCasts();<br>
+  if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {<br>
+    switch (BO->getOpcode()) {<br>
+    // These two cases will in practice only be reached when using fold<br>
+    // expressions with || and &&, since otherwise the || and && will have been<br>
+    // broken down into atomic constraints during satisfaction checking.<br>
+    case BO_LOr:<br>
+      // Or evaluated to false - meaning both RHS and LHS evaluated to false.<br>
+      diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);<br>
+      diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),<br>
+                                                  /*First=*/false);<br>
+      return;<br>
+    case BO_LAnd:<br>
+      bool LHSSatisfied;<br>
+      BO->getLHS()->EvaluateAsBooleanCondition(LHSSatisfied, S.Context);<br>
+      if (LHSSatisfied) {<br>
+        // LHS is true, so RHS must be false.<br>
+        diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), First);<br>
+        return;<br>
+      }<br>
+      // LHS is false<br>
+      diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);<br>
+<br>
+      // RHS might also be false<br>
+      bool RHSSatisfied;<br>
+      BO->getRHS()->EvaluateAsBooleanCondition(RHSSatisfied, S.Context);<br>
+      if (!RHSSatisfied)<br>
+        diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),<br>
+                                                    /*First=*/false);<br>
+      return;<br>
+    case BO_GE:<br>
+    case BO_LE:<br>
+    case BO_GT:<br>
+    case BO_LT:<br>
+    case BO_EQ:<br>
+    case BO_NE:<br>
+      if (BO->getLHS()->getType()->isIntegerType() &&<br>
+          BO->getRHS()->getType()->isIntegerType()) {<br>
+        Expr::EvalResult SimplifiedLHS;<br>
+        Expr::EvalResult SimplifiedRHS;<br>
+        BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);<br>
+        BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);<br>
+        if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {<br>
+          S.Diag(SubstExpr->getBeginLoc(),<br>
+                 diag::note_atomic_constraint_evaluated_to_false_elaborated)<br>
+              << (int)First << SubstExpr<br>
+              << SimplifiedLHS.Val.getInt().toString(10)<br>
+              << BinaryOperator::getOpcodeStr(BO->getOpcode())<br>
+              << SimplifiedRHS.Val.getInt().toString(10);<br>
+          return;<br>
+        }<br>
+      }<br>
+      break;<br>
+<br>
+    default:<br>
+      break;<br>
+    }<br>
+  } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {<br>
+    if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {<br>
+      S.Diag(<br>
+          CSE->getSourceRange().getBegin(),<br>
+          diag::<br>
+          note_single_arg_concept_specialization_constraint_evaluated_to_false)<br>
+          << (int)First<br>
+          << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()<br>
+          << CSE->getNamedConcept();<br>
+    } else {<br>
+      S.Diag(SubstExpr->getSourceRange().getBegin(),<br>
+             diag::note_concept_specialization_constraint_evaluated_to_false)<br>
+          << (int)First << CSE;<br>
+    }<br>
+    S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());<br>
+    return;<br>
+  }<br>
+<br>
+  S.Diag(SubstExpr->getSourceRange().getBegin(),<br>
+         diag::note_atomic_constraint_evaluated_to_false)<br>
+      << (int)First << SubstExpr;<br>
+}<br>
+<br>
+template<typename SubstitutionDiagnostic><br>
+static void diagnoseUnsatisfiedConstraintExpr(<br>
+    Sema &S, const Expr *E,<br>
+    const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record,<br>
+    bool First = true) {<br>
+  if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()){<br>
+    S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)<br>
+        << Diag->second;<br>
+    return;<br>
+  }<br>
+<br>
+  diagnoseWellFormedUnsatisfiedConstraintExpr(S,<br>
+      Record.template get<Expr *>(), First);<br>
+}<br>
+<br>
+void Sema::DiagnoseUnsatisfiedConstraint(<br>
+    const ConstraintSatisfaction& Satisfaction) {<br>
+  assert(!Satisfaction.IsSatisfied &&<br>
+         "Attempted to diagnose a satisfied constraint");<br>
+  bool First = true;<br>
+  for (auto &Pair : Satisfaction.Details) {<br>
+    diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);<br>
+    First = false;<br>
+  }<br>
+}<br>
+<br>
+void Sema::DiagnoseUnsatisfiedConstraint(<br>
+    const ASTConstraintSatisfaction &Satisfaction) {<br>
+  assert(!Satisfaction.IsSatisfied &&<br>
+         "Attempted to diagnose a satisfied constraint");<br>
+  bool First = true;<br>
+  for (auto &Pair : Satisfaction) {<br>
+    diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);<br>
+    First = false;<br>
+  }<br>
 }<br>
\ No newline at end of file<br>
<br>
diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp<br>
index 0201d014e6f2..ccf6c0a604b2 100644<br>
--- a/clang/lib/Sema/SemaDeclCXX.cpp<br>
+++ b/clang/lib/Sema/SemaDeclCXX.cpp<br>
@@ -14453,8 +14453,16 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,<br>
       std::string InnerCondDescription;<br>
       std::tie(InnerCond, InnerCondDescription) =<br>
         findFailedBooleanCondition(Converted.get());<br>
-      if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond)<br>
-                    && !isa<IntegerLiteral>(InnerCond)) {<br>
+      if (InnerCond && isa<ConceptSpecializationExpr>(InnerCond)) {<br>
+        // Drill down into concept specialization expressions to see why they<br>
+        // weren't satisfied.<br>
+        Diag(StaticAssertLoc, diag::err_static_assert_failed)<br>
+          << !AssertMessage << Msg.str() << AssertExpr->getSourceRange();<br>
+        ConstraintSatisfaction Satisfaction;<br>
+        if (!CheckConstraintSatisfaction(InnerCond, Satisfaction))<br>
+          DiagnoseUnsatisfiedConstraint(Satisfaction);<br>
+      } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond)<br>
+                           && !isa<IntegerLiteral>(InnerCond)) {<br>
         Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed)<br>
           << InnerCondDescription << !AssertMessage<br>
           << Msg.str() << InnerCond->getSourceRange();<br>
<br>
diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp<br>
index 47c1e3cec0ea..b79855a6afb3 100644<br>
--- a/clang/lib/Sema/SemaOverload.cpp<br>
+++ b/clang/lib/Sema/SemaOverload.cpp<br>
@@ -591,6 +591,12 @@ namespace {<br>
     TemplateArgumentList *TemplateArgs;<br>
     unsigned CallArgIndex;<br>
   };<br>
+  // Structure used by DeductionFailureInfo to store information about<br>
+  // unsatisfied constraints.<br>
+  struct CNSInfo {<br>
+    TemplateArgumentList *TemplateArgs;<br>
+    ConstraintSatisfaction Satisfaction;<br>
+  };<br>
 }<br>
<br>
 /// Convert from Sema's representation of template deduction information<br>
@@ -661,6 +667,14 @@ clang::MakeDeductionFailureInfo(ASTContext &Context,<br>
     }<br>
     break;<br>
<br>
+  case Sema::TDK_ConstraintsNotSatisfied: {<br>
+    CNSInfo *Saved = new (Context) CNSInfo;<br>
+    Saved->TemplateArgs = Info.take();<br>
+    Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction;<br>
+    Result.Data = Saved;<br>
+    break;<br>
+  }<br>
+<br>
   case Sema::TDK_Success:<br>
   case Sema::TDK_NonDependentConversionFailure:<br>
     llvm_unreachable("not a deduction failure");<br>
@@ -701,6 +715,15 @@ void DeductionFailureInfo::Destroy() {<br>
     }<br>
     break;<br>
<br>
+  case Sema::TDK_ConstraintsNotSatisfied:<br>
+    // FIXME: Destroy the template argument list?<br>
+    Data = nullptr;<br>
+    if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {<br>
+      Diag->~PartialDiagnosticAt();<br>
+      HasDiagnostic = false;<br>
+    }<br>
+    break;<br>
+<br>
   // Unhandled<br>
   case Sema::TDK_MiscellaneousDeductionFailure:<br>
     break;<br>
@@ -726,6 +749,7 @@ TemplateParameter DeductionFailureInfo::getTemplateParameter() {<br>
   case Sema::TDK_NonDeducedMismatch:<br>
   case Sema::TDK_CUDATargetMismatch:<br>
   case Sema::TDK_NonDependentConversionFailure:<br>
+  case Sema::TDK_ConstraintsNotSatisfied:<br>
     return TemplateParameter();<br>
<br>
   case Sema::TDK_Incomplete:<br>
@@ -769,6 +793,9 @@ TemplateArgumentList *DeductionFailureInfo::getTemplateArgumentList() {<br>
   case Sema::TDK_SubstitutionFailure:<br>
     return static_cast<TemplateArgumentList*>(Data);<br>
<br>
+  case Sema::TDK_ConstraintsNotSatisfied:<br>
+    return static_cast<CNSInfo*>(Data)->TemplateArgs;<br>
+<br>
   // Unhandled<br>
   case Sema::TDK_MiscellaneousDeductionFailure:<br>
     break;<br>
@@ -789,6 +816,7 @@ const TemplateArgument *DeductionFailureInfo::getFirstArg() {<br>
   case Sema::TDK_SubstitutionFailure:<br>
   case Sema::TDK_CUDATargetMismatch:<br>
   case Sema::TDK_NonDependentConversionFailure:<br>
+  case Sema::TDK_ConstraintsNotSatisfied:<br>
     return nullptr;<br>
<br>
   case Sema::TDK_IncompletePack:<br>
@@ -820,6 +848,7 @@ const TemplateArgument *DeductionFailureInfo::getSecondArg() {<br>
   case Sema::TDK_SubstitutionFailure:<br>
   case Sema::TDK_CUDATargetMismatch:<br>
   case Sema::TDK_NonDependentConversionFailure:<br>
+  case Sema::TDK_ConstraintsNotSatisfied:<br>
     return nullptr;<br>
<br>
   case Sema::TDK_Inconsistent:<br>
@@ -1255,6 +1284,8 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,<br>
     return NewTarget != OldTarget;<br>
   }<br>
<br>
+  // TODO: Concepts: Check function trailing requires clauses here.<br>
+<br>
   // The signatures match; this is not an overload.<br>
   return false;<br>
 }<br>
@@ -10333,6 +10364,21 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,<br>
     MaybeEmitInheritedConstructorNote(S, Found);<br>
     return;<br>
<br>
+  case Sema::TDK_ConstraintsNotSatisfied: {<br>
+    // Format the template argument list into the argument string.<br>
+    SmallString<128> TemplateArgString;<br>
+    TemplateArgumentList *Args = DeductionFailure.getTemplateArgumentList();<br>
+    TemplateArgString = " ";<br>
+    TemplateArgString += S.getTemplateArgumentBindingsText(<br>
+        getDescribedTemplate(Templated)->getTemplateParameters(), *Args);<br>
+    S.Diag(Templated->getLocation(),<br>
+           diag::note_ovl_candidate_unsatisfied_constraints)<br>
+        << TemplateArgString;<br>
+<br>
+    S.DiagnoseUnsatisfiedConstraint(<br>
+        static_cast<CNSInfo*>(DeductionFailure.Data)->Satisfaction);<br>
+    return;<br>
+  }<br>
   case Sema::TDK_TooManyArguments:<br>
   case Sema::TDK_TooFewArguments:<br>
     DiagnoseArityMismatch(S, Found, Templated, NumArgs);<br>
@@ -10785,6 +10831,7 @@ static unsigned RankDeductionFailure(const DeductionFailureInfo &DFI) {<br>
<br>
   case Sema::TDK_SubstitutionFailure:<br>
   case Sema::TDK_DeducedMismatch:<br>
+  case Sema::TDK_ConstraintsNotSatisfied:<br>
   case Sema::TDK_DeducedMismatchNested:<br>
   case Sema::TDK_NonDeducedMismatch:<br>
   case Sema::TDK_MiscellaneousDeductionFailure:<br>
<br>
diff  --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp<br>
index 3f2d38630c36..3003a9a889df 100644<br>
--- a/clang/lib/Sema/SemaTemplate.cpp<br>
+++ b/clang/lib/Sema/SemaTemplate.cpp<br>
@@ -3210,8 +3210,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,<br>
<br>
   TemplateDecl *Template = Name.getAsTemplateDecl();<br>
   if (!Template || isa<FunctionTemplateDecl>(Template) ||<br>
-      isa<VarTemplateDecl>(Template) ||<br>
-      isa<ConceptDecl>(Template)) {<br>
+      isa<VarTemplateDecl>(Template) || isa<ConceptDecl>(Template)) {<br>
     // We might have a substituted template template parameter pack. If so,<br>
     // build a template specialization type for it.<br>
     if (Name.getAsSubstTemplateTemplateParmPack())<br>
@@ -3227,7 +3226,8 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,<br>
   // template.<br>
   SmallVector<TemplateArgument, 4> Converted;<br>
   if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs,<br>
-                                false, Converted))<br>
+                                false, Converted,<br>
+                                /*UpdateArgsWithConversion=*/true))<br>
     return QualType();<br>
<br>
   QualType CanonType;<br>
@@ -3235,6 +3235,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,<br>
   bool InstantiationDependent = false;<br>
   if (TypeAliasTemplateDecl *AliasTemplate =<br>
           dyn_cast<TypeAliasTemplateDecl>(Template)) {<br>
+<br>
     // Find the canonical type for this type alias template specialization.<br>
     TypeAliasDecl *Pattern = AliasTemplate->getTemplatedDecl();<br>
     if (Pattern->isInvalidDecl())<br>
@@ -3872,7 +3873,8 @@ DeclResult Sema::ActOnVarTemplateSpecialization(<br>
   // template.<br>
   SmallVector<TemplateArgument, 4> Converted;<br>
   if (CheckTemplateArgumentList(VarTemplate, TemplateNameLoc, TemplateArgs,<br>
-                                false, Converted))<br>
+                                false, Converted,<br>
+                                /*UpdateArgsWithConversion=*/true))<br>
     return true;<br>
<br>
   // Find the variable template (partial) specialization declaration that<br>
@@ -4043,7 +4045,7 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,<br>
   if (CheckTemplateArgumentList(<br>
           Template, TemplateNameLoc,<br>
           const_cast<TemplateArgumentListInfo &>(TemplateArgs), false,<br>
-          Converted))<br>
+          Converted, /*UpdateArgsWithConversion=*/true))<br>
     return true;<br>
<br>
   // Find the variable template specialization declaration that<br>
@@ -4234,7 +4236,7 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,<br>
                                 /*UpdateArgsWithConversion=*/false))<br>
     return ExprError();<br>
<br>
-  Optional<bool> IsSatisfied;<br>
+  ConstraintSatisfaction Satisfaction;<br>
   bool AreArgsDependent = false;<br>
   for (TemplateArgument &Arg : Converted) {<br>
     if (Arg.isDependent()) {<br>
@@ -4242,25 +4244,21 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,<br>
       break;<br>
     }<br>
   }<br>
-  if (!AreArgsDependent) {<br>
-    InstantiatingTemplate Inst(*this, ConceptNameLoc,<br>
-        InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted,<br>
-        SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc,<br>
-                    TemplateArgs->getRAngleLoc()));<br>
-    MultiLevelTemplateArgumentList MLTAL;<br>
-    MLTAL.addOuterTemplateArguments(Converted);<br>
-    bool Satisfied;<br>
-    if (CalculateConstraintSatisfaction(NamedConcept, MLTAL,<br>
-                                        NamedConcept->getConstraintExpr(),<br>
-                                        Satisfied))<br>
+  if (!AreArgsDependent &&<br>
+      CheckConstraintSatisfaction(NamedConcept,<br>
+                                  {NamedConcept->getConstraintExpr()},<br>
+                                  Converted,<br>
+                                  SourceRange(SS.isSet() ? SS.getBeginLoc() :<br>
+                                                           ConceptNameLoc,<br>
+                                              TemplateArgs->getRAngleLoc()),<br>
+                                  Satisfaction))<br>
       return ExprError();<br>
-    IsSatisfied = Satisfied;<br>
-  }<br>
+<br>
   return ConceptSpecializationExpr::Create(Context,<br>
       SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},<br>
       TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept,<br>
       ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted,<br>
-      IsSatisfied);<br>
+      AreArgsDependent ? nullptr : &Satisfaction);<br>
 }<br>
<br>
 ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,<br>
@@ -5206,7 +5204,11 @@ bool Sema::CheckTemplateArgumentList(<br>
     TemplateDecl *Template, SourceLocation TemplateLoc,<br>
     TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs,<br>
     SmallVectorImpl<TemplateArgument> &Converted,<br>
-    bool UpdateArgsWithConversions) {<br>
+    bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied) {<br>
+<br>
+  if (ConstraintsNotSatisfied)<br>
+    *ConstraintsNotSatisfied = false;<br>
+<br>
   // Make a copy of the template arguments for processing.  Only make the<br>
   // changes at the end when successful in matching the arguments to the<br>
   // template.<br>
@@ -5321,7 +5323,6 @@ bool Sema::CheckTemplateArgumentList(<br>
       if ((*Param)->isTemplateParameterPack() && !ArgumentPack.empty())<br>
         Converted.push_back(<br>
             TemplateArgument::CreatePackCopy(Context, ArgumentPack));<br>
-<br>
       return false;<br>
     }<br>
<br>
@@ -5460,6 +5461,15 @@ bool Sema::CheckTemplateArgumentList(<br>
   if (UpdateArgsWithConversions)<br>
     TemplateArgs = std::move(NewArgs);<br>
<br>
+  if (!PartialTemplateArgs &&<br>
+      EnsureTemplateArgumentListConstraints(<br>
+        Template, Converted, SourceRange(TemplateLoc,<br>
+                                         TemplateArgs.getRAngleLoc()))) {<br>
+    if (ConstraintsNotSatisfied)<br>
+      *ConstraintsNotSatisfied = true;<br>
+    return true;<br>
+  }<br>
+<br>
   return false;<br>
 }<br>
<br>
@@ -7794,7 +7804,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(<br>
   // template.<br>
   SmallVector<TemplateArgument, 4> Converted;<br>
   if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc,<br>
-                                TemplateArgs, false, Converted))<br>
+                                TemplateArgs, false, Converted,<br>
+                                /*UpdateArgsWithConversion=*/true))<br>
     return true;<br>
<br>
   // Find the class template (partial) specialization declaration that<br>
@@ -9040,7 +9051,8 @@ DeclResult Sema::ActOnExplicitInstantiation(<br>
   // template.<br>
   SmallVector<TemplateArgument, 4> Converted;<br>
   if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc,<br>
-                                TemplateArgs, false, Converted))<br>
+                                TemplateArgs, false, Converted,<br>
+                                /*UpdateArgsWithConversion=*/true))<br>
     return true;<br>
<br>
   // Find the class template specialization declaration that<br>
<br>
diff  --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp<br>
index 64ef819e30d4..327447746c39 100644<br>
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp<br>
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp<br>
@@ -2591,6 +2591,23 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param,<br>
   return ConvertArg(Arg, 0);<br>
 }<br>
<br>
+template<typename TemplateDeclT><br>
+static Sema::TemplateDeductionResult<br>
+CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,<br>
+                                ArrayRef<TemplateArgument> DeducedArgs,<br>
+                                TemplateDeductionInfo &Info) {<br>
+  llvm::SmallVector<const Expr *, 3> AssociatedConstraints;<br>
+  Template->getAssociatedConstraints(AssociatedConstraints);<br>
+  if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints,<br>
+                                    DeducedArgs, Info.getLocation(),<br>
+                                    Info.AssociatedConstraintsSatisfaction) ||<br>
+      !Info.AssociatedConstraintsSatisfaction.IsSatisfied) {<br>
+    Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));<br>
+    return Sema::TDK_ConstraintsNotSatisfied;<br>
+  }<br>
+  return Sema::TDK_Success;<br>
+}<br>
+<br>
 // FIXME: This should not be a template, but<br>
 // ClassTemplatePartialSpecializationDecl sadly does not derive from<br>
 // TemplateDecl.<br>
@@ -2688,6 +2705,10 @@ static Sema::TemplateDeductionResult ConvertDeducedTemplateArguments(<br>
     // If we get here, we successfully used the default template argument.<br>
   }<br>
<br>
+  if (Sema::TemplateDeductionResult Result<br>
+        = CheckDeducedArgumentConstraints(S, Template, Builder, Info))<br>
+    return Result;<br>
+<br>
   return Sema::TDK_Success;<br>
 }<br>
<br>
@@ -2767,10 +2788,14 @@ FinishTemplateArgumentDeduction(<br>
     return Sema::TDK_SubstitutionFailure;<br>
   }<br>
<br>
+  bool ConstraintsNotSatisfied;<br>
   SmallVector<TemplateArgument, 4> ConvertedInstArgs;<br>
   if (S.CheckTemplateArgumentList(Template, Partial->getLocation(), InstArgs,<br>
-                                  false, ConvertedInstArgs))<br>
-    return Sema::TDK_SubstitutionFailure;<br>
+                                  false, ConvertedInstArgs,<br>
+                                  /*UpdateArgsWithConversions=*/true,<br>
+                                  &ConstraintsNotSatisfied))<br>
+    return ConstraintsNotSatisfied ? Sema::TDK_ConstraintsNotSatisfied :<br>
+                                     Sema::TDK_SubstitutionFailure;<br>
<br>
   TemplateParameterList *TemplateParams = Template->getTemplateParameters();<br>
   for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {<br>
@@ -2831,7 +2856,6 @@ static Sema::TemplateDeductionResult FinishTemplateArgumentDeduction(<br>
   return Sema::TDK_Success;<br>
 }<br>
<br>
-<br>
 /// Perform template argument deduction to determine whether<br>
 /// the given template arguments match the given class template<br>
 /// partial specialization per C++ [temp.class.spec.match].<br>
@@ -5049,6 +5073,7 @@ template<typename TemplateLikeDecl><br>
 static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,<br>
                                      TemplateLikeDecl *P2,<br>
                                      TemplateDeductionInfo &Info) {<br>
+  // TODO: Concepts: Regard constraints<br>
   // C++ [temp.class.order]p1:<br>
   //   For two class template partial specializations, the first is at least as<br>
   //   specialized as the second if, given the following rewrite to two<br>
<br>
diff  --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp<br>
index 0daa33cfbef5..e6c3ab422067 100644<br>
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp<br>
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp<br>
@@ -363,7 +363,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(<br>
<br>
 Sema::InstantiatingTemplate::InstantiatingTemplate(<br>
     Sema &SemaRef, SourceLocation PointOfInstantiation,<br>
-    ConstraintsCheck, TemplateDecl *Template,<br>
+    ConstraintsCheck, NamedDecl *Template,<br>
     ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange)<br>
     : InstantiatingTemplate(<br>
           SemaRef, CodeSynthesisContext::ConstraintsCheck,<br>
@@ -372,7 +372,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(<br>
<br>
 Sema::InstantiatingTemplate::InstantiatingTemplate(<br>
     Sema &SemaRef, SourceLocation PointOfInstantiation,<br>
-    ConstraintSubstitution, TemplateDecl *Template,<br>
+    ConstraintSubstitution, NamedDecl *Template,<br>
     sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange)<br>
     : InstantiatingTemplate(<br>
           SemaRef, CodeSynthesisContext::ConstraintSubstitution,<br>
@@ -691,24 +691,27 @@ void Sema::PrintInstantiationStack() {<br>
     case CodeSynthesisContext::Memoization:<br>
       break;<br>
<br>
-    case CodeSynthesisContext::ConstraintsCheck:<br>
-      if (auto *CD = dyn_cast<ConceptDecl>(Active->Entity)) {<br>
-        SmallVector<char, 128> TemplateArgsStr;<br>
-        llvm::raw_svector_ostream OS(TemplateArgsStr);<br>
-        CD->printName(OS);<br>
-        printTemplateArgumentList(OS, Active->template_arguments(),<br>
-                                  getPrintingPolicy());<br>
-        Diags.Report(Active->PointOfInstantiation,<br>
-                     diag::note_concept_specialization_here)<br>
-          << OS.str()<br>
-          << Active->InstantiationRange;<br>
-        break;<br>
+    case CodeSynthesisContext::ConstraintsCheck: {<br>
+      unsigned DiagID = 0;<br>
+      if (isa<ConceptDecl>(Active->Entity))<br>
+        DiagID = diag::note_concept_specialization_here;<br>
+      else if (isa<TemplateDecl>(Active->Entity))<br>
+        DiagID = diag::note_checking_constraints_for_template_id_here;<br>
+      else if (isa<VarTemplatePartialSpecializationDecl>(Active->Entity))<br>
+        DiagID = diag::note_checking_constraints_for_var_spec_id_here;<br>
+      else {<br>
+        assert(isa<ClassTemplatePartialSpecializationDecl>(Active->Entity));<br>
+        DiagID = diag::note_checking_constraints_for_class_spec_id_here;<br>
       }<br>
-      // TODO: Concepts - implement this for constrained templates and partial<br>
-      // specializations.<br>
-      llvm_unreachable("only concept constraints are supported right now");<br>
+      SmallVector<char, 128> TemplateArgsStr;<br>
+      llvm::raw_svector_ostream OS(TemplateArgsStr);<br>
+      cast<NamedDecl>(Active->Entity)->printName(OS);<br>
+      printTemplateArgumentList(OS, Active->template_arguments(),<br>
+                                getPrintingPolicy());<br>
+      Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str()<br>
+        << Active->InstantiationRange;<br>
       break;<br>
-      <br>
+    }<br>
     case CodeSynthesisContext::ConstraintSubstitution:<br>
       Diags.Report(Active->PointOfInstantiation,<br>
                    diag::note_constraint_substitution_here)<br>
<br>
diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp<br>
index 31a4302ba826..c1d7b7f84f30 100644<br>
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp<br>
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp<br>
@@ -3279,7 +3279,8 @@ TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl(<br>
                                         D->getLocation(),<br>
                                         InstTemplateArgs,<br>
                                         false,<br>
-                                        Converted))<br>
+                                        Converted,<br>
+                                        /*UpdateArgsWithConversion=*/true))<br>
     return nullptr;<br>
<br>
   // Figure out where to insert this class template explicit specialization<br>
@@ -3400,7 +3401,8 @@ Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl(<br>
   // Check that the template argument list is well-formed for this template.<br>
   SmallVector<TemplateArgument, 4> Converted;<br>
   if (SemaRef.CheckTemplateArgumentList(InstVarTemplate, D->getLocation(),<br>
-                                        VarTemplateArgsInfo, false, Converted))<br>
+                                        VarTemplateArgsInfo, false, Converted,<br>
+                                        /*UpdateArgsWithConversion=*/true))<br>
     return nullptr;<br>
<br>
   // Check whether we've already seen a declaration of this specialization.<br>
<br>
diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp<br>
index a275e0c30579..f5d659affac7 100644<br>
--- a/clang/lib/Serialization/ASTReaderStmt.cpp<br>
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp<br>
@@ -12,6 +12,7 @@<br>
 //===----------------------------------------------------------------------===//<br>
<br>
 #include "clang/Serialization/ASTReader.h"<br>
+#include "clang/AST/ASTConcept.h"<br>
 #include "clang/AST/ASTContext.h"<br>
 #include "clang/AST/AttrIterator.h"<br>
 #include "clang/AST/Decl.h"<br>
@@ -742,14 +743,33 @@ void ASTStmtReader::VisitConceptSpecializationExpr(<br>
   E->TemplateKWLoc = Record.readSourceLocation();<br>
   E->ConceptNameLoc = Record.readSourceLocation();<br>
   E->FoundDecl = ReadDeclAs<NamedDecl>();<br>
-  E->NamedConcept.setPointer(ReadDeclAs<ConceptDecl>());<br>
+  E->NamedConcept = ReadDeclAs<ConceptDecl>();<br>
   const ASTTemplateArgumentListInfo *ArgsAsWritten =<br>
       Record.readASTTemplateArgumentListInfo();<br>
   llvm::SmallVector<TemplateArgument, 4> Args;<br>
   for (unsigned I = 0; I < NumTemplateArgs; ++I)<br>
     Args.push_back(Record.readTemplateArgument());<br>
   E->setTemplateArguments(ArgsAsWritten, Args);<br>
-  E->NamedConcept.setInt(Record.readInt() == 1);<br>
+  ConstraintSatisfaction Satisfaction;<br>
+  Satisfaction.IsSatisfied = Record.readInt();<br>
+  if (!Satisfaction.IsSatisfied) {<br>
+    unsigned NumDetailRecords = Record.readInt();<br>
+    for (unsigned i = 0; i != NumDetailRecords; ++i) {<br>
+      Expr *ConstraintExpr = Record.readExpr();<br>
+      bool IsDiagnostic = Record.readInt();<br>
+      if (IsDiagnostic) {<br>
+        SourceLocation DiagLocation = Record.readSourceLocation();<br>
+        std::string DiagMessage = Record.readString();<br>
+        Satisfaction.Details.emplace_back(<br>
+            ConstraintExpr, new (Record.getContext())<br>
+                                ConstraintSatisfaction::SubstitutionDiagnostic{<br>
+                                    DiagLocation, DiagMessage});<br>
+      } else<br>
+        Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr());<br>
+    }<br>
+  }<br>
+  E->Satisfaction = ASTConstraintSatisfaction::Create(Record.getContext(),<br>
+                                                      Satisfaction);<br>
 }<br>
<br>
 void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {<br>
<br>
diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp<br>
index c39d4d39bcdf..671e55e14521 100644<br>
--- a/clang/lib/Serialization/ASTWriterStmt.cpp<br>
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp<br>
@@ -401,7 +401,25 @@ void ASTStmtWriter::VisitConceptSpecializationExpr(<br>
   Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());<br>
   for (const TemplateArgument &Arg : TemplateArgs)<br>
     Record.AddTemplateArgument(Arg);<br>
-  Record.push_back(E->isSatisfied());<br>
+  const ASTConstraintSatisfaction &Satisfaction = E->getSatisfaction();<br>
+  Record.push_back(Satisfaction.IsSatisfied);<br>
+  if (!Satisfaction.IsSatisfied) {<br>
+    Record.push_back(Satisfaction.NumRecords);<br>
+    for (const auto &DetailRecord : Satisfaction) {<br>
+      Record.AddStmt(const_cast<Expr *>(DetailRecord.first));<br>
+      auto *E = DetailRecord.second.dyn_cast<Expr *>();<br>
+      Record.push_back(E == nullptr);<br>
+      if (E)<br>
+        Record.AddStmt(E);<br>
+      else {<br>
+        auto *Diag = DetailRecord.second.get<std::pair<SourceLocation,<br>
+                                                       StringRef> *>();<br>
+        Record.AddSourceLocation(Diag->first);<br>
+        Record.AddString(Diag->second);<br>
+      }<br>
+    }<br>
+  }<br>
+<br>
   Code = serialization::EXPR_CONCEPT_SPECIALIZATION;<br>
 }<br>
<br>
<br>
diff  --git a/clang/test/CXX/expr/expr.prim/<a href="http://expr.prim.id/p3.cpp" rel="noreferrer" target="_blank">expr.prim.id/p3.cpp</a> b/clang/test/CXX/expr/expr.prim/<a href="http://expr.prim.id/p3.cpp" rel="noreferrer" target="_blank">expr.prim.id/p3.cpp</a><br>
index dd3f0c0e3d6c..1e10d4550ce0 100644<br>
--- a/clang/test/CXX/expr/expr.prim/<a href="http://expr.prim.id/p3.cpp" rel="noreferrer" target="_blank">expr.prim.id/p3.cpp</a><br>
+++ b/clang/test/CXX/expr/expr.prim/<a href="http://expr.prim.id/p3.cpp" rel="noreferrer" target="_blank">expr.prim.id/p3.cpp</a><br>
@@ -72,6 +72,15 @@ template<typename T> struct T2 { static constexpr bool value = sizeof(T) == 2; }<br>
 static_assert(IsTypePredicate<T2>);<br>
 static_assert(!IsTypePredicate<T1>);<br>
<br>
+template<typename T, typename U, typename... Ts><br>
+concept OneOf = (Same<T, Ts> || ...);<br>
+<br>
+template<typename... X><br>
+constexpr bool S = OneOf<X..., int, int>;<br>
+<br>
+static_assert(S<int, long, int>);<br>
+static_assert(!S<long, int, char, char>);<br>
+<br>
 namespace piecewise_substitution {<br>
   template <typename T><br>
   concept True = true;<br>
@@ -147,3 +156,25 @@ template<typename T><br>
 struct X { static constexpr bool a = SameSize<T>; };<br>
<br>
 static_assert(X<unsigned>::a);<br>
+<br>
+// static_assert concept diagnostics<br>
+template<typename T><br>
+concept Large = sizeof(T) > 100;<br>
+// expected-note@-1 2{{because 'sizeof(small) > 100' (1 > 100) evaluated to false}}<br>
+<br>
+struct small { };<br>
+static_assert(Large<small>);<br>
+// expected-error@-1 {{static_assert failed}}<br>
+// expected-note@-2 {{because 'small' does not satisfy 'Large'}}<br>
+static_assert(Large<small>, "small isn't large");<br>
+// expected-error@-1 {{static_assert failed "small isn't large"}}<br>
+// expected-note@-2 {{because 'small' does not satisfy 'Large'}}<br>
+<br>
+// Make sure access-checking can fail a concept specialization<br>
+<br>
+class T4 { static constexpr bool f = true; };<br>
+template<typename T> concept AccessPrivate = T{}.f;<br>
+// expected-note@-1{{because substituted constraint expression is ill-formed: 'f' is a private member of 'T4'}}<br>
+static_assert(AccessPrivate<T4>);<br>
+// expected-error@-1{{static_assert failed}}<br>
+// expected-note@-2{{because 'T4' does not satisfy 'AccessPrivate'}}<br>
<br>
diff  --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp<br>
new file mode 100644<br>
index 000000000000..c1a3a27fbeac<br>
--- /dev/null<br>
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/function-templates.cpp<br>
@@ -0,0 +1,43 @@<br>
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s<br>
+<br>
+template<typename T><br>
+constexpr bool is_ptr_v = false;<br>
+<br>
+template<typename T><br>
+constexpr bool is_ptr_v<T*> = true;<br>
+<br>
+template<typename T, typename U><br>
+constexpr bool is_same_v = false;<br>
+<br>
+template<typename T><br>
+constexpr bool is_same_v<T, T> = true;<br>
+<br>
+template<typename T> requires is_ptr_v<T> // expected-note   {{because 'is_ptr_v<int>' evaluated to false}}<br>
+                         // expected-note@-1{{because 'is_ptr_v<char>' evaluated to false}}<br>
+auto dereference(T t) { // expected-note   {{candidate template ignored: constraints not satisfied [with T = int]}}<br>
+                        // expected-note@-1{{candidate template ignored: constraints not satisfied [with T = char]}}<br>
+  return *t;<br>
+}<br>
+<br>
+static_assert(is_same_v<decltype(dereference<int*>(nullptr)), int>);<br>
+static_assert(is_same_v<decltype(dereference(2)), int>); // expected-error {{no matching function for call to 'dereference'}}<br>
+static_assert(is_same_v<decltype(dereference<char>('a')), char>); // expected-error {{no matching function for call to 'dereference'}}<br>
+<br>
+<br>
+template<typename T> requires T{} + T{} // expected-note {{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}<br>
+auto foo(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}<br>
+  return t + t;<br>
+}<br>
+<br>
+<br>
+template<typename T> requires !((T{} - T{}) && (T{} + T{})) || false<br>
+// expected-note@-1{{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}<br>
+// expected-note@-2{{and 'false' evaluated to false}}<br>
+auto bar(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}<br>
+  return t + t;<br>
+}<br>
+<br>
+struct A { };<br>
+<br>
+static_assert(foo(A{})); // expected-error {{no matching function for call to 'foo'}}<br>
+static_assert(bar(A{})); // expected-error {{no matching function for call to 'bar'}}<br>
\ No newline at end of file<br>
<br>
diff  --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp<br>
new file mode 100644<br>
index 000000000000..24caa5063a1b<br>
--- /dev/null<br>
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp<br>
@@ -0,0 +1,92 @@<br>
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s<br>
+<br>
+template<typename T> requires sizeof(T) >= 2 // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}}<br>
+struct A {<br>
+  static constexpr int value = sizeof(T);<br>
+};<br>
+<br>
+static_assert(A<int>::value == 4);<br>
+static_assert(A<char>::value == 1); // expected-error{{constraints not satisfied for class template 'A' [with T = char]}}<br>
+<br>
+template<typename T, typename U><br>
+  requires sizeof(T) != sizeof(U) // expected-note{{because 'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}}<br>
+           && sizeof(T) >= 4 // expected-note{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}<br>
+constexpr int SizeDiff = sizeof(T) > sizeof(U) ? sizeof(T) - sizeof(U) : sizeof(U) - sizeof(T);<br>
+<br>
+static_assert(SizeDiff<int, char> == 3);<br>
+static_assert(SizeDiff<int, char[4]> == 0); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = int, U = char [4]]}}<br>
+static_assert(SizeDiff<char, int> == 3); // expected-error{{constraints not satisfied for variable template 'SizeDiff' [with T = char, U = int]}}<br>
+<br>
+template<typename... Ts><br>
+  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}}<br>
+constexpr auto SumSizes = (sizeof(Ts) + ...);<br>
+<br>
+static_assert(SumSizes<char, long long, int> == 13);<br>
+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]>]}}<br>
+<br>
+template<typename T><br>
+concept IsBig = sizeof(T) > 100; // expected-note{{because 'sizeof(int) > 100' (4 > 100) evaluated to false}}<br>
+<br>
+template<typename T><br>
+  requires IsBig<T> // expected-note{{'int' does not satisfy 'IsBig'}}<br>
+using BigPtr = T*;<br>
+<br>
+static_assert(sizeof(BigPtr<int>)); // expected-error{{constraints not satisfied for alias template 'BigPtr' [with T = int]}}}}<br>
+<br>
+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}}<br>
+struct S { static constexpr bool value = true; };<br>
+<br>
+struct S2 { static constexpr bool value = true; };<br>
+<br>
+static_assert(S<int>::value); // expected-error{{constraints not satisfied for class template 'S' [with T = int]}}<br>
+static_assert(S<S2>::value);<br>
+<br>
+template<typename T><br>
+struct AA<br>
+{<br>
+    template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}<br>
+    struct B<br>
+    {<br>
+        static constexpr int a = 0;<br>
+    };<br>
+<br>
+    template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}<br>
+    static constexpr int b = 1;<br>
+<br>
+    template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}<br>
+    static constexpr int getB() { // expected-note{{candidate template ignored: constraints not satisfied [with U = int [2]]}}<br>
+        return 2;<br>
+    }<br>
+<br>
+    static auto foo()<br>
+    {<br>
+        return B<T[2]>::a; // expected-error{{constraints not satisfied for class template 'B' [with U = int [2]]}}<br>
+    }<br>
+<br>
+    static auto foo1()<br>
+    {<br>
+        return b<T[2]>; // expected-error{{constraints not satisfied for variable template 'b' [with U = int [2]]}}<br>
+    }<br>
+<br>
+    static auto foo2()<br>
+    {<br>
+        return AA<T>::getB<T[2]>(); // expected-error{{no matching function for call to 'getB'}}<br>
+    }<br>
+};<br>
+<br>
+constexpr auto x = AA<int>::foo(); // expected-note{{in instantiation of member function 'AA<int>::foo' requested here}}<br>
+constexpr auto x1 = AA<int>::foo1(); // expected-note{{in instantiation of member function 'AA<int>::foo1' requested here}}<br>
+constexpr auto x2 = AA<int>::foo2(); // expected-note{{in instantiation of member function 'AA<int>::foo2' requested here}}<br>
+<br>
+template<typename T><br>
+struct B { using type = typename T::type; }; // expected-error{{type 'int' cannot be used prior to '::' because it has no members}}<br>
+<br>
+template<typename T> requires B<T>::type // expected-note{{in instantiation of template class 'B<int>' requested here}}<br>
+                                         // expected-note@-1{{while substituting template arguments into constraint expression here}}<br>
+struct C { };<br>
+<br>
+template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}<br>
+struct D { };<br>
+<br>
+static_assert(C<int>{}); // expected-note{{while checking constraint satisfaction for template 'C<int>' required here}}<br>
+static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' required here}}<br>
\ No newline at end of file<br>
<br>
diff  --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp<br>
new file mode 100644<br>
index 000000000000..47bd2a550769<br>
--- /dev/null<br>
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp<br>
@@ -0,0 +1,67 @@<br>
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s<br>
+<br>
+namespace class_templates<br>
+{<br>
+  template<typename T, typename U> requires sizeof(T) >= 4 // expected-note {{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}<br>
+  struct is_same { static constexpr bool value = false; };<br>
+<br>
+  template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4<br>
+  struct is_same<T*, T*> { static constexpr bool value = true; };<br>
+<br>
+  static_assert(!is_same<char*, char*>::value);<br>
+  static_assert(!is_same<short*, short*>::value);<br>
+  static_assert(is_same<int*, int*>::value);<br>
+  static_assert(is_same<char, char>::value); // expected-error {{constraints not satisfied for class template 'is_same' [with T = char, U = char]}}<br>
+<br>
+  template<typename T><br>
+  struct A { using type = typename T::type; }; // expected-error{{type 'int *' cannot be used prior to '::' because it has no members}}<br>
+<br>
+  template<typename T><br>
+  struct B {};<br>
+<br>
+  template<typename T> requires A<T>::type // expected-note{{in instantiation of template class 'class_templates::A<int *>' requested here}}<br>
+                                           // expected-note@-1{{while substituting template arguments into constraint expression here}}<br>
+  struct B<T*> {};<br>
+<br>
+  template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}<br>
+  struct B<T**> {};<br>
+<br>
+  static_assert((B<int**>{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B<int *>' required here}}<br>
+  // expected-note@-1{{while checking constraint satisfaction for class template partial specialization 'B<int>' required here}}<br>
+  // expected-note@-2{{during template argument deduction for class template partial specialization 'B<T *>' [with T = int *]}}<br>
+  // expected-note@-3{{during template argument deduction for class template partial specialization 'B<T **>' [with T = int]}}<br>
+  // expected-note@-4 2{{in instantiation of template class 'class_templates::B<int **>' requested here}}<br>
+}<br>
+<br>
+namespace variable_templates<br>
+{<br>
+  template<typename T, typename U> requires sizeof(T) >= 4<br>
+  constexpr bool is_same_v = false;<br>
+<br>
+  template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4<br>
+  constexpr bool is_same_v<T*, T*> = true;<br>
+<br>
+  static_assert(!is_same_v<char*, char*>);<br>
+  static_assert(!is_same_v<short*, short*>);<br>
+  static_assert(is_same_v<int*, int*>);<br>
+<br>
+  template<typename T><br>
+  struct A { using type = typename T::type; }; // expected-error{{type 'int *' cannot be used prior to '::' because it has no members}}<br>
+<br>
+  template<typename T><br>
+  constexpr bool v1 = false;<br>
+<br>
+  template<typename T> requires A<T>::type // expected-note{{in instantiation of template class 'variable_templates::A<int *>' requested here}}<br>
+                                           // expected-note@-1{{while substituting template arguments into constraint expression here}}<br>
+  constexpr bool v1<T*> = true;<br>
+<br>
+  template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}<br>
+  constexpr bool v1<T**> = true;<br>
+<br>
+  static_assert(v1<int**>); // expected-note{{while checking constraint satisfaction for variable template partial specialization 'v1<int *>' required here}}<br>
+  // expected-note@-1{{while checking constraint satisfaction for variable template partial specialization 'v1<int>' required here}}<br>
+  // expected-note@-2{{during template argument deduction for variable template partial specialization 'v1<T *>' [with T = int *]}}<br>
+  // expected-note@-3{{during template argument deduction for variable template partial specialization 'v1<T **>' [with T = int]}}<br>
+  // expected-error@-4{{static_assert failed due to requirement 'v1<int **>'}}<br>
+<br>
+}<br>
\ No newline at end of file<br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>